api(waitForSelector): make "state: visible" default, includes rename to state (#2091)
This commit is contained in:
parent
1f0217986e
commit
bcce48362a
12
docs/api.md
12
docs/api.md
|
|
@ -1811,11 +1811,11 @@ return finalResponse.ok();
|
||||||
#### page.waitForSelector(selector[, options])
|
#### page.waitForSelector(selector[, options])
|
||||||
- `selector` <[string]> A selector of an element to wait for
|
- `selector` <[string]> A selector of an element to wait for
|
||||||
- `options` <[Object]>
|
- `options` <[Object]>
|
||||||
- `waitFor` <"attached"|"detached"|"visible"|"hidden"> Wait for element to become visible (`visible`), hidden (`hidden`), present in dom (`attached`) or not present in dom (`detached`). Defaults to `attached`.
|
- `state` <"attached"|"detached"|"visible"|"hidden"> Wait for element to become visible (`visible`), hidden (`hidden`), present in dom (`attached`) or not present in dom (`detached`). Defaults to `visible`.
|
||||||
- `timeout` <[number]> Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
|
- `timeout` <[number]> Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
|
||||||
- returns: <[Promise]<?[ElementHandle]>> Promise which resolves when element specified by selector satisfies `waitFor` option. Resolves to `null` if waiting for `hidden` or `detached`.
|
- returns: <[Promise]<?[ElementHandle]>> Promise which resolves when element specified by selector satisfies `state` option. Resolves to `null` if waiting for `hidden` or `detached`.
|
||||||
|
|
||||||
Wait for the `selector` to satisfy `waitFor` option (either appear/disappear from dom, or become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw.
|
Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw.
|
||||||
|
|
||||||
Element is considered `visible` when it has non-empty bounding box and no `visibility:hidden`. Note that element without any content or with `display:none` has an empty bounding box and is not considered visible. Element is considired `hidden` when it is not `visible` as defined above.
|
Element is considered `visible` when it has non-empty bounding box and no `visibility:hidden`. Note that element without any content or with `display:none` has an empty bounding box and is not considered visible. Element is considired `hidden` when it is not `visible` as defined above.
|
||||||
|
|
||||||
|
|
@ -2435,11 +2435,11 @@ const [response] = await Promise.all([
|
||||||
#### frame.waitForSelector(selector[, options])
|
#### frame.waitForSelector(selector[, options])
|
||||||
- `selector` <[string]> A selector of an element to wait for
|
- `selector` <[string]> A selector of an element to wait for
|
||||||
- `options` <[Object]>
|
- `options` <[Object]>
|
||||||
- `waitFor` <"attached"|"detached"|"visible"|"hidden"> Wait for element to become visible (`visible`), hidden (`hidden`), present in dom (`attached`) or not present in dom (`detached`). Defaults to `attached`.
|
- `state` <"attached"|"detached"|"visible"|"hidden"> Wait for element to become visible (`visible`), hidden (`hidden`), present in dom (`attached`) or not present in dom (`detached`). Defaults to `visible`.
|
||||||
- `timeout` <[number]> Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
|
- `timeout` <[number]> Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
|
||||||
- returns: <[Promise]<?[ElementHandle]>> Promise which resolves when element specified by selector satisfies `waitFor` option. Resolves to `null` if waiting for `hidden` or `detached`.
|
- returns: <[Promise]<?[ElementHandle]>> Promise which resolves when element specified by selector satisfies `state` option. Resolves to `null` if waiting for `hidden` or `detached`.
|
||||||
|
|
||||||
Wait for the `selector` to satisfy `waitFor` option (either appear/disappear from dom, or become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw.
|
Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw.
|
||||||
|
|
||||||
Element is considered `visible` when it has non-empty bounding box and no `visibility:hidden`. Note that element without any content or with `display:none` has an empty bounding box and is not considered visible. Element is considired `hidden` when it is not `visible` as defined above.
|
Element is considered `visible` when it has non-empty bounding box and no `visibility:hidden`. Note that element without any content or with `display:none` has an empty bounding box and is not considered visible. Element is considired `hidden` when it is not `visible` as defined above.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -204,18 +204,18 @@ You can explicitly wait for an element to appear in the DOM or to become visible
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Wait for #search to appear in the DOM.
|
// Wait for #search to appear in the DOM.
|
||||||
await page.waitForSelector('#search', { waitFor: 'attached' });
|
await page.waitForSelector('#search', { state: 'attached' });
|
||||||
// Wait for #promo to become visible, for example with `visibility:visible`.
|
// Wait for #promo to become visible, for example with `visibility:visible`.
|
||||||
await page.waitForSelector('#promo', { waitFor: 'visible' });
|
await page.waitForSelector('#promo');
|
||||||
```
|
```
|
||||||
|
|
||||||
... or to become hidden or detached
|
... or to become hidden or detached
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Wait for #details to become hidden, for example with `display:none`.
|
// Wait for #details to become hidden, for example with `display:none`.
|
||||||
await page.waitForSelector('#details', { waitFor: 'hidden' });
|
await page.waitForSelector('#details', { state: 'hidden' });
|
||||||
// Wait for #promo to be removed from the DOM.
|
// Wait for #promo to be removed from the DOM.
|
||||||
await page.waitForSelector('#promo', { waitFor: 'detached' });
|
await page.waitForSelector('#promo', { state: 'detached' });
|
||||||
```
|
```
|
||||||
|
|
||||||
#### API reference
|
#### API reference
|
||||||
|
|
|
||||||
|
|
@ -434,14 +434,16 @@ export class Frame {
|
||||||
|
|
||||||
async waitForSelector(selector: string, options?: types.WaitForElementOptions): Promise<dom.ElementHandle<Element> | null> {
|
async waitForSelector(selector: string, options?: types.WaitForElementOptions): Promise<dom.ElementHandle<Element> | null> {
|
||||||
if (options && (options as any).visibility)
|
if (options && (options as any).visibility)
|
||||||
throw new Error('options.visibility is not supported, did you mean options.waitFor?');
|
throw new Error('options.visibility is not supported, did you mean options.state?');
|
||||||
const { waitFor = 'attached' } = (options || {});
|
if (options && (options as any).waitFor && (options as any).waitFor !== 'visible')
|
||||||
if (!['attached', 'detached', 'visible', 'hidden'].includes(waitFor))
|
throw new Error('options.waitFor is not supported, did you mean options.state?');
|
||||||
throw new Error(`Unsupported waitFor option "${waitFor}"`);
|
const { state = 'visible' } = (options || {});
|
||||||
|
if (!['attached', 'detached', 'visible', 'hidden'].includes(state))
|
||||||
|
throw new Error(`Unsupported waitFor option "${state}"`);
|
||||||
|
|
||||||
const deadline = this._page._timeoutSettings.computeDeadline(options);
|
const deadline = this._page._timeoutSettings.computeDeadline(options);
|
||||||
const { world, task } = selectors._waitForSelectorTask(selector, waitFor, deadline);
|
const { world, task } = selectors._waitForSelectorTask(selector, state, deadline);
|
||||||
const result = await this._scheduleRerunnableTask(task, world, deadline, `selector "${selectorToString(selector, waitFor)}"`);
|
const result = await this._scheduleRerunnableTask(task, world, deadline, `selector "${selectorToString(selector, state)}"`);
|
||||||
if (!result.asElement()) {
|
if (!result.asElement()) {
|
||||||
result.dispose();
|
result.dispose();
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -936,9 +938,9 @@ class RerunnableTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectorToString(selector: string, waitFor: 'attached' | 'detached' | 'visible' | 'hidden'): string {
|
function selectorToString(selector: string, state: 'attached' | 'detached' | 'visible' | 'hidden'): string {
|
||||||
let label;
|
let label;
|
||||||
switch (waitFor) {
|
switch (state) {
|
||||||
case 'visible': label = '[visible] '; break;
|
case 'visible': label = '[visible] '; break;
|
||||||
case 'hidden': label = '[hidden] '; break;
|
case 'hidden': label = '[hidden] '; break;
|
||||||
case 'attached': label = ''; break;
|
case 'attached': label = ''; break;
|
||||||
|
|
|
||||||
|
|
@ -142,12 +142,12 @@ export class Selectors {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_waitForSelectorTask(selector: string, waitFor: 'attached' | 'detached' | 'visible' | 'hidden', deadline: number): { world: 'main' | 'utility', task: (context: dom.FrameExecutionContext) => Promise<js.JSHandle> } {
|
_waitForSelectorTask(selector: string, state: 'attached' | 'detached' | 'visible' | 'hidden', deadline: number): { world: 'main' | 'utility', task: (context: dom.FrameExecutionContext) => Promise<js.JSHandle> } {
|
||||||
const parsed = this._parseSelector(selector);
|
const parsed = this._parseSelector(selector);
|
||||||
const task = async (context: dom.FrameExecutionContext) => context.evaluateHandleInternal(({ evaluator, parsed, waitFor, timeout }) => {
|
const task = async (context: dom.FrameExecutionContext) => context.evaluateHandleInternal(({ evaluator, parsed, state, timeout }) => {
|
||||||
return evaluator.injected.poll('raf', timeout, () => {
|
return evaluator.injected.poll('raf', timeout, () => {
|
||||||
const element = evaluator.querySelector(parsed, document);
|
const element = evaluator.querySelector(parsed, document);
|
||||||
switch (waitFor) {
|
switch (state) {
|
||||||
case 'attached':
|
case 'attached':
|
||||||
return element || false;
|
return element || false;
|
||||||
case 'detached':
|
case 'detached':
|
||||||
|
|
@ -158,7 +158,7 @@ export class Selectors {
|
||||||
return !element || !evaluator.injected.isVisible(element);
|
return !element || !evaluator.injected.isVisible(element);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, { evaluator: await this._prepareEvaluator(context), parsed, waitFor, timeout: helper.timeUntilDeadline(deadline) });
|
}, { evaluator: await this._prepareEvaluator(context), parsed, state, timeout: helper.timeUntilDeadline(deadline) });
|
||||||
return { world: this._needsMainContext(parsed) ? 'main' : 'utility', task };
|
return { world: this._needsMainContext(parsed) ? 'main' : 'utility', task };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ export type Quad = [ Point, Point, Point, Point ];
|
||||||
|
|
||||||
export type TimeoutOptions = { timeout?: number };
|
export type TimeoutOptions = { timeout?: number };
|
||||||
|
|
||||||
export type WaitForElementOptions = TimeoutOptions & { waitFor?: 'attached' | 'detached' | 'visible' | 'hidden' };
|
export type WaitForElementOptions = TimeoutOptions & { state?: 'attached' | 'detached' | 'visible' | 'hidden' };
|
||||||
|
|
||||||
export type Polling = 'raf' | number;
|
export type Polling = 'raf' | number;
|
||||||
export type WaitForFunctionOptions = TimeoutOptions & { polling?: Polling };
|
export type WaitForFunctionOptions = TimeoutOptions & { polling?: Polling };
|
||||||
|
|
|
||||||
|
|
@ -171,10 +171,10 @@ describe('Browser.disconnect', function() {
|
||||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const page = await remote.newPage();
|
const page = await remote.newPage();
|
||||||
const watchdog = page.waitForSelector('div', { timeout: 60000 }).catch(e => e);
|
const watchdog = page.waitForSelector('div', { state: 'attached', timeout: 60000 }).catch(e => e);
|
||||||
|
|
||||||
// Make sure the previous waitForSelector has time to make it to the browser before we disconnect.
|
// Make sure the previous waitForSelector has time to make it to the browser before we disconnect.
|
||||||
await page.waitForSelector('body');
|
await page.waitForSelector('body', { state: 'attached' });
|
||||||
|
|
||||||
await remote.close();
|
await remote.close();
|
||||||
const error = await watchdog;
|
const error = await watchdog;
|
||||||
|
|
|
||||||
|
|
@ -162,12 +162,22 @@ describe('Frame.waitForFunction', function() {
|
||||||
|
|
||||||
describe('Frame.waitForSelector', function() {
|
describe('Frame.waitForSelector', function() {
|
||||||
const addElement = tag => document.body.appendChild(document.createElement(tag));
|
const addElement = tag => document.body.appendChild(document.createElement(tag));
|
||||||
|
it('should throw on waitFor', async({page, server}) => {
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
let error;
|
||||||
|
await page.waitForSelector('*', { waitFor: 'attached' }).catch(e => error = e);
|
||||||
|
expect(error.message).toBe('options.waitFor is not supported, did you mean options.state?');
|
||||||
|
});
|
||||||
|
it('should tolerate waitFor=visible', async({page, server}) => {
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.waitForSelector('*', { waitFor: 'visible' }).catch(e => error = e);
|
||||||
|
});
|
||||||
it('should immediately resolve promise if node exists', async({page, server}) => {
|
it('should immediately resolve promise if node exists', async({page, server}) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
const frame = page.mainFrame();
|
const frame = page.mainFrame();
|
||||||
await frame.waitForSelector('*');
|
await frame.waitForSelector('*');
|
||||||
await frame.evaluate(addElement, 'div');
|
await frame.evaluate(addElement, 'div');
|
||||||
await frame.waitForSelector('div');
|
await frame.waitForSelector('div', { state: 'attached'});
|
||||||
});
|
});
|
||||||
it('should work with removed MutationObserver', async({page, server}) => {
|
it('should work with removed MutationObserver', async({page, server}) => {
|
||||||
await page.evaluate(() => delete window.MutationObserver);
|
await page.evaluate(() => delete window.MutationObserver);
|
||||||
|
|
@ -180,7 +190,7 @@ describe('Frame.waitForSelector', function() {
|
||||||
it('should resolve promise when node is added', async({page, server}) => {
|
it('should resolve promise when node is added', async({page, server}) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
const frame = page.mainFrame();
|
const frame = page.mainFrame();
|
||||||
const watchdog = frame.waitForSelector('div');
|
const watchdog = frame.waitForSelector('div', { state: 'attached' });
|
||||||
await frame.evaluate(addElement, 'br');
|
await frame.evaluate(addElement, 'br');
|
||||||
await frame.evaluate(addElement, 'div');
|
await frame.evaluate(addElement, 'div');
|
||||||
const eHandle = await watchdog;
|
const eHandle = await watchdog;
|
||||||
|
|
@ -206,7 +216,7 @@ describe('Frame.waitForSelector', function() {
|
||||||
});
|
});
|
||||||
it('should work when node is added through innerHTML', async({page, server}) => {
|
it('should work when node is added through innerHTML', async({page, server}) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
const watchdog = page.waitForSelector('h3 div');
|
const watchdog = page.waitForSelector('h3 div', { state: 'attached'});
|
||||||
await page.evaluate(addElement, 'span');
|
await page.evaluate(addElement, 'span');
|
||||||
await page.evaluate(() => document.querySelector('span').innerHTML = '<h3><div></div></h3>');
|
await page.evaluate(() => document.querySelector('span').innerHTML = '<h3><div></div></h3>');
|
||||||
await watchdog;
|
await watchdog;
|
||||||
|
|
@ -215,7 +225,7 @@ describe('Frame.waitForSelector', function() {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||||
const otherFrame = page.frames()[1];
|
const otherFrame = page.frames()[1];
|
||||||
const watchdog = page.waitForSelector('div');
|
const watchdog = page.waitForSelector('div', { state: 'attached' });
|
||||||
await otherFrame.evaluate(addElement, 'div');
|
await otherFrame.evaluate(addElement, 'div');
|
||||||
await page.evaluate(addElement, 'div');
|
await page.evaluate(addElement, 'div');
|
||||||
const eHandle = await watchdog;
|
const eHandle = await watchdog;
|
||||||
|
|
@ -226,7 +236,7 @@ describe('Frame.waitForSelector', function() {
|
||||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||||
const frame1 = page.frames()[1];
|
const frame1 = page.frames()[1];
|
||||||
const frame2 = page.frames()[2];
|
const frame2 = page.frames()[2];
|
||||||
const waitForSelectorPromise = frame2.waitForSelector('div');
|
const waitForSelectorPromise = frame2.waitForSelector('div', { state: 'attached' });
|
||||||
await frame1.evaluate(addElement, 'div');
|
await frame1.evaluate(addElement, 'div');
|
||||||
await frame2.evaluate(addElement, 'div');
|
await frame2.evaluate(addElement, 'div');
|
||||||
const eHandle = await waitForSelectorPromise;
|
const eHandle = await waitForSelectorPromise;
|
||||||
|
|
@ -255,7 +265,7 @@ describe('Frame.waitForSelector', function() {
|
||||||
});
|
});
|
||||||
it('should wait for visible', async({page, server}) => {
|
it('should wait for visible', async({page, server}) => {
|
||||||
let divFound = false;
|
let divFound = false;
|
||||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'visible' }).then(() => divFound = true);
|
const waitForSelector = page.waitForSelector('div').then(() => divFound = true);
|
||||||
await page.setContent(`<div style='display: none; visibility: hidden;'>1</div>`);
|
await page.setContent(`<div style='display: none; visibility: hidden;'>1</div>`);
|
||||||
expect(divFound).toBe(false);
|
expect(divFound).toBe(false);
|
||||||
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
|
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
|
||||||
|
|
@ -266,17 +276,17 @@ describe('Frame.waitForSelector', function() {
|
||||||
});
|
});
|
||||||
it('should not consider visible when zero-sized', async({page, server}) => {
|
it('should not consider visible when zero-sized', async({page, server}) => {
|
||||||
await page.setContent(`<div style='width: 0; height: 0;'>1</div>`);
|
await page.setContent(`<div style='width: 0; height: 0;'>1</div>`);
|
||||||
let error = await page.waitForSelector('div', { waitFor: 'visible', timeout: 1000 }).catch(e => e);
|
let error = await page.waitForSelector('div', { timeout: 1000 }).catch(e => e);
|
||||||
expect(error.message).toContain('timeout exceeded');
|
expect(error.message).toContain('timeout exceeded');
|
||||||
await page.evaluate(() => document.querySelector('div').style.width = '10px');
|
await page.evaluate(() => document.querySelector('div').style.width = '10px');
|
||||||
error = await page.waitForSelector('div', { waitFor: 'visible', timeout: 1000 }).catch(e => e);
|
error = await page.waitForSelector('div', { timeout: 1000 }).catch(e => e);
|
||||||
expect(error.message).toContain('timeout exceeded');
|
expect(error.message).toContain('timeout exceeded');
|
||||||
await page.evaluate(() => document.querySelector('div').style.height = '10px');
|
await page.evaluate(() => document.querySelector('div').style.height = '10px');
|
||||||
expect(await page.waitForSelector('div', { waitFor: 'visible', timeout: 1000 })).toBeTruthy();
|
expect(await page.waitForSelector('div', { timeout: 1000 })).toBeTruthy();
|
||||||
});
|
});
|
||||||
it('should wait for visible recursively', async({page, server}) => {
|
it('should wait for visible recursively', async({page, server}) => {
|
||||||
let divVisible = false;
|
let divVisible = false;
|
||||||
const waitForSelector = page.waitForSelector('div#inner', { waitFor: 'visible' }).then(() => divVisible = true);
|
const waitForSelector = page.waitForSelector('div#inner').then(() => divVisible = true);
|
||||||
await page.setContent(`<div style='display: none; visibility: hidden;'><div id="inner">hi</div></div>`);
|
await page.setContent(`<div style='display: none; visibility: hidden;'><div id="inner">hi</div></div>`);
|
||||||
expect(divVisible).toBe(false);
|
expect(divVisible).toBe(false);
|
||||||
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
|
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
|
||||||
|
|
@ -288,7 +298,7 @@ describe('Frame.waitForSelector', function() {
|
||||||
it('hidden should wait for hidden', async({page, server}) => {
|
it('hidden should wait for hidden', async({page, server}) => {
|
||||||
let divHidden = false;
|
let divHidden = false;
|
||||||
await page.setContent(`<div style='display: block;'>content</div>`);
|
await page.setContent(`<div style='display: block;'>content</div>`);
|
||||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'hidden' }).then(() => divHidden = true);
|
const waitForSelector = page.waitForSelector('div', { state: 'hidden' }).then(() => divHidden = true);
|
||||||
await page.waitForSelector('div'); // do a round trip
|
await page.waitForSelector('div'); // do a round trip
|
||||||
expect(divHidden).toBe(false);
|
expect(divHidden).toBe(false);
|
||||||
await page.evaluate(() => document.querySelector('div').style.setProperty('visibility', 'hidden'));
|
await page.evaluate(() => document.querySelector('div').style.setProperty('visibility', 'hidden'));
|
||||||
|
|
@ -298,7 +308,7 @@ describe('Frame.waitForSelector', function() {
|
||||||
it('hidden should wait for display: none', async({page, server}) => {
|
it('hidden should wait for display: none', async({page, server}) => {
|
||||||
let divHidden = false;
|
let divHidden = false;
|
||||||
await page.setContent(`<div style='display: block;'>content</div>`);
|
await page.setContent(`<div style='display: block;'>content</div>`);
|
||||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'hidden' }).then(() => divHidden = true);
|
const waitForSelector = page.waitForSelector('div', { state: 'hidden' }).then(() => divHidden = true);
|
||||||
await page.waitForSelector('div'); // do a round trip
|
await page.waitForSelector('div'); // do a round trip
|
||||||
expect(divHidden).toBe(false);
|
expect(divHidden).toBe(false);
|
||||||
await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
|
await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
|
||||||
|
|
@ -308,7 +318,7 @@ describe('Frame.waitForSelector', function() {
|
||||||
it('hidden should wait for removal', async({page, server}) => {
|
it('hidden should wait for removal', async({page, server}) => {
|
||||||
await page.setContent(`<div>content</div>`);
|
await page.setContent(`<div>content</div>`);
|
||||||
let divRemoved = false;
|
let divRemoved = false;
|
||||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'hidden' }).then(() => divRemoved = true);
|
const waitForSelector = page.waitForSelector('div', { state: 'hidden' }).then(() => divRemoved = true);
|
||||||
await page.waitForSelector('div'); // do a round trip
|
await page.waitForSelector('div'); // do a round trip
|
||||||
expect(divRemoved).toBe(false);
|
expect(divRemoved).toBe(false);
|
||||||
await page.evaluate(() => document.querySelector('div').remove());
|
await page.evaluate(() => document.querySelector('div').remove());
|
||||||
|
|
@ -316,12 +326,12 @@ describe('Frame.waitForSelector', function() {
|
||||||
expect(divRemoved).toBe(true);
|
expect(divRemoved).toBe(true);
|
||||||
});
|
});
|
||||||
it('should return null if waiting to hide non-existing element', async({page, server}) => {
|
it('should return null if waiting to hide non-existing element', async({page, server}) => {
|
||||||
const handle = await page.waitForSelector('non-existing', { waitFor: 'hidden' });
|
const handle = await page.waitForSelector('non-existing', { state: 'hidden' });
|
||||||
expect(handle).toBe(null);
|
expect(handle).toBe(null);
|
||||||
});
|
});
|
||||||
it('should respect timeout', async({page, server}) => {
|
it('should respect timeout', async({page, server}) => {
|
||||||
let error = null;
|
let error = null;
|
||||||
await page.waitForSelector('div', { timeout: 10 }).catch(e => error = e);
|
await page.waitForSelector('div', { timeout: 10, state: 'attached' }).catch(e => error = e);
|
||||||
expect(error).toBeTruthy();
|
expect(error).toBeTruthy();
|
||||||
expect(error.message).toContain('waiting for selector "div" failed: timeout');
|
expect(error.message).toContain('waiting for selector "div" failed: timeout');
|
||||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||||
|
|
@ -329,13 +339,13 @@ describe('Frame.waitForSelector', function() {
|
||||||
it('should have an error message specifically for awaiting an element to be hidden', async({page, server}) => {
|
it('should have an error message specifically for awaiting an element to be hidden', async({page, server}) => {
|
||||||
await page.setContent(`<div>content</div>`);
|
await page.setContent(`<div>content</div>`);
|
||||||
let error = null;
|
let error = null;
|
||||||
await page.waitForSelector('div', { waitFor: 'hidden', timeout: 1000 }).catch(e => error = e);
|
await page.waitForSelector('div', { state: 'hidden', timeout: 1000 }).catch(e => error = e);
|
||||||
expect(error).toBeTruthy();
|
expect(error).toBeTruthy();
|
||||||
expect(error.message).toContain('waiting for selector "[hidden] div" failed: timeout');
|
expect(error.message).toContain('waiting for selector "[hidden] div" failed: timeout');
|
||||||
});
|
});
|
||||||
it('should respond to node attribute mutation', async({page, server}) => {
|
it('should respond to node attribute mutation', async({page, server}) => {
|
||||||
let divFound = false;
|
let divFound = false;
|
||||||
const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true);
|
const waitForSelector = page.waitForSelector('.zombo', { state: 'attached'}).then(() => divFound = true);
|
||||||
await page.setContent(`<div class='notZombo'></div>`);
|
await page.setContent(`<div class='notZombo'></div>`);
|
||||||
expect(divFound).toBe(false);
|
expect(divFound).toBe(false);
|
||||||
await page.evaluate(() => document.querySelector('div').className = 'zombo');
|
await page.evaluate(() => document.querySelector('div').className = 'zombo');
|
||||||
|
|
@ -353,28 +363,28 @@ describe('Frame.waitForSelector', function() {
|
||||||
});
|
});
|
||||||
it('should throw for unknown waitFor option', async({page, server}) => {
|
it('should throw for unknown waitFor option', async({page, server}) => {
|
||||||
await page.setContent('<section>test</section>');
|
await page.setContent('<section>test</section>');
|
||||||
const error = await page.waitForSelector('section', { waitFor: 'foo' }).catch(e => e);
|
const error = await page.waitForSelector('section', { state: 'foo' }).catch(e => e);
|
||||||
expect(error.message).toContain('Unsupported waitFor option');
|
expect(error.message).toContain('Unsupported waitFor option');
|
||||||
});
|
});
|
||||||
it('should throw for visibility option', async({page, server}) => {
|
it('should throw for visibility option', async({page, server}) => {
|
||||||
await page.setContent('<section>test</section>');
|
await page.setContent('<section>test</section>');
|
||||||
const error = await page.waitForSelector('section', { visibility: 'hidden' }).catch(e => e);
|
const error = await page.waitForSelector('section', { visibility: 'hidden' }).catch(e => e);
|
||||||
expect(error.message).toBe('options.visibility is not supported, did you mean options.waitFor?');
|
expect(error.message).toBe('options.visibility is not supported, did you mean options.state?');
|
||||||
});
|
});
|
||||||
it('should throw for true waitFor option', async({page, server}) => {
|
it('should throw for true waitFor option', async({page, server}) => {
|
||||||
await page.setContent('<section>test</section>');
|
await page.setContent('<section>test</section>');
|
||||||
const error = await page.waitForSelector('section', { waitFor: true }).catch(e => e);
|
const error = await page.waitForSelector('section', { state: true }).catch(e => e);
|
||||||
expect(error.message).toContain('Unsupported waitFor option');
|
expect(error.message).toContain('Unsupported waitFor option');
|
||||||
});
|
});
|
||||||
it('should throw for false waitFor option', async({page, server}) => {
|
it('should throw for false waitFor option', async({page, server}) => {
|
||||||
await page.setContent('<section>test</section>');
|
await page.setContent('<section>test</section>');
|
||||||
const error = await page.waitForSelector('section', { waitFor: false }).catch(e => e);
|
const error = await page.waitForSelector('section', { state: false }).catch(e => e);
|
||||||
expect(error.message).toContain('Unsupported waitFor option');
|
expect(error.message).toContain('Unsupported waitFor option');
|
||||||
});
|
});
|
||||||
it('should support >> selector syntax', async({page, server}) => {
|
it('should support >> selector syntax', async({page, server}) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
const frame = page.mainFrame();
|
const frame = page.mainFrame();
|
||||||
const watchdog = frame.waitForSelector('css=div >> css=span');
|
const watchdog = frame.waitForSelector('css=div >> css=span', { state: 'attached'});
|
||||||
await frame.evaluate(addElement, 'br');
|
await frame.evaluate(addElement, 'br');
|
||||||
await frame.evaluate(addElement, 'div');
|
await frame.evaluate(addElement, 'div');
|
||||||
await frame.evaluate(() => document.querySelector('div').appendChild(document.createElement('span')));
|
await frame.evaluate(() => document.querySelector('div').appendChild(document.createElement('span')));
|
||||||
|
|
@ -384,12 +394,12 @@ describe('Frame.waitForSelector', function() {
|
||||||
});
|
});
|
||||||
it('should wait for detached if already detached', async({page, server}) => {
|
it('should wait for detached if already detached', async({page, server}) => {
|
||||||
await page.setContent('<section id="testAttribute">43543</section>');
|
await page.setContent('<section id="testAttribute">43543</section>');
|
||||||
expect(await page.waitForSelector('css=div', { waitFor: 'detached'})).toBe(null);
|
expect(await page.waitForSelector('css=div', { state: 'detached'})).toBe(null);
|
||||||
});
|
});
|
||||||
it('should wait for detached', async({page, server}) => {
|
it('should wait for detached', async({page, server}) => {
|
||||||
await page.setContent('<section id="testAttribute"><div>43543</div></section>');
|
await page.setContent('<section id="testAttribute"><div>43543</div></section>');
|
||||||
let done = false;
|
let done = false;
|
||||||
const waitFor = page.waitForSelector('css=div', { waitFor: 'detached'}).then(() => done = true);
|
const waitFor = page.waitForSelector('css=div', { state: 'detached'}).then(() => done = true);
|
||||||
expect(done).toBe(false);
|
expect(done).toBe(false);
|
||||||
await page.waitForSelector('css=section');
|
await page.waitForSelector('css=section');
|
||||||
expect(done).toBe(false);
|
expect(done).toBe(false);
|
||||||
|
|
@ -409,7 +419,7 @@ describe('Frame.waitForSelector xpath', function() {
|
||||||
});
|
});
|
||||||
it('should respect timeout', async({page}) => {
|
it('should respect timeout', async({page}) => {
|
||||||
let error = null;
|
let error = null;
|
||||||
await page.waitForSelector('//div', { timeout: 10 }).catch(e => error = e);
|
await page.waitForSelector('//div', { state: 'attached', timeout: 10 }).catch(e => error = e);
|
||||||
expect(error).toBeTruthy();
|
expect(error).toBeTruthy();
|
||||||
expect(error.message).toContain('waiting for selector "//div" failed: timeout');
|
expect(error.message).toContain('waiting for selector "//div" failed: timeout');
|
||||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||||
|
|
@ -419,7 +429,7 @@ describe('Frame.waitForSelector xpath', function() {
|
||||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||||
const frame1 = page.frames()[1];
|
const frame1 = page.frames()[1];
|
||||||
const frame2 = page.frames()[2];
|
const frame2 = page.frames()[2];
|
||||||
const waitForXPathPromise = frame2.waitForSelector('//div');
|
const waitForXPathPromise = frame2.waitForSelector('//div', { state: 'attached' });
|
||||||
await frame1.evaluate(addElement, 'div');
|
await frame1.evaluate(addElement, 'div');
|
||||||
await frame2.evaluate(addElement, 'div');
|
await frame2.evaluate(addElement, 'div');
|
||||||
const eHandle = await waitForXPathPromise;
|
const eHandle = await waitForXPathPromise;
|
||||||
|
|
|
||||||
2
utils/generate_types/overrides.d.ts
vendored
2
utils/generate_types/overrides.d.ts
vendored
|
|
@ -39,7 +39,7 @@ type HTMLOrSVGElement = SVGElement | HTMLElement;
|
||||||
type HTMLOrSVGElementHandle = ElementHandle<HTMLOrSVGElement>;
|
type HTMLOrSVGElementHandle = ElementHandle<HTMLOrSVGElement>;
|
||||||
|
|
||||||
type WaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
|
type WaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
|
||||||
waitFor: 'visible'|'attached';
|
state: 'visible'|'attached';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Page {
|
export interface Page {
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ playwright.chromium.launch().then(async browser => {
|
||||||
|
|
||||||
let currentURL: string;
|
let currentURL: string;
|
||||||
page
|
page
|
||||||
.waitForSelector('img', { waitFor: 'visible' })
|
.waitForSelector('img')
|
||||||
.then(() => console.log('First URL with image: ' + currentURL));
|
.then(() => console.log('First URL with image: ' + currentURL));
|
||||||
for (currentURL of [
|
for (currentURL of [
|
||||||
'https://example.com',
|
'https://example.com',
|
||||||
|
|
@ -594,7 +594,7 @@ playwright.chromium.launch().then(async browser => {
|
||||||
}, 5);
|
}, 5);
|
||||||
|
|
||||||
const something = Math.random() > .5 ? 'visible' : 'attached';
|
const something = Math.random() > .5 ? 'visible' : 'attached';
|
||||||
const handle = await page.waitForSelector('a', {waitFor: something});
|
const handle = await page.waitForSelector('a', {state: something});
|
||||||
await handle.$eval('span', (element, { x, y }) => {
|
await handle.$eval('span', (element, { x, y }) => {
|
||||||
const spanAssertion: AssertType<HTMLSpanElement, typeof element> = true;
|
const spanAssertion: AssertType<HTMLSpanElement, typeof element> = true;
|
||||||
const numberAssertion: AssertType<number, typeof x> = true;
|
const numberAssertion: AssertType<number, typeof x> = true;
|
||||||
|
|
@ -654,19 +654,19 @@ playwright.chromium.launch().then(async browser => {
|
||||||
const canBeNull: AssertType<null, typeof handle> = false;
|
const canBeNull: AssertType<null, typeof handle> = false;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const waitFor = Math.random() > .5 ? 'attached' : 'visible';
|
const state = Math.random() > .5 ? 'attached' : 'visible';
|
||||||
const handle = await frameLike.waitForSelector('body', {waitFor});
|
const handle = await frameLike.waitForSelector('body', {state});
|
||||||
const bodyAssertion: AssertType<playwright.ElementHandle<HTMLBodyElement>, typeof handle> = true;
|
const bodyAssertion: AssertType<playwright.ElementHandle<HTMLBodyElement>, typeof handle> = true;
|
||||||
const canBeNull: AssertType<null, typeof handle> = false;
|
const canBeNull: AssertType<null, typeof handle> = false;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const handle = await frameLike.waitForSelector('body', {waitFor: 'hidden'});
|
const handle = await frameLike.waitForSelector('body', {state: 'hidden'});
|
||||||
const bodyAssertion: AssertType<playwright.ElementHandle<HTMLBodyElement>, typeof handle> = true;
|
const bodyAssertion: AssertType<playwright.ElementHandle<HTMLBodyElement>, typeof handle> = true;
|
||||||
const canBeNull: AssertType<null, typeof handle> = true;
|
const canBeNull: AssertType<null, typeof handle> = true;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const waitFor = Math.random() > .5 ? 'hidden' : 'visible';
|
const state = Math.random() > .5 ? 'hidden' : 'visible';
|
||||||
const handle = await frameLike.waitForSelector('body', {waitFor});
|
const handle = await frameLike.waitForSelector('body', {state});
|
||||||
const bodyAssertion: AssertType<playwright.ElementHandle<HTMLBodyElement>, typeof handle> = true;
|
const bodyAssertion: AssertType<playwright.ElementHandle<HTMLBodyElement>, typeof handle> = true;
|
||||||
const canBeNull: AssertType<null, typeof handle> = true;
|
const canBeNull: AssertType<null, typeof handle> = true;
|
||||||
}
|
}
|
||||||
|
|
@ -677,14 +677,14 @@ playwright.chromium.launch().then(async browser => {
|
||||||
const canBeNull: AssertType<null, typeof handle> = false;
|
const canBeNull: AssertType<null, typeof handle> = false;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const waitFor = Math.random() > .5 ? 'attached' : 'visible';
|
const state = Math.random() > .5 ? 'attached' : 'visible';
|
||||||
const handle = await frameLike.waitForSelector('something-strange', {waitFor});
|
const handle = await frameLike.waitForSelector('something-strange', {state});
|
||||||
const elementAssertion: AssertType<playwright.ElementHandle<HTMLElement|SVGElement>, typeof handle> = true;
|
const elementAssertion: AssertType<playwright.ElementHandle<HTMLElement|SVGElement>, typeof handle> = true;
|
||||||
const canBeNull: AssertType<null, typeof handle> = false;
|
const canBeNull: AssertType<null, typeof handle> = false;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const waitFor = Math.random() > .5 ? 'hidden' : 'visible';
|
const state = Math.random() > .5 ? 'hidden' : 'visible';
|
||||||
const handle = await frameLike.waitForSelector('something-strange', {waitFor});
|
const handle = await frameLike.waitForSelector('something-strange', {state});
|
||||||
const elementAssertion: AssertType<playwright.ElementHandle<HTMLElement|SVGElement>, typeof handle> = true;
|
const elementAssertion: AssertType<playwright.ElementHandle<HTMLElement|SVGElement>, typeof handle> = true;
|
||||||
const canBeNull: AssertType<null, typeof handle> = true;
|
const canBeNull: AssertType<null, typeof handle> = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue