From 9e75b95153acfb06d7317f8ca441fd407a6c64c9 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Tue, 30 May 2023 17:45:48 +0200 Subject: [PATCH] fix: display testId as regex in trace-viewer (#23361) Fixes https://github.com/microsoft/playwright/issues/23298 --- .../src/utils/isomorphic/locatorGenerators.ts | 32 ++++++++++++++++--- .../src/utils/isomorphic/locatorParser.ts | 6 ++-- tests/library/locator-generator.spec.ts | 7 ++++ tests/library/trace-viewer.spec.ts | 4 +++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts b/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts index 50d0595f3d..1f8c944e4c 100644 --- a/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts +++ b/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts @@ -286,7 +286,7 @@ export class JavaScriptLocatorFactory implements LocatorFactory { case 'or': return `or(${body})`; case 'test-id': - return `getByTestId(${this.quote(body as string)})`; + return `getByTestId(${this.toTestIdValue(body)})`; case 'text': return this.toCallWithExact('getByText', body, !!options.exact); case 'alt': @@ -318,6 +318,12 @@ export class JavaScriptLocatorFactory implements LocatorFactory { return this.quote(body); } + private toTestIdValue(value: string | RegExp): string { + if (isRegExp(value)) + return String(value); + return this.quote(value); + } + private quote(text: string) { return escapeWithQuotes(text, '\''); } @@ -370,7 +376,7 @@ export class PythonLocatorFactory implements LocatorFactory { case 'or': return `or_(${body})`; case 'test-id': - return `get_by_test_id(${this.quote(body as string)})`; + return `get_by_test_id(${this.toTestIdValue(body)})`; case 'text': return this.toCallWithExact('get_by_text', body, !!options.exact); case 'alt': @@ -409,6 +415,12 @@ export class PythonLocatorFactory implements LocatorFactory { return `${this.quote(body)}`; } + private toTestIdValue(value: string | RegExp) { + if (isRegExp(value)) + return this.regexToString(value); + return this.quote(value); + } + private quote(text: string) { return escapeWithQuotes(text, '\"'); } @@ -463,7 +475,7 @@ export class JavaLocatorFactory implements LocatorFactory { case 'or': return `or(${body})`; case 'test-id': - return `getByTestId(${this.quote(body as string)})`; + return `getByTestId(${this.toTestIdValue(body)})`; case 'text': return this.toCallWithExact(clazz, 'getByText', body, !!options.exact); case 'alt': @@ -502,6 +514,12 @@ export class JavaLocatorFactory implements LocatorFactory { return this.quote(body); } + private toTestIdValue(value: string | RegExp) { + if (isRegExp(value)) + return this.regexToString(value); + return this.quote(value); + } + private quote(text: string) { return escapeWithQuotes(text, '\"'); } @@ -550,7 +568,7 @@ export class CSharpLocatorFactory implements LocatorFactory { case 'or': return `Or(${body})`; case 'test-id': - return `GetByTestId(${this.quote(body as string)})`; + return `GetByTestId(${this.toTestIdValue(body)})`; case 'text': return this.toCallWithExact('GetByText', body, !!options.exact); case 'alt': @@ -589,6 +607,12 @@ export class CSharpLocatorFactory implements LocatorFactory { return `HasText = ${this.quote(body)}`; } + private toTestIdValue(value: string | RegExp) { + if (isRegExp(value)) + return this.regexToString(value); + return this.quote(value); + } + private toHasNotText(body: string | RegExp) { if (isRegExp(body)) return `HasNotTextRegex = ${this.regexToString(body)}`; diff --git a/packages/playwright-core/src/utils/isomorphic/locatorParser.ts b/packages/playwright-core/src/utils/isomorphic/locatorParser.ts index ca8ad5dc24..767ceb048d 100644 --- a/packages/playwright-core/src/utils/isomorphic/locatorParser.ts +++ b/packages/playwright-core/src/utils/isomorphic/locatorParser.ts @@ -160,7 +160,7 @@ function transform(template: string, params: TemplateParams, testIdAttributeName .replace(/getbyrole\(([^)]+)\)/g, 'internal:role=$1') .replace(/getbytext\(([^)]+)\)/g, 'internal:text=$1') .replace(/getbylabel\(([^)]+)\)/g, 'internal:label=$1') - .replace(/getbytestid\(([^)]+)\)/g, `internal:testid=[${testIdAttributeName}=$1s]`) + .replace(/getbytestid\(([^)]+)\)/g, `internal:testid=[${testIdAttributeName}=$1]`) .replace(/getby(placeholder|alt|title)(?:text)?\(([^)]+)\)/g, 'internal:attr=[$1=$2]') .replace(/first(\(\))?/g, 'nth=0') .replace(/last(\(\))?/g, 'nth=-1') @@ -200,7 +200,9 @@ function transform(template: string, params: TemplateParams, testIdAttributeName const param = params[+ordinal - 1]; if (t.startsWith('internal:has=') || t.startsWith('internal:has-not=')) return param.text; - if (t.startsWith('internal:attr') || t.startsWith('internal:testid') || t.startsWith('internal:role')) + if (t.startsWith('internal:testid')) + return escapeForAttributeSelector(param.text, true); + if (t.startsWith('internal:attr') || t.startsWith('internal:role')) return escapeForAttributeSelector(param.text, suffix === 's'); return escapeForTextSelector(param.text, suffix === 's'); }); diff --git a/tests/library/locator-generator.spec.ts b/tests/library/locator-generator.spec.ts index de880e18b3..403f02765d 100644 --- a/tests/library/locator-generator.spec.ts +++ b/tests/library/locator-generator.spec.ts @@ -61,6 +61,13 @@ it('reverse engineer locators', async ({ page }) => { csharp: 'GetByTestId("He\\"llo")' }); + expect.soft(generate(page.getByTestId(/He"llo/))).toEqual({ + javascript: 'getByTestId(/He"llo/)', + python: 'get_by_test_id(re.compile(r"He\\"llo"))', + java: 'getByTestId(Pattern.compile("He\\"llo"))', + csharp: 'GetByTestId(new Regex("He\\"llo"))' + }); + expect.soft(generate(page.getByText('Hello', { exact: true }))).toEqual({ csharp: 'GetByText("Hello", new() { Exact = true })', java: 'getByText("Hello", new Page.GetByTextOptions().setExact(true))', diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts index 9d5580459c..46635c63df 100644 --- a/tests/library/trace-viewer.spec.ts +++ b/tests/library/trace-viewer.spec.ts @@ -34,6 +34,8 @@ test.beforeAll(async function recordTrace({ browser, browserName, browserType, s await page.goto('data:text/html,Hello world'); await page.setContent(''); await expect(page.locator('button')).toHaveText('Click'); + await expect(page.getByTestId('amazing-btn')).toBeHidden(); + await expect(page.getByTestId(/amazing-btn-regex/)).toBeHidden(); await page.evaluate(({ a }) => { console.log('Info'); console.warn('Warning'); @@ -102,6 +104,8 @@ test('should open simple trace viewer', async ({ showTraceViewer }) => { /page.gotodata:text\/html,Hello world<\/html>/, /page.setContent/, /expect.toHaveTextlocator\('button'\)/, + /expect.toBeHiddengetByTestId\('amazing-btn'\)/, + /expect.toBeHiddengetByTestId\(\/amazing-btn-regex\/\)/, /page.evaluate/, /page.evaluate/, /locator.clickgetByText\('Click'\)/,