From c27ad3529904a36e5b524f014a99a8e5cde04697 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Mon, 30 Jan 2023 13:27:41 -0800 Subject: [PATCH] fix(locators): properly escape slash inside attributes (#20510) Fixes #20471. --- .../src/utils/isomorphic/stringUtils.ts | 2 +- tests/page/selectors-get-by.spec.ts | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/playwright-core/src/utils/isomorphic/stringUtils.ts b/packages/playwright-core/src/utils/isomorphic/stringUtils.ts index 91fb7b82b2..07d1544853 100644 --- a/packages/playwright-core/src/utils/isomorphic/stringUtils.ts +++ b/packages/playwright-core/src/utils/isomorphic/stringUtils.ts @@ -78,5 +78,5 @@ export function escapeForAttributeSelector(value: string, exact: boolean): strin // cssEscape(value).replace(/\\ /g, ' ') // However, our attribute selectors do not conform to CSS parsing spec, // so we escape them differently. - return `"${value.replace(/["]/g, '\\"')}"${exact ? 's' : 'i'}`; + return `"${value.replace(/\\/g, '\\\\').replace(/["]/g, '\\"')}"${exact ? 's' : 'i'}`; } diff --git a/tests/page/selectors-get-by.spec.ts b/tests/page/selectors-get-by.spec.ts index 4ff4d3ed37..d8ec170f55 100644 --- a/tests/page/selectors-get-by.spec.ts +++ b/tests/page/selectors-get-by.spec.ts @@ -192,6 +192,13 @@ world`); await expect(page.getByPlaceholder('hello my\nworld')).toHaveAttribute('id', 'control'); await expect(page.getByAltText('hello my\nworld')).toHaveAttribute('id', 'control'); await expect(page.getByTitle('hello my\nworld')).toHaveAttribute('id', 'control'); + + await page.setContent(`
Text here
`); + await expect.soft(page.getByTitle('my title', { exact: true })).toHaveCount(1, { timeout: 500 }); + await expect.soft(page.getByTitle('my t\itle', { exact: true })).toHaveCount(1, { timeout: 500 }); + await expect.soft(page.getByTitle('my t\\itle', { exact: true })).toHaveCount(0, { timeout: 500 }); + await expect.soft(page.getByTitle('my t\\\itle', { exact: true })).toHaveCount(0, { timeout: 500 }); + await expect.soft(page.getByTitle('my t\\\\itle', { exact: true })).toHaveCount(0, { timeout: 500 }); }); it('getByRole escaping', async ({ page }) => { @@ -225,4 +232,17 @@ it('getByRole escaping', async ({ page }) => { expect.soft(await page.getByRole('link', { name: ' he \n llo 56 ', exact: true }).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([ `he llo 56`, ]); + + expect.soft(await page.getByRole('button', { name: 'Click me', exact: true }).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([ + ``, + ]); + expect.soft(await page.getByRole('button', { name: 'Click \me', exact: true }).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([ + ``, + ]); + expect.soft(await page.getByRole('button', { name: 'Click \\me', exact: true }).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([ + ]); + expect.soft(await page.getByRole('button', { name: 'Click \\\me', exact: true }).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([ + ]); + expect.soft(await page.getByRole('button', { name: 'Click \\\\me', exact: true }).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([ + ]); });