fix: make evaluate not wait for scheduled navigations (#23402)
Fixes #23141.
This commit is contained in:
parent
2505d48b32
commit
6bb5c0a549
|
|
@ -471,7 +471,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||
await frame.goto(internalMetadata, origin);
|
||||
const storage = await frame.evaluateExpression(`({
|
||||
localStorage: Object.keys(localStorage).map(name => ({ name, value: localStorage.getItem(name) })),
|
||||
})`, false, undefined, 'utility');
|
||||
})`, { world: 'utility' });
|
||||
originStorage.localStorage = storage.localStorage;
|
||||
if (storage.localStorage.length)
|
||||
result.origins.push(originStorage);
|
||||
|
|
@ -536,7 +536,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||
originState => {
|
||||
for (const { name, value } of (originState.localStorage || []))
|
||||
localStorage.setItem(name, value);
|
||||
}`, true, originState, 'utility');
|
||||
}`, { isFunction: true, world: 'utility' }, originState);
|
||||
}
|
||||
await page.close(internalMetadata);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ export class CRPage implements PageDelegate {
|
|||
|
||||
async exposeBinding(binding: PageBinding) {
|
||||
await this._forAllFrameSessions(frame => frame._initBinding(binding));
|
||||
await Promise.all(this._page.frames().map(frame => frame.evaluateExpression(binding.source, false, {}).catch(e => {})));
|
||||
await Promise.all(this._page.frames().map(frame => frame.evaluateExpression(binding.source).catch(e => {})));
|
||||
}
|
||||
|
||||
async removeExposedBindings() {
|
||||
|
|
@ -507,9 +507,9 @@ class FrameSession {
|
|||
worldName: UTILITY_WORLD_NAME,
|
||||
});
|
||||
for (const binding of this._crPage._browserContext._pageBindings.values())
|
||||
frame.evaluateExpression(binding.source, false, undefined).catch(e => {});
|
||||
frame.evaluateExpression(binding.source).catch(e => {});
|
||||
for (const source of this._crPage._browserContext.initScripts)
|
||||
frame.evaluateExpression(source, false, undefined, 'main').catch(e => {});
|
||||
frame.evaluateExpression(source).catch(e => {});
|
||||
}
|
||||
const isInitialEmptyPage = this._isMainFrame() && this._page.mainFrame().url() === ':';
|
||||
if (isInitialEmptyPage) {
|
||||
|
|
|
|||
|
|
@ -58,12 +58,12 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
|
|||
|
||||
async evaluateExpression(params: channels.ElectronApplicationEvaluateExpressionParams): Promise<channels.ElectronApplicationEvaluateExpressionResult> {
|
||||
const handle = await this._object._nodeElectronHandlePromise;
|
||||
return { value: serializeResult(await handle.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg))) };
|
||||
return { value: serializeResult(await handle.evaluateExpression(params.expression, { isFunction: params.isFunction }, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async evaluateExpressionHandle(params: channels.ElectronApplicationEvaluateExpressionHandleParams): Promise<channels.ElectronApplicationEvaluateExpressionHandleResult> {
|
||||
const handle = await this._object._nodeElectronHandlePromise;
|
||||
const result = await handle.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
|
||||
const result = await handle.evaluateExpressionHandle(params.expression, { isFunction: params.isFunction }, parseArgument(params.arg));
|
||||
return { handle: ElementHandleDispatcher.fromJSHandle(this, result) };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -209,11 +209,11 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
|
|||
}
|
||||
|
||||
async evalOnSelector(params: channels.ElementHandleEvalOnSelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleEvalOnSelectorResult> {
|
||||
return { value: serializeResult(await this._elementHandle.evalOnSelectorAndWaitForSignals(params.selector, !!params.strict, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
return { value: serializeResult(await this._elementHandle.evalOnSelector(params.selector, !!params.strict, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async evalOnSelectorAll(params: channels.ElementHandleEvalOnSelectorAllParams, metadata: CallMetadata): Promise<channels.ElementHandleEvalOnSelectorAllResult> {
|
||||
return { value: serializeResult(await this._elementHandle.evalOnSelectorAllAndWaitForSignals(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
return { value: serializeResult(await this._elementHandle.evalOnSelectorAll(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async waitForElementState(params: channels.ElementHandleWaitForElementStateParams, metadata: CallMetadata): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -76,11 +76,11 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
|
|||
}
|
||||
|
||||
async evaluateExpression(params: channels.FrameEvaluateExpressionParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionResult> {
|
||||
return { value: serializeResult(await this._frame.evaluateExpressionAndWaitForSignals(params.expression, { isFunction: params.isFunction, exposeUtilityScript: params.exposeUtilityScript }, parseArgument(params.arg), 'main')) };
|
||||
return { value: serializeResult(await this._frame.evaluateExpression(params.expression, { isFunction: params.isFunction, exposeUtilityScript: params.exposeUtilityScript }, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async evaluateExpressionHandle(params: channels.FrameEvaluateExpressionHandleParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionHandleResult> {
|
||||
return { handle: ElementHandleDispatcher.fromJSHandle(this.parentScope(), await this._frame.evaluateExpressionHandleAndWaitForSignals(params.expression, params.isFunction, parseArgument(params.arg), 'main')) };
|
||||
return { handle: ElementHandleDispatcher.fromJSHandle(this.parentScope(), await this._frame.evaluateExpressionHandle(params.expression, { isFunction: params.isFunction }, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async waitForSelector(params: channels.FrameWaitForSelectorParams, metadata: CallMetadata): Promise<channels.FrameWaitForSelectorResult> {
|
||||
|
|
@ -92,11 +92,11 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
|
|||
}
|
||||
|
||||
async evalOnSelector(params: channels.FrameEvalOnSelectorParams, metadata: CallMetadata): Promise<channels.FrameEvalOnSelectorResult> {
|
||||
return { value: serializeResult(await this._frame.evalOnSelectorAndWaitForSignals(params.selector, !!params.strict, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
return { value: serializeResult(await this._frame.evalOnSelector(params.selector, !!params.strict, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async evalOnSelectorAll(params: channels.FrameEvalOnSelectorAllParams, metadata: CallMetadata): Promise<channels.FrameEvalOnSelectorAllResult> {
|
||||
return { value: serializeResult(await this._frame.evalOnSelectorAllAndWaitForSignals(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
return { value: serializeResult(await this._frame.evalOnSelectorAll(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async querySelector(params: channels.FrameQuerySelectorParams, metadata: CallMetadata): Promise<channels.FrameQuerySelectorResult> {
|
||||
|
|
|
|||
|
|
@ -36,11 +36,11 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, channels.JSHandl
|
|||
}
|
||||
|
||||
async evaluateExpression(params: channels.JSHandleEvaluateExpressionParams): Promise<channels.JSHandleEvaluateExpressionResult> {
|
||||
return { value: serializeResult(await this._object.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg))) };
|
||||
return { value: serializeResult(await this._object.evaluateExpression(params.expression, { isFunction: params.isFunction }, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async evaluateExpressionHandle(params: channels.JSHandleEvaluateExpressionHandleParams): Promise<channels.JSHandleEvaluateExpressionHandleResult> {
|
||||
const jsHandle = await this._object.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
|
||||
const jsHandle = await this._object.evaluateExpressionHandle(params.expression, { isFunction: params.isFunction }, parseArgument(params.arg));
|
||||
return { handle: ElementHandleDispatcher.fromJSHandle(this.parentScope(), jsHandle) };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,10 +52,6 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||
this.world = world;
|
||||
}
|
||||
|
||||
override async waitForSignalsCreatedBy<T>(action: () => Promise<T>): Promise<T> {
|
||||
return this.frame._page._frameManager.waitForSignalsCreatedBy(null, false, action);
|
||||
}
|
||||
|
||||
override adoptIfNeeded(handle: js.JSHandle): Promise<js.JSHandle> | null {
|
||||
if (handle instanceof ElementHandle && handle._context !== this)
|
||||
return this.frame._page._delegate.adoptElementHandle(handle, this);
|
||||
|
|
@ -74,16 +70,8 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||
return js.evaluateExpression(this, expression, { ...options, returnByValue: true }, arg);
|
||||
}
|
||||
|
||||
async evaluateExpressionAndWaitForSignals(expression: string, options: { isFunction?: boolean, exposeUtilityScript?: boolean }, arg?: any): Promise<any> {
|
||||
return await this.frame._page._frameManager.waitForSignalsCreatedBy(null, false /* noWaitFor */, async () => {
|
||||
return this.evaluateExpression(expression, options, arg);
|
||||
});
|
||||
}
|
||||
|
||||
async evaluateExpressionHandleAndWaitForSignals(expression: string, options: { isFunction?: boolean, exposeUtilityScript?: boolean }, arg: any): Promise<any> {
|
||||
return await this.frame._page._frameManager.waitForSignalsCreatedBy(null, false /* noWaitFor */, async () => {
|
||||
return js.evaluateExpression(this, expression, { ...options, returnByValue: false }, arg);
|
||||
});
|
||||
async evaluateExpressionHandle(expression: string, options: { isFunction?: boolean, exposeUtilityScript?: boolean }, arg?: any): Promise<js.JSHandle<any>> {
|
||||
return js.evaluateExpression(this, expression, { ...options, returnByValue: false }, arg);
|
||||
}
|
||||
|
||||
override createHandle(remoteObject: js.RemoteObject): js.JSHandle {
|
||||
|
|
@ -765,18 +753,18 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
return this._frame.selectors.queryAll(selector, this);
|
||||
}
|
||||
|
||||
async evalOnSelectorAndWaitForSignals(selector: string, strict: boolean, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
async evalOnSelector(selector: string, strict: boolean, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
const handle = await this._frame.selectors.query(selector, { strict }, this);
|
||||
if (!handle)
|
||||
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
||||
const result = await handle.evaluateExpressionAndWaitForSignals(expression, isFunction, true, arg);
|
||||
const result = await handle.evaluateExpression(expression, { isFunction }, arg);
|
||||
handle.dispose();
|
||||
return result;
|
||||
}
|
||||
|
||||
async evalOnSelectorAllAndWaitForSignals(selector: string, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
async evalOnSelectorAll(selector: string, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
const arrayHandle = await this._frame.selectors.queryArrayInMainWorld(selector, this);
|
||||
const result = await arrayHandle.evaluateExpressionAndWaitForSignals(expression, isFunction, true, arg);
|
||||
const result = await arrayHandle.evaluateExpression(expression, { isFunction }, arg);
|
||||
arrayHandle.dispose();
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ export class RawMouseImpl implements input.RawMouse {
|
|||
|
||||
async wheel(x: number, y: number, buttons: Set<types.MouseButton>, modifiers: Set<types.KeyboardModifier>, deltaX: number, deltaY: number): Promise<void> {
|
||||
// Wheel events hit the compositor first, so wait one frame for it to be synced.
|
||||
await this._page!.mainFrame().evaluateExpression(`new Promise(requestAnimationFrame)`, false, false, 'utility');
|
||||
await this._page!.mainFrame().evaluateExpression(`new Promise(requestAnimationFrame)`, { world: 'utility' });
|
||||
await this._client.send('Page.dispatchWheelEvent', {
|
||||
deltaX,
|
||||
deltaY,
|
||||
|
|
|
|||
|
|
@ -751,21 +751,15 @@ export class Frame extends SdkObject {
|
|||
return this._context('utility');
|
||||
}
|
||||
|
||||
async evaluateExpressionHandleAndWaitForSignals(expression: string, isFunction: boolean | undefined, arg: any, world: types.World = 'main'): Promise<any> {
|
||||
const context = await this._context(world);
|
||||
const handle = await context.evaluateExpressionHandleAndWaitForSignals(expression, { isFunction }, arg);
|
||||
return handle;
|
||||
}
|
||||
|
||||
async evaluateExpression(expression: string, isFunction: boolean | undefined, arg: any, world: types.World = 'main'): Promise<any> {
|
||||
const context = await this._context(world);
|
||||
const value = await context.evaluateExpression(expression, { isFunction }, arg);
|
||||
async evaluateExpression(expression: string, options: { isFunction?: boolean, exposeUtilityScript?: boolean, world?: types.World } = {}, arg?: any): Promise<any> {
|
||||
const context = await this._context(options.world ?? 'main');
|
||||
const value = await context.evaluateExpression(expression, options, arg);
|
||||
return value;
|
||||
}
|
||||
|
||||
async evaluateExpressionAndWaitForSignals(expression: string, options: { isFunction?: boolean, exposeUtilityScript?: boolean }, arg: any, world: types.World = 'main'): Promise<any> {
|
||||
const context = await this._context(world);
|
||||
const value = await context.evaluateExpressionAndWaitForSignals(expression, options, arg);
|
||||
async evaluateExpressionHandle(expression: string, options: { isFunction?: boolean, exposeUtilityScript?: boolean, world?: types.World } = {}, arg?: any): Promise<js.JSHandle<any>> {
|
||||
const context = await this._context(options.world ?? 'main');
|
||||
const value = await context.evaluateExpressionHandle(expression, options, arg);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
@ -841,18 +835,18 @@ export class Frame extends SdkObject {
|
|||
}, { type, eventInit }, { mainWorld: true, ...options });
|
||||
}
|
||||
|
||||
async evalOnSelectorAndWaitForSignals(selector: string, strict: boolean, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
async evalOnSelector(selector: string, strict: boolean, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
const handle = await this.selectors.query(selector, { strict });
|
||||
if (!handle)
|
||||
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
||||
const result = await handle.evaluateExpressionAndWaitForSignals(expression, isFunction, true, arg);
|
||||
const result = await handle.evaluateExpression(expression, { isFunction }, arg);
|
||||
handle.dispose();
|
||||
return result;
|
||||
}
|
||||
|
||||
async evalOnSelectorAllAndWaitForSignals(selector: string, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
async evalOnSelectorAll(selector: string, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
const arrayHandle = await this.selectors.queryArrayInMainWorld(selector);
|
||||
const result = await arrayHandle.evaluateExpressionAndWaitForSignals(expression, isFunction, true, arg);
|
||||
const result = await arrayHandle.evaluateExpression(expression, { isFunction }, arg);
|
||||
arrayHandle.dispose();
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ export class HarTracer {
|
|||
title: document.title,
|
||||
domContentLoaded: performance.timing.domContentLoadedEventStart,
|
||||
};
|
||||
}), true, undefined, 'utility').then(result => {
|
||||
}), { isFunction: true, world: 'utility' }).then(result => {
|
||||
pageEntry.title = result.title;
|
||||
if (!this._options.omitTiming)
|
||||
pageEntry.pageTimings.onContentLoad = result.domContentLoaded;
|
||||
|
|
@ -164,7 +164,7 @@ export class HarTracer {
|
|||
title: document.title,
|
||||
loaded: performance.timing.loadEventStart,
|
||||
};
|
||||
}), true, undefined, 'utility').then(result => {
|
||||
}), { isFunction: true, world: 'utility' }).then(result => {
|
||||
pageEntry.title = result.title;
|
||||
if (!this._options.omitTiming)
|
||||
pageEntry.pageTimings.onLoad = result.loaded;
|
||||
|
|
|
|||
|
|
@ -108,10 +108,6 @@ export class ExecutionContext extends SdkObject {
|
|||
return this._delegate.releaseHandle(objectId);
|
||||
}
|
||||
|
||||
async waitForSignalsCreatedBy<T>(action: () => Promise<T>): Promise<T> {
|
||||
return action();
|
||||
}
|
||||
|
||||
adoptIfNeeded(handle: JSHandle): Promise<JSHandle> | null {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -171,8 +167,14 @@ export class JSHandle<T = any> extends SdkObject {
|
|||
return evaluate(this._context, false /* returnByValue */, pageFunction, this, arg);
|
||||
}
|
||||
|
||||
async evaluateExpressionAndWaitForSignals(expression: string, isFunction: boolean | undefined, returnByValue: boolean, arg: any) {
|
||||
const value = await evaluateExpressionAndWaitForSignals(this._context, returnByValue, expression, isFunction, this, arg);
|
||||
async evaluateExpression(expression: string, options: { isFunction?: boolean }, arg: any) {
|
||||
const value = await evaluateExpression(this._context, expression, { ...options, returnByValue: true }, this, arg);
|
||||
await this._context.doSlowMo();
|
||||
return value;
|
||||
}
|
||||
|
||||
async evaluateExpressionHandle(expression: string, options: { isFunction?: boolean }, arg: any): Promise<JSHandle<any>> {
|
||||
const value = await evaluateExpression(this._context, expression, { ...options, returnByValue: false }, this, arg);
|
||||
await this._context.doSlowMo();
|
||||
return value;
|
||||
}
|
||||
|
|
@ -298,10 +300,6 @@ export async function evaluateExpression(context: ExecutionContext, expression:
|
|||
}
|
||||
}
|
||||
|
||||
export async function evaluateExpressionAndWaitForSignals(context: ExecutionContext, returnByValue: boolean, expression: string, isFunction: boolean | undefined, ...args: any[]): Promise<any> {
|
||||
return await context.waitForSignalsCreatedBy(() => evaluateExpression(context, expression, { returnByValue, isFunction }, ...args));
|
||||
}
|
||||
|
||||
export function parseUnserializableValue(unserializableValue: string): any {
|
||||
if (unserializableValue === 'NaN')
|
||||
return NaN;
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ export class Recorder implements InstrumentationListener {
|
|||
|
||||
private _refreshOverlay() {
|
||||
for (const page of this._context.pages())
|
||||
page.mainFrame().evaluateExpression('window.__pw_refreshOverlay()', false, undefined, 'main').catch(() => {});
|
||||
page.mainFrame().evaluateExpression('window.__pw_refreshOverlay()').catch(() => {});
|
||||
}
|
||||
|
||||
async onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata) {
|
||||
|
|
|
|||
|
|
@ -149,25 +149,25 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
|
|||
async setMode(mode: 'none' | 'recording' | 'inspecting'): Promise<void> {
|
||||
await this._page.mainFrame().evaluateExpression(((mode: Mode) => {
|
||||
window.playwrightSetMode(mode);
|
||||
}).toString(), true, mode, 'main').catch(() => {});
|
||||
}).toString(), { isFunction: true }, mode).catch(() => {});
|
||||
}
|
||||
|
||||
async setFileIfNeeded(file: string): Promise<void> {
|
||||
await this._page.mainFrame().evaluateExpression(((file: string) => {
|
||||
window.playwrightSetFileIfNeeded(file);
|
||||
}).toString(), true, file, 'main').catch(() => {});
|
||||
}).toString(), { isFunction: true }, file).catch(() => {});
|
||||
}
|
||||
|
||||
async setPaused(paused: boolean): Promise<void> {
|
||||
await this._page.mainFrame().evaluateExpression(((paused: boolean) => {
|
||||
window.playwrightSetPaused(paused);
|
||||
}).toString(), true, paused, 'main').catch(() => {});
|
||||
}).toString(), { isFunction: true }, paused).catch(() => {});
|
||||
}
|
||||
|
||||
async setSources(sources: Source[]): Promise<void> {
|
||||
await this._page.mainFrame().evaluateExpression(((sources: Source[]) => {
|
||||
window.playwrightSetSources(sources);
|
||||
}).toString(), true, sources, 'main').catch(() => {});
|
||||
}).toString(), { isFunction: true }, sources).catch(() => {});
|
||||
|
||||
// Testing harness for runCLI mode.
|
||||
if (process.env.PWTEST_CLI_IS_UNDER_TEST && sources.length)
|
||||
|
|
@ -181,12 +181,12 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
|
|||
}
|
||||
await this._page.mainFrame().evaluateExpression(((arg: any) => {
|
||||
window.playwrightSetSelector(arg.selector, arg.focus);
|
||||
}).toString(), true, { selector, focus }, 'main').catch(() => {});
|
||||
}).toString(), { isFunction: true }, { selector, focus }).catch(() => {});
|
||||
}
|
||||
|
||||
async updateCallLogs(callLogs: CallLog[]): Promise<void> {
|
||||
await this._page.mainFrame().evaluateExpression(((callLogs: CallLog[]) => {
|
||||
window.playwrightUpdateLogs(callLogs);
|
||||
}).toString(), true, callLogs, 'main').catch(() => {});
|
||||
}).toString(), { isFunction: true }, callLogs).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ class StdinServer {
|
|||
|
||||
private _loadTrace(url: string) {
|
||||
clearTimeout(this._pollTimer);
|
||||
this._page?.mainFrame().evaluateExpression(`window.setTraceURL(${JSON.stringify(url)})`, false, undefined).catch(() => {});
|
||||
this._page?.mainFrame().evaluateExpression(`window.setTraceURL(${JSON.stringify(url)})`).catch(() => {});
|
||||
}
|
||||
|
||||
private _pollLoadTrace(url: string) {
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ export class RawMouseImpl implements input.RawMouse {
|
|||
throw new Error('Mouse wheel is not supported in mobile WebKit');
|
||||
await this._session!.send('Page.updateScrollingState');
|
||||
// Wheel events hit the compositor first, so wait one frame for it to be synced.
|
||||
await this._page!.mainFrame().evaluateExpression(`new Promise(requestAnimationFrame)`, false, false, 'utility');
|
||||
await this._page!.mainFrame().evaluateExpression(`new Promise(requestAnimationFrame)`, { world: 'utility' });
|
||||
await this._pageProxySession.send('Input.dispatchWheelEvent', {
|
||||
x,
|
||||
y,
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ export class WKPage implements PageDelegate {
|
|||
const bootstrapScript = this._calculateBootstrapScript();
|
||||
if (bootstrapScript.length)
|
||||
promises.push(session.send('Page.setBootstrapScript', { source: bootstrapScript }));
|
||||
this._page.frames().map(frame => frame.evaluateExpression(bootstrapScript, false, undefined).catch(e => {}));
|
||||
this._page.frames().map(frame => frame.evaluateExpression(bootstrapScript).catch(e => {}));
|
||||
if (contextOptions.bypassCSP)
|
||||
promises.push(session.send('Page.setBypassCSP', { enabled: true }));
|
||||
const emulatedSize = this._page.emulatedSize();
|
||||
|
|
@ -767,7 +767,7 @@ export class WKPage implements PageDelegate {
|
|||
async exposeBinding(binding: PageBinding): Promise<void> {
|
||||
this._session.send('Runtime.addBinding', { name: binding.name });
|
||||
await this._updateBootstrapScript();
|
||||
await Promise.all(this._page.frames().map(frame => frame.evaluateExpression(binding.source, false, {}).catch(e => {})));
|
||||
await Promise.all(this._page.frames().map(frame => frame.evaluateExpression(binding.source).catch(e => {})));
|
||||
}
|
||||
|
||||
async removeExposedBindings(): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ class UIMode {
|
|||
|
||||
private _dispatchEvent(message: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
this._page.mainFrame().evaluateExpression(dispatchFuncSource, true, message).catch(e => this._originalStderrWrite.call(process.stderr, String(e)));
|
||||
this._page.mainFrame().evaluateExpression(dispatchFuncSource, { isFunction: true }, message).catch(e => this._originalStderrWrite.call(process.stderr, String(e)));
|
||||
}
|
||||
|
||||
private async _listTests() {
|
||||
|
|
|
|||
|
|
@ -89,3 +89,18 @@ it('should access page after beforeunload', async ({ page, server }) => {
|
|||
await page.evaluate(() => document.title);
|
||||
});
|
||||
|
||||
it('should not stall on evaluate when dismissing beforeunload', async ({ page, server }) => {
|
||||
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/23141' });
|
||||
|
||||
await page.goto(server.PREFIX + '/beforeunload.html');
|
||||
// We have to interact with a page so that 'beforeunload' handlers fire.
|
||||
await page.click('body');
|
||||
|
||||
await Promise.all([
|
||||
page.evaluate(() => {
|
||||
window.location.reload();
|
||||
}),
|
||||
page.waitForEvent('dialog').then(dialog => dialog.dismiss()),
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -43,48 +43,6 @@ it('should not stall on JS navigation link', async ({ page, browserName }) => {
|
|||
await page.click('a');
|
||||
});
|
||||
|
||||
it('should await navigation when clicking anchor programmatically', async ({ page, server }) => {
|
||||
const messages = initServer(server);
|
||||
await page.setContent(`<a id="anchor" href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
await Promise.all([
|
||||
page.evaluate(() => (window as any).anchor.click()).then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
|
||||
it('should await navigation when clicking anchor via $eval', async ({ page, server }) => {
|
||||
const messages = initServer(server);
|
||||
await page.setContent(`<a id="anchor" href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
await Promise.all([
|
||||
page.$eval('#anchor', anchor => (anchor as any).click()).then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
|
||||
it('should await navigation when clicking anchor via handle.eval', async ({ page, server }) => {
|
||||
const messages = initServer(server);
|
||||
await page.setContent(`<a id="anchor" href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
const handle = await page.evaluateHandle('document');
|
||||
await Promise.all([
|
||||
handle.evaluate(doc => (doc as any).getElementById('anchor').click()).then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
|
||||
it('should await navigation when clicking anchor via handle.$eval', async ({ page, server }) => {
|
||||
const messages = initServer(server);
|
||||
await page.setContent(`<a id="anchor" href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
const handle = await page.$('body');
|
||||
await Promise.all([
|
||||
handle.$eval('#anchor', anchor => (anchor as any).click()).then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
|
||||
it('should await cross-process navigation when clicking anchor', async ({ page, server }) => {
|
||||
const messages = initServer(server);
|
||||
await page.setContent(`<a href="${server.CROSS_PROCESS_PREFIX + '/empty.html'}">empty.html</a>`);
|
||||
|
|
@ -96,17 +54,6 @@ it('should await cross-process navigation when clicking anchor', async ({ page,
|
|||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
|
||||
it('should await cross-process navigation when clicking anchor programatically', async ({ page, server }) => {
|
||||
const messages = initServer(server);
|
||||
await page.setContent(`<a id="anchor" href="${server.CROSS_PROCESS_PREFIX + '/empty.html'}">empty.html</a>`);
|
||||
|
||||
await Promise.all([
|
||||
page.evaluate(() => (window as any).anchor.click()).then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
|
||||
it('should await form-get on click', async ({ page, server }) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html?foo=bar', async (req, res) => {
|
||||
|
|
@ -143,39 +90,6 @@ it('should await form-post on click', async ({ page, server }) => {
|
|||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
|
||||
it('should await navigation when assigning location', async ({ page, server }) => {
|
||||
const messages = initServer(server);
|
||||
await Promise.all([
|
||||
page.evaluate(`window.location.href = "${server.EMPTY_PAGE}"`).then(() => messages.push('evaluate')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|evaluate');
|
||||
});
|
||||
|
||||
it('should await navigation when assigning location twice', async ({ page, server }) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html?cancel', async (req, res) => { res.end('done'); });
|
||||
server.setRoute('/empty.html?override', async (req, res) => { messages.push('routeoverride'); res.end('done'); });
|
||||
await page.evaluate(`
|
||||
window.location.href = "${server.EMPTY_PAGE}?cancel";
|
||||
window.location.href = "${server.EMPTY_PAGE}?override";
|
||||
`);
|
||||
messages.push('evaluate');
|
||||
expect(messages.join('|')).toBe('routeoverride|evaluate');
|
||||
});
|
||||
|
||||
it('should await navigation when evaluating reload', async ({ page, server, browserName }) => {
|
||||
it.skip(browserName === 'firefox', 'With fission enabled, navigations in Firefox start asynchronously');
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const messages = initServer(server);
|
||||
await Promise.all([
|
||||
page.evaluate(`window.location.reload()`).then(() => messages.push('evaluate')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|evaluate');
|
||||
});
|
||||
|
||||
it('should work with noWaitAfter: true', async ({ page, server }) => {
|
||||
server.setRoute('/empty.html', async () => {});
|
||||
await page.setContent(`<a id="anchor" href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue