Skip to content

feat(Playwright): visibleLocator config option - #5707

Merged
DavertMik merged 2 commits into
4.xfrom
feature/playwright-visible-locator
Sep 10, 2026
Merged

feat(Playwright): visibleLocator config option#5707
DavertMik merged 2 commits into
4.xfrom
feature/playwright-visible-locator

Conversation

@DavertMik

@DavertMik DavertMik commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Adds a visibleLocator option to the Playwright helper, built on locator.visible() from Playwright 1.63.

When enabled, locators match only visible elements, so actions no longer pick a hidden duplicate and then time out on actionability.

helpers: { Playwright: { visibleLocator: true } }
I.click('Save')                                       // visible only
I.click('Save', stepOpts({ visibleLocator: false }))  // matches hidden too

How it resolves

_beforeStep decides once per step and writes the answer to the store:

_beforeStep(step) {
  store.visibleLocator = step.opts?.visibleLocator ?? (this.options.visibleLocator && !domPresenceSteps.includes(step.helperMethod))
}

Step opts beat helper config, the same way exact, strictMode and elementIndex already work. visibleLocator is documented in the StepOptions typedef alongside them.

Because the answer is already in the store, withVisibleLocator is a plain function — nothing is threaded through signatures and nothing needs binding:

function withVisibleLocator(locator) {
  if (!store.visibleLocator) return locator
  ...
  return locator.visible()
}

It wraps the five places a Locator is built: findElements (plain and {pw:} branches), handleRoleLocator, findByRole, and the two getByRole('button'|'link') fallbacks in findClickable. findClickable, findCheckable and findFields all bottom out there, so click, check, fill, select, attach, focus and moveCursorTo are covered. Every existing call site is untouched.

findByPlaywrightLocator now returns a Locator instead of an array, with .all() applied by findElements. That keeps the {pw:} path uniform with the others and fixes its getByTestId branch, which previously returned a bare Locator to callers expecting an array.

A named error is thrown if the installed Playwright predates 1.63, since playwright is a peer install.

Note: this makes Playwright the first helper in lib/helper/ to define _beforeStep, which adds one recorder task per step. It is a synchronous store write, negligible against a browser round-trip, but worth stating.

Behaviour changes when enabled

  • A locator matching only hidden elements fails as "element not found" instead of timing out on actionability.
  • strict: true ignores hidden duplicates, so cases that previously failed with MultipleElementsFound now pass.
  • Elements hidden by CSS — such as a custom checkbox built on a visually hidden native input — are no longer found.

Not covered

  • seeElementInDOM, dontSeeElementInDOM and seeNumberOfElements are excluded in _beforeStep, since they assert DOM presence regardless of visibility.
  • dragAndDrop passes selectors to Playwright directly rather than building a Locator, so it is excluded and named as such in the config docs.
  • grabTextFrom's CSS path calls page.textContent(selector) and never goes through _locate, so it does not filter. Its role-locator path does. That divergence predates this PR.
  • The flag is resolved per step, so it applies to steps only. Calling a helper method directly on the instance outside a step leaves the last resolved value in place — the same contract as every other step option.

Testing

playwright devDependency bumped to ^1.63.0.

9 tests in test/helper/Playwright_test.js. They live in the Playwright helper spec rather than the shared webapi.js one, since the option exists only in this helper. They drive _beforeStep directly, so the exclusion and override logic is exercised rather than bypassed: config on/off, per-step on/off, all-hidden → not found, DOM assertions unaffected, {pw:} locators, a combobox case covering findByRole, and fields/checkboxes. webapi.js is unchanged.

Verified locally: unit suite 815 passed / 0 failed, and the 9 new tests pass. The browser helper and acceptance suites are left to CI.

Note on CI: the Obscura Helper Tests job fails on #waitInUrl and the #checkOption - ARIA roles cases. Those fail identically on base 4.x at cfc9545 and are unrelated to this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01D6RydiYkagn6C8Pts2Leou

@DavertMik
DavertMik force-pushed the feature/playwright-visible-locator branch 2 times, most recently from 36ac791 to 5793d13 Compare September 9, 2026 23:27
Appends Playwright's locator.visible() (1.63+) to locators, so actions
match only visible elements. Resolved per step: stepOpts({ visibleLocator })
overrides the helper config, following exact/strictMode/elementIndex.

seeElementInDOM, dontSeeElementInDOM and seeNumberOfElements opt out by
setting the step option, since they assert DOM presence regardless of
visibility.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D6RydiYkagn6C8Pts2Leou
@DavertMik
DavertMik force-pushed the feature/playwright-visible-locator branch from 5793d13 to 28398d4 Compare September 9, 2026 23:34
@DavertMik
DavertMik merged commit 894b664 into 4.x Sep 10, 2026
13 of 14 checks passed
@DavertMik
DavertMik deleted the feature/playwright-visible-locator branch September 10, 2026 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant