diff --git a/packages/playwright-core/src/client/android.ts b/packages/playwright-core/src/client/android.ts index 5b0e1798907f6..bad1546eab08b 100644 --- a/packages/playwright-core/src/client/android.ts +++ b/packages/playwright-core/src/client/android.ts @@ -272,11 +272,10 @@ export class AndroidDevice extends ChannelOwner i async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise { return await this._wrapApiCall(async () => { - const { timeout, signal } = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); + const timeoutOptions = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const waiter = Waiter.createForEvent(this, event); - waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); - waiter.rejectOnSignal(signal); + waiter.rejectOnTimeout(timeoutOptions, `Timeout ${timeoutOptions.timeout}ms exceeded while waiting for event "${event}"`); if (event !== Events.AndroidDevice.Close) waiter.rejectOnEvent(this, Events.AndroidDevice.Close, () => new TargetClosedError()); const result = await waiter.waitForEvent(this, event, predicate as any); diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 89c160f78928d..82445b69f0941 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -449,11 +449,10 @@ export class BrowserContext extends ChannelOwner async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise { return await this._wrapApiCall(async () => { - const { timeout, signal } = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); + const timeoutOptions = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const waiter = Waiter.createForEvent(this, event); - waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); - waiter.rejectOnSignal(signal); + waiter.rejectOnTimeout(timeoutOptions, `Timeout ${timeoutOptions.timeout}ms exceeded while waiting for event "${event}"`); if (event !== Events.BrowserContext.Close) waiter.rejectOnEvent(this, Events.BrowserContext.Close, () => new TargetClosedError(this._effectiveCloseReason())); const result = await waiter.waitForEvent(this, event, predicate as any); diff --git a/packages/playwright-core/src/client/electron.ts b/packages/playwright-core/src/client/electron.ts index c773ac36f6c8e..d433427c57ba2 100644 --- a/packages/playwright-core/src/client/electron.ts +++ b/packages/playwright-core/src/client/electron.ts @@ -139,11 +139,10 @@ export class ElectronApplication extends ChannelOwner { return await this._wrapApiCall(async () => { - const { timeout, signal } = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); + const timeoutOptions = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const waiter = Waiter.createForEvent(this, event); - waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); - waiter.rejectOnSignal(signal); + waiter.rejectOnTimeout(timeoutOptions, `Timeout ${timeoutOptions.timeout}ms exceeded while waiting for event "${event}"`); if (event !== Events.ElectronApplication.Close) waiter.rejectOnEvent(this, Events.ElectronApplication.Close, () => new TargetClosedError()); const result = await waiter.waitForEvent(this, event, predicate as any); diff --git a/packages/playwright-core/src/client/frame.ts b/packages/playwright-core/src/client/frame.ts index 6c9de8ecdcfdb..83f1339bf45db 100644 --- a/packages/playwright-core/src/client/frame.ts +++ b/packages/playwright-core/src/client/frame.ts @@ -129,9 +129,8 @@ export class Frame extends ChannelOwner implements api.Fr waiter.rejectOnEvent(this._page!, Events.Page.Close, () => this._page!._closeErrorWithReason()); waiter.rejectOnEvent(this._page!, Events.Page.Crash, new Error('Navigation failed because page crashed!')); waiter.rejectOnEvent(this._page!, Events.Page.FrameDetached, new Error('Navigating frame was detached!'), frame => frame === this); - const { timeout } = this._page!._timeoutSettings.navigationTimeout(options); - waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded.`); - waiter.rejectOnSignal(options.signal); + const timeoutOptions = this._page!._timeoutSettings.navigationTimeout(options); + waiter.rejectOnTimeout(timeoutOptions, `Timeout ${timeoutOptions.timeout}ms exceeded.`); return waiter; } diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index 7c4fb67cbffa6..4d36f5dd0a8b1 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -804,11 +804,10 @@ export class WebSocket extends ChannelOwner implement async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise { return await this._wrapApiCall(async () => { - const { timeout, signal } = this._page._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); + const timeoutOptions = this._page._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const waiter = Waiter.createForEvent(this, event); - waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); - waiter.rejectOnSignal(signal); + waiter.rejectOnTimeout(timeoutOptions, `Timeout ${timeoutOptions.timeout}ms exceeded while waiting for event "${event}"`); if (event !== Events.WebSocket.Error) waiter.rejectOnEvent(this, Events.WebSocket.Error, new Error('Socket error')); if (event !== Events.WebSocket.Close) diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 1fedf84e23ad9..2d5bfbf14f032 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -477,13 +477,12 @@ export class Page extends ChannelOwner implements api.Page private async _waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions, logLine?: string): Promise { return await this._wrapApiCall(async () => { - const { timeout, signal } = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); + const timeoutOptions = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const waiter = Waiter.createForEvent(this, event); if (logLine) waiter.log(logLine); - waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); - waiter.rejectOnSignal(signal); + waiter.rejectOnTimeout(timeoutOptions, `Timeout ${timeoutOptions.timeout}ms exceeded while waiting for event "${event}"`); if (event !== Events.Page.Crash) waiter.rejectOnEvent(this, Events.Page.Crash, new Error('Page crashed')); if (event !== Events.Page.Close) diff --git a/packages/playwright-core/src/client/waiter.ts b/packages/playwright-core/src/client/waiter.ts index a20ae8dc61142..d5a5706deb8cc 100644 --- a/packages/playwright-core/src/client/waiter.ts +++ b/packages/playwright-core/src/client/waiter.ts @@ -80,25 +80,21 @@ export class Waiter { this._rejectOn(promise.then(() => { throw (typeof error === 'function' ? error() : error); }), dispose); } - rejectOnTimeout(timeout: number, message: string) { - if (!timeout) - return; - const { promise, dispose } = waitForTimeout(timeout); - this._rejectOn(promise.then(() => { throw new TimeoutError(message); }), dispose); - } + rejectOnTimeout({ timeout, signal }: channels.TimeoutOptions, timeoutMessage: string) { + if (signal) { + if (signal.aborted) + return this.rejectImmediately(new AbortError(undefined, { cause: signal.reason })); + let rejectPromise: (e: any) => void; + const promise = new Promise((_, reject) => { rejectPromise = reject; }); + const listener = () => rejectPromise!(new AbortError(undefined, { cause: signal.reason })); + signal.addEventListener('abort', listener, { once: true }); + this._rejectOn(promise, () => signal.removeEventListener('abort', listener)); + } - rejectOnSignal(signal: AbortSignal | undefined) { - if (!signal) - return; - if (signal.aborted) { - this.rejectImmediately(new AbortError(undefined, { cause: signal.reason })); - return; + if (timeout) { + const { promise, dispose } = waitForTimeout(timeout); + this._rejectOn(promise.then(() => { throw new TimeoutError(timeoutMessage); }), dispose); } - let rejectPromise: (e: any) => void; - const promise = new Promise((_, reject) => { rejectPromise = reject; }); - const listener = () => rejectPromise!(new AbortError(undefined, { cause: signal.reason })); - signal.addEventListener('abort', listener, { once: true }); - this._rejectOn(promise, () => signal.removeEventListener('abort', listener)); } rejectImmediately(error: Error) { diff --git a/packages/playwright-core/src/client/worker.ts b/packages/playwright-core/src/client/worker.ts index 5b193d6c5bf8e..90987e03a822c 100644 --- a/packages/playwright-core/src/client/worker.ts +++ b/packages/playwright-core/src/client/worker.ts @@ -89,11 +89,10 @@ export class Worker extends ChannelOwner implements api. async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise { return await this._wrapApiCall(async () => { const timeoutSettings = this._page?._timeoutSettings ?? this._context?._timeoutSettings ?? new TimeoutSettings(); - const { timeout, signal } = timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); + const timeoutOptions = timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const waiter = Waiter.createForEvent(this, event); - waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); - waiter.rejectOnSignal(signal); + waiter.rejectOnTimeout(timeoutOptions, `Timeout ${timeoutOptions.timeout}ms exceeded while waiting for event "${event}"`); if (event !== Events.Worker.Close) waiter.rejectOnEvent(this, Events.Worker.Close, () => this._closeErrorWithReason()); const result = await waiter.waitForEvent(this, event, predicate as any);