chore: more codegen fixes (#32816)

This commit is contained in:
Pavel Feldman 2024-09-25 18:18:36 -07:00 committed by GitHub
parent 0480bd0cac
commit 61801aa1ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 147 additions and 76 deletions

View File

@ -205,7 +205,7 @@ class InspectTool implements RecorderTool {
class RecordActionTool implements RecorderTool {
private _recorder: Recorder;
private _performingAction: actions.PerformOnRecordAction | null = null;
private _performingActions = new Set<actions.PerformOnRecordAction>();
private _hoveredModel: HighlightModel | null = null;
private _hoveredElement: HTMLElement | null = null;
private _activeModel: HighlightModel | null = null;
@ -333,21 +333,21 @@ class RecordActionTool implements RecorderTool {
onPointerDown(event: PointerEvent) {
if (this._shouldIgnoreMouseEvent(event))
return;
if (!this._performingAction)
if (!this._performingActions.size)
consumeEvent(event);
}
onPointerUp(event: PointerEvent) {
if (this._shouldIgnoreMouseEvent(event))
return;
if (!this._performingAction)
if (!this._performingActions.size)
consumeEvent(event);
}
onMouseDown(event: MouseEvent) {
if (this._shouldIgnoreMouseEvent(event))
return;
if (!this._performingAction)
if (!this._performingActions.size)
consumeEvent(event);
this._activeModel = this._hoveredModel;
}
@ -355,7 +355,7 @@ class RecordActionTool implements RecorderTool {
onMouseUp(event: MouseEvent) {
if (this._shouldIgnoreMouseEvent(event))
return;
if (!this._performingAction)
if (!this._performingActions.size)
consumeEvent(event);
}
@ -509,12 +509,13 @@ class RecordActionTool implements RecorderTool {
private _actionInProgress(event: Event): boolean {
// If Playwright is performing action for us, bail.
const isKeyEvent = event instanceof KeyboardEvent;
if (this._performingAction?.name === 'press' && isKeyEvent && event.key === this._performingAction.key)
return true;
const isMouseOrPointerEvent = event instanceof MouseEvent || event instanceof PointerEvent;
if (isMouseOrPointerEvent && (this._performingAction?.name === 'click' || this._performingAction?.name === 'check' || this._performingAction?.name === 'uncheck'))
return true;
for (const action of this._performingActions) {
if (isKeyEvent && action.name === 'press' && event.key === action.key)
return true;
if (isMouseOrPointerEvent && (action.name === 'click' || action.name === 'check' || action.name === 'uncheck'))
return true;
}
// Consume event if action is not being executed.
consumeEvent(event);
@ -540,9 +541,9 @@ class RecordActionTool implements RecorderTool {
this._hoveredModel = null;
this._activeModel = null;
this._recorder.updateHighlight(null, false);
this._performingAction = action;
this._performingActions.add(action);
void this._recorder.performAction(action).then(() => {
this._performingAction = null;
this._performingActions.delete(action);
// If that was a keyboard action, it similarly requires new selectors for active model.
this._onFocus(false);

View File

@ -421,18 +421,32 @@ await page.GetByRole(AriaRole.Textbox).PressAsync("Shift+Enter");`);
<input name="two"></input>
`);
await page.click('input[name="one"]');
await recorder.waitForOutput('JavaScript', 'click');
await page.keyboard.type('foobar123');
await recorder.waitForOutput('JavaScript', 'foobar123');
const input1 = page.locator('input[name="one"]');
const input2 = page.locator('input[name="two"]');
await page.keyboard.press('Tab');
await recorder.waitForOutput('JavaScript', 'Tab');
await page.keyboard.type('barfoo321');
// I can't explain it atm, first character is being consumed for no apparent reason.
if (browserName === 'webkit' && codegenMode === 'trace-events')
await page.waitForTimeout(1000);
await recorder.waitForOutput('JavaScript', 'barfoo321');
{
await input1.click();
await recorder.waitForOutput('JavaScript', 'click');
await expect(input1).toBeFocused();
}
{
await page.keyboard.type('foobar123');
await recorder.waitForOutput('JavaScript', 'foobar123');
await expect(input1).toHaveValue('foobar123');
}
{
await page.keyboard.press('Tab');
await recorder.waitForOutput('JavaScript', 'Tab');
await expect(input2).toBeFocused();
}
{
await page.keyboard.type('barfoo321');
await recorder.waitForOutput('JavaScript', 'barfoo321');
await expect(input2).toHaveValue('barfoo321');
}
const text = recorder.sources().get('JavaScript')!.text;
expect(text).toContain(`

View File

@ -220,55 +220,30 @@ await page.GetByRole(AriaRole.Textbox).SetInputFilesAsync(new[] { });`);
page.waitForEvent('download'),
page.click('a')
]);
await Promise.all([
page.waitForEvent('download'),
page.click('a')
]);
const sources = await recorder.waitForOutput('JavaScript', 'download1Promise');
const sources = await recorder.waitForOutput('JavaScript', 'downloadPromise');
expect.soft(sources.get('JavaScript')!.text).toContain(`
const downloadPromise = page.waitForEvent('download');
await page.getByRole('link', { name: 'Download' }).click();
const download = await downloadPromise;`);
expect.soft(sources.get('JavaScript')!.text).toContain(`
const download1Promise = page.waitForEvent('download');
await page.getByRole('link', { name: 'Download' }).click();
const download1 = await download1Promise;`);
expect.soft(sources.get('Java')!.text).toContain(`
Download download = page.waitForDownload(() -> {
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("Download")).click();
});`);
expect.soft(sources.get('Java')!.text).toContain(`
Download download1 = page.waitForDownload(() -> {
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("Download")).click();
});`);
expect.soft(sources.get('Python')!.text).toContain(`
with page.expect_download() as download_info:
page.get_by_role("link", name="Download").click()
download = download_info.value`);
expect.soft(sources.get('Python')!.text).toContain(`
with page.expect_download() as download1_info:
page.get_by_role("link", name="Download").click()
download1 = download1_info.value`);
expect.soft(sources.get('Python Async')!.text).toContain(`
async with page.expect_download() as download_info:
await page.get_by_role("link", name="Download").click()
download = await download_info.value`);
expect.soft(sources.get('Python Async')!.text).toContain(`
async with page.expect_download() as download1_info:
await page.get_by_role("link", name="Download").click()
download1 = await download1_info.value`);
expect.soft(sources.get('C#')!.text).toContain(`
var download = await page.RunAndWaitForDownloadAsync(async () =>
{
await page.GetByRole(AriaRole.Link, new() { Name = "Download" }).ClickAsync();
});`);
expect.soft(sources.get('C#')!.text).toContain(`
var download1 = await page.RunAndWaitForDownloadAsync(async () =>
{
await page.GetByRole(AriaRole.Link, new() { Name = "Download" }).ClickAsync();
});`);

View File

@ -14,7 +14,10 @@
* limitations under the License.
*/
import type { TestServer } from 'tests/config/testserver';
import type { Recorder } from './inspectorTest';
import { test, expect } from './inspectorTest';
import type { Page } from '@playwright/test';
test.describe('cli codegen', () => {
test.skip(({ mode }) => mode !== 'default');
@ -90,7 +93,82 @@ await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).Nth(1).ClickAsy
expect(message.text()).toBe('click2');
});
test('should generate frame locators', async ({ openRecorder, server }) => {
test('should generate frame locators (1)', async ({ openRecorder, server }) => {
const { page, recorder } = await openRecorder();
const { frameHello1 } = await createFrameHierarchy(page, recorder, server);
const [sources] = await Promise.all([
recorder.waitForOutput('JavaScript', 'Hello1'),
frameHello1.click('text=Hello1'),
]);
expect.soft(sources.get('JavaScript')!.text).toContain(`
await page.locator('#frame1').contentFrame().getByText('Hello1').click();`);
expect.soft(sources.get('Java')!.text).toContain(`
page.locator("#frame1").contentFrame().getByText("Hello1").click();`);
expect.soft(sources.get('Python')!.text).toContain(`
page.locator("#frame1").content_frame.get_by_text("Hello1").click()`);
expect.soft(sources.get('Python Async')!.text).toContain(`
await page.locator("#frame1").content_frame.get_by_text("Hello1").click()`);
expect.soft(sources.get('C#')!.text).toContain(`
await page.Locator("#frame1").ContentFrame.GetByText("Hello1").ClickAsync();`);
});
test('should generate frame locators (2)', async ({ openRecorder, server }) => {
const { page, recorder } = await openRecorder();
const { frameHello2 } = await createFrameHierarchy(page, recorder, server);
const [sources] = await Promise.all([
recorder.waitForOutput('JavaScript', 'Hello2'),
frameHello2.click('text=Hello2'),
]);
expect.soft(sources.get('JavaScript')!.text).toContain(`
await page.locator('#frame1').contentFrame().locator('iframe').contentFrame().getByText('Hello2').click();`);
expect.soft(sources.get('Java')!.text).toContain(`
page.locator("#frame1").contentFrame().locator("iframe").contentFrame().getByText("Hello2").click();`);
expect.soft(sources.get('Python')!.text).toContain(`
page.locator("#frame1").content_frame.locator("iframe").content_frame.get_by_text("Hello2").click()`);
expect.soft(sources.get('Python Async')!.text).toContain(`
await page.locator("#frame1").content_frame.locator("iframe").content_frame.get_by_text("Hello2").click()`);
expect.soft(sources.get('C#')!.text).toContain(`
await page.Locator("#frame1").ContentFrame.Locator("iframe").ContentFrame.GetByText("Hello2").ClickAsync();`);
});
test('should generate frame locators (3)', async ({ openRecorder, server }) => {
const { page, recorder } = await openRecorder();
const { frameAnonymous } = await createFrameHierarchy(page, recorder, server);
const [sources] = await Promise.all([
recorder.waitForOutput('JavaScript', 'HelloNameAnonymous'),
frameAnonymous.click('text=HelloNameAnonymous'),
]);
expect.soft(sources.get('JavaScript')!.text).toContain(`
await page.locator('#frame1').contentFrame().locator('iframe').contentFrame().locator('iframe').nth(2).contentFrame().getByText('HelloNameAnonymous').click();`);
expect.soft(sources.get('Java')!.text).toContain(`
page.locator("#frame1").contentFrame().locator("iframe").contentFrame().locator("iframe").nth(2).contentFrame().getByText("HelloNameAnonymous").click();`);
expect.soft(sources.get('Python')!.text).toContain(`
page.locator("#frame1").content_frame.locator("iframe").content_frame.locator("iframe").nth(2).content_frame.get_by_text("HelloNameAnonymous").click()`);
expect.soft(sources.get('Python Async')!.text).toContain(`
await page.locator("#frame1").content_frame.locator("iframe").content_frame.locator("iframe").nth(2).content_frame.get_by_text("HelloNameAnonymous").click()`);
expect.soft(sources.get('C#')!.text).toContain(`
await page.Locator("#frame1").ContentFrame.Locator("iframe").ContentFrame.Locator("iframe").Nth(2).ContentFrame.GetByText("HelloNameAnonymous").ClickAsync();`);
});
test('should generate frame locators (4)', async ({ openRecorder, server }) => {
const { page, recorder } = await openRecorder();
/*
iframe
@ -99,8 +177,6 @@ await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).Nth(1).ClickAsy
div Hello2
iframe[name=one]
div HelloNameOne
iframe[name=two]
dev HelloNameTwo
iframe
dev HelloAnonymous
*/
@ -109,8 +185,6 @@ await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).Nth(1).ClickAsy
`, server.EMPTY_PAGE, 6);
const frameHello1 = page.mainFrame().childFrames()[0];
const frameHello2 = frameHello1.childFrames()[0];
const frameOne = page.frame({ name: 'one' })!;
await frameOne.setContent(`<div>HelloNameOne</div>`);
const frameTwo = page.frame({ name: 'two' })!;
await frameTwo.setContent(`<div>HelloNameTwo</div>`);
const frameAnonymous = frameHello2.childFrames().find(f => !f.name())!;
@ -157,27 +231,6 @@ await page.Locator("#frame1").ContentFrame.GetByText("Hello1").ClickAsync();`);
expect.soft(sources.get('C#')!.text).toContain(`
await page.Locator("#frame1").ContentFrame.Locator("iframe").ContentFrame.GetByText("Hello2").ClickAsync();`);
[sources] = await Promise.all([
recorder.waitForOutput('JavaScript', 'one'),
frameOne.click('text=HelloNameOne'),
]);
expect.soft(sources.get('JavaScript')!.text).toContain(`
await page.locator('#frame1').contentFrame().locator('iframe').contentFrame().locator('iframe[name="one"]').contentFrame().getByText('HelloNameOne').click();`);
expect.soft(sources.get('Java')!.text).toContain(`
page.locator("#frame1").contentFrame().locator("iframe").contentFrame().locator("iframe[name=\\"one\\"]").contentFrame().getByText("HelloNameOne").click();`);
expect.soft(sources.get('Python')!.text).toContain(`
page.locator("#frame1").content_frame.locator("iframe").content_frame.locator("iframe[name=\\"one\\"]").content_frame.get_by_text("HelloNameOne").click()`);
expect.soft(sources.get('Python Async')!.text).toContain(`
await page.locator("#frame1").content_frame.locator("iframe").content_frame.locator("iframe[name=\\"one\\"]").content_frame.get_by_text("HelloNameOne").click()`);
expect.soft(sources.get('C#')!.text).toContain(`
await page.Locator("#frame1").ContentFrame.Locator("iframe").ContentFrame.Locator("iframe[name=\\"one\\"]").ContentFrame.GetByText("HelloNameOne").ClickAsync();`);
[sources] = await Promise.all([
recorder.waitForOutput('JavaScript', 'HelloNameAnonymous'),
frameAnonymous.click('text=HelloNameAnonymous'),
@ -758,3 +811,31 @@ await page.GetByLabel("Coun\\"try").ClickAsync();`);
await expect(recorder.page.locator('x-pw-glass')).toBeVisible();
});
});
async function createFrameHierarchy(page: Page, recorder: Recorder, server: TestServer) {
/*
iframe
div Hello1
iframe
div Hello2
iframe[name=one]
div HelloNameOne
iframe
dev HelloAnonymous
*/
await recorder.setContentAndWait(`
<iframe id=frame1 srcdoc="<div>Hello1</div><iframe srcdoc='<div>Hello2</div><iframe name=one></iframe><iframe name=two></iframe><iframe></iframe>'>">
`, server.EMPTY_PAGE, 6);
const frameHello1 = page.mainFrame().childFrames()[0];
const frameHello2 = frameHello1.childFrames()[0];
const frameTwo = page.frame({ name: 'two' })!;
await frameTwo.setContent(`<div>HelloNameTwo</div>`);
const frameAnonymous = frameHello2.childFrames().find(f => !f.name())!;
await frameAnonymous.setContent(`<div>HelloNameAnonymous</div>`);
return {
frameHello1,
frameHello2,
frameTwo,
frameAnonymous,
};
}

View File

@ -97,7 +97,7 @@ export const test = contextTest.extend<CLITestArgs>({
},
});
class Recorder {
export class Recorder {
page: Page;
_highlightCallback: Function;
_highlightInstalled: boolean;