Skip to content

Commit

Permalink
chore: Add Page.Console event to BiDi (#9700)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lightning00Blade committed Feb 20, 2023
1 parent fb0d405 commit 9b54365
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 48 deletions.
2 changes: 1 addition & 1 deletion packages/puppeteer-core/src/common/JSHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ declare const __JSHandleSymbol: unique symbol;
/**
* @internal
*/
export class CDPJSHandle<T> extends JSHandle<T> {
export class CDPJSHandle<T = unknown> extends JSHandle<T> {
/**
* Used for nominally typing {@link JSHandle}.
*/
Expand Down
3 changes: 1 addition & 2 deletions packages/puppeteer-core/src/common/WebWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
import {Protocol} from 'devtools-protocol';

import {JSHandle} from '../api/JSHandle.js';
import {createDeferredPromise} from '../util/DeferredPromise.js';

import {CDPSession} from './Connection.js';
Expand All @@ -31,7 +30,7 @@ import {debugError} from './util.js';
*/
export type ConsoleAPICalledCallback = (
eventType: ConsoleMessageType,
handles: JSHandle[],
handles: CDPJSHandle[],
trace: Protocol.Runtime.StackTrace
) => void;

Expand Down
5 changes: 1 addition & 4 deletions packages/puppeteer-core/src/common/bidi/Browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,7 @@ export class Browser extends BrowserBase {
static async create(opts: Options): Promise<Browser> {
// TODO: await until the connection is established.
try {
// TODO: Add 'session.new' to BiDi types
(await opts.connection.send('session.new' as any, {})) as unknown as {
sessionId: string;
};
await opts.connection.send('session.new', {});
} catch {}
return new Browser(opts);
}
Expand Down
14 changes: 14 additions & 0 deletions packages/puppeteer-core/src/common/bidi/Connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ interface Commands {
params: Bidi.Script.DisownParameters;
returnType: Bidi.Script.DisownResult;
};

'browsingContext.create': {
params: Bidi.BrowsingContext.CreateParameters;
returnType: Bidi.BrowsingContext.CreateResult;
Expand All @@ -50,10 +51,23 @@ interface Commands {
params: Bidi.BrowsingContext.CloseParameters;
returnType: Bidi.BrowsingContext.CloseResult;
};

'session.new': {
params: {capabilities?: Record<any, unknown>}; // TODO: Update Types in chromium bidi
returnType: {sessionId: string};
};
'session.status': {
params: {context: string}; // TODO: Update Types in chromium bidi
returnType: Bidi.Session.StatusResult;
};
'session.subscribe': {
params: Bidi.Session.SubscribeParameters;
returnType: Bidi.Session.SubscribeResult;
};
'session.unsubscribe': {
params: Bidi.Session.SubscribeParameters;
returnType: Bidi.Session.UnsubscribeResult;
};
}

/**
Expand Down
27 changes: 19 additions & 8 deletions packages/puppeteer-core/src/common/bidi/JSHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,9 @@ export class JSHandle<T = unknown> extends BaseJSHandle<T> {
}

override async jsonValue(): Promise<T> {
if (!('handle' in this.#remoteValue)) {
return BidiSerializer.deserialize(this.#remoteValue);
}
const value = await this.evaluate(object => {
return object;
});
if (value === undefined) {
const value = BidiSerializer.deserialize(this.#remoteValue);

if (this.#remoteValue.type !== 'undefined' && value === undefined) {
throw new Error('Could not serialize referenced object');
}
return value;
Expand All @@ -130,8 +126,23 @@ export class JSHandle<T = unknown> extends BaseJSHandle<T> {
}
}

get isPrimitiveValue(): boolean {
switch (this.#remoteValue.type) {
case 'string':
case 'number':
case 'bigint':
case 'boolean':
case 'undefined':
case 'null':
return true;

default:
return false;
}
}

override toString(): string {
if (!('handle' in this.#remoteValue)) {
if (this.isPrimitiveValue) {
return 'JSHandle:' + BidiSerializer.deserialize(this.#remoteValue);
}

Expand Down
98 changes: 92 additions & 6 deletions packages/puppeteer-core/src/common/bidi/Page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,90 @@
* limitations under the License.
*/

import {Page as PageBase} from '../../api/Page.js';
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';

import {Page as PageBase, PageEmittedEvents} from '../../api/Page.js';
import {stringifyFunction} from '../../util/Function.js';
import {ConsoleMessage, ConsoleMessageLocation} from '../ConsoleMessage.js';
import type {EvaluateFunc, HandleFor} from '../types.js';
import {isString} from '../util.js';

import {Connection} from './Connection.js';
import {JSHandle} from './JSHandle.js';
import {BidiSerializer} from './Serializer.js';
import {Reference} from './types.js';

/**
* @internal
*/
export class Page extends PageBase {
#connection: Connection;
#subscribedEvents = [
'log.entryAdded',
] as Bidi.Session.SubscribeParameters['events'];
_contextId: string;

constructor(connection: Connection, contextId: string) {
super();
this.#connection = connection;
this._contextId = contextId;

// TODO: Investigate an implementation similar to CDPSession
this.connection.send('session.subscribe', {
events: this.#subscribedEvents,
contexts: [this._contextId],
});

this.connection.on('log.entryAdded', this.#onLogEntryAdded.bind(this));
}

#onLogEntryAdded(event: Bidi.Log.LogEntry): void {
if (isConsoleLogEntry(event)) {
const args = event.args.map(arg => {
return getBidiHandle(this, arg);
});

const text = args
.reduce((value, arg) => {
const parsedValue = arg.isPrimitiveValue
? BidiSerializer.deserialize(arg.bidiObject())
: arg.toString();
return `${value} ${parsedValue}`;
}, '')
.slice(1);

this.emit(
PageEmittedEvents.Console,
new ConsoleMessage(
event.method as any,
text,
args,
getStackTraceLocations(event.stackTrace)
)
);
} else if (isJavaScriptLogEntry(event)) {
this.emit(
PageEmittedEvents.Console,
new ConsoleMessage(
event.level as any,
event.text ?? '',
[],
getStackTraceLocations(event.stackTrace)
)
);
}
}

override async close(): Promise<void> {
await this.#connection.send('browsingContext.close', {
context: this._contextId,
});

this.connection.send('session.unsubscribe', {
events: this.#subscribedEvents,
contexts: [this._contextId],
});

this.connection.off('log.entryAdded', this.#onLogEntryAdded.bind(this));
}

get connection(): Connection {
Expand Down Expand Up @@ -122,20 +179,49 @@ export class Page extends PageBase {

return returnByValue
? BidiSerializer.deserialize(result.result)
: getBidiHandle(this, result.result as Reference);
: getBidiHandle(this, result.result);
}
}

/**
* @internal
*/
export function getBidiHandle(context: Page, result: Reference): JSHandle {
// TODO: | ElementHandle<Node>
export function getBidiHandle(
context: Page,
result: Bidi.CommonDataTypes.RemoteValue
): JSHandle {
if (
(result.type === 'node' || result.type === 'window') &&
context._contextId
) {
throw new Error('ElementHandle not implemented');
// TODO: Implement ElementHandle
return new JSHandle(context, result);
}
return new JSHandle(context, result);
}

function isConsoleLogEntry(
event: Bidi.Log.LogEntry
): event is Bidi.Log.ConsoleLogEntry {
return event.type === 'console';
}

function isJavaScriptLogEntry(
event: Bidi.Log.LogEntry
): event is Bidi.Log.JavascriptLogEntry {
return event.type === 'javascript';
}

function getStackTraceLocations(stackTrace?: Bidi.Script.StackTrace) {
const stackTraceLocations: ConsoleMessageLocation[] = [];
if (stackTrace) {
for (const callFrame of stackTrace.callFrames) {
stackTraceLocations.push({
url: callFrame.url,
lineNumber: callFrame.lineNumber,
columnNumber: callFrame.columnNumber,
});
}
}
return stackTraceLocations;
}
6 changes: 0 additions & 6 deletions packages/puppeteer-core/src/common/bidi/types.ts

This file was deleted.

28 changes: 14 additions & 14 deletions test/TestExpectations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1068,7 +1068,7 @@
"expectations": ["FAIL"]
},
{
"testIdPattern": "[page.spec] Page Page.Events.Console should work for different console API calls",
"testIdPattern": "[page.spec] Page Page.Events.Console should work for different console API calls with logging functions",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox"],
"expectations": ["FAIL"]
Expand Down Expand Up @@ -1763,24 +1763,12 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[jshandle.spec] JSHandle Page.evaluateHandle should work",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[jshandle.spec] JSHandle Page.evaluateHandle should return the RemoteObject",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[jshandle.spec] JSHandle Page.evaluateHandle should use the same JS wrappers",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[jshandle.spec] JSHandle JSHandle.jsonValue should not work with dates",
"platforms": ["darwin", "linux", "win32"],
Expand Down Expand Up @@ -1808,7 +1796,7 @@
{
"testIdPattern": "[jshandle.spec] JSHandle JSHandle.toString should work with different subtypes",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{
Expand All @@ -1822,5 +1810,17 @@
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["SKIP", "FAIL"]
},
{
"testIdPattern": "[page.spec] Page Page.Events.Console should work",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[page.spec] Page Page.Events.Console should work for different console API calls with timing functions",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"]
}
]
30 changes: 23 additions & 7 deletions test/src/page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ describe('Page', function () {
expect(await message.args()[1]!.jsonValue()).toEqual(5);
expect(await message.args()[2]!.jsonValue()).toEqual({foo: 'bar'});
});
it('should work for different console API calls', async () => {
it('should work for different console API calls with logging functions', async () => {
const {page} = getTestState();

const messages: any[] = [];
Expand All @@ -677,9 +677,6 @@ describe('Page', function () {
});
// All console events will be reported before `page.evaluate` is finished.
await page.evaluate(() => {
// A pair of time/timeEnd generates only one Console API call.
console.time('calling console.time');
console.timeEnd('calling console.time');
console.trace('calling console.trace');
console.dir('calling console.dir');
console.warn('calling console.warn');
Expand All @@ -690,10 +687,9 @@ describe('Page', function () {
messages.map(msg => {
return msg.type();
})
).toEqual(['timeEnd', 'trace', 'dir', 'warning', 'error', 'log']);
expect(messages[0]!.text()).toContain('calling console.time');
).toEqual(['trace', 'dir', 'warning', 'error', 'log']);
expect(
messages.slice(1).map(msg => {
messages.map(msg => {
return msg.text();
})
).toEqual([
Expand All @@ -704,6 +700,26 @@ describe('Page', function () {
'JSHandle@promise',
]);
});
it('should work for different console API calls with timing functions', async () => {
const {page} = getTestState();

const messages: any[] = [];
page.on('console', msg => {
return messages.push(msg);
});
// All console events will be reported before `page.evaluate` is finished.
await page.evaluate(() => {
// A pair of time/timeEnd generates only one Console API call.
console.time('calling console.time');
console.timeEnd('calling console.time');
});
expect(
messages.map(msg => {
return msg.type();
})
).toEqual(['timeEnd']);
expect(messages[0]!.text()).toContain('calling console.time');
});
it('should not fail for window object', async () => {
const {page} = getTestState();

Expand Down

0 comments on commit 9b54365

Please sign in to comment.