feat(waitFor): allow actions to wait for selector; make visible requirement default (#254)

This commit is contained in:
Pavel Feldman 2019-12-14 19:13:22 -08:00 committed by Yury Semikhatsky
parent e375749e5e
commit df95b9ddb4
9 changed files with 125 additions and 108 deletions

View File

@ -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'});
```

View File

@ -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<ResolvedSelector> {
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<ElementHandle<Element> | 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<ElementHandle<Element>[]> {
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<string | ScopedSelector> = 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<T extends Node = Node> extends js.JSHandle<T> {
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<ElementHandle | null> {
@ -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);
};
}

View File

@ -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<LifecycleEvent>;
@ -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<string[]> {
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<string[]> {
const handle = await this._optionallyWaitForInUtilityContext(selector, options);
const toDispose: Promise<dom.ElementHandle>[] = [];
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<dom.ElementHandle | null> {
private async _optionallyWaitForInUtilityContext(selector: string | types.Selector,options: WaitForOptions): Promise<dom.ElementHandle | null> {
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<dom.ElementHandle | null> {
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<dom.ElementHandle | null> {
const handle = await this._waitForSelectorInUtilityContext(selector, options);
if (!handle)
return null;
const maincontext = await this._mainContext();
if (handle.executionContext() === maincontext)
return handle.asElement();

View File

@ -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<string[]> {
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<string[]> {
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);
}

View File

@ -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 = {

View File

@ -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');
});

View File

@ -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}) => {

View File

@ -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('<section id="testAttribute" style="display: none">43543</section>');
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('<section id="testAttribute">43543</section>');
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('<section>hello</section>');
@ -135,8 +135,8 @@ module.exports.addTests = function({testRunner, expect, product, FFOX, CHROME, W
});
it('should respect visibility', async({page, server}) => {
await page.setContent('<section style="display: none">1</section><section style="display: none">2</section><section>3</section>');
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('<section id="testAttribute">43543</section>');
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('<section id="testAttribute" style="display: none">43543</section>');
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('<section style="display: none">1</section><section style="display: none">2</section><section>3</section>');
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('<html><body><div class="second"><div class="inner" style="display:none">A</div></div></body></html>');
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');
});
});

View File

@ -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(`<div style='display: none; visibility: hidden;'>1</div>`);
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(`<div style='display: none; visibility: hidden;'><div id="inner">hi</div></div>`);
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(`<div style='display: block;'></div>`);
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(`<div style='display: block;'></div>`);
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(`<div></div>`);
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(`<div></div>`);
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');
});