diff --git a/.mocharc.cjs b/.mocharc.cjs index 236a6604f0cc8..d1cd74f4eff55 100644 --- a/.mocharc.cjs +++ b/.mocharc.cjs @@ -19,8 +19,8 @@ module.exports = { logLevel: 'debug', require: ['./test/build/mocha-utils.js', 'source-map-support/register'], spec: 'test/build/**/*.spec.js', - exit: true, - retries: 0, + exit: !!process.env.CI, + retries: process.env.CI ? 2 : 0, parallel: !!process.env.PARALLEL, timeout: 25_000, reporter: process.env.CI ? 'spec' : 'dot', diff --git a/packages/puppeteer-core/src/common/Frame.ts b/packages/puppeteer-core/src/common/Frame.ts index 8e257a2f0b343..487d7e6263ffa 100644 --- a/packages/puppeteer-core/src/common/Frame.ts +++ b/packages/puppeteer-core/src/common/Frame.ts @@ -396,7 +396,6 @@ export class Frame { waitUntil, timeout ); - const navResponse = watcher.navigationResponse(); const error = await Promise.race([ watcher.timeoutOrTerminationPromise(), watcher.sameDocumentNavigationPromise(), @@ -408,7 +407,7 @@ export class Frame { throw error; } log('LIFECYCLE WATCHER wait for navigationResponse'); - return await navResponse; + return await watcher.navigationResponse(); } finally { log('LIFECYCLE WATCHER DISPOSED'); watcher.dispose(); diff --git a/packages/puppeteer-core/src/common/FrameManager.ts b/packages/puppeteer-core/src/common/FrameManager.ts index a120ea70f49ef..4e261b526f71e 100644 --- a/packages/puppeteer-core/src/common/FrameManager.ts +++ b/packages/puppeteer-core/src/common/FrameManager.ts @@ -145,15 +145,15 @@ export class FrameManager extends EventEmitter { const result = await Promise.all([ client.send('Page.enable'), client.send('Page.getFrameTree'), - client.send('Page.setLifecycleEventsEnabled', {enabled: true}), - client.send('Runtime.enable').then(() => { - return this.#createIsolatedWorld(client, UTILITY_WORLD_NAME); - }) ]); const {frameTree} = result[1]; this.#handleFrameTree(client, frameTree); await Promise.all([ + client.send('Page.setLifecycleEventsEnabled', {enabled: true}), + client.send('Runtime.enable').then(() => { + return this.#createIsolatedWorld(client, UTILITY_WORLD_NAME); + }), // TODO: Network manager is not aware of OOP iframes yet. client === this.#client ? this.#networkManager.initialize() @@ -243,7 +243,20 @@ export class FrameManager extends EventEmitter { session: CDPSession, frameTree: Protocol.Page.FrameTree ): void { - log('Handling frame tree', frameTree.frame.id, frameTree.frame.url); + log( + 'Handling frame tree', + frameTree.frame.id, + frameTree.frame.url, + 'this.#frameNavigatedReceived', + Array.from(this.#frameNavigatedReceived) + ); + if (frameTree.frame.parentId) { + this.#onFrameAttached( + session, + frameTree.frame.id, + frameTree.frame.parentId + ); + } if (!this.#frameNavigatedReceived.has(frameTree.frame.id)) { this.#onFrameNavigated(frameTree.frame); } else { @@ -273,7 +286,7 @@ export class FrameManager extends EventEmitter { // If an OOP iframes becomes a normal iframe again // it is first attached to the parent page before // the target is removed. - log('Updating client in #onFrameAttached') + log('Updating client in #onFrameAttached'); frame.updateClient(session); } return; @@ -386,15 +399,20 @@ export class FrameManager extends EventEmitter { ): void { const auxData = contextPayload.auxData as {frameId?: string} | undefined; const frameId = auxData && auxData.frameId; + log('#onExecutionContextCreated', frameId); const frame = typeof frameId === 'string' ? this.frame(frameId) : undefined; let world: IsolatedWorld | undefined; + log('HAS FRAME', !!frame); if (frame) { // Only care about execution contexts created for the current session. if (frame._client() !== session) { return; } - - if (contextPayload.auxData && !!contextPayload.auxData['isDefault']) { + log( + "contextPayload.auxData && contextPayload.auxData['isDefault']", + contextPayload.auxData && contextPayload.auxData['isDefault'] + ); + if (contextPayload.auxData && contextPayload.auxData['isDefault']) { world = frame.worlds[MAIN_WORLD]; } else if ( contextPayload.name === UTILITY_WORLD_NAME && @@ -411,6 +429,7 @@ export class FrameManager extends EventEmitter { contextPayload, world ); + log('HAS WORLD', !!world); if (world) { world.setContext(context); } diff --git a/packages/puppeteer-core/src/common/IsolatedWorld.ts b/packages/puppeteer-core/src/common/IsolatedWorld.ts index 24806e5c5c100..7cad59b1dcc47 100644 --- a/packages/puppeteer-core/src/common/IsolatedWorld.ts +++ b/packages/puppeteer-core/src/common/IsolatedWorld.ts @@ -296,6 +296,7 @@ export class IsolatedWorld { waitUntil = ['load'], timeout = this.#timeoutSettings.navigationTimeout(), } = options; + // We rely upon the fact that document.open() will reset frame lifecycle with "init" // lifecycle event. @see https://crrev.com/608658 await this.evaluate(html => { diff --git a/packages/puppeteer-core/src/common/LifecycleWatcher.ts b/packages/puppeteer-core/src/common/LifecycleWatcher.ts index 6f07718a86e73..f3b932eae8e09 100644 --- a/packages/puppeteer-core/src/common/LifecycleWatcher.ts +++ b/packages/puppeteer-core/src/common/LifecycleWatcher.ts @@ -286,7 +286,12 @@ export class LifecycleWatcher { } #checkLifecycleComplete(): void { - log('LifeCycleWacher checkLifecycleComplete', this.#frame._lifecycleEvents, this.#expectedLifecycle, this.#swapped); + log( + 'LifeCycleWacher checkLifecycleComplete', + Array.from(this.#frame._lifecycleEvents), + this.#expectedLifecycle, + this.#swapped + ); // We expect navigation to commit. if (!checkLifecycle(this.#frame, this.#expectedLifecycle)) { return; diff --git a/test/src/launcher.spec.ts b/test/src/launcher.spec.ts index 477d0236e053f..d67aceed1a232 100644 --- a/test/src/launcher.spec.ts +++ b/test/src/launcher.spec.ts @@ -797,6 +797,9 @@ describe('Launcher specs', function () { const restoredPage = pages.find(page => { return page.url() === server.PREFIX + '/frames/nested-frames.html'; })!; + await new Promise(resolve => { + return setTimeout(resolve, 1000); + }); expect(utils.dumpFrames(restoredPage.mainFrame())).toEqual([ 'http://localhost:/frames/nested-frames.html', ' http://localhost:/frames/two-frames.html (2frames)', diff --git a/test/src/mocha-utils.ts b/test/src/mocha-utils.ts index 281c163c5be6d..ca678c4778ad2 100644 --- a/test/src/mocha-utils.ts +++ b/test/src/mocha-utils.ts @@ -286,22 +286,23 @@ export const describeWithDebugLogs = ( description: string, body: (this: Mocha.Suite) => void ): Mocha.Suite | void => { - beforeEach(() => { - setLogCapture(true); - }); + describe(description + '-debug', () => { + beforeEach(() => { + setLogCapture(true); + }); - afterEach(function () { - if (this.currentTest?.state === 'failed') { - console.log( - `\n"${this.currentTest.fullTitle()}" failed. Here is a debug log:` - ); - console.log(getCapturedLogs().join('\n') + '\n'); - } - setLogCapture(false); - }); + afterEach(function () { + if (this.currentTest?.state === 'failed') { + console.log( + `\n"${this.currentTest.fullTitle()}" failed. Here is a debug log:` + ); + console.log(getCapturedLogs().join('\n') + '\n'); + } + setLogCapture(false); + }); - // eslint-disable-next-line mocha/no-exclusive-tests - return describe.only(description, body); + describe(description, body); + }); }; export const shortWaitForArrayToHaveAtLeastNElements = async (