From 175362c04892f064eb734d49f9db1950f65b0c9d Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Fri, 10 Mar 2023 16:59:02 +0100 Subject: [PATCH] chore: add BiDi for `goto` navigation (#9795) --- docs/api/puppeteer.httprequest.md | 6 +- package-lock.json | 13 +- packages/puppeteer-core/package.json | 2 +- .../puppeteer-core/src/common/HTTPRequest.ts | 10 +- .../puppeteer-core/src/common/HTTPResponse.ts | 10 +- packages/puppeteer-core/src/common/Page.ts | 2 +- .../src/common/bidi/Connection.ts | 15 +- .../puppeteer-core/src/common/bidi/Context.ts | 93 ++++++- .../puppeteer-core/src/common/bidi/Page.ts | 38 ++- test/TestExpectations.json | 228 ++++++++++++++++++ test/src/navigation.spec.ts | 43 ++-- 11 files changed, 409 insertions(+), 51 deletions(-) diff --git a/docs/api/puppeteer.httprequest.md b/docs/api/puppeteer.httprequest.md index f6f001d6fa6..6db6e461fcf 100644 --- a/docs/api/puppeteer.httprequest.md +++ b/docs/api/puppeteer.httprequest.md @@ -34,9 +34,9 @@ The constructor for this class is marked as internal. Third-party code should no ## Properties -| Property | Modifiers | Type | Description | -| ------------------------------------------- | --------------------- | ---------- | ----------------------------------------------------------------- | -| [client](./puppeteer.httprequest.client.md) | readonly | CDPSession | Warning! Using this client can break Puppeteer. Use with caution. | +| Property | Modifiers | Type | Description | +| ------------------------------------------- | --------------------- | --------------------------------------- | ----------------------------------------------------------------- | +| [client](./puppeteer.httprequest.client.md) | readonly | [CDPSession](./puppeteer.cdpsession.md) | Warning! Using this client can break Puppeteer. Use with caution. | ## Methods diff --git a/package-lock.json b/package-lock.json index fb22f32ab76..1a9d481755a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2625,8 +2625,9 @@ "license": "ISC" }, "node_modules/chromium-bidi": { - "version": "0.4.4", - "license": "Apache-2.0", + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.5.tgz", + "integrity": "sha512-rkav9YzRfAshSTG3wNXF7P7yNiI29QAo1xBXElPoCoSQR5n20q3cOyVhDv6S7+GlF/CJ/emUxlQiR0xOPurkGg==", "dependencies": { "mitt": "3.0.0" }, @@ -9228,7 +9229,7 @@ "version": "19.7.4", "license": "Apache-2.0", "dependencies": { - "chromium-bidi": "0.4.4", + "chromium-bidi": "0.4.5", "cross-fetch": "3.1.5", "debug": "4.3.4", "devtools-protocol": "0.0.1094867", @@ -11025,7 +11026,9 @@ "version": "1.1.4" }, "chromium-bidi": { - "version": "0.4.4", + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.5.tgz", + "integrity": "sha512-rkav9YzRfAshSTG3wNXF7P7yNiI29QAo1xBXElPoCoSQR5n20q3cOyVhDv6S7+GlF/CJ/emUxlQiR0xOPurkGg==", "requires": { "mitt": "3.0.0" } @@ -13982,7 +13985,7 @@ "puppeteer-core": { "version": "file:packages/puppeteer-core", "requires": { - "chromium-bidi": "0.4.4", + "chromium-bidi": "0.4.5", "cross-fetch": "3.1.5", "debug": "4.3.4", "devtools-protocol": "0.0.1094867", diff --git a/packages/puppeteer-core/package.json b/packages/puppeteer-core/package.json index d4cdd1c329f..7c6e5d3afd7 100644 --- a/packages/puppeteer-core/package.json +++ b/packages/puppeteer-core/package.json @@ -131,7 +131,7 @@ "author": "The Chromium Authors", "license": "Apache-2.0", "dependencies": { - "chromium-bidi": "0.4.4", + "chromium-bidi": "0.4.5", "cross-fetch": "3.1.5", "debug": "4.3.4", "devtools-protocol": "0.0.1094867", diff --git a/packages/puppeteer-core/src/common/HTTPRequest.ts b/packages/puppeteer-core/src/common/HTTPRequest.ts index a4b802ae366..de5eb4a2e9a 100644 --- a/packages/puppeteer-core/src/common/HTTPRequest.ts +++ b/packages/puppeteer-core/src/common/HTTPRequest.ts @@ -14,12 +14,11 @@ * limitations under the License. */ import {Protocol} from 'devtools-protocol'; -import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js'; import {assert} from '../util/assert.js'; +import {CDPSession} from './Connection.js'; import {ProtocolError} from './Errors.js'; -import {EventEmitter} from './EventEmitter.js'; import {Frame} from './Frame.js'; import {HTTPResponse} from './HTTPResponse.js'; import {debugError, isString} from './util.js'; @@ -74,13 +73,6 @@ export type ResourceType = Lowercase; */ export const DEFAULT_INTERCEPT_RESOLUTION_PRIORITY = 0; -interface CDPSession extends EventEmitter { - send( - method: T, - ...paramArgs: ProtocolMapping.Commands[T]['paramsType'] - ): Promise; -} - /** * Represents an HTTP request sent by a page. * @remarks diff --git a/packages/puppeteer-core/src/common/HTTPResponse.ts b/packages/puppeteer-core/src/common/HTTPResponse.ts index f6a42878089..028abfc9890 100644 --- a/packages/puppeteer-core/src/common/HTTPResponse.ts +++ b/packages/puppeteer-core/src/common/HTTPResponse.ts @@ -14,10 +14,9 @@ * limitations under the License. */ import {Protocol} from 'devtools-protocol'; -import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js'; +import {CDPSession} from './Connection.js'; import {ProtocolError} from './Errors.js'; -import {EventEmitter} from './EventEmitter.js'; import {Frame} from './Frame.js'; import {HTTPRequest} from './HTTPRequest.js'; import {SecurityDetails} from './SecurityDetails.js'; @@ -30,13 +29,6 @@ export interface RemoteAddress { port?: number; } -interface CDPSession extends EventEmitter { - send( - method: T, - ...paramArgs: ProtocolMapping.Commands[T]['paramsType'] - ): Promise; -} - /** * The HTTPResponse class represents responses which are received by the * {@link Page} class. diff --git a/packages/puppeteer-core/src/common/Page.ts b/packages/puppeteer-core/src/common/Page.ts index 337a3e84e5b..f4ea6706621 100644 --- a/packages/puppeteer-core/src/common/Page.ts +++ b/packages/puppeteer-core/src/common/Page.ts @@ -822,7 +822,7 @@ export class CDPPage extends Page { } const textTokens = []; for (const arg of args) { - const remoteObject = arg.remoteObject() as Protocol.Runtime.RemoteObject; + const remoteObject = arg.remoteObject(); if (remoteObject.objectId) { textTokens.push(arg.toString()); } else { diff --git a/packages/puppeteer-core/src/common/bidi/Connection.ts b/packages/puppeteer-core/src/common/bidi/Connection.ts index fa8ce9f08af..f87e9718e58 100644 --- a/packages/puppeteer-core/src/common/bidi/Connection.ts +++ b/packages/puppeteer-core/src/common/bidi/Connection.ts @@ -52,6 +52,10 @@ interface Commands { params: Bidi.BrowsingContext.CloseParameters; returnType: Bidi.BrowsingContext.CloseResult; }; + 'browsingContext.navigate': { + params: Bidi.BrowsingContext.NavigateParameters; + returnType: Bidi.BrowsingContext.NavigateResult; + }; 'session.new': { params: {capabilities?: Record}; // TODO: Update Types in chromium bidi @@ -148,17 +152,20 @@ export class Connection extends EventEmitter { if (callback.method === 'browsingContext.create') { this.#contexts.set( object.result.context, - new Context(this, object.result.context) + new Context(this, object.result) ); } callback.resolve(object); } } } else { - if ('source' in object.params && !!object.params.source.context) { - const context = this.#contexts.get(object.params.source.context); - context?.emit(object.method, object.params); + let context: Context | undefined; + if ('context' in object.params) { + context = this.#contexts.get(object.params.context); + } else if ('source' in object.params && !!object.params.source.context) { + context = this.#contexts.get(object.params.source.context); } + context?.emit(object.method, object.params); this.emit(object.method, object.params); } diff --git a/packages/puppeteer-core/src/common/bidi/Context.ts b/packages/puppeteer-core/src/common/bidi/Context.ts index 73d596a9e84..fb29886aee4 100644 --- a/packages/puppeteer-core/src/common/bidi/Context.ts +++ b/packages/puppeteer-core/src/common/bidi/Context.ts @@ -16,8 +16,13 @@ import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; +import {WaitForOptions} from '../../api/Page.js'; +import {assert} from '../../util/assert.js'; import {stringifyFunction} from '../../util/Function.js'; +import {ProtocolError, TimeoutError} from '../Errors.js'; import {EventEmitter} from '../EventEmitter.js'; +import {PuppeteerLifeCycleEvent} from '../LifecycleWatcher.js'; +import {TimeoutSettings} from '../TimeoutSettings.js'; import {EvaluateFunc, HandleFor} from '../types.js'; import {isString} from '../util.js'; @@ -26,17 +31,31 @@ import {ElementHandle} from './ElementHandle.js'; import {JSHandle} from './JSHandle.js'; import {BidiSerializer} from './Serializer.js'; +/** + * @internal + */ +const puppeteerToReadinessState = new Map< + PuppeteerLifeCycleEvent, + Bidi.BrowsingContext.ReadinessState +>([ + ['load', 'complete'], + ['domcontentloaded', 'interactive'], +]); + /** * @internal */ export class Context extends EventEmitter { #connection: Connection; + #url: string; _contextId: string; + _timeoutSettings = new TimeoutSettings(); - constructor(connection: Connection, contextId: string) { + constructor(connection: Connection, result: Bidi.BrowsingContext.Info) { super(); this.#connection = connection; - this._contextId = contextId; + this._contextId = result.context; + this.#url = result.url; } get connection(): Connection { @@ -124,6 +143,76 @@ export class Context extends EventEmitter { ? BidiSerializer.deserialize(result.result) : getBidiHandle(this, result.result); } + + async goto( + url: string, + options: WaitForOptions & { + referer?: string | undefined; + referrerPolicy?: string | undefined; + } = {} + ): Promise { + const {waitUntil = 'load'} = options; + + try { + const response = await Promise.race([ + this.connection.send('browsingContext.navigate', { + url: url, + context: this.id, + wait: getWaitUntil(waitUntil), + }), + new Promise((_, reject) => { + const timeout = + options.timeout ?? this._timeoutSettings.navigationTimeout(); + if (!timeout) { + return; + } + const error = new TimeoutError( + 'Navigation timeout of ' + timeout + ' ms exceeded' + ); + return setTimeout(() => { + return reject(error); + }, timeout); + }), + ]); + this.#url = (response as Bidi.BrowsingContext.NavigateResult).result.url; + return null; + } catch (error) { + if (error instanceof ProtocolError) { + error.message += ` at ${url}`; + } + throw error; + } + + function getWaitUntil( + event: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[] + ): Bidi.BrowsingContext.ReadinessState { + if (Array.isArray(event) && event.length > 1) { + throw new Error('BiDi support only single `waitUntil` argument'); + } + const waitUntilSingle = Array.isArray(event) + ? (event.find(lifecycle => { + return lifecycle === 'domcontentloaded' || lifecycle === 'load'; + }) as PuppeteerLifeCycleEvent) + : event; + + if ( + waitUntilSingle === 'networkidle0' || + waitUntilSingle === 'networkidle2' + ) { + throw new Error(`BiDi does not support 'waitUntil' ${waitUntilSingle}`); + } + + assert(waitUntilSingle, `Invalid waitUntil option ${waitUntilSingle}`); + + return puppeteerToReadinessState.get( + waitUntilSingle + ) as Bidi.BrowsingContext.ReadinessState; + } + } + + url(): string { + return this.#url; + } } /** diff --git a/packages/puppeteer-core/src/common/bidi/Page.ts b/packages/puppeteer-core/src/common/bidi/Page.ts index 1d48c01ade9..f4015171e30 100644 --- a/packages/puppeteer-core/src/common/bidi/Page.ts +++ b/packages/puppeteer-core/src/common/bidi/Page.ts @@ -16,8 +16,13 @@ import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; -import {Page as PageBase, PageEmittedEvents} from '../../api/Page.js'; +import { + Page as PageBase, + PageEmittedEvents, + WaitForOptions, +} from '../../api/Page.js'; import {ConsoleMessage, ConsoleMessageLocation} from '../ConsoleMessage.js'; +import {HTTPResponse} from '../HTTPResponse.js'; import {EvaluateFunc, HandleFor} from '../types.js'; import {Context, getBidiHandle} from './Context.js'; @@ -30,8 +35,11 @@ export class Page extends PageBase { #context: Context; #subscribedEvents = [ 'log.entryAdded', + 'browsingContext.load', ] as Bidi.Session.SubscribeParameters['events']; + #boundOnLogEntryAdded = this.#onLogEntryAdded.bind(this); + #boundOnLoaded = this.#onLoad.bind(this); constructor(context: Context) { super(); @@ -43,6 +51,7 @@ export class Page extends PageBase { }); this.#context.on('log.entryAdded', this.#boundOnLogEntryAdded); + this.#context.on('browsingContext.load', this.#boundOnLoaded); } #onLogEntryAdded(event: Bidi.Log.LogEntry): void { @@ -82,6 +91,10 @@ export class Page extends PageBase { } } + #onLoad(_event: Bidi.BrowsingContext.NavigationInfo): void { + this.emit(PageEmittedEvents.Load); + } + override async close(): Promise { await this.#context.connection.send('session.unsubscribe', { events: this.#subscribedEvents, @@ -93,6 +106,7 @@ export class Page extends PageBase { }); this.#context.off('log.entryAdded', this.#boundOnLogEntryAdded); + this.#context.off('browsingContext.load', this.#boundOnLogEntryAdded); } override async evaluateHandle< @@ -114,6 +128,28 @@ export class Page extends PageBase { ): Promise>> { return this.#context.evaluate(pageFunction, ...args); } + + override async goto( + url: string, + options?: WaitForOptions & { + referer?: string | undefined; + referrerPolicy?: string | undefined; + } + ): Promise { + return this.#context.goto(url, options); + } + + override url(): string { + return this.#context.url(); + } + + override setDefaultNavigationTimeout(timeout: number): void { + this.#context._timeoutSettings.setDefaultNavigationTimeout(timeout); + } + + override setDefaultTimeout(timeout: number): void { + this.#context._timeoutSettings.setDefaultTimeout(timeout); + } } function isConsoleLogEntry( diff --git a/test/TestExpectations.json b/test/TestExpectations.json index e0d1975fbb6..4bbbcd8382a 100644 --- a/test/TestExpectations.json +++ b/test/TestExpectations.json @@ -1817,6 +1817,12 @@ "parameters": ["firefox", "webDriverBiDi"], "expectations": ["FAIL"] }, + { + "testIdPattern": "[jshandle.spec] JSHandle Page.evaluateHandle should accept object handle as an argument", + "platforms": ["darwin"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["PASS", "FAIL"] + }, { "testIdPattern": "[queryhandler.spec] Query handler tests P selectors should work ARIA selectors", "platforms": ["darwin", "linux", "win32"], @@ -1840,5 +1846,227 @@ "platforms": ["darwin", "linux", "win32"], "parameters": ["firefox", "webDriverBiDi"], "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec]", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should work with redirects", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should return response when page changes its URL after load", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to empty page with domcontentloaded", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should work when page calls history API in beforeunload", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to empty page with networkidle0", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to empty page with networkidle2", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should fail when navigating to bad SSL", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should work when navigating to valid url", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should work when navigating to data url", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should work when navigating to 404", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should not throw an error for a 404 response with an empty body", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should not throw an error for a 500 response with an empty body", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should return last response in redirect chain", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should wait for network idle to succeed navigation", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to dataURL and fire dataURL requests", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to URL with hash and fire requests without hash", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should work with self requesting page", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should send referer", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with both domcontentloaded and load", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with clicking on anchor links", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with history.pushState()", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with history.replaceState()", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with DOM history.back()/history.forward()", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work when subframe issues window.stop()", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL", "TIMEOUT"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goBack should work", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goBack should work with HistoryAPI", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Frame.goto should navigate subframes", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Frame.goto should reject when frame detaches", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Frame.goto should return matching responses", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Frame.waitForNavigation should work", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Frame.waitForNavigation should fail when frame detaches", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.reload should work", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should fail when navigating to bad url", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should fail when navigating to bad SSL after redirects", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[navigation.spec] navigation \"after all\" hook in \"navigation\"", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "headless", "webDriverBiDi"], + "expectations": ["FAIL"] } ] diff --git a/test/src/navigation.spec.ts b/test/src/navigation.spec.ts index 38fb6cc8607..6a79934d2b4 100644 --- a/test/src/navigation.spec.ts +++ b/test/src/navigation.spec.ts @@ -25,11 +25,12 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, } from './mocha-utils.js'; -import utils from './utils.js'; +import {attachFrame, isFavicon, waitEvent} from './utils.js'; describe('navigation', function () { setupTestBrowserHooks(); setupTestPageAndContextHooks(); + describe('Page.goto', function () { it('should work', async () => { const {page, server} = getTestState(); @@ -361,10 +362,14 @@ describe('navigation', function () { server.waitForRequest('/fetch-request-a.js'), server.waitForRequest('/fetch-request-b.js'), server.waitForRequest('/fetch-request-c.js'), - ]); - const secondFetchResourceRequested = server.waitForRequest( - '/fetch-request-d.js' - ); + ]).catch(() => { + // Ignore Error that arise from test server during hooks + }); + const secondFetchResourceRequested = server + .waitForRequest('/fetch-request-d.js') + .catch(() => { + // Ignore Error that arise from test server during hooks + }); // Navigate to a page which loads immediately and then does a bunch of // requests via javascript's fetch method. @@ -466,7 +471,7 @@ describe('navigation', function () { const requests: HTTPRequest[] = []; page.on('request', request => { - return !utils.isFavicon(request) && requests.push(request); + return !isFavicon(request) && requests.push(request); }); const dataURL = 'data:text/html,
yo
'; const response = (await page.goto(dataURL))!; @@ -479,7 +484,7 @@ describe('navigation', function () { const requests: HTTPRequest[] = []; page.on('request', request => { - return !utils.isFavicon(request) && requests.push(request); + return !isFavicon(request) && requests.push(request); }); const response = (await page.goto(server.EMPTY_PAGE + '#hash'))!; expect(response.status()).toBe(200); @@ -509,13 +514,17 @@ describe('navigation', function () { it('should send referer', async () => { const {page, server} = getTestState(); - const [request1, request2] = await Promise.all([ + const requests = Promise.all([ server.waitForRequest('/grid.html'), server.waitForRequest('/digits/1.png'), page.goto(server.PREFIX + '/grid.html', { referer: 'http://google.com/', }), - ]); + ]).catch(() => { + return []; + }); + + const [request1, request2] = await requests; expect(request1.headers['referer']).toBe('http://google.com/'); // Make sure subresources do not inherit referer. expect(request2.headers['referer']).toBe(server.PREFIX + '/grid.html'); @@ -530,7 +539,9 @@ describe('navigation', function () { page.goto(server.PREFIX + '/grid.html', { referrerPolicy: 'no-referer', }), - ]); + ]).catch(() => { + return []; + }); expect(request1.headers['referer']).toBeUndefined(); expect(request2.headers['referer']).toBe(server.PREFIX + '/grid.html'); }); @@ -571,7 +582,7 @@ describe('navigation', function () { return (bothFired = true); }); - await server.waitForRequest('/one-style.css'); + await server.waitForRequest('/one-style.css').catch(() => {}); await domContentLoadedPromise; expect(bothFired).toBe(false); response.end(); @@ -659,7 +670,7 @@ describe('navigation', function () { const navigationPromise = page.goto( server.PREFIX + '/frames/one-frame.html' ); - const frame = await utils.waitEvent(page, 'frameattached'); + const frame = await waitEvent(page, 'frameattached'); await new Promise(fulfill => { page.on('framenavigated', f => { if (f === frame) { @@ -737,7 +748,7 @@ describe('navigation', function () { .catch(error_ => { return error_; }); - await server.waitForRequest('/empty.html'); + await server.waitForRequest('/empty.html').catch(() => {}); await page.$eval('iframe', frame => { return frame.remove(); @@ -753,9 +764,9 @@ describe('navigation', function () { await page.goto(server.EMPTY_PAGE); // Attach three frames. const frames = await Promise.all([ - utils.attachFrame(page, 'frame1', server.EMPTY_PAGE), - utils.attachFrame(page, 'frame2', server.EMPTY_PAGE), - utils.attachFrame(page, 'frame3', server.EMPTY_PAGE), + attachFrame(page, 'frame1', server.EMPTY_PAGE), + attachFrame(page, 'frame2', server.EMPTY_PAGE), + attachFrame(page, 'frame3', server.EMPTY_PAGE), ]); // Navigate all frames to the same URL. const serverResponses: ServerResponse[] = [];