fix(inspector): support custom test id attribute (#18996)

Fixes #18959.
This commit is contained in:
Dmitry Gozman 2022-11-29 11:43:47 -08:00 committed by GitHub
parent 4f72a895e9
commit 43a6bf4d45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 50 additions and 26 deletions

View file

@ -114,14 +114,16 @@ export class DebugController extends SdkObject {
const { context } = await browser.newContextForReuse({}, internalMetadata);
await context.newPage(internalMetadata);
}
// Update test id attribute.
if (params.testIdAttributeName) {
for (const page of this._playwright.allPages())
page.context().selectors().setTestIdAttributeName(params.testIdAttributeName);
}
// Toggle the mode.
for (const recorder of await this._allRecorders()) {
recorder.hideHighlightedSelecor();
if (params.mode === 'recording') {
if (params.mode === 'recording')
recorder.setOutput(this._codegenId, params.file);
if (params.testIdAttributeName)
recorder.setTestIdAttributeName(params.testIdAttributeName);
}
recorder.setMode(params.mode);
}
this.setAutoCloseEnabled(true);

View file

@ -48,7 +48,7 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
}
async newContextForReuse(params: channels.BrowserNewContextForReuseParams, metadata: CallMetadata): Promise<channels.BrowserNewContextForReuseResult> {
return newContextForReuse(this._object, this, params, null, metadata);
return await newContextForReuse(this._object, this, params, null, metadata);
}
async close(): Promise<void> {
@ -105,7 +105,7 @@ export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.Bro
}
async newContextForReuse(params: channels.BrowserNewContextForReuseParams, metadata: CallMetadata): Promise<channels.BrowserNewContextForReuseResult> {
return newContextForReuse(this._object, this as any as BrowserDispatcher, params, this.selectors, metadata);
return await newContextForReuse(this._object, this as any as BrowserDispatcher, params, this.selectors, metadata);
}
async close(): Promise<void> {

View file

@ -172,7 +172,7 @@ export class Recorder implements InstrumentationListener {
actionPoint,
actionSelector,
language: this._currentLanguage,
testIdAttributeName: this._contextRecorder.testIdAttributeName(),
testIdAttributeName: this._context.selectors().testIdAttributeName(),
};
return uiState;
});
@ -224,7 +224,7 @@ export class Recorder implements InstrumentationListener {
}
setHighlightedSelector(language: Language, selector: string) {
this._highlightedSelector = locatorOrSelectorAsSelector(language, selector, this._contextRecorder.testIdAttributeName());
this._highlightedSelector = locatorOrSelectorAsSelector(language, selector, this._context.selectors().testIdAttributeName());
this._refreshOverlay();
}
@ -233,10 +233,6 @@ export class Recorder implements InstrumentationListener {
this._refreshOverlay();
}
setTestIdAttributeName(testIdAttributeName: string) {
this._contextRecorder.setTestIdAttributeName(testIdAttributeName);
}
setOutput(codegenId: string, outputFile: string | undefined) {
this._contextRecorder.setOutput(codegenId, outputFile);
}
@ -397,14 +393,6 @@ class ContextRecorder extends EventEmitter {
this._generator = generator;
}
testIdAttributeName() {
return this._testIdAttributeName;
}
setTestIdAttributeName(testIdAttributeName: string) {
this._testIdAttributeName = testIdAttributeName;
}
setOutput(codegenId: string, outputFile?: string) {
const languages = new Set([
new JavaLanguageGenerator(),

View file

@ -185,30 +185,35 @@ test('test', async ({ page }) => {
});
test('should record custom data-testid', async ({ backend, connectedBrowser }) => {
// This test emulates "record at cursor" functionality
// with custom test id attribute in the config.
const events = [];
backend.on('sourceChanged', event => events.push(event));
// 1. "Show browser" (or "run test").
const context = await connectedBrowser._newContextForReuse();
const page = await context.newPage();
await page.setContent(`<div data-custom-id='one'>One</div>`);
// 2. "Record at cursor".
await backend.setMode({ mode: 'recording', testIdAttributeName: 'data-custom-id' });
const context = await connectedBrowser._newContextForReuse();
const [page] = context.pages();
await page.setContent(`<div data-custom-id='one'>One</div>`);
// 3. Record a click action.
await page.locator('div').click();
// 4. Expect "getByTestId" locator.
await expect.poll(() => events[events.length - 1]).toEqual({
header: `import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {`,
footer: `});`,
actions: [
` await page.goto('about:blank');`,
` await page.getByTestId('one').click();`,
],
text: `import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
await page.goto('about:blank');
await page.getByTestId('one').click();
});`
});

View file

@ -413,6 +413,35 @@ it.describe('pause', () => {
'keyup',
]);
});
it('should highlight locators with custom testId', async ({ page, playwright, recorderPageGetter }) => {
await page.setContent('<div id=target1>click me</div><div data-custom-id=foo id=target2>and me</div>');
const scriptPromise = (async () => {
await page.pause();
await page.getByText('click me').click();
playwright.selectors.setTestIdAttribute('data-custom-id');
await page.getByTestId('foo').click();
})();
const recorderPage = await recorderPageGetter();
await recorderPage.click('[title="Step over (F10)"]');
const div1Box1 = roundBox(await page.locator('x-pw-highlight').boundingBox());
const div1Box2 = roundBox(await page.locator('#target1').boundingBox());
expect(div1Box1).toEqual(div1Box2);
await recorderPage.click('[title="Step over (F10)"]');
let div2Box1: Box;
await expect.poll(async () => {
div2Box1 = await page.locator('x-pw-highlight').boundingBox();
return div2Box1;
}).toBeTruthy();
div2Box1 = roundBox(div2Box1);
const div2Box2 = roundBox(await page.locator('#target2').boundingBox());
expect(div2Box1).toEqual(div2Box2);
await recorderPage.click('[title="Resume (F8)"]');
await scriptPromise;
});
});
async function sanitizeLog(recorderPage: Page): Promise<string[]> {