Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/helpers/Playwright.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Type: [object][6]
* `ignoreHTTPSErrors` **[boolean][27]?** Allows access to untrustworthy pages, e.g. to a page with an expired certificate. Default value is `false`
* `bypassCSP` **[boolean][27]?** bypass Content Security Policy or CSP
* `highlightElement` **[boolean][27]?** highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
* `visibleLocator` **[boolean][27]?** append [`visible()`][49] to locators, so only visible elements are matched. Requires Playwright 1.63 or newer. Switch it off for a single step with `stepOpts({ visibleLocator: false })`. Not applied to `dragAndDrop`, which passes selectors to Playwright directly, nor to `seeElementInDOM`, `dontSeeElementInDOM` and `seeNumberOfElements`, which check the DOM regardless of visibility. When enabled, a locator matching only hidden elements fails as "element not found" instead of timing out on actionability, `strict` mode ignores hidden duplicates, and elements hidden by CSS (like a custom checkbox built on a visually hidden `input`) are no longer found.
* `visibleLocator` **[boolean][27]?** append [`visible()`][49] to locators, so only visible elements are matched. Requires Playwright 1.63 or newer. Switch it off for a single step with `stepOpts({ visibleLocator: false })`. Not applied to `dragAndDrop`, which passes selectors to Playwright directly, nor to steps that must reach hidden elements: `grab*` methods, `scrollTo`, `seeElementInDOM`, `dontSeeElementInDOM` and `seeNumberOfElements`. When enabled, a locator matching only hidden elements fails as "element not found" instead of timing out on actionability, `strict` mode ignores hidden duplicates, and elements hidden by CSS (like a custom checkbox built on a visually hidden `input`) are no longer found.
* `recordHar` **[object][6]?** record HAR and will be saved to `output/har`. See more of [HAR options][3].
* `testIdAttribute` **[string][9]?** locate elements based on the testIdAttribute. See more of [locate by test id][50].
* `storageState` **([string][9] | [object][6])?** Playwright storage state (path to JSON file or object)
Expand Down
7 changes: 4 additions & 3 deletions lib/helper/Playwright.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ let defaultSelectorEnginesInitialized = false
const popupStore = new Popup()
const consoleLogStore = new Console()
const availableBrowsers = ['chromium', 'webkit', 'firefox', 'electron']
const domPresenceSteps = ['seeElementInDOM', 'dontSeeElementInDOM', 'seeNumberOfElements']
const visibilityAgnosticSteps = ['seeElementInDOM', 'dontSeeElementInDOM', 'seeNumberOfElements', 'scrollTo']
const checkableRoles = ['checkbox', 'radio', 'switch']

import { setRestartStrategy, restartsSession, restartsContext, restartsBrowser } from './extras/PlaywrightRestartOpts.js'
Expand Down Expand Up @@ -103,7 +103,7 @@ const pathSeparator = path.sep
* @prop {boolean} [ignoreHTTPSErrors] - Allows access to untrustworthy pages, e.g. to a page with an expired certificate. Default value is `false`
* @prop {boolean} [bypassCSP] - bypass Content Security Policy or CSP
* @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
* @prop {boolean} [visibleLocator=false] - append [`visible()`](https://playwright.dev/docs/api/class-locator#locator-visible) to locators, so only visible elements are matched. Requires Playwright 1.63 or newer. Switch it off for a single step with `stepOpts({ visibleLocator: false })`. Not applied to `dragAndDrop`, which passes selectors to Playwright directly, nor to `seeElementInDOM`, `dontSeeElementInDOM` and `seeNumberOfElements`, which check the DOM regardless of visibility. When enabled, a locator matching only hidden elements fails as "element not found" instead of timing out on actionability, `strict` mode ignores hidden duplicates, and elements hidden by CSS (like a custom checkbox built on a visually hidden `input`) are no longer found.
* @prop {boolean} [visibleLocator=false] - append [`visible()`](https://playwright.dev/docs/api/class-locator#locator-visible) to locators, so only visible elements are matched. Requires Playwright 1.63 or newer. Switch it off for a single step with `stepOpts({ visibleLocator: false })`. Not applied to `dragAndDrop`, which passes selectors to Playwright directly, nor to steps that must reach hidden elements: `grab*` methods, `scrollTo`, `seeElementInDOM`, `dontSeeElementInDOM` and `seeNumberOfElements`. When enabled, a locator matching only hidden elements fails as "element not found" instead of timing out on actionability, `strict` mode ignores hidden duplicates, and elements hidden by CSS (like a custom checkbox built on a visually hidden `input`) are no longer found.
* @prop {object} [recordHar] - record HAR and will be saved to `output/har`. See more of [HAR options](https://playwright.dev/docs/api/class-browser#browser-new-context-option-record-har).
* @prop {string} [testIdAttribute=data-testid] - locate elements based on the testIdAttribute. See more of [locate by test id](https://playwright.dev/docs/locators#locate-by-test-id).
* @prop {string|object} [storageState] - Playwright storage state (path to JSON file or object)
Expand Down Expand Up @@ -559,7 +559,8 @@ class Playwright extends Helper {
}

_beforeStep(step) {
store.visibleLocator = step.opts?.visibleLocator ?? (this.options.visibleLocator && !domPresenceSteps.includes(step.helperMethod))
const reachesHidden = step.helperMethod?.startsWith('grab') || visibilityAgnosticSteps.includes(step.helperMethod)
store.visibleLocator = step.opts?.visibleLocator ?? (this.options.visibleLocator && !reachesHidden)
}

async _before(test) {
Expand Down
2 changes: 2 additions & 0 deletions test/data/app/view/form/scroll.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
<button type="submit" name="button4" value="fourth">A Submit Button</button>
</form>
</div>
<div id="reveal_on_scroll" style="visibility: hidden">Revealed on scroll</div>
<a id="scroll_anchor"></a>
<script>

</script>
Expand Down
30 changes: 30 additions & 0 deletions test/helper/Playwright_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,36 @@ describe('Playwright', function () {
await I.dontSeeElementInDOM({ css: 'button[data-missing]' })
})

it('should scroll to elements revealed by scrolling', async () => {
await I.amOnPage('/form/scroll')
await I.resizeWindow(500, 700)
I.options.visibleLocator = true
step('scrollTo')

await I.scrollTo('#reveal_on_scroll')
const { y } = await I.grabPageScrollPosition()
assert.notEqual(y, 0)

await I.scrollPageToTop()
await I.scrollTo('#scroll_anchor')
const { y: anchorY } = await I.grabPageScrollPosition()
assert.notEqual(anchorY, 0)
})

it('should grab from hidden elements', async () => {
I.options.visibleLocator = true

await I.amOnPage('/form/hidden')
step('grabValueFrom')
expect(await I.grabValueFrom('#action')).to.equal('kill_people')
step('grabAttributeFrom')
expect(await I.grabAttributeFrom('#action', 'name')).to.equal('action')

await I.amOnPage('/invisible_elements')
step('grabHTMLFrom')
expect(await I.grabHTMLFrom('button[style]')).to.equal('Hello World')
})

it('should apply to playwright locators', async () => {
await I.amOnPage('/invisible_elements')
I.options.visibleLocator = true
Expand Down
Loading