From 0757d04358d1d6df26303f1ee860a7ed42a41c7a Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Fri, 28 Jul 2023 11:11:14 +0200 Subject: [PATCH] chore: update chromium-bidi (#10620) --- package-lock.json | 36 +++++--- packages/puppeteer-core/package.json | 2 +- .../puppeteer-core/src/api/ElementHandle.ts | 3 + .../src/common/bidi/BidiOverCDP.ts | 37 ++++---- .../puppeteer-core/src/common/bidi/Browser.ts | 24 +++-- .../src/common/bidi/BrowserContext.ts | 4 +- .../src/common/bidi/BrowsingContext.ts | 18 ++-- .../src/common/bidi/Connection.ts | 89 ++++++------------- .../src/common/bidi/ElementHandle.ts | 14 +-- .../puppeteer-core/src/common/bidi/Frame.ts | 31 +++++-- .../src/common/bidi/HTTPRequest.ts | 8 +- .../src/common/bidi/HTTPResponse.ts | 13 +-- .../puppeteer-core/src/common/bidi/Input.ts | 85 +++++++++++------- .../src/common/bidi/JSHandle.ts | 11 ++- .../src/common/bidi/NetworkManager.ts | 8 +- .../puppeteer-core/src/common/bidi/Page.ts | 58 +++++++++--- .../puppeteer-core/src/common/bidi/Realm.ts | 6 +- .../src/common/bidi/Serializer.ts | 62 ++++++------- .../puppeteer-core/src/common/bidi/utils.ts | 2 +- packages/puppeteer-core/src/common/util.ts | 24 ++--- test/TestExpectations.json | 84 +++++++++++------ test/src/elementhandle.spec.ts | 4 +- test/src/frame.spec.ts | 1 + test/src/launcher.spec.ts | 25 +++--- test/src/mocha-utils.ts | 5 +- test/src/navigation.spec.ts | 2 - test/src/stacktrace.spec.ts | 19 ++-- tools/mochaRunner/src/utils.ts | 7 +- 28 files changed, 397 insertions(+), 285 deletions(-) diff --git a/package-lock.json b/package-lock.json index c05de4ba9f1..4104c982b10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3411,16 +3411,21 @@ } }, "node_modules/chromium-bidi": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", - "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.19.tgz", + "integrity": "sha512-8pzgvc/f4OpQMNRqgwqJKLd1cJFzKgh/foXbUgSD+lCyB+SSHUgo6FYaOHoMfHv10+0kP3/WYxKVGV1MH8QjtQ==", "dependencies": { - "mitt": "3.0.0" + "mitt": "3.0.1" }, "peerDependencies": { "devtools-protocol": "*" } }, + "node_modules/chromium-bidi/node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + }, "node_modules/ci-info": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", @@ -7222,6 +7227,7 @@ }, "node_modules/mitt": { "version": "3.0.0", + "dev": true, "license": "MIT" }, "node_modules/mkdirp": { @@ -10563,7 +10569,7 @@ "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "1.4.6", - "chromium-bidi": "0.4.16", + "chromium-bidi": "0.4.19", "cross-fetch": "4.0.0", "debug": "4.3.4", "devtools-protocol": "0.0.1147663", @@ -12894,11 +12900,18 @@ "dev": true }, "chromium-bidi": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", - "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.19.tgz", + "integrity": "sha512-8pzgvc/f4OpQMNRqgwqJKLd1cJFzKgh/foXbUgSD+lCyB+SSHUgo6FYaOHoMfHv10+0kP3/WYxKVGV1MH8QjtQ==", "requires": { - "mitt": "3.0.0" + "mitt": "3.0.1" + }, + "dependencies": { + "mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + } } }, "ci-info": { @@ -15419,7 +15432,8 @@ } }, "mitt": { - "version": "3.0.0" + "version": "3.0.0", + "dev": true }, "mkdirp": { "version": "1.0.4", @@ -16338,7 +16352,7 @@ "version": "file:packages/puppeteer-core", "requires": { "@puppeteer/browsers": "1.4.6", - "chromium-bidi": "0.4.16", + "chromium-bidi": "0.4.19", "cross-fetch": "4.0.0", "debug": "4.3.4", "devtools-protocol": "0.0.1147663", diff --git a/packages/puppeteer-core/package.json b/packages/puppeteer-core/package.json index c39bfaa1304..79c802d132b 100644 --- a/packages/puppeteer-core/package.json +++ b/packages/puppeteer-core/package.json @@ -144,7 +144,7 @@ "author": "The Chromium Authors", "license": "Apache-2.0", "dependencies": { - "chromium-bidi": "0.4.16", + "chromium-bidi": "0.4.19", "cross-fetch": "4.0.0", "debug": "4.3.4", "devtools-protocol": "0.0.1147663", diff --git a/packages/puppeteer-core/src/api/ElementHandle.ts b/packages/puppeteer-core/src/api/ElementHandle.ts index f670fba21b1..2d481d23992 100644 --- a/packages/puppeteer-core/src/api/ElementHandle.ts +++ b/packages/puppeteer-core/src/api/ElementHandle.ts @@ -1074,6 +1074,9 @@ export class ElementHandle< } } +/** + * @public + */ export interface AutofillData { creditCard: { // See https://chromedevtools.github.io/devtools-protocol/tot/Autofill/#type-CreditCard. diff --git a/packages/puppeteer-core/src/common/bidi/BidiOverCDP.ts b/packages/puppeteer-core/src/common/bidi/BidiOverCDP.ts index 95a13ceb035..56335f1f59c 100644 --- a/packages/puppeteer-core/src/common/bidi/BidiOverCDP.ts +++ b/packages/puppeteer-core/src/common/bidi/BidiOverCDP.ts @@ -86,7 +86,7 @@ class CDPConnectionAdapter { throw new Error('Unknown CDP session with id' + id); } if (!this.#adapters.has(session)) { - const adapter = new CDPClientAdapter(session); + const adapter = new CDPClientAdapter(session, id, this.#browser); this.#adapters.set(session, adapter); return adapter; } @@ -113,13 +113,25 @@ class CDPClientAdapter> { #closed = false; #client: T; + sessionId: string | undefined = undefined; + #browserClient?: BidiMapper.CdpClient; - constructor(client: T) { + constructor( + client: T, + sessionId?: string, + browserClient?: BidiMapper.CdpClient + ) { super(); this.#client = client; + this.sessionId = sessionId; + this.#browserClient = browserClient; this.#client.on('*', this.#forwardMessage as Handler); } + browserClient(): BidiMapper.CdpClient { + return this.#browserClient!; + } + #forwardMessage = ( method: T, event: CdpEvents[T] @@ -163,32 +175,27 @@ class NoOpTransport extends BidiMapper.EventEmitter implements BidiMapper.BidiTransport { - #onMessage: ( - message: Bidi.Message.RawCommandRequest - ) => Promise | void = async ( - _m: Bidi.Message.RawCommandRequest - ): Promise => { - return; - }; + #onMessage: (message: Bidi.ChromiumBidi.Command) => Promise | void = + async (_m: Bidi.ChromiumBidi.Command): Promise => { + return; + }; - emitMessage(message: Bidi.Message.RawCommandRequest) { + emitMessage(message: Bidi.ChromiumBidi.Command) { void this.#onMessage(message); } setOnMessage( - onMessage: (message: Bidi.Message.RawCommandRequest) => Promise | void + onMessage: (message: Bidi.ChromiumBidi.Command) => Promise | void ): void { this.#onMessage = onMessage; } - async sendMessage(message: Bidi.Message.OutgoingMessage): Promise { + async sendMessage(message: Bidi.ChromiumBidi.Message): Promise { this.emit('bidiResponse', message); } close() { - this.#onMessage = async ( - _m: Bidi.Message.RawCommandRequest - ): Promise => { + this.#onMessage = async (_m: Bidi.ChromiumBidi.Command): Promise => { return; }; } diff --git a/packages/puppeteer-core/src/common/bidi/Browser.ts b/packages/puppeteer-core/src/common/bidi/Browser.ts index 9962dbad1ca..e869ce524ef 100644 --- a/packages/puppeteer-core/src/common/bidi/Browser.ts +++ b/packages/puppeteer-core/src/common/bidi/Browser.ts @@ -49,7 +49,8 @@ import {debugError} from './utils.js'; * @internal */ export class Browser extends BrowserBase { - static readonly subscribeModules: Bidi.Session.SubscriptionRequestEvent[] = [ + // TODO: Update generator to include fully module + static readonly subscribeModules: string[] = [ 'browsingContext', 'network', 'log', @@ -114,12 +115,16 @@ export class Browser extends BrowserBase { #contexts: BrowserContext[] = []; #browserTarget: BiDiBrowserTarget; - #connectionEventHandlers = new Map>([ + #connectionEventHandlers = new Map< + Bidi.BrowsingContextEvent['method'], + Handler + >([ ['browsingContext.contextCreated', this.#onContextCreated.bind(this)], ['browsingContext.contextDestroyed', this.#onContextDestroyed.bind(this)], + ['browsingContext.domContentLoaded', this.#onContextDomLoaded.bind(this)], ['browsingContext.fragmentNavigated', this.#onContextNavigation.bind(this)], ['browsingContext.navigationStarted', this.#onContextNavigation.bind(this)], - ]) as Map; + ]); constructor( opts: Options & { @@ -151,6 +156,15 @@ export class Browser extends BrowserBase { } } + #onContextDomLoaded(event: Bidi.BrowsingContext.Info) { + const context = this.#connection.getBrowsingContext(event.context); + context.url = event.url; + const target = this.#targets.get(event.context); + if (target) { + this.emit(BrowserEmittedEvents.TargetChanged, target); + } + } + #onContextNavigation(event: Bidi.BrowsingContext.NavigationInfo) { const context = this.#connection.getBrowsingContext(event.context); context.url = event.url; @@ -163,7 +177,7 @@ export class Browser extends BrowserBase { } } - #onContextCreated(event: Bidi.BrowsingContext.ContextCreatedEvent['params']) { + #onContextCreated(event: Bidi.BrowsingContext.ContextCreated['params']) { const context = new BrowsingContext(this.#connection, event); this.#connection.registerBrowsingContexts(context); // TODO: once more browsing context types are supported, this should be @@ -197,7 +211,7 @@ export class Browser extends BrowserBase { } async #onContextDestroyed( - event: Bidi.BrowsingContext.ContextDestroyedEvent['params'] + event: Bidi.BrowsingContext.ContextDestroyed['params'] ) { const context = this.#connection.getBrowsingContext(event.context); const topLevelContext = this.#connection.getTopLevelContext(event.context); diff --git a/packages/puppeteer-core/src/common/bidi/BrowserContext.ts b/packages/puppeteer-core/src/common/bidi/BrowserContext.ts index 3a0f6ce69bc..b770c76bd3c 100644 --- a/packages/puppeteer-core/src/common/bidi/BrowserContext.ts +++ b/packages/puppeteer-core/src/common/bidi/BrowserContext.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; + import {BrowserContext as BrowserContextBase} from '../../api/BrowserContext.js'; import {Page as PageBase} from '../../api/Page.js'; import {Target} from '../../api/Target.js'; @@ -66,7 +68,7 @@ export class BrowserContext extends BrowserContextBase { override async newPage(): Promise { const {result} = await this.#connection.send('browsingContext.create', { - type: 'tab', + type: Bidi.BrowsingContext.CreateType.Tab, }); const target = this.#browser._getTargetById(result.context); diff --git a/packages/puppeteer-core/src/common/bidi/BrowsingContext.ts b/packages/puppeteer-core/src/common/bidi/BrowsingContext.ts index 52494cbd074..15322a09b0a 100644 --- a/packages/puppeteer-core/src/common/bidi/BrowsingContext.ts +++ b/packages/puppeteer-core/src/common/bidi/BrowsingContext.ts @@ -32,8 +32,8 @@ const lifeCycleToReadinessState = new Map< PuppeteerLifeCycleEvent, Bidi.BrowsingContext.ReadinessState >([ - ['load', 'complete'], - ['domcontentloaded', 'interactive'], + ['load', Bidi.BrowsingContext.ReadinessState.Complete], + ['domcontentloaded', Bidi.BrowsingContext.ReadinessState.Interactive], ]); /** @@ -84,7 +84,7 @@ export class CDPSessionWrapper extends EventEmitter implements CDPSession { ); } const session = await this.#sessionId.valueOrThrow(); - const result = await this.#context.connection.send('cdp.sendCommand', { + const {result} = await this.#context.connection.send('cdp.sendCommand', { method: method, params: paramArgs[0], session, @@ -140,12 +140,12 @@ export class BrowsingContext extends Realm { this.#parent = info.parent; this.#cdpSession = new CDPSessionWrapper(this); - this.on( - 'browsingContext.fragmentNavigated', - (info: Bidi.BrowsingContext.NavigationInfo) => { - this.#url = info.url; - } - ); + this.on('browsingContext.domContentLoaded', this.#updateUrl.bind(this)); + this.on('browsingContext.load', this.#updateUrl.bind(this)); + } + + #updateUrl(info: Bidi.BrowsingContext.NavigationInfo) { + this.url = info.url; } createSandboxRealm(sandbox: string): Realm { diff --git a/packages/puppeteer-core/src/common/bidi/Connection.ts b/packages/puppeteer-core/src/common/bidi/Connection.ts index 636920d30fb..ed1665de82b 100644 --- a/packages/puppeteer-core/src/common/bidi/Connection.ts +++ b/packages/puppeteer-core/src/common/bidi/Connection.ts @@ -22,33 +22,11 @@ import {debug} from '../Debug.js'; import {EventEmitter} from '../EventEmitter.js'; import {BrowsingContext, cdpSessions} from './BrowsingContext.js'; +import {debugError} from './utils.js'; const debugProtocolSend = debug('puppeteer:webDriverBiDi:SEND ►'); const debugProtocolReceive = debug('puppeteer:webDriverBiDi:RECV ◀'); -interface Capability { - // session.CapabilityRequest = { - // ? acceptInsecureCerts: bool, - // ? browserName: text, - // ? browserVersion: text, - // ? platformName: text, - // ? proxy: { - // ? proxyType: "pac" / "direct" / "autodetect" / "system" / "manual", - // ? proxyAutoconfigUrl: text, - // ? ftpProxy: text, - // ? httpProxy: text, - // ? noProxy: [*text], - // ? sslProxy: text, - // ? socksProxy: text, - // ? socksVersion: 0..255, - // }, - // Extensible - // }; - acceptInsecureCerts?: boolean; - browserName?: string; - browserVersion?: string; -} - /** * @internal */ @@ -59,11 +37,11 @@ interface Commands { }; 'script.callFunction': { params: Bidi.Script.CallFunctionParameters; - returnType: Bidi.Script.CallFunctionResult; + returnType: Bidi.Script.EvaluateResult; }; 'script.disown': { params: Bidi.Script.DisownParameters; - returnType: Bidi.Script.DisownResult; + returnType: Bidi.EmptyResult; }; 'script.addPreloadScript': { params: Bidi.Script.AddPreloadScriptParameters; @@ -76,7 +54,7 @@ interface Commands { }; 'browsingContext.close': { params: Bidi.BrowsingContext.CloseParameters; - returnType: Bidi.Message.EmptyResult; + returnType: Bidi.EmptyResult; }; 'browsingContext.getTree': { params: Bidi.BrowsingContext.GetTreeParameters; @@ -88,7 +66,7 @@ interface Commands { }; 'browsingContext.reload': { params: Bidi.BrowsingContext.ReloadParameters; - returnType: Bidi.Message.EmptyResult; + returnType: Bidi.EmptyResult; }; 'browsingContext.print': { params: Bidi.BrowsingContext.PrintParameters; @@ -101,27 +79,16 @@ interface Commands { 'input.performActions': { params: Bidi.Input.PerformActionsParameters; - returnType: Bidi.Message.EmptyResult; + returnType: Bidi.EmptyResult; }; 'input.releaseActions': { params: Bidi.Input.ReleaseActionsParameters; - returnType: Bidi.Message.EmptyResult; + returnType: Bidi.EmptyResult; }; 'session.new': { - params: { - // capabilities: session.CapabilitiesRequest - capabilities?: { - // session.CapabilitiesRequest = { - // ? alwaysMatch: session.CapabilityRequest, - // ? firstMatch: [*session.CapabilityRequest] - // } - alwaysMatch?: Capability; - }; - }; // TODO: Update Types in chromium bidi - returnType: { - result: {sessionId: string; capabilities: Capability}; - }; + params: Bidi.Session.NewParameters; + returnType: Bidi.Session.NewResult; }; 'session.status': { params: object; @@ -129,18 +96,18 @@ interface Commands { }; 'session.subscribe': { params: Bidi.Session.SubscriptionRequest; - returnType: Bidi.Message.EmptyResult; + returnType: Bidi.EmptyResult; }; 'session.unsubscribe': { params: Bidi.Session.SubscriptionRequest; - returnType: Bidi.Message.EmptyResult; + returnType: Bidi.EmptyResult; }; 'cdp.sendCommand': { - params: Bidi.Cdp.SendCommandParams; + params: Bidi.Cdp.SendCommandParameters; returnType: Bidi.Cdp.SendCommandResult; }; 'cdp.getSession': { - params: Bidi.Cdp.GetSessionParams; + params: Bidi.Cdp.GetSessionParameters; returnType: Bidi.Cdp.GetSessionResult; }; } @@ -184,16 +151,16 @@ export class Connection extends EventEmitter { send( method: T, params: Commands[T]['params'] - ): Promise { + ): Promise<{result: Commands[T]['returnType']}> { return this.#callbacks.create(method, this.#timeout, id => { const stringifiedMessage = JSON.stringify({ id, method, params, - } as Bidi.Message.CommandRequest); + } as Bidi.Command); debugProtocolSend(stringifiedMessage); this.#transport.send(stringifiedMessage); - }) as Promise; + }) as Promise<{result: Commands[T]['returnType']}>; } /** @@ -206,27 +173,29 @@ export class Connection extends EventEmitter { }); } debugProtocolReceive(message); - const object = JSON.parse(message) as - | Bidi.Message.CommandResponse - | Bidi.Message.EventMessage; + const object = JSON.parse(message) as Bidi.ChromiumBidi.Message; - if ('id' in object) { + if ('id' in object && object.id) { if ('error' in object) { this.#callbacks.reject( object.id, - createProtocolError(object), + createProtocolError(object as Bidi.ErrorResponse), object.message ); } else { this.#callbacks.resolve(object.id, object); } } else { - this.#maybeEmitOnContext(object); - this.emit(object.method, object.params); + if ('error' in object || 'id' in object || 'launched' in object) { + debugError(object); + } else { + this.#maybeEmitOnContext(object); + this.emit(object.method, object.params); + } } } - #maybeEmitOnContext(event: Bidi.Message.EventMessage) { + #maybeEmitOnContext(event: Bidi.ChromiumBidi.Event) { let context: BrowsingContext | undefined; // Context specific events if ('context' in event.params && event.params.context) { @@ -292,7 +261,7 @@ export class Connection extends EventEmitter { /** * @internal */ -function createProtocolError(object: Bidi.Message.ErrorResult): string { +function createProtocolError(object: Bidi.ErrorResponse): string { let message = `${object.error} ${object.message}`; if (object.stacktrace) { message += ` ${object.stacktrace}`; @@ -300,8 +269,6 @@ function createProtocolError(object: Bidi.Message.ErrorResult): string { return message; } -function isCDPEvent( - event: Bidi.Message.EventMessage -): event is Bidi.Cdp.EventReceivedEvent { +function isCDPEvent(event: Bidi.ChromiumBidi.Event): event is Bidi.Cdp.Event { return event.method.startsWith('cdp.'); } diff --git a/packages/puppeteer-core/src/common/bidi/ElementHandle.ts b/packages/puppeteer-core/src/common/bidi/ElementHandle.ts index 08faf01f173..d379dd0db05 100644 --- a/packages/puppeteer-core/src/common/bidi/ElementHandle.ts +++ b/packages/puppeteer-core/src/common/bidi/ElementHandle.ts @@ -40,7 +40,7 @@ export class ElementHandle< constructor( realm: Realm, - remoteValue: Bidi.CommonDataTypes.RemoteValue, + remoteValue: Bidi.Script.RemoteValue, frame: Frame ) { super(new JSHandle(realm, remoteValue)); @@ -59,7 +59,7 @@ export class ElementHandle< return this.handle.isPrimitiveValue; } - remoteValue(): Bidi.CommonDataTypes.RemoteValue { + remoteValue(): Bidi.Script.RemoteValue { return this.handle.remoteValue(); } @@ -102,7 +102,7 @@ export class ElementHandle< Object.assign({}, options, { origin: { type: 'element' as const, - element: remoteValue as Bidi.CommonDataTypes.SharedReference, + element: remoteValue as Bidi.Script.SharedReference, }, }) ); @@ -115,7 +115,7 @@ export class ElementHandle< return this.#frame.page().mouse.move(0, 0, { origin: { type: 'element' as const, - element: remoteValue as Bidi.CommonDataTypes.SharedReference, + element: remoteValue as Bidi.Script.SharedReference, }, }); } @@ -127,7 +127,7 @@ export class ElementHandle< return this.#frame.page().touchscreen.tap(0, 0, { origin: { type: 'element' as const, - element: remoteValue as Bidi.CommonDataTypes.SharedReference, + element: remoteValue as Bidi.Script.SharedReference, }, }); } @@ -139,7 +139,7 @@ export class ElementHandle< return this.#frame.page().touchscreen.touchStart(0, 0, { origin: { type: 'element' as const, - element: remoteValue as Bidi.CommonDataTypes.SharedReference, + element: remoteValue as Bidi.Script.SharedReference, }, }); } @@ -151,7 +151,7 @@ export class ElementHandle< return this.#frame.page().touchscreen.touchMove(0, 0, { origin: { type: 'element' as const, - element: remoteValue as Bidi.CommonDataTypes.SharedReference, + element: remoteValue as Bidi.Script.SharedReference, }, }); } diff --git a/packages/puppeteer-core/src/common/bidi/Frame.ts b/packages/puppeteer-core/src/common/bidi/Frame.ts index 56db90c4e97..d687d904a0b 100644 --- a/packages/puppeteer-core/src/common/bidi/Frame.ts +++ b/packages/puppeteer-core/src/common/bidi/Frame.ts @@ -227,6 +227,8 @@ export class Frame extends BaseFrame { ) as string; const [info] = await Promise.all([ + // TODO(lightning00blade): Should also keep tack of + // navigationAborted and navigationFailed waitForEvent( this.#context, waitUntilEvent, @@ -236,15 +238,26 @@ export class Frame extends BaseFrame { timeout, this.#abortDeferred.valueOrThrow() ), - waitForEvent( - this.#context, - Bidi.BrowsingContext.EventNames.FragmentNavigated, - () => { - return true; - }, - timeout, - this.#abortDeferred.valueOrThrow() - ), + Deferred.race([ + waitForEvent( + this.#context, + Bidi.ChromiumBidi.BrowsingContext.EventNames.NavigationStarted, + () => { + return true; + }, + timeout, + this.#abortDeferred.valueOrThrow() + ), + waitForEvent( + this.#context, + Bidi.ChromiumBidi.BrowsingContext.EventNames.FragmentNavigated, + () => { + return true; + }, + timeout, + this.#abortDeferred.valueOrThrow() + ), + ]), ]); return this.#page.getNavigationResponse(info.navigation); diff --git a/packages/puppeteer-core/src/common/bidi/HTTPRequest.ts b/packages/puppeteer-core/src/common/bidi/HTTPRequest.ts index 91d00d8e5a3..eb021ac7e04 100644 --- a/packages/puppeteer-core/src/common/bidi/HTTPRequest.ts +++ b/packages/puppeteer-core/src/common/bidi/HTTPRequest.ts @@ -41,7 +41,7 @@ export class HTTPRequest extends BaseHTTPRequest { #frame: Frame | null; constructor( - event: Bidi.Network.BeforeRequestSentParams, + event: Bidi.Network.BeforeRequestSentParameters, frame: Frame | null, redirectChain: HTTPRequest[] ) { @@ -58,11 +58,11 @@ export class HTTPRequest extends BaseHTTPRequest { this._redirectChain = redirectChain ?? []; this._navigationId = event.navigation; - for (const {name, value} of event.request.headers) { + for (const header of event.request.headers) { // TODO: How to handle Binary Headers // https://w3c.github.io/webdriver-bidi/#type-network-Header - if (value) { - this.#headers[name.toLowerCase()] = value; + if (header.value.type === 'string') { + this.#headers[header.name.toLowerCase()] = header.value.value; } } } diff --git a/packages/puppeteer-core/src/common/bidi/HTTPResponse.ts b/packages/puppeteer-core/src/common/bidi/HTTPResponse.ts index 10ac181d64e..a920f47eea7 100644 --- a/packages/puppeteer-core/src/common/bidi/HTTPResponse.ts +++ b/packages/puppeteer-core/src/common/bidi/HTTPResponse.ts @@ -39,10 +39,9 @@ export class HTTPResponse extends BaseHTTPResponse { constructor( request: HTTPRequest, - responseEvent: Bidi.Network.ResponseCompletedParams + {response}: Bidi.Network.ResponseCompletedParameters ) { super(); - const {response} = responseEvent; this.#request = request; this.#remoteAddress = { @@ -54,12 +53,16 @@ export class HTTPResponse extends BaseHTTPResponse { this.#fromCache = response.fromCache; this.#status = response.status; this.#statusText = response.statusText; - // TODO: update once BiDi has types - this.#timings = (response as any).timings ?? null; + // TODO: File and issue with BiDi spec + this.#timings = null; // TODO: Removed once the Firefox implementation is compliant with https://w3c.github.io/webdriver-bidi/#get-the-response-data. for (const header of response.headers || []) { - this.#headers[header.name] = header.value ?? ''; + // TODO: How to handle Binary Headers + // https://w3c.github.io/webdriver-bidi/#type-network-Header + if (header.value.type === 'string') { + this.#headers[header.name.toLowerCase()] = header.value.value; + } } } diff --git a/packages/puppeteer-core/src/common/bidi/Input.ts b/packages/puppeteer-core/src/common/bidi/Input.ts index 869f92d2563..642cfdb0db2 100644 --- a/packages/puppeteer-core/src/common/bidi/Input.ts +++ b/packages/puppeteer-core/src/common/bidi/Input.ts @@ -41,6 +41,23 @@ const enum InputId { Finger = '__puppeteer_finger', } +enum SourceActionsType { + None = 'none', + Key = 'key', + Pointer = 'pointer', + Wheel = 'wheel', +} + +enum ActionType { + Pause = 'pause', + KeyDown = 'keyDown', + KeyUp = 'keyUp', + PointerUp = 'pointerUp', + PointerDown = 'pointerDown', + PointerMove = 'pointerMove', + Scroll = 'scroll', +} + const getBidiKeyValue = (key: KeyInput) => { switch (key) { case '\r': @@ -286,11 +303,11 @@ export class Keyboard extends BaseKeyboard { context: this.#context.id, actions: [ { - type: Bidi.Input.SourceActionsType.Key, + type: SourceActionsType.Key, id: InputId.Keyboard, actions: [ { - type: Bidi.Input.ActionType.KeyDown, + type: ActionType.KeyDown, value: getBidiKeyValue(key), }, ], @@ -304,11 +321,11 @@ export class Keyboard extends BaseKeyboard { context: this.#context.id, actions: [ { - type: Bidi.Input.SourceActionsType.Key, + type: SourceActionsType.Key, id: InputId.Keyboard, actions: [ { - type: Bidi.Input.ActionType.KeyUp, + type: ActionType.KeyUp, value: getBidiKeyValue(key), }, ], @@ -324,25 +341,25 @@ export class Keyboard extends BaseKeyboard { const {delay = 0} = options; const actions: Bidi.Input.KeySourceAction[] = [ { - type: Bidi.Input.ActionType.KeyDown, + type: ActionType.KeyDown, value: getBidiKeyValue(key), }, ]; if (delay > 0) { actions.push({ - type: Bidi.Input.ActionType.Pause, + type: ActionType.Pause, duration: delay, }); } actions.push({ - type: Bidi.Input.ActionType.KeyUp, + type: ActionType.KeyUp, value: getBidiKeyValue(key), }); await this.#context.connection.send('input.performActions', { context: this.#context.id, actions: [ { - type: Bidi.Input.SourceActionsType.Key, + type: SourceActionsType.Key, id: InputId.Keyboard, actions, }, @@ -363,11 +380,11 @@ export class Keyboard extends BaseKeyboard { for (const value of values) { actions.push( { - type: Bidi.Input.ActionType.KeyDown, + type: ActionType.KeyDown, value, }, { - type: Bidi.Input.ActionType.KeyUp, + type: ActionType.KeyUp, value, } ); @@ -376,15 +393,15 @@ export class Keyboard extends BaseKeyboard { for (const value of values) { actions.push( { - type: Bidi.Input.ActionType.KeyDown, + type: ActionType.KeyDown, value, }, { - type: Bidi.Input.ActionType.Pause, + type: ActionType.Pause, duration: delay, }, { - type: Bidi.Input.ActionType.KeyUp, + type: ActionType.KeyUp, value, } ); @@ -394,7 +411,7 @@ export class Keyboard extends BaseKeyboard { context: this.#context.id, actions: [ { - type: Bidi.Input.SourceActionsType.Key, + type: SourceActionsType.Key, id: InputId.Keyboard, actions, }, @@ -474,11 +491,11 @@ export class Mouse extends BaseMouse { context: this.#context.id, actions: [ { - type: Bidi.Input.SourceActionsType.Pointer, + type: SourceActionsType.Pointer, id: InputId.Mouse, actions: [ { - type: Bidi.Input.ActionType.PointerMove, + type: ActionType.PointerMove, x, y, duration: (options.steps ?? 0) * 50, @@ -495,11 +512,11 @@ export class Mouse extends BaseMouse { context: this.#context.id, actions: [ { - type: Bidi.Input.SourceActionsType.Pointer, + type: SourceActionsType.Pointer, id: InputId.Mouse, actions: [ { - type: Bidi.Input.ActionType.PointerDown, + type: ActionType.PointerDown, button: getBidiButton(options.button ?? MouseButton.Left), }, ], @@ -513,11 +530,11 @@ export class Mouse extends BaseMouse { context: this.#context.id, actions: [ { - type: Bidi.Input.SourceActionsType.Pointer, + type: SourceActionsType.Pointer, id: InputId.Mouse, actions: [ { - type: Bidi.Input.ActionType.PointerUp, + type: ActionType.PointerUp, button: getBidiButton(options.button ?? MouseButton.Left), }, ], @@ -533,18 +550,18 @@ export class Mouse extends BaseMouse { ): Promise { const actions: Bidi.Input.PointerSourceAction[] = [ { - type: Bidi.Input.ActionType.PointerMove, + type: ActionType.PointerMove, x, y, origin: options.origin, }, ]; const pointerDownAction = { - type: Bidi.Input.ActionType.PointerDown, + type: ActionType.PointerDown, button: getBidiButton(options.button ?? MouseButton.Left), } as const; const pointerUpAction = { - type: Bidi.Input.ActionType.PointerUp, + type: ActionType.PointerUp, button: pointerDownAction.button, } as const; for (let i = 1; i < (options.count ?? 1); ++i) { @@ -553,7 +570,7 @@ export class Mouse extends BaseMouse { actions.push(pointerDownAction); if (options.delay) { actions.push({ - type: Bidi.Input.ActionType.Pause, + type: ActionType.Pause, duration: options.delay, }); } @@ -562,7 +579,7 @@ export class Mouse extends BaseMouse { context: this.#context.id, actions: [ { - type: Bidi.Input.SourceActionsType.Pointer, + type: SourceActionsType.Pointer, id: InputId.Mouse, actions, }, @@ -577,11 +594,11 @@ export class Mouse extends BaseMouse { context: this.#context.id, actions: [ { - type: Bidi.Input.SourceActionsType.Wheel, + type: SourceActionsType.Wheel, id: InputId.Wheel, actions: [ { - type: Bidi.Input.ActionType.Scroll, + type: ActionType.Scroll, ...(this.#lastMovePoint ?? { x: 0, y: 0, @@ -628,20 +645,20 @@ export class Touchscreen extends BaseTouchscreen { context: this.#context.id, actions: [ { - type: Bidi.Input.SourceActionsType.Pointer, + type: SourceActionsType.Pointer, id: InputId.Finger, parameters: { pointerType: Bidi.Input.PointerType.Touch, }, actions: [ { - type: Bidi.Input.ActionType.PointerMove, + type: ActionType.PointerMove, x, y, origin: options.origin, }, { - type: Bidi.Input.ActionType.PointerDown, + type: ActionType.PointerDown, button: 0, }, ], @@ -659,14 +676,14 @@ export class Touchscreen extends BaseTouchscreen { context: this.#context.id, actions: [ { - type: Bidi.Input.SourceActionsType.Pointer, + type: SourceActionsType.Pointer, id: InputId.Finger, parameters: { pointerType: Bidi.Input.PointerType.Touch, }, actions: [ { - type: Bidi.Input.ActionType.PointerMove, + type: ActionType.PointerMove, x, y, origin: options.origin, @@ -682,14 +699,14 @@ export class Touchscreen extends BaseTouchscreen { context: this.#context.id, actions: [ { - type: Bidi.Input.SourceActionsType.Pointer, + type: SourceActionsType.Pointer, id: InputId.Finger, parameters: { pointerType: Bidi.Input.PointerType.Touch, }, actions: [ { - type: Bidi.Input.ActionType.PointerUp, + type: ActionType.PointerUp, button: 0, }, ], diff --git a/packages/puppeteer-core/src/common/bidi/JSHandle.ts b/packages/puppeteer-core/src/common/bidi/JSHandle.ts index 4fcba7c4e80..ed6b0322f0f 100644 --- a/packages/puppeteer-core/src/common/bidi/JSHandle.ts +++ b/packages/puppeteer-core/src/common/bidi/JSHandle.ts @@ -28,9 +28,9 @@ import {releaseReference} from './utils.js'; export class JSHandle extends BaseJSHandle { #disposed = false; #realm: Realm; - #remoteValue; + #remoteValue: Bidi.Script.RemoteValue; - constructor(realm: Realm, remoteValue: Bidi.CommonDataTypes.RemoteValue) { + constructor(realm: Realm, remoteValue: Bidi.Script.RemoteValue) { super(); this.#realm = realm; this.#remoteValue = remoteValue; @@ -130,7 +130,10 @@ export class JSHandle extends BaseJSHandle { } this.#disposed = true; if ('handle' in this.#remoteValue) { - await releaseReference(this.#realm, this.#remoteValue); + await releaseReference( + this.#realm, + this.#remoteValue as Bidi.Script.RemoteReference + ); } } @@ -161,7 +164,7 @@ export class JSHandle extends BaseJSHandle { return 'handle' in this.#remoteValue ? this.#remoteValue.handle : undefined; } - remoteValue(): Bidi.CommonDataTypes.RemoteValue { + remoteValue(): Bidi.Script.RemoteValue { return this.#remoteValue; } } diff --git a/packages/puppeteer-core/src/common/bidi/NetworkManager.ts b/packages/puppeteer-core/src/common/bidi/NetworkManager.ts index 3c15d06e48b..27ac4a39f32 100644 --- a/packages/puppeteer-core/src/common/bidi/NetworkManager.ts +++ b/packages/puppeteer-core/src/common/bidi/NetworkManager.ts @@ -36,7 +36,7 @@ export class NetworkManager extends EventEmitter { ['network.responseStarted', this.#onResponseStarted.bind(this)], ['network.responseCompleted', this.#onResponseCompleted.bind(this)], ['network.fetchError', this.#onFetchError.bind(this)], - ]) as Map; + ]) as Map; #requestMap = new Map(); #navigationMap = new Map(); @@ -52,7 +52,7 @@ export class NetworkManager extends EventEmitter { } } - #onBeforeRequestSent(event: Bidi.Network.BeforeRequestSentParams): void { + #onBeforeRequestSent(event: Bidi.Network.BeforeRequestSentParameters): void { const frame = this.#page.frame(event.context ?? ''); if (!frame) { return; @@ -73,7 +73,7 @@ export class NetworkManager extends EventEmitter { #onResponseStarted(_event: any) {} - #onResponseCompleted(event: Bidi.Network.ResponseCompletedParams): void { + #onResponseCompleted(event: Bidi.Network.ResponseCompletedParameters): void { const request = this.#requestMap.get(event.request.request); if (!request) { return; @@ -91,7 +91,7 @@ export class NetworkManager extends EventEmitter { this.#requestMap.delete(event.request.request); } - #onFetchError(event: Bidi.Network.FetchErrorParams) { + #onFetchError(event: Bidi.Network.FetchErrorParameters) { const request = this.#requestMap.get(event.request.request); if (!request) { return; diff --git a/packages/puppeteer-core/src/common/bidi/Page.ts b/packages/puppeteer-core/src/common/bidi/Page.ts index a24149d28bc..8fa79a3d79f 100644 --- a/packages/puppeteer-core/src/common/bidi/Page.ts +++ b/packages/puppeteer-core/src/common/bidi/Page.ts @@ -78,15 +78,18 @@ export class Page extends PageBase { #networkManager: NetworkManager; #viewport: Viewport | null = null; #closedDeferred = Deferred.create(); - #subscribedEvents = new Map>([ + #subscribedEvents = new Map>([ ['log.entryAdded', this.#onLogEntryAdded.bind(this)], ['browsingContext.load', this.#onFrameLoaded.bind(this)], [ 'browsingContext.domContentLoaded', this.#onFrameDOMContentLoaded.bind(this), ], - ['browsingContext.fragmentNavigated', this.#onFrameNavigated.bind(this)], - ]) as Map; + [ + 'browsingContext.navigationStarted', + this.#onFrameNavigationStarted.bind(this), + ], + ]); #networkManagerEvents = new Map>([ [ NetworkManagerEmittedEvents.Request, @@ -252,19 +255,47 @@ export class Page extends PageBase { context.parent ); this.#frameTree.addFrame(frame); - this.emit(PageEmittedEvents.FrameAttached, frame); + if (frame !== this.mainFrame()) { + this.emit(PageEmittedEvents.FrameAttached, frame); + } } } - async #onFrameNavigated( + async #onFrameNavigationStarted( info: Bidi.BrowsingContext.NavigationInfo ): Promise { const frameId = info.context; - let frame = this.frame(frameId); - // Detach all child frames first. + const frame = this.frame(frameId); + if (frame) { - frame = await this.#frameTree.waitForFrame(frameId); + // TODO: Investigate if a navigationCompleted event should be in Spec + const predicate = ( + event: Bidi.BrowsingContext.DomContentLoaded['params'] + ) => { + if (event.context === frame?._id) { + return true; + } + return false; + }; + + await Deferred.race([ + waitForEvent( + this.#connection, + 'browsingContext.domContentLoaded', + predicate, + 0, + this.#closedDeferred.valueOrThrow() + ).catch(debugError), + waitForEvent( + this.#connection, + 'browsingContext.fragmentNavigated', + predicate, + 0, + this.#closedDeferred.valueOrThrow() + ).catch(debugError), + ]); + this.emit(PageEmittedEvents.FrameNavigated, frame); } } @@ -290,7 +321,7 @@ export class Page extends PageBase { this.emit(PageEmittedEvents.FrameDetached, frame); } - #onLogEntryAdded(event: Bidi.Log.LogEntry): void { + #onLogEntryAdded(event: Bidi.Log.Entry): void { const frame = this.frame(event.source.context); if (!frame) { return; @@ -514,11 +545,12 @@ export class Page extends PageBase { landscape, width, height, - pageRanges, + pageRanges: ranges, scale, preferCSSPageSize, timeout, } = this._getPDFOptions(options, 'cm'); + const pageRanges = ranges ? ranges.split(', ') : []; const {result} = await waitWithTimeout( this.#connection.send('browsingContext.print', { context: this.mainFrame()._id, @@ -529,7 +561,7 @@ export class Page extends PageBase { width, height, }, - pageRanges: pageRanges.split(', '), + pageRanges, scale, shrinkToFit: !preferCSSPageSize, }), @@ -667,13 +699,13 @@ export class Page extends PageBase { } function isConsoleLogEntry( - event: Bidi.Log.LogEntry + event: Bidi.Log.Entry ): event is Bidi.Log.ConsoleLogEntry { return event.type === 'console'; } function isJavaScriptLogEntry( - event: Bidi.Log.LogEntry + event: Bidi.Log.Entry ): event is Bidi.Log.JavascriptLogEntry { return event.type === 'javascript'; } diff --git a/packages/puppeteer-core/src/common/bidi/Realm.ts b/packages/puppeteer-core/src/common/bidi/Realm.ts index 8938b84be3e..8c42d552511 100644 --- a/packages/puppeteer-core/src/common/bidi/Realm.ts +++ b/packages/puppeteer-core/src/common/bidi/Realm.ts @@ -114,7 +114,9 @@ export class Realm extends EventEmitter { ); let responsePromise; - const resultOwnership = returnByValue ? 'none' : 'root'; + const resultOwnership = returnByValue + ? Bidi.Script.ResultOwnership.None + : Bidi.Script.ResultOwnership.Root; if (isString(pageFunction)) { const expression = SOURCE_URL_REGEX.test(pageFunction) ? pageFunction @@ -161,7 +163,7 @@ export class Realm extends EventEmitter { */ export function getBidiHandle( realmOrContext: Realm, - result: Bidi.CommonDataTypes.RemoteValue, + result: Bidi.Script.RemoteValue, frame: Frame ): JSHandle | ElementHandle { if (result.type === 'node' || result.type === 'window') { diff --git a/packages/puppeteer-core/src/common/bidi/Serializer.ts b/packages/puppeteer-core/src/common/bidi/Serializer.ts index 04f9e1e6f4d..0adfbb1ea6f 100644 --- a/packages/puppeteer-core/src/common/bidi/Serializer.ts +++ b/packages/puppeteer-core/src/common/bidi/Serializer.ts @@ -32,8 +32,8 @@ class UnserializableError extends Error {} * @internal */ export class BidiSerializer { - static serializeNumber(arg: number): Bidi.CommonDataTypes.LocalValue { - let value: Bidi.CommonDataTypes.SpecialNumber | number; + static serializeNumber(arg: number): Bidi.Script.LocalValue { + let value: Bidi.Script.SpecialNumber | number; if (Object.is(arg, -0)) { value = '-0'; } else if (Object.is(arg, Infinity)) { @@ -51,7 +51,7 @@ export class BidiSerializer { }; } - static serializeObject(arg: object | null): Bidi.CommonDataTypes.LocalValue { + static serializeObject(arg: object | null): Bidi.Script.LocalValue { if (arg === null) { return { type: 'null', @@ -78,7 +78,7 @@ export class BidiSerializer { throw error; } - const parsedObject: Bidi.CommonDataTypes.MappingLocalValue = []; + const parsedObject: Bidi.Script.MappingLocalValue = []; for (const key in arg) { parsedObject.push([ BidiSerializer.serializeRemoveValue(key), @@ -110,7 +110,7 @@ export class BidiSerializer { ); } - static serializeRemoveValue(arg: unknown): Bidi.CommonDataTypes.LocalValue { + static serializeRemoveValue(arg: unknown): Bidi.Script.LocalValue { switch (typeof arg) { case 'symbol': case 'function': @@ -145,9 +145,7 @@ export class BidiSerializer { static async serialize( arg: unknown, context: BrowsingContext - ): Promise< - Bidi.CommonDataTypes.LocalValue | Bidi.CommonDataTypes.RemoteValue - > { + ): Promise { if (arg instanceof LazyArg) { arg = await arg.get(context); } @@ -167,15 +165,13 @@ export class BidiSerializer { if (objectHandle.disposed) { throw new Error('JSHandle is disposed!'); } - return objectHandle.remoteValue(); + return objectHandle.remoteValue() as Bidi.Script.RemoteReference; } return BidiSerializer.serializeRemoveValue(arg); } - static deserializeNumber( - value: Bidi.CommonDataTypes.SpecialNumber | number - ): number { + static deserializeNumber(value: Bidi.Script.SpecialNumber | number): number { switch (value) { case '-0': return -0; @@ -190,20 +186,22 @@ export class BidiSerializer { } } - static deserializeLocalValue( - result: Bidi.CommonDataTypes.RemoteValue - ): unknown { + static deserializeLocalValue(result: Bidi.Script.RemoteValue): unknown { switch (result.type) { case 'array': - // TODO: Check expected output when value is undefined - return result.value?.map(value => { - return BidiSerializer.deserializeLocalValue(value); - }); + if (result.value) { + return result.value.map(value => { + return BidiSerializer.deserializeLocalValue(value); + }); + } + break; case 'set': - // TODO: Check expected output when value is undefined - return result.value.reduce((acc: Set, value) => { - return acc.add(BidiSerializer.deserializeLocalValue(value)); - }, new Set()); + if (result.value) { + return result.value.reduce((acc: Set, value) => { + return acc.add(BidiSerializer.deserializeLocalValue(value)); + }, new Set()); + } + break; case 'object': if (result.value) { return result.value.reduce((acc: Record, tuple) => { @@ -213,12 +211,14 @@ export class BidiSerializer { }, {}); } break; - case 'map': - return result.value.reduce((acc: Map, tuple) => { - const {key, value} = BidiSerializer.deserializeTuple(tuple); - return acc.set(key, value); - }, new Map()); + if (result.value) { + return result.value?.reduce((acc: Map, tuple) => { + const {key, value} = BidiSerializer.deserializeTuple(tuple); + return acc.set(key, value); + }, new Map()); + } + break; case 'promise': return {}; case 'regexp': @@ -246,8 +246,8 @@ export class BidiSerializer { } static deserializeTuple([serializedKey, serializedValue]: [ - Bidi.CommonDataTypes.RemoteValue | string, - Bidi.CommonDataTypes.RemoteValue, + Bidi.Script.RemoteValue | string, + Bidi.Script.RemoteValue, ]): {key: unknown; value: unknown} { const key = typeof serializedKey === 'string' @@ -258,7 +258,7 @@ export class BidiSerializer { return {key, value}; } - static deserialize(result: Bidi.CommonDataTypes.RemoteValue): any { + static deserialize(result: Bidi.Script.RemoteValue): any { if (!result) { debugError('Service did not produce a result.'); return undefined; diff --git a/packages/puppeteer-core/src/common/bidi/utils.ts b/packages/puppeteer-core/src/common/bidi/utils.ts index 9b563162329..f86c40bd0e1 100644 --- a/packages/puppeteer-core/src/common/bidi/utils.ts +++ b/packages/puppeteer-core/src/common/bidi/utils.ts @@ -31,7 +31,7 @@ export const debugError = debug('puppeteer:error'); */ export async function releaseReference( client: Realm, - remoteReference: Bidi.CommonDataTypes.RemoteReference + remoteReference: Bidi.Script.RemoteReference ): Promise { if (!remoteReference.handle) { return; diff --git a/packages/puppeteer-core/src/common/util.ts b/packages/puppeteer-core/src/common/util.ts index 7e50c3e4d9d..80faf6bc9ba 100644 --- a/packages/puppeteer-core/src/common/util.ts +++ b/packages/puppeteer-core/src/common/util.ts @@ -394,19 +394,19 @@ export async function waitForEvent( deferred.resolve(event); } }); - return Deferred.race([deferred, abortPromise]).then( - r => { - removeEventListeners([listener]); - if (isErrorLike(r)) { - throw r; - } - return r; - }, - error => { - removeEventListeners([listener]); - throw error; + + try { + const response = await Deferred.race([deferred, abortPromise]); + if (isErrorLike(response)) { + throw response; } - ); + + return response; + } catch (error) { + throw error; + } finally { + removeEventListeners([listener]); + } } /** diff --git a/test/TestExpectations.json b/test/TestExpectations.json index 9e17d7619a3..20339df5a42 100644 --- a/test/TestExpectations.json +++ b/test/TestExpectations.json @@ -1067,18 +1067,6 @@ "parameters": ["webDriverBiDi"], "expectations": ["FAIL", "PASS"] }, - { - "testIdPattern": "[network.spec] network raw network headers Same-origin set-cookie navigation", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["FAIL"] - }, - { - "testIdPattern": "[network.spec] network raw network headers Same-origin set-cookie subresource", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["FAIL"] - }, { "testIdPattern": "[network.spec] network Request.frame should work for subframe navigation request", "platforms": ["darwin", "linux", "win32"], @@ -1169,12 +1157,6 @@ "parameters": ["cdp", "firefox"], "expectations": ["SKIP"] }, - { - "testIdPattern": "[page.spec] Page Page.close should *not* run beforeunload by default", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["chrome", "webDriverBiDi"], - "expectations": ["PASS"] - }, { "testIdPattern": "[page.spec] Page Page.close should reject all promises when page is closed", "platforms": ["darwin", "linux", "win32"], @@ -1907,6 +1889,18 @@ "parameters": ["cdp", "firefox"], "expectations": ["FAIL"] }, + { + "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.click should throw for hidden nodes", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.click should throw for recursively hidden nodes", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[emulation.spec] Emulation Page.emulate should support clicking", "platforms": ["darwin", "linux", "win32"], @@ -2048,14 +2042,14 @@ { "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work right after framenavigated", "platforms": ["darwin", "linux", "win32"], - "parameters": ["chrome", "webDriverBiDi"], - "expectations": ["PASS"] + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["FAIL"] }, { "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work right after framenavigated", "platforms": ["darwin", "linux", "win32"], - "parameters": ["firefox", "webDriverBiDi"], - "expectations": ["FAIL"] + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["SKIP"] }, { "testIdPattern": "[fixtures.spec] Fixtures should close the browser when the node process closes", @@ -2115,7 +2109,7 @@ "testIdPattern": "[frame.spec] Frame specs Frame Management should support framesets", "platforms": ["darwin", "linux", "win32"], "parameters": ["chrome", "webDriverBiDi"], - "expectations": ["PASS"] + "expectations": ["FAIL", "PASS"] }, { "testIdPattern": "[frame.spec] Frame specs Frame Management should support lazy frames", @@ -2603,6 +2597,12 @@ "parameters": ["firefox", "webDriverBiDi"], "expectations": ["SKIP"] }, + { + "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to dataURL and fire dataURL requests", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["FAIL", "PASS"] + }, { "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to empty page with domcontentloaded", "platforms": ["darwin", "linux", "win32"], @@ -2641,9 +2641,9 @@ }, { "testIdPattern": "[navigation.spec] navigation Page.goto should not leak listeners during navigation of 11 pages", - "platforms": ["darwin"], + "platforms": ["darwin", "linux", "win32"], "parameters": ["chrome", "webDriverBiDi"], - "expectations": ["PASS", "TIMEOUT"] + "expectations": ["SKIP"] }, { "testIdPattern": "[navigation.spec] navigation Page.goto should return last response in redirect chain", @@ -2741,6 +2741,12 @@ "parameters": ["firefox", "webDriverBiDi"], "expectations": ["FAIL"] }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work when subframe issues window.stop()", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["FAIL"] + }, { "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with both domcontentloaded and load", "platforms": ["darwin", "linux", "win32"], @@ -2873,6 +2879,30 @@ "parameters": ["cdp", "firefox"], "expectations": ["FAIL"] }, + { + "testIdPattern": "[network.spec] network raw network headers Same-origin set-cookie navigation", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[network.spec] network raw network headers Same-origin set-cookie navigation", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[network.spec] network raw network headers Same-origin set-cookie subresource", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[network.spec] network raw network headers Same-origin set-cookie subresource", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[network.spec] network raw network headers Same-origin set-cookie subresource", "platforms": ["darwin", "linux", "win32"], @@ -2889,7 +2919,7 @@ "testIdPattern": "[network.spec] network Request.headers should define Firefox as user agent header", "platforms": ["darwin", "linux", "win32"], "parameters": ["firefox", "webDriverBiDi"], - "expectations": ["FAIL"] + "expectations": ["PASS"] }, { "testIdPattern": "[network.spec] network Request.initiator should return the initiator", @@ -2961,7 +2991,7 @@ "testIdPattern": "[network.spec] network Response.headers should work", "platforms": ["darwin", "linux", "win32"], "parameters": ["firefox", "webDriverBiDi"], - "expectations": ["FAIL"] + "expectations": ["PASS"] }, { "testIdPattern": "[network.spec] network Response.json should work", diff --git a/test/src/elementhandle.spec.ts b/test/src/elementhandle.spec.ts index 0f6ff369df3..a0c88edd1ae 100644 --- a/test/src/elementhandle.spec.ts +++ b/test/src/elementhandle.spec.ts @@ -295,7 +295,7 @@ describe('ElementHandle specs', function () { }); expect(error.message).atLeastOneToContain([ 'Node is either not clickable or not an HTMLElement', - 'no such node', + 'no such element', ]); }); it('should throw for recursively hidden nodes', async () => { @@ -311,7 +311,7 @@ describe('ElementHandle specs', function () { }); expect(error.message).atLeastOneToContain([ 'Node is either not clickable or not an HTMLElement', - 'no such node', + 'no such element', ]); }); it('should throw for
elements', async () => { diff --git a/test/src/frame.spec.ts b/test/src/frame.spec.ts index 4d2de2c0a85..5c7a1db029e 100644 --- a/test/src/frame.spec.ts +++ b/test/src/frame.spec.ts @@ -213,6 +213,7 @@ describe('Frame specs', function () { return navigatedFrames.push(frame); }); await page.goto(server.PREFIX + '/frames/nested-frames.html'); + expect(attachedFrames).toHaveLength(4); expect(detachedFrames).toHaveLength(0); expect(navigatedFrames).toHaveLength(5); diff --git a/test/src/launcher.spec.ts b/test/src/launcher.spec.ts index 788ad553470..68009241e17 100644 --- a/test/src/launcher.spec.ts +++ b/test/src/launcher.spec.ts @@ -387,18 +387,21 @@ describe('Launcher specs', function () { expect(puppeteer.product).toBe('firefox'); } }); - it('should work with no default arguments', async () => { - const {context, close} = await launch({ - ignoreDefaultArgs: true, - }); - try { - const page = await context.newPage(); - expect(await page.evaluate('11 * 11')).toBe(121); - await page.close(); - } finally { - await close(); + (!isHeadless ? it : it.skip)( + 'should work with no default arguments', + async () => { + const {context, close} = await launch({ + ignoreDefaultArgs: true, + }); + try { + const page = await context.newPage(); + expect(await page.evaluate('11 * 11')).toBe(121); + await page.close(); + } finally { + await close(); + } } - }); + ); it('should filter out ignored default arguments in Chrome', async () => { const {defaultBrowserOptions, puppeteer} = await getTestState({ skipLaunch: true, diff --git a/test/src/mocha-utils.ts b/test/src/mocha-utils.ts index 19a0aedb447..b948fd0fdd0 100644 --- a/test/src/mocha-utils.ts +++ b/test/src/mocha-utils.ts @@ -145,7 +145,8 @@ export const setupTestBrowserHooks = (): void => { timeout: this.timeout() - 1_000, }); } - } catch { + } catch (error) { + console.error(error); // Intentionally empty as `getTestState` will throw // if browser is not found } @@ -459,7 +460,7 @@ const closeLaunched = (storage: Array<() => Promise>) => { }; export const launch = async ( - launchOptions: PuppeteerLaunchOptions, + launchOptions: Readonly, options: { after?: 'each' | 'all'; createContext?: boolean; diff --git a/test/src/navigation.spec.ts b/test/src/navigation.spec.ts index ebe70ddcb43..afdc3bdc590 100644 --- a/test/src/navigation.spec.ts +++ b/test/src/navigation.spec.ts @@ -704,7 +704,6 @@ describe('navigation', function () { server.setRoute('/frames/style.css', () => {}); let frame: Frame | undefined; - let timeout: NodeJS.Timeout | undefined; const eventPromises = Deferred.race([ Promise.all([ waitEvent(page, 'frameattached').then(_frame => { @@ -724,7 +723,6 @@ describe('navigation', function () { ); try { await eventPromises; - clearTimeout(timeout); } catch (error) { navigationPromise.catch(() => {}); throw error; diff --git a/test/src/stacktrace.spec.ts b/test/src/stacktrace.spec.ts index 288e6c0a0d8..78be1b1394e 100644 --- a/test/src/stacktrace.spec.ts +++ b/test/src/stacktrace.spec.ts @@ -25,6 +25,7 @@ const FILENAME = __filename.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); const parseStackTrace = (stack: string): string => { stack = stack.replace(new RegExp(FILENAME, 'g'), ''); stack = stack.replace(/:(\d+):(\d+)/g, '::'); + stack = stack.replace(/:(\d+):(\d+)/g, '::'); return stack; }; @@ -51,7 +52,7 @@ describe('Stack trace', function () { ).toMatchObject({ ...[ 'Error: Test', - 'evaluate (evaluate at Context. (::), :1:18)', + 'evaluate (evaluate at Context. (::), ::)', ], }); }); @@ -75,7 +76,7 @@ describe('Stack trace', function () { ).toMatchObject({ ...[ 'Error: Test', - 'evaluateHandle (evaluateHandle at Context. (::), :1:18)', + 'evaluateHandle (evaluateHandle at Context. (::), ::)', ], }); }); @@ -104,8 +105,8 @@ describe('Stack trace', function () { ).toMatchObject({ ...[ 'Error: Test', - 'evaluateHandle (evaluateHandle at Context. (::), :2:22)', - 'evaluate (evaluate at Context. (::), :1:12)', + 'evaluateHandle (evaluateHandle at Context. (::), ::)', + 'evaluate (evaluate at Context. (::), ::)', ], }); }); @@ -141,11 +142,11 @@ describe('Stack trace', function () { ).toMatchObject({ ...[ 'Error: Test', - 'a (evaluate at Context. (::), :2:22)', - 'b (evaluate at Context. (::), :5:16)', - 'c (evaluate at Context. (::), :8:16)', - 'd (evaluate at Context. (::), :11:16)', - 'evaluate (evaluate at Context. (::), :13:12)', + 'a (evaluate at Context. (::), ::)', + 'b (evaluate at Context. (::), ::)', + 'c (evaluate at Context. (::), ::)', + 'd (evaluate at Context. (::), ::)', + 'evaluate (evaluate at Context. (::), ::)', ], }); }); diff --git a/tools/mochaRunner/src/utils.ts b/tools/mochaRunner/src/utils.ts index a01a7118899..59290ab4ca9 100644 --- a/tools/mochaRunner/src/utils.ts +++ b/tools/mochaRunner/src/utils.ts @@ -167,9 +167,6 @@ export function getExpectationUpdates( } for (const failure of results.failures) { - if (passesByKey.has(getTestId(failure.file, failure.fullTitle))) { - continue; - } // If an error occurs during a hook // the error not have a file associated with it if (!failure.file) { @@ -185,6 +182,10 @@ export function getExpectationUpdates( continue; } + if (passesByKey.has(getTestId(failure.file, failure.fullTitle))) { + continue; + } + const expectationEntry = findEffectiveExpectationForTest( expectations, failure