From df95b9ddb4b8a5a3e802730501ee839bcb926046 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Sat, 14 Dec 2019 19:13:22 -0800 Subject: [PATCH] feat(waitFor): allow actions to wait for selector; make visible requirement default (#254) --- docs/api.md | 12 +++---- src/dom.ts | 46 ++++++++++++------------- src/frames.ts | 69 +++++++++++++++++++++----------------- src/page.ts | 22 ++++++------ src/types.ts | 16 ++++++--- test/click.spec.js | 8 ++--- test/page.spec.js | 16 ++++----- test/queryselector.spec.js | 30 ++++++++--------- test/waittask.spec.js | 14 ++++---- 9 files changed, 125 insertions(+), 108 deletions(-) diff --git a/docs/api.md b/docs/api.md index 1bd0e798c8..6f35dba09a 100644 --- a/docs/api.md +++ b/docs/api.md @@ -3869,8 +3869,8 @@ const handle = await page.$('"foo"'); const handle = await divHandle.$('css=span'); ``` -#### selector.visible -- returns: <[boolean]> Optional visibility to check for. If `true`, only visible elements match. If `false`, only non-visible elements match. If `undefined`, all elements match. +#### selector.visibility +- returns: "visible"|"hidden"|"any"> Defaults to `visible`. Optional visibility to check for. If `visible` or `undefined`, only visible elements match. If `hidden`, only non-visible elements match. If `any`, all elements match. Note that elements are first queried by `selector`, and only after that are checked for visiblity. In particular, [page.$()](#pageselector) will not skip to the first visible element, but instead return `null` if the first matching element is not visible. @@ -3878,16 +3878,16 @@ Element is defined visible if it does not have `visibility: hidden` CSS property ```js // queries 'div', and only returns it when visible -const handle = await page.$({selector: 'css=div', visible: true}); +const handle = await page.$({selector: 'css=div', visibility: 'visible'}); // queries 'div', and only returns it when non-visible -const handle = await page.$({selector: 'css=div', visible: false}); +const handle = await page.$({selector: 'css=div', visibility: 'hidden'}); // queries 'div', and returns it no matter the visibility -const handle = await page.$({selector: 'css=div'}); +const handle = await page.$({selector: 'css=div', visibility: 'any'}); // returns all visible 'div' elements -const handles = await page.$$({selector: 'css=div', visible: true}); +const handles = await page.$$({selector: 'css=div', visibility: 'visible'}); ``` diff --git a/src/dom.ts b/src/dom.ts index 53951fe832..e6cc789ad0 100644 --- a/src/dom.ts +++ b/src/dom.ts @@ -14,7 +14,7 @@ import Injected from './injected/injected'; import { Page } from './page'; type ScopedSelector = types.Selector & { scope?: ElementHandle }; -type ResolvedSelector = { scope?: ElementHandle, selector: string, visible?: boolean, disposeScope?: boolean }; +type ResolvedSelector = { scope?: ElementHandle, selector: string, visibility: types.Visibility, disposeScope?: boolean }; export class FrameExecutionContext extends js.ExecutionContext { private readonly _frame: frames.Frame; @@ -56,24 +56,24 @@ export class FrameExecutionContext extends js.ExecutionContext { async _resolveSelector(selector: string | ScopedSelector): Promise { if (helper.isString(selector)) - return { selector: normalizeSelector(selector) }; + return { selector: normalizeSelector(selector), visibility: 'any' }; if (selector.scope && selector.scope.executionContext() !== this) { const scope = await this._adoptElementHandle(selector.scope); - return { scope, selector: normalizeSelector(selector.selector), disposeScope: true, visible: selector.visible }; + return { scope, selector: normalizeSelector(selector.selector), disposeScope: true, visibility: selector.visibility || 'any' }; } - return { scope: selector.scope, selector: normalizeSelector(selector.selector), visible: selector.visible }; + return { scope: selector.scope, selector: normalizeSelector(selector.selector), visibility: selector.visibility || 'any' }; } async _$(selector: string | ScopedSelector): Promise | null> { const resolved = await this._resolveSelector(selector); const handle = await this.evaluateHandle( - (injected: Injected, selector: string, scope?: Node, visible?: boolean) => { + (injected: Injected, selector: string, visibility: types.Visibility, scope?: Node) => { const element = injected.querySelector(selector, scope || document); - if (visible === undefined || !element) + if (visibility === 'any' || !element) return element; - return injected.isVisible(element) === visible ? element : undefined; + return injected.isVisible(element) === (visibility === 'visible') ? element : undefined; }, - await this._injected(), resolved.selector, resolved.scope, resolved.visible + await this._injected(), resolved.selector, resolved.visibility, resolved.scope ); if (resolved.disposeScope) await resolved.scope.dispose(); @@ -85,13 +85,13 @@ export class FrameExecutionContext extends js.ExecutionContext { async _$$(selector: string | ScopedSelector): Promise[]> { const resolved = await this._resolveSelector(selector); const arrayHandle = await this.evaluateHandle( - (injected: Injected, selector: string, scope?: Node, visible?: boolean) => { + (injected: Injected, selector: string, visibility: types.Visibility, scope?: Node) => { const elements = injected.querySelectorAll(selector, scope || document); - if (visible !== undefined) - return elements.filter(element => injected.isVisible(element) === visible); + if (visibility !== 'any') + return elements.filter(element => injected.isVisible(element) === (visibility === 'visible')); return elements; }, - await this._injected(), resolved.selector, resolved.scope, resolved.visible + await this._injected(), resolved.selector, resolved.visibility, resolved.scope ); if (resolved.disposeScope) await resolved.scope.dispose(); @@ -120,13 +120,13 @@ export class FrameExecutionContext extends js.ExecutionContext { _$$eval: types.$$Eval = async (selector, pageFunction, ...args) => { const resolved = await this._resolveSelector(selector); const arrayHandle = await this.evaluateHandle( - (injected: Injected, selector: string, scope?: Node, visible?: boolean) => { + (injected: Injected, selector: string, visibility: types.Visibility, scope?: Node) => { const elements = injected.querySelectorAll(selector, scope || document); - if (visible !== undefined) - return elements.filter(element => injected.isVisible(element) === visible); + if (visibility !== 'any') + return elements.filter(element => injected.isVisible(element) === (visibility === 'visible')); return elements; }, - await this._injected(), resolved.selector, resolved.scope, resolved.visible + await this._injected(), resolved.selector, resolved.visibility, resolved.scope ); const result = await arrayHandle.evaluate(pageFunction, ...args as any); await arrayHandle.dispose(); @@ -372,7 +372,7 @@ export class ElementHandle extends js.JSHandle { selector = types.clearSelector(selector); if (helper.isString(selector)) selector = { selector }; - return { scope: this, selector: selector.selector, visible: selector.visible }; + return { scope: this, selector: selector.selector, visibility: selector.visibility }; } $(selector: string | types.Selector): Promise { @@ -451,19 +451,19 @@ export function waitForFunctionTask(pageFunction: Function | string, options: ty export function waitForSelectorTask(selector: string | types.Selector, timeout: number): Task { return async (context: FrameExecutionContext) => { const resolved = await context._resolveSelector(selector); - return context.evaluateHandle((injected: Injected, selector: string, scope: Node | undefined, visible: boolean | undefined, timeout: number) => { - if (visible !== undefined) + return context.evaluateHandle((injected: Injected, selector: string, visibility: types.Visibility, timeout: number, scope?: Node) => { + if (visibility !== 'any') return injected.pollRaf(predicate, timeout); return injected.pollMutation(predicate, timeout); function predicate(): Element | boolean { const element = injected.querySelector(selector, scope || document); if (!element) - return visible === false; - if (visible === undefined) + return visibility === 'hidden'; + if (visibility === 'any') return element; - return injected.isVisible(element) === visible ? element : false; + return injected.isVisible(element) === (visibility === 'visible') ? element : false; } - }, await context._injected(), resolved.selector, resolved.scope, resolved.visible, timeout); + }, await context._injected(), resolved.selector, resolved.visibility, timeout, resolved.scope); }; } diff --git a/src/frames.ts b/src/frames.ts index e3a7815147..1d0a89eecd 100644 --- a/src/frames.ts +++ b/src/frames.ts @@ -48,6 +48,8 @@ export type GotoOptions = NavigateOptions & { export type LifecycleEvent = 'load' | 'domcontentloaded'; +export type WaitForOptions = types.TimeoutOptions & { waitFor?: boolean }; + export class Frame { _id: string; readonly _firedLifecycleEvents: Set; @@ -306,59 +308,47 @@ export class Frame { return result; } - async click(selector: string | types.Selector, options?: ClickOptions) { - const context = await this._utilityContext(); - const handle = await context._$(types.clearSelector(selector)); - assert(handle, 'No node found for selector: ' + types.selectorToString(selector)); + async click(selector: string | types.Selector, options?: WaitForOptions & ClickOptions) { + const handle = await this._optionallyWaitForInUtilityContext(selector, options); await handle.click(options); await handle.dispose(); } - async dblclick(selector: string | types.Selector, options?: MultiClickOptions) { - const context = await this._utilityContext(); - const handle = await context._$(types.clearSelector(selector)); - assert(handle, 'No node found for selector: ' + types.selectorToString(selector)); + async dblclick(selector: string | types.Selector, options?: WaitForOptions & MultiClickOptions) { + const handle = await this._optionallyWaitForInUtilityContext(selector, options); await handle.dblclick(options); await handle.dispose(); } - async tripleclick(selector: string | types.Selector, options?: MultiClickOptions) { - const context = await this._utilityContext(); - const handle = await context._$(types.clearSelector(selector)); - assert(handle, 'No node found for selector: ' + types.selectorToString(selector)); + async tripleclick(selector: string | types.Selector, options?: WaitForOptions & MultiClickOptions) { + const handle = await this._optionallyWaitForInUtilityContext(selector, options); await handle.tripleclick(options); await handle.dispose(); } - async fill(selector: string | types.Selector, value: string) { - const context = await this._utilityContext(); - const handle = await context._$(types.clearSelector(selector)); - assert(handle, 'No node found for selector: ' + types.selectorToString(selector)); + async fill(selector: string | types.Selector, value: string, options?: WaitForOptions) { + const handle = await this._optionallyWaitForInUtilityContext(selector, options); await handle.fill(value); await handle.dispose(); } - async focus(selector: string | types.Selector) { - const context = await this._utilityContext(); - const handle = await context._$(types.clearSelector(selector)); - assert(handle, 'No node found for selector: ' + types.selectorToString(selector)); + async focus(selector: string | types.Selector, options?: WaitForOptions) { + const handle = await this._optionallyWaitForInUtilityContext(selector, options); await handle.focus(); await handle.dispose(); } - async hover(selector: string | types.Selector, options?: PointerActionOptions) { - const context = await this._utilityContext(); - const handle = await context._$(types.clearSelector(selector)); - assert(handle, 'No node found for selector: ' + types.selectorToString(selector)); + async hover(selector: string | types.Selector, options?: WaitForOptions & PointerActionOptions) { + const handle = await this._optionallyWaitForInUtilityContext(selector, options); await handle.hover(options); await handle.dispose(); } - async select(selector: string | types.Selector, ...values: (string | dom.ElementHandle | SelectOption)[]): Promise { - const context = await this._utilityContext(); - const handle = await context._$(types.clearSelector(selector)); - assert(handle, 'No node found for selector: ' + types.selectorToString(selector)); + async select(selector: string | types.Selector, value: string | dom.ElementHandle | SelectOption | string[] | dom.ElementHandle[] | SelectOption[] | undefined, options?: WaitForOptions): Promise { + const handle = await this._optionallyWaitForInUtilityContext(selector, options); const toDispose: Promise[] = []; + const values = value === undefined ? [] : value instanceof Array ? value : [value]; + const context = await this._utilityContext(); const adoptedValues = await Promise.all(values.map(async value => { if (value instanceof dom.ElementHandle && value.executionContext() !== context) { const adopted = context._adoptElementHandle(value); @@ -373,7 +363,7 @@ export class Frame { return result; } - async type(selector: string | types.Selector, text: string, options: { delay: (number | undefined); } | undefined) { + async type(selector: string | types.Selector, text: string, options: WaitForOptions & { delay: (number | undefined); } | undefined) { const context = await this._utilityContext(); const handle = await context._$(types.clearSelector(selector)); assert(handle, 'No node found for selector: ' + types.selectorToString(selector)); @@ -391,7 +381,19 @@ export class Frame { return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout))); } - async waitForSelector(selector: string | types.Selector, options: types.TimeoutOptions = {}): Promise { + private async _optionallyWaitForInUtilityContext(selector: string | types.Selector,options: WaitForOptions): Promise { + let handle: dom.ElementHandle | null; + if (options && options.waitFor) { + handle = await this._waitForSelectorInUtilityContext(selector, options); + } else { + const context = await this._utilityContext(); + handle = await context._$(types.clearSelector(selector)); + } + assert(handle, 'No node found for selector: ' + types.selectorToString(selector)); + return handle; + } + + private async _waitForSelectorInUtilityContext(selector: string | types.Selector, options: types.TimeoutOptions = {}): Promise { const { timeout = this._page._timeoutSettings.timeout() } = options; const task = dom.waitForSelectorTask(types.clearSelector(selector), timeout); const handle = await this._scheduleRerunnableTask(task, 'utility', timeout, `selector "${types.selectorToString(selector)}"`); @@ -399,6 +401,13 @@ export class Frame { await handle.dispose(); return null; } + return handle.asElement(); + } + + async waitForSelector(selector: string | types.Selector, options: types.TimeoutOptions = {}): Promise { + const handle = await this._waitForSelectorInUtilityContext(selector, options); + if (!handle) + return null; const maincontext = await this._mainContext(); if (handle.executionContext() === maincontext) return handle.asElement(); diff --git a/src/page.ts b/src/page.ts index 53fd921fdf..0bf1a5a16d 100644 --- a/src/page.ts +++ b/src/page.ts @@ -447,35 +447,35 @@ export class Page extends EventEmitter { return this._closed; } - click(selector: string | types.Selector, options?: input.ClickOptions) { + click(selector: string | types.Selector, options?: frames.WaitForOptions & input.ClickOptions) { return this.mainFrame().click(selector, options); } - dblclick(selector: string | types.Selector, options?: input.MultiClickOptions) { + dblclick(selector: string | types.Selector, options?: frames.WaitForOptions & input.MultiClickOptions) { return this.mainFrame().dblclick(selector, options); } - tripleclick(selector: string | types.Selector, options?: input.MultiClickOptions) { + tripleclick(selector: string | types.Selector, options?: frames.WaitForOptions & input.MultiClickOptions) { return this.mainFrame().tripleclick(selector, options); } - fill(selector: string | types.Selector, value: string) { - return this.mainFrame().fill(selector, value); + fill(selector: string | types.Selector, value: string, options?: frames.WaitForOptions) { + return this.mainFrame().fill(selector, value, options); } - focus(selector: string | types.Selector) { - return this.mainFrame().focus(selector); + focus(selector: string | types.Selector, options?: frames.WaitForOptions) { + return this.mainFrame().focus(selector, options); } - hover(selector: string | types.Selector, options?: input.PointerActionOptions) { + hover(selector: string | types.Selector, options?: frames.WaitForOptions & input.PointerActionOptions) { return this.mainFrame().hover(selector, options); } - select(selector: string | types.Selector, ...values: (string | dom.ElementHandle | input.SelectOption)[]): Promise { - return this.mainFrame().select(selector, ...values); + select(selector: string | types.Selector, value: string | dom.ElementHandle | input.SelectOption | string[] | dom.ElementHandle[] | input.SelectOption[] | undefined, options?: frames.WaitForOptions): Promise { + return this.mainFrame().select(selector, value, options); } - type(selector: string | types.Selector, text: string, options: { delay: (number | undefined); } | undefined) { + type(selector: string | types.Selector, text: string, options: frames.WaitForOptions & { delay: (number | undefined); } | undefined) { return this.mainFrame().type(selector, text, options); } diff --git a/src/types.ts b/src/types.ts index fd7839c39a..d925140fd8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -25,8 +25,8 @@ export type Rect = Size & Point; export type Quad = [ Point, Point, Point, Point ]; export type TimeoutOptions = { timeout?: number }; - -export type Selector = { selector: string, visible?: boolean }; +export type Visibility = 'visible' | 'hidden' | 'any'; +export type Selector = { selector: string, visibility?: Visibility }; export type Polling = 'raf' | 'mutation' | number; export type WaitForFunctionOptions = TimeoutOptions & { polling?: Polling }; @@ -34,14 +34,22 @@ export type WaitForFunctionOptions = TimeoutOptions & { polling?: Polling }; export function selectorToString(selector: string | Selector): string { if (typeof selector === 'string') return selector; - return `${selector.visible ? '[visible] ' : selector.visible === false ? '[hidden] ' : ''}${selector.selector}`; + let label; + switch (selector.visibility) { + case 'visible': label = '[visible] '; break; + case 'hidden': label = '[hidden] '; break; + case 'any': + case undefined: + label = ''; break; + } + return `${label}${selector.selector}`; } // Ensures that we don't use accidental properties in selector, e.g. scope. export function clearSelector(selector: string | Selector): string | Selector { if (helper.isString(selector)) return selector; - return { selector: selector.selector, visible: selector.visible }; + return { selector: selector.selector, visibility: selector.visibility }; } export type ElementScreenshotOptions = { diff --git a/test/click.spec.js b/test/click.spec.js index 052c2bf76e..4e26fb0bc9 100644 --- a/test/click.spec.js +++ b/test/click.spec.js @@ -128,26 +128,26 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME it('should respect selector visibilty', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); - await page.click({selector: 'button', visible: true}); + await page.click({selector: 'button', visibility: 'visible'}); expect(await page.evaluate(() => result)).toBe('Clicked'); let error = null; await page.goto(server.PREFIX + '/input/button.html'); - await page.click({selector: 'button', visible: false}).catch(e => error = e); + await page.click({selector: 'button', visibility: 'hidden'}).catch(e => error = e); expect(error.message).toBe('No node found for selector: [hidden] button'); expect(await page.evaluate(() => result)).toBe('Was not clicked'); error = null; await page.goto(server.PREFIX + '/input/button.html'); await page.$eval('button', b => b.style.display = 'none'); - await page.click({selector: 'button', visible: true}).catch(e => error = e); + await page.click({selector: 'button', visibility: 'visible'}).catch(e => error = e); expect(error.message).toBe('No node found for selector: [visible] button'); expect(await page.evaluate(() => result)).toBe('Was not clicked'); error = null; await page.goto(server.PREFIX + '/input/button.html'); await page.$eval('button', b => b.style.display = 'none'); - await page.click({selector: 'button', visible: false}).catch(e => error = e); + await page.click({selector: 'button', visibility: 'hidden'}).catch(e => error = e); expect(error.message).toBe('Node is either not visible or not an HTMLElement'); expect(await page.evaluate(() => result)).toBe('Was not clicked'); }); diff --git a/test/page.spec.js b/test/page.spec.js index 3894d980e0..9477f5fbf8 100644 --- a/test/page.spec.js +++ b/test/page.spec.js @@ -936,14 +936,14 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF it('should select multiple options', async({page, server}) => { await page.goto(server.PREFIX + '/input/select.html'); await page.evaluate(() => makeMultiple()); - await page.select('select', 'blue', 'green', 'red'); + await page.select('select', ['blue', 'green', 'red']); expect(await page.evaluate(() => result.onInput)).toEqual(['blue', 'green', 'red']); expect(await page.evaluate(() => result.onChange)).toEqual(['blue', 'green', 'red']); }); it('should select multiple options with attributes', async({page, server}) => { await page.goto(server.PREFIX + '/input/select.html'); await page.evaluate(() => makeMultiple()); - await page.select('select', 'blue', { label: 'Green' }, { index: 4 }); + await page.select('select', ['blue', { label: 'Green' }, { index: 4 }]); expect(await page.evaluate(() => result.onInput)).toEqual(['blue', 'gray', 'green']); expect(await page.evaluate(() => result.onChange)).toEqual(['blue', 'gray', 'green']); }); @@ -972,7 +972,7 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF }); it('should return an array of one element when multiple is not set', async({page, server}) => { await page.goto(server.PREFIX + '/input/select.html'); - const result = await page.select('select','42','blue','black','magenta'); + const result = await page.select('select',['42','blue','black','magenta']); expect(result.length).toEqual(1); }); it('should return [] on no values',async({page, server}) => { @@ -983,13 +983,13 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF it('should deselect all options when passed no values for a multiple select',async({page, server}) => { await page.goto(server.PREFIX + '/input/select.html'); await page.evaluate(() => makeMultiple()); - await page.select('select','blue','black','magenta'); + await page.select('select', ['blue','black','magenta']); await page.select('select'); expect(await page.$eval('select', select => Array.from(select.options).every(option => !option.selected))).toEqual(true); }); it('should deselect all options when passed no values for a select without multiple',async({page, server}) => { await page.goto(server.PREFIX + '/input/select.html'); - await page.select('select','blue','black','magenta'); + await page.select('select', ['blue','black','magenta']); await page.select('select'); expect(await page.$eval('select', select => Array.from(select.options).every(option => !option.selected))).toEqual(true); }); @@ -1111,18 +1111,18 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF }); it('should respect selector visibilty', async({page, server}) => { await page.goto(server.PREFIX + '/input/textarea.html'); - await page.fill({selector: 'input', visible: true}, 'some value'); + await page.fill({selector: 'input', visibility: 'visible'}, 'some value'); expect(await page.evaluate(() => result)).toBe('some value'); let error = null; await page.goto(server.PREFIX + '/input/textarea.html'); - await page.fill({selector: 'input', visible: false}, 'some value').catch(e => error = e); + await page.fill({selector: 'input', visibility: 'hidden'}, 'some value').catch(e => error = e); expect(error.message).toBe('No node found for selector: [hidden] input'); error = null; await page.goto(server.PREFIX + '/input/textarea.html'); await page.$eval('input', i => i.style.display = 'none'); - await page.fill({selector: 'input', visible: true}, 'some value').catch(e => error = e); + await page.fill({selector: 'input', visibility: 'visible'}, 'some value').catch(e => error = e); expect(error.message).toBe('No node found for selector: [visible] input'); }); it('should throw on disabled and readonly elements', async({page, server}) => { diff --git a/test/queryselector.spec.js b/test/queryselector.spec.js index 4ea20d48ca..0fb9c88594 100644 --- a/test/queryselector.spec.js +++ b/test/queryselector.spec.js @@ -44,15 +44,15 @@ module.exports.addTests = function({testRunner, expect, product, FFOX, CHROME, W it('should respect visibility', async({page, server}) => { let error = null; await page.setContent(''); - await page.$eval({selector: 'css=section', visible: true}, e => e.id).catch(e => error = e); + await page.$eval({selector: 'css=section', visibility: 'visible'}, e => e.id).catch(e => error = e); expect(error.message).toContain('failed to find element matching selector "[visible] css=section"'); - expect(await page.$eval({selector: 'css=section', visible: false}, e => e.id)).toBe('testAttribute'); + expect(await page.$eval({selector: 'css=section', visibility: 'hidden'}, e => e.id)).toBe('testAttribute'); error = null; await page.setContent('
43543
'); - await page.$eval({selector: 'css=section', visible: false}, e => e.id).catch(e => error = e); + await page.$eval({selector: 'css=section', visibility: 'hidden'}, e => e.id).catch(e => error = e); expect(error.message).toContain('failed to find element matching selector "[hidden] css=section"'); - expect(await page.$eval({selector: 'css=section', visible: true}, e => e.id)).toBe('testAttribute'); + expect(await page.$eval({selector: 'css=section', visibility: 'visible'}, e => e.id)).toBe('testAttribute'); }); it('should accept arguments', async({page, server}) => { await page.setContent('
hello
'); @@ -135,8 +135,8 @@ module.exports.addTests = function({testRunner, expect, product, FFOX, CHROME, W }); it('should respect visibility', async({page, server}) => { await page.setContent('
1
2
3
'); - expect(await page.$$eval({selector: 'css=section', visible: true}, x => x.length)).toBe(1); - expect(await page.$$eval({selector: 'css=section', visible: false}, x => x.length)).toBe(2); + expect(await page.$$eval({selector: 'css=section', visibility: 'visible'}, x => x.length)).toBe(1); + expect(await page.$$eval({selector: 'css=section', visibility: 'hidden'}, x => x.length)).toBe(2); expect(await page.$$eval({selector: 'css=section'}, x => x.length)).toBe(3); }); }); @@ -183,13 +183,13 @@ module.exports.addTests = function({testRunner, expect, product, FFOX, CHROME, W }); it('should respect visibility', async({page, server}) => { await page.setContent('
43543
'); - expect(await page.$({selector: 'css=section', visible: true})).toBeTruthy(); - expect(await page.$({selector: 'css=section', visible: false})).not.toBeTruthy(); + expect(await page.$({selector: 'css=section', visibility: 'visible'})).toBeTruthy(); + expect(await page.$({selector: 'css=section', visibility: 'hidden'})).not.toBeTruthy(); expect(await page.$({selector: 'css=section'})).toBeTruthy(); await page.setContent(''); - expect(await page.$({selector: 'css=section', visible: true})).not.toBeTruthy(); - expect(await page.$({selector: 'css=section', visible: false})).toBeTruthy(); + expect(await page.$({selector: 'css=section', visibility: 'visible'})).not.toBeTruthy(); + expect(await page.$({selector: 'css=section', visibility: 'hidden'})).toBeTruthy(); expect(await page.$({selector: 'css=section'})).toBeTruthy(); }); }); @@ -209,8 +209,8 @@ module.exports.addTests = function({testRunner, expect, product, FFOX, CHROME, W }); it('should respect visibility', async({page, server}) => { await page.setContent('
1
2
3
'); - expect((await page.$$({selector: 'css=section', visible: true})).length).toBe(1); - expect((await page.$$({selector: 'css=section', visible: false})).length).toBe(2); + expect((await page.$$({selector: 'css=section', visibility: 'visible'})).length).toBe(1); + expect((await page.$$({selector: 'css=section', visibility: 'hidden'})).length).toBe(2); expect((await page.$$({selector: 'css=section'})).length).toBe(3); }); }); @@ -267,14 +267,14 @@ module.exports.addTests = function({testRunner, expect, product, FFOX, CHROME, W await page.setContent('
'); const second = await page.$('html .second'); - let inner = await second.$({selector: '.inner', visible: true}); + let inner = await second.$({selector: '.inner', visibility: 'visible'}); expect(inner).not.toBeTruthy(); - inner = await second.$({selector: '.inner', visible: false}); + inner = await second.$({selector: '.inner', visibility: 'hidden'}); expect(await inner.evaluate(e => e.textContent)).toBe('A'); await inner.evaluate(e => e.style.display = 'block'); - inner = await second.$({selector: '.inner', visible: true}); + inner = await second.$({selector: '.inner', visibility: 'visible'}); expect(await inner.evaluate(e => e.textContent)).toBe('A'); }); }); diff --git a/test/waittask.spec.js b/test/waittask.spec.js index 44c44f850c..4ed1b01f61 100644 --- a/test/waittask.spec.js +++ b/test/waittask.spec.js @@ -290,7 +290,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO }); it('should wait for visible', async({page, server}) => { let divFound = false; - const waitForSelector = page.waitForSelector({selector: 'div', visible: true}).then(() => divFound = true); + const waitForSelector = page.waitForSelector({selector: 'div', visibility: 'visible'}).then(() => divFound = true); await page.setContent(`
1
`); expect(divFound).toBe(false); await page.evaluate(() => document.querySelector('div').style.removeProperty('display')); @@ -301,7 +301,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO }); it('should wait for visible recursively', async({page, server}) => { let divVisible = false; - const waitForSelector = page.waitForSelector({selector: 'div#inner', visible: true}).then(() => divVisible = true); + const waitForSelector = page.waitForSelector({selector: 'div#inner', visibility: 'visible'}).then(() => divVisible = true); await page.setContent(`
hi
`); expect(divVisible).toBe(false); await page.evaluate(() => document.querySelector('div').style.removeProperty('display')); @@ -313,7 +313,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO it('hidden should wait for visibility: hidden', async({page, server}) => { let divHidden = false; await page.setContent(`
`); - const waitForSelector = page.waitForSelector({selector: 'div', visible: false}).then(() => divHidden = true); + const waitForSelector = page.waitForSelector({selector: 'div', visibility: 'hidden'}).then(() => divHidden = true); await page.waitForSelector('div'); // do a round trip expect(divHidden).toBe(false); await page.evaluate(() => document.querySelector('div').style.setProperty('visibility', 'hidden')); @@ -323,7 +323,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO it('hidden should wait for display: none', async({page, server}) => { let divHidden = false; await page.setContent(`
`); - const waitForSelector = page.waitForSelector({selector: 'div', visible: false}).then(() => divHidden = true); + const waitForSelector = page.waitForSelector({selector: 'div', visibility: 'hidden'}).then(() => divHidden = true); await page.waitForSelector('div'); // do a round trip expect(divHidden).toBe(false); await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none')); @@ -333,7 +333,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO it('hidden should wait for removal', async({page, server}) => { await page.setContent(`
`); let divRemoved = false; - const waitForSelector = page.waitForSelector({selector: 'div', visible: false}).then(() => divRemoved = true); + const waitForSelector = page.waitForSelector({selector: 'div', visibility: 'hidden'}).then(() => divRemoved = true); await page.waitForSelector('div'); // do a round trip expect(divRemoved).toBe(false); await page.evaluate(() => document.querySelector('div').remove()); @@ -341,7 +341,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO expect(divRemoved).toBe(true); }); it('should return null if waiting to hide non-existing element', async({page, server}) => { - const handle = await page.waitForSelector({selector: 'non-existing', visible: false }); + const handle = await page.waitForSelector({selector: 'non-existing', visibility: 'hidden' }); expect(handle).toBe(null); }); it('should respect timeout', async({page, server}) => { @@ -354,7 +354,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO it('should have an error message specifically for awaiting an element to be hidden', async({page, server}) => { await page.setContent(`
`); let error = null; - await page.waitForSelector({selector: 'div', visible: false}, {timeout: 10}).catch(e => error = e); + await page.waitForSelector({selector: 'div', visibility: 'hidden'}, {timeout: 10}).catch(e => error = e); expect(error).toBeTruthy(); expect(error.message).toContain('waiting for selector "[hidden] div" failed: timeout'); });