diff --git a/package-lock.json b/package-lock.json index 2c182272..758b1c41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,6 @@ "@typescript-eslint/eslint-plugin": "5.46.1", "@typescript-eslint/parser": "5.46.1", "c8": "7.12.0", - "chromium-bidi": "0.4.3", "commonmark": "0.30.0", "cross-env": "7.0.3", "diff": "5.1.0", @@ -2663,7 +2662,6 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.3.tgz", "integrity": "sha512-A40H1rdpJqkTdnGhnYDzMhtDdIbkXNFj2wgIfivMXL7LyHFDmBtv1hdyycDhnxtYunbPLDZtTs/n+ZT5j7Vnew==", - "dev": true, "peerDependencies": { "devtools-protocol": "*", "mitt": "*" @@ -6036,8 +6034,7 @@ "node_modules/mitt": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", - "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==", - "dev": true + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==" }, "node_modules/mkdirp-classic": { "version": "0.5.3", @@ -8677,6 +8674,7 @@ "version": "19.6.3", "license": "Apache-2.0", "dependencies": { + "chromium-bidi": "0.4.3", "cross-fetch": "3.1.5", "debug": "4.3.4", "devtools-protocol": "0.0.1082910", @@ -11003,7 +11001,6 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.3.tgz", "integrity": "sha512-A40H1rdpJqkTdnGhnYDzMhtDdIbkXNFj2wgIfivMXL7LyHFDmBtv1hdyycDhnxtYunbPLDZtTs/n+ZT5j7Vnew==", - "dev": true, "requires": {} }, "cli-cursor": { @@ -13404,8 +13401,7 @@ "mitt": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", - "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==", - "dev": true + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==" }, "mkdirp-classic": { "version": "0.5.3", @@ -14106,6 +14102,7 @@ "puppeteer-core": { "version": "file:packages/puppeteer-core", "requires": { + "chromium-bidi": "0.4.3", "cross-fetch": "3.1.5", "debug": "4.3.4", "devtools-protocol": "0.0.1082910", diff --git a/package.json b/package.json index f63a0a72..29151332 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,6 @@ "@typescript-eslint/eslint-plugin": "5.46.1", "@typescript-eslint/parser": "5.46.1", "c8": "7.12.0", - "chromium-bidi": "0.4.3", "commonmark": "0.30.0", "cross-env": "7.0.3", "diff": "5.1.0", diff --git a/packages/puppeteer-core/package.json b/packages/puppeteer-core/package.json index e1588296..1bcdc416 100644 --- a/packages/puppeteer-core/package.json +++ b/packages/puppeteer-core/package.json @@ -163,6 +163,7 @@ "author": "The Chromium Authors", "license": "Apache-2.0", "dependencies": { + "chromium-bidi": "0.4.3", "cross-fetch": "3.1.5", "debug": "4.3.4", "devtools-protocol": "0.0.1082910", diff --git a/packages/puppeteer-core/src/common/ExecutionContext.ts b/packages/puppeteer-core/src/common/ExecutionContext.ts index 8c691147..b8ae9cc7 100644 --- a/packages/puppeteer-core/src/common/ExecutionContext.ts +++ b/packages/puppeteer-core/src/common/ExecutionContext.ts @@ -26,6 +26,7 @@ import { createJSHandle, getExceptionMessage, isString, + stringifyFunction, valueFromRemoteObject, } from './util.js'; @@ -266,29 +267,11 @@ export class ExecutionContext { : createJSHandle(this, remoteObject); } - let functionText = pageFunction.toString(); - try { - new Function('(' + functionText + ')'); - } catch (error) { - // This means we might have a function shorthand. Try another - // time prefixing 'function '. - if (functionText.startsWith('async ')) { - functionText = - 'async function ' + functionText.substring('async '.length); - } else { - functionText = 'function ' + functionText; - } - try { - new Function('(' + functionText + ')'); - } catch (error) { - // We tried hard to serialize, but there's a weird beast here. - throw new Error('Passed function is not well-serializable!'); - } - } let callFunctionOnPromise; try { callFunctionOnPromise = this._client.send('Runtime.callFunctionOn', { - functionDeclaration: functionText + '\n' + suffix + '\n', + functionDeclaration: + stringifyFunction(pageFunction) + '\n' + suffix + '\n', executionContextId: this._contextId, arguments: await Promise.all(args.map(convertArgument.bind(this))), returnByValue, diff --git a/packages/puppeteer-core/src/common/bidi/BidiOverCDP.ts b/packages/puppeteer-core/src/common/bidi/BidiOverCDP.ts index d3c9d894..0d7bdda0 100644 --- a/packages/puppeteer-core/src/common/bidi/BidiOverCDP.ts +++ b/packages/puppeteer-core/src/common/bidi/BidiOverCDP.ts @@ -1,6 +1,7 @@ import {CDPSession, Connection as CDPPPtrConnection} from '../Connection.js'; import {Connection as BidiPPtrConnection} from './Connection.js'; -import {Bidi, BidiMapper} from '../../../third_party/chromium-bidi/index.js'; +import * as BidiMapper from 'chromium-bidi/lib/cjs/bidiMapper/bidiMapper.js'; +import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; import type {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js'; import {Handler} from '../EventEmitter.js'; diff --git a/packages/puppeteer-core/src/common/bidi/Browser.ts b/packages/puppeteer-core/src/common/bidi/Browser.ts index 2dd25f7e..695b5827 100644 --- a/packages/puppeteer-core/src/common/bidi/Browser.ts +++ b/packages/puppeteer-core/src/common/bidi/Browser.ts @@ -34,7 +34,10 @@ export class Browser extends BrowserBase { static async create(opts: Options): Promise { // TODO: await until the connection is established. try { - (await opts.connection.send('session.new', {})) as {sessionId: string}; + // TODO: Add 'session.new' to BiDi types + (await opts.connection.send('session.new' as any, {})) as unknown as { + sessionId: string; + }; } catch {} return new Browser(opts); } diff --git a/packages/puppeteer-core/src/common/bidi/BrowserContext.ts b/packages/puppeteer-core/src/common/bidi/BrowserContext.ts index ccb231bd..d1f4b1b4 100644 --- a/packages/puppeteer-core/src/common/bidi/BrowserContext.ts +++ b/packages/puppeteer-core/src/common/bidi/BrowserContext.ts @@ -31,10 +31,10 @@ export class BrowserContext extends BrowserContextBase { } override async newPage(): Promise { - const result = (await this.#connection.send('browsingContext.create', { + const response = await this.#connection.send('browsingContext.create', { type: 'tab', - })) as {context: string}; - return new Page(this.#connection, result.context); + }); + return new Page(this.#connection, response.result.context); } override async close(): Promise {} diff --git a/packages/puppeteer-core/src/common/bidi/Connection.ts b/packages/puppeteer-core/src/common/bidi/Connection.ts index b6b18bcf..c21e71db 100644 --- a/packages/puppeteer-core/src/common/bidi/Connection.ts +++ b/packages/puppeteer-core/src/common/bidi/Connection.ts @@ -22,28 +22,32 @@ import {ConnectionTransport} from '../ConnectionTransport.js'; import {EventEmitter} from '../EventEmitter.js'; import {ProtocolError} from '../Errors.js'; import {ConnectionCallback} from '../Connection.js'; +import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; -interface Command { - id: number; - method: string; - params: object; -} - -interface CommandResponse { - id: number; - result: object; -} - -interface ErrorResponse { - id: number; - error: string; - message: string; - stacktrace?: string; -} - -interface Event { - method: string; - params: object; +/** + * @internal + */ +interface Commands { + 'script.evaluate': { + params: Bidi.Script.EvaluateParameters; + returnType: Bidi.Script.EvaluateResult; + }; + 'script.callFunction': { + params: Bidi.Script.CallFunctionParameters; + returnType: Bidi.Script.CallFunctionResult; + }; + 'browsingContext.create': { + params: Bidi.BrowsingContext.CreateParameters; + returnType: Bidi.BrowsingContext.CreateResult; + }; + 'browsingContext.close': { + params: Bidi.BrowsingContext.CloseParameters; + returnType: Bidi.BrowsingContext.CloseResult; + }; + 'session.status': { + params: {context: string}; // TODO: Update Types in chromium bidi + returnType: Bidi.Session.StatusResult; + }; } /** @@ -69,13 +73,16 @@ export class Connection extends EventEmitter { return this.#closed; } - send(method: string, params: object): Promise { + send( + method: T, + params: Commands[T]['params'] + ): Promise { const id = ++this.#lastId; const stringifiedMessage = JSON.stringify({ id, method, params, - } as Command); + } as Bidi.Message.CommandRequest); debugProtocolSend(stringifiedMessage); this.#transport.send(stringifiedMessage); return new Promise((resolve, reject) => { @@ -99,9 +106,8 @@ export class Connection extends EventEmitter { } debugProtocolReceive(message); const object = JSON.parse(message) as - | Event - | ErrorResponse - | CommandResponse; + | Bidi.Message.CommandResponse + | Bidi.EventResponse; if ('id' in object) { const callback = this.#callbacks.get(object.id); // Callbacks could be all rejected if someone has called `.dispose()`. @@ -112,7 +118,7 @@ export class Connection extends EventEmitter { createProtocolError(callback.error, callback.method, object) ); } else { - callback.resolve(object.result); + callback.resolve(object); } } } else { @@ -154,10 +160,13 @@ function rewriteError( return error; } +/** + * @internal + */ function createProtocolError( error: ProtocolError, method: string, - object: ErrorResponse + object: Bidi.Message.ErrorResult ): Error { let message = `Protocol error (${method}): ${object.error} ${object.message}`; if (object.stacktrace) { diff --git a/packages/puppeteer-core/src/common/bidi/Page.ts b/packages/puppeteer-core/src/common/bidi/Page.ts index 426b7399..a4c4fe37 100644 --- a/packages/puppeteer-core/src/common/bidi/Page.ts +++ b/packages/puppeteer-core/src/common/bidi/Page.ts @@ -16,8 +16,9 @@ import {Page as PageBase} from '../../api/Page.js'; import {Connection} from './Connection.js'; -import type {EvaluateFunc} from '..//types.js'; - +import type {EvaluateFunc} from '../types.js'; +import {isString, stringifyFunction} from '../util.js'; +import {BidiSerializer} from './Serializer.js'; /** * @internal */ @@ -42,15 +43,32 @@ export class Page extends PageBase { Func extends EvaluateFunc = EvaluateFunc >( pageFunction: Func | string, - ..._args: Params + ...args: Params ): Promise>> { - // TODO: re-use evaluate logic from Execution context. - const str = `(${pageFunction.toString()})()`; - const result = (await this.#connection.send('script.evaluate', { - expression: str, - target: {context: this.#contextId}, - awaitPromise: true, - })) as {result: {type: string; value: any}}; - return result.result.value; + let responsePromise; + if (isString(pageFunction)) { + responsePromise = this.#connection.send('script.evaluate', { + expression: pageFunction, + target: {context: this.#contextId}, + awaitPromise: true, + }); + } else { + responsePromise = this.#connection.send('script.callFunction', { + functionDeclaration: stringifyFunction(pageFunction), + arguments: await Promise.all(args.map(BidiSerializer.serialize)), + target: {context: this.#contextId}, + awaitPromise: true, + }); + } + + const {result} = await responsePromise; + + if ('type' in result && result.type === 'exception') { + throw new Error(result.exceptionDetails.text); + } + + return BidiSerializer.deserialize(result.result) as Awaited< + ReturnType + >; } } diff --git a/packages/puppeteer-core/src/common/bidi/Serializer.ts b/packages/puppeteer-core/src/common/bidi/Serializer.ts new file mode 100644 index 00000000..dbee522f --- /dev/null +++ b/packages/puppeteer-core/src/common/bidi/Serializer.ts @@ -0,0 +1,203 @@ +import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; +import {debugError, isPlainObject} from '../util.js'; + +/** + * @internal + */ +class UnserializableError extends Error {} + +/** + * @internal + */ +export class BidiSerializer { + static serializeNumber(arg: number): Bidi.CommonDataTypes.LocalOrRemoteValue { + let value: Bidi.CommonDataTypes.SpecialNumber | number; + if (Object.is(arg, -0)) { + value = '-0'; + } else if (Object.is(arg, Infinity)) { + value = 'Infinity'; + } else if (Object.is(arg, -Infinity)) { + value = '-Infinity'; + } else if (Object.is(arg, NaN)) { + value = 'NaN'; + } else { + value = arg; + } + return { + type: 'number', + value, + }; + } + + static serializeObject( + arg: object | null + ): Bidi.CommonDataTypes.LocalOrRemoteValue { + if (arg === null) { + return { + type: 'null', + }; + } else if (Array.isArray(arg)) { + const parsedArray = arg.map(subArg => { + return BidiSerializer.serializeRemoveValue(subArg); + }); + + return { + type: 'array', + value: parsedArray, + }; + } else if (isPlainObject(arg)) { + const parsedObject: Bidi.CommonDataTypes.MappingLocalValue = []; + for (const key in arg) { + parsedObject.push([ + BidiSerializer.serializeRemoveValue(key), + BidiSerializer.serializeRemoveValue(arg[key]), + ]); + } + + return { + type: 'object', + value: parsedObject, + }; + } + + throw new UnserializableError( + 'Custom object sterilization not possible. Use plain objects instead.' + ); + } + + static serializeRemoveValue( + arg: unknown + ): Bidi.CommonDataTypes.LocalOrRemoteValue { + switch (typeof arg) { + case 'symbol': + case 'function': + throw new UnserializableError(`Unable to serializable ${typeof arg}`); + case 'object': + return BidiSerializer.serializeObject(arg); + + case 'undefined': + return { + type: 'undefined', + }; + case 'number': + return BidiSerializer.serializeNumber(arg); + case 'bigint': + return { + type: 'bigint', + value: arg.toString(), + }; + case 'string': + return { + type: 'string', + value: arg, + }; + case 'boolean': + return { + type: 'boolean', + value: arg, + }; + } + } + + static serialize(arg: unknown): Bidi.CommonDataTypes.LocalOrRemoteValue { + // TODO: See use case of LazyArgs + return BidiSerializer.serializeRemoveValue(arg); + } + + static deserializeNumber( + value: Bidi.CommonDataTypes.SpecialNumber | number + ): number { + switch (value) { + case '-0': + return -0; + case 'NaN': + return NaN; + case 'Infinity': + case '+Infinity': + return Infinity; + case '-Infinity': + return -Infinity; + default: + return value; + } + } + + static deserializeLocalValue( + result: Bidi.CommonDataTypes.RemoteValue + ): unknown { + switch (result.type) { + case 'array': + // TODO: Check expected output when value is undefined + return result.value?.map(value => { + return BidiSerializer.deserializeLocalValue(value); + }); + case 'set': + // TODO: Check expected output when value is undefined + return result.value.reduce((acc: Set, value: unknown) => { + return acc.add(value); + }, new Set()); + case 'object': + if (result.value) { + return result.value.reduce((acc: Record, tuple) => { + const {key, value} = BidiSerializer.deserializeTuple(tuple); + acc[key as any] = value; + return acc; + }, {}); + } + break; + case 'map': + return result.value.reduce((acc: Map, tuple) => { + const {key, value} = BidiSerializer.deserializeTuple(tuple); + return acc.set(key, value); + }, new Map()); + case 'promise': + return {}; + case 'undefined': + return undefined; + case 'null': + return null; + case 'number': + return BidiSerializer.deserializeNumber(result.value); + case 'bigint': + return BigInt(result.value); + case 'boolean': + return Boolean(result.value); + case 'string': + return result.value; + } + + throw new UnserializableError( + `Deserialization of type ${result.type} not supported.` + ); + } + + static deserializeTuple([serializedKey, serializedValue]: [ + Bidi.CommonDataTypes.RemoteValue | string, + Bidi.CommonDataTypes.RemoteValue + ]): {key: unknown; value: unknown} { + const key = + typeof serializedKey === 'string' + ? serializedKey + : BidiSerializer.deserializeLocalValue(serializedKey); + const value = BidiSerializer.deserializeLocalValue(serializedValue); + + return {key, value}; + } + + static deserialize(result: Bidi.CommonDataTypes.RemoteValue): unknown { + if (!result) { + debugError('Service did not produce a result.'); + return undefined; + } + + try { + return BidiSerializer.deserializeLocalValue(result); + } catch (error) { + if (error instanceof UnserializableError) { + debugError(error.message); + return undefined; + } + throw error; + } + } +} diff --git a/packages/puppeteer-core/src/common/util.ts b/packages/puppeteer-core/src/common/util.ts index a39e914f..006f485a 100644 --- a/packages/puppeteer-core/src/common/util.ts +++ b/packages/puppeteer-core/src/common/util.ts @@ -159,6 +159,13 @@ export const isNumber = (obj: unknown): obj is number => { return typeof obj === 'number' || obj instanceof Number; }; +/** + * @internal + */ +export const isPlainObject = (obj: unknown): obj is Record => { + return typeof obj === 'object' && obj?.constructor === Object; +}; + /** * @internal */ @@ -440,3 +447,29 @@ export async function getReadableFromProtocolStream( }, }); } + +/** + * @internal + */ +export function stringifyFunction(expression: Function): string { + let functionText = expression.toString(); + try { + new Function('(' + functionText + ')'); + } catch (error) { + // This means we might have a function shorthand. Try another + // time prefixing 'function '. + if (functionText.startsWith('async ')) { + functionText = + 'async function ' + functionText.substring('async '.length); + } else { + functionText = 'function ' + functionText; + } + try { + new Function('(' + functionText + ')'); + } catch (error) { + // We tried hard to serialize, but there's a weird beast here. + throw new Error('Passed function is not well-serializable!'); + } + } + return functionText; +} diff --git a/packages/puppeteer-core/third_party/chromium-bidi/index.ts b/packages/puppeteer-core/third_party/chromium-bidi/index.ts deleted file mode 100644 index b68d134c..00000000 --- a/packages/puppeteer-core/third_party/chromium-bidi/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright 2022 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export * as BidiMapper from 'chromium-bidi/lib/cjs/bidiMapper/bidiMapper.js'; -export * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; diff --git a/test/TestExpectations.json b/test/TestExpectations.json index e9ea873f..8e102fa3 100644 --- a/test/TestExpectations.json +++ b/test/TestExpectations.json @@ -379,7 +379,7 @@ }, { "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should not throw an error when evaluation does a navigation", - "platforms": ["darwin", "win32"], + "platforms": ["darwin", "win32", "linux"], "parameters": ["firefox"], "expectations": ["SKIP"] }, @@ -3174,9 +3174,111 @@ "expectations": ["PASS"] }, { - "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work", + "testIdPattern": "[evaluation.spec]", "platforms": ["darwin", "linux", "win32"], "parameters": ["webDriverBiDi"], "expectations": ["PASS"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should evaluate in the page context", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work right after framenavigated", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work from-inside an exposed function", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should be able to throw a tricky error", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should accept element handle as an argument", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should throw if underlying element was disposed", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should throw if elementHandles are from other frames", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should throw a nice error after a navigation", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should throw when evaluation triggers reload", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should simulate a user gesture", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should not throw an error when evaluation does a navigation", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should not throw an error when evaluation does a navigation", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluateOnNewDocument should evaluate before anything else on the page", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluateOnNewDocument should work with CSP", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Frame.evaluate should have different execution contexts", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Frame.evaluate should have correct execution contexts", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Frame.evaluate should execute after cross-site navigation", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["SKIP"] } ] diff --git a/test/src/bidi/Connection.spec.ts b/test/src/bidi/Connection.spec.ts index 1ce470bf..25f8702b 100644 --- a/test/src/bidi/Connection.spec.ts +++ b/test/src/bidi/Connection.spec.ts @@ -51,7 +51,7 @@ describe('WebDriver BiDi', () => { JSON.stringify(rawResponse) ); const response = await responsePromise; - expect(response).toEqual(rawResponse.result); + expect(response).toEqual(rawResponse); connection.dispose(); expect(transport.closed).toBeTruthy(); }); diff --git a/test/src/evaluation.spec.ts b/test/src/evaluation.spec.ts index 4f34e2d8..5dcb74c0 100644 --- a/test/src/evaluation.spec.ts +++ b/test/src/evaluation.spec.ts @@ -328,6 +328,20 @@ describe('Evaluation specs', function () { }) ).toBe(undefined); }); + it('should return promise as empty object', async () => { + const {page} = getTestState(); + + const result = await page.evaluate(() => { + return { + promise: new Promise(resolve => { + setTimeout(resolve, 1000); + }), + }; + }); + expect(result).toEqual({ + promise: {}, + }); + }); it('should fail for circular object', async () => { const {page} = getTestState();