chore: add BiDi for goto navigation (#9795)

This commit is contained in:
Nikolay Vitkov 2023-03-10 16:59:02 +01:00 committed by GitHub
parent 07391bbf5f
commit 175362c048
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 409 additions and 51 deletions

View File

@ -34,9 +34,9 @@ The constructor for this class is marked as internal. Third-party code should no
## Properties ## Properties
| Property | Modifiers | Type | Description | | Property | Modifiers | Type | Description |
| ------------------------------------------- | --------------------- | ---------- | ----------------------------------------------------------------- | | ------------------------------------------- | --------------------- | --------------------------------------- | ----------------------------------------------------------------- |
| [client](./puppeteer.httprequest.client.md) | <code>readonly</code> | CDPSession | Warning! Using this client can break Puppeteer. Use with caution. | | [client](./puppeteer.httprequest.client.md) | <code>readonly</code> | [CDPSession](./puppeteer.cdpsession.md) | Warning! Using this client can break Puppeteer. Use with caution. |
## Methods ## Methods

13
package-lock.json generated
View File

@ -2625,8 +2625,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/chromium-bidi": { "node_modules/chromium-bidi": {
"version": "0.4.4", "version": "0.4.5",
"license": "Apache-2.0", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.5.tgz",
"integrity": "sha512-rkav9YzRfAshSTG3wNXF7P7yNiI29QAo1xBXElPoCoSQR5n20q3cOyVhDv6S7+GlF/CJ/emUxlQiR0xOPurkGg==",
"dependencies": { "dependencies": {
"mitt": "3.0.0" "mitt": "3.0.0"
}, },
@ -9228,7 +9229,7 @@
"version": "19.7.4", "version": "19.7.4",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"chromium-bidi": "0.4.4", "chromium-bidi": "0.4.5",
"cross-fetch": "3.1.5", "cross-fetch": "3.1.5",
"debug": "4.3.4", "debug": "4.3.4",
"devtools-protocol": "0.0.1094867", "devtools-protocol": "0.0.1094867",
@ -11025,7 +11026,9 @@
"version": "1.1.4" "version": "1.1.4"
}, },
"chromium-bidi": { "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": { "requires": {
"mitt": "3.0.0" "mitt": "3.0.0"
} }
@ -13982,7 +13985,7 @@
"puppeteer-core": { "puppeteer-core": {
"version": "file:packages/puppeteer-core", "version": "file:packages/puppeteer-core",
"requires": { "requires": {
"chromium-bidi": "0.4.4", "chromium-bidi": "0.4.5",
"cross-fetch": "3.1.5", "cross-fetch": "3.1.5",
"debug": "4.3.4", "debug": "4.3.4",
"devtools-protocol": "0.0.1094867", "devtools-protocol": "0.0.1094867",

View File

@ -131,7 +131,7 @@
"author": "The Chromium Authors", "author": "The Chromium Authors",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"chromium-bidi": "0.4.4", "chromium-bidi": "0.4.5",
"cross-fetch": "3.1.5", "cross-fetch": "3.1.5",
"debug": "4.3.4", "debug": "4.3.4",
"devtools-protocol": "0.0.1094867", "devtools-protocol": "0.0.1094867",

View File

@ -14,12 +14,11 @@
* limitations under the License. * limitations under the License.
*/ */
import {Protocol} from 'devtools-protocol'; import {Protocol} from 'devtools-protocol';
import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
import {assert} from '../util/assert.js'; import {assert} from '../util/assert.js';
import {CDPSession} from './Connection.js';
import {ProtocolError} from './Errors.js'; import {ProtocolError} from './Errors.js';
import {EventEmitter} from './EventEmitter.js';
import {Frame} from './Frame.js'; import {Frame} from './Frame.js';
import {HTTPResponse} from './HTTPResponse.js'; import {HTTPResponse} from './HTTPResponse.js';
import {debugError, isString} from './util.js'; import {debugError, isString} from './util.js';
@ -74,13 +73,6 @@ export type ResourceType = Lowercase<Protocol.Network.ResourceType>;
*/ */
export const DEFAULT_INTERCEPT_RESOLUTION_PRIORITY = 0; export const DEFAULT_INTERCEPT_RESOLUTION_PRIORITY = 0;
interface CDPSession extends EventEmitter {
send<T extends keyof ProtocolMapping.Commands>(
method: T,
...paramArgs: ProtocolMapping.Commands[T]['paramsType']
): Promise<ProtocolMapping.Commands[T]['returnType']>;
}
/** /**
* Represents an HTTP request sent by a page. * Represents an HTTP request sent by a page.
* @remarks * @remarks

View File

@ -14,10 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
import {Protocol} from 'devtools-protocol'; 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 {ProtocolError} from './Errors.js';
import {EventEmitter} from './EventEmitter.js';
import {Frame} from './Frame.js'; import {Frame} from './Frame.js';
import {HTTPRequest} from './HTTPRequest.js'; import {HTTPRequest} from './HTTPRequest.js';
import {SecurityDetails} from './SecurityDetails.js'; import {SecurityDetails} from './SecurityDetails.js';
@ -30,13 +29,6 @@ export interface RemoteAddress {
port?: number; port?: number;
} }
interface CDPSession extends EventEmitter {
send<T extends keyof ProtocolMapping.Commands>(
method: T,
...paramArgs: ProtocolMapping.Commands[T]['paramsType']
): Promise<ProtocolMapping.Commands[T]['returnType']>;
}
/** /**
* The HTTPResponse class represents responses which are received by the * The HTTPResponse class represents responses which are received by the
* {@link Page} class. * {@link Page} class.

View File

@ -822,7 +822,7 @@ export class CDPPage extends Page {
} }
const textTokens = []; const textTokens = [];
for (const arg of args) { for (const arg of args) {
const remoteObject = arg.remoteObject() as Protocol.Runtime.RemoteObject; const remoteObject = arg.remoteObject();
if (remoteObject.objectId) { if (remoteObject.objectId) {
textTokens.push(arg.toString()); textTokens.push(arg.toString());
} else { } else {

View File

@ -52,6 +52,10 @@ interface Commands {
params: Bidi.BrowsingContext.CloseParameters; params: Bidi.BrowsingContext.CloseParameters;
returnType: Bidi.BrowsingContext.CloseResult; returnType: Bidi.BrowsingContext.CloseResult;
}; };
'browsingContext.navigate': {
params: Bidi.BrowsingContext.NavigateParameters;
returnType: Bidi.BrowsingContext.NavigateResult;
};
'session.new': { 'session.new': {
params: {capabilities?: Record<any, unknown>}; // TODO: Update Types in chromium bidi params: {capabilities?: Record<any, unknown>}; // TODO: Update Types in chromium bidi
@ -148,17 +152,20 @@ export class Connection extends EventEmitter {
if (callback.method === 'browsingContext.create') { if (callback.method === 'browsingContext.create') {
this.#contexts.set( this.#contexts.set(
object.result.context, object.result.context,
new Context(this, object.result.context) new Context(this, object.result)
); );
} }
callback.resolve(object); callback.resolve(object);
} }
} }
} else { } else {
if ('source' in object.params && !!object.params.source.context) { let context: Context | undefined;
const context = this.#contexts.get(object.params.source.context); if ('context' in object.params) {
context?.emit(object.method, 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); this.emit(object.method, object.params);
} }

View File

@ -16,8 +16,13 @@
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; 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 {stringifyFunction} from '../../util/Function.js';
import {ProtocolError, TimeoutError} from '../Errors.js';
import {EventEmitter} from '../EventEmitter.js'; import {EventEmitter} from '../EventEmitter.js';
import {PuppeteerLifeCycleEvent} from '../LifecycleWatcher.js';
import {TimeoutSettings} from '../TimeoutSettings.js';
import {EvaluateFunc, HandleFor} from '../types.js'; import {EvaluateFunc, HandleFor} from '../types.js';
import {isString} from '../util.js'; import {isString} from '../util.js';
@ -26,17 +31,31 @@ import {ElementHandle} from './ElementHandle.js';
import {JSHandle} from './JSHandle.js'; import {JSHandle} from './JSHandle.js';
import {BidiSerializer} from './Serializer.js'; import {BidiSerializer} from './Serializer.js';
/**
* @internal
*/
const puppeteerToReadinessState = new Map<
PuppeteerLifeCycleEvent,
Bidi.BrowsingContext.ReadinessState
>([
['load', 'complete'],
['domcontentloaded', 'interactive'],
]);
/** /**
* @internal * @internal
*/ */
export class Context extends EventEmitter { export class Context extends EventEmitter {
#connection: Connection; #connection: Connection;
#url: string;
_contextId: string; _contextId: string;
_timeoutSettings = new TimeoutSettings();
constructor(connection: Connection, contextId: string) { constructor(connection: Connection, result: Bidi.BrowsingContext.Info) {
super(); super();
this.#connection = connection; this.#connection = connection;
this._contextId = contextId; this._contextId = result.context;
this.#url = result.url;
} }
get connection(): Connection { get connection(): Connection {
@ -124,6 +143,76 @@ export class Context extends EventEmitter {
? BidiSerializer.deserialize(result.result) ? BidiSerializer.deserialize(result.result)
: getBidiHandle(this, result.result); : getBidiHandle(this, result.result);
} }
async goto(
url: string,
options: WaitForOptions & {
referer?: string | undefined;
referrerPolicy?: string | undefined;
} = {}
): Promise<null> {
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;
}
} }
/** /**

View File

@ -16,8 +16,13 @@
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; 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 {ConsoleMessage, ConsoleMessageLocation} from '../ConsoleMessage.js';
import {HTTPResponse} from '../HTTPResponse.js';
import {EvaluateFunc, HandleFor} from '../types.js'; import {EvaluateFunc, HandleFor} from '../types.js';
import {Context, getBidiHandle} from './Context.js'; import {Context, getBidiHandle} from './Context.js';
@ -30,8 +35,11 @@ export class Page extends PageBase {
#context: Context; #context: Context;
#subscribedEvents = [ #subscribedEvents = [
'log.entryAdded', 'log.entryAdded',
'browsingContext.load',
] as Bidi.Session.SubscribeParameters['events']; ] as Bidi.Session.SubscribeParameters['events'];
#boundOnLogEntryAdded = this.#onLogEntryAdded.bind(this); #boundOnLogEntryAdded = this.#onLogEntryAdded.bind(this);
#boundOnLoaded = this.#onLoad.bind(this);
constructor(context: Context) { constructor(context: Context) {
super(); super();
@ -43,6 +51,7 @@ export class Page extends PageBase {
}); });
this.#context.on('log.entryAdded', this.#boundOnLogEntryAdded); this.#context.on('log.entryAdded', this.#boundOnLogEntryAdded);
this.#context.on('browsingContext.load', this.#boundOnLoaded);
} }
#onLogEntryAdded(event: Bidi.Log.LogEntry): void { #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<void> { override async close(): Promise<void> {
await this.#context.connection.send('session.unsubscribe', { await this.#context.connection.send('session.unsubscribe', {
events: this.#subscribedEvents, events: this.#subscribedEvents,
@ -93,6 +106,7 @@ export class Page extends PageBase {
}); });
this.#context.off('log.entryAdded', this.#boundOnLogEntryAdded); this.#context.off('log.entryAdded', this.#boundOnLogEntryAdded);
this.#context.off('browsingContext.load', this.#boundOnLogEntryAdded);
} }
override async evaluateHandle< override async evaluateHandle<
@ -114,6 +128,28 @@ export class Page extends PageBase {
): Promise<Awaited<ReturnType<Func>>> { ): Promise<Awaited<ReturnType<Func>>> {
return this.#context.evaluate(pageFunction, ...args); return this.#context.evaluate(pageFunction, ...args);
} }
override async goto(
url: string,
options?: WaitForOptions & {
referer?: string | undefined;
referrerPolicy?: string | undefined;
}
): Promise<HTTPResponse | null> {
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( function isConsoleLogEntry(

View File

@ -1817,6 +1817,12 @@
"parameters": ["firefox", "webDriverBiDi"], "parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"] "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", "testIdPattern": "[queryhandler.spec] Query handler tests P selectors should work ARIA selectors",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -1840,5 +1846,227 @@
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"], "parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"] "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"]
} }
] ]

View File

@ -25,11 +25,12 @@ import {
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
} from './mocha-utils.js'; } from './mocha-utils.js';
import utils from './utils.js'; import {attachFrame, isFavicon, waitEvent} from './utils.js';
describe('navigation', function () { describe('navigation', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
describe('Page.goto', function () { describe('Page.goto', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -361,10 +362,14 @@ describe('navigation', function () {
server.waitForRequest('/fetch-request-a.js'), server.waitForRequest('/fetch-request-a.js'),
server.waitForRequest('/fetch-request-b.js'), server.waitForRequest('/fetch-request-b.js'),
server.waitForRequest('/fetch-request-c.js'), server.waitForRequest('/fetch-request-c.js'),
]); ]).catch(() => {
const secondFetchResourceRequested = server.waitForRequest( // Ignore Error that arise from test server during hooks
'/fetch-request-d.js' });
); 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 // Navigate to a page which loads immediately and then does a bunch of
// requests via javascript's fetch method. // requests via javascript's fetch method.
@ -466,7 +471,7 @@ describe('navigation', function () {
const requests: HTTPRequest[] = []; const requests: HTTPRequest[] = [];
page.on('request', request => { page.on('request', request => {
return !utils.isFavicon(request) && requests.push(request); return !isFavicon(request) && requests.push(request);
}); });
const dataURL = 'data:text/html,<div>yo</div>'; const dataURL = 'data:text/html,<div>yo</div>';
const response = (await page.goto(dataURL))!; const response = (await page.goto(dataURL))!;
@ -479,7 +484,7 @@ describe('navigation', function () {
const requests: HTTPRequest[] = []; const requests: HTTPRequest[] = [];
page.on('request', request => { 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'))!; const response = (await page.goto(server.EMPTY_PAGE + '#hash'))!;
expect(response.status()).toBe(200); expect(response.status()).toBe(200);
@ -509,13 +514,17 @@ describe('navigation', function () {
it('should send referer', async () => { it('should send referer', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
const [request1, request2] = await Promise.all([ const requests = Promise.all([
server.waitForRequest('/grid.html'), server.waitForRequest('/grid.html'),
server.waitForRequest('/digits/1.png'), server.waitForRequest('/digits/1.png'),
page.goto(server.PREFIX + '/grid.html', { page.goto(server.PREFIX + '/grid.html', {
referer: 'http://google.com/', referer: 'http://google.com/',
}), }),
]); ]).catch(() => {
return [];
});
const [request1, request2] = await requests;
expect(request1.headers['referer']).toBe('http://google.com/'); expect(request1.headers['referer']).toBe('http://google.com/');
// Make sure subresources do not inherit referer. // Make sure subresources do not inherit referer.
expect(request2.headers['referer']).toBe(server.PREFIX + '/grid.html'); expect(request2.headers['referer']).toBe(server.PREFIX + '/grid.html');
@ -530,7 +539,9 @@ describe('navigation', function () {
page.goto(server.PREFIX + '/grid.html', { page.goto(server.PREFIX + '/grid.html', {
referrerPolicy: 'no-referer', referrerPolicy: 'no-referer',
}), }),
]); ]).catch(() => {
return [];
});
expect(request1.headers['referer']).toBeUndefined(); expect(request1.headers['referer']).toBeUndefined();
expect(request2.headers['referer']).toBe(server.PREFIX + '/grid.html'); expect(request2.headers['referer']).toBe(server.PREFIX + '/grid.html');
}); });
@ -571,7 +582,7 @@ describe('navigation', function () {
return (bothFired = true); return (bothFired = true);
}); });
await server.waitForRequest('/one-style.css'); await server.waitForRequest('/one-style.css').catch(() => {});
await domContentLoadedPromise; await domContentLoadedPromise;
expect(bothFired).toBe(false); expect(bothFired).toBe(false);
response.end(); response.end();
@ -659,7 +670,7 @@ describe('navigation', function () {
const navigationPromise = page.goto( const navigationPromise = page.goto(
server.PREFIX + '/frames/one-frame.html' server.PREFIX + '/frames/one-frame.html'
); );
const frame = await utils.waitEvent(page, 'frameattached'); const frame = await waitEvent(page, 'frameattached');
await new Promise<void>(fulfill => { await new Promise<void>(fulfill => {
page.on('framenavigated', f => { page.on('framenavigated', f => {
if (f === frame) { if (f === frame) {
@ -737,7 +748,7 @@ describe('navigation', function () {
.catch(error_ => { .catch(error_ => {
return error_; return error_;
}); });
await server.waitForRequest('/empty.html'); await server.waitForRequest('/empty.html').catch(() => {});
await page.$eval('iframe', frame => { await page.$eval('iframe', frame => {
return frame.remove(); return frame.remove();
@ -753,9 +764,9 @@ describe('navigation', function () {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
// Attach three frames. // Attach three frames.
const frames = await Promise.all([ const frames = await Promise.all([
utils.attachFrame(page, 'frame1', server.EMPTY_PAGE), attachFrame(page, 'frame1', server.EMPTY_PAGE),
utils.attachFrame(page, 'frame2', server.EMPTY_PAGE), attachFrame(page, 'frame2', server.EMPTY_PAGE),
utils.attachFrame(page, 'frame3', server.EMPTY_PAGE), attachFrame(page, 'frame3', server.EMPTY_PAGE),
]); ]);
// Navigate all frames to the same URL. // Navigate all frames to the same URL.
const serverResponses: ServerResponse[] = []; const serverResponses: ServerResponse[] = [];