diff --git a/packages/playwright-core/src/utils/isomorphic/stringUtils.ts b/packages/playwright-core/src/utils/isomorphic/stringUtils.ts index 761eb9c6a5b6d..27b65b400352c 100644 --- a/packages/playwright-core/src/utils/isomorphic/stringUtils.ts +++ b/packages/playwright-core/src/utils/isomorphic/stringUtils.ts @@ -74,6 +74,12 @@ export function normalizeEscapedRegexQuotes(source: string) { } function escapeRegexForSelector(re: RegExp): string { + // Unicode mode does not allow "identity character escapes", so we do not escape and + // hope that it does not contain quotes and/or >> signs. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Character_escape + // TODO: rework RE usages in internal selectors away from literal representation to json, e.g. {source,flags}. + if (re.unicode || (re as any).unicodeSets) + return String(re); // Even number of backslashes followed by the quote -> insert a backslash. return String(re).replace(/(^|[^\\])(\\\\)*(["'`])/g, '$1$2\\$3').replace(/>>/g, '\\>\\>'); } diff --git a/tests/page/locator-query.spec.ts b/tests/page/locator-query.spec.ts index d2725df553263..141e1b427bb65 100644 --- a/tests/page/locator-query.spec.ts +++ b/tests/page/locator-query.spec.ts @@ -115,6 +115,10 @@ it('should filter by regex with a single quote', async ({ page }) => { await expect.soft(page.getByRole('button', { name: /let\\'s let\\\'s/i }).locator('span')).toHaveText('hello'); await expect.soft(page.locator('button', { hasText: /let\\\'s let\\'s/i }).locator('span')).toHaveText('hello'); await expect.soft(page.getByRole('button', { name: /let\\\'s let\\'s/i }).locator('span')).toHaveText('hello'); + + await page.setContent(``); + await expect.soft(page.locator('button', { hasText: /let's/iu })).toHaveText(`let's hello`); + await expect.soft(page.getByRole('button', { name: /let's/iu })).toHaveText(`let's hello`); }); it('should filter by regex and regexp flags', async ({ page }) => {