fix(inspector): support custom test id attribute (#18996)
Fixes #18959.
This commit is contained in:
parent
4f72a895e9
commit
43a6bf4d45
|
|
@ -114,14 +114,16 @@ export class DebugController extends SdkObject {
|
||||||
const { context } = await browser.newContextForReuse({}, internalMetadata);
|
const { context } = await browser.newContextForReuse({}, internalMetadata);
|
||||||
await context.newPage(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.
|
// Toggle the mode.
|
||||||
for (const recorder of await this._allRecorders()) {
|
for (const recorder of await this._allRecorders()) {
|
||||||
recorder.hideHighlightedSelecor();
|
recorder.hideHighlightedSelecor();
|
||||||
if (params.mode === 'recording') {
|
if (params.mode === 'recording')
|
||||||
recorder.setOutput(this._codegenId, params.file);
|
recorder.setOutput(this._codegenId, params.file);
|
||||||
if (params.testIdAttributeName)
|
|
||||||
recorder.setTestIdAttributeName(params.testIdAttributeName);
|
|
||||||
}
|
|
||||||
recorder.setMode(params.mode);
|
recorder.setMode(params.mode);
|
||||||
}
|
}
|
||||||
this.setAutoCloseEnabled(true);
|
this.setAutoCloseEnabled(true);
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
|
||||||
}
|
}
|
||||||
|
|
||||||
async newContextForReuse(params: channels.BrowserNewContextForReuseParams, metadata: CallMetadata): Promise<channels.BrowserNewContextForReuseResult> {
|
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> {
|
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> {
|
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> {
|
async close(): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ export class Recorder implements InstrumentationListener {
|
||||||
actionPoint,
|
actionPoint,
|
||||||
actionSelector,
|
actionSelector,
|
||||||
language: this._currentLanguage,
|
language: this._currentLanguage,
|
||||||
testIdAttributeName: this._contextRecorder.testIdAttributeName(),
|
testIdAttributeName: this._context.selectors().testIdAttributeName(),
|
||||||
};
|
};
|
||||||
return uiState;
|
return uiState;
|
||||||
});
|
});
|
||||||
|
|
@ -224,7 +224,7 @@ export class Recorder implements InstrumentationListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
setHighlightedSelector(language: Language, selector: string) {
|
setHighlightedSelector(language: Language, selector: string) {
|
||||||
this._highlightedSelector = locatorOrSelectorAsSelector(language, selector, this._contextRecorder.testIdAttributeName());
|
this._highlightedSelector = locatorOrSelectorAsSelector(language, selector, this._context.selectors().testIdAttributeName());
|
||||||
this._refreshOverlay();
|
this._refreshOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,10 +233,6 @@ export class Recorder implements InstrumentationListener {
|
||||||
this._refreshOverlay();
|
this._refreshOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
setTestIdAttributeName(testIdAttributeName: string) {
|
|
||||||
this._contextRecorder.setTestIdAttributeName(testIdAttributeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
setOutput(codegenId: string, outputFile: string | undefined) {
|
setOutput(codegenId: string, outputFile: string | undefined) {
|
||||||
this._contextRecorder.setOutput(codegenId, outputFile);
|
this._contextRecorder.setOutput(codegenId, outputFile);
|
||||||
}
|
}
|
||||||
|
|
@ -397,14 +393,6 @@ class ContextRecorder extends EventEmitter {
|
||||||
this._generator = generator;
|
this._generator = generator;
|
||||||
}
|
}
|
||||||
|
|
||||||
testIdAttributeName() {
|
|
||||||
return this._testIdAttributeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTestIdAttributeName(testIdAttributeName: string) {
|
|
||||||
this._testIdAttributeName = testIdAttributeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
setOutput(codegenId: string, outputFile?: string) {
|
setOutput(codegenId: string, outputFile?: string) {
|
||||||
const languages = new Set([
|
const languages = new Set([
|
||||||
new JavaLanguageGenerator(),
|
new JavaLanguageGenerator(),
|
||||||
|
|
|
||||||
|
|
@ -185,30 +185,35 @@ test('test', async ({ page }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should record custom data-testid', async ({ backend, connectedBrowser }) => {
|
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 = [];
|
const events = [];
|
||||||
backend.on('sourceChanged', event => events.push(event));
|
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' });
|
await backend.setMode({ mode: 'recording', testIdAttributeName: 'data-custom-id' });
|
||||||
|
|
||||||
const context = await connectedBrowser._newContextForReuse();
|
// 3. Record a click action.
|
||||||
const [page] = context.pages();
|
|
||||||
|
|
||||||
await page.setContent(`<div data-custom-id='one'>One</div>`);
|
|
||||||
await page.locator('div').click();
|
await page.locator('div').click();
|
||||||
|
|
||||||
|
// 4. Expect "getByTestId" locator.
|
||||||
await expect.poll(() => events[events.length - 1]).toEqual({
|
await expect.poll(() => events[events.length - 1]).toEqual({
|
||||||
header: `import { test, expect } from '@playwright/test';
|
header: `import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
test('test', async ({ page }) => {`,
|
test('test', async ({ page }) => {`,
|
||||||
footer: `});`,
|
footer: `});`,
|
||||||
actions: [
|
actions: [
|
||||||
` await page.goto('about:blank');`,
|
|
||||||
` await page.getByTestId('one').click();`,
|
` await page.getByTestId('one').click();`,
|
||||||
],
|
],
|
||||||
text: `import { test, expect } from '@playwright/test';
|
text: `import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
test('test', async ({ page }) => {
|
test('test', async ({ page }) => {
|
||||||
await page.goto('about:blank');
|
|
||||||
await page.getByTestId('one').click();
|
await page.getByTestId('one').click();
|
||||||
});`
|
});`
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -413,6 +413,35 @@ it.describe('pause', () => {
|
||||||
'keyup',
|
'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[]> {
|
async function sanitizeLog(recorderPage: Page): Promise<string[]> {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue