fix(codegen): assertValue works with disabled input (#29205)

Also add tests for new codegen functionality.

Fixes #29161.
This commit is contained in:
Dmitry Gozman 2024-01-27 14:54:22 -08:00 committed by GitHub
parent 36ebdfb441
commit acc72c2663
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 116 additions and 9 deletions

View file

@ -547,12 +547,7 @@ class TextAssertionTool implements RecorderTool {
onClick(event: MouseEvent) {
consumeEvent(event);
if (this._kind === 'value') {
const action = this._generateAction();
if (action) {
this._recorder.delegate.recordAction?.(action);
this._recorder.delegate.setMode?.('recording');
this._recorder.overlay?.flashToolSucceeded('assertingValue');
}
this._commitAssertValue();
} else {
if (!this._dialogElement)
this._showDialog();
@ -565,6 +560,15 @@ class TextAssertionTool implements RecorderTool {
event.preventDefault();
}
onPointerUp(event: PointerEvent) {
const target = this._hoverHighlight?.elements[0];
if (this._kind === 'value' && target && target.nodeName === 'INPUT' && (target as HTMLInputElement).disabled) {
// Click on a disabled input does not produce a "click" event, but we still want
// to assert the value.
this._commitAssertValue();
}
}
onMouseMove(event: MouseEvent) {
if (this._dialogElement)
return;
@ -719,6 +723,17 @@ class TextAssertionTool implements RecorderTool {
this._recorder.document.removeEventListener('keydown', this._keyboardListener!);
this._dialogElement = null;
}
private _commitAssertValue() {
if (this._kind !== 'value')
return;
const action = this._generateAction();
if (!action)
return;
this._recorder.delegate.recordAction?.(action);
this._recorder.delegate.setMode?.('recording');
this._recorder.overlay?.flashToolSucceeded('assertingValue');
}
}
class Overlay {
@ -1138,8 +1153,10 @@ export class Recorder {
}
private _ignoreOverlayEvent(event: Event) {
const target = event.composedPath()[0] as Element;
return target.nodeName.toLowerCase() === 'x-pw-glass';
return event.composedPath().some(e => {
const nodeName = (e as Element).nodeName || '';
return nodeName.toLowerCase() === 'x-pw-glass';
});
}
deepEventTarget(event: Event): HTMLElement {

View file

@ -161,7 +161,7 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
case 'assertVisible':
return `await Expect(${subject}.${this._asLocator(action.selector)}).ToBeVisibleAsync();`;
case 'assertValue': {
const assertion = action.value ? `ToHaveValueAsync(${quote(action.value)})` : `ToBeEmpty()`;
const assertion = action.value ? `ToHaveValueAsync(${quote(action.value)})` : `ToBeEmptyAsync()`;
return `await Expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
}
}

View file

@ -558,4 +558,94 @@ await page.GetByLabel("Coun\\"try").ClickAsync();`);
'click',
]);
});
test('should assert value', async ({ openRecorder }) => {
const recorder = await openRecorder();
await recorder.setContentAndWait(`
<input id=first value=foo>
<input id=second disabled value=bar>
<input id=third>
<input id=fourth type=checkbox checked>
`);
await recorder.page.click('x-pw-tool-item.value');
await recorder.hoverOverElement('#first');
const [sources1] = await Promise.all([
recorder.waitForOutput('JavaScript', '#first'),
recorder.trustedClick(),
]);
expect.soft(sources1.get('JavaScript')!.text).toContain(`await expect(page.locator('#first')).toHaveValue('foo')`);
expect.soft(sources1.get('Python')!.text).toContain(`expect(page.locator("#first")).to_have_value("foo")`);
expect.soft(sources1.get('Python Async')!.text).toContain(`await expect(page.locator("#first")).to_have_value("foo")`);
expect.soft(sources1.get('Java')!.text).toContain(`assertThat(page.locator("#first")).hasValue("foo")`);
expect.soft(sources1.get('C#')!.text).toContain(`await Expect(page.Locator("#first")).ToHaveValueAsync("foo")`);
await recorder.page.click('x-pw-tool-item.value');
await recorder.hoverOverElement('#third');
const [sources3] = await Promise.all([
recorder.waitForOutput('JavaScript', '#third'),
recorder.trustedClick(),
]);
expect.soft(sources3.get('JavaScript')!.text).toContain(`await expect(page.locator('#third')).toBeEmpty()`);
expect.soft(sources3.get('Python')!.text).toContain(`expect(page.locator("#third")).to_be_empty()`);
expect.soft(sources3.get('Python Async')!.text).toContain(`await expect(page.locator("#third")).to_be_empty()`);
expect.soft(sources3.get('Java')!.text).toContain(`assertThat(page.locator("#third")).isEmpty()`);
expect.soft(sources3.get('C#')!.text).toContain(`await Expect(page.Locator("#third")).ToBeEmptyAsync()`);
await recorder.page.click('x-pw-tool-item.value');
await recorder.hoverOverElement('#fourth');
const [sources4] = await Promise.all([
recorder.waitForOutput('JavaScript', '#fourth'),
recorder.trustedClick(),
]);
expect.soft(sources4.get('JavaScript')!.text).toContain(`await expect(page.locator('#fourth')).toBeChecked()`);
expect.soft(sources4.get('Python')!.text).toContain(`expect(page.locator("#fourth")).to_be_checked()`);
expect.soft(sources4.get('Python Async')!.text).toContain(`await expect(page.locator("#fourth")).to_be_checked()`);
expect.soft(sources4.get('Java')!.text).toContain(`assertThat(page.locator("#fourth")).isChecked()`);
expect.soft(sources4.get('C#')!.text).toContain(`await Expect(page.Locator("#fourth")).ToBeCheckedAsync()`);
});
test('should assert value on disabled input', async ({ openRecorder, browserName }) => {
test.fixme(browserName === 'firefox', 'pointerup event is not dispatched on a disabled input');
const recorder = await openRecorder();
await recorder.setContentAndWait(`
<input id=first value=foo>
<input id=second disabled value=bar>
<input id=third>
<input id=fourth type=checkbox checked>
`);
await recorder.page.click('x-pw-tool-item.value');
await recorder.hoverOverElement('#second');
const [sources2] = await Promise.all([
recorder.waitForOutput('JavaScript', '#second'),
recorder.trustedClick(),
]);
expect.soft(sources2.get('JavaScript')!.text).toContain(`await expect(page.locator('#second')).toHaveValue('bar')`);
expect.soft(sources2.get('Python')!.text).toContain(`expect(page.locator("#second")).to_have_value("bar")`);
expect.soft(sources2.get('Python Async')!.text).toContain(`await expect(page.locator("#second")).to_have_value("bar")`);
expect.soft(sources2.get('Java')!.text).toContain(`assertThat(page.locator("#second")).hasValue("bar")`);
expect.soft(sources2.get('C#')!.text).toContain(`await Expect(page.Locator("#second")).ToHaveValueAsync("bar")`);
});
test('should assert visibility', async ({ openRecorder }) => {
const recorder = await openRecorder();
await recorder.setContentAndWait(`<input>`);
await recorder.page.click('x-pw-tool-item.visibility');
await recorder.hoverOverElement('input');
const [sources1] = await Promise.all([
recorder.waitForOutput('JavaScript', 'textbox'),
recorder.trustedClick(),
]);
expect.soft(sources1.get('JavaScript')!.text).toContain(`await expect(page.getByRole('textbox')).toBeVisible()`);
expect.soft(sources1.get('Python')!.text).toContain(`expect(page.get_by_role("textbox")).to_be_visible()`);
expect.soft(sources1.get('Python Async')!.text).toContain(`await expect(page.get_by_role("textbox")).to_be_visible()`);
expect.soft(sources1.get('Java')!.text).toContain(`assertThat(page.getByRole(AriaRole.TEXTBOX)).isVisible()`);
expect.soft(sources1.get('C#')!.text).toContain(`await Expect(page.GetByRole(AriaRole.Textbox)).ToBeVisibleAsync()`);
});
});