diff --git a/packages/playwright-core/src/browserServerImpl.ts b/packages/playwright-core/src/browserServerImpl.ts index 7e6b90741b529..47ab0f5dcf6e2 100644 --- a/packages/playwright-core/src/browserServerImpl.ts +++ b/packages/playwright-core/src/browserServerImpl.ts @@ -21,6 +21,7 @@ import { serverSideCallMetadata } from './server/instrumentation'; import { createPlaywright } from './server/playwright'; import { createGuid } from './server/utils/crypto'; import { rewriteErrorMessage } from './utils/isomorphic/stackTrace'; +import { DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT } from './utils/isomorphic/time'; import { ws } from './utilsBundle'; import type { BrowserServer, BrowserServerLauncher } from './client/browserType'; @@ -48,6 +49,7 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher { ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined, ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs), env: options.env ? envObjectToArray(options.env) : undefined, + timeout: options.timeout ?? DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT, }, toProtocolLogger(options.logger)).catch(e => { const log = helper.formatBrowserLogs(metadata.log); rewriteErrorMessage(e, `${e.message} Failed to launch browser.${log}`); diff --git a/packages/playwright-core/src/client/android.ts b/packages/playwright-core/src/client/android.ts index 9833dd3024da0..5b0b7efaecf71 100644 --- a/packages/playwright-core/src/client/android.ts +++ b/packages/playwright-core/src/client/android.ts @@ -51,7 +51,6 @@ export class Android extends ChannelOwner implements ap setDefaultTimeout(timeout: number) { this._timeoutSettings.setDefaultTimeout(timeout); - this._channel.setDefaultTimeoutNoReply({ timeout }).catch(() => {}); } async devices(options: { port?: number } = {}): Promise { @@ -69,7 +68,7 @@ export class Android extends ChannelOwner implements ap return await this._wrapApiCall(async () => { const deadline = options.timeout ? monotonicTime() + options.timeout : 0; const headers = { 'x-playwright-browser': 'android', ...options.headers }; - const connectParams: channels.LocalUtilsConnectParams = { wsEndpoint, headers, slowMo: options.slowMo, timeout: options.timeout }; + const connectParams: channels.LocalUtilsConnectParams = { wsEndpoint, headers, slowMo: options.slowMo, timeout: options.timeout || 0 }; const connection = await connectOverWebSocket(this._connection, connectParams); let device: AndroidDevice; @@ -133,7 +132,6 @@ export class AndroidDevice extends ChannelOwner i setDefaultTimeout(timeout: number) { this._timeoutSettings.setDefaultTimeout(timeout); - this._channel.setDefaultTimeoutNoReply({ timeout }).catch(() => {}); } serial(): string { @@ -162,49 +160,49 @@ export class AndroidDevice extends ChannelOwner i return await this.waitForEvent('webview', { ...options, predicate }); } - async wait(selector: api.AndroidSelector, options?: { state?: 'gone' } & types.TimeoutOptions) { - await this._channel.wait({ selector: toSelectorChannel(selector), ...options }); + async wait(selector: api.AndroidSelector, options: { state?: 'gone' } & types.TimeoutOptions = {}) { + await this._channel.wait({ selector: toSelectorChannel(selector), ...options, timeout: this._timeoutSettings.timeout(options) }); } - async fill(selector: api.AndroidSelector, text: string, options?: types.TimeoutOptions) { - await this._channel.fill({ selector: toSelectorChannel(selector), text, ...options }); + async fill(selector: api.AndroidSelector, text: string, options: types.TimeoutOptions = {}) { + await this._channel.fill({ selector: toSelectorChannel(selector), text, ...options, timeout: this._timeoutSettings.timeout(options) }); } - async press(selector: api.AndroidSelector, key: api.AndroidKey, options?: types.TimeoutOptions) { + async press(selector: api.AndroidSelector, key: api.AndroidKey, options: types.TimeoutOptions = {}) { await this.tap(selector, options); await this.input.press(key); } - async tap(selector: api.AndroidSelector, options?: { duration?: number } & types.TimeoutOptions) { - await this._channel.tap({ selector: toSelectorChannel(selector), ...options }); + async tap(selector: api.AndroidSelector, options: { duration?: number } & types.TimeoutOptions = {}) { + await this._channel.tap({ selector: toSelectorChannel(selector), ...options, timeout: this._timeoutSettings.timeout(options) }); } - async drag(selector: api.AndroidSelector, dest: types.Point, options?: SpeedOptions & types.TimeoutOptions) { - await this._channel.drag({ selector: toSelectorChannel(selector), dest, ...options }); + async drag(selector: api.AndroidSelector, dest: types.Point, options: SpeedOptions & types.TimeoutOptions = {}) { + await this._channel.drag({ selector: toSelectorChannel(selector), dest, ...options, timeout: this._timeoutSettings.timeout(options) }); } - async fling(selector: api.AndroidSelector, direction: Direction, options?: SpeedOptions & types.TimeoutOptions) { - await this._channel.fling({ selector: toSelectorChannel(selector), direction, ...options }); + async fling(selector: api.AndroidSelector, direction: Direction, options: SpeedOptions & types.TimeoutOptions = {}) { + await this._channel.fling({ selector: toSelectorChannel(selector), direction, ...options, timeout: this._timeoutSettings.timeout(options) }); } - async longTap(selector: api.AndroidSelector, options?: types.TimeoutOptions) { - await this._channel.longTap({ selector: toSelectorChannel(selector), ...options }); + async longTap(selector: api.AndroidSelector, options: types.TimeoutOptions = {}) { + await this._channel.longTap({ selector: toSelectorChannel(selector), ...options, timeout: this._timeoutSettings.timeout(options) }); } - async pinchClose(selector: api.AndroidSelector, percent: number, options?: SpeedOptions & types.TimeoutOptions) { - await this._channel.pinchClose({ selector: toSelectorChannel(selector), percent, ...options }); + async pinchClose(selector: api.AndroidSelector, percent: number, options: SpeedOptions & types.TimeoutOptions = {}) { + await this._channel.pinchClose({ selector: toSelectorChannel(selector), percent, ...options, timeout: this._timeoutSettings.timeout(options) }); } - async pinchOpen(selector: api.AndroidSelector, percent: number, options?: SpeedOptions & types.TimeoutOptions) { - await this._channel.pinchOpen({ selector: toSelectorChannel(selector), percent, ...options }); + async pinchOpen(selector: api.AndroidSelector, percent: number, options: SpeedOptions & types.TimeoutOptions = {}) { + await this._channel.pinchOpen({ selector: toSelectorChannel(selector), percent, ...options, timeout: this._timeoutSettings.timeout(options) }); } - async scroll(selector: api.AndroidSelector, direction: Direction, percent: number, options?: SpeedOptions & types.TimeoutOptions) { - await this._channel.scroll({ selector: toSelectorChannel(selector), direction, percent, ...options }); + async scroll(selector: api.AndroidSelector, direction: Direction, percent: number, options: SpeedOptions & types.TimeoutOptions = {}) { + await this._channel.scroll({ selector: toSelectorChannel(selector), direction, percent, ...options, timeout: this._timeoutSettings.timeout(options) }); } - async swipe(selector: api.AndroidSelector, direction: Direction, percent: number, options?: SpeedOptions & types.TimeoutOptions) { - await this._channel.swipe({ selector: toSelectorChannel(selector), direction, percent, ...options }); + async swipe(selector: api.AndroidSelector, direction: Direction, percent: number, options: SpeedOptions & types.TimeoutOptions = {}) { + await this._channel.swipe({ selector: toSelectorChannel(selector), direction, percent, ...options, timeout: this._timeoutSettings.timeout(options) }); } async info(selector: api.AndroidSelector): Promise { diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 50c61e1fab74d..7d89f87ac14d2 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -90,6 +90,7 @@ export class BrowserContext extends ChannelOwner this._isChromium = this._browser?._name === 'chromium'; this.tracing = Tracing.from(initializer.tracing); this.request = APIRequestContext.from(initializer.requestContext); + this.request._timeoutSettings = this._timeoutSettings; this.clock = new Clock(this); this._channel.on('bindingCall', ({ binding }) => this._onBinding(BindingCall.from(binding))); @@ -245,12 +246,10 @@ export class BrowserContext extends ChannelOwner setDefaultNavigationTimeout(timeout: number | undefined) { this._timeoutSettings.setDefaultNavigationTimeout(timeout); - this._channel.setDefaultNavigationTimeoutNoReply({ timeout }).catch(() => {}); } setDefaultTimeout(timeout: number | undefined) { this._timeoutSettings.setDefaultTimeout(timeout); - this._channel.setDefaultTimeoutNoReply({ timeout }).catch(() => {}); } browser(): Browser | null { diff --git a/packages/playwright-core/src/client/browserType.ts b/packages/playwright-core/src/client/browserType.ts index f67c97ced866c..3df71a8ec4c39 100644 --- a/packages/playwright-core/src/client/browserType.ts +++ b/packages/playwright-core/src/client/browserType.ts @@ -24,6 +24,7 @@ import { headersObjectToArray } from '../utils/isomorphic/headers'; import { monotonicTime } from '../utils/isomorphic/time'; import { raceAgainstDeadline } from '../utils/isomorphic/timeoutRunner'; import { connectOverWebSocket } from './webSocket'; +import { TimeoutSettings } from './timeoutSettings'; import type { Playwright } from './playwright'; import type { ConnectOptions, LaunchOptions, LaunchPersistentContextOptions, LaunchServerOptions, Logger } from './types'; @@ -73,6 +74,7 @@ export class BrowserType extends ChannelOwner imple ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined, ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs), env: options.env ? envObjectToArray(options.env) : undefined, + timeout: new TimeoutSettings(this._platform).launchTimeout(options), }; return await this._wrapApiCall(async () => { const browser = Browser.from((await this._channel.launch(launchOptions)).browser); @@ -100,6 +102,7 @@ export class BrowserType extends ChannelOwner imple env: options.env ? envObjectToArray(options.env) : undefined, channel: options.channel, userDataDir: (this._platform.path().isAbsolute(userDataDir) || !userDataDir) ? userDataDir : this._platform.path().resolve(userDataDir), + timeout: new TimeoutSettings(this._platform).launchTimeout(options), }; return await this._wrapApiCall(async () => { const result = await this._channel.launchPersistentContext(persistentParams); @@ -128,7 +131,7 @@ export class BrowserType extends ChannelOwner imple headers, exposeNetwork: params.exposeNetwork ?? params._exposeNetwork, slowMo: params.slowMo, - timeout: params.timeout, + timeout: params.timeout || 0, }; if ((params as any).__testHookRedirectPortForwarding) connectParams.socksProxyRedirectPortForTest = (params as any).__testHookRedirectPortForwarding; @@ -188,7 +191,7 @@ export class BrowserType extends ChannelOwner imple endpointURL, headers, slowMo: params.slowMo, - timeout: params.timeout + timeout: new TimeoutSettings(this._platform).timeout(params), }); const browser = Browser.from(result.browser); this._didLaunchBrowser(browser, {}, params.logger); diff --git a/packages/playwright-core/src/client/electron.ts b/packages/playwright-core/src/client/electron.ts index 3d9241321d3bf..b3b509a11b206 100644 --- a/packages/playwright-core/src/client/electron.ts +++ b/packages/playwright-core/src/client/electron.ts @@ -38,6 +38,7 @@ type ElectronOptions = Omit implements ...await prepareBrowserContextParams(this._platform, options), env: envObjectToArray(options.env ? options.env : this._platform.env), tracesDir: options.tracesDir, + timeout: new TimeoutSettings(this._platform).launchTimeout(options), }; const app = ElectronApplication.from((await this._channel.launch(params)).electronApplication); app._context._setOptions(params, options); diff --git a/packages/playwright-core/src/client/elementHandle.ts b/packages/playwright-core/src/client/elementHandle.ts index dbb9d5db9ae44..7e7519d298d45 100644 --- a/packages/playwright-core/src/client/elementHandle.ts +++ b/packages/playwright-core/src/client/elementHandle.ts @@ -25,13 +25,14 @@ import { getMimeTypeForPath } from '../utils/isomorphic/mimeType'; import type { BrowserContext } from './browserContext'; import type { ChannelOwner } from './channelOwner'; import type { Locator } from './locator'; -import type { FilePayload, Rect, SelectOption, SelectOptionOptions } from './types'; +import type { FilePayload, Rect, SelectOption, SelectOptionOptions, TimeoutOptions } from './types'; import type * as structs from '../../types/structs'; import type * as api from '../../types/types'; import type { Platform } from './platform'; import type * as channels from '@protocol/channels'; export class ElementHandle extends JSHandle implements api.ElementHandle { + private _frame: Frame; readonly _elementChannel: channels.ElementHandleChannel; static override from(handle: channels.ElementHandleChannel): ElementHandle { @@ -44,6 +45,7 @@ export class ElementHandle extends JSHandle implements constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.JSHandleInitializer) { super(parent, type, guid, initializer); + this._frame = parent as Frame; this._elementChannel = this._channel as channels.ElementHandleChannel; } @@ -114,65 +116,65 @@ export class ElementHandle extends JSHandle implements await this._elementChannel.dispatchEvent({ type, eventInit: serializeArgument(eventInit) }); } - async scrollIntoViewIfNeeded(options: channels.ElementHandleScrollIntoViewIfNeededOptions = {}) { - await this._elementChannel.scrollIntoViewIfNeeded(options); + async scrollIntoViewIfNeeded(options: channels.ElementHandleScrollIntoViewIfNeededOptions & TimeoutOptions = {}) { + await this._elementChannel.scrollIntoViewIfNeeded({ ...options, timeout: this._frame._timeout(options) }); } - async hover(options: channels.ElementHandleHoverOptions = {}): Promise { - await this._elementChannel.hover(options); + async hover(options: channels.ElementHandleHoverOptions & TimeoutOptions = {}): Promise { + await this._elementChannel.hover({ ...options, timeout: this._frame._timeout(options) }); } - async click(options: channels.ElementHandleClickOptions = {}): Promise { - return await this._elementChannel.click(options); + async click(options: channels.ElementHandleClickOptions & TimeoutOptions = {}): Promise { + return await this._elementChannel.click({ ...options, timeout: this._frame._timeout(options) }); } - async dblclick(options: channels.ElementHandleDblclickOptions = {}): Promise { - return await this._elementChannel.dblclick(options); + async dblclick(options: channels.ElementHandleDblclickOptions & TimeoutOptions = {}): Promise { + return await this._elementChannel.dblclick({ ...options, timeout: this._frame._timeout(options) }); } - async tap(options: channels.ElementHandleTapOptions = {}): Promise { - return await this._elementChannel.tap(options); + async tap(options: channels.ElementHandleTapOptions & TimeoutOptions = {}): Promise { + return await this._elementChannel.tap({ ...options, timeout: this._frame._timeout(options) }); } async selectOption(values: string | api.ElementHandle | SelectOption | string[] | api.ElementHandle[] | SelectOption[] | null, options: SelectOptionOptions = {}): Promise { - const result = await this._elementChannel.selectOption({ ...convertSelectOptionValues(values), ...options }); + const result = await this._elementChannel.selectOption({ ...convertSelectOptionValues(values), ...options, timeout: this._frame._timeout(options) }); return result.values; } - async fill(value: string, options: channels.ElementHandleFillOptions = {}): Promise { - return await this._elementChannel.fill({ value, ...options }); + async fill(value: string, options: channels.ElementHandleFillOptions & TimeoutOptions = {}): Promise { + return await this._elementChannel.fill({ value, ...options, timeout: this._frame._timeout(options) }); } - async selectText(options: channels.ElementHandleSelectTextOptions = {}): Promise { - await this._elementChannel.selectText(options); + async selectText(options: channels.ElementHandleSelectTextOptions & TimeoutOptions = {}): Promise { + await this._elementChannel.selectText({ ...options, timeout: this._frame._timeout(options) }); } - async setInputFiles(files: string | FilePayload | string[] | FilePayload[], options: channels.ElementHandleSetInputFilesOptions = {}) { + async setInputFiles(files: string | FilePayload | string[] | FilePayload[], options: channels.ElementHandleSetInputFilesOptions & TimeoutOptions = {}) { const frame = await this.ownerFrame(); if (!frame) throw new Error('Cannot set input files to detached element'); const converted = await convertInputFiles(this._platform, files, frame.page().context()); - await this._elementChannel.setInputFiles({ ...converted, ...options }); + await this._elementChannel.setInputFiles({ ...converted, ...options, timeout: this._frame._timeout(options) }); } async focus(): Promise { await this._elementChannel.focus(); } - async type(text: string, options: channels.ElementHandleTypeOptions = {}): Promise { - await this._elementChannel.type({ text, ...options }); + async type(text: string, options: channels.ElementHandleTypeOptions & TimeoutOptions = {}): Promise { + await this._elementChannel.type({ text, ...options, timeout: this._frame._timeout(options) }); } - async press(key: string, options: channels.ElementHandlePressOptions = {}): Promise { - await this._elementChannel.press({ key, ...options }); + async press(key: string, options: channels.ElementHandlePressOptions & TimeoutOptions = {}): Promise { + await this._elementChannel.press({ key, ...options, timeout: this._frame._timeout(options) }); } - async check(options: channels.ElementHandleCheckOptions = {}) { - return await this._elementChannel.check(options); + async check(options: channels.ElementHandleCheckOptions & TimeoutOptions = {}) { + return await this._elementChannel.check({ ...options, timeout: this._frame._timeout(options) }); } - async uncheck(options: channels.ElementHandleUncheckOptions = {}) { - return await this._elementChannel.uncheck(options); + async uncheck(options: channels.ElementHandleUncheckOptions & TimeoutOptions = {}) { + return await this._elementChannel.uncheck({ ...options, timeout: this._frame._timeout(options) }); } async setChecked(checked: boolean, options?: channels.ElementHandleCheckOptions) { @@ -187,9 +189,9 @@ export class ElementHandle extends JSHandle implements return value === undefined ? null : value; } - async screenshot(options: Omit & { path?: string, mask?: api.Locator[] } = {}): Promise { + async screenshot(options: Omit & TimeoutOptions & { path?: string, mask?: api.Locator[] } = {}): Promise { const mask = options.mask as Locator[] | undefined; - const copy: channels.ElementHandleScreenshotOptions = { ...options, mask: undefined }; + const copy: channels.ElementHandleScreenshotParams = { ...options, mask: undefined, timeout: this._frame._timeout(options) }; if (!copy.type) copy.type = determineScreenshotType(options); if (mask) { @@ -225,14 +227,14 @@ export class ElementHandle extends JSHandle implements return parseResult(result.value); } - async waitForElementState(state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled', options: channels.ElementHandleWaitForElementStateOptions = {}): Promise { - return await this._elementChannel.waitForElementState({ state, ...options }); + async waitForElementState(state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled', options: TimeoutOptions = {}): Promise { + return await this._elementChannel.waitForElementState({ state, ...options, timeout: this._frame._timeout(options) }); } - waitForSelector(selector: string, options: channels.ElementHandleWaitForSelectorOptions & { state: 'attached' | 'visible' }): Promise>; - waitForSelector(selector: string, options?: channels.ElementHandleWaitForSelectorOptions): Promise | null>; - async waitForSelector(selector: string, options: channels.ElementHandleWaitForSelectorOptions = {}): Promise | null> { - const result = await this._elementChannel.waitForSelector({ selector, ...options }); + waitForSelector(selector: string, options: channels.ElementHandleWaitForSelectorOptions & TimeoutOptions & { state: 'attached' | 'visible' }): Promise>; + waitForSelector(selector: string, options?: channels.ElementHandleWaitForSelectorOptions & TimeoutOptions): Promise | null>; + async waitForSelector(selector: string, options: channels.ElementHandleWaitForSelectorOptions & TimeoutOptions = {}): Promise | null> { + const result = await this._elementChannel.waitForSelector({ selector, ...options, timeout: this._frame._timeout(options) }); return ElementHandle.fromNullable(result.element) as ElementHandle | null; } } diff --git a/packages/playwright-core/src/client/fetch.ts b/packages/playwright-core/src/client/fetch.ts index dada11ed3076e..c16a24069fbff 100644 --- a/packages/playwright-core/src/client/fetch.ts +++ b/packages/playwright-core/src/client/fetch.ts @@ -23,9 +23,10 @@ import { assert } from '../utils/isomorphic/assert'; import { mkdirIfNeeded } from './fileUtils'; import { headersObjectToArray } from '../utils/isomorphic/headers'; import { isString } from '../utils/isomorphic/rtti'; +import { TimeoutSettings } from './timeoutSettings'; import type { Playwright } from './playwright'; -import type { ClientCertificate, FilePayload, Headers, SetStorageState, StorageState } from './types'; +import type { ClientCertificate, FilePayload, Headers, SetStorageState, StorageState, TimeoutOptions } from './types'; import type { Serializable } from '../../types/structs'; import type * as api from '../../types/types'; import type { HeadersArray, NameValue } from '../utils/isomorphic/types'; @@ -63,10 +64,9 @@ export class APIRequest implements api.APIRequest { this._playwright = playwright; } - async newContext(options: NewContextOptions = {}): Promise { + async newContext(options: NewContextOptions & TimeoutOptions = {}): Promise { options = { ...this._playwright._defaultContextOptions, - timeout: this._playwright._defaultContextTimeout, ...options, }; const storageState = typeof options.storageState === 'string' ? @@ -81,6 +81,7 @@ export class APIRequest implements api.APIRequest { })).request); this._contexts.add(context); context._request = this; + context._timeoutSettings.setDefaultTimeout(options.timeout ?? this._playwright._defaultContextTimeout); context._tracing._tracesDir = this._playwright._defaultLaunchOptions?.tracesDir; await context._instrumentation.runAfterCreateRequestContext(context); return context; @@ -91,6 +92,7 @@ export class APIRequestContext extends ChannelOwner implements api.Fr return this._page!; } - async goto(url: string, options: channels.FrameGotoOptions = {}): Promise { + _timeout(options?: TimeoutOptions): number { + const timeoutSettings = this._page?._timeoutSettings || new TimeoutSettings(this._platform); + return timeoutSettings.timeout(options || {}); + } + + _navigationTimeout(options?: TimeoutOptions): number { + const timeoutSettings = this._page?._timeoutSettings || new TimeoutSettings(this._platform); + return timeoutSettings.navigationTimeout(options || {}); + } + + async goto(url: string, options: channels.FrameGotoOptions & TimeoutOptions = {}): Promise { const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); - return network.Response.fromNullable((await this._channel.goto({ url, ...options, waitUntil })).response); + return network.Response.fromNullable((await this._channel.goto({ url, ...options, waitUntil, timeout: this._navigationTimeout(options) })).response); } private _setupNavigationWaiter(options: { timeout?: number }): Waiter { @@ -199,19 +210,19 @@ export class Frame extends ChannelOwner implements api.Fr return ElementHandle.fromNullable(result.element) as ElementHandle | null; } - waitForSelector(selector: string, options: channels.FrameWaitForSelectorOptions & { state: 'attached' | 'visible' }): Promise>; - waitForSelector(selector: string, options?: channels.FrameWaitForSelectorOptions): Promise | null>; - async waitForSelector(selector: string, options: channels.FrameWaitForSelectorOptions = {}): Promise | null> { + waitForSelector(selector: string, options: channels.FrameWaitForSelectorOptions & TimeoutOptions & { state: 'attached' | 'visible' }): Promise>; + waitForSelector(selector: string, options?: channels.FrameWaitForSelectorOptions & TimeoutOptions): Promise | null>; + async waitForSelector(selector: string, options: channels.FrameWaitForSelectorOptions & TimeoutOptions = {}): Promise | null> { if ((options as any).visibility) throw new Error('options.visibility is not supported, did you mean options.state?'); if ((options as any).waitFor && (options as any).waitFor !== 'visible') throw new Error('options.waitFor is not supported, did you mean options.state?'); - const result = await this._channel.waitForSelector({ selector, ...options }); + const result = await this._channel.waitForSelector({ selector, ...options, timeout: this._timeout(options) }); return ElementHandle.fromNullable(result.element) as ElementHandle | null; } - async dispatchEvent(selector: string, type: string, eventInit?: any, options: channels.FrameDispatchEventOptions = {}): Promise { - await this._channel.dispatchEvent({ selector, type, eventInit: serializeArgument(eventInit), ...options }); + async dispatchEvent(selector: string, type: string, eventInit?: any, options: channels.FrameDispatchEventOptions & TimeoutOptions = {}): Promise { + await this._channel.dispatchEvent({ selector, type, eventInit: serializeArgument(eventInit), ...options, timeout: this._timeout(options) }); } async $eval(selector: string, pageFunction: structs.PageFunctionOn, arg?: Arg): Promise { @@ -239,9 +250,9 @@ export class Frame extends ChannelOwner implements api.Fr return (await this._channel.content()).value; } - async setContent(html: string, options: channels.FrameSetContentOptions = {}): Promise { + async setContent(html: string, options: channels.FrameSetContentOptions & TimeoutOptions = {}): Promise { const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); - await this._channel.setContent({ html, ...options, waitUntil }); + await this._channel.setContent({ html, ...options, waitUntil, timeout: this._navigationTimeout(options) }); } name(): string { @@ -282,24 +293,24 @@ export class Frame extends ChannelOwner implements api.Fr return ElementHandle.from((await this._channel.addStyleTag({ ...copy })).element); } - async click(selector: string, options: channels.FrameClickOptions = {}) { - return await this._channel.click({ selector, ...options }); + async click(selector: string, options: channels.FrameClickOptions & TimeoutOptions = {}) { + return await this._channel.click({ selector, ...options, timeout: this._timeout(options) }); } - async dblclick(selector: string, options: channels.FrameDblclickOptions = {}) { - return await this._channel.dblclick({ selector, ...options }); + async dblclick(selector: string, options: channels.FrameDblclickOptions & TimeoutOptions = {}) { + return await this._channel.dblclick({ selector, ...options, timeout: this._timeout(options) }); } - async dragAndDrop(source: string, target: string, options: channels.FrameDragAndDropOptions = {}) { - return await this._channel.dragAndDrop({ source, target, ...options }); + async dragAndDrop(source: string, target: string, options: channels.FrameDragAndDropOptions & TimeoutOptions = {}) { + return await this._channel.dragAndDrop({ source, target, ...options, timeout: this._timeout(options) }); } - async tap(selector: string, options: channels.FrameTapOptions = {}) { - return await this._channel.tap({ selector, ...options }); + async tap(selector: string, options: channels.FrameTapOptions & TimeoutOptions = {}) { + return await this._channel.tap({ selector, ...options, timeout: this._timeout(options) }); } - async fill(selector: string, value: string, options: channels.FrameFillOptions = {}) { - return await this._channel.fill({ selector, value, ...options }); + async fill(selector: string, value: string, options: channels.FrameFillOptions & TimeoutOptions = {}) { + return await this._channel.fill({ selector, value, ...options, timeout: this._timeout(options) }); } async _highlight(selector: string) { @@ -342,83 +353,83 @@ export class Frame extends ChannelOwner implements api.Fr return new FrameLocator(this, selector); } - async focus(selector: string, options: channels.FrameFocusOptions = {}) { - await this._channel.focus({ selector, ...options }); + async focus(selector: string, options: channels.FrameFocusOptions & TimeoutOptions = {}) { + await this._channel.focus({ selector, ...options, timeout: this._timeout(options) }); } - async textContent(selector: string, options: channels.FrameTextContentOptions = {}): Promise { - const value = (await this._channel.textContent({ selector, ...options })).value; + async textContent(selector: string, options: channels.FrameTextContentOptions & TimeoutOptions = {}): Promise { + const value = (await this._channel.textContent({ selector, ...options, timeout: this._timeout(options) })).value; return value === undefined ? null : value; } - async innerText(selector: string, options: channels.FrameInnerTextOptions = {}): Promise { - return (await this._channel.innerText({ selector, ...options })).value; + async innerText(selector: string, options: channels.FrameInnerTextOptions & TimeoutOptions = {}): Promise { + return (await this._channel.innerText({ selector, ...options, timeout: this._timeout(options) })).value; } - async innerHTML(selector: string, options: channels.FrameInnerHTMLOptions = {}): Promise { - return (await this._channel.innerHTML({ selector, ...options })).value; + async innerHTML(selector: string, options: channels.FrameInnerHTMLOptions & TimeoutOptions = {}): Promise { + return (await this._channel.innerHTML({ selector, ...options, timeout: this._timeout(options) })).value; } - async getAttribute(selector: string, name: string, options: channels.FrameGetAttributeOptions = {}): Promise { - const value = (await this._channel.getAttribute({ selector, name, ...options })).value; + async getAttribute(selector: string, name: string, options: channels.FrameGetAttributeOptions & TimeoutOptions = {}): Promise { + const value = (await this._channel.getAttribute({ selector, name, ...options, timeout: this._timeout(options) })).value; return value === undefined ? null : value; } - async inputValue(selector: string, options: channels.FrameInputValueOptions = {}): Promise { - return (await this._channel.inputValue({ selector, ...options })).value; + async inputValue(selector: string, options: channels.FrameInputValueOptions & TimeoutOptions = {}): Promise { + return (await this._channel.inputValue({ selector, ...options, timeout: this._timeout(options) })).value; } - async isChecked(selector: string, options: channels.FrameIsCheckedOptions = {}): Promise { - return (await this._channel.isChecked({ selector, ...options })).value; + async isChecked(selector: string, options: channels.FrameIsCheckedOptions & TimeoutOptions = {}): Promise { + return (await this._channel.isChecked({ selector, ...options, timeout: this._timeout(options) })).value; } - async isDisabled(selector: string, options: channels.FrameIsDisabledOptions = {}): Promise { - return (await this._channel.isDisabled({ selector, ...options })).value; + async isDisabled(selector: string, options: channels.FrameIsDisabledOptions & TimeoutOptions = {}): Promise { + return (await this._channel.isDisabled({ selector, ...options, timeout: this._timeout(options) })).value; } - async isEditable(selector: string, options: channels.FrameIsEditableOptions = {}): Promise { - return (await this._channel.isEditable({ selector, ...options })).value; + async isEditable(selector: string, options: channels.FrameIsEditableOptions & TimeoutOptions = {}): Promise { + return (await this._channel.isEditable({ selector, ...options, timeout: this._timeout(options) })).value; } - async isEnabled(selector: string, options: channels.FrameIsEnabledOptions = {}): Promise { - return (await this._channel.isEnabled({ selector, ...options })).value; + async isEnabled(selector: string, options: channels.FrameIsEnabledOptions & TimeoutOptions = {}): Promise { + return (await this._channel.isEnabled({ selector, ...options, timeout: this._timeout(options) })).value; } - async isHidden(selector: string, options: channels.FrameIsHiddenOptions = {}): Promise { + async isHidden(selector: string, options: channels.FrameIsHiddenOptions & TimeoutOptions = {}): Promise { return (await this._channel.isHidden({ selector, ...options })).value; } - async isVisible(selector: string, options: channels.FrameIsVisibleOptions = {}): Promise { + async isVisible(selector: string, options: channels.FrameIsVisibleOptions & TimeoutOptions = {}): Promise { return (await this._channel.isVisible({ selector, ...options })).value; } - async hover(selector: string, options: channels.FrameHoverOptions = {}) { - await this._channel.hover({ selector, ...options }); + async hover(selector: string, options: channels.FrameHoverOptions & TimeoutOptions = {}) { + await this._channel.hover({ selector, ...options, timeout: this._timeout(options) }); } async selectOption(selector: string, values: string | api.ElementHandle | SelectOption | string[] | api.ElementHandle[] | SelectOption[] | null, options: SelectOptionOptions & StrictOptions = {}): Promise { - return (await this._channel.selectOption({ selector, ...convertSelectOptionValues(values), ...options })).values; + return (await this._channel.selectOption({ selector, ...convertSelectOptionValues(values), ...options, timeout: this._timeout(options) })).values; } - async setInputFiles(selector: string, files: string | FilePayload | string[] | FilePayload[], options: channels.FrameSetInputFilesOptions = {}): Promise { + async setInputFiles(selector: string, files: string | FilePayload | string[] | FilePayload[], options: channels.FrameSetInputFilesOptions & TimeoutOptions = {}): Promise { const converted = await convertInputFiles(this._platform, files, this.page().context()); - await this._channel.setInputFiles({ selector, ...converted, ...options }); + await this._channel.setInputFiles({ selector, ...converted, ...options, timeout: this._timeout(options) }); } - async type(selector: string, text: string, options: channels.FrameTypeOptions = {}) { - await this._channel.type({ selector, text, ...options }); + async type(selector: string, text: string, options: channels.FrameTypeOptions & TimeoutOptions = {}) { + await this._channel.type({ selector, text, ...options, timeout: this._timeout(options) }); } - async press(selector: string, key: string, options: channels.FramePressOptions = {}) { - await this._channel.press({ selector, key, ...options }); + async press(selector: string, key: string, options: channels.FramePressOptions & TimeoutOptions = {}) { + await this._channel.press({ selector, key, ...options, timeout: this._timeout(options) }); } - async check(selector: string, options: channels.FrameCheckOptions = {}) { - await this._channel.check({ selector, ...options }); + async check(selector: string, options: channels.FrameCheckOptions & TimeoutOptions = {}) { + await this._channel.check({ selector, ...options, timeout: this._timeout(options) }); } - async uncheck(selector: string, options: channels.FrameUncheckOptions = {}) { - await this._channel.uncheck({ selector, ...options }); + async uncheck(selector: string, options: channels.FrameUncheckOptions & TimeoutOptions = {}) { + await this._channel.uncheck({ selector, ...options, timeout: this._timeout(options) }); } async setChecked(selector: string, checked: boolean, options?: channels.FrameCheckOptions) { @@ -441,6 +452,7 @@ export class Frame extends ChannelOwner implements api.Fr expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg), + timeout: this._timeout(options), }); return JSHandle.from(result.handle) as any as structs.SmartHandle; } diff --git a/packages/playwright-core/src/client/locator.ts b/packages/playwright-core/src/client/locator.ts index eff5193bcc887..071a7eee5ed4f 100644 --- a/packages/playwright-core/src/client/locator.ts +++ b/packages/playwright-core/src/client/locator.ts @@ -74,7 +74,7 @@ export class Locator implements api.Locator { } private async _withElement(task: (handle: ElementHandle, timeout?: number) => Promise, timeout?: number): Promise { - timeout = this._frame.page()._timeoutSettings.timeout({ timeout }); + timeout = this._frame._timeout({ timeout }); const deadline = timeout ? monotonicTime() + timeout : 0; return await this._frame._wrapApiCall(async () => { @@ -102,15 +102,15 @@ export class Locator implements api.Locator { return await this._withElement(h => h.boundingBox(), options?.timeout); } - async check(options: channels.ElementHandleCheckOptions = {}) { + async check(options: channels.ElementHandleCheckOptions & TimeoutOptions = {}) { return await this._frame.check(this._selector, { strict: true, ...options }); } - async click(options: channels.ElementHandleClickOptions = {}): Promise { + async click(options: channels.ElementHandleClickOptions & TimeoutOptions = {}): Promise { return await this._frame.click(this._selector, { strict: true, ...options }); } - async dblclick(options: channels.ElementHandleDblclickOptions = {}): Promise { + async dblclick(options: channels.ElementHandleDblclickOptions & TimeoutOptions = {}): Promise { return await this._frame.dblclick(this._selector, { strict: true, ...options }); } @@ -118,7 +118,7 @@ export class Locator implements api.Locator { return await this._frame.dispatchEvent(this._selector, type, eventInit, { strict: true, ...options }); } - async dragTo(target: Locator, options: channels.FrameDragAndDropOptions = {}) { + async dragTo(target: Locator, options: channels.FrameDragAndDropOptions & TimeoutOptions = {}) { return await this._frame.dragAndDrop(this._selector, target._selector, { strict: true, ...options, @@ -137,7 +137,7 @@ export class Locator implements api.Locator { return await this._withElement(h => h.evaluateHandle(pageFunction, arg), options?.timeout); } - async fill(value: string, options: channels.ElementHandleFillOptions = {}): Promise { + async fill(value: string, options: channels.ElementHandleFillOptions & TimeoutOptions = {}): Promise { return await this._frame.fill(this._selector, value, { strict: true, ...options }); } @@ -243,7 +243,7 @@ export class Locator implements api.Locator { } async blur(options?: TimeoutOptions): Promise { - await this._frame._channel.blur({ selector: this._selector, strict: true, ...options }); + await this._frame._channel.blur({ selector: this._selector, strict: true, ...options, timeout: this._frame._timeout(options) }); } async count(): Promise { @@ -258,7 +258,7 @@ export class Locator implements api.Locator { return await this._frame.getAttribute(this._selector, name, { strict: true, ...options }); } - async hover(options: channels.ElementHandleHoverOptions = {}): Promise { + async hover(options: channels.ElementHandleHoverOptions & TimeoutOptions = {}): Promise { return await this._frame.hover(this._selector, { strict: true, ...options }); } @@ -298,21 +298,21 @@ export class Locator implements api.Locator { return await this._frame.isVisible(this._selector, { strict: true, ...options }); } - async press(key: string, options: channels.ElementHandlePressOptions = {}): Promise { + async press(key: string, options: channels.ElementHandlePressOptions & TimeoutOptions = {}): Promise { return await this._frame.press(this._selector, key, { strict: true, ...options }); } - async screenshot(options: Omit & { path?: string, mask?: api.Locator[] } = {}): Promise { + async screenshot(options: Omit & TimeoutOptions & { path?: string, mask?: api.Locator[] } = {}): Promise { const mask = options.mask as Locator[] | undefined; return await this._withElement((h, timeout) => h.screenshot({ ...options, mask, timeout }), options.timeout); } async ariaSnapshot(options?: TimeoutOptions): Promise { - const result = await this._frame._channel.ariaSnapshot({ ...options, selector: this._selector }); + const result = await this._frame._channel.ariaSnapshot({ ...options, selector: this._selector, timeout: this._frame._timeout(options) }); return result.snapshot; } - async scrollIntoViewIfNeeded(options: channels.ElementHandleScrollIntoViewIfNeededOptions = {}) { + async scrollIntoViewIfNeeded(options: channels.ElementHandleScrollIntoViewIfNeededOptions & TimeoutOptions = {}) { return await this._withElement((h, timeout) => h.scrollIntoViewIfNeeded({ ...options, timeout }), options.timeout); } @@ -320,22 +320,22 @@ export class Locator implements api.Locator { return await this._frame.selectOption(this._selector, values, { strict: true, ...options }); } - async selectText(options: channels.ElementHandleSelectTextOptions = {}): Promise { + async selectText(options: channels.ElementHandleSelectTextOptions & TimeoutOptions = {}): Promise { return await this._withElement((h, timeout) => h.selectText({ ...options, timeout }), options.timeout); } - async setChecked(checked: boolean, options?: channels.ElementHandleCheckOptions) { + async setChecked(checked: boolean, options?: channels.ElementHandleCheckOptions & TimeoutOptions) { if (checked) await this.check(options); else await this.uncheck(options); } - async setInputFiles(files: string | FilePayload | string[] | FilePayload[], options: channels.ElementHandleSetInputFilesOptions = {}) { + async setInputFiles(files: string | FilePayload | string[] | FilePayload[], options: channels.ElementHandleSetInputFilesOptions & TimeoutOptions = {}) { return await this._frame.setInputFiles(this._selector, files, { strict: true, ...options }); } - async tap(options: channels.ElementHandleTapOptions = {}): Promise { + async tap(options: channels.ElementHandleTapOptions & TimeoutOptions = {}): Promise { return await this._frame.tap(this._selector, { strict: true, ...options }); } @@ -343,15 +343,15 @@ export class Locator implements api.Locator { return await this._frame.textContent(this._selector, { strict: true, ...options }); } - async type(text: string, options: channels.ElementHandleTypeOptions = {}): Promise { + async type(text: string, options: channels.ElementHandleTypeOptions & TimeoutOptions = {}): Promise { return await this._frame.type(this._selector, text, { strict: true, ...options }); } - async pressSequentially(text: string, options: channels.ElementHandleTypeOptions = {}): Promise { + async pressSequentially(text: string, options: channels.ElementHandleTypeOptions & TimeoutOptions = {}): Promise { return await this.type(text, options); } - async uncheck(options: channels.ElementHandleUncheckOptions = {}) { + async uncheck(options: channels.ElementHandleUncheckOptions & TimeoutOptions = {}) { return await this._frame.uncheck(this._selector, { strict: true, ...options }); } @@ -367,10 +367,10 @@ export class Locator implements api.Locator { return await this._frame.$$eval(this._selector, ee => ee.map(e => e.textContent || '')); } - waitFor(options: channels.FrameWaitForSelectorOptions & { state: 'attached' | 'visible' }): Promise; - waitFor(options?: channels.FrameWaitForSelectorOptions): Promise; - async waitFor(options?: channels.FrameWaitForSelectorOptions): Promise { - await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, omitReturnValue: true, ...options }); + waitFor(options: channels.FrameWaitForSelectorOptions & TimeoutOptions & { state: 'attached' | 'visible' }): Promise; + waitFor(options?: channels.FrameWaitForSelectorOptions & TimeoutOptions): Promise; + async waitFor(options?: channels.FrameWaitForSelectorOptions & TimeoutOptions): Promise { + await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, omitReturnValue: true, ...options, timeout: this._frame._timeout(options) }); } async _expect(expression: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }> { diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 1cf7c1b4ca8c2..155e68d6bf9b5 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -48,7 +48,7 @@ import type { APIRequestContext } from './fetch'; import type { WaitForNavigationOptions } from './frame'; import type { FrameLocator, Locator, LocatorOptions } from './locator'; import type { Request, RouteHandlerCallback, WebSocketRouteHandlerCallback } from './network'; -import type { FilePayload, Headers, LifecycleEvent, SelectOption, SelectOptionOptions, Size, WaitForEventOptions, WaitForFunctionOptions } from './types'; +import type { FilePayload, Headers, LifecycleEvent, SelectOption, SelectOptionOptions, Size, TimeoutOptions, WaitForEventOptions, WaitForFunctionOptions } from './types'; import type * as structs from '../../types/structs'; import type * as api from '../../types/types'; import type { ByRoleOptions } from '../utils/isomorphic/locatorUtils'; @@ -277,16 +277,10 @@ export class Page extends ChannelOwner implements api.Page setDefaultNavigationTimeout(timeout: number) { this._timeoutSettings.setDefaultNavigationTimeout(timeout); - this._wrapApiCall(async () => { - await this._channel.setDefaultNavigationTimeoutNoReply({ timeout }); - }, true).catch(() => {}); } setDefaultTimeout(timeout: number) { this._timeoutSettings.setDefaultTimeout(timeout); - this._wrapApiCall(async () => { - await this._channel.setDefaultTimeoutNoReply({ timeout }); - }, true).catch(() => {}); } private _forceVideo(): Video { @@ -308,9 +302,9 @@ export class Page extends ChannelOwner implements api.Page return await this._mainFrame.$(selector, options); } - waitForSelector(selector: string, options: channels.FrameWaitForSelectorOptions & { state: 'attached' | 'visible' }): Promise>; - waitForSelector(selector: string, options?: channels.FrameWaitForSelectorOptions): Promise | null>; - async waitForSelector(selector: string, options?: channels.FrameWaitForSelectorOptions): Promise | null> { + waitForSelector(selector: string, options: channels.FrameWaitForSelectorOptions & TimeoutOptions & { state: 'attached' | 'visible' }): Promise>; + waitForSelector(selector: string, options?: channels.FrameWaitForSelectorOptions & TimeoutOptions): Promise | null>; + async waitForSelector(selector: string, options?: channels.FrameWaitForSelectorOptions & TimeoutOptions): Promise | null> { return await this._mainFrame.waitForSelector(selector, options); } @@ -369,17 +363,17 @@ export class Page extends ChannelOwner implements api.Page return await this._mainFrame.content(); } - async setContent(html: string, options?: channels.FrameSetContentOptions): Promise { + async setContent(html: string, options?: channels.FrameSetContentOptions & TimeoutOptions): Promise { return await this._mainFrame.setContent(html, options); } - async goto(url: string, options?: channels.FrameGotoOptions): Promise { + async goto(url: string, options?: channels.FrameGotoOptions & TimeoutOptions): Promise { return await this._mainFrame.goto(url, options); } - async reload(options: channels.PageReloadOptions = {}): Promise { + async reload(options: channels.PageReloadOptions & TimeoutOptions = {}): Promise { const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); - return Response.fromNullable((await this._channel.reload({ ...options, waitUntil })).response); + return Response.fromNullable((await this._channel.reload({ ...options, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options) })).response); } async addLocatorHandler(locator: Locator, handler: (locator: Locator) => any, options: { times?: number, noWaitAfter?: boolean } = {}): Promise { @@ -417,7 +411,7 @@ export class Page extends ChannelOwner implements api.Page } } - async waitForLoadState(state?: LifecycleEvent, options?: { timeout?: number }): Promise { + async waitForLoadState(state?: LifecycleEvent, options?: TimeoutOptions): Promise { return await this._mainFrame.waitForLoadState(state, options); } @@ -425,11 +419,11 @@ export class Page extends ChannelOwner implements api.Page return await this._mainFrame.waitForNavigation(options); } - async waitForURL(url: URLMatch, options?: { waitUntil?: LifecycleEvent, timeout?: number }): Promise { + async waitForURL(url: URLMatch, options?: TimeoutOptions & { waitUntil?: LifecycleEvent }): Promise { return await this._mainFrame.waitForURL(url, options); } - async waitForRequest(urlOrPredicate: string | RegExp | ((r: Request) => boolean | Promise), options: { timeout?: number } = {}): Promise { + async waitForRequest(urlOrPredicate: string | RegExp | ((r: Request) => boolean | Promise), options: TimeoutOptions = {}): Promise { const predicate = async (request: Request) => { if (isString(urlOrPredicate) || isRegExp(urlOrPredicate)) return urlMatches(this._browserContext._options.baseURL, request.url(), urlOrPredicate); @@ -440,7 +434,7 @@ export class Page extends ChannelOwner implements api.Page return await this._waitForEvent(Events.Page.Request, { predicate, timeout: options.timeout }, logLine); } - async waitForResponse(urlOrPredicate: string | RegExp | ((r: Response) => boolean | Promise), options: { timeout?: number } = {}): Promise { + async waitForResponse(urlOrPredicate: string | RegExp | ((r: Response) => boolean | Promise), options: TimeoutOptions = {}): Promise { const predicate = async (response: Response) => { if (isString(urlOrPredicate) || isRegExp(urlOrPredicate)) return urlMatches(this._browserContext._options.baseURL, response.url(), urlOrPredicate); @@ -477,14 +471,14 @@ export class Page extends ChannelOwner implements api.Page }); } - async goBack(options: channels.PageGoBackOptions = {}): Promise { + async goBack(options: channels.PageGoBackOptions & TimeoutOptions = {}): Promise { const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); - return Response.fromNullable((await this._channel.goBack({ ...options, waitUntil })).response); + return Response.fromNullable((await this._channel.goBack({ ...options, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options) })).response); } - async goForward(options: channels.PageGoForwardOptions = {}): Promise { + async goForward(options: channels.PageGoForwardOptions & TimeoutOptions = {}): Promise { const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); - return Response.fromNullable((await this._channel.goForward({ ...options, waitUntil })).response); + return Response.fromNullable((await this._channel.goForward({ ...options, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options) })).response); } async requestGC() { @@ -584,9 +578,9 @@ export class Page extends ChannelOwner implements api.Page await this._channel.setWebSocketInterceptionPatterns({ patterns }); } - async screenshot(options: Omit & { path?: string, mask?: api.Locator[] } = {}): Promise { + async screenshot(options: Omit & TimeoutOptions & { path?: string, mask?: api.Locator[] } = {}): Promise { const mask = options.mask as Locator[] | undefined; - const copy: channels.PageScreenshotOptions = { ...options, mask: undefined }; + const copy: channels.PageScreenshotParams = { ...options, mask: undefined, timeout: this._timeoutSettings.timeout(options) }; if (!copy.type) copy.type = determineScreenshotType(options); if (mask) { @@ -651,23 +645,23 @@ export class Page extends ChannelOwner implements api.Page return this._closed; } - async click(selector: string, options?: channels.FrameClickOptions) { + async click(selector: string, options?: channels.FrameClickOptions & TimeoutOptions) { return await this._mainFrame.click(selector, options); } - async dragAndDrop(source: string, target: string, options?: channels.FrameDragAndDropOptions) { + async dragAndDrop(source: string, target: string, options?: channels.FrameDragAndDropOptions & TimeoutOptions) { return await this._mainFrame.dragAndDrop(source, target, options); } - async dblclick(selector: string, options?: channels.FrameDblclickOptions) { + async dblclick(selector: string, options?: channels.FrameDblclickOptions & TimeoutOptions) { return await this._mainFrame.dblclick(selector, options); } - async tap(selector: string, options?: channels.FrameTapOptions) { + async tap(selector: string, options?: channels.FrameTapOptions & TimeoutOptions) { return await this._mainFrame.tap(selector, options); } - async fill(selector: string, value: string, options?: channels.FrameFillOptions) { + async fill(selector: string, value: string, options?: channels.FrameFillOptions & TimeoutOptions) { return await this._mainFrame.fill(selector, value, options); } @@ -707,55 +701,55 @@ export class Page extends ChannelOwner implements api.Page return this.mainFrame().frameLocator(selector); } - async focus(selector: string, options?: channels.FrameFocusOptions) { + async focus(selector: string, options?: channels.FrameFocusOptions & TimeoutOptions) { return await this._mainFrame.focus(selector, options); } - async textContent(selector: string, options?: channels.FrameTextContentOptions): Promise { + async textContent(selector: string, options?: channels.FrameTextContentOptions & TimeoutOptions): Promise { return await this._mainFrame.textContent(selector, options); } - async innerText(selector: string, options?: channels.FrameInnerTextOptions): Promise { + async innerText(selector: string, options?: channels.FrameInnerTextOptions & TimeoutOptions): Promise { return await this._mainFrame.innerText(selector, options); } - async innerHTML(selector: string, options?: channels.FrameInnerHTMLOptions): Promise { + async innerHTML(selector: string, options?: channels.FrameInnerHTMLOptions & TimeoutOptions): Promise { return await this._mainFrame.innerHTML(selector, options); } - async getAttribute(selector: string, name: string, options?: channels.FrameGetAttributeOptions): Promise { + async getAttribute(selector: string, name: string, options?: channels.FrameGetAttributeOptions & TimeoutOptions): Promise { return await this._mainFrame.getAttribute(selector, name, options); } - async inputValue(selector: string, options?: channels.FrameInputValueOptions): Promise { + async inputValue(selector: string, options?: channels.FrameInputValueOptions & TimeoutOptions): Promise { return await this._mainFrame.inputValue(selector, options); } - async isChecked(selector: string, options?: channels.FrameIsCheckedOptions): Promise { + async isChecked(selector: string, options?: channels.FrameIsCheckedOptions & TimeoutOptions): Promise { return await this._mainFrame.isChecked(selector, options); } - async isDisabled(selector: string, options?: channels.FrameIsDisabledOptions): Promise { + async isDisabled(selector: string, options?: channels.FrameIsDisabledOptions & TimeoutOptions): Promise { return await this._mainFrame.isDisabled(selector, options); } - async isEditable(selector: string, options?: channels.FrameIsEditableOptions): Promise { + async isEditable(selector: string, options?: channels.FrameIsEditableOptions & TimeoutOptions): Promise { return await this._mainFrame.isEditable(selector, options); } - async isEnabled(selector: string, options?: channels.FrameIsEnabledOptions): Promise { + async isEnabled(selector: string, options?: channels.FrameIsEnabledOptions & TimeoutOptions): Promise { return await this._mainFrame.isEnabled(selector, options); } - async isHidden(selector: string, options?: channels.FrameIsHiddenOptions): Promise { + async isHidden(selector: string, options?: channels.FrameIsHiddenOptions & TimeoutOptions): Promise { return await this._mainFrame.isHidden(selector, options); } - async isVisible(selector: string, options?: channels.FrameIsVisibleOptions): Promise { + async isVisible(selector: string, options?: channels.FrameIsVisibleOptions & TimeoutOptions): Promise { return await this._mainFrame.isVisible(selector, options); } - async hover(selector: string, options?: channels.FrameHoverOptions) { + async hover(selector: string, options?: channels.FrameHoverOptions & TimeoutOptions) { return await this._mainFrame.hover(selector, options); } @@ -763,27 +757,27 @@ export class Page extends ChannelOwner implements api.Page return await this._mainFrame.selectOption(selector, values, options); } - async setInputFiles(selector: string, files: string | FilePayload | string[] | FilePayload[], options?: channels.FrameSetInputFilesOptions): Promise { + async setInputFiles(selector: string, files: string | FilePayload | string[] | FilePayload[], options?: channels.FrameSetInputFilesOptions & TimeoutOptions): Promise { return await this._mainFrame.setInputFiles(selector, files, options); } - async type(selector: string, text: string, options?: channels.FrameTypeOptions) { + async type(selector: string, text: string, options?: channels.FrameTypeOptions & TimeoutOptions) { return await this._mainFrame.type(selector, text, options); } - async press(selector: string, key: string, options?: channels.FramePressOptions) { + async press(selector: string, key: string, options?: channels.FramePressOptions & TimeoutOptions) { return await this._mainFrame.press(selector, key, options); } - async check(selector: string, options?: channels.FrameCheckOptions) { + async check(selector: string, options?: channels.FrameCheckOptions & TimeoutOptions) { return await this._mainFrame.check(selector, options); } - async uncheck(selector: string, options?: channels.FrameUncheckOptions) { + async uncheck(selector: string, options?: channels.FrameUncheckOptions & TimeoutOptions) { return await this._mainFrame.uncheck(selector, options); } - async setChecked(selector: string, checked: boolean, options?: channels.FrameCheckOptions) { + async setChecked(selector: string, checked: boolean, options?: channels.FrameCheckOptions & TimeoutOptions) { return await this._mainFrame.setChecked(selector, checked, options); } diff --git a/packages/playwright-core/src/client/timeoutSettings.ts b/packages/playwright-core/src/client/timeoutSettings.ts index 90cb7e7558670..5e5034dcbb21d 100644 --- a/packages/playwright-core/src/client/timeoutSettings.ts +++ b/packages/playwright-core/src/client/timeoutSettings.ts @@ -15,11 +15,9 @@ * limitations under the License. */ -import type { Platform } from './platform'; +import { DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT, DEFAULT_PLAYWRIGHT_TIMEOUT } from '../utils/isomorphic/time'; -// Keep in sync with server. -export const DEFAULT_TIMEOUT = 30000; -export const DEFAULT_LAUNCH_TIMEOUT = 3 * 60 * 1000; // 3 minutes +import type { Platform } from './platform'; export class TimeoutSettings { private _parent: TimeoutSettings | undefined; @@ -59,7 +57,7 @@ export class TimeoutSettings { return this._defaultTimeout; if (this._parent) return this._parent.navigationTimeout(options); - return DEFAULT_TIMEOUT; + return DEFAULT_PLAYWRIGHT_TIMEOUT; } timeout(options: { timeout?: number }): number { @@ -71,6 +69,16 @@ export class TimeoutSettings { return this._defaultTimeout; if (this._parent) return this._parent.timeout(options); - return DEFAULT_TIMEOUT; + return DEFAULT_PLAYWRIGHT_TIMEOUT; + } + + launchTimeout(options: { timeout?: number }): number { + if (typeof options.timeout === 'number') + return options.timeout; + if (this._platform.isDebugMode()) + return 0; + if (this._parent) + return this._parent.launchTimeout(options); + return DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT; } } diff --git a/packages/playwright-core/src/client/types.ts b/packages/playwright-core/src/client/types.ts index 5235fef8b4379..acfbcbe056468 100644 --- a/packages/playwright-core/src/client/types.ts +++ b/packages/playwright-core/src/client/types.ts @@ -17,7 +17,7 @@ import type { Size } from '../utils/isomorphic/types'; import type * as channels from '@protocol/channels'; -export type { HeadersArray, Point, Quad, Rect, Size, TimeoutOptions } from '../utils/isomorphic/types'; +export type { HeadersArray, Point, Quad, Rect, Size } from '../utils/isomorphic/types'; type LoggerSeverity = 'verbose' | 'info' | 'warning' | 'error'; export interface Logger { @@ -25,15 +25,16 @@ export interface Logger { log(name: string, severity: LoggerSeverity, message: string | Error, args: any[], hints: { color?: string }): void; } +export type TimeoutOptions = { timeout?: number }; export type StrictOptions = { strict?: boolean }; export type Headers = { [key: string]: string }; export type Env = { [key: string]: string | number | boolean | undefined }; -export type WaitForEventOptions = Function | { predicate?: Function, timeout?: number }; -export type WaitForFunctionOptions = { timeout?: number, polling?: 'raf' | number }; +export type WaitForEventOptions = Function | TimeoutOptions & { predicate?: Function }; +export type WaitForFunctionOptions = TimeoutOptions & { polling?: 'raf' | number }; export type SelectOption = { value?: string, label?: string, index?: number, valueOrLabel?: string }; -export type SelectOptionOptions = { force?: boolean, timeout?: number }; +export type SelectOptionOptions = TimeoutOptions & { force?: boolean }; export type FilePayload = { name: string, mimeType: string, buffer: Buffer }; export type StorageState = { cookies: channels.NetworkCookie[], @@ -90,7 +91,7 @@ type LaunchOverrides = { env?: Env; logger?: Logger; firefoxUserPrefs?: { [key: string]: string | number | boolean }; -}; +} & TimeoutOptions; export type LaunchOptions = Omit & LaunchOverrides; export type LaunchPersistentContextOptions = Omit; diff --git a/packages/playwright-core/src/protocol/debug.ts b/packages/playwright-core/src/protocol/debug.ts index 5420e32796a41..b0b31751f96e4 100644 --- a/packages/playwright-core/src/protocol/debug.ts +++ b/packages/playwright-core/src/protocol/debug.ts @@ -74,8 +74,6 @@ export const methodMetainfo = new Map = { ignoreAllDefaultArgs: false, handleSIGINT: false, handleSIGTERM: false, @@ -322,5 +322,6 @@ const defaultLaunchOptions: LaunchOptions = { const optionsThatAllowBrowserReuse: (keyof LaunchOptions)[] = [ 'headless', + 'timeout', 'tracesDir', ]; diff --git a/packages/playwright-core/src/remote/playwrightServer.ts b/packages/playwright-core/src/remote/playwrightServer.ts index 7f6a58a0a5c13..73ed85c2ce766 100644 --- a/packages/playwright-core/src/remote/playwrightServer.ts +++ b/packages/playwright-core/src/remote/playwrightServer.ts @@ -18,6 +18,7 @@ import { PlaywrightConnection } from './playwrightConnection'; import { createPlaywright } from '../server/playwright'; import { debugLogger } from '../server/utils/debugLogger'; import { Semaphore } from '../utils/isomorphic/semaphore'; +import { DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT } from '../utils/isomorphic/time'; import { WSServer } from '../server/utils/wsServer'; import { wrapInASCIIBox } from '../server/utils/ascii'; import { getPlaywrightVersion } from '../server/utils/userAgent'; @@ -76,7 +77,7 @@ export class PlaywrightServer { const launchOptionsHeader = request.headers['x-playwright-launch-options'] || ''; const launchOptionsHeaderValue = Array.isArray(launchOptionsHeader) ? launchOptionsHeader[0] : launchOptionsHeader; const launchOptionsParam = url.searchParams.get('launch-options'); - let launchOptions: LaunchOptions = {}; + let launchOptions: LaunchOptions = { timeout: DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT }; try { launchOptions = JSON.parse(launchOptionsParam || launchOptionsHeaderValue); } catch (e) { diff --git a/packages/playwright-core/src/server/android/android.ts b/packages/playwright-core/src/server/android/android.ts index c6c80126d1234..cb70e51db43da 100644 --- a/packages/playwright-core/src/server/android/android.ts +++ b/packages/playwright-core/src/server/android/android.ts @@ -19,7 +19,6 @@ import fs from 'fs'; import os from 'os'; import path from 'path'; -import { TimeoutSettings } from '../timeoutSettings'; import { PipeTransport } from '../utils/pipeTransport'; import { createGuid } from '../utils/crypto'; import { isUnderTest } from '../utils/debug'; @@ -68,16 +67,10 @@ export interface SocketBackend extends EventEmitter { export class Android extends SdkObject { private _backend: Backend; private _devices = new Map(); - readonly _timeoutSettings: TimeoutSettings; constructor(parent: SdkObject, backend: Backend) { super(parent, 'android'); this._backend = backend; - this._timeoutSettings = new TimeoutSettings(); - } - - setDefaultTimeout(timeout: number) { - this._timeoutSettings.setDefaultTimeout(timeout); } async devices(options: channels.AndroidDevicesOptions): Promise { @@ -111,7 +104,6 @@ export class AndroidDevice extends SdkObject { private _lastId = 0; private _callbacks = new Map void, reject: (error: Error) => void }>(); private _pollingWebViews: NodeJS.Timeout | undefined; - readonly _timeoutSettings: TimeoutSettings; private _webViews = new Map(); static Events = { @@ -131,7 +123,6 @@ export class AndroidDevice extends SdkObject { this.model = model; this.serial = backend.serial; this._options = options; - this._timeoutSettings = new TimeoutSettings(android._timeoutSettings); } static async create(android: Android, backend: DeviceBackend, options: channels.AndroidDevicesOptions): Promise { @@ -154,10 +145,6 @@ export class AndroidDevice extends SdkObject { poll(); } - setDefaultTimeout(timeout: number) { - this._timeoutSettings.setDefaultTimeout(timeout); - } - async shell(command: string): Promise { const result = await this._backend.runCommand(`shell:${command}`); await this._refreshWebViews(); @@ -237,8 +224,8 @@ export class AndroidDevice extends SdkObject { } async send(method: string, params: any = {}): Promise { - // Patch the timeout in! - params.timeout = this._timeoutSettings.timeout(params); + // Patch the timeout in, just in case it's missing in one of the commands. + params.timeout = params.timeout || 0; const driver = await this._driver(); if (!driver) throw new Error('Device is closed'); @@ -344,7 +331,7 @@ export class AndroidDevice extends SdkObject { proxy: options.proxy, protocolLogger: helper.debugProtocolLogger(), browserLogsCollector: new RecentLogsCollector(), - originalLaunchOptions: {}, + originalLaunchOptions: { timeout: 0 }, }; validateBrowserContextOptions(options, browserOptions); diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index b2f54eaec7033..7467efd275c78 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -18,7 +18,6 @@ import fs from 'fs'; import path from 'path'; -import { TimeoutSettings } from './timeoutSettings'; import { createGuid } from './utils/crypto'; import { debugMode } from './utils/debug'; import { Clock } from './clock'; @@ -69,7 +68,6 @@ export abstract class BrowserContext extends SdkObject { VideoStarted: 'videostarted', }; - readonly _timeoutSettings = new TimeoutSettings(); readonly _pageBindings = new Map(); readonly _activeProgressControllers = new Set(); readonly _options: types.BrowserContextOptions; @@ -195,8 +193,6 @@ export abstract class BrowserContext extends SdkObject { } async resetForReuse(metadata: CallMetadata, params: channels.BrowserNewContextForReuseParams | null) { - this.setDefaultNavigationTimeout(undefined); - this.setDefaultTimeout(undefined); this.tracing.resetForReuse(); if (params) { @@ -376,14 +372,6 @@ export abstract class BrowserContext extends SdkObject { await this.doClearPermissions(); } - setDefaultNavigationTimeout(timeout: number | undefined) { - this._timeoutSettings.setDefaultNavigationTimeout(timeout); - } - - setDefaultTimeout(timeout: number | undefined) { - this._timeoutSettings.setDefaultTimeout(timeout); - } - async _loadDefaultContextAsIs(progress: Progress): Promise { if (!this.possiblyUninitializedPages().length) { const waitForEvent = helper.waitForEvent(progress, this, BrowserContext.Events.Page); @@ -562,7 +550,7 @@ export abstract class BrowserContext extends SdkObject { }); for (const origin of originsToSave) { const frame = page.mainFrame(); - await frame.goto(internalMetadata, origin); + await frame.goto(internalMetadata, origin, { timeout: 0 }); const storage: SerializedStorage = await frame.evaluateExpression(collectScript, { world: 'utility' }); if (storage.localStorage.length || storage.indexedDB?.length) result.origins.push({ origin, localStorage: storage.localStorage, indexedDB: storage.indexedDB }); @@ -593,7 +581,7 @@ export abstract class BrowserContext extends SdkObject { for (const origin of new Set([...oldOrigins, ...newOrigins.keys()])) { const frame = page.mainFrame(); - await frame.goto(internalMetadata, origin); + await frame.goto(internalMetadata, origin, { timeout: 0 }); await frame.resetStorageForCurrentOriginBestEffort(newOrigins.get(origin)); } @@ -627,7 +615,7 @@ export abstract class BrowserContext extends SdkObject { }); for (const originState of state.origins) { const frame = page.mainFrame(); - await frame.goto(metadata, originState.origin); + await frame.goto(metadata, originState.origin, { timeout: 0 }); const restoreScript = `(() => { const module = {}; ${js.prepareGeneratedScript(rawStorageSource.source)} diff --git a/packages/playwright-core/src/server/browserType.ts b/packages/playwright-core/src/server/browserType.ts index 37388bfb97d49..45e99ce75dc50 100644 --- a/packages/playwright-core/src/server/browserType.ts +++ b/packages/playwright-core/src/server/browserType.ts @@ -19,10 +19,10 @@ import os from 'os'; import path from 'path'; import { normalizeProxySettings, validateBrowserContextOptions } from './browserContext'; -import { DEFAULT_TIMEOUT, TimeoutSettings } from './timeoutSettings'; import { debugMode } from './utils/debug'; import { assert } from '../utils/isomorphic/assert'; import { ManualPromise } from '../utils/isomorphic/manualPromise'; +import { DEFAULT_PLAYWRIGHT_TIMEOUT } from '../utils/isomorphic/time'; import { existsAsync } from './utils/fileUtils'; import { helper } from './helper'; import { SdkObject } from './instrumentation'; @@ -91,11 +91,11 @@ export abstract class BrowserType extends SdkObject { if (seleniumHubUrl) return this._launchWithSeleniumHub(progress, seleniumHubUrl, options); return this._innerLaunchWithRetries(progress, options, undefined, helper.debugProtocolLogger(protocolLogger)).catch(e => { throw this._rewriteStartupLog(e); }); - }, TimeoutSettings.launchTimeout(options)); + }, options.timeout); return browser; } - async launchPersistentContext(metadata: CallMetadata, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { cdpPort?: number, internalIgnoreHTTPSErrors?: boolean }): Promise { + async launchPersistentContext(metadata: CallMetadata, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { timeout: number, cdpPort?: number, internalIgnoreHTTPSErrors?: boolean }): Promise { const launchOptions = this._validateLaunchOptions(options); const controller = new ProgressController(metadata, this); controller.setLogName('browser'); @@ -112,7 +112,7 @@ export abstract class BrowserType extends SdkObject { const browser = await this._innerLaunchWithRetries(progress, launchOptions, options, helper.debugProtocolLogger(), userDataDir).catch(e => { throw this._rewriteStartupLog(e); }); browser._defaultContext!._clientCertificatesProxy = clientCertificatesProxy; return browser; - }, TimeoutSettings.launchTimeout(launchOptions)); + }, launchOptions.timeout); return browser._defaultContext!; } @@ -266,7 +266,7 @@ export abstract class BrowserType extends SdkObject { browserProcess = { onclose: undefined, process: launchedProcess, - close: () => closeOrKill((options as any).__testHookBrowserCloseTimeout || DEFAULT_TIMEOUT), + close: () => closeOrKill((options as any).__testHookBrowserCloseTimeout || DEFAULT_PLAYWRIGHT_TIMEOUT), kill }; progress.cleanupWhenAborted(() => closeOrKill(progress.timeUntilDeadline())); diff --git a/packages/playwright-core/src/server/chromium/chromium.ts b/packages/playwright-core/src/server/chromium/chromium.ts index b3d0fca3335ff..288170a7866ab 100644 --- a/packages/playwright-core/src/server/chromium/chromium.ts +++ b/packages/playwright-core/src/server/chromium/chromium.ts @@ -22,7 +22,6 @@ import path from 'path'; import { chromiumSwitches } from './chromiumSwitches'; import { CRBrowser } from './crBrowser'; import { kBrowserCloseMessageId } from './crConnection'; -import { TimeoutSettings } from '../timeoutSettings'; import { debugMode, headersArrayToObject, headersObjectToArray, } from '../../utils'; import { wrapInASCIIBox } from '../utils/ascii'; import { RecentLogsCollector } from '../utils/debugLogger'; @@ -64,12 +63,12 @@ export class Chromium extends BrowserType { this._devtools = this._createDevTools(); } - override async connectOverCDP(metadata: CallMetadata, endpointURL: string, options: { slowMo?: number, headers?: types.HeadersArray, timeout?: number }) { + override async connectOverCDP(metadata: CallMetadata, endpointURL: string, options: { slowMo?: number, headers?: types.HeadersArray, timeout: number }) { const controller = new ProgressController(metadata, this); controller.setLogName('browser'); return controller.run(async progress => { return await this._connectOverCDPInternal(progress, endpointURL, options); - }, TimeoutSettings.timeout(options)); + }, options.timeout); } async _connectOverCDPInternal(progress: Progress, endpointURL: string, options: types.LaunchOptions & { headers?: types.HeadersArray }, onClose?: () => Promise) { @@ -111,7 +110,7 @@ export class Chromium extends BrowserType { artifactsDir, downloadsPath: options.downloadsPath || artifactsDir, tracesDir: options.tracesDir || artifactsDir, - originalLaunchOptions: {}, + originalLaunchOptions: { timeout: options.timeout }, }; validateBrowserContextOptions(persistent, browserOptions); progress.throwIfAborted(); diff --git a/packages/playwright-core/src/server/debugController.ts b/packages/playwright-core/src/server/debugController.ts index 70a8b82c6cddb..9338b59406dd5 100644 --- a/packages/playwright-core/src/server/debugController.ts +++ b/packages/playwright-core/src/server/debugController.ts @@ -17,7 +17,7 @@ import { SdkObject, createInstrumentation, serverSideCallMetadata } from './instrumentation'; import { gracefullyProcessExitDoNotHang } from './utils/processLauncher'; import { Recorder } from './recorder'; -import { asLocator } from '../utils'; +import { asLocator, DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT, DEFAULT_PLAYWRIGHT_TIMEOUT } from '../utils'; import { parseAriaSnapshotUnsafe } from '../utils/isomorphic/ariaSnapshot'; import { yaml } from '../utilsBundle'; import { EmptyRecorderApp } from './recorder/recorderApp'; @@ -84,7 +84,7 @@ export class DebugController extends SdkObject { async navigate(url: string) { for (const p of this._playwright.allPages()) - await p.mainFrame().goto(internalMetadata, url); + await p.mainFrame().goto(internalMetadata, url, { timeout: DEFAULT_PLAYWRIGHT_TIMEOUT }); } async setRecorderMode(params: { mode: Mode, file?: string, testIdAttributeName?: string }) { @@ -100,7 +100,7 @@ export class DebugController extends SdkObject { } if (!this._playwright.allBrowsers().length) - await this._playwright.chromium.launch(internalMetadata, { headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS }); + await this._playwright.chromium.launch(internalMetadata, { headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS, timeout: DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT }); // Create page if none. const pages = this._playwright.allPages(); if (!pages.length) { diff --git a/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts b/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts index 8f008defbfa6d..3eb2ebfc08813 100644 --- a/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts @@ -35,10 +35,6 @@ export class AndroidDispatcher extends Dispatcher AndroidDeviceDispatcher.from(this, d)) }; } - - async setDefaultTimeoutNoReply(params: channels.AndroidSetDefaultTimeoutNoReplyParams) { - this._object.setDefaultTimeout(params.timeout); - } } export class AndroidDeviceDispatcher extends Dispatcher implements channels.AndroidDeviceChannel { @@ -169,10 +165,6 @@ export class AndroidDeviceDispatcher extends Dispatcher { return { context: new BrowserContextDispatcher(this, await this._object.connectToWebView(params.socketName)) }; } diff --git a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts index fd80ba1eeb8fd..37a8125593b26 100644 --- a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts @@ -200,14 +200,6 @@ export class BrowserContextDispatcher extends Dispatcher { await this._context.exposeBinding(params.name, !!params.needsHandle, (source, ...args) => { // When reusing the context, we might have some bindings called late enough, diff --git a/packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts b/packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts index 7c46400c3f9d8..e13b0e8c60e22 100644 --- a/packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts @@ -17,7 +17,6 @@ import { BrowserContextDispatcher } from './browserContextDispatcher'; import { FrameDispatcher } from './frameDispatcher'; import { JSHandleDispatcher, parseArgument, serializeResult } from './jsHandleDispatcher'; -import { PageDispatcher, WorkerDispatcher } from './pageDispatcher'; import type { ElementHandle } from '../dom'; import type { Frame } from '../frames'; @@ -27,16 +26,16 @@ import type { JSHandleDispatcherParentScope } from './jsHandleDispatcher'; import type * as channels from '@protocol/channels'; -export class ElementHandleDispatcher extends JSHandleDispatcher implements channels.ElementHandleChannel { +export class ElementHandleDispatcher extends JSHandleDispatcher implements channels.ElementHandleChannel { _type_ElementHandle = true; readonly _elementHandle: ElementHandle; - static from(scope: JSHandleDispatcherParentScope, handle: ElementHandle): ElementHandleDispatcher { + static from(scope: FrameDispatcher, handle: ElementHandle): ElementHandleDispatcher { return scope.connection.existingDispatcher(handle) || new ElementHandleDispatcher(scope, handle); } - static fromNullable(scope: JSHandleDispatcherParentScope, handle: ElementHandle | null): ElementHandleDispatcher | undefined { + static fromNullable(scope: FrameDispatcher, handle: ElementHandle | null): ElementHandleDispatcher | undefined { if (!handle) return undefined; return scope.connection.existingDispatcher(handle) || new ElementHandleDispatcher(scope, handle); @@ -46,10 +45,15 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann const result = scope.connection.existingDispatcher(handle); if (result) return result; - return handle.asElement() ? new ElementHandleDispatcher(scope, handle.asElement()!) : new JSHandleDispatcher(scope, handle); + const elementHandle = handle.asElement(); + if (!elementHandle) + return new JSHandleDispatcher(scope, handle); + if (!(scope instanceof FrameDispatcher)) + throw new Error('ElementHandle can only be created from FrameDispatcher'); + return new ElementHandleDispatcher(scope, elementHandle); } - private constructor(scope: JSHandleDispatcherParentScope, elementHandle: ElementHandle) { + private constructor(scope: FrameDispatcher, elementHandle: ElementHandle) { super(scope, elementHandle); this._elementHandle = elementHandle; } @@ -216,18 +220,10 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann } private _browserContextDispatcher(): BrowserContextDispatcher { - const scope = this.parentScope(); - if (scope instanceof BrowserContextDispatcher) - return scope; - if (scope instanceof PageDispatcher) - return scope.parentScope(); - if ((scope instanceof WorkerDispatcher) || (scope instanceof FrameDispatcher)) { - const parentScope = scope.parentScope(); - if (parentScope instanceof BrowserContextDispatcher) - return parentScope; - return parentScope.parentScope(); - } - throw new Error('ElementHandle belongs to unexpected scope'); + const parentScope = this.parentScope().parentScope(); + if (parentScope instanceof BrowserContextDispatcher) + return parentScope; + return parentScope.parentScope(); } } diff --git a/packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts b/packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts index cf492f39b7b0a..4cb929b75fb7a 100644 --- a/packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts @@ -27,10 +27,10 @@ import type * as channels from '@protocol/channels'; export type JSHandleDispatcherParentScope = PageDispatcher | FrameDispatcher | WorkerDispatcher | ElectronApplicationDispatcher; -export class JSHandleDispatcher extends Dispatcher implements channels.JSHandleChannel { +export class JSHandleDispatcher extends Dispatcher implements channels.JSHandleChannel { _type_JSHandle = true; - protected constructor(scope: JSHandleDispatcherParentScope, jsHandle: js.JSHandle) { + protected constructor(scope: ParentScope, jsHandle: js.JSHandle) { // Do not call this directly, use createHandle() instead. super(scope, jsHandle, jsHandle.asElement() ? 'ElementHandle' : 'JSHandle', { preview: jsHandle.toString(), diff --git a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts index 48fe42320fa3d..5a3dc562df4a6 100644 --- a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts @@ -119,7 +119,7 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels. }; pipe.on('close', () => transport.close()); return { pipe, headers: transport.headers }; - }, params.timeout || 0); + }, params.timeout); } async globToRegex(params: channels.LocalUtilsGlobToRegexParams, metadata?: CallMetadata): Promise { @@ -128,11 +128,11 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels. } } -async function urlToWSEndpoint(progress: Progress | undefined, endpointURL: string): Promise { +async function urlToWSEndpoint(progress: Progress, endpointURL: string): Promise { if (endpointURL.startsWith('ws')) return endpointURL; - progress?.log(` retrieving websocket url from ${endpointURL}`); + progress.log(` retrieving websocket url from ${endpointURL}`); const fetchUrl = new URL(endpointURL); if (!fetchUrl.pathname.endsWith('/')) fetchUrl.pathname += '/'; @@ -140,13 +140,13 @@ async function urlToWSEndpoint(progress: Progress | undefined, endpointURL: stri const json = await fetchData({ url: fetchUrl.toString(), method: 'GET', - timeout: progress?.timeUntilDeadline() ?? 30_000, + timeout: progress.timeUntilDeadline(), headers: { 'User-Agent': getUserAgent() }, }, async (params: HTTPRequestParams, response: http.IncomingMessage) => { return new Error(`Unexpected status ${response.statusCode} when connecting to ${fetchUrl.toString()}.\n` + `This does not look like a Playwright server, try connecting via ws://.`); }); - progress?.throwIfAborted(); + progress.throwIfAborted(); const wsUrl = new URL(endpointURL); let wsEndpointPath = JSON.parse(json).wsEndpointPath; diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index 84d0e3f1a00a3..c6f93f9e50b44 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -106,14 +106,6 @@ export class PageDispatcher extends Dispatcher { - this._page.setDefaultNavigationTimeout(params.timeout); - } - - async setDefaultTimeoutNoReply(params: channels.PageSetDefaultTimeoutNoReplyParams, metadata: CallMetadata): Promise { - this._page.setDefaultTimeout(params.timeout); - } - async exposeBinding(params: channels.PageExposeBindingParams, metadata: CallMetadata): Promise { await this._page.exposeBinding(params.name, !!params.needsHandle, (source, ...args) => { // When reusing the context, we might have some bindings called late enough, @@ -373,11 +365,12 @@ export class BindingCallDispatcher extends Dispatcher<{ guid: string }, channels private _promise: Promise; constructor(scope: PageDispatcher, name: string, needsHandle: boolean, source: { context: BrowserContext, page: Page, frame: Frame }, args: any[]) { + const frameDispatcher = FrameDispatcher.from(scope.parentScope(), source.frame); super(scope, { guid: 'bindingCall@' + createGuid() }, 'BindingCall', { - frame: FrameDispatcher.from(scope.parentScope(), source.frame), + frame: frameDispatcher, name, args: needsHandle ? undefined : args.map(serializeResult), - handle: needsHandle ? ElementHandleDispatcher.fromJSHandle(scope, args[0] as JSHandle) : undefined, + handle: needsHandle ? ElementHandleDispatcher.fromJSHandle(frameDispatcher, args[0] as JSHandle) : undefined, }); this._promise = new Promise((resolve, reject) => { this._resolve = resolve; diff --git a/packages/playwright-core/src/server/dom.ts b/packages/playwright-core/src/server/dom.ts index afcf0a087d66e..970e15fbaa7b8 100644 --- a/packages/playwright-core/src/server/dom.ts +++ b/packages/playwright-core/src/server/dom.ts @@ -30,7 +30,6 @@ import type { Page } from './page'; import type { Progress } from './progress'; import type { ScreenshotOptions } from './screenshotter'; import type * as types from './types'; -import type { TimeoutOptions } from '../utils/isomorphic/types'; import type * as channels from '@protocol/channels'; export type InputFilesItems = { @@ -195,27 +194,27 @@ export class ElementHandle extends js.JSHandle { } async getAttribute(metadata: CallMetadata, name: string): Promise { - return this._frame.getAttribute(metadata, ':scope', name, {}, this); + return this._frame.getAttribute(metadata, ':scope', name, { timeout: 0 }, this); } async inputValue(metadata: CallMetadata): Promise { - return this._frame.inputValue(metadata, ':scope', {}, this); + return this._frame.inputValue(metadata, ':scope', { timeout: 0 }, this); } async textContent(metadata: CallMetadata): Promise { - return this._frame.textContent(metadata, ':scope', {}, this); + return this._frame.textContent(metadata, ':scope', { timeout: 0 }, this); } async innerText(metadata: CallMetadata): Promise { - return this._frame.innerText(metadata, ':scope', {}, this); + return this._frame.innerText(metadata, ':scope', { timeout: 0 }, this); } async innerHTML(metadata: CallMetadata): Promise { - return this._frame.innerHTML(metadata, ':scope', {}, this); + return this._frame.innerHTML(metadata, ':scope', { timeout: 0 }, this); } async dispatchEvent(metadata: CallMetadata, type: string, eventInit: Object = {}) { - return this._frame.dispatchEvent(metadata, ':scope', type, eventInit, {}, this); + return this._frame.dispatchEvent(metadata, ':scope', type, eventInit, { timeout: 0 }, this); } async _scrollRectIntoViewIfNeeded(rect?: types.Rect): Promise<'error:notvisible' | 'error:notconnected' | 'done'> { @@ -235,11 +234,11 @@ export class ElementHandle extends js.JSHandle { assertDone(throwRetargetableDOMError(result)); } - async scrollIntoViewIfNeeded(metadata: CallMetadata, options: types.TimeoutOptions = {}) { + async scrollIntoViewIfNeeded(metadata: CallMetadata, options: types.TimeoutOptions) { const controller = new ProgressController(metadata, this); return controller.run( progress => this._waitAndScrollIntoViewIfNeeded(progress, false /* waitForVisible */), - this._page.timeoutSettings.timeout(options)); + options.timeout); } private async _clickablePoint(): Promise { @@ -522,20 +521,20 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._hover(progress, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } _hover(progress: Progress, options: types.PointerActionOptions & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> { return this._retryPointerAction(progress, 'hover', false /* waitForEnabled */, point => this._page.mouse.move(point.x, point.y), { ...options, waitAfter: 'disabled' }); } - async click(metadata: CallMetadata, options: { noWaitAfter?: boolean } & types.MouseClickOptions & types.PointerActionWaitOptions = {}): Promise { + async click(metadata: CallMetadata, options: { noWaitAfter?: boolean } & types.MouseClickOptions & types.PointerActionWaitOptions): Promise { const controller = new ProgressController(metadata, this); return controller.run(async progress => { await this._markAsTargetElement(metadata); const result = await this._click(progress, { ...options, waitAfter: !options.noWaitAfter }); return assertDone(throwRetargetableDOMError(result)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } _click(progress: Progress, options: { waitAfter: boolean | 'disabled' } & types.MouseClickOptions & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -548,20 +547,20 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._dblclick(progress, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } _dblclick(progress: Progress, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> { return this._retryPointerAction(progress, 'dblclick', true /* waitForEnabled */, point => this._page.mouse.dblclick(point.x, point.y, options), { ...options, waitAfter: 'disabled' }); } - async tap(metadata: CallMetadata, options: types.PointerActionWaitOptions = {}): Promise { + async tap(metadata: CallMetadata, options: types.PointerActionWaitOptions): Promise { const controller = new ProgressController(metadata, this); return controller.run(async progress => { await this._markAsTargetElement(metadata); const result = await this._tap(progress, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } _tap(progress: Progress, options: types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -574,7 +573,7 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._selectOption(progress, elements, values, options); return throwRetargetableDOMError(result); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async _selectOption(progress: Progress, elements: ElementHandle[], values: types.SelectOption[], options: types.CommonActionOptions): Promise { @@ -604,13 +603,13 @@ export class ElementHandle extends js.JSHandle { return resultingOptions; } - async fill(metadata: CallMetadata, value: string, options: types.CommonActionOptions = {}): Promise { + async fill(metadata: CallMetadata, value: string, options: types.CommonActionOptions): Promise { const controller = new ProgressController(metadata, this); return controller.run(async progress => { await this._markAsTargetElement(metadata); const result = await this._fill(progress, value, options); assertDone(throwRetargetableDOMError(result)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async _fill(progress: Progress, value: string, options: types.CommonActionOptions): Promise<'error:notconnected' | 'done'> { @@ -640,7 +639,7 @@ export class ElementHandle extends js.JSHandle { }, options); } - async selectText(metadata: CallMetadata, options: types.CommonActionOptions = {}): Promise { + async selectText(metadata: CallMetadata, options: types.CommonActionOptions): Promise { const controller = new ProgressController(metadata, this); return controller.run(async progress => { const result = await this._retryAction(progress, 'selectText', async () => { @@ -656,7 +655,7 @@ export class ElementHandle extends js.JSHandle { }, { force: options.force }); }, options); assertDone(throwRetargetableDOMError(result)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async setInputFiles(metadata: CallMetadata, params: channels.ElementHandleSetInputFilesParams) { @@ -666,7 +665,7 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._setInputFiles(progress, inputFileItems); return assertDone(throwRetargetableDOMError(result)); - }, this._page.timeoutSettings.timeout(params)); + }, params.timeout); } async _setInputFiles(progress: Progress, items: InputFilesItems): Promise<'error:notconnected' | 'done'> { @@ -735,7 +734,7 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._type(progress, text, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async _type(progress: Progress, text: string, options: { delay?: number } & types.TimeoutOptions & types.StrictOptions): Promise<'error:notconnected' | 'done'> { @@ -755,7 +754,7 @@ export class ElementHandle extends js.JSHandle { await this._markAsTargetElement(metadata); const result = await this._press(progress, key, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async _press(progress: Progress, key: string, options: { delay?: number, noWaitAfter?: boolean } & types.TimeoutOptions & types.StrictOptions): Promise<'error:notconnected' | 'done'> { @@ -776,7 +775,7 @@ export class ElementHandle extends js.JSHandle { return controller.run(async progress => { const result = await this._setChecked(progress, true, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async uncheck(metadata: CallMetadata, options: { position?: types.Point } & types.PointerActionWaitOptions) { @@ -784,7 +783,7 @@ export class ElementHandle extends js.JSHandle { return controller.run(async progress => { const result = await this._setChecked(progress, false, options); return assertDone(throwRetargetableDOMError(result)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async _setChecked(progress: Progress, state: boolean, options: { position?: types.Point } & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> { @@ -815,11 +814,11 @@ export class ElementHandle extends js.JSHandle { return await this.evaluateInUtility(([injected, element, options]) => injected.ariaSnapshot(element, options), options); } - async screenshot(metadata: CallMetadata, options: ScreenshotOptions & TimeoutOptions = {}): Promise { + async screenshot(metadata: CallMetadata, options: ScreenshotOptions & types.TimeoutOptions): Promise { const controller = new ProgressController(metadata, this); return controller.run( progress => this._page.screenshotter.screenshotElement(progress, this, options), - this._page.timeoutSettings.timeout(options)); + options.timeout); } async querySelector(selector: string, options: types.StrictOptions): Promise { @@ -847,22 +846,22 @@ export class ElementHandle extends js.JSHandle { } async isEnabled(metadata: CallMetadata): Promise { - return this._frame.isEnabled(metadata, ':scope', {}, this); + return this._frame.isEnabled(metadata, ':scope', { timeout: 0 }, this); } async isDisabled(metadata: CallMetadata): Promise { - return this._frame.isDisabled(metadata, ':scope', {}, this); + return this._frame.isDisabled(metadata, ':scope', { timeout: 0 }, this); } async isEditable(metadata: CallMetadata): Promise { - return this._frame.isEditable(metadata, ':scope', {}, this); + return this._frame.isEditable(metadata, ':scope', { timeout: 0 }, this); } async isChecked(metadata: CallMetadata): Promise { - return this._frame.isChecked(metadata, ':scope', {}, this); + return this._frame.isChecked(metadata, ':scope', { timeout: 0 }, this); } - async waitForElementState(metadata: CallMetadata, state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled' | 'editable', options: types.TimeoutOptions = {}): Promise { + async waitForElementState(metadata: CallMetadata, state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled' | 'editable', options: types.TimeoutOptions): Promise { const controller = new ProgressController(metadata, this); return controller.run(async progress => { const actionName = `wait for ${state}`; @@ -872,10 +871,10 @@ export class ElementHandle extends js.JSHandle { }, state); }, {}); assertDone(throwRetargetableDOMError(result)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } - async waitForSelector(metadata: CallMetadata, selector: string, options: types.WaitForElementOptions = {}): Promise | null> { + async waitForSelector(metadata: CallMetadata, selector: string, options: types.WaitForElementOptions): Promise | null> { return this._frame.waitForSelector(metadata, selector, options, this); } diff --git a/packages/playwright-core/src/server/electron/electron.ts b/packages/playwright-core/src/server/electron/electron.ts index b261860e047c7..1602ca8c79af5 100644 --- a/packages/playwright-core/src/server/electron/electron.ts +++ b/packages/playwright-core/src/server/electron/electron.ts @@ -19,7 +19,6 @@ import os from 'os'; import path from 'path'; import * as readline from 'readline'; -import { TimeoutSettings } from '../timeoutSettings'; import { ManualPromise } from '../../utils'; import { wrapInASCIIBox } from '../utils/ascii'; import { RecentLogsCollector } from '../utils/debugLogger'; @@ -64,7 +63,6 @@ export class ElectronApplication extends SdkObject { private _nodeSession: CRSession; private _nodeExecutionContext: js.ExecutionContext | undefined; _nodeElectronHandlePromise: ManualPromise> = new ManualPromise(); - readonly _timeoutSettings = new TimeoutSettings(); private _process: childProcess.ChildProcess; constructor(parent: SdkObject, browser: CRBrowser, nodeConnection: CRConnection, process: childProcess.ChildProcess) { @@ -284,14 +282,14 @@ export class Electron extends SdkObject { artifactsDir, downloadsPath: artifactsDir, tracesDir: options.tracesDir || artifactsDir, - originalLaunchOptions: {}, + originalLaunchOptions: { timeout: options.timeout }, }; validateBrowserContextOptions(contextOptions, browserOptions); const browser = await CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions); app = new ElectronApplication(this, browser, nodeConnection, launchedProcess); await app.initialize(); return app; - }, TimeoutSettings.launchTimeout(options)); + }, options.timeout); } } diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index 71f9de0833e0b..c5b28c5ae770d 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -20,7 +20,6 @@ import { Transform, pipeline } from 'stream'; import { TLSSocket } from 'tls'; import * as zlib from 'zlib'; -import { TimeoutSettings } from './timeoutSettings'; import { assert, constructURLBasedOnBaseURL, createProxyAgent, eventsHelper, monotonicTime } from '../utils'; import { createGuid } from './utils/crypto'; import { getUserAgent } from './utils/userAgent'; @@ -52,7 +51,6 @@ type FetchRequestOptions = { failOnStatusCode?: boolean; httpCredentials?: HTTPCredentials; proxy?: ProxySettings; - timeoutSettings: TimeoutSettings; ignoreHTTPSErrors?: boolean; maxRedirects?: number; baseURL?: string; @@ -187,7 +185,7 @@ export abstract class APIRequestContext extends SdkObject { let maxRedirects = params.maxRedirects ?? (defaults.maxRedirects ?? 20); maxRedirects = maxRedirects === 0 ? -1 : maxRedirects; - const timeout = defaults.timeoutSettings.timeout(params); + const timeout = params.timeout; const deadline = timeout && (monotonicTime() + timeout); const options: SendRequestOptions = { @@ -612,7 +610,6 @@ export class BrowserContextAPIRequestContext extends APIRequestContext { failOnStatusCode: undefined, httpCredentials: this._context._options.httpCredentials, proxy: this._context._options.proxy || this._context._browser.options.proxy, - timeoutSettings: this._context._timeoutSettings, ignoreHTTPSErrors: this._context._options.ignoreHTTPSErrors, baseURL: this._context._options.baseURL, clientCertificates: this._context._options.clientCertificates, @@ -642,9 +639,6 @@ export class GlobalAPIRequestContext extends APIRequestContext { constructor(playwright: Playwright, options: channels.PlaywrightNewRequestOptions) { super(playwright); this.attribution.context = this; - const timeoutSettings = new TimeoutSettings(); - if (options.timeout !== undefined) - timeoutSettings.setDefaultTimeout(options.timeout); if (options.storageState) { this._origins = options.storageState.origins?.map(origin => ({ indexedDB: [], ...origin })); this._cookieStore.addCookies(options.storageState.cookies || []); @@ -660,7 +654,6 @@ export class GlobalAPIRequestContext extends APIRequestContext { httpCredentials: options.httpCredentials, clientCertificates: options.clientCertificates, proxy: options.proxy, - timeoutSettings, }; this._tracing = new Tracing(this, options.tracesDir); } diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index b45bba2fb83fd..534992d861354 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -644,17 +644,15 @@ export class Frame extends SdkObject { data.gotoPromise.finally(() => this._redirectedNavigations.delete(documentId)); } - async goto(metadata: CallMetadata, url: string, options: types.GotoOptions = {}): Promise { + async goto(metadata: CallMetadata, url: string, options: types.GotoOptions): Promise { const constructedNavigationURL = constructURLBasedOnBaseURL(this._page.browserContext._options.baseURL, url); const controller = new ProgressController(metadata, this); - return controller.run(progress => this._goto(progress, constructedNavigationURL, options), this._page.timeoutSettings.navigationTimeout(options)); + return controller.run(progress => { + return this.raceNavigationAction(progress, options, async () => this._gotoAction(progress, constructedNavigationURL, options)); + }, options.timeout); } - private async _goto(progress: Progress, url: string, options: types.GotoOptions): Promise { - return this.raceNavigationAction(progress, options, async () => this._gotoAction(progress, url, options)); - } - - private async _gotoAction(progress: Progress, url: string, options: types.GotoOptions): Promise { + private async _gotoAction(progress: Progress, url: string, options: Omit): Promise { const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); progress.log(`navigating to "${url}", waiting until "${waitUntil}"`); const headers = this._page.extraHTTPHeaders() || []; @@ -792,10 +790,10 @@ export class Frame extends SdkObject { return controller.run(async progress => { progress.log(`waiting for ${this._asLocator(selector)}${state === 'attached' ? '' : ' to be ' + state}`); return await this.waitForSelectorInternal(progress, selector, true, options, scope); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } - async waitForSelectorInternal(progress: Progress, selector: string, performActionPreChecks: boolean, options: types.WaitForElementOptions, scope?: dom.ElementHandle): Promise | null> { + async waitForSelectorInternal(progress: Progress, selector: string, performActionPreChecks: boolean, options: Omit, scope?: dom.ElementHandle): Promise | null> { const { state = 'visible' } = options; const promise = this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async continuePolling => { if (performActionPreChecks) @@ -851,7 +849,7 @@ export class Frame extends SdkObject { return scope ? scope._context._raceAgainstContextDestroyed(promise) : promise; } - async dispatchEvent(metadata: CallMetadata, selector: string, type: string, eventInit: Object = {}, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { + async dispatchEvent(metadata: CallMetadata, selector: string, type: string, eventInit: Object = {}, options: types.QueryOnSelectorOptions, scope?: dom.ElementHandle): Promise { await this._callOnElementOnceMatches(metadata, selector, (injectedScript, element, data) => { injectedScript.dispatchEvent(element, data.type, data.eventInit); }, { type, eventInit }, { mainWorld: true, ...options }, scope); @@ -907,7 +905,7 @@ export class Frame extends SdkObject { } } - async setContent(metadata: CallMetadata, html: string, options: types.NavigateOptions = {}): Promise { + async setContent(metadata: CallMetadata, html: string, options: types.NavigateOptions): Promise { const controller = new ProgressController(metadata, this); return controller.run(async progress => { await this.raceNavigationAction(progress, options, async () => { @@ -931,7 +929,7 @@ export class Frame extends SdkObject { await Promise.all([contentPromise, lifecyclePromise]); return null; }); - }, this._page.timeoutSettings.navigationTimeout(options)); + }, options.timeout); } name(): string { @@ -1183,17 +1181,17 @@ export class Frame extends SdkObject { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._click(progress, { ...options, waitAfter: !options.noWaitAfter }))); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } - async dblclick(metadata: CallMetadata, selector: string, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions = {}) { + async dblclick(metadata: CallMetadata, selector: string, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._dblclick(progress, options))); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } - async dragAndDrop(metadata: CallMetadata, source: string, target: string, options: types.DragActionOptions & types.PointerActionWaitOptions = {}) { + async dragAndDrop(metadata: CallMetadata, source: string, target: string, options: types.DragActionOptions & types.PointerActionWaitOptions) { const controller = new ProgressController(metadata, this); await controller.run(async progress => { dom.assertDone(await this._retryWithProgressIfNotConnected(progress, source, options.strict, !options.force /* performActionPreChecks */, async handle => { @@ -1219,7 +1217,7 @@ export class Frame extends SdkObject { timeout: progress.timeUntilDeadline(), }); })); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async tap(metadata: CallMetadata, selector: string, options: types.PointerActionWaitOptions) { @@ -1228,35 +1226,35 @@ export class Frame extends SdkObject { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._tap(progress, options))); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async fill(metadata: CallMetadata, selector: string, value: string, options: types.TimeoutOptions & types.StrictOptions & { force?: boolean }) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._fill(progress, value, options))); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } - async focus(metadata: CallMetadata, selector: string, options: types.TimeoutOptions & types.StrictOptions = {}) { + async focus(metadata: CallMetadata, selector: string, options: types.TimeoutOptions & types.StrictOptions) { const controller = new ProgressController(metadata, this); await controller.run(async progress => { dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true /* performActionPreChecks */, handle => handle._focus(progress))); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } - async blur(metadata: CallMetadata, selector: string, options: types.TimeoutOptions & types.StrictOptions = {}) { + async blur(metadata: CallMetadata, selector: string, options: types.TimeoutOptions & types.StrictOptions) { const controller = new ProgressController(metadata, this); await controller.run(async progress => { dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true /* performActionPreChecks */, handle => handle._blur(progress))); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } - async textContent(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { + async textContent(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions, scope?: dom.ElementHandle): Promise { return this._callOnElementOnceMatches(metadata, selector, (injected, element) => element.textContent, undefined, options, scope); } - async innerText(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { + async innerText(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions, scope?: dom.ElementHandle): Promise { return this._callOnElementOnceMatches(metadata, selector, (injectedScript, element) => { if (element.namespaceURI !== 'http://www.w3.org/1999/xhtml') throw injectedScript.createStacklessError('Node is not an HTMLElement'); @@ -1264,15 +1262,15 @@ export class Frame extends SdkObject { }, undefined, options, scope); } - async innerHTML(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { + async innerHTML(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions, scope?: dom.ElementHandle): Promise { return this._callOnElementOnceMatches(metadata, selector, (injected, element) => element.innerHTML, undefined, options, scope); } - async getAttribute(metadata: CallMetadata, selector: string, name: string, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { + async getAttribute(metadata: CallMetadata, selector: string, name: string, options: types.QueryOnSelectorOptions, scope?: dom.ElementHandle): Promise { return this._callOnElementOnceMatches(metadata, selector, (injected, element, data) => element.getAttribute(data.name), { name }, options, scope); } - async inputValue(metadata: CallMetadata, selector: string, options: types.TimeoutOptions & types.StrictOptions = {}, scope?: dom.ElementHandle): Promise { + async inputValue(metadata: CallMetadata, selector: string, options: types.TimeoutOptions & types.StrictOptions, scope?: dom.ElementHandle): Promise { return this._callOnElementOnceMatches(metadata, selector, (injectedScript, node) => { const element = injectedScript.retarget(node, 'follow-label'); if (!element || (element.nodeName !== 'INPUT' && element.nodeName !== 'TEXTAREA' && element.nodeName !== 'SELECT')) @@ -1300,7 +1298,7 @@ export class Frame extends SdkObject { }); } - private async _elementState(metadata: CallMetadata, selector: string, state: ElementStateWithoutStable, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { + private async _elementState(metadata: CallMetadata, selector: string, state: ElementStateWithoutStable, options: types.QueryOnSelectorOptions, scope?: dom.ElementHandle): Promise { const result = await this._callOnElementOnceMatches(metadata, selector, (injected, element, data) => { return injected.elementState(element, data.state); }, { state }, options, scope); @@ -1314,7 +1312,7 @@ export class Frame extends SdkObject { return controller.run(async progress => { progress.log(` checking visibility of ${this._asLocator(selector)}`); return await this.isVisibleInternal(selector, options, scope); - }, this._page.timeoutSettings.timeout({})); + }, 0); // Note: isVisible is a one-shot operation without a timeout. } async isVisibleInternal(selector: string, options: types.StrictOptions = {}, scope?: dom.ElementHandle): Promise { @@ -1338,34 +1336,34 @@ export class Frame extends SdkObject { return !(await this.isVisible(metadata, selector, options, scope)); } - async isDisabled(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { + async isDisabled(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions, scope?: dom.ElementHandle): Promise { return this._elementState(metadata, selector, 'disabled', options, scope); } - async isEnabled(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { + async isEnabled(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions, scope?: dom.ElementHandle): Promise { return this._elementState(metadata, selector, 'enabled', options, scope); } - async isEditable(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { + async isEditable(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions, scope?: dom.ElementHandle): Promise { return this._elementState(metadata, selector, 'editable', options, scope); } - async isChecked(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { + async isChecked(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions, scope?: dom.ElementHandle): Promise { return this._elementState(metadata, selector, 'checked', options, scope); } - async hover(metadata: CallMetadata, selector: string, options: types.PointerActionOptions & types.PointerActionWaitOptions = {}) { + async hover(metadata: CallMetadata, selector: string, options: types.PointerActionOptions & types.PointerActionWaitOptions) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._hover(progress, options))); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } - async selectOption(metadata: CallMetadata, selector: string, elements: dom.ElementHandle[], values: types.SelectOption[], options: types.CommonActionOptions = {}): Promise { + async selectOption(metadata: CallMetadata, selector: string, elements: dom.ElementHandle[], values: types.SelectOption[], options: types.CommonActionOptions): Promise { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._selectOption(progress, elements, values, options)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async setInputFiles(metadata: CallMetadata, selector: string, params: channels.FrameSetInputFilesParams): Promise { @@ -1373,35 +1371,35 @@ export class Frame extends SdkObject { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, params.strict, true /* performActionPreChecks */, handle => handle._setInputFiles(progress, inputFileItems))); - }, this._page.timeoutSettings.timeout(params)); + }, params.timeout); } - async type(metadata: CallMetadata, selector: string, text: string, options: { delay?: number } & types.TimeoutOptions & types.StrictOptions = {}) { + async type(metadata: CallMetadata, selector: string, text: string, options: { delay?: number } & types.TimeoutOptions & types.StrictOptions) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true /* performActionPreChecks */, handle => handle._type(progress, text, options))); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } - async press(metadata: CallMetadata, selector: string, key: string, options: { delay?: number, noWaitAfter?: boolean } & types.TimeoutOptions & types.StrictOptions = {}) { + async press(metadata: CallMetadata, selector: string, key: string, options: { delay?: number, noWaitAfter?: boolean } & types.TimeoutOptions & types.StrictOptions) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true /* performActionPreChecks */, handle => handle._press(progress, key, options))); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } - async check(metadata: CallMetadata, selector: string, options: types.PointerActionWaitOptions = {}) { + async check(metadata: CallMetadata, selector: string, options: types.PointerActionWaitOptions) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._setChecked(progress, true, options))); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } - async uncheck(metadata: CallMetadata, selector: string, options: types.PointerActionWaitOptions = {}) { + async uncheck(metadata: CallMetadata, selector: string, options: types.PointerActionWaitOptions) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performActionPreChecks */, handle => handle._setChecked(progress, false, options))); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async waitForTimeout(metadata: CallMetadata, timeout: number) { @@ -1411,11 +1409,11 @@ export class Frame extends SdkObject { }); } - async ariaSnapshot(metadata: CallMetadata, selector: string, options: { forAI?: boolean } & types.TimeoutOptions = {}): Promise { + async ariaSnapshot(metadata: CallMetadata, selector: string, options: { forAI?: boolean } & types.TimeoutOptions): Promise { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return await this._retryWithProgressIfNotConnected(progress, selector, true /* strict */, true /* performActionPreChecks */, handle => handle.ariaSnapshot(options)); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async expect(metadata: CallMetadata, selector: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }> { @@ -1429,7 +1427,7 @@ export class Frame extends SdkObject { private async _expectImpl(metadata: CallMetadata, selector: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }> { const lastIntermediateResult: { received?: any, isSet: boolean } = { isSet: false }; try { - let timeout = this._page.timeoutSettings.timeout(options); + let timeout = options.timeout; const start = timeout > 0 ? monotonicTime() : 0; // Step 1: perform locator handlers checkpoint with a specified timeout. @@ -1581,7 +1579,7 @@ export class Frame extends SdkObject { progress.cleanupWhenAborted(() => handle.evaluate(h => h.abort()).catch(() => {})); return handle.evaluateHandle(h => h.result); }); - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } async waitForFunctionValueInUtility(progress: Progress, pageFunction: js.Func1) { @@ -1628,7 +1626,7 @@ export class Frame extends SdkObject { this._parentFrame = null; } - private async _callOnElementOnceMatches(metadata: CallMetadata, selector: string, body: ElementCallback, taskData: T, options: types.TimeoutOptions & types.StrictOptions & { mainWorld?: boolean } = {}, scope?: dom.ElementHandle): Promise { + private async _callOnElementOnceMatches(metadata: CallMetadata, selector: string, body: ElementCallback, taskData: T, options: types.TimeoutOptions & types.StrictOptions & { mainWorld?: boolean }, scope?: dom.ElementHandle): Promise { const callbackText = body.toString(); const controller = new ProgressController(metadata, this); return controller.run(async progress => { @@ -1656,7 +1654,7 @@ export class Frame extends SdkObject { return value!; }); return scope ? scope._context._raceAgainstContextDestroyed(promise) : promise; - }, this._page.timeoutSettings.timeout(options)); + }, options.timeout); } private _setContext(world: types.World, context: dom.FrameExecutionContext | null) { diff --git a/packages/playwright-core/src/server/launchApp.ts b/packages/playwright-core/src/server/launchApp.ts index 1652a611c4ffe..5ef69fcb876ac 100644 --- a/packages/playwright-core/src/server/launchApp.ts +++ b/packages/playwright-core/src/server/launchApp.ts @@ -56,6 +56,7 @@ export async function launchApp(browserType: BrowserType, options: { acceptDownloads: options?.persistentContextOptions?.acceptDownloads ?? (isUnderTest() ? 'accept' : 'internal-browser-default'), colorScheme: options?.persistentContextOptions?.colorScheme ?? 'no-override', args, + timeout: 0, // Deliberately no timeout for our apps. }); const [page] = context.pages(); // Chromium on macOS opens a new tab when clicking on the dock icon. diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 501a5b50be822..b0898b3544d94 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -27,7 +27,6 @@ import { SdkObject } from './instrumentation'; import * as js from './javascript'; import { ProgressController } from './progress'; import { Screenshotter, validateScreenshotOptions } from './screenshotter'; -import { TimeoutSettings } from './timeoutSettings'; import { LongStandingScope, assert, trimStringWithEllipsis } from '../utils'; import { asLocator } from '../utils'; import { getComparator } from './utils/comparators'; @@ -44,7 +43,6 @@ import type * as network from './network'; import type { Progress } from './progress'; import type { ScreenshotOptions } from './screenshotter'; import type * as types from './types'; -import type { TimeoutOptions } from '../utils/isomorphic/types'; import type { ImageComparatorOptions } from './utils/comparators'; import type * as channels from '@protocol/channels'; import type { BindingPayload, UtilityScript } from '@injected/utilityScript'; @@ -111,7 +109,7 @@ type EmulatedMedia = { }; type ExpectScreenshotOptions = ImageComparatorOptions & ScreenshotOptions & { - timeout?: number, + timeout: number, expected?: Buffer, isNot?: boolean, locator?: { @@ -148,7 +146,6 @@ export class Page extends SdkObject { readonly keyboard: input.Keyboard; readonly mouse: input.Mouse; readonly touchscreen: input.Touchscreen; - readonly timeoutSettings: TimeoutSettings; readonly delegate: PageDelegate; private _emulatedSize: EmulatedSize | undefined; private _extraHTTPHeaders: types.HeadersArray | undefined; @@ -186,7 +183,6 @@ export class Page extends SdkObject { this.keyboard = new input.Keyboard(delegate.rawKeyboard); this.mouse = new input.Mouse(delegate.rawMouse, this); this.touchscreen = new input.Touchscreen(delegate.rawTouchscreen, this); - this.timeoutSettings = new TimeoutSettings(browserContext._timeoutSettings); this.screenshotter = new Screenshotter(this); this.frameManager = new frames.FrameManager(this); if (delegate.pdf) @@ -259,8 +255,6 @@ export class Page extends SdkObject { } async resetForReuse(metadata: CallMetadata) { - this.setDefaultNavigationTimeout(undefined); - this.setDefaultTimeout(undefined); this._locatorHandlers.clear(); await this._removeExposedBindings(); @@ -269,7 +263,8 @@ export class Page extends SdkObject { await this.setServerRequestInterceptor(undefined); await this.setFileChooserIntercepted(false); // Re-navigate once init scripts are gone. - await this.mainFrame().goto(metadata, 'about:blank'); + // TODO: we should have a timeout for `resetForReuse`. + await this.mainFrame().goto(metadata, 'about:blank', { timeout: 0 }); this._emulatedSize = undefined; this._emulatedMedia = {}; this._extraHTTPHeaders = undefined; @@ -332,14 +327,6 @@ export class Page extends SdkObject { return this.frameManager.frames(); } - setDefaultNavigationTimeout(timeout: number | undefined) { - this.timeoutSettings.setDefaultNavigationTimeout(timeout); - } - - setDefaultTimeout(timeout: number | undefined) { - this.timeoutSettings.setDefaultTimeout(timeout); - } - async exposeBinding(name: string, needsHandle: boolean, playwrightBinding: frames.FunctionWithSource) { if (this._pageBindings.has(name)) throw new Error(`Function "${name}" has been already registered`); @@ -395,7 +382,7 @@ export class Page extends SdkObject { this.delegate.reload(), ]); return response; - }), this.timeoutSettings.navigationTimeout(options)); + }), options.timeout); } async goBack(metadata: CallMetadata, options: types.NavigateOptions): Promise { @@ -415,7 +402,7 @@ export class Page extends SdkObject { if (error) throw error; return response; - }), this.timeoutSettings.navigationTimeout(options)); + }), options.timeout); } async goForward(metadata: CallMetadata, options: types.NavigateOptions): Promise { @@ -435,7 +422,7 @@ export class Page extends SdkObject { if (error) throw error; return response; - }), this.timeoutSettings.navigationTimeout(options)); + }), options.timeout); } requestGC(): Promise { @@ -596,7 +583,7 @@ export class Page extends SdkObject { await this.delegate.updateRequestInterception(); } - async expectScreenshot(metadata: CallMetadata, options: ExpectScreenshotOptions = {}): Promise<{ actual?: Buffer, previous?: Buffer, diff?: Buffer, errorMessage?: string, log?: string[] }> { + async expectScreenshot(metadata: CallMetadata, options: ExpectScreenshotOptions): Promise<{ actual?: Buffer, previous?: Buffer, diff?: Buffer, errorMessage?: string, log?: string[] }> { const locator = options.locator; const rafrafScreenshot = locator ? async (progress: Progress, timeout: number) => { return await locator.frame.rafrafTimeoutScreenshotElementWithProgress(progress, locator.selector, timeout, options || {}); @@ -631,7 +618,7 @@ export class Page extends SdkObject { intermediateResult = { errorMessage: comparatorResult.errorMessage, diff: comparatorResult.diff, actual, previous }; return false; }; - const callTimeout = this.timeoutSettings.timeout(options); + const callTimeout = options.timeout; return controller.run(async progress => { let actual: Buffer | undefined; let previous: Buffer | undefined; @@ -698,11 +685,11 @@ export class Page extends SdkObject { }); } - async screenshot(metadata: CallMetadata, options: ScreenshotOptions & TimeoutOptions = {}): Promise { + async screenshot(metadata: CallMetadata, options: ScreenshotOptions & types.TimeoutOptions): Promise { const controller = new ProgressController(metadata, this); return controller.run( progress => this.screenshotter.screenshotPage(progress, options), - this.timeoutSettings.timeout(options)); + options.timeout); } async close(metadata: CallMetadata, options: { runBeforeUnload?: boolean, reason?: string } = {}) { diff --git a/packages/playwright-core/src/server/recorder/recorderApp.ts b/packages/playwright-core/src/server/recorder/recorderApp.ts index d14c5b01895a6..4276951f33393 100644 --- a/packages/playwright-core/src/server/recorder/recorderApp.ts +++ b/packages/playwright-core/src/server/recorder/recorderApp.ts @@ -90,7 +90,7 @@ export class RecorderApp extends EventEmitter implements IRecorderApp { }); const mainFrame = this._page.mainFrame(); - await mainFrame.goto(serverSideCallMetadata(), process.env.PW_HMR ? 'http://localhost:44225' : 'https://playwright/index.html'); + await mainFrame.goto(serverSideCallMetadata(), process.env.PW_HMR ? 'http://localhost:44225' : 'https://playwright/index.html', { timeout: 0 }); } static factory(context: BrowserContext): IRecorderAppFactory { @@ -117,6 +117,7 @@ export class RecorderApp extends EventEmitter implements IRecorderApp { executablePath: inspectedContext._browser.options.isChromium ? inspectedContext._browser.options.customExecutablePath : undefined, // Use the same channel as the inspected context to guarantee that the browser is installed. channel: inspectedContext._browser.options.isChromium ? inspectedContext._browser.options.channel : undefined, + timeout: 0, } }); const controller = new ProgressController(serverSideCallMetadata(), context._browser); diff --git a/packages/playwright-core/src/server/registry/index.ts b/packages/playwright-core/src/server/registry/index.ts index 4cbd7b5c89c61..be066ecb5feeb 100644 --- a/packages/playwright-core/src/server/registry/index.ts +++ b/packages/playwright-core/src/server/registry/index.ts @@ -27,7 +27,7 @@ import { calculateSha1, getAsBooleanFromENV, getFromENV, getPackageManagerExecCo import { wrapInASCIIBox } from '../utils/ascii'; import { debugLogger } from '../utils/debugLogger'; import { hostPlatform, isOfficiallySupportedPlatform } from '../utils/hostPlatform'; -import { fetchData } from '../utils/network'; +import { fetchData, NET_DEFAULT_TIMEOUT } from '../utils/network'; import { spawnAsync } from '../utils/spawnAsync'; import { getEmbedderName } from '../utils/userAgent'; import { lockfile } from '../../utilsBundle'; @@ -1192,7 +1192,7 @@ export class Registry { // PLAYWRIGHT_DOWNLOAD_CONNECTION_TIMEOUT is a misnomer, it actually controls the socket's // max idle timeout. Unfortunately, we cannot rename it without breaking existing user workflows. const downloadSocketTimeoutEnv = getFromENV('PLAYWRIGHT_DOWNLOAD_CONNECTION_TIMEOUT'); - const downloadSocketTimeout = +(downloadSocketTimeoutEnv || '0') || 30_000; + const downloadSocketTimeout = +(downloadSocketTimeoutEnv || '0') || NET_DEFAULT_TIMEOUT; await downloadBrowserWithProgressBar(title, descriptor.dir, executablePath, downloadURLs, downloadFileName, downloadSocketTimeout).catch(e => { throw new Error(`Failed to download ${title}, caused by\n${e.stack}`); }); diff --git a/packages/playwright-core/src/server/timeoutSettings.ts b/packages/playwright-core/src/server/timeoutSettings.ts deleted file mode 100644 index 1c71437e2a382..0000000000000 --- a/packages/playwright-core/src/server/timeoutSettings.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2019 Google Inc. All rights reserved. - * Modifications copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { debugMode } from './utils/debug'; - -// Keep in sync with client. -export const DEFAULT_TIMEOUT = 30000; -export const DEFAULT_LAUNCH_TIMEOUT = 3 * 60 * 1000; // 3 minutes - -export class TimeoutSettings { - private _parent: TimeoutSettings | undefined; - private _defaultTimeout: number | undefined; - private _defaultNavigationTimeout: number | undefined; - - constructor(parent?: TimeoutSettings) { - this._parent = parent; - } - - setDefaultTimeout(timeout: number | undefined) { - this._defaultTimeout = timeout; - } - - setDefaultNavigationTimeout(timeout: number | undefined) { - this._defaultNavigationTimeout = timeout; - } - - defaultNavigationTimeout() { - return this._defaultNavigationTimeout; - } - - defaultTimeout() { - return this._defaultTimeout; - } - - navigationTimeout(options: { timeout?: number }): number { - if (typeof options.timeout === 'number') - return options.timeout; - if (this._defaultNavigationTimeout !== undefined) - return this._defaultNavigationTimeout; - if (debugMode()) - return 0; - if (this._defaultTimeout !== undefined) - return this._defaultTimeout; - if (this._parent) - return this._parent.navigationTimeout(options); - return DEFAULT_TIMEOUT; - } - - timeout(options: { timeout?: number }): number { - if (typeof options.timeout === 'number') - return options.timeout; - if (debugMode()) - return 0; - if (this._defaultTimeout !== undefined) - return this._defaultTimeout; - if (this._parent) - return this._parent.timeout(options); - return DEFAULT_TIMEOUT; - } - - static timeout(options: { timeout?: number }): number { - if (typeof options.timeout === 'number') - return options.timeout; - if (debugMode()) - return 0; - return DEFAULT_TIMEOUT; - } - - static launchTimeout(options: { timeout?: number }): number { - if (typeof options.timeout === 'number') - return options.timeout; - if (debugMode()) - return 0; - return DEFAULT_LAUNCH_TIMEOUT; - } -} diff --git a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts index 1549839b7ab79..7904125c61171 100644 --- a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts +++ b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts @@ -177,6 +177,7 @@ export async function openTraceViewerApp(url: string, browserName: string, optio cdpPort: isUnderTest() ? 0 : undefined, headless: !!options?.headless, colorScheme: isUnderTest() ? 'light' : undefined, + timeout: 0, }, }); @@ -194,7 +195,7 @@ export async function openTraceViewerApp(url: string, browserName: string, optio if (isUnderTest()) page.on('close', () => context.close({ reason: 'Trace viewer closed' }).catch(() => {})); - await page.mainFrame().goto(serverSideCallMetadata(), url); + await page.mainFrame().goto(serverSideCallMetadata(), url, { timeout: 0 }); return page; } diff --git a/packages/playwright-core/src/server/transport.ts b/packages/playwright-core/src/server/transport.ts index 8e87301b78654..e95ec8eaee429 100644 --- a/packages/playwright-core/src/server/transport.ts +++ b/packages/playwright-core/src/server/transport.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { makeWaitForNextTask } from '../utils'; +import { DEFAULT_PLAYWRIGHT_TIMEOUT, makeWaitForNextTask } from '../utils'; import { httpHappyEyeballsAgent, httpsHappyEyeballsAgent } from './utils/happyEyeballs'; import { ws } from '../utilsBundle'; @@ -139,7 +139,7 @@ export class WebSocketTransport implements ConnectionTransport { this._ws = new ws(url, [], { maxPayload: 256 * 1024 * 1024, // 256Mb, // Prevent internal http client error when passing negative timeout. - handshakeTimeout: Math.max(progress?.timeUntilDeadline() ?? 30_000, 1), + handshakeTimeout: Math.max(progress?.timeUntilDeadline() ?? DEFAULT_PLAYWRIGHT_TIMEOUT, 1), headers: options.headers, followRedirects: options.followRedirects, agent: (/^(https|wss):\/\//.test(url)) ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent, diff --git a/packages/playwright-core/src/server/types.ts b/packages/playwright-core/src/server/types.ts index 8cfdf008ef18f..3b97aabbf81fe 100644 --- a/packages/playwright-core/src/server/types.ts +++ b/packages/playwright-core/src/server/types.ts @@ -15,10 +15,12 @@ * limitations under the License. */ -import type { HeadersArray, Point, Size, TimeoutOptions } from '../utils/isomorphic/types'; -export type { HeadersArray, Point, Quad, Rect, Size, TimeoutOptions } from '../utils/isomorphic/types'; +import type { HeadersArray, Point, Size } from '../utils/isomorphic/types'; +export type { HeadersArray, Point, Quad, Rect, Size } from '../utils/isomorphic/types'; import type * as channels from '@protocol/channels'; +export type TimeoutOptions = { timeout: number }; + export type StrictOptions = { strict?: boolean, }; @@ -152,7 +154,7 @@ export type NormalizedContinueOverrides = { export type EmulatedSize = { viewport: Size, screen: Size }; -export type LaunchOptions = channels.BrowserTypeLaunchOptions & { +export type LaunchOptions = channels.BrowserTypeLaunchParams & { cdpPort?: number, proxyOverride?: ProxySettings, assistantMode?: boolean, diff --git a/packages/playwright-core/src/utils/isomorphic/time.ts b/packages/playwright-core/src/utils/isomorphic/time.ts index 55cb4048be0ea..5db2c621f5bcb 100644 --- a/packages/playwright-core/src/utils/isomorphic/time.ts +++ b/packages/playwright-core/src/utils/isomorphic/time.ts @@ -29,3 +29,6 @@ export function timeOrigin(): number { export function monotonicTime(): number { return Math.floor((performance.now() + _timeShift) * 1000) / 1000; } + +export const DEFAULT_PLAYWRIGHT_TIMEOUT = 30_000; +export const DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT = 3 * 60 * 1000; // 3 minutes diff --git a/packages/playwright-core/src/utils/isomorphic/types.ts b/packages/playwright-core/src/utils/isomorphic/types.ts index f71d4237cdc5c..8bf7df0614d1c 100644 --- a/packages/playwright-core/src/utils/isomorphic/types.ts +++ b/packages/playwright-core/src/utils/isomorphic/types.ts @@ -18,6 +18,5 @@ export type Size = { width: number, height: number }; export type Point = { x: number, y: number }; export type Rect = Size & Point; export type Quad = [ Point, Point, Point, Point ]; -export type TimeoutOptions = { timeout?: number }; export type NameValue = { name: string, value: string }; export type HeadersArray = NameValue[]; diff --git a/packages/playwright/src/runner/testServer.ts b/packages/playwright/src/runner/testServer.ts index ef05a7c99d310..1f42186d95a09 100644 --- a/packages/playwright/src/runner/testServer.ts +++ b/packages/playwright/src/runner/testServer.ts @@ -458,6 +458,7 @@ export async function runUIMode(configFile: string | undefined, configCLIOverrid persistentContextOptions: { handleSIGINT: false, channel, + timeout: 0, }, }); page.on('close', () => cancelPromise.resolve()); diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index 3ac6b9a9a9b10..f91682f2925fd 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -364,7 +364,7 @@ export type APIRequestContextFetchParams = { jsonData?: string, formData?: NameValue[], multipartData?: FormField[], - timeout?: number, + timeout: number, failOnStatusCode?: boolean, ignoreHTTPSErrors?: boolean, maxRedirects?: number, @@ -379,7 +379,6 @@ export type APIRequestContextFetchOptions = { jsonData?: string, formData?: NameValue[], multipartData?: FormField[], - timeout?: number, failOnStatusCode?: boolean, ignoreHTTPSErrors?: boolean, maxRedirects?: number, @@ -539,14 +538,13 @@ export type LocalUtilsConnectParams = { headers?: any, exposeNetwork?: string, slowMo?: number, - timeout?: number, + timeout: number, socksProxyRedirectPortForTest?: number, }; export type LocalUtilsConnectOptions = { headers?: any, exposeNetwork?: string, slowMo?: number, - timeout?: number, socksProxyRedirectPortForTest?: number, }; export type LocalUtilsConnectResult = { @@ -661,7 +659,6 @@ export type PlaywrightNewRequestParams = { username?: string, password?: string, }, - timeout?: number, storageState?: { cookies?: NetworkCookie[], origins?: SetOriginStorage[], @@ -694,7 +691,6 @@ export type PlaywrightNewRequestOptions = { username?: string, password?: string, }, - timeout?: number, storageState?: { cookies?: NetworkCookie[], origins?: SetOriginStorage[], @@ -953,7 +949,7 @@ export type BrowserTypeLaunchParams = { handleSIGINT?: boolean, handleSIGTERM?: boolean, handleSIGHUP?: boolean, - timeout?: number, + timeout: number, env?: NameValue[], headless?: boolean, devtools?: boolean, @@ -980,7 +976,6 @@ export type BrowserTypeLaunchOptions = { handleSIGINT?: boolean, handleSIGTERM?: boolean, handleSIGHUP?: boolean, - timeout?: number, env?: NameValue[], headless?: boolean, devtools?: boolean, @@ -1010,7 +1005,7 @@ export type BrowserTypeLaunchPersistentContextParams = { handleSIGINT?: boolean, handleSIGTERM?: boolean, handleSIGHUP?: boolean, - timeout?: number, + timeout: number, env?: NameValue[], headless?: boolean, devtools?: boolean, @@ -1093,7 +1088,6 @@ export type BrowserTypeLaunchPersistentContextOptions = { handleSIGINT?: boolean, handleSIGTERM?: boolean, handleSIGHUP?: boolean, - timeout?: number, env?: NameValue[], headless?: boolean, devtools?: boolean, @@ -1172,12 +1166,11 @@ export type BrowserTypeConnectOverCDPParams = { endpointURL: string, headers?: NameValue[], slowMo?: number, - timeout?: number, + timeout: number, }; export type BrowserTypeConnectOverCDPOptions = { headers?: NameValue[], slowMo?: number, - timeout?: number, }; export type BrowserTypeConnectOverCDPResult = { browser: BrowserChannel, @@ -1589,8 +1582,6 @@ export interface BrowserContextChannel extends BrowserContextEventTarget, EventT exposeBinding(params: BrowserContextExposeBindingParams, metadata?: CallMetadata): Promise; grantPermissions(params: BrowserContextGrantPermissionsParams, metadata?: CallMetadata): Promise; newPage(params?: BrowserContextNewPageParams, metadata?: CallMetadata): Promise; - setDefaultNavigationTimeoutNoReply(params: BrowserContextSetDefaultNavigationTimeoutNoReplyParams, metadata?: CallMetadata): Promise; - setDefaultTimeoutNoReply(params: BrowserContextSetDefaultTimeoutNoReplyParams, metadata?: CallMetadata): Promise; setExtraHTTPHeaders(params: BrowserContextSetExtraHTTPHeadersParams, metadata?: CallMetadata): Promise; setGeolocation(params: BrowserContextSetGeolocationParams, metadata?: CallMetadata): Promise; setHTTPCredentials(params: BrowserContextSetHTTPCredentialsParams, metadata?: CallMetadata): Promise; @@ -1750,20 +1741,6 @@ export type BrowserContextNewPageOptions = {}; export type BrowserContextNewPageResult = { page: PageChannel, }; -export type BrowserContextSetDefaultNavigationTimeoutNoReplyParams = { - timeout?: number, -}; -export type BrowserContextSetDefaultNavigationTimeoutNoReplyOptions = { - timeout?: number, -}; -export type BrowserContextSetDefaultNavigationTimeoutNoReplyResult = void; -export type BrowserContextSetDefaultTimeoutNoReplyParams = { - timeout?: number, -}; -export type BrowserContextSetDefaultTimeoutNoReplyOptions = { - timeout?: number, -}; -export type BrowserContextSetDefaultTimeoutNoReplyResult = void; export type BrowserContextSetExtraHTTPHeadersParams = { headers: NameValue[], }; @@ -2026,8 +2003,6 @@ export interface PageEventTarget { } export interface PageChannel extends PageEventTarget, EventTargetChannel { _type_Page: boolean; - setDefaultNavigationTimeoutNoReply(params: PageSetDefaultNavigationTimeoutNoReplyParams, metadata?: CallMetadata): Promise; - setDefaultTimeoutNoReply(params: PageSetDefaultTimeoutNoReplyParams, metadata?: CallMetadata): Promise; addInitScript(params: PageAddInitScriptParams, metadata?: CallMetadata): Promise; close(params: PageCloseParams, metadata?: CallMetadata): Promise; emulateMedia(params: PageEmulateMediaParams, metadata?: CallMetadata): Promise; @@ -2110,20 +2085,6 @@ export type PageWebSocketEvent = { export type PageWorkerEvent = { worker: WorkerChannel, }; -export type PageSetDefaultNavigationTimeoutNoReplyParams = { - timeout?: number, -}; -export type PageSetDefaultNavigationTimeoutNoReplyOptions = { - timeout?: number, -}; -export type PageSetDefaultNavigationTimeoutNoReplyResult = void; -export type PageSetDefaultTimeoutNoReplyParams = { - timeout?: number, -}; -export type PageSetDefaultTimeoutNoReplyOptions = { - timeout?: number, -}; -export type PageSetDefaultTimeoutNoReplyResult = void; export type PageAddInitScriptParams = { source: string, }; @@ -2164,22 +2125,20 @@ export type PageExposeBindingOptions = { }; export type PageExposeBindingResult = void; export type PageGoBackParams = { - timeout?: number, + timeout: number, waitUntil?: LifecycleEvent, }; export type PageGoBackOptions = { - timeout?: number, waitUntil?: LifecycleEvent, }; export type PageGoBackResult = { response?: ResponseChannel, }; export type PageGoForwardParams = { - timeout?: number, + timeout: number, waitUntil?: LifecycleEvent, }; export type PageGoForwardOptions = { - timeout?: number, waitUntil?: LifecycleEvent, }; export type PageGoForwardResult = { @@ -2214,11 +2173,10 @@ export type PageUnregisterLocatorHandlerOptions = { }; export type PageUnregisterLocatorHandlerResult = void; export type PageReloadParams = { - timeout?: number, + timeout: number, waitUntil?: LifecycleEvent, }; export type PageReloadOptions = { - timeout?: number, waitUntil?: LifecycleEvent, }; export type PageReloadResult = { @@ -2281,7 +2239,7 @@ export type PageExpectScreenshotResult = { log?: string[], }; export type PageScreenshotParams = { - timeout?: number, + timeout: number, type?: 'png' | 'jpeg', quality?: number, fullPage?: boolean, @@ -2298,7 +2256,6 @@ export type PageScreenshotParams = { style?: string, }; export type PageScreenshotOptions = { - timeout?: number, type?: 'png' | 'jpeg', quality?: number, fullPage?: boolean, @@ -2710,11 +2667,10 @@ export type FrameAddStyleTagResult = { export type FrameAriaSnapshotParams = { selector: string, forAI?: boolean, - timeout?: number, + timeout: number, }; export type FrameAriaSnapshotOptions = { forAI?: boolean, - timeout?: number, }; export type FrameAriaSnapshotResult = { snapshot: string, @@ -2722,11 +2678,10 @@ export type FrameAriaSnapshotResult = { export type FrameBlurParams = { selector: string, strict?: boolean, - timeout?: number, + timeout: number, }; export type FrameBlurOptions = { strict?: boolean, - timeout?: number, }; export type FrameBlurResult = void; export type FrameCheckParams = { @@ -2734,14 +2689,13 @@ export type FrameCheckParams = { strict?: boolean, force?: boolean, position?: Point, - timeout?: number, + timeout: number, trial?: boolean, }; export type FrameCheckOptions = { strict?: boolean, force?: boolean, position?: Point, - timeout?: number, trial?: boolean, }; export type FrameCheckResult = void; @@ -2755,7 +2709,7 @@ export type FrameClickParams = { delay?: number, button?: 'left' | 'right' | 'middle', clickCount?: number, - timeout?: number, + timeout: number, trial?: boolean, }; export type FrameClickOptions = { @@ -2767,7 +2721,6 @@ export type FrameClickOptions = { delay?: number, button?: 'left' | 'right' | 'middle', clickCount?: number, - timeout?: number, trial?: boolean, }; export type FrameClickResult = void; @@ -2780,7 +2733,7 @@ export type FrameDragAndDropParams = { source: string, target: string, force?: boolean, - timeout?: number, + timeout: number, trial?: boolean, sourcePosition?: Point, targetPosition?: Point, @@ -2788,7 +2741,6 @@ export type FrameDragAndDropParams = { }; export type FrameDragAndDropOptions = { force?: boolean, - timeout?: number, trial?: boolean, sourcePosition?: Point, targetPosition?: Point, @@ -2803,7 +2755,7 @@ export type FrameDblclickParams = { position?: Point, delay?: number, button?: 'left' | 'right' | 'middle', - timeout?: number, + timeout: number, trial?: boolean, }; export type FrameDblclickOptions = { @@ -2813,7 +2765,6 @@ export type FrameDblclickOptions = { position?: Point, delay?: number, button?: 'left' | 'right' | 'middle', - timeout?: number, trial?: boolean, }; export type FrameDblclickResult = void; @@ -2822,11 +2773,10 @@ export type FrameDispatchEventParams = { strict?: boolean, type: string, eventInit: SerializedArgument, - timeout?: number, + timeout: number, }; export type FrameDispatchEventOptions = { strict?: boolean, - timeout?: number, }; export type FrameDispatchEventResult = void; export type FrameEvaluateExpressionParams = { @@ -2856,22 +2806,20 @@ export type FrameFillParams = { strict?: boolean, value: string, force?: boolean, - timeout?: number, + timeout: number, }; export type FrameFillOptions = { strict?: boolean, force?: boolean, - timeout?: number, }; export type FrameFillResult = void; export type FrameFocusParams = { selector: string, strict?: boolean, - timeout?: number, + timeout: number, }; export type FrameFocusOptions = { strict?: boolean, - timeout?: number, }; export type FrameFocusResult = void; export type FrameFrameElementParams = {}; @@ -2890,23 +2838,21 @@ export type FrameGetAttributeParams = { selector: string, strict?: boolean, name: string, - timeout?: number, + timeout: number, }; export type FrameGetAttributeOptions = { strict?: boolean, - timeout?: number, }; export type FrameGetAttributeResult = { value?: string, }; export type FrameGotoParams = { url: string, - timeout?: number, + timeout: number, waitUntil?: LifecycleEvent, referer?: string, }; export type FrameGotoOptions = { - timeout?: number, waitUntil?: LifecycleEvent, referer?: string, }; @@ -2919,7 +2865,7 @@ export type FrameHoverParams = { force?: boolean, modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, - timeout?: number, + timeout: number, trial?: boolean, }; export type FrameHoverOptions = { @@ -2927,18 +2873,16 @@ export type FrameHoverOptions = { force?: boolean, modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, - timeout?: number, trial?: boolean, }; export type FrameHoverResult = void; export type FrameInnerHTMLParams = { selector: string, strict?: boolean, - timeout?: number, + timeout: number, }; export type FrameInnerHTMLOptions = { strict?: boolean, - timeout?: number, }; export type FrameInnerHTMLResult = { value: string, @@ -2946,11 +2890,10 @@ export type FrameInnerHTMLResult = { export type FrameInnerTextParams = { selector: string, strict?: boolean, - timeout?: number, + timeout: number, }; export type FrameInnerTextOptions = { strict?: boolean, - timeout?: number, }; export type FrameInnerTextResult = { value: string, @@ -2958,11 +2901,10 @@ export type FrameInnerTextResult = { export type FrameInputValueParams = { selector: string, strict?: boolean, - timeout?: number, + timeout: number, }; export type FrameInputValueOptions = { strict?: boolean, - timeout?: number, }; export type FrameInputValueResult = { value: string, @@ -2970,11 +2912,10 @@ export type FrameInputValueResult = { export type FrameIsCheckedParams = { selector: string, strict?: boolean, - timeout?: number, + timeout: number, }; export type FrameIsCheckedOptions = { strict?: boolean, - timeout?: number, }; export type FrameIsCheckedResult = { value: boolean, @@ -2982,11 +2923,10 @@ export type FrameIsCheckedResult = { export type FrameIsDisabledParams = { selector: string, strict?: boolean, - timeout?: number, + timeout: number, }; export type FrameIsDisabledOptions = { strict?: boolean, - timeout?: number, }; export type FrameIsDisabledResult = { value: boolean, @@ -2994,11 +2934,10 @@ export type FrameIsDisabledResult = { export type FrameIsEnabledParams = { selector: string, strict?: boolean, - timeout?: number, + timeout: number, }; export type FrameIsEnabledOptions = { strict?: boolean, - timeout?: number, }; export type FrameIsEnabledResult = { value: boolean, @@ -3026,11 +2965,10 @@ export type FrameIsVisibleResult = { export type FrameIsEditableParams = { selector: string, strict?: boolean, - timeout?: number, + timeout: number, }; export type FrameIsEditableOptions = { strict?: boolean, - timeout?: number, }; export type FrameIsEditableResult = { value: boolean, @@ -3041,13 +2979,12 @@ export type FramePressParams = { key: string, delay?: number, noWaitAfter?: boolean, - timeout?: number, + timeout: number, }; export type FramePressOptions = { strict?: boolean, delay?: number, noWaitAfter?: boolean, - timeout?: number, }; export type FramePressResult = void; export type FrameQuerySelectorParams = { @@ -3089,7 +3026,7 @@ export type FrameSelectOptionParams = { index?: number, }[], force?: boolean, - timeout?: number, + timeout: number, }; export type FrameSelectOptionOptions = { strict?: boolean, @@ -3101,18 +3038,16 @@ export type FrameSelectOptionOptions = { index?: number, }[], force?: boolean, - timeout?: number, }; export type FrameSelectOptionResult = { values: string[], }; export type FrameSetContentParams = { html: string, - timeout?: number, + timeout: number, waitUntil?: LifecycleEvent, }; export type FrameSetContentOptions = { - timeout?: number, waitUntil?: LifecycleEvent, }; export type FrameSetContentResult = void; @@ -3128,7 +3063,7 @@ export type FrameSetInputFilesParams = { directoryStream?: WritableStreamChannel, localPaths?: string[], streams?: WritableStreamChannel[], - timeout?: number, + timeout: number, }; export type FrameSetInputFilesOptions = { strict?: boolean, @@ -3141,7 +3076,6 @@ export type FrameSetInputFilesOptions = { directoryStream?: WritableStreamChannel, localPaths?: string[], streams?: WritableStreamChannel[], - timeout?: number, }; export type FrameSetInputFilesResult = void; export type FrameTapParams = { @@ -3150,7 +3084,7 @@ export type FrameTapParams = { force?: boolean, modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, - timeout?: number, + timeout: number, trial?: boolean, }; export type FrameTapOptions = { @@ -3158,18 +3092,16 @@ export type FrameTapOptions = { force?: boolean, modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, - timeout?: number, trial?: boolean, }; export type FrameTapResult = void; export type FrameTextContentParams = { selector: string, strict?: boolean, - timeout?: number, + timeout: number, }; export type FrameTextContentOptions = { strict?: boolean, - timeout?: number, }; export type FrameTextContentResult = { value?: string, @@ -3184,12 +3116,11 @@ export type FrameTypeParams = { strict?: boolean, text: string, delay?: number, - timeout?: number, + timeout: number, }; export type FrameTypeOptions = { strict?: boolean, delay?: number, - timeout?: number, }; export type FrameTypeResult = void; export type FrameUncheckParams = { @@ -3197,14 +3128,13 @@ export type FrameUncheckParams = { strict?: boolean, force?: boolean, position?: Point, - timeout?: number, + timeout: number, trial?: boolean, }; export type FrameUncheckOptions = { strict?: boolean, force?: boolean, position?: Point, - timeout?: number, trial?: boolean, }; export type FrameUncheckResult = void; @@ -3219,12 +3149,11 @@ export type FrameWaitForFunctionParams = { expression: string, isFunction?: boolean, arg: SerializedArgument, - timeout?: number, + timeout: number, pollingInterval?: number, }; export type FrameWaitForFunctionOptions = { isFunction?: boolean, - timeout?: number, pollingInterval?: number, }; export type FrameWaitForFunctionResult = { @@ -3233,13 +3162,12 @@ export type FrameWaitForFunctionResult = { export type FrameWaitForSelectorParams = { selector: string, strict?: boolean, - timeout?: number, + timeout: number, state?: 'attached' | 'detached' | 'visible' | 'hidden', omitReturnValue?: boolean, }; export type FrameWaitForSelectorOptions = { strict?: boolean, - timeout?: number, state?: 'attached' | 'detached' | 'visible' | 'hidden', omitReturnValue?: boolean, }; @@ -3465,13 +3393,12 @@ export type ElementHandleBoundingBoxResult = { export type ElementHandleCheckParams = { force?: boolean, position?: Point, - timeout?: number, + timeout: number, trial?: boolean, }; export type ElementHandleCheckOptions = { force?: boolean, position?: Point, - timeout?: number, trial?: boolean, }; export type ElementHandleCheckResult = void; @@ -3483,7 +3410,7 @@ export type ElementHandleClickParams = { delay?: number, button?: 'left' | 'right' | 'middle', clickCount?: number, - timeout?: number, + timeout: number, trial?: boolean, }; export type ElementHandleClickOptions = { @@ -3494,7 +3421,6 @@ export type ElementHandleClickOptions = { delay?: number, button?: 'left' | 'right' | 'middle', clickCount?: number, - timeout?: number, trial?: boolean, }; export type ElementHandleClickResult = void; @@ -3509,7 +3435,7 @@ export type ElementHandleDblclickParams = { position?: Point, delay?: number, button?: 'left' | 'right' | 'middle', - timeout?: number, + timeout: number, trial?: boolean, }; export type ElementHandleDblclickOptions = { @@ -3518,7 +3444,6 @@ export type ElementHandleDblclickOptions = { position?: Point, delay?: number, button?: 'left' | 'right' | 'middle', - timeout?: number, trial?: boolean, }; export type ElementHandleDblclickResult = void; @@ -3533,11 +3458,10 @@ export type ElementHandleDispatchEventResult = void; export type ElementHandleFillParams = { value: string, force?: boolean, - timeout?: number, + timeout: number, }; export type ElementHandleFillOptions = { force?: boolean, - timeout?: number, }; export type ElementHandleFillResult = void; export type ElementHandleFocusParams = {}; @@ -3561,14 +3485,13 @@ export type ElementHandleHoverParams = { force?: boolean, modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, - timeout?: number, + timeout: number, trial?: boolean, }; export type ElementHandleHoverOptions = { force?: boolean, modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, - timeout?: number, trial?: boolean, }; export type ElementHandleHoverResult = void; @@ -3625,12 +3548,11 @@ export type ElementHandleOwnerFrameResult = { export type ElementHandlePressParams = { key: string, delay?: number, - timeout?: number, + timeout: number, noWaitAfter?: boolean, }; export type ElementHandlePressOptions = { delay?: number, - timeout?: number, noWaitAfter?: boolean, }; export type ElementHandlePressResult = void; @@ -3654,7 +3576,7 @@ export type ElementHandleQuerySelectorAllResult = { elements: ElementHandleChannel[], }; export type ElementHandleScreenshotParams = { - timeout?: number, + timeout: number, type?: 'png' | 'jpeg', quality?: number, omitBackground?: boolean, @@ -3669,7 +3591,6 @@ export type ElementHandleScreenshotParams = { style?: string, }; export type ElementHandleScreenshotOptions = { - timeout?: number, type?: 'png' | 'jpeg', quality?: number, omitBackground?: boolean, @@ -3687,10 +3608,10 @@ export type ElementHandleScreenshotResult = { binary: Binary, }; export type ElementHandleScrollIntoViewIfNeededParams = { - timeout?: number, + timeout: number, }; export type ElementHandleScrollIntoViewIfNeededOptions = { - timeout?: number, + }; export type ElementHandleScrollIntoViewIfNeededResult = void; export type ElementHandleSelectOptionParams = { @@ -3702,7 +3623,7 @@ export type ElementHandleSelectOptionParams = { index?: number, }[], force?: boolean, - timeout?: number, + timeout: number, }; export type ElementHandleSelectOptionOptions = { elements?: ElementHandleChannel[], @@ -3713,18 +3634,16 @@ export type ElementHandleSelectOptionOptions = { index?: number, }[], force?: boolean, - timeout?: number, }; export type ElementHandleSelectOptionResult = { values: string[], }; export type ElementHandleSelectTextParams = { force?: boolean, - timeout?: number, + timeout: number, }; export type ElementHandleSelectTextOptions = { force?: boolean, - timeout?: number, }; export type ElementHandleSelectTextResult = void; export type ElementHandleSetInputFilesParams = { @@ -3737,7 +3656,7 @@ export type ElementHandleSetInputFilesParams = { directoryStream?: WritableStreamChannel, localPaths?: string[], streams?: WritableStreamChannel[], - timeout?: number, + timeout: number, }; export type ElementHandleSetInputFilesOptions = { payloads?: { @@ -3749,21 +3668,19 @@ export type ElementHandleSetInputFilesOptions = { directoryStream?: WritableStreamChannel, localPaths?: string[], streams?: WritableStreamChannel[], - timeout?: number, }; export type ElementHandleSetInputFilesResult = void; export type ElementHandleTapParams = { force?: boolean, modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, - timeout?: number, + timeout: number, trial?: boolean, }; export type ElementHandleTapOptions = { force?: boolean, modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, - timeout?: number, trial?: boolean, }; export type ElementHandleTapResult = void; @@ -3775,43 +3692,40 @@ export type ElementHandleTextContentResult = { export type ElementHandleTypeParams = { text: string, delay?: number, - timeout?: number, + timeout: number, }; export type ElementHandleTypeOptions = { delay?: number, - timeout?: number, }; export type ElementHandleTypeResult = void; export type ElementHandleUncheckParams = { force?: boolean, position?: Point, - timeout?: number, + timeout: number, trial?: boolean, }; export type ElementHandleUncheckOptions = { force?: boolean, position?: Point, - timeout?: number, trial?: boolean, }; export type ElementHandleUncheckResult = void; export type ElementHandleWaitForElementStateParams = { state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled' | 'editable', - timeout?: number, + timeout: number, }; export type ElementHandleWaitForElementStateOptions = { - timeout?: number, + }; export type ElementHandleWaitForElementStateResult = void; export type ElementHandleWaitForSelectorParams = { selector: string, strict?: boolean, - timeout?: number, + timeout: number, state?: 'attached' | 'detached' | 'visible' | 'hidden', }; export type ElementHandleWaitForSelectorOptions = { strict?: boolean, - timeout?: number, state?: 'attached' | 'detached' | 'visible' | 'hidden', }; export type ElementHandleWaitForSelectorResult = { @@ -4396,7 +4310,7 @@ export type ElectronLaunchParams = { args?: string[], cwd?: string, env?: NameValue[], - timeout?: number, + timeout: number, acceptDownloads?: 'accept' | 'deny' | 'internal-browser-default', bypassCSP?: boolean, colorScheme?: 'dark' | 'light' | 'no-preference' | 'no-override', @@ -4431,7 +4345,6 @@ export type ElectronLaunchOptions = { args?: string[], cwd?: string, env?: NameValue[], - timeout?: number, acceptDownloads?: 'accept' | 'deny' | 'internal-browser-default', bypassCSP?: boolean, colorScheme?: 'dark' | 'light' | 'no-preference' | 'no-override', @@ -4546,7 +4459,6 @@ export interface AndroidEventTarget { export interface AndroidChannel extends AndroidEventTarget, Channel { _type_Android: boolean; devices(params: AndroidDevicesParams, metadata?: CallMetadata): Promise; - setDefaultTimeoutNoReply(params: AndroidSetDefaultTimeoutNoReplyParams, metadata?: CallMetadata): Promise; } export type AndroidDevicesParams = { host?: string, @@ -4561,13 +4473,6 @@ export type AndroidDevicesOptions = { export type AndroidDevicesResult = { devices: AndroidDeviceChannel[], }; -export type AndroidSetDefaultTimeoutNoReplyParams = { - timeout: number, -}; -export type AndroidSetDefaultTimeoutNoReplyOptions = { - -}; -export type AndroidSetDefaultTimeoutNoReplyResult = void; export interface AndroidEvents { } @@ -4637,7 +4542,6 @@ export interface AndroidDeviceChannel extends AndroidDeviceEventTarget, EventTar shell(params: AndroidDeviceShellParams, metadata?: CallMetadata): Promise; installApk(params: AndroidDeviceInstallApkParams, metadata?: CallMetadata): Promise; push(params: AndroidDevicePushParams, metadata?: CallMetadata): Promise; - setDefaultTimeoutNoReply(params: AndroidDeviceSetDefaultTimeoutNoReplyParams, metadata?: CallMetadata): Promise; connectToWebView(params: AndroidDeviceConnectToWebViewParams, metadata?: CallMetadata): Promise; close(params?: AndroidDeviceCloseParams, metadata?: CallMetadata): Promise; } @@ -4651,82 +4555,76 @@ export type AndroidDeviceWebViewRemovedEvent = { export type AndroidDeviceWaitParams = { selector: AndroidSelector, state?: 'gone', - timeout?: number, + timeout: number, }; export type AndroidDeviceWaitOptions = { state?: 'gone', - timeout?: number, }; export type AndroidDeviceWaitResult = void; export type AndroidDeviceFillParams = { selector: AndroidSelector, text: string, - timeout?: number, + timeout: number, }; export type AndroidDeviceFillOptions = { - timeout?: number, + }; export type AndroidDeviceFillResult = void; export type AndroidDeviceTapParams = { selector: AndroidSelector, duration?: number, - timeout?: number, + timeout: number, }; export type AndroidDeviceTapOptions = { duration?: number, - timeout?: number, }; export type AndroidDeviceTapResult = void; export type AndroidDeviceDragParams = { selector: AndroidSelector, dest: Point, speed?: number, - timeout?: number, + timeout: number, }; export type AndroidDeviceDragOptions = { speed?: number, - timeout?: number, }; export type AndroidDeviceDragResult = void; export type AndroidDeviceFlingParams = { selector: AndroidSelector, direction: 'up' | 'down' | 'left' | 'right', speed?: number, - timeout?: number, + timeout: number, }; export type AndroidDeviceFlingOptions = { speed?: number, - timeout?: number, }; export type AndroidDeviceFlingResult = void; export type AndroidDeviceLongTapParams = { selector: AndroidSelector, - timeout?: number, + timeout: number, }; export type AndroidDeviceLongTapOptions = { - timeout?: number, + }; export type AndroidDeviceLongTapResult = void; export type AndroidDevicePinchCloseParams = { selector: AndroidSelector, percent: number, speed?: number, - timeout?: number, + timeout: number, }; export type AndroidDevicePinchCloseOptions = { speed?: number, - timeout?: number, }; export type AndroidDevicePinchCloseResult = void; export type AndroidDevicePinchOpenParams = { selector: AndroidSelector, percent: number, speed?: number, - timeout?: number, + timeout: number, }; export type AndroidDevicePinchOpenOptions = { speed?: number, - timeout?: number, }; export type AndroidDevicePinchOpenResult = void; export type AndroidDeviceScrollParams = { @@ -4734,11 +4632,10 @@ export type AndroidDeviceScrollParams = { direction: 'up' | 'down' | 'left' | 'right', percent: number, speed?: number, - timeout?: number, + timeout: number, }; export type AndroidDeviceScrollOptions = { speed?: number, - timeout?: number, }; export type AndroidDeviceScrollResult = void; export type AndroidDeviceSwipeParams = { @@ -4746,11 +4643,10 @@ export type AndroidDeviceSwipeParams = { direction: 'up' | 'down' | 'left' | 'right', percent: number, speed?: number, - timeout?: number, + timeout: number, }; export type AndroidDeviceSwipeOptions = { speed?: number, - timeout?: number, }; export type AndroidDeviceSwipeResult = void; export type AndroidDeviceInfoParams = { @@ -4973,13 +4869,6 @@ export type AndroidDevicePushOptions = { mode?: number, }; export type AndroidDevicePushResult = void; -export type AndroidDeviceSetDefaultTimeoutNoReplyParams = { - timeout: number, -}; -export type AndroidDeviceSetDefaultTimeoutNoReplyOptions = { - -}; -export type AndroidDeviceSetDefaultTimeoutNoReplyResult = void; export type AndroidDeviceConnectToWebViewParams = { socketName: string, }; diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index fad3581f095be..086339b013dc9 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -373,7 +373,7 @@ APIRequestContext: multipartData: type: array? items: FormField - timeout: number? + timeout: number failOnStatusCode: boolean? ignoreHTTPSErrors: boolean? maxRedirects: number? @@ -485,7 +485,7 @@ LaunchOptions: handleSIGINT: boolean? handleSIGTERM: boolean? handleSIGHUP: boolean? - timeout: number? + timeout: number env: type: array? items: NameValue @@ -715,7 +715,7 @@ LocalUtils: headers: json? exposeNetwork: string? slowMo: number? - timeout: number? + timeout: number socksProxyRedirectPortForTest: number? returns: pipe: JsonPipe @@ -828,7 +828,6 @@ Playwright: bypass: string? username: string? password: string? - timeout: number? storageState: type: object? properties: @@ -1049,7 +1048,7 @@ BrowserType: type: array? items: NameValue slowMo: number? - timeout: number? + timeout: number returns: browser: Browser defaultContext: BrowserContext? @@ -1266,16 +1265,6 @@ BrowserContext: returns: page: Page - setDefaultNavigationTimeoutNoReply: - internal: true - parameters: - timeout: number? - - setDefaultTimeoutNoReply: - internal: true - parameters: - timeout: number? - setExtraHTTPHeaders: title: Set extra HTTP headers parameters: @@ -1546,16 +1535,6 @@ Page: commands: - setDefaultNavigationTimeoutNoReply: - internal: true - parameters: - timeout: number? - - setDefaultTimeoutNoReply: - internal: true - parameters: - timeout: number? - addInitScript: parameters: source: string @@ -1611,7 +1590,7 @@ Page: goBack: title: Go back parameters: - timeout: number? + timeout: number waitUntil: LifecycleEvent? returns: response: Response? @@ -1622,7 +1601,7 @@ Page: goForward: title: Go forward parameters: - timeout: number? + timeout: number waitUntil: LifecycleEvent? returns: response: Response? @@ -1655,7 +1634,7 @@ Page: reload: title: Reload parameters: - timeout: number? + timeout: number waitUntil: LifecycleEvent? returns: response: Response? @@ -1696,7 +1675,7 @@ Page: screenshot: title: Screenshot parameters: - timeout: number? + timeout: number type: type: enum? literals: @@ -2112,7 +2091,7 @@ Frame: parameters: selector: string forAI: boolean? - timeout: number? + timeout: number returns: snapshot: string flags: @@ -2123,7 +2102,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number? + timeout: number flags: slowMo: true snapshot: true @@ -2135,7 +2114,7 @@ Frame: strict: boolean? force: boolean? position: Point? - timeout: number? + timeout: number trial: boolean? flags: slowMo: true @@ -2168,7 +2147,7 @@ Frame: - right - middle clickCount: number? - timeout: number? + timeout: number trial: boolean? flags: slowMo: true @@ -2188,7 +2167,7 @@ Frame: source: string target: string force: boolean? - timeout: number? + timeout: number trial: boolean? sourcePosition: Point? targetPosition: Point? @@ -2222,7 +2201,7 @@ Frame: - left - right - middle - timeout: number? + timeout: number trial: boolean? flags: slowMo: true @@ -2236,7 +2215,7 @@ Frame: strict: boolean? type: string eventInit: SerializedArgument - timeout: number? + timeout: number flags: slowMo: true snapshot: true @@ -2270,7 +2249,7 @@ Frame: strict: boolean? value: string force: boolean? - timeout: number? + timeout: number flags: slowMo: true snapshot: true @@ -2281,7 +2260,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number? + timeout: number flags: slowMo: true snapshot: true @@ -2302,7 +2281,7 @@ Frame: selector: string strict: boolean? name: string - timeout: number? + timeout: number returns: value: string? flags: @@ -2312,7 +2291,7 @@ Frame: title: Navigate parameters: url: string - timeout: number? + timeout: number waitUntil: LifecycleEvent? referer: string? returns: @@ -2338,7 +2317,7 @@ Frame: - Meta - Shift position: Point? - timeout: number? + timeout: number trial: boolean? flags: slowMo: true @@ -2350,7 +2329,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number? + timeout: number returns: value: string flags: @@ -2361,7 +2340,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number? + timeout: number returns: value: string flags: @@ -2372,7 +2351,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number? + timeout: number returns: value: string flags: @@ -2383,7 +2362,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number? + timeout: number returns: value: boolean flags: @@ -2394,7 +2373,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number? + timeout: number returns: value: boolean flags: @@ -2405,7 +2384,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number? + timeout: number returns: value: boolean flags: @@ -2436,7 +2415,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number? + timeout: number returns: value: boolean flags: @@ -2450,7 +2429,7 @@ Frame: key: string delay: number? noWaitAfter: boolean? - timeout: number? + timeout: number flags: slowMo: true snapshot: true @@ -2504,7 +2483,7 @@ Frame: label: string? index: number? force: boolean? - timeout: number? + timeout: number returns: values: type: array @@ -2518,7 +2497,7 @@ Frame: title: Set content parameters: html: string - timeout: number? + timeout: number waitUntil: LifecycleEvent? flags: snapshot: true @@ -2545,7 +2524,7 @@ Frame: streams: type: array? items: WritableStream - timeout: number? + timeout: number flags: slowMo: true snapshot: true @@ -2568,7 +2547,7 @@ Frame: - Meta - Shift position: Point? - timeout: number? + timeout: number trial: boolean? flags: slowMo: true @@ -2580,7 +2559,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number? + timeout: number returns: value: string? flags: @@ -2598,7 +2577,7 @@ Frame: strict: boolean? text: string delay: number? - timeout: number? + timeout: number flags: slowMo: true snapshot: true @@ -2611,7 +2590,7 @@ Frame: strict: boolean? force: boolean? position: Point? - timeout: number? + timeout: number trial: boolean? flags: slowMo: true @@ -2631,7 +2610,7 @@ Frame: expression: string isFunction: boolean? arg: SerializedArgument - timeout: number? + timeout: number # When present, polls on interval. Otherwise, polls on raf. pollingInterval: number? returns: @@ -2644,7 +2623,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number? + timeout: number state: type: enum? literals: @@ -2839,7 +2818,7 @@ ElementHandle: parameters: force: boolean? position: Point? - timeout: number? + timeout: number trial: boolean? flags: slowMo: true @@ -2870,7 +2849,7 @@ ElementHandle: - right - middle clickCount: number? - timeout: number? + timeout: number trial: boolean? flags: slowMo: true @@ -2906,7 +2885,7 @@ ElementHandle: - left - right - middle - timeout: number? + timeout: number trial: boolean? flags: slowMo: true @@ -2927,7 +2906,7 @@ ElementHandle: parameters: value: string force: boolean? - timeout: number? + timeout: number flags: slowMo: true snapshot: true @@ -2966,7 +2945,7 @@ ElementHandle: - Meta - Shift position: Point? - timeout: number? + timeout: number trial: boolean? flags: slowMo: true @@ -3046,7 +3025,7 @@ ElementHandle: parameters: key: string delay: number? - timeout: number? + timeout: number noWaitAfter: boolean? flags: slowMo: true @@ -3077,7 +3056,7 @@ ElementHandle: screenshot: title: Screenshot parameters: - timeout: number? + timeout: number type: type: enum? literals: @@ -3093,7 +3072,7 @@ ElementHandle: scrollIntoViewIfNeeded: title: Scroll into view parameters: - timeout: number? + timeout: number flags: slowMo: true snapshot: true @@ -3114,7 +3093,7 @@ ElementHandle: label: string? index: number? force: boolean? - timeout: number? + timeout: number returns: values: type: array @@ -3128,7 +3107,7 @@ ElementHandle: title: Select text parameters: force: boolean? - timeout: number? + timeout: number flags: slowMo: true snapshot: true @@ -3153,7 +3132,7 @@ ElementHandle: streams: type: array? items: WritableStream - timeout: number? + timeout: number flags: slowMo: true snapshot: true @@ -3174,7 +3153,7 @@ ElementHandle: - Meta - Shift position: Point? - timeout: number? + timeout: number trial: boolean? flags: slowMo: true @@ -3193,7 +3172,7 @@ ElementHandle: parameters: text: string delay: number? - timeout: number? + timeout: number flags: slowMo: true snapshot: true @@ -3204,7 +3183,7 @@ ElementHandle: parameters: force: boolean? position: Point? - timeout: number? + timeout: number trial: boolean? flags: slowMo: true @@ -3223,7 +3202,7 @@ ElementHandle: - enabled - disabled - editable - timeout: number? + timeout: number flags: snapshot: true @@ -3232,7 +3211,7 @@ ElementHandle: parameters: selector: string strict: boolean? - timeout: number? + timeout: number state: type: enum? literals: @@ -3706,7 +3685,7 @@ Electron: env: type: array? items: NameValue - timeout: number? + timeout: number acceptDownloads: type: enum? literals: @@ -3823,11 +3802,6 @@ Android: type: array items: AndroidDevice - setDefaultTimeoutNoReply: - internal: true - parameters: - timeout: number - AndroidSocket: type: interface @@ -3864,21 +3838,21 @@ AndroidDevice: type: enum? literals: - gone - timeout: number? + timeout: number fill: title: Fill parameters: selector: AndroidSelector text: string - timeout: number? + timeout: number tap: title: Tap parameters: selector: AndroidSelector duration: number? - timeout: number? + timeout: number drag: title: Drag @@ -3886,7 +3860,7 @@ AndroidDevice: selector: AndroidSelector dest: Point speed: number? - timeout: number? + timeout: number fling: title: Fling @@ -3900,13 +3874,13 @@ AndroidDevice: - left - right speed: number? - timeout: number? + timeout: number longTap: title: Long tap parameters: selector: AndroidSelector - timeout: number? + timeout: number pinchClose: title: Pinch close @@ -3914,7 +3888,7 @@ AndroidDevice: selector: AndroidSelector percent: number speed: number? - timeout: number? + timeout: number pinchOpen: title: Pinch open @@ -3922,7 +3896,7 @@ AndroidDevice: selector: AndroidSelector percent: number speed: number? - timeout: number? + timeout: number scroll: title: Scroll @@ -3937,7 +3911,7 @@ AndroidDevice: - right percent: number speed: number? - timeout: number? + timeout: number swipe: title: Swipe @@ -3952,7 +3926,7 @@ AndroidDevice: - right percent: number speed: number? - timeout: number? + timeout: number info: internal: true @@ -4044,11 +4018,6 @@ AndroidDevice: path: string mode: number? - setDefaultTimeoutNoReply: - internal: true - parameters: - timeout: number - connectToWebView: internal: true parameters: