chore: make handle beforeunload when reusing the context (#18357)

Fixes: https://github.com/microsoft/playwright/issues/17903
This commit is contained in:
Pavel Feldman 2022-10-26 15:17:40 -07:00 committed by GitHub
parent d4bab139b2
commit 1505a952fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 6 deletions

View File

@ -180,10 +180,13 @@ export abstract class BrowserContext extends SdkObject {
page = undefined;
}
// Unless I do this early, setting extra http headers below does not respond.
// Unless dialogs are dismissed, setting extra http headers below does not respond.
page?._frameManager.setCloseAllOpeningDialogs(true);
await page?._frameManager.closeOpenDialogs();
// Navigate to about:blank first to ensure no page scripts are running after this point.
await page?.mainFrame().goto(metadata, 'about:blank', { timeout: 0 });
page?._frameManager.setCloseAllOpeningDialogs(false);
await this._resetStorage();
await this._removeExposedBindings();
await this._removeInitScripts();

View File

@ -25,13 +25,13 @@ export type DialogType = 'alert' | 'beforeunload' | 'confirm' | 'prompt';
export class Dialog extends SdkObject {
private _page: Page;
private _type: string;
private _type: DialogType;
private _message: string;
private _onHandle: OnHandle;
private _handled = false;
private _defaultValue: string;
constructor(page: Page, type: string, message: string, onHandle: OnHandle, defaultValue?: string) {
constructor(page: Page, type: DialogType, message: string, onHandle: OnHandle, defaultValue?: string) {
super(page, 'dialog');
this._page = page;
this._type = type;
@ -53,7 +53,7 @@ export class Dialog extends SdkObject {
return this._defaultValue;
}
async accept(promptText: string | undefined) {
async accept(promptText?: string) {
assert(!this._handled, 'Cannot accept dialog which is already handled!');
this._handled = true;
this._page._frameManager.dialogWillClose(this);
@ -66,4 +66,11 @@ export class Dialog extends SdkObject {
this._page._frameManager.dialogWillClose(this);
await this._onHandle(false);
}
async close() {
if (this._type === 'beforeunload')
await this.accept();
else
await this.dismiss();
}
}

View File

@ -108,6 +108,7 @@ export class FrameManager {
readonly _signalBarriers = new Set<SignalBarrier>();
private _webSockets = new Map<string, network.WebSocket>();
_openedDialogs: Set<Dialog> = new Set();
private _closeAllOpeningDialogs = false;
constructor(page: Page) {
this._page = page;
@ -352,7 +353,10 @@ export class FrameManager {
// Any ongoing evaluations will be stalled until the dialog is closed.
for (const frame of this._frames.values())
frame._invalidateNonStallingEvaluations('JavaScript dialog interrupted evaluation');
this._openedDialogs.add(dialog);
if (this._closeAllOpeningDialogs)
dialog.close().then(() => {});
else
this._openedDialogs.add(dialog);
}
dialogWillClose(dialog: Dialog) {
@ -360,10 +364,14 @@ export class FrameManager {
}
async closeOpenDialogs() {
await Promise.all([...this._openedDialogs].map(dialog => dialog.dismiss())).catch(() => {});
await Promise.all([...this._openedDialogs].map(dialog => dialog.close())).catch(() => {});
this._openedDialogs.clear();
}
setCloseAllOpeningDialogs(closeDialogs: boolean) {
this._closeAllOpeningDialogs = closeDialogs;
}
removeChildFramesRecursively(frame: Frame) {
for (const child of frame.childFrames())
this._removeFramesRecursively(child);

View File

@ -349,3 +349,28 @@ test('should restore cookies', async ({ runInlineTest }) => {
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(3);
});
test('should reuse context with beforeunload', async ({ runInlineTest }) => {
const result = await runInlineTest({
'src/reuse.test.ts': `
const { test } = pwt;
let lastContextGuid;
test('one', async ({ page, context }) => {
lastContextGuid = context._guid;
await page.evaluate(() => {
window.addEventListener('beforeunload', event => {
event.preventDefault();
return event.returnValue = "Are you sure you want to exit?";
});
});
});
test('two', async ({ context }) => {
expect(context._guid).toBe(lastContextGuid);
});
`,
}, { workers: 1 }, { PW_TEST_REUSE_CONTEXT: '1' });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(2);
});