Skip to content

Commit

Permalink
fix(codegen): assertValue works with disabled input (#29205)
Browse files Browse the repository at this point in the history
Also add tests for new codegen functionality.

Fixes #29161.
  • Loading branch information
dgozman authored Jan 27, 2024
1 parent 36ebdfb commit acc72c2
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 9 deletions.
33 changes: 25 additions & 8 deletions packages/playwright-core/src/server/injected/recorder/recorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/server/recorder/csharp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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};`;
}
}
Expand Down
90 changes: 90 additions & 0 deletions tests/library/inspector/cli-codegen-3.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()`);
});
});

0 comments on commit acc72c2

Please sign in to comment.