mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	chore: migrate from timeouts to deadlines internally (#1695)
This commit is contained in:
		
							parent
							
								
									362b72cf9d
								
							
						
					
					
						commit
						c6f580faf8
					
				
							
								
								
									
										14
									
								
								docs/api.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								docs/api.md
									
									
									
									
									
								
							@ -745,7 +745,7 @@ Emitted when attachment is downloaded. User can access basic file operations on
 | 
			
		||||
  - `element` <[ElementHandle]> handle to the input element that was clicked
 | 
			
		||||
  - `multiple` <[boolean]> Whether file chooser allow for [multiple](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#attr-multiple) file selection.
 | 
			
		||||
 | 
			
		||||
Emitted when a file chooser is supposed to appear, such as after clicking the  `<input type=file>`. Playwright can respond to it via setting the input files using [`elementHandle.setInputFiles`](#elementhandlesetinputfilesfiles) which can be uploaded in the end.
 | 
			
		||||
Emitted when a file chooser is supposed to appear, such as after clicking the  `<input type=file>`. Playwright can respond to it via setting the input files using [`elementHandle.setInputFiles`](#elementhandlesetinputfilesfiles-options) which can be uploaded in the end.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
page.on('filechooser', async ({element, multiple}) => {
 | 
			
		||||
@ -2498,7 +2498,7 @@ ElementHandle instances can be used as an argument in [`page.$eval()`](#pageeval
 | 
			
		||||
- [elementHandle.screenshot([options])](#elementhandlescreenshotoptions)
 | 
			
		||||
- [elementHandle.scrollIntoViewIfNeeded()](#elementhandlescrollintoviewifneeded)
 | 
			
		||||
- [elementHandle.selectOption(values[, options])](#elementhandleselectoptionvalues-options)
 | 
			
		||||
- [elementHandle.setInputFiles(files)](#elementhandlesetinputfilesfiles)
 | 
			
		||||
- [elementHandle.setInputFiles(files[, options])](#elementhandlesetinputfilesfiles-options)
 | 
			
		||||
- [elementHandle.toString()](#elementhandletostring)
 | 
			
		||||
- [elementHandle.type(text[, options])](#elementhandletypetext-options)
 | 
			
		||||
- [elementHandle.uncheck([options])](#elementhandleuncheckoptions)
 | 
			
		||||
@ -2759,11 +2759,19 @@ handle.selectOption('red', 'green', 'blue');
 | 
			
		||||
handle.selectOption({ value: 'blue' }, { index: 2 }, 'red');
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### elementHandle.setInputFiles(files)
 | 
			
		||||
#### elementHandle.setInputFiles(files[, options])
 | 
			
		||||
- `files` <[string]|[Array]<[string]>|[Object]|[Array]<[Object]>>
 | 
			
		||||
  - `name` <[string]> [File] name **required**
 | 
			
		||||
  - `type` <[string]> [File] type **required**
 | 
			
		||||
  - `data` <[string]> Base64-encoded data **required**
 | 
			
		||||
- `options` <[Object]>
 | 
			
		||||
  - `waitUntil` <"load"|"domcontentloaded"|"networkidle0"|"networkidle2"|"nowait"> Actions that cause navigations are waiting for those navigations to fire `domcontentloaded` by default. This behavior can be changed to either wait for another load phase or to omit the waiting altogether using `nowait`:
 | 
			
		||||
    - `'domcontentloaded'` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
 | 
			
		||||
    - `'load'` - consider navigation to be finished when the `load` event is fired.
 | 
			
		||||
    - `'networkidle0'` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
 | 
			
		||||
    - `'networkidle2'` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
 | 
			
		||||
    - `'nowait'` - do not wait.
 | 
			
		||||
  - `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]>
 | 
			
		||||
 | 
			
		||||
This method expects `elementHandle` to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
 | 
			
		||||
 | 
			
		||||
@ -86,8 +86,8 @@ export abstract class BrowserContextBase extends ExtendedEventEmitter implements
 | 
			
		||||
    return event === Events.BrowserContext.Close ? super._abortPromiseForEvent(event) : this._closePromise;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected _timeoutForEvent(event: string): number {
 | 
			
		||||
    return this._timeoutSettings.timeout();
 | 
			
		||||
  protected _computeDeadline(options?: types.TimeoutOptions): number {
 | 
			
		||||
    return this._timeoutSettings.computeDeadline(options);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _browserClosed() {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										43
									
								
								src/dom.ts
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								src/dom.ts
									
									
									
									
									
								
							@ -57,7 +57,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
 | 
			
		||||
  async _doEvaluateInternal(returnByValue: boolean, waitForNavigations: boolean, pageFunction: string | Function, ...args: any[]): Promise<any> {
 | 
			
		||||
    return await this.frame._page._frameManager.waitForNavigationsCreatedBy(async () => {
 | 
			
		||||
      return this._delegate.evaluate(this, returnByValue, pageFunction, ...args);
 | 
			
		||||
    }, waitForNavigations ? undefined : { waitUntil: 'nowait' });
 | 
			
		||||
    }, Number.MAX_SAFE_INTEGER, waitForNavigations ? undefined : { waitUntil: 'nowait' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _createHandle(remoteObject: any): js.JSHandle {
 | 
			
		||||
@ -181,19 +181,21 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
 | 
			
		||||
    return point;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async _performPointerAction(action: (point: types.Point) => Promise<void>, options?: PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<void> {
 | 
			
		||||
  async _performPointerAction(action: (point: types.Point) => Promise<void>, options: PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}): Promise<void> {
 | 
			
		||||
    const deadline = this._page._timeoutSettings.computeDeadline(options);
 | 
			
		||||
    const { force = false } = (options || {});
 | 
			
		||||
    if (!force)
 | 
			
		||||
      await this._waitForDisplayedAtStablePosition(options);
 | 
			
		||||
      await this._waitForDisplayedAtStablePosition(deadline);
 | 
			
		||||
    const position = options ? options.position : undefined;
 | 
			
		||||
    await this._scrollRectIntoViewIfNeeded(position ? { x: position.x, y: position.y, width: 0, height: 0 } : undefined);
 | 
			
		||||
    const point = position ? await this._offsetPoint(position) : await this._clickablePoint();
 | 
			
		||||
    if (!force)
 | 
			
		||||
      await this._waitForHitTargetAt(point, options);
 | 
			
		||||
 | 
			
		||||
    point.x = (point.x * 100 | 0) / 100;
 | 
			
		||||
    point.y = (point.y * 100 | 0) / 100;
 | 
			
		||||
 | 
			
		||||
    if (!force)
 | 
			
		||||
      await this._waitForHitTargetAt(point, deadline);
 | 
			
		||||
 | 
			
		||||
    await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
 | 
			
		||||
      let restoreModifiers: input.Modifier[] | undefined;
 | 
			
		||||
      if (options && options.modifiers)
 | 
			
		||||
@ -203,7 +205,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
 | 
			
		||||
      debugInput('...done');
 | 
			
		||||
      if (restoreModifiers)
 | 
			
		||||
        await this._page.keyboard._ensureModifiers(restoreModifiers);
 | 
			
		||||
    }, options, true);
 | 
			
		||||
    }, deadline, options, true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  hover(options?: PointerActionOptions & types.PointerActionWaitOptions): Promise<void> {
 | 
			
		||||
@ -219,6 +221,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async selectOption(values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[], options?: types.NavigatingActionWaitOptions): Promise<string[]> {
 | 
			
		||||
    const deadline = this._page._timeoutSettings.computeDeadline(options);
 | 
			
		||||
    let vals: string[] | ElementHandle[] | types.SelectOption[];
 | 
			
		||||
    if (!Array.isArray(values))
 | 
			
		||||
      vals = [ values ] as (string[] | ElementHandle[] | types.SelectOption[]);
 | 
			
		||||
@ -237,11 +240,12 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
 | 
			
		||||
    }
 | 
			
		||||
    return await this._page._frameManager.waitForNavigationsCreatedBy<string[]>(async () => {
 | 
			
		||||
      return this._evaluateInUtility(({ injected, node }, selectOptions) => injected.selectOptions(node, selectOptions), selectOptions);
 | 
			
		||||
    }, options);
 | 
			
		||||
    }, deadline, options);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async fill(value: string, options?: types.NavigatingActionWaitOptions): Promise<void> {
 | 
			
		||||
    assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"');
 | 
			
		||||
    const deadline = this._page._timeoutSettings.computeDeadline(options);
 | 
			
		||||
    await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
 | 
			
		||||
      const errorOrNeedsInput = await this._evaluateInUtility(({ injected, node }, value) => injected.fill(node, value), value);
 | 
			
		||||
      if (typeof errorOrNeedsInput === 'string')
 | 
			
		||||
@ -252,10 +256,11 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
 | 
			
		||||
        else
 | 
			
		||||
          await this._page.keyboard.press('Delete');
 | 
			
		||||
      }
 | 
			
		||||
    }, options, true);
 | 
			
		||||
    }, deadline, options, true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async setInputFiles(files: string | types.FilePayload | string[] | types.FilePayload[]) {
 | 
			
		||||
  async setInputFiles(files: string | types.FilePayload | string[] | types.FilePayload[], options?: types.NavigatingActionWaitOptions) {
 | 
			
		||||
    const deadline = this._page._timeoutSettings.computeDeadline(options);
 | 
			
		||||
    const multiple = await this._evaluateInUtility(({ node }) => {
 | 
			
		||||
      if (node.nodeType !== Node.ELEMENT_NODE || (node as Node as Element).tagName !== 'INPUT')
 | 
			
		||||
        throw new Error('Node is not an HTMLInputElement');
 | 
			
		||||
@ -283,7 +288,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
 | 
			
		||||
    }
 | 
			
		||||
    await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
 | 
			
		||||
      await this._page._delegate.setInputFiles(this as any as ElementHandle<HTMLInputElement>, filePayloads);
 | 
			
		||||
    });
 | 
			
		||||
    }, deadline, options);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async focus() {
 | 
			
		||||
@ -298,17 +303,19 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async type(text: string, options?: { delay?: number } & types.NavigatingActionWaitOptions) {
 | 
			
		||||
    const deadline = this._page._timeoutSettings.computeDeadline(options);
 | 
			
		||||
    await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
 | 
			
		||||
      await this.focus();
 | 
			
		||||
      await this._page.keyboard.type(text, options);
 | 
			
		||||
    }, options, true);
 | 
			
		||||
    }, deadline, options, true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async press(key: string, options?: { delay?: number } & types.NavigatingActionWaitOptions) {
 | 
			
		||||
    const deadline = this._page._timeoutSettings.computeDeadline(options);
 | 
			
		||||
    await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
 | 
			
		||||
      await this.focus();
 | 
			
		||||
      await this._page.keyboard.press(key, options);
 | 
			
		||||
    }, options, true);
 | 
			
		||||
    }, deadline, options, true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async check(options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
 | 
			
		||||
@ -367,16 +374,16 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async _waitForDisplayedAtStablePosition(options: types.TimeoutOptions = {}): Promise<void> {
 | 
			
		||||
  async _waitForDisplayedAtStablePosition(deadline: number): Promise<void> {
 | 
			
		||||
    debugInput('waiting for element to be displayed and not moving...');
 | 
			
		||||
    const stablePromise = this._evaluateInUtility(({ injected, node }, timeout) => {
 | 
			
		||||
      return injected.waitForDisplayedAtStablePosition(node, timeout);
 | 
			
		||||
    }, options.timeout || 0);
 | 
			
		||||
    await helper.waitWithTimeout(stablePromise, 'element to be displayed and not moving', options.timeout || 0);
 | 
			
		||||
    }, helper.timeUntilDeadline(deadline));
 | 
			
		||||
    await helper.waitWithDeadline(stablePromise, 'element to be displayed and not moving', deadline);
 | 
			
		||||
    debugInput('...done');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async _waitForHitTargetAt(point: types.Point, options: types.TimeoutOptions = {}): Promise<void> {
 | 
			
		||||
  async _waitForHitTargetAt(point: types.Point, deadline: number): Promise<void> {
 | 
			
		||||
    debugInput(`waiting for element to receive pointer events at (${point.x},${point.y}) ...`);
 | 
			
		||||
    const frame = await this.ownerFrame();
 | 
			
		||||
    if (frame && frame.parentFrame()) {
 | 
			
		||||
@ -389,8 +396,8 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
 | 
			
		||||
    }
 | 
			
		||||
    const hitTargetPromise = this._evaluateInUtility(({ injected, node }, { timeout, point }) => {
 | 
			
		||||
      return injected.waitForHitTargetAt(node, timeout, point);
 | 
			
		||||
    }, { timeout: options.timeout || 0, point });
 | 
			
		||||
    await helper.waitWithTimeout(hitTargetPromise, 'element to receive pointer events', options.timeout || 0);
 | 
			
		||||
    }, { timeout: helper.timeUntilDeadline(deadline), point });
 | 
			
		||||
    await helper.waitWithDeadline(hitTargetPromise, 'element to receive pointer events', deadline);
 | 
			
		||||
    debugInput('...done');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -16,21 +16,22 @@
 | 
			
		||||
 | 
			
		||||
import { EventEmitter } from 'events';
 | 
			
		||||
import { helper } from './helper';
 | 
			
		||||
import { TimeoutOptions } from './types';
 | 
			
		||||
 | 
			
		||||
export class ExtendedEventEmitter extends EventEmitter {
 | 
			
		||||
  protected _abortPromiseForEvent(event: string) {
 | 
			
		||||
    return new Promise<Error>(() => void 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected _timeoutForEvent(event: string): number {
 | 
			
		||||
  protected _computeDeadline(options?: TimeoutOptions): number {
 | 
			
		||||
    throw new Error('unimplemented');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async waitForEvent(event: string, optionsOrPredicate: Function|{ predicate?: Function, timeout?: number } = {}): Promise<any> {
 | 
			
		||||
    const deadline = this._computeDeadline(typeof optionsOrPredicate === 'function' ? undefined : optionsOrPredicate);
 | 
			
		||||
    const {
 | 
			
		||||
      predicate = () => true,
 | 
			
		||||
      timeout = this._timeoutForEvent(event)
 | 
			
		||||
    } = typeof optionsOrPredicate === 'function' ? {predicate: optionsOrPredicate} : optionsOrPredicate;
 | 
			
		||||
    return helper.waitForEvent(this, event, predicate, timeout, this._abortPromiseForEvent(event));
 | 
			
		||||
    return helper.waitForEvent(this, event, predicate, deadline, this._abortPromiseForEvent(event));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -99,10 +99,10 @@ export class FrameManager {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async waitForNavigationsCreatedBy<T>(action: () => Promise<T>, options: types.NavigatingActionWaitOptions = {}, input?: boolean): Promise<T> {
 | 
			
		||||
  async waitForNavigationsCreatedBy<T>(action: () => Promise<T>, deadline: number, options: types.NavigatingActionWaitOptions = {}, input?: boolean): Promise<T> {
 | 
			
		||||
    if (options.waitUntil === 'nowait')
 | 
			
		||||
      return action();
 | 
			
		||||
    const barrier = new PendingNavigationBarrier({ waitUntil: 'domcontentloaded', ...options });
 | 
			
		||||
    const barrier = new PendingNavigationBarrier({ waitUntil: 'domcontentloaded', ...options }, deadline);
 | 
			
		||||
    this._pendingNavigationBarriers.add(barrier);
 | 
			
		||||
    try {
 | 
			
		||||
      const result = await action();
 | 
			
		||||
@ -438,12 +438,13 @@ 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 { timeout = this._page._timeoutSettings.timeout(), waitFor = 'attached' } = (options || {});
 | 
			
		||||
    const { waitFor = 'attached' } = (options || {});
 | 
			
		||||
    if (!['attached', 'detached', 'visible', 'hidden'].includes(waitFor))
 | 
			
		||||
      throw new Error(`Unsupported waitFor option "${waitFor}"`);
 | 
			
		||||
 | 
			
		||||
    const { world, task } = selectors._waitForSelectorTask(selector, waitFor, timeout);
 | 
			
		||||
    const result = await this._scheduleRerunnableTask(task, world, timeout, `selector "${selectorToString(selector, waitFor)}"`);
 | 
			
		||||
    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)}"`);
 | 
			
		||||
    if (!result.asElement()) {
 | 
			
		||||
      result.dispose();
 | 
			
		||||
      return null;
 | 
			
		||||
@ -667,64 +668,64 @@ export class Frame {
 | 
			
		||||
    return result!;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async click(selector: string, options?: dom.ClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
 | 
			
		||||
    const handle = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.click(options);
 | 
			
		||||
  async click(selector: string, options: dom.ClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
 | 
			
		||||
    const { handle, deadline } = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.click(helper.optionsWithUpdatedTimeout(options, deadline));
 | 
			
		||||
    handle.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async dblclick(selector: string, options?: dom.MultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
 | 
			
		||||
    const handle = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.dblclick(options);
 | 
			
		||||
  async dblclick(selector: string, options: dom.MultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
 | 
			
		||||
    const { handle, deadline } = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.dblclick(helper.optionsWithUpdatedTimeout(options, deadline));
 | 
			
		||||
    handle.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async fill(selector: string, value: string, options?: types.NavigatingActionWaitOptions) {
 | 
			
		||||
    const handle = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.fill(value, options);
 | 
			
		||||
  async fill(selector: string, value: string, options: types.NavigatingActionWaitOptions = {}) {
 | 
			
		||||
    const { handle, deadline } = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.fill(value, helper.optionsWithUpdatedTimeout(options, deadline));
 | 
			
		||||
    handle.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async focus(selector: string, options?: types.TimeoutOptions) {
 | 
			
		||||
    const handle = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    const { handle } = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.focus();
 | 
			
		||||
    handle.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async hover(selector: string, options?: dom.PointerActionOptions & types.PointerActionWaitOptions) {
 | 
			
		||||
    const handle = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.hover(options);
 | 
			
		||||
    const { handle, deadline } = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.hover(helper.optionsWithUpdatedTimeout(options, deadline));
 | 
			
		||||
    handle.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async selectOption(selector: string, values: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[], options?: types.NavigatingActionWaitOptions): Promise<string[]> {
 | 
			
		||||
    const handle = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    const result = await handle.selectOption(values, options);
 | 
			
		||||
    const { handle, deadline } = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    const result = await handle.selectOption(values, helper.optionsWithUpdatedTimeout(options, deadline));
 | 
			
		||||
    handle.dispose();
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async type(selector: string, text: string, options?: { delay?: number } & types.NavigatingActionWaitOptions) {
 | 
			
		||||
    const handle = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.type(text, options);
 | 
			
		||||
    const { handle, deadline } = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.type(text, helper.optionsWithUpdatedTimeout(options, deadline));
 | 
			
		||||
    handle.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async press(selector: string, key: string, options?: { delay?: number } & types.NavigatingActionWaitOptions) {
 | 
			
		||||
    const handle = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.press(key, options);
 | 
			
		||||
    const { handle, deadline } = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.press(key, helper.optionsWithUpdatedTimeout(options, deadline));
 | 
			
		||||
    handle.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async check(selector: string, options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
 | 
			
		||||
    const handle = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.check(options);
 | 
			
		||||
    const { handle, deadline } = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.check(helper.optionsWithUpdatedTimeout(options, deadline));
 | 
			
		||||
    handle.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async uncheck(selector: string, options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
 | 
			
		||||
    const handle = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.uncheck(options);
 | 
			
		||||
    const { handle, deadline } = await this._waitForSelectorInUtilityContext(selector, options);
 | 
			
		||||
    await handle.uncheck(helper.optionsWithUpdatedTimeout(options, deadline));
 | 
			
		||||
    handle.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -738,17 +739,19 @@ export class Frame {
 | 
			
		||||
    return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout)));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _waitForSelectorInUtilityContext(selector: string, options?: types.WaitForElementOptions): Promise<dom.ElementHandle<Element>> {
 | 
			
		||||
    const { timeout = this._page._timeoutSettings.timeout(), waitFor = 'attached' } = (options || {});
 | 
			
		||||
    const { world, task } = selectors._waitForSelectorTask(selector, waitFor, timeout);
 | 
			
		||||
    const result = await this._scheduleRerunnableTask(task, world, timeout, `selector "${selectorToString(selector, waitFor)}"`);
 | 
			
		||||
    return result.asElement() as dom.ElementHandle<Element>;
 | 
			
		||||
  private async _waitForSelectorInUtilityContext(selector: string, options?: types.WaitForElementOptions): Promise<{ handle: dom.ElementHandle<Element>, deadline: number }> {
 | 
			
		||||
    const { waitFor = 'attached' } = options || {};
 | 
			
		||||
    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)}"`);
 | 
			
		||||
    return { handle: result.asElement() as dom.ElementHandle<Element>, deadline };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async waitForFunction<R, Arg>(pageFunction: types.Func1<Arg, R>, arg: Arg, options?: types.WaitForFunctionOptions): Promise<types.SmartHandle<R>>;
 | 
			
		||||
  async waitForFunction<R>(pageFunction: types.Func1<void, R>, arg?: any, options?: types.WaitForFunctionOptions): Promise<types.SmartHandle<R>>;
 | 
			
		||||
  async waitForFunction<R, Arg>(pageFunction: types.Func1<Arg, R>, arg: Arg, options: types.WaitForFunctionOptions = {}): Promise<types.SmartHandle<R>> {
 | 
			
		||||
    const { polling = 'raf', timeout = this._page._timeoutSettings.timeout() } = options;
 | 
			
		||||
    const { polling = 'raf' } = options;
 | 
			
		||||
    const deadline = this._page._timeoutSettings.computeDeadline(options);
 | 
			
		||||
    if (helper.isString(polling))
 | 
			
		||||
      assert(polling === 'raf' || polling === 'mutation', 'Unknown polling option: ' + polling);
 | 
			
		||||
    else if (helper.isNumber(polling))
 | 
			
		||||
@ -760,8 +763,8 @@ export class Frame {
 | 
			
		||||
    const task = async (context: dom.FrameExecutionContext) => context.evaluateHandleInternal(({ injected, predicateBody, polling, timeout, arg }) => {
 | 
			
		||||
      const innerPredicate = new Function('arg', predicateBody);
 | 
			
		||||
      return injected.poll(polling, timeout, () => innerPredicate(arg));
 | 
			
		||||
    }, { injected: await context._injected(), predicateBody, polling, timeout, arg });
 | 
			
		||||
    return this._scheduleRerunnableTask(task, 'main', timeout) as any as types.SmartHandle<R>;
 | 
			
		||||
    }, { injected: await context._injected(), predicateBody, polling, timeout: helper.timeUntilDeadline(deadline), arg });
 | 
			
		||||
    return this._scheduleRerunnableTask(task, 'main', deadline) as any as types.SmartHandle<R>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async title(): Promise<string> {
 | 
			
		||||
@ -781,9 +784,9 @@ export class Frame {
 | 
			
		||||
    this._parentFrame = null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _scheduleRerunnableTask(task: Task, contextType: ContextType, timeout?: number, title?: string): Promise<js.JSHandle> {
 | 
			
		||||
  private _scheduleRerunnableTask(task: Task, contextType: ContextType, deadline: number, title?: string): Promise<js.JSHandle> {
 | 
			
		||||
    const data = this._contextData.get(contextType)!;
 | 
			
		||||
    const rerunnableTask = new RerunnableTask(data, task, timeout, title);
 | 
			
		||||
    const rerunnableTask = new RerunnableTask(data, task, deadline, title);
 | 
			
		||||
    data.rerunnableTasks.add(rerunnableTask);
 | 
			
		||||
    if (data.context)
 | 
			
		||||
      rerunnableTask.rerun(data.context);
 | 
			
		||||
@ -834,7 +837,7 @@ class RerunnableTask {
 | 
			
		||||
  private _timeoutTimer?: NodeJS.Timer;
 | 
			
		||||
  private _terminated = false;
 | 
			
		||||
 | 
			
		||||
  constructor(data: ContextData, task: Task, timeout?: number, title?: string) {
 | 
			
		||||
  constructor(data: ContextData, task: Task, deadline: number, title?: string) {
 | 
			
		||||
    this._contextData = data;
 | 
			
		||||
    this._task = task;
 | 
			
		||||
    this._runCount = 0;
 | 
			
		||||
@ -844,10 +847,8 @@ class RerunnableTask {
 | 
			
		||||
    });
 | 
			
		||||
    // Since page navigation requires us to re-install the pageScript, we should track
 | 
			
		||||
    // timeout on our end.
 | 
			
		||||
    if (timeout) {
 | 
			
		||||
      const timeoutError = new TimeoutError(`waiting for ${title || 'function'} failed: timeout ${timeout}ms exceeded`);
 | 
			
		||||
      this._timeoutTimer = setTimeout(() => this.terminate(timeoutError), timeout);
 | 
			
		||||
    }
 | 
			
		||||
    const timeoutError = new TimeoutError(`waiting for ${title || 'function'} failed: timeout exceeded`);
 | 
			
		||||
    this._timeoutTimer = setTimeout(() => this.terminate(timeoutError), helper.timeUntilDeadline(deadline));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  terminate(error: Error) {
 | 
			
		||||
@ -918,13 +919,15 @@ function selectorToString(selector: string, waitFor: 'attached' | 'detached' | '
 | 
			
		||||
 | 
			
		||||
class PendingNavigationBarrier {
 | 
			
		||||
  private _frameIds = new Map<string, number>();
 | 
			
		||||
  private _options: types.NavigatingActionWaitOptions | undefined;
 | 
			
		||||
  private _options: types.NavigatingActionWaitOptions;
 | 
			
		||||
  private _protectCount = 0;
 | 
			
		||||
  private _promise: Promise<void>;
 | 
			
		||||
  private _promiseCallback = () => {};
 | 
			
		||||
  private _deadline: number;
 | 
			
		||||
 | 
			
		||||
  constructor(options: types.NavigatingActionWaitOptions | undefined) {
 | 
			
		||||
  constructor(options: types.NavigatingActionWaitOptions, deadline: number) {
 | 
			
		||||
    this._options = options;
 | 
			
		||||
    this._deadline = deadline;
 | 
			
		||||
    this._promise = new Promise(f => this._promiseCallback = f);
 | 
			
		||||
    this.retain();
 | 
			
		||||
  }
 | 
			
		||||
@ -936,7 +939,9 @@ class PendingNavigationBarrier {
 | 
			
		||||
 | 
			
		||||
  async addFrame(frame: Frame) {
 | 
			
		||||
    this.retain();
 | 
			
		||||
    await frame.waitForNavigation(this._options as types.NavigateOptions).catch(e => {});
 | 
			
		||||
    const timeout = helper.timeUntilDeadline(this._deadline);
 | 
			
		||||
    const options = { ...this._options, timeout } as types.NavigateOptions;
 | 
			
		||||
    await frame.waitForNavigation(options).catch(e => {});
 | 
			
		||||
    this.release();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -974,7 +979,7 @@ export class FrameTask {
 | 
			
		||||
    let timeoutPromise = new Promise<TimeoutError>(() => {});
 | 
			
		||||
    const { timeout = frame._page._timeoutSettings.navigationTimeout() } = options;
 | 
			
		||||
    if (timeout) {
 | 
			
		||||
      const errorMessage = 'Navigation timeout of ' + timeout + ' ms exceeded';
 | 
			
		||||
      const errorMessage = 'Navigation timeout exceeded';
 | 
			
		||||
      timeoutPromise = new Promise(fulfill => this._timer = setTimeout(fulfill, timeout))
 | 
			
		||||
          .then(() => { throw new TimeoutError(errorMessage); });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -149,9 +149,8 @@ class Helper {
 | 
			
		||||
    emitter: EventEmitter,
 | 
			
		||||
    eventName: (string | symbol),
 | 
			
		||||
    predicate: Function,
 | 
			
		||||
    timeout: number,
 | 
			
		||||
    deadline: number,
 | 
			
		||||
    abortPromise: Promise<Error>): Promise<any> {
 | 
			
		||||
    let eventTimeout: NodeJS.Timer;
 | 
			
		||||
    let resolveCallback: (event: any) => void = () => {};
 | 
			
		||||
    let rejectCallback: (error: any) => void = () => {};
 | 
			
		||||
    const promise = new Promise((resolve, reject) => {
 | 
			
		||||
@ -167,11 +166,9 @@ class Helper {
 | 
			
		||||
        rejectCallback(e);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    if (timeout) {
 | 
			
		||||
      eventTimeout = setTimeout(() => {
 | 
			
		||||
        rejectCallback(new TimeoutError(`Timeout exceeded while waiting for ${String(eventName)}`));
 | 
			
		||||
      }, timeout);
 | 
			
		||||
    }
 | 
			
		||||
    const eventTimeout = setTimeout(() => {
 | 
			
		||||
      rejectCallback(new TimeoutError(`Timeout exceeded while waiting for ${String(eventName)}`));
 | 
			
		||||
    }, helper.timeUntilDeadline(deadline));
 | 
			
		||||
    function cleanup() {
 | 
			
		||||
      Helper.removeEventListeners([listener]);
 | 
			
		||||
      clearTimeout(eventTimeout);
 | 
			
		||||
@ -189,12 +186,14 @@ class Helper {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static async waitWithTimeout<T>(promise: Promise<T>, taskName: string, timeout: number): Promise<T> {
 | 
			
		||||
    return this.waitWithDeadline(promise, taskName, helper.monotonicTime() + timeout);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static async waitWithDeadline<T>(promise: Promise<T>, taskName: string, deadline: number): Promise<T> {
 | 
			
		||||
    let reject: (error: Error) => void;
 | 
			
		||||
    const timeoutError = new TimeoutError(`waiting for ${taskName} failed: timeout ${timeout}ms exceeded`);
 | 
			
		||||
    const timeoutError = new TimeoutError(`waiting for ${taskName} failed: timeout exceeded`);
 | 
			
		||||
    const timeoutPromise = new Promise<T>((resolve, x) => reject = x);
 | 
			
		||||
    let timeoutTimer = null;
 | 
			
		||||
    if (timeout)
 | 
			
		||||
      timeoutTimer = setTimeout(() => reject(timeoutError), timeout);
 | 
			
		||||
    const timeoutTimer = setTimeout(() => reject(timeoutError), helper.timeUntilDeadline(deadline));
 | 
			
		||||
    try {
 | 
			
		||||
      return await Promise.race([promise, timeoutPromise]);
 | 
			
		||||
    } finally {
 | 
			
		||||
@ -341,6 +340,19 @@ class Helper {
 | 
			
		||||
  static guid(): string {
 | 
			
		||||
    return crypto.randomBytes(16).toString('hex');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static monotonicTime(): number {
 | 
			
		||||
    const [seconds, nanoseconds] = process.hrtime();
 | 
			
		||||
    return seconds * 1000 + (nanoseconds / 1000000 | 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static timeUntilDeadline(deadline: number): number {
 | 
			
		||||
    return Math.min(deadline - this.monotonicTime(), 2147483647); // 2^31-1 safe setTimeout in Node.
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static optionsWithUpdatedTimeout<T extends types.TimeoutOptions>(options: T | undefined, deadline: number): T {
 | 
			
		||||
    return { ...(options || {}) as T, timeout: this.timeUntilDeadline(deadline) };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function assert(value: any, message?: string): asserts value {
 | 
			
		||||
 | 
			
		||||
@ -257,7 +257,7 @@ class Injected {
 | 
			
		||||
      return isDisplayedAndStable;
 | 
			
		||||
    });
 | 
			
		||||
    if (!result)
 | 
			
		||||
      throw new Error(`waiting for element to be displayed and not moving failed: timeout ${timeout}ms exceeded`);
 | 
			
		||||
      throw new Error(`waiting for element to be displayed and not moving failed: timeout exceeded`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async waitForHitTargetAt(node: Node, timeout: number, point: types.Point) {
 | 
			
		||||
@ -273,7 +273,7 @@ class Injected {
 | 
			
		||||
      return hitElement === element;
 | 
			
		||||
    });
 | 
			
		||||
    if (!result)
 | 
			
		||||
      throw new Error(`waiting for element to receive mouse events failed: timeout ${timeout}ms exceeded`);
 | 
			
		||||
      throw new Error(`waiting for element to receive mouse events failed: timeout exceeded`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _parentElementOrShadowHost(element: Element): Element | undefined {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								src/page.ts
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/page.ts
									
									
									
									
									
								
							@ -147,8 +147,8 @@ export class Page extends ExtendedEventEmitter {
 | 
			
		||||
    return this._disconnectedPromise;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected _timeoutForEvent(event: string): number {
 | 
			
		||||
    return this._timeoutSettings.timeout();
 | 
			
		||||
  protected _computeDeadline(options?: types.TimeoutOptions): number {
 | 
			
		||||
    return this._timeoutSettings.computeDeadline(options);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _didClose() {
 | 
			
		||||
@ -313,21 +313,21 @@ export class Page extends ExtendedEventEmitter {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async waitForRequest(urlOrPredicate: string | RegExp | ((r: network.Request) => boolean), options: types.TimeoutOptions = {}): Promise<network.Request> {
 | 
			
		||||
    const { timeout = this._timeoutSettings.timeout() } = options;
 | 
			
		||||
    const deadline = this._timeoutSettings.computeDeadline(options);
 | 
			
		||||
    return helper.waitForEvent(this, Events.Page.Request, (request: network.Request) => {
 | 
			
		||||
      if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate))
 | 
			
		||||
        return helper.urlMatches(request.url(), urlOrPredicate);
 | 
			
		||||
      return urlOrPredicate(request);
 | 
			
		||||
    }, timeout, this._disconnectedPromise);
 | 
			
		||||
    }, deadline, this._disconnectedPromise);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async waitForResponse(urlOrPredicate: string | RegExp | ((r: network.Response) => boolean), options: types.TimeoutOptions = {}): Promise<network.Response> {
 | 
			
		||||
    const { timeout = this._timeoutSettings.timeout() } = options;
 | 
			
		||||
    const deadline = this._timeoutSettings.computeDeadline(options);
 | 
			
		||||
    return helper.waitForEvent(this, Events.Page.Response, (response: network.Response) => {
 | 
			
		||||
      if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate))
 | 
			
		||||
        return helper.urlMatches(response.url(), urlOrPredicate);
 | 
			
		||||
      return urlOrPredicate(response);
 | 
			
		||||
    }, timeout, this._disconnectedPromise);
 | 
			
		||||
    }, deadline, this._disconnectedPromise);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async goBack(options?: types.NavigateOptions): Promise<network.Response | null> {
 | 
			
		||||
 | 
			
		||||
@ -134,7 +134,7 @@ export class Selectors {
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _waitForSelectorTask(selector: string, waitFor: 'attached' | 'detached' | 'visible' | 'hidden', timeout: number): { world: 'main' | 'utility', task: (context: dom.FrameExecutionContext) => Promise<js.JSHandle> } {
 | 
			
		||||
  _waitForSelectorTask(selector: string, waitFor: '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 polling = (waitFor === 'attached' || waitFor === 'detached') ? 'mutation' : 'raf';
 | 
			
		||||
@ -151,7 +151,7 @@ export class Selectors {
 | 
			
		||||
            return !element || !evaluator.injected.isVisible(element);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }, { evaluator: await this._prepareEvaluator(context), parsed, waitFor, timeout });
 | 
			
		||||
    }, { evaluator: await this._prepareEvaluator(context), parsed, waitFor, timeout: helper.timeUntilDeadline(deadline) });
 | 
			
		||||
    return { world: this._needsMainContext(parsed) ? 'main' : 'utility', task };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,9 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { TimeoutOptions } from './types';
 | 
			
		||||
import { helper } from './helper';
 | 
			
		||||
 | 
			
		||||
const DEFAULT_TIMEOUT = 30000;
 | 
			
		||||
 | 
			
		||||
export class TimeoutSettings {
 | 
			
		||||
@ -44,11 +47,20 @@ export class TimeoutSettings {
 | 
			
		||||
    return DEFAULT_TIMEOUT;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  timeout(): number {
 | 
			
		||||
  private _timeout(): number {
 | 
			
		||||
    if (this._defaultTimeout !== null)
 | 
			
		||||
      return this._defaultTimeout;
 | 
			
		||||
    if (this._parent)
 | 
			
		||||
      return this._parent.timeout();
 | 
			
		||||
      return this._parent._timeout();
 | 
			
		||||
    return DEFAULT_TIMEOUT;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  computeDeadline(options?: TimeoutOptions) {
 | 
			
		||||
    const { timeout } = options || {};
 | 
			
		||||
    if (timeout === 0)
 | 
			
		||||
      return Number.MAX_SAFE_INTEGER;
 | 
			
		||||
    else if (typeof timeout === 'number')
 | 
			
		||||
      return helper.monotonicTime() + timeout;
 | 
			
		||||
    return helper.monotonicTime() + this._timeout();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -169,7 +169,7 @@ const utils = require('./utils');
 | 
			
		||||
      await page.goto(server.PREFIX + '/input/button.html');
 | 
			
		||||
      await page.$eval('button', b => b.style.display = 'none');
 | 
			
		||||
      const error = await page.click('button', { timeout: 100 }).catch(e => e);
 | 
			
		||||
      expect(error.message).toContain('timeout 100ms exceeded');
 | 
			
		||||
      expect(error.message).toContain('timeout exceeded');
 | 
			
		||||
    });
 | 
			
		||||
    it('should waitFor visible when parent is hidden', async({page, server}) => {
 | 
			
		||||
      let done = false;
 | 
			
		||||
@ -413,7 +413,7 @@ const utils = require('./utils');
 | 
			
		||||
        button.style.marginLeft = '200px';
 | 
			
		||||
      });
 | 
			
		||||
      const error = await button.click({ timeout: 100 }).catch(e => e);
 | 
			
		||||
      expect(error.message).toContain('timeout 100ms exceeded');
 | 
			
		||||
      expect(error.message).toContain('timeout exceeded');
 | 
			
		||||
    });
 | 
			
		||||
    it('should wait for becoming hit target', async({page, server}) => {
 | 
			
		||||
      await page.goto(server.PREFIX + '/input/button.html');
 | 
			
		||||
@ -461,7 +461,7 @@ const utils = require('./utils');
 | 
			
		||||
        document.body.appendChild(blocker);
 | 
			
		||||
      });
 | 
			
		||||
      const error = await button.click({ timeout: 100 }).catch(e => e);
 | 
			
		||||
      expect(error.message).toContain('timeout 100ms exceeded');
 | 
			
		||||
      expect(error.message).toContain('timeout exceeded');
 | 
			
		||||
    });
 | 
			
		||||
    it('should fail when obscured and not waiting for hit target', async({page, server}) => {
 | 
			
		||||
      await page.goto(server.PREFIX + '/input/button.html');
 | 
			
		||||
@ -553,10 +553,10 @@ const utils = require('./utils');
 | 
			
		||||
      // guarantee timeout.
 | 
			
		||||
      const error1 = await page.click('button', { timeout: 250 }).catch(e => e);
 | 
			
		||||
      if (error1)
 | 
			
		||||
        expect(error1.message).toContain('timeout 250ms exceeded');
 | 
			
		||||
        expect(error1.message).toContain('timeout exceeded');
 | 
			
		||||
      const error2 = await page.click('button', { timeout: 250 }).catch(e => e);
 | 
			
		||||
      if (error2)
 | 
			
		||||
        expect(error2.message).toContain('timeout 250ms exceeded');
 | 
			
		||||
        expect(error2.message).toContain('timeout exceeded');
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -198,7 +198,7 @@ module.exports.describe = function({playwright, MAC, WIN, FFOX, CHROMIUM, WEBKIT
 | 
			
		||||
      server.setRoute('/empty.html', (req, res) => { });
 | 
			
		||||
      let error = null;
 | 
			
		||||
      await page.goto(server.PREFIX + '/empty.html', {timeout: 1}).catch(e => error = e);
 | 
			
		||||
      const message = 'Navigation timeout of 1 ms exceeded';
 | 
			
		||||
      const message = 'Navigation timeout exceeded';
 | 
			
		||||
      expect(error.message).toContain(message);
 | 
			
		||||
      expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
 | 
			
		||||
    });
 | 
			
		||||
@ -209,7 +209,7 @@ module.exports.describe = function({playwright, MAC, WIN, FFOX, CHROMIUM, WEBKIT
 | 
			
		||||
      page.context().setDefaultNavigationTimeout(2);
 | 
			
		||||
      page.setDefaultNavigationTimeout(1);
 | 
			
		||||
      await page.goto(server.PREFIX + '/empty.html').catch(e => error = e);
 | 
			
		||||
      const message = 'Navigation timeout of 1 ms exceeded';
 | 
			
		||||
      const message = 'Navigation timeout exceeded';
 | 
			
		||||
      expect(error.message).toContain(message);
 | 
			
		||||
      expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
 | 
			
		||||
    });
 | 
			
		||||
@ -219,7 +219,7 @@ module.exports.describe = function({playwright, MAC, WIN, FFOX, CHROMIUM, WEBKIT
 | 
			
		||||
      let error = null;
 | 
			
		||||
      page.context().setDefaultNavigationTimeout(2);
 | 
			
		||||
      await page.goto(server.PREFIX + '/empty.html').catch(e => error = e);
 | 
			
		||||
      const message = 'Navigation timeout of 2 ms exceeded';
 | 
			
		||||
      const message = 'Navigation timeout exceeded';
 | 
			
		||||
      expect(error.message).toContain(message);
 | 
			
		||||
      expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
 | 
			
		||||
    });
 | 
			
		||||
@ -230,7 +230,7 @@ module.exports.describe = function({playwright, MAC, WIN, FFOX, CHROMIUM, WEBKIT
 | 
			
		||||
      page.context().setDefaultTimeout(2);
 | 
			
		||||
      page.setDefaultTimeout(1);
 | 
			
		||||
      await page.goto(server.PREFIX + '/empty.html').catch(e => error = e);
 | 
			
		||||
      const message = 'Navigation timeout of 1 ms exceeded';
 | 
			
		||||
      const message = 'Navigation timeout exceeded';
 | 
			
		||||
      expect(error.message).toContain(message);
 | 
			
		||||
      expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
 | 
			
		||||
    });
 | 
			
		||||
@ -240,7 +240,7 @@ module.exports.describe = function({playwright, MAC, WIN, FFOX, CHROMIUM, WEBKIT
 | 
			
		||||
      let error = null;
 | 
			
		||||
      page.context().setDefaultTimeout(2);
 | 
			
		||||
      await page.goto(server.PREFIX + '/empty.html').catch(e => error = e);
 | 
			
		||||
      const message = 'Navigation timeout of 2 ms exceeded';
 | 
			
		||||
      const message = 'Navigation timeout exceeded';
 | 
			
		||||
      expect(error.message).toContain(message);
 | 
			
		||||
      expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
 | 
			
		||||
    });
 | 
			
		||||
@ -251,7 +251,7 @@ module.exports.describe = function({playwright, MAC, WIN, FFOX, CHROMIUM, WEBKIT
 | 
			
		||||
      page.setDefaultTimeout(0);
 | 
			
		||||
      page.setDefaultNavigationTimeout(1);
 | 
			
		||||
      await page.goto(server.PREFIX + '/empty.html').catch(e => error = e);
 | 
			
		||||
      const message = 'Navigation timeout of 1 ms exceeded';
 | 
			
		||||
      const message = 'Navigation timeout exceeded';
 | 
			
		||||
      expect(error.message).toContain(message);
 | 
			
		||||
      expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
 | 
			
		||||
    });
 | 
			
		||||
@ -818,7 +818,7 @@ module.exports.describe = function({playwright, MAC, WIN, FFOX, CHROMIUM, WEBKIT
 | 
			
		||||
      server.setRoute('/one-style.css', (req, res) => response = res);
 | 
			
		||||
      await page.goto(server.PREFIX + '/one-style.html', {waitUntil: 'domcontentloaded'});
 | 
			
		||||
      const error = await page.waitForLoadState('load', { timeout: 1 }).catch(e => e);
 | 
			
		||||
      expect(error.message).toBe('Navigation timeout of 1 ms exceeded');
 | 
			
		||||
      expect(error.message).toBe('Navigation timeout exceeded');
 | 
			
		||||
    });
 | 
			
		||||
    it('should resolve immediately if loaded', async({page, server}) => {
 | 
			
		||||
      await page.goto(server.PREFIX + '/one-style.html');
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user