From f88e1da6385bc72e9ffde8514c28e4a0ff9e396a Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Thu, 25 Jan 2024 15:57:45 +0100 Subject: [PATCH] feat: add experimental browser.debugInfo (#11748) --- docs/api/index.md | 1 + docs/api/puppeteer.browser.md | 7 ++-- docs/api/puppeteer.debuginfo.md | 17 +++++++++ packages/puppeteer-core/src/api/Browser.ts | 20 +++++++++++ packages/puppeteer-core/src/bidi/Browser.ts | 7 ++++ .../puppeteer-core/src/bidi/Connection.ts | 4 +++ packages/puppeteer-core/src/cdp/Browser.ts | 7 ++++ packages/puppeteer-core/src/cdp/CDPSession.ts | 7 ++++ packages/puppeteer-core/src/cdp/Connection.ts | 12 +++++++ .../src/common/CallbackRegistry.ts | 13 +++++++ test/TestExpectations.json | 8 ++++- test/src/debugInfo.spec.ts | 36 +++++++++++++++++++ 12 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 docs/api/puppeteer.debuginfo.md create mode 100644 test/src/debugInfo.spec.ts diff --git a/docs/api/index.md b/docs/api/index.md index d680e73f456..7b44f36e489 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -91,6 +91,7 @@ sidebar_label: API | [Credentials](./puppeteer.credentials.md) | | | [CSSCoverageOptions](./puppeteer.csscoverageoptions.md) | Set of configurable options for CSS coverage. | | [CustomQueryHandler](./puppeteer.customqueryhandler.md) | | +| [DebugInfo](./puppeteer.debuginfo.md) | | | [Device](./puppeteer.device.md) | | | [ElementScreenshotOptions](./puppeteer.elementscreenshotoptions.md) | | | [FrameAddScriptTagOptions](./puppeteer.frameaddscripttagoptions.md) | | diff --git a/docs/api/puppeteer.browser.md b/docs/api/puppeteer.browser.md index f7191c54892..68c24dce2d5 100644 --- a/docs/api/puppeteer.browser.md +++ b/docs/api/puppeteer.browser.md @@ -56,9 +56,10 @@ await browser2.close(); ## Properties -| Property | Modifiers | Type | Description | -| --------- | --------------------- | ------- | ------------------------------------------------------------------------- | -| connected | readonly | boolean | Whether Puppeteer is connected to this [browser](./puppeteer.browser.md). | +| Property | Modifiers | Type | Description | +| --------- | --------------------- | ------------------------------------- | ------------------------------------------------------------------------- | +| connected | readonly | boolean | Whether Puppeteer is connected to this [browser](./puppeteer.browser.md). | +| debugInfo | readonly | [DebugInfo](./puppeteer.debuginfo.md) | Get debug information from Puppeteer. | ## Methods diff --git a/docs/api/puppeteer.debuginfo.md b/docs/api/puppeteer.debuginfo.md new file mode 100644 index 00000000000..f20ed755451 --- /dev/null +++ b/docs/api/puppeteer.debuginfo.md @@ -0,0 +1,17 @@ +--- +sidebar_label: DebugInfo +--- + +# DebugInfo interface + +#### Signature: + +```typescript +export interface DebugInfo +``` + +## Properties + +| Property | Modifiers | Type | Description | Default | +| --------------------- | --------- | --------- | ----------- | ------- | +| pendingProtocolErrors | | Error\[\] | | | diff --git a/packages/puppeteer-core/src/api/Browser.ts b/packages/puppeteer-core/src/api/Browser.ts index cd7b25f5409..e3b465c80e2 100644 --- a/packages/puppeteer-core/src/api/Browser.ts +++ b/packages/puppeteer-core/src/api/Browser.ts @@ -186,6 +186,14 @@ export interface BrowserEvents extends Record { [BrowserEvent.TargetDiscovered]: Protocol.Target.TargetInfo; } +/** + * @public + * @experimental + */ +export interface DebugInfo { + pendingProtocolErrors: Error[]; +} + /** * {@link Browser} represents a browser instance that is either: * @@ -431,4 +439,16 @@ export abstract class Browser extends EventEmitter { * @internal */ abstract get protocol(): ProtocolType; + + /** + * Get debug information from Puppeteer. + * + * @remarks + * + * Currently, includes pending protocol calls. In the future, we might add more info. + * + * @public + * @experimental + */ + abstract get debugInfo(): DebugInfo; } diff --git a/packages/puppeteer-core/src/bidi/Browser.ts b/packages/puppeteer-core/src/bidi/Browser.ts index 51d0a057d60..42979790c92 100644 --- a/packages/puppeteer-core/src/bidi/Browser.ts +++ b/packages/puppeteer-core/src/bidi/Browser.ts @@ -13,6 +13,7 @@ import { BrowserEvent, type BrowserCloseCallback, type BrowserContextOptions, + type DebugInfo, } from '../api/Browser.js'; import {BrowserContextEvent} from '../api/BrowserContext.js'; import type {Page} from '../api/Page.js'; @@ -307,4 +308,10 @@ export class BidiBrowser extends Browser { this.connection.dispose(); } } + + override get debugInfo(): DebugInfo { + return { + pendingProtocolErrors: this.connection.getPendingProtocolErrors(), + }; + } } diff --git a/packages/puppeteer-core/src/bidi/Connection.ts b/packages/puppeteer-core/src/bidi/Connection.ts index 6cbd4723e74..bce952ba39f 100644 --- a/packages/puppeteer-core/src/bidi/Connection.ts +++ b/packages/puppeteer-core/src/bidi/Connection.ts @@ -234,6 +234,10 @@ export class BidiConnection this.unbind(); this.#transport.close(); } + + getPendingProtocolErrors(): Error[] { + return this.#callbacks.getPendingProtocolErrors(); + } } /** diff --git a/packages/puppeteer-core/src/cdp/Browser.ts b/packages/puppeteer-core/src/cdp/Browser.ts index ea7e8b82915..7698acd1642 100644 --- a/packages/puppeteer-core/src/cdp/Browser.ts +++ b/packages/puppeteer-core/src/cdp/Browser.ts @@ -8,6 +8,7 @@ import type {ChildProcess} from 'child_process'; import type {Protocol} from 'devtools-protocol'; +import type {DebugInfo} from '../api/Browser.js'; import { Browser as BrowserBase, BrowserEvent, @@ -417,6 +418,12 @@ export class CdpBrowser extends BrowserBase { #getVersion(): Promise { return this.#connection.send('Browser.getVersion'); } + + override get debugInfo(): DebugInfo { + return { + pendingProtocolErrors: this.#connection.getPendingProtocolErrors(), + }; + } } /** diff --git a/packages/puppeteer-core/src/cdp/CDPSession.ts b/packages/puppeteer-core/src/cdp/CDPSession.ts index 324d092a552..fe5faa56473 100644 --- a/packages/puppeteer-core/src/cdp/CDPSession.ts +++ b/packages/puppeteer-core/src/cdp/CDPSession.ts @@ -157,4 +157,11 @@ export class CdpCDPSession extends CDPSession { override id(): string { return this.#sessionId; } + + /** + * @internal + */ + getPendingProtocolErrors(): Error[] { + return this.#callbacks.getPendingProtocolErrors(); + } } diff --git a/packages/puppeteer-core/src/cdp/Connection.ts b/packages/puppeteer-core/src/cdp/Connection.ts index 0a11cf4ead9..3c565341b38 100644 --- a/packages/puppeteer-core/src/cdp/Connection.ts +++ b/packages/puppeteer-core/src/cdp/Connection.ts @@ -251,6 +251,18 @@ export class Connection extends EventEmitter { ): Promise { return await this._createSession(targetInfo, false); } + + /** + * @internal + */ + getPendingProtocolErrors(): Error[] { + const result: Error[] = []; + result.push(...this.#callbacks.getPendingProtocolErrors()); + for (const session of this.#sessions.values()) { + result.push(...session.getPendingProtocolErrors()); + } + return result; + } } /** diff --git a/packages/puppeteer-core/src/common/CallbackRegistry.ts b/packages/puppeteer-core/src/common/CallbackRegistry.ts index fc8b18a5cbf..ea9f3d5abb5 100644 --- a/packages/puppeteer-core/src/common/CallbackRegistry.ts +++ b/packages/puppeteer-core/src/common/CallbackRegistry.ts @@ -94,6 +94,19 @@ export class CallbackRegistry { } this.#callbacks.clear(); } + + /** + * @internal + */ + getPendingProtocolErrors(): Error[] { + const result: Error[] = []; + for (const callback of this.#callbacks.values()) { + result.push( + new Error(`${callback.label} timed out. Trace: ${callback.error.stack}`) + ); + } + return result; + } } /** * @internal diff --git a/test/TestExpectations.json b/test/TestExpectations.json index c53ec37f747..18b5b5ec988 100644 --- a/test/TestExpectations.json +++ b/test/TestExpectations.json @@ -41,6 +41,12 @@ "parameters": ["webDriverBiDi"], "expectations": ["PASS"] }, + { + "testIdPattern": "[debugInfo.spec] *", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[device-request-prompt.spec] *", "platforms": ["darwin", "linux", "win32"], @@ -3366,7 +3372,7 @@ "testIdPattern": "[target.spec] Target Browser.waitForTarget should wait for a target", "platforms": ["darwin", "linux", "win32"], "parameters": ["chrome", "webDriverBiDi"], - "expectations": ["PASS"] + "expectations": ["FAIL", "PASS"] }, { "testIdPattern": "[target.spec] Target Browser.waitForTarget should wait for a target", diff --git a/test/src/debugInfo.spec.ts b/test/src/debugInfo.spec.ts new file mode 100644 index 00000000000..079107cab7c --- /dev/null +++ b/test/src/debugInfo.spec.ts @@ -0,0 +1,36 @@ +/** + * @license + * Copyright 2024 Google Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import expect from 'expect'; + +import {getTestState, setupTestBrowserHooks} from './mocha-utils.js'; + +describe('DebugInfo', function () { + setupTestBrowserHooks(); + + describe('Browser.debugInfo', function () { + it('should work', async () => { + const {page, browser} = await getTestState(); + + const promise = page.evaluate(() => { + return new Promise(resolve => { + // @ts-expect-error another context + window.resolve = resolve; + }); + }); + try { + expect(browser.debugInfo.pendingProtocolErrors).toHaveLength(1); + } finally { + await page.evaluate(() => { + // @ts-expect-error another context + window.resolve(); + }); + } + await promise; + expect(browser.debugInfo.pendingProtocolErrors).toHaveLength(0); + }); + }); +});