chore: update chromium-bidi (#10620)

This commit is contained in:
Nikolay Vitkov 2023-07-28 11:11:14 +02:00 committed by GitHub
parent fdada74ba7
commit 0757d04358
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 397 additions and 285 deletions

36
package-lock.json generated
View File

@ -3411,16 +3411,21 @@
} }
}, },
"node_modules/chromium-bidi": { "node_modules/chromium-bidi": {
"version": "0.4.16", "version": "0.4.19",
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.19.tgz",
"integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", "integrity": "sha512-8pzgvc/f4OpQMNRqgwqJKLd1cJFzKgh/foXbUgSD+lCyB+SSHUgo6FYaOHoMfHv10+0kP3/WYxKVGV1MH8QjtQ==",
"dependencies": { "dependencies": {
"mitt": "3.0.0" "mitt": "3.0.1"
}, },
"peerDependencies": { "peerDependencies": {
"devtools-protocol": "*" "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": { "node_modules/ci-info": {
"version": "3.8.0", "version": "3.8.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
@ -7222,6 +7227,7 @@
}, },
"node_modules/mitt": { "node_modules/mitt": {
"version": "3.0.0", "version": "3.0.0",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/mkdirp": { "node_modules/mkdirp": {
@ -10563,7 +10569,7 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@puppeteer/browsers": "1.4.6", "@puppeteer/browsers": "1.4.6",
"chromium-bidi": "0.4.16", "chromium-bidi": "0.4.19",
"cross-fetch": "4.0.0", "cross-fetch": "4.0.0",
"debug": "4.3.4", "debug": "4.3.4",
"devtools-protocol": "0.0.1147663", "devtools-protocol": "0.0.1147663",
@ -12894,11 +12900,18 @@
"dev": true "dev": true
}, },
"chromium-bidi": { "chromium-bidi": {
"version": "0.4.16", "version": "0.4.19",
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.19.tgz",
"integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", "integrity": "sha512-8pzgvc/f4OpQMNRqgwqJKLd1cJFzKgh/foXbUgSD+lCyB+SSHUgo6FYaOHoMfHv10+0kP3/WYxKVGV1MH8QjtQ==",
"requires": { "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": { "ci-info": {
@ -15419,7 +15432,8 @@
} }
}, },
"mitt": { "mitt": {
"version": "3.0.0" "version": "3.0.0",
"dev": true
}, },
"mkdirp": { "mkdirp": {
"version": "1.0.4", "version": "1.0.4",
@ -16338,7 +16352,7 @@
"version": "file:packages/puppeteer-core", "version": "file:packages/puppeteer-core",
"requires": { "requires": {
"@puppeteer/browsers": "1.4.6", "@puppeteer/browsers": "1.4.6",
"chromium-bidi": "0.4.16", "chromium-bidi": "0.4.19",
"cross-fetch": "4.0.0", "cross-fetch": "4.0.0",
"debug": "4.3.4", "debug": "4.3.4",
"devtools-protocol": "0.0.1147663", "devtools-protocol": "0.0.1147663",

View File

@ -144,7 +144,7 @@
"author": "The Chromium Authors", "author": "The Chromium Authors",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"chromium-bidi": "0.4.16", "chromium-bidi": "0.4.19",
"cross-fetch": "4.0.0", "cross-fetch": "4.0.0",
"debug": "4.3.4", "debug": "4.3.4",
"devtools-protocol": "0.0.1147663", "devtools-protocol": "0.0.1147663",

View File

@ -1074,6 +1074,9 @@ export class ElementHandle<
} }
} }
/**
* @public
*/
export interface AutofillData { export interface AutofillData {
creditCard: { creditCard: {
// See https://chromedevtools.github.io/devtools-protocol/tot/Autofill/#type-CreditCard. // See https://chromedevtools.github.io/devtools-protocol/tot/Autofill/#type-CreditCard.

View File

@ -86,7 +86,7 @@ class CDPConnectionAdapter {
throw new Error('Unknown CDP session with id' + id); throw new Error('Unknown CDP session with id' + id);
} }
if (!this.#adapters.has(session)) { if (!this.#adapters.has(session)) {
const adapter = new CDPClientAdapter(session); const adapter = new CDPClientAdapter(session, id, this.#browser);
this.#adapters.set(session, adapter); this.#adapters.set(session, adapter);
return adapter; return adapter;
} }
@ -113,13 +113,25 @@ class CDPClientAdapter<T extends EventEmitter & Pick<CDPPPtrConnection, 'send'>>
{ {
#closed = false; #closed = false;
#client: T; #client: T;
sessionId: string | undefined = undefined;
#browserClient?: BidiMapper.CdpClient;
constructor(client: T) { constructor(
client: T,
sessionId?: string,
browserClient?: BidiMapper.CdpClient
) {
super(); super();
this.#client = client; this.#client = client;
this.sessionId = sessionId;
this.#browserClient = browserClient;
this.#client.on('*', this.#forwardMessage as Handler<any>); this.#client.on('*', this.#forwardMessage as Handler<any>);
} }
browserClient(): BidiMapper.CdpClient {
return this.#browserClient!;
}
#forwardMessage = <T extends keyof CdpEvents>( #forwardMessage = <T extends keyof CdpEvents>(
method: T, method: T,
event: CdpEvents[T] event: CdpEvents[T]
@ -163,32 +175,27 @@ class NoOpTransport
extends BidiMapper.EventEmitter<any> extends BidiMapper.EventEmitter<any>
implements BidiMapper.BidiTransport implements BidiMapper.BidiTransport
{ {
#onMessage: ( #onMessage: (message: Bidi.ChromiumBidi.Command) => Promise<void> | void =
message: Bidi.Message.RawCommandRequest async (_m: Bidi.ChromiumBidi.Command): Promise<void> => {
) => Promise<void> | void = async (
_m: Bidi.Message.RawCommandRequest
): Promise<void> => {
return; return;
}; };
emitMessage(message: Bidi.Message.RawCommandRequest) { emitMessage(message: Bidi.ChromiumBidi.Command) {
void this.#onMessage(message); void this.#onMessage(message);
} }
setOnMessage( setOnMessage(
onMessage: (message: Bidi.Message.RawCommandRequest) => Promise<void> | void onMessage: (message: Bidi.ChromiumBidi.Command) => Promise<void> | void
): void { ): void {
this.#onMessage = onMessage; this.#onMessage = onMessage;
} }
async sendMessage(message: Bidi.Message.OutgoingMessage): Promise<void> { async sendMessage(message: Bidi.ChromiumBidi.Message): Promise<void> {
this.emit('bidiResponse', message); this.emit('bidiResponse', message);
} }
close() { close() {
this.#onMessage = async ( this.#onMessage = async (_m: Bidi.ChromiumBidi.Command): Promise<void> => {
_m: Bidi.Message.RawCommandRequest
): Promise<void> => {
return; return;
}; };
} }

View File

@ -49,7 +49,8 @@ import {debugError} from './utils.js';
* @internal * @internal
*/ */
export class Browser extends BrowserBase { export class Browser extends BrowserBase {
static readonly subscribeModules: Bidi.Session.SubscriptionRequestEvent[] = [ // TODO: Update generator to include fully module
static readonly subscribeModules: string[] = [
'browsingContext', 'browsingContext',
'network', 'network',
'log', 'log',
@ -114,12 +115,16 @@ export class Browser extends BrowserBase {
#contexts: BrowserContext[] = []; #contexts: BrowserContext[] = [];
#browserTarget: BiDiBrowserTarget; #browserTarget: BiDiBrowserTarget;
#connectionEventHandlers = new Map<string, Handler<any>>([ #connectionEventHandlers = new Map<
Bidi.BrowsingContextEvent['method'],
Handler<any>
>([
['browsingContext.contextCreated', this.#onContextCreated.bind(this)], ['browsingContext.contextCreated', this.#onContextCreated.bind(this)],
['browsingContext.contextDestroyed', this.#onContextDestroyed.bind(this)], ['browsingContext.contextDestroyed', this.#onContextDestroyed.bind(this)],
['browsingContext.domContentLoaded', this.#onContextDomLoaded.bind(this)],
['browsingContext.fragmentNavigated', this.#onContextNavigation.bind(this)], ['browsingContext.fragmentNavigated', this.#onContextNavigation.bind(this)],
['browsingContext.navigationStarted', this.#onContextNavigation.bind(this)], ['browsingContext.navigationStarted', this.#onContextNavigation.bind(this)],
]) as Map<Bidi.BrowsingContext.EventNames, Handler>; ]);
constructor( constructor(
opts: Options & { 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) { #onContextNavigation(event: Bidi.BrowsingContext.NavigationInfo) {
const context = this.#connection.getBrowsingContext(event.context); const context = this.#connection.getBrowsingContext(event.context);
context.url = event.url; 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); const context = new BrowsingContext(this.#connection, event);
this.#connection.registerBrowsingContexts(context); this.#connection.registerBrowsingContexts(context);
// TODO: once more browsing context types are supported, this should be // TODO: once more browsing context types are supported, this should be
@ -197,7 +211,7 @@ export class Browser extends BrowserBase {
} }
async #onContextDestroyed( async #onContextDestroyed(
event: Bidi.BrowsingContext.ContextDestroyedEvent['params'] event: Bidi.BrowsingContext.ContextDestroyed['params']
) { ) {
const context = this.#connection.getBrowsingContext(event.context); const context = this.#connection.getBrowsingContext(event.context);
const topLevelContext = this.#connection.getTopLevelContext(event.context); const topLevelContext = this.#connection.getTopLevelContext(event.context);

View File

@ -14,6 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import {BrowserContext as BrowserContextBase} from '../../api/BrowserContext.js'; import {BrowserContext as BrowserContextBase} from '../../api/BrowserContext.js';
import {Page as PageBase} from '../../api/Page.js'; import {Page as PageBase} from '../../api/Page.js';
import {Target} from '../../api/Target.js'; import {Target} from '../../api/Target.js';
@ -66,7 +68,7 @@ export class BrowserContext extends BrowserContextBase {
override async newPage(): Promise<PageBase> { override async newPage(): Promise<PageBase> {
const {result} = await this.#connection.send('browsingContext.create', { const {result} = await this.#connection.send('browsingContext.create', {
type: 'tab', type: Bidi.BrowsingContext.CreateType.Tab,
}); });
const target = this.#browser._getTargetById(result.context); const target = this.#browser._getTargetById(result.context);

View File

@ -32,8 +32,8 @@ const lifeCycleToReadinessState = new Map<
PuppeteerLifeCycleEvent, PuppeteerLifeCycleEvent,
Bidi.BrowsingContext.ReadinessState Bidi.BrowsingContext.ReadinessState
>([ >([
['load', 'complete'], ['load', Bidi.BrowsingContext.ReadinessState.Complete],
['domcontentloaded', 'interactive'], ['domcontentloaded', Bidi.BrowsingContext.ReadinessState.Interactive],
]); ]);
/** /**
@ -84,7 +84,7 @@ export class CDPSessionWrapper extends EventEmitter implements CDPSession {
); );
} }
const session = await this.#sessionId.valueOrThrow(); 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, method: method,
params: paramArgs[0], params: paramArgs[0],
session, session,
@ -140,12 +140,12 @@ export class BrowsingContext extends Realm {
this.#parent = info.parent; this.#parent = info.parent;
this.#cdpSession = new CDPSessionWrapper(this); this.#cdpSession = new CDPSessionWrapper(this);
this.on( this.on('browsingContext.domContentLoaded', this.#updateUrl.bind(this));
'browsingContext.fragmentNavigated', this.on('browsingContext.load', this.#updateUrl.bind(this));
(info: Bidi.BrowsingContext.NavigationInfo) => {
this.#url = info.url;
} }
);
#updateUrl(info: Bidi.BrowsingContext.NavigationInfo) {
this.url = info.url;
} }
createSandboxRealm(sandbox: string): Realm { createSandboxRealm(sandbox: string): Realm {

View File

@ -22,33 +22,11 @@ import {debug} from '../Debug.js';
import {EventEmitter} from '../EventEmitter.js'; import {EventEmitter} from '../EventEmitter.js';
import {BrowsingContext, cdpSessions} from './BrowsingContext.js'; import {BrowsingContext, cdpSessions} from './BrowsingContext.js';
import {debugError} from './utils.js';
const debugProtocolSend = debug('puppeteer:webDriverBiDi:SEND ►'); const debugProtocolSend = debug('puppeteer:webDriverBiDi:SEND ►');
const debugProtocolReceive = debug('puppeteer:webDriverBiDi:RECV ◀'); 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 * @internal
*/ */
@ -59,11 +37,11 @@ interface Commands {
}; };
'script.callFunction': { 'script.callFunction': {
params: Bidi.Script.CallFunctionParameters; params: Bidi.Script.CallFunctionParameters;
returnType: Bidi.Script.CallFunctionResult; returnType: Bidi.Script.EvaluateResult;
}; };
'script.disown': { 'script.disown': {
params: Bidi.Script.DisownParameters; params: Bidi.Script.DisownParameters;
returnType: Bidi.Script.DisownResult; returnType: Bidi.EmptyResult;
}; };
'script.addPreloadScript': { 'script.addPreloadScript': {
params: Bidi.Script.AddPreloadScriptParameters; params: Bidi.Script.AddPreloadScriptParameters;
@ -76,7 +54,7 @@ interface Commands {
}; };
'browsingContext.close': { 'browsingContext.close': {
params: Bidi.BrowsingContext.CloseParameters; params: Bidi.BrowsingContext.CloseParameters;
returnType: Bidi.Message.EmptyResult; returnType: Bidi.EmptyResult;
}; };
'browsingContext.getTree': { 'browsingContext.getTree': {
params: Bidi.BrowsingContext.GetTreeParameters; params: Bidi.BrowsingContext.GetTreeParameters;
@ -88,7 +66,7 @@ interface Commands {
}; };
'browsingContext.reload': { 'browsingContext.reload': {
params: Bidi.BrowsingContext.ReloadParameters; params: Bidi.BrowsingContext.ReloadParameters;
returnType: Bidi.Message.EmptyResult; returnType: Bidi.EmptyResult;
}; };
'browsingContext.print': { 'browsingContext.print': {
params: Bidi.BrowsingContext.PrintParameters; params: Bidi.BrowsingContext.PrintParameters;
@ -101,27 +79,16 @@ interface Commands {
'input.performActions': { 'input.performActions': {
params: Bidi.Input.PerformActionsParameters; params: Bidi.Input.PerformActionsParameters;
returnType: Bidi.Message.EmptyResult; returnType: Bidi.EmptyResult;
}; };
'input.releaseActions': { 'input.releaseActions': {
params: Bidi.Input.ReleaseActionsParameters; params: Bidi.Input.ReleaseActionsParameters;
returnType: Bidi.Message.EmptyResult; returnType: Bidi.EmptyResult;
}; };
'session.new': { 'session.new': {
params: { params: Bidi.Session.NewParameters;
// capabilities: session.CapabilitiesRequest returnType: Bidi.Session.NewResult;
capabilities?: {
// session.CapabilitiesRequest = {
// ? alwaysMatch: session.CapabilityRequest,
// ? firstMatch: [*session.CapabilityRequest]
// }
alwaysMatch?: Capability;
};
}; // TODO: Update Types in chromium bidi
returnType: {
result: {sessionId: string; capabilities: Capability};
};
}; };
'session.status': { 'session.status': {
params: object; params: object;
@ -129,18 +96,18 @@ interface Commands {
}; };
'session.subscribe': { 'session.subscribe': {
params: Bidi.Session.SubscriptionRequest; params: Bidi.Session.SubscriptionRequest;
returnType: Bidi.Message.EmptyResult; returnType: Bidi.EmptyResult;
}; };
'session.unsubscribe': { 'session.unsubscribe': {
params: Bidi.Session.SubscriptionRequest; params: Bidi.Session.SubscriptionRequest;
returnType: Bidi.Message.EmptyResult; returnType: Bidi.EmptyResult;
}; };
'cdp.sendCommand': { 'cdp.sendCommand': {
params: Bidi.Cdp.SendCommandParams; params: Bidi.Cdp.SendCommandParameters;
returnType: Bidi.Cdp.SendCommandResult; returnType: Bidi.Cdp.SendCommandResult;
}; };
'cdp.getSession': { 'cdp.getSession': {
params: Bidi.Cdp.GetSessionParams; params: Bidi.Cdp.GetSessionParameters;
returnType: Bidi.Cdp.GetSessionResult; returnType: Bidi.Cdp.GetSessionResult;
}; };
} }
@ -184,16 +151,16 @@ export class Connection extends EventEmitter {
send<T extends keyof Commands>( send<T extends keyof Commands>(
method: T, method: T,
params: Commands[T]['params'] params: Commands[T]['params']
): Promise<Commands[T]['returnType']> { ): Promise<{result: Commands[T]['returnType']}> {
return this.#callbacks.create(method, this.#timeout, id => { return this.#callbacks.create(method, this.#timeout, id => {
const stringifiedMessage = JSON.stringify({ const stringifiedMessage = JSON.stringify({
id, id,
method, method,
params, params,
} as Bidi.Message.CommandRequest); } as Bidi.Command);
debugProtocolSend(stringifiedMessage); debugProtocolSend(stringifiedMessage);
this.#transport.send(stringifiedMessage); this.#transport.send(stringifiedMessage);
}) as Promise<Commands[T]['returnType']>; }) as Promise<{result: Commands[T]['returnType']}>;
} }
/** /**
@ -206,27 +173,29 @@ export class Connection extends EventEmitter {
}); });
} }
debugProtocolReceive(message); debugProtocolReceive(message);
const object = JSON.parse(message) as const object = JSON.parse(message) as Bidi.ChromiumBidi.Message;
| Bidi.Message.CommandResponse
| Bidi.Message.EventMessage;
if ('id' in object) { if ('id' in object && object.id) {
if ('error' in object) { if ('error' in object) {
this.#callbacks.reject( this.#callbacks.reject(
object.id, object.id,
createProtocolError(object), createProtocolError(object as Bidi.ErrorResponse),
object.message object.message
); );
} else { } else {
this.#callbacks.resolve(object.id, object); this.#callbacks.resolve(object.id, object);
} }
} else {
if ('error' in object || 'id' in object || 'launched' in object) {
debugError(object);
} else { } else {
this.#maybeEmitOnContext(object); this.#maybeEmitOnContext(object);
this.emit(object.method, object.params); this.emit(object.method, object.params);
} }
} }
}
#maybeEmitOnContext(event: Bidi.Message.EventMessage) { #maybeEmitOnContext(event: Bidi.ChromiumBidi.Event) {
let context: BrowsingContext | undefined; let context: BrowsingContext | undefined;
// Context specific events // Context specific events
if ('context' in event.params && event.params.context) { if ('context' in event.params && event.params.context) {
@ -292,7 +261,7 @@ export class Connection extends EventEmitter {
/** /**
* @internal * @internal
*/ */
function createProtocolError(object: Bidi.Message.ErrorResult): string { function createProtocolError(object: Bidi.ErrorResponse): string {
let message = `${object.error} ${object.message}`; let message = `${object.error} ${object.message}`;
if (object.stacktrace) { if (object.stacktrace) {
message += ` ${object.stacktrace}`; message += ` ${object.stacktrace}`;
@ -300,8 +269,6 @@ function createProtocolError(object: Bidi.Message.ErrorResult): string {
return message; return message;
} }
function isCDPEvent( function isCDPEvent(event: Bidi.ChromiumBidi.Event): event is Bidi.Cdp.Event {
event: Bidi.Message.EventMessage
): event is Bidi.Cdp.EventReceivedEvent {
return event.method.startsWith('cdp.'); return event.method.startsWith('cdp.');
} }

View File

@ -40,7 +40,7 @@ export class ElementHandle<
constructor( constructor(
realm: Realm, realm: Realm,
remoteValue: Bidi.CommonDataTypes.RemoteValue, remoteValue: Bidi.Script.RemoteValue,
frame: Frame frame: Frame
) { ) {
super(new JSHandle(realm, remoteValue)); super(new JSHandle(realm, remoteValue));
@ -59,7 +59,7 @@ export class ElementHandle<
return this.handle.isPrimitiveValue; return this.handle.isPrimitiveValue;
} }
remoteValue(): Bidi.CommonDataTypes.RemoteValue { remoteValue(): Bidi.Script.RemoteValue {
return this.handle.remoteValue(); return this.handle.remoteValue();
} }
@ -102,7 +102,7 @@ export class ElementHandle<
Object.assign({}, options, { Object.assign({}, options, {
origin: { origin: {
type: 'element' as const, 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, { return this.#frame.page().mouse.move(0, 0, {
origin: { origin: {
type: 'element' as const, 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, { return this.#frame.page().touchscreen.tap(0, 0, {
origin: { origin: {
type: 'element' as const, 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, { return this.#frame.page().touchscreen.touchStart(0, 0, {
origin: { origin: {
type: 'element' as const, 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, { return this.#frame.page().touchscreen.touchMove(0, 0, {
origin: { origin: {
type: 'element' as const, type: 'element' as const,
element: remoteValue as Bidi.CommonDataTypes.SharedReference, element: remoteValue as Bidi.Script.SharedReference,
}, },
}); });
} }

View File

@ -227,6 +227,8 @@ export class Frame extends BaseFrame {
) as string; ) as string;
const [info] = await Promise.all([ const [info] = await Promise.all([
// TODO(lightning00blade): Should also keep tack of
// navigationAborted and navigationFailed
waitForEvent<Bidi.BrowsingContext.NavigationInfo>( waitForEvent<Bidi.BrowsingContext.NavigationInfo>(
this.#context, this.#context,
waitUntilEvent, waitUntilEvent,
@ -236,15 +238,26 @@ export class Frame extends BaseFrame {
timeout, timeout,
this.#abortDeferred.valueOrThrow() this.#abortDeferred.valueOrThrow()
), ),
Deferred.race([
waitForEvent( waitForEvent(
this.#context, this.#context,
Bidi.BrowsingContext.EventNames.FragmentNavigated, Bidi.ChromiumBidi.BrowsingContext.EventNames.NavigationStarted,
() => { () => {
return true; return true;
}, },
timeout, timeout,
this.#abortDeferred.valueOrThrow() this.#abortDeferred.valueOrThrow()
), ),
waitForEvent(
this.#context,
Bidi.ChromiumBidi.BrowsingContext.EventNames.FragmentNavigated,
() => {
return true;
},
timeout,
this.#abortDeferred.valueOrThrow()
),
]),
]); ]);
return this.#page.getNavigationResponse(info.navigation); return this.#page.getNavigationResponse(info.navigation);

View File

@ -41,7 +41,7 @@ export class HTTPRequest extends BaseHTTPRequest {
#frame: Frame | null; #frame: Frame | null;
constructor( constructor(
event: Bidi.Network.BeforeRequestSentParams, event: Bidi.Network.BeforeRequestSentParameters,
frame: Frame | null, frame: Frame | null,
redirectChain: HTTPRequest[] redirectChain: HTTPRequest[]
) { ) {
@ -58,11 +58,11 @@ export class HTTPRequest extends BaseHTTPRequest {
this._redirectChain = redirectChain ?? []; this._redirectChain = redirectChain ?? [];
this._navigationId = event.navigation; 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 // TODO: How to handle Binary Headers
// https://w3c.github.io/webdriver-bidi/#type-network-Header // https://w3c.github.io/webdriver-bidi/#type-network-Header
if (value) { if (header.value.type === 'string') {
this.#headers[name.toLowerCase()] = value; this.#headers[header.name.toLowerCase()] = header.value.value;
} }
} }
} }

View File

@ -39,10 +39,9 @@ export class HTTPResponse extends BaseHTTPResponse {
constructor( constructor(
request: HTTPRequest, request: HTTPRequest,
responseEvent: Bidi.Network.ResponseCompletedParams {response}: Bidi.Network.ResponseCompletedParameters
) { ) {
super(); super();
const {response} = responseEvent;
this.#request = request; this.#request = request;
this.#remoteAddress = { this.#remoteAddress = {
@ -54,12 +53,16 @@ export class HTTPResponse extends BaseHTTPResponse {
this.#fromCache = response.fromCache; this.#fromCache = response.fromCache;
this.#status = response.status; this.#status = response.status;
this.#statusText = response.statusText; this.#statusText = response.statusText;
// TODO: update once BiDi has types // TODO: File and issue with BiDi spec
this.#timings = (response as any).timings ?? null; this.#timings = null;
// TODO: Removed once the Firefox implementation is compliant with https://w3c.github.io/webdriver-bidi/#get-the-response-data. // 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 || []) { 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;
}
} }
} }

View File

@ -41,6 +41,23 @@ const enum InputId {
Finger = '__puppeteer_finger', 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) => { const getBidiKeyValue = (key: KeyInput) => {
switch (key) { switch (key) {
case '\r': case '\r':
@ -286,11 +303,11 @@ export class Keyboard extends BaseKeyboard {
context: this.#context.id, context: this.#context.id,
actions: [ actions: [
{ {
type: Bidi.Input.SourceActionsType.Key, type: SourceActionsType.Key,
id: InputId.Keyboard, id: InputId.Keyboard,
actions: [ actions: [
{ {
type: Bidi.Input.ActionType.KeyDown, type: ActionType.KeyDown,
value: getBidiKeyValue(key), value: getBidiKeyValue(key),
}, },
], ],
@ -304,11 +321,11 @@ export class Keyboard extends BaseKeyboard {
context: this.#context.id, context: this.#context.id,
actions: [ actions: [
{ {
type: Bidi.Input.SourceActionsType.Key, type: SourceActionsType.Key,
id: InputId.Keyboard, id: InputId.Keyboard,
actions: [ actions: [
{ {
type: Bidi.Input.ActionType.KeyUp, type: ActionType.KeyUp,
value: getBidiKeyValue(key), value: getBidiKeyValue(key),
}, },
], ],
@ -324,25 +341,25 @@ export class Keyboard extends BaseKeyboard {
const {delay = 0} = options; const {delay = 0} = options;
const actions: Bidi.Input.KeySourceAction[] = [ const actions: Bidi.Input.KeySourceAction[] = [
{ {
type: Bidi.Input.ActionType.KeyDown, type: ActionType.KeyDown,
value: getBidiKeyValue(key), value: getBidiKeyValue(key),
}, },
]; ];
if (delay > 0) { if (delay > 0) {
actions.push({ actions.push({
type: Bidi.Input.ActionType.Pause, type: ActionType.Pause,
duration: delay, duration: delay,
}); });
} }
actions.push({ actions.push({
type: Bidi.Input.ActionType.KeyUp, type: ActionType.KeyUp,
value: getBidiKeyValue(key), value: getBidiKeyValue(key),
}); });
await this.#context.connection.send('input.performActions', { await this.#context.connection.send('input.performActions', {
context: this.#context.id, context: this.#context.id,
actions: [ actions: [
{ {
type: Bidi.Input.SourceActionsType.Key, type: SourceActionsType.Key,
id: InputId.Keyboard, id: InputId.Keyboard,
actions, actions,
}, },
@ -363,11 +380,11 @@ export class Keyboard extends BaseKeyboard {
for (const value of values) { for (const value of values) {
actions.push( actions.push(
{ {
type: Bidi.Input.ActionType.KeyDown, type: ActionType.KeyDown,
value, value,
}, },
{ {
type: Bidi.Input.ActionType.KeyUp, type: ActionType.KeyUp,
value, value,
} }
); );
@ -376,15 +393,15 @@ export class Keyboard extends BaseKeyboard {
for (const value of values) { for (const value of values) {
actions.push( actions.push(
{ {
type: Bidi.Input.ActionType.KeyDown, type: ActionType.KeyDown,
value, value,
}, },
{ {
type: Bidi.Input.ActionType.Pause, type: ActionType.Pause,
duration: delay, duration: delay,
}, },
{ {
type: Bidi.Input.ActionType.KeyUp, type: ActionType.KeyUp,
value, value,
} }
); );
@ -394,7 +411,7 @@ export class Keyboard extends BaseKeyboard {
context: this.#context.id, context: this.#context.id,
actions: [ actions: [
{ {
type: Bidi.Input.SourceActionsType.Key, type: SourceActionsType.Key,
id: InputId.Keyboard, id: InputId.Keyboard,
actions, actions,
}, },
@ -474,11 +491,11 @@ export class Mouse extends BaseMouse {
context: this.#context.id, context: this.#context.id,
actions: [ actions: [
{ {
type: Bidi.Input.SourceActionsType.Pointer, type: SourceActionsType.Pointer,
id: InputId.Mouse, id: InputId.Mouse,
actions: [ actions: [
{ {
type: Bidi.Input.ActionType.PointerMove, type: ActionType.PointerMove,
x, x,
y, y,
duration: (options.steps ?? 0) * 50, duration: (options.steps ?? 0) * 50,
@ -495,11 +512,11 @@ export class Mouse extends BaseMouse {
context: this.#context.id, context: this.#context.id,
actions: [ actions: [
{ {
type: Bidi.Input.SourceActionsType.Pointer, type: SourceActionsType.Pointer,
id: InputId.Mouse, id: InputId.Mouse,
actions: [ actions: [
{ {
type: Bidi.Input.ActionType.PointerDown, type: ActionType.PointerDown,
button: getBidiButton(options.button ?? MouseButton.Left), button: getBidiButton(options.button ?? MouseButton.Left),
}, },
], ],
@ -513,11 +530,11 @@ export class Mouse extends BaseMouse {
context: this.#context.id, context: this.#context.id,
actions: [ actions: [
{ {
type: Bidi.Input.SourceActionsType.Pointer, type: SourceActionsType.Pointer,
id: InputId.Mouse, id: InputId.Mouse,
actions: [ actions: [
{ {
type: Bidi.Input.ActionType.PointerUp, type: ActionType.PointerUp,
button: getBidiButton(options.button ?? MouseButton.Left), button: getBidiButton(options.button ?? MouseButton.Left),
}, },
], ],
@ -533,18 +550,18 @@ export class Mouse extends BaseMouse {
): Promise<void> { ): Promise<void> {
const actions: Bidi.Input.PointerSourceAction[] = [ const actions: Bidi.Input.PointerSourceAction[] = [
{ {
type: Bidi.Input.ActionType.PointerMove, type: ActionType.PointerMove,
x, x,
y, y,
origin: options.origin, origin: options.origin,
}, },
]; ];
const pointerDownAction = { const pointerDownAction = {
type: Bidi.Input.ActionType.PointerDown, type: ActionType.PointerDown,
button: getBidiButton(options.button ?? MouseButton.Left), button: getBidiButton(options.button ?? MouseButton.Left),
} as const; } as const;
const pointerUpAction = { const pointerUpAction = {
type: Bidi.Input.ActionType.PointerUp, type: ActionType.PointerUp,
button: pointerDownAction.button, button: pointerDownAction.button,
} as const; } as const;
for (let i = 1; i < (options.count ?? 1); ++i) { for (let i = 1; i < (options.count ?? 1); ++i) {
@ -553,7 +570,7 @@ export class Mouse extends BaseMouse {
actions.push(pointerDownAction); actions.push(pointerDownAction);
if (options.delay) { if (options.delay) {
actions.push({ actions.push({
type: Bidi.Input.ActionType.Pause, type: ActionType.Pause,
duration: options.delay, duration: options.delay,
}); });
} }
@ -562,7 +579,7 @@ export class Mouse extends BaseMouse {
context: this.#context.id, context: this.#context.id,
actions: [ actions: [
{ {
type: Bidi.Input.SourceActionsType.Pointer, type: SourceActionsType.Pointer,
id: InputId.Mouse, id: InputId.Mouse,
actions, actions,
}, },
@ -577,11 +594,11 @@ export class Mouse extends BaseMouse {
context: this.#context.id, context: this.#context.id,
actions: [ actions: [
{ {
type: Bidi.Input.SourceActionsType.Wheel, type: SourceActionsType.Wheel,
id: InputId.Wheel, id: InputId.Wheel,
actions: [ actions: [
{ {
type: Bidi.Input.ActionType.Scroll, type: ActionType.Scroll,
...(this.#lastMovePoint ?? { ...(this.#lastMovePoint ?? {
x: 0, x: 0,
y: 0, y: 0,
@ -628,20 +645,20 @@ export class Touchscreen extends BaseTouchscreen {
context: this.#context.id, context: this.#context.id,
actions: [ actions: [
{ {
type: Bidi.Input.SourceActionsType.Pointer, type: SourceActionsType.Pointer,
id: InputId.Finger, id: InputId.Finger,
parameters: { parameters: {
pointerType: Bidi.Input.PointerType.Touch, pointerType: Bidi.Input.PointerType.Touch,
}, },
actions: [ actions: [
{ {
type: Bidi.Input.ActionType.PointerMove, type: ActionType.PointerMove,
x, x,
y, y,
origin: options.origin, origin: options.origin,
}, },
{ {
type: Bidi.Input.ActionType.PointerDown, type: ActionType.PointerDown,
button: 0, button: 0,
}, },
], ],
@ -659,14 +676,14 @@ export class Touchscreen extends BaseTouchscreen {
context: this.#context.id, context: this.#context.id,
actions: [ actions: [
{ {
type: Bidi.Input.SourceActionsType.Pointer, type: SourceActionsType.Pointer,
id: InputId.Finger, id: InputId.Finger,
parameters: { parameters: {
pointerType: Bidi.Input.PointerType.Touch, pointerType: Bidi.Input.PointerType.Touch,
}, },
actions: [ actions: [
{ {
type: Bidi.Input.ActionType.PointerMove, type: ActionType.PointerMove,
x, x,
y, y,
origin: options.origin, origin: options.origin,
@ -682,14 +699,14 @@ export class Touchscreen extends BaseTouchscreen {
context: this.#context.id, context: this.#context.id,
actions: [ actions: [
{ {
type: Bidi.Input.SourceActionsType.Pointer, type: SourceActionsType.Pointer,
id: InputId.Finger, id: InputId.Finger,
parameters: { parameters: {
pointerType: Bidi.Input.PointerType.Touch, pointerType: Bidi.Input.PointerType.Touch,
}, },
actions: [ actions: [
{ {
type: Bidi.Input.ActionType.PointerUp, type: ActionType.PointerUp,
button: 0, button: 0,
}, },
], ],

View File

@ -28,9 +28,9 @@ import {releaseReference} from './utils.js';
export class JSHandle<T = unknown> extends BaseJSHandle<T> { export class JSHandle<T = unknown> extends BaseJSHandle<T> {
#disposed = false; #disposed = false;
#realm: Realm; #realm: Realm;
#remoteValue; #remoteValue: Bidi.Script.RemoteValue;
constructor(realm: Realm, remoteValue: Bidi.CommonDataTypes.RemoteValue) { constructor(realm: Realm, remoteValue: Bidi.Script.RemoteValue) {
super(); super();
this.#realm = realm; this.#realm = realm;
this.#remoteValue = remoteValue; this.#remoteValue = remoteValue;
@ -130,7 +130,10 @@ export class JSHandle<T = unknown> extends BaseJSHandle<T> {
} }
this.#disposed = true; this.#disposed = true;
if ('handle' in this.#remoteValue) { 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<T = unknown> extends BaseJSHandle<T> {
return 'handle' in this.#remoteValue ? this.#remoteValue.handle : undefined; return 'handle' in this.#remoteValue ? this.#remoteValue.handle : undefined;
} }
remoteValue(): Bidi.CommonDataTypes.RemoteValue { remoteValue(): Bidi.Script.RemoteValue {
return this.#remoteValue; return this.#remoteValue;
} }
} }

View File

@ -36,7 +36,7 @@ export class NetworkManager extends EventEmitter {
['network.responseStarted', this.#onResponseStarted.bind(this)], ['network.responseStarted', this.#onResponseStarted.bind(this)],
['network.responseCompleted', this.#onResponseCompleted.bind(this)], ['network.responseCompleted', this.#onResponseCompleted.bind(this)],
['network.fetchError', this.#onFetchError.bind(this)], ['network.fetchError', this.#onFetchError.bind(this)],
]) as Map<Bidi.Message.EventNames, Handler>; ]) as Map<Bidi.Event['method'], Handler>;
#requestMap = new Map<string, HTTPRequest>(); #requestMap = new Map<string, HTTPRequest>();
#navigationMap = new Map<string, HTTPResponse>(); #navigationMap = new Map<string, HTTPResponse>();
@ -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 ?? ''); const frame = this.#page.frame(event.context ?? '');
if (!frame) { if (!frame) {
return; return;
@ -73,7 +73,7 @@ export class NetworkManager extends EventEmitter {
#onResponseStarted(_event: any) {} #onResponseStarted(_event: any) {}
#onResponseCompleted(event: Bidi.Network.ResponseCompletedParams): void { #onResponseCompleted(event: Bidi.Network.ResponseCompletedParameters): void {
const request = this.#requestMap.get(event.request.request); const request = this.#requestMap.get(event.request.request);
if (!request) { if (!request) {
return; return;
@ -91,7 +91,7 @@ export class NetworkManager extends EventEmitter {
this.#requestMap.delete(event.request.request); this.#requestMap.delete(event.request.request);
} }
#onFetchError(event: Bidi.Network.FetchErrorParams) { #onFetchError(event: Bidi.Network.FetchErrorParameters) {
const request = this.#requestMap.get(event.request.request); const request = this.#requestMap.get(event.request.request);
if (!request) { if (!request) {
return; return;

View File

@ -78,15 +78,18 @@ export class Page extends PageBase {
#networkManager: NetworkManager; #networkManager: NetworkManager;
#viewport: Viewport | null = null; #viewport: Viewport | null = null;
#closedDeferred = Deferred.create<TargetCloseError>(); #closedDeferred = Deferred.create<TargetCloseError>();
#subscribedEvents = new Map<string, Handler<any>>([ #subscribedEvents = new Map<Bidi.Event['method'], Handler<any>>([
['log.entryAdded', this.#onLogEntryAdded.bind(this)], ['log.entryAdded', this.#onLogEntryAdded.bind(this)],
['browsingContext.load', this.#onFrameLoaded.bind(this)], ['browsingContext.load', this.#onFrameLoaded.bind(this)],
[ [
'browsingContext.domContentLoaded', 'browsingContext.domContentLoaded',
this.#onFrameDOMContentLoaded.bind(this), this.#onFrameDOMContentLoaded.bind(this),
], ],
['browsingContext.fragmentNavigated', this.#onFrameNavigated.bind(this)], [
]) as Map<Bidi.Session.SubscriptionRequestEvent, Handler>; 'browsingContext.navigationStarted',
this.#onFrameNavigationStarted.bind(this),
],
]);
#networkManagerEvents = new Map<symbol, Handler<any>>([ #networkManagerEvents = new Map<symbol, Handler<any>>([
[ [
NetworkManagerEmittedEvents.Request, NetworkManagerEmittedEvents.Request,
@ -252,19 +255,47 @@ export class Page extends PageBase {
context.parent context.parent
); );
this.#frameTree.addFrame(frame); this.#frameTree.addFrame(frame);
if (frame !== this.mainFrame()) {
this.emit(PageEmittedEvents.FrameAttached, frame); this.emit(PageEmittedEvents.FrameAttached, frame);
} }
} }
}
async #onFrameNavigated( async #onFrameNavigationStarted(
info: Bidi.BrowsingContext.NavigationInfo info: Bidi.BrowsingContext.NavigationInfo
): Promise<void> { ): Promise<void> {
const frameId = info.context; const frameId = info.context;
let frame = this.frame(frameId); const frame = this.frame(frameId);
// Detach all child frames first.
if (frame) { 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); this.emit(PageEmittedEvents.FrameNavigated, frame);
} }
} }
@ -290,7 +321,7 @@ export class Page extends PageBase {
this.emit(PageEmittedEvents.FrameDetached, frame); this.emit(PageEmittedEvents.FrameDetached, frame);
} }
#onLogEntryAdded(event: Bidi.Log.LogEntry): void { #onLogEntryAdded(event: Bidi.Log.Entry): void {
const frame = this.frame(event.source.context); const frame = this.frame(event.source.context);
if (!frame) { if (!frame) {
return; return;
@ -514,11 +545,12 @@ export class Page extends PageBase {
landscape, landscape,
width, width,
height, height,
pageRanges, pageRanges: ranges,
scale, scale,
preferCSSPageSize, preferCSSPageSize,
timeout, timeout,
} = this._getPDFOptions(options, 'cm'); } = this._getPDFOptions(options, 'cm');
const pageRanges = ranges ? ranges.split(', ') : [];
const {result} = await waitWithTimeout( const {result} = await waitWithTimeout(
this.#connection.send('browsingContext.print', { this.#connection.send('browsingContext.print', {
context: this.mainFrame()._id, context: this.mainFrame()._id,
@ -529,7 +561,7 @@ export class Page extends PageBase {
width, width,
height, height,
}, },
pageRanges: pageRanges.split(', '), pageRanges,
scale, scale,
shrinkToFit: !preferCSSPageSize, shrinkToFit: !preferCSSPageSize,
}), }),
@ -667,13 +699,13 @@ export class Page extends PageBase {
} }
function isConsoleLogEntry( function isConsoleLogEntry(
event: Bidi.Log.LogEntry event: Bidi.Log.Entry
): event is Bidi.Log.ConsoleLogEntry { ): event is Bidi.Log.ConsoleLogEntry {
return event.type === 'console'; return event.type === 'console';
} }
function isJavaScriptLogEntry( function isJavaScriptLogEntry(
event: Bidi.Log.LogEntry event: Bidi.Log.Entry
): event is Bidi.Log.JavascriptLogEntry { ): event is Bidi.Log.JavascriptLogEntry {
return event.type === 'javascript'; return event.type === 'javascript';
} }

View File

@ -114,7 +114,9 @@ export class Realm extends EventEmitter {
); );
let responsePromise; let responsePromise;
const resultOwnership = returnByValue ? 'none' : 'root'; const resultOwnership = returnByValue
? Bidi.Script.ResultOwnership.None
: Bidi.Script.ResultOwnership.Root;
if (isString(pageFunction)) { if (isString(pageFunction)) {
const expression = SOURCE_URL_REGEX.test(pageFunction) const expression = SOURCE_URL_REGEX.test(pageFunction)
? pageFunction ? pageFunction
@ -161,7 +163,7 @@ export class Realm extends EventEmitter {
*/ */
export function getBidiHandle( export function getBidiHandle(
realmOrContext: Realm, realmOrContext: Realm,
result: Bidi.CommonDataTypes.RemoteValue, result: Bidi.Script.RemoteValue,
frame: Frame frame: Frame
): JSHandle | ElementHandle<Node> { ): JSHandle | ElementHandle<Node> {
if (result.type === 'node' || result.type === 'window') { if (result.type === 'node' || result.type === 'window') {

View File

@ -32,8 +32,8 @@ class UnserializableError extends Error {}
* @internal * @internal
*/ */
export class BidiSerializer { export class BidiSerializer {
static serializeNumber(arg: number): Bidi.CommonDataTypes.LocalValue { static serializeNumber(arg: number): Bidi.Script.LocalValue {
let value: Bidi.CommonDataTypes.SpecialNumber | number; let value: Bidi.Script.SpecialNumber | number;
if (Object.is(arg, -0)) { if (Object.is(arg, -0)) {
value = '-0'; value = '-0';
} else if (Object.is(arg, Infinity)) { } 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) { if (arg === null) {
return { return {
type: 'null', type: 'null',
@ -78,7 +78,7 @@ export class BidiSerializer {
throw error; throw error;
} }
const parsedObject: Bidi.CommonDataTypes.MappingLocalValue = []; const parsedObject: Bidi.Script.MappingLocalValue = [];
for (const key in arg) { for (const key in arg) {
parsedObject.push([ parsedObject.push([
BidiSerializer.serializeRemoveValue(key), 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) { switch (typeof arg) {
case 'symbol': case 'symbol':
case 'function': case 'function':
@ -145,9 +145,7 @@ export class BidiSerializer {
static async serialize( static async serialize(
arg: unknown, arg: unknown,
context: BrowsingContext context: BrowsingContext
): Promise< ): Promise<Bidi.Script.LocalValue> {
Bidi.CommonDataTypes.LocalValue | Bidi.CommonDataTypes.RemoteValue
> {
if (arg instanceof LazyArg) { if (arg instanceof LazyArg) {
arg = await arg.get(context); arg = await arg.get(context);
} }
@ -167,15 +165,13 @@ export class BidiSerializer {
if (objectHandle.disposed) { if (objectHandle.disposed) {
throw new Error('JSHandle is disposed!'); throw new Error('JSHandle is disposed!');
} }
return objectHandle.remoteValue(); return objectHandle.remoteValue() as Bidi.Script.RemoteReference;
} }
return BidiSerializer.serializeRemoveValue(arg); return BidiSerializer.serializeRemoveValue(arg);
} }
static deserializeNumber( static deserializeNumber(value: Bidi.Script.SpecialNumber | number): number {
value: Bidi.CommonDataTypes.SpecialNumber | number
): number {
switch (value) { switch (value) {
case '-0': case '-0':
return -0; return -0;
@ -190,20 +186,22 @@ export class BidiSerializer {
} }
} }
static deserializeLocalValue( static deserializeLocalValue(result: Bidi.Script.RemoteValue): unknown {
result: Bidi.CommonDataTypes.RemoteValue
): unknown {
switch (result.type) { switch (result.type) {
case 'array': case 'array':
// TODO: Check expected output when value is undefined if (result.value) {
return result.value?.map(value => { return result.value.map(value => {
return BidiSerializer.deserializeLocalValue(value); return BidiSerializer.deserializeLocalValue(value);
}); });
}
break;
case 'set': case 'set':
// TODO: Check expected output when value is undefined if (result.value) {
return result.value.reduce((acc: Set<unknown>, value) => { return result.value.reduce((acc: Set<unknown>, value) => {
return acc.add(BidiSerializer.deserializeLocalValue(value)); return acc.add(BidiSerializer.deserializeLocalValue(value));
}, new Set()); }, new Set());
}
break;
case 'object': case 'object':
if (result.value) { if (result.value) {
return result.value.reduce((acc: Record<any, unknown>, tuple) => { return result.value.reduce((acc: Record<any, unknown>, tuple) => {
@ -213,12 +211,14 @@ export class BidiSerializer {
}, {}); }, {});
} }
break; break;
case 'map': case 'map':
return result.value.reduce((acc: Map<unknown, unknown>, tuple) => { if (result.value) {
return result.value?.reduce((acc: Map<unknown, unknown>, tuple) => {
const {key, value} = BidiSerializer.deserializeTuple(tuple); const {key, value} = BidiSerializer.deserializeTuple(tuple);
return acc.set(key, value); return acc.set(key, value);
}, new Map()); }, new Map());
}
break;
case 'promise': case 'promise':
return {}; return {};
case 'regexp': case 'regexp':
@ -246,8 +246,8 @@ export class BidiSerializer {
} }
static deserializeTuple([serializedKey, serializedValue]: [ static deserializeTuple([serializedKey, serializedValue]: [
Bidi.CommonDataTypes.RemoteValue | string, Bidi.Script.RemoteValue | string,
Bidi.CommonDataTypes.RemoteValue, Bidi.Script.RemoteValue,
]): {key: unknown; value: unknown} { ]): {key: unknown; value: unknown} {
const key = const key =
typeof serializedKey === 'string' typeof serializedKey === 'string'
@ -258,7 +258,7 @@ export class BidiSerializer {
return {key, value}; return {key, value};
} }
static deserialize(result: Bidi.CommonDataTypes.RemoteValue): any { static deserialize(result: Bidi.Script.RemoteValue): any {
if (!result) { if (!result) {
debugError('Service did not produce a result.'); debugError('Service did not produce a result.');
return undefined; return undefined;

View File

@ -31,7 +31,7 @@ export const debugError = debug('puppeteer:error');
*/ */
export async function releaseReference( export async function releaseReference(
client: Realm, client: Realm,
remoteReference: Bidi.CommonDataTypes.RemoteReference remoteReference: Bidi.Script.RemoteReference
): Promise<void> { ): Promise<void> {
if (!remoteReference.handle) { if (!remoteReference.handle) {
return; return;

View File

@ -394,19 +394,19 @@ export async function waitForEvent<T>(
deferred.resolve(event); deferred.resolve(event);
} }
}); });
return Deferred.race<T | Error>([deferred, abortPromise]).then(
r => { try {
removeEventListeners([listener]); const response = await Deferred.race<T | Error>([deferred, abortPromise]);
if (isErrorLike(r)) { if (isErrorLike(response)) {
throw r; throw response;
} }
return r;
}, return response;
error => { } catch (error) {
removeEventListeners([listener]);
throw error; throw error;
} finally {
removeEventListeners([listener]);
} }
);
} }
/** /**

View File

@ -1067,18 +1067,6 @@
"parameters": ["webDriverBiDi"], "parameters": ["webDriverBiDi"],
"expectations": ["FAIL", "PASS"] "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", "testIdPattern": "[network.spec] network Request.frame should work for subframe navigation request",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -1169,12 +1157,6 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["SKIP"] "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", "testIdPattern": "[page.spec] Page Page.close should reject all promises when page is closed",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -1907,6 +1889,18 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["FAIL"] "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", "testIdPattern": "[emulation.spec] Emulation Page.emulate should support clicking",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -2048,14 +2042,14 @@
{ {
"testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work right after framenavigated", "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work right after framenavigated",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"], "parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"] "expectations": ["FAIL"]
}, },
{ {
"testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work right after framenavigated", "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work right after framenavigated",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"], "parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"] "expectations": ["SKIP"]
}, },
{ {
"testIdPattern": "[fixtures.spec] Fixtures should close the browser when the node process closes", "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", "testIdPattern": "[frame.spec] Frame specs Frame Management should support framesets",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"], "parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"] "expectations": ["FAIL", "PASS"]
}, },
{ {
"testIdPattern": "[frame.spec] Frame specs Frame Management should support lazy frames", "testIdPattern": "[frame.spec] Frame specs Frame Management should support lazy frames",
@ -2603,6 +2597,12 @@
"parameters": ["firefox", "webDriverBiDi"], "parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"] "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", "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to empty page with domcontentloaded",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -2641,9 +2641,9 @@
}, },
{ {
"testIdPattern": "[navigation.spec] navigation Page.goto should not leak listeners during navigation of 11 pages", "testIdPattern": "[navigation.spec] navigation Page.goto should not leak listeners during navigation of 11 pages",
"platforms": ["darwin"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"], "parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS", "TIMEOUT"] "expectations": ["SKIP"]
}, },
{ {
"testIdPattern": "[navigation.spec] navigation Page.goto should return last response in redirect chain", "testIdPattern": "[navigation.spec] navigation Page.goto should return last response in redirect chain",
@ -2741,6 +2741,12 @@
"parameters": ["firefox", "webDriverBiDi"], "parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"] "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", "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with both domcontentloaded and load",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -2873,6 +2879,30 @@
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "firefox"],
"expectations": ["FAIL"] "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", "testIdPattern": "[network.spec] network raw network headers Same-origin set-cookie subresource",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -2889,7 +2919,7 @@
"testIdPattern": "[network.spec] network Request.headers should define Firefox as user agent header", "testIdPattern": "[network.spec] network Request.headers should define Firefox as user agent header",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"], "parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"] "expectations": ["PASS"]
}, },
{ {
"testIdPattern": "[network.spec] network Request.initiator should return the initiator", "testIdPattern": "[network.spec] network Request.initiator should return the initiator",
@ -2961,7 +2991,7 @@
"testIdPattern": "[network.spec] network Response.headers should work", "testIdPattern": "[network.spec] network Response.headers should work",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"], "parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"] "expectations": ["PASS"]
}, },
{ {
"testIdPattern": "[network.spec] network Response.json should work", "testIdPattern": "[network.spec] network Response.json should work",

View File

@ -295,7 +295,7 @@ describe('ElementHandle specs', function () {
}); });
expect(error.message).atLeastOneToContain([ expect(error.message).atLeastOneToContain([
'Node is either not clickable or not an HTMLElement', 'Node is either not clickable or not an HTMLElement',
'no such node', 'no such element',
]); ]);
}); });
it('should throw for recursively hidden nodes', async () => { it('should throw for recursively hidden nodes', async () => {
@ -311,7 +311,7 @@ describe('ElementHandle specs', function () {
}); });
expect(error.message).atLeastOneToContain([ expect(error.message).atLeastOneToContain([
'Node is either not clickable or not an HTMLElement', 'Node is either not clickable or not an HTMLElement',
'no such node', 'no such element',
]); ]);
}); });
it('should throw for <br> elements', async () => { it('should throw for <br> elements', async () => {

View File

@ -213,6 +213,7 @@ describe('Frame specs', function () {
return navigatedFrames.push(frame); return navigatedFrames.push(frame);
}); });
await page.goto(server.PREFIX + '/frames/nested-frames.html'); await page.goto(server.PREFIX + '/frames/nested-frames.html');
expect(attachedFrames).toHaveLength(4); expect(attachedFrames).toHaveLength(4);
expect(detachedFrames).toHaveLength(0); expect(detachedFrames).toHaveLength(0);
expect(navigatedFrames).toHaveLength(5); expect(navigatedFrames).toHaveLength(5);

View File

@ -387,7 +387,9 @@ describe('Launcher specs', function () {
expect(puppeteer.product).toBe('firefox'); expect(puppeteer.product).toBe('firefox');
} }
}); });
it('should work with no default arguments', async () => { (!isHeadless ? it : it.skip)(
'should work with no default arguments',
async () => {
const {context, close} = await launch({ const {context, close} = await launch({
ignoreDefaultArgs: true, ignoreDefaultArgs: true,
}); });
@ -398,7 +400,8 @@ describe('Launcher specs', function () {
} finally { } finally {
await close(); await close();
} }
}); }
);
it('should filter out ignored default arguments in Chrome', async () => { it('should filter out ignored default arguments in Chrome', async () => {
const {defaultBrowserOptions, puppeteer} = await getTestState({ const {defaultBrowserOptions, puppeteer} = await getTestState({
skipLaunch: true, skipLaunch: true,

View File

@ -145,7 +145,8 @@ export const setupTestBrowserHooks = (): void => {
timeout: this.timeout() - 1_000, timeout: this.timeout() - 1_000,
}); });
} }
} catch { } catch (error) {
console.error(error);
// Intentionally empty as `getTestState` will throw // Intentionally empty as `getTestState` will throw
// if browser is not found // if browser is not found
} }
@ -459,7 +460,7 @@ const closeLaunched = (storage: Array<() => Promise<void>>) => {
}; };
export const launch = async ( export const launch = async (
launchOptions: PuppeteerLaunchOptions, launchOptions: Readonly<PuppeteerLaunchOptions>,
options: { options: {
after?: 'each' | 'all'; after?: 'each' | 'all';
createContext?: boolean; createContext?: boolean;

View File

@ -704,7 +704,6 @@ describe('navigation', function () {
server.setRoute('/frames/style.css', () => {}); server.setRoute('/frames/style.css', () => {});
let frame: Frame | undefined; let frame: Frame | undefined;
let timeout: NodeJS.Timeout | undefined;
const eventPromises = Deferred.race([ const eventPromises = Deferred.race([
Promise.all([ Promise.all([
waitEvent(page, 'frameattached').then(_frame => { waitEvent(page, 'frameattached').then(_frame => {
@ -724,7 +723,6 @@ describe('navigation', function () {
); );
try { try {
await eventPromises; await eventPromises;
clearTimeout(timeout);
} catch (error) { } catch (error) {
navigationPromise.catch(() => {}); navigationPromise.catch(() => {});
throw error; throw error;

View File

@ -25,6 +25,7 @@ const FILENAME = __filename.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');
const parseStackTrace = (stack: string): string => { const parseStackTrace = (stack: string): string => {
stack = stack.replace(new RegExp(FILENAME, 'g'), '<filename>'); stack = stack.replace(new RegExp(FILENAME, 'g'), '<filename>');
stack = stack.replace(/<filename>:(\d+):(\d+)/g, '<filename>:<line>:<col>'); stack = stack.replace(/<filename>:(\d+):(\d+)/g, '<filename>:<line>:<col>');
stack = stack.replace(/<anonymous>:(\d+):(\d+)/g, '<anonymous>:<line>:<col>');
return stack; return stack;
}; };
@ -51,7 +52,7 @@ describe('Stack trace', function () {
).toMatchObject({ ).toMatchObject({
...[ ...[
'Error: Test', 'Error: Test',
'evaluate (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:1:18)', 'evaluate (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:<line>:<col>)',
], ],
}); });
}); });
@ -75,7 +76,7 @@ describe('Stack trace', function () {
).toMatchObject({ ).toMatchObject({
...[ ...[
'Error: Test', 'Error: Test',
'evaluateHandle (evaluateHandle at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:1:18)', 'evaluateHandle (evaluateHandle at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:<line>:<col>)',
], ],
}); });
}); });
@ -104,8 +105,8 @@ describe('Stack trace', function () {
).toMatchObject({ ).toMatchObject({
...[ ...[
'Error: Test', 'Error: Test',
'evaluateHandle (evaluateHandle at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:2:22)', 'evaluateHandle (evaluateHandle at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:<line>:<col>)',
'evaluate (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:1:12)', 'evaluate (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:<line>:<col>)',
], ],
}); });
}); });
@ -141,11 +142,11 @@ describe('Stack trace', function () {
).toMatchObject({ ).toMatchObject({
...[ ...[
'Error: Test', 'Error: Test',
'a (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:2:22)', 'a (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:<line>:<col>)',
'b (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:5:16)', 'b (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:<line>:<col>)',
'c (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:8:16)', 'c (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:<line>:<col>)',
'd (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:11:16)', 'd (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:<line>:<col>)',
'evaluate (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:13:12)', 'evaluate (evaluate at Context.<anonymous> (<filename>:<line>:<col>), <anonymous>:<line>:<col>)',
], ],
}); });
}); });

View File

@ -167,9 +167,6 @@ export function getExpectationUpdates(
} }
for (const failure of results.failures) { for (const failure of results.failures) {
if (passesByKey.has(getTestId(failure.file, failure.fullTitle))) {
continue;
}
// If an error occurs during a hook // If an error occurs during a hook
// the error not have a file associated with it // the error not have a file associated with it
if (!failure.file) { if (!failure.file) {
@ -185,6 +182,10 @@ export function getExpectationUpdates(
continue; continue;
} }
if (passesByKey.has(getTestId(failure.file, failure.fullTitle))) {
continue;
}
const expectationEntry = findEffectiveExpectationForTest( const expectationEntry = findEffectiveExpectationForTest(
expectations, expectations,
failure failure