Skip to content

Commit

Permalink
api(waitForSelector): make "state: visible" default, includes rename …
Browse files Browse the repository at this point in the history
…to state (#2091)
  • Loading branch information
pavelfeldman committed May 4, 2020
1 parent 1f02179 commit bcce483
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 63 deletions.
12 changes: 6 additions & 6 deletions docs/api.md
Expand Up @@ -1811,11 +1811,11 @@ return finalResponse.ok();
#### page.waitForSelector(selector[, options])
- `selector` <[string]> A selector of an element to wait for
- `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.
- 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.

Expand Down Expand Up @@ -2435,11 +2435,11 @@ const [response] = await Promise.all([
#### frame.waitForSelector(selector[, options])
- `selector` <[string]> A selector of an element to wait for
- `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.
- 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.

Expand Down
8 changes: 4 additions & 4 deletions docs/core-concepts.md
Expand Up @@ -204,18 +204,18 @@ You can explicitly wait for an element to appear in the DOM or to become visible

```js
// 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`.
await page.waitForSelector('#promo', { waitFor: 'visible' });
await page.waitForSelector('#promo');
```

... or to become hidden or detached

```js
// 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.
await page.waitForSelector('#promo', { waitFor: 'detached' });
await page.waitForSelector('#promo', { state: 'detached' });
```

#### API reference
Expand Down
18 changes: 10 additions & 8 deletions src/frames.ts
Expand Up @@ -434,14 +434,16 @@ export class Frame {

async waitForSelector(selector: string, options?: types.WaitForElementOptions): Promise<dom.ElementHandle<Element> | null> {
if (options && (options as any).visibility)
throw new Error('options.visibility is not supported, did you mean options.waitFor?');
const { waitFor = 'attached' } = (options || {});
if (!['attached', 'detached', 'visible', 'hidden'].includes(waitFor))
throw new Error(`Unsupported waitFor option "${waitFor}"`);
throw new Error('options.visibility is not supported, did you mean options.state?');
if (options && (options as any).waitFor && (options as any).waitFor !== 'visible')
throw new Error('options.waitFor is not supported, did you mean options.state?');
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 { world, task } = selectors._waitForSelectorTask(selector, waitFor, deadline);
const result = await this._scheduleRerunnableTask(task, world, deadline, `selector "${selectorToString(selector, waitFor)}"`);
const { world, task } = selectors._waitForSelectorTask(selector, state, deadline);
const result = await this._scheduleRerunnableTask(task, world, deadline, `selector "${selectorToString(selector, state)}"`);
if (!result.asElement()) {
result.dispose();
return null;
Expand Down Expand Up @@ -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;
switch (waitFor) {
switch (state) {
case 'visible': label = '[visible] '; break;
case 'hidden': label = '[hidden] '; break;
case 'attached': label = ''; break;
Expand Down
8 changes: 4 additions & 4 deletions src/selectors.ts
Expand Up @@ -142,12 +142,12 @@ export class Selectors {
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 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, () => {
const element = evaluator.querySelector(parsed, document);
switch (waitFor) {
switch (state) {
case 'attached':
return element || false;
case 'detached':
Expand All @@ -158,7 +158,7 @@ export class Selectors {
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 };
}

Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Expand Up @@ -37,7 +37,7 @@ export type Quad = [ Point, Point, Point, Point ];

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 WaitForFunctionOptions = TimeoutOptions & { polling?: Polling };
Expand Down
4 changes: 2 additions & 2 deletions test/launcher.spec.js
Expand Up @@ -171,10 +171,10 @@ describe('Browser.disconnect', function() {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
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.
await page.waitForSelector('body');
await page.waitForSelector('body', { state: 'attached' });

await remote.close();
const error = await watchdog;
Expand Down

0 comments on commit bcce483

Please sign in to comment.