diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 1ee49a01dc88f..8ed1d7c98d7bb 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -674,6 +674,11 @@ export class Page extends ChannelOwner implements api.Page if (isTargetClosedError(e) && !options.runBeforeUnload) return; throw e; + } finally { + // If the page actually closed, the 'close' event (and with it, screencast's + // auto-save) is dispatched by the server before the close response is sent, + // so this is guaranteed to observe an in-flight save, if any, at this point. + await this.screencast._waitForPendingAutoSave(); } } diff --git a/packages/playwright-core/src/client/screencast.ts b/packages/playwright-core/src/client/screencast.ts index 8aefd9c5c8b46..acf0a25b014cf 100644 --- a/packages/playwright-core/src/client/screencast.ts +++ b/packages/playwright-core/src/client/screencast.ts @@ -27,6 +27,7 @@ export class Screencast implements api.Screencast { private _savePath: string | undefined; private _onFrame: ((frame: { data: Buffer, timestamp: number, viewportWidth: number, viewportHeight: number }) => Promise) | null = null; private _artifact: Artifact | undefined; + private _pendingAutoSave: Promise | undefined; constructor(page: Page) { this._page = page; @@ -35,7 +36,8 @@ export class Screencast implements api.Screencast { }); this._page.once(Events.Page.Close, () => { // Auto-save the video when the page closes, so recordings are not lost - // if the user forgets to call stop(). + // if the user forgets to call stop(). page.close() awaits this via + // _waitForPendingAutoSave() before it resolves. if (this._started && this._savePath && this._artifact) { const artifact = this._artifact; const savePath = this._savePath; @@ -43,11 +45,15 @@ export class Screencast implements api.Screencast { this._onFrame = null; this._artifact = undefined; this._savePath = undefined; - artifact.saveAs(savePath).catch(() => {}); + this._pendingAutoSave = artifact.saveAs(savePath).catch(() => {}); } }); } + async _waitForPendingAutoSave(): Promise { + await this._pendingAutoSave; + } + async start(options: { onFrame?: (frame: { data: Buffer, timestamp: number, viewportWidth: number, viewportHeight: number }) => Promise|any, path?: string, size?: { width: number, height: number }, quality?: number } = {}): Promise { if (this._started) throw new Error('Screencast is already started');