Skip to content

[Bug]: getByText/getByLabel with a global or sticky regex (/x/g, /x/y) match only some of the elements #42813

Closed

Description

@Abnoz01

Version

1.64.0-next (main @ 07f1a61); long-standing

Steps to reproduce

import { test, expect } from '@playwright/test';

test('regex with g flag', async ({ page }) => {
  await page.setContent(`
    <div>foo</div><div>foo</div><div>foo</div>
    <label>foo<input></label><label>foo<input></label><label>foo<input></label>
  `);
  console.log(await page.getByText(/foo/).count());   // 6
  console.log(await page.getByText(/foo/g).count());  // 4
  console.log(await page.getByLabel(/foo/g).count()); // 2
  console.log(await page.getByText(/foo/y).count());  // 4
});

Expected behavior

The g and y flags have no meaning for a locator match, so /foo/g should match the same elements as /foo/. getByRole('button', { name: /foo/g }) and filter({ hasText: /foo/g }) already behave that way.

Actual behavior

Every other matching element is skipped, depending on traversal order:

6
4
2
4

Additional context

createTextMatcher in packages/injected/src/injectedScript.ts builds one RegExp and reuses re.test(elementText.full) for every element. RegExp.prototype.test is stateful when the g or y flag is set: lastIndex advances after a hit and only resets after a miss, so the next element is tested from a non-zero offset. escapeRegexForSelector passes user flags through verbatim and the selector parser accepts [dgimsuvy], so the flag reaches the engine.

Role-name matching uses String.prototype.match and has-text builds a fresh RegExp per element, which is why those are unaffected and the inconsistency is confusing. Copying a /pattern/g regex from String.replace code into getByText is an easy habit; the result is a silently wrong count or a hidden strict-mode violation.

I intend to work on this and will send a PR.

Environment

- Operating System: macOS (Darwin 25.6.0)
- Node.js: 24.8.0
- Browser: Chromium (bundled r1246)
- Playwright: main @ 07f1a6154
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions