feat(page): expose heap snapshot getter (#14610)

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: OrKoN <[email protected]>
This commit is contained in:
Alex Rudenko
2026-01-28 07:06:58 +00:00
committed by GitHub
co-authored by google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> OrKoN
parent 9aa2690c51
commit 057608f94f
9 changed files with 235 additions and 1 deletions
+9
View File
@@ -1017,6 +1017,15 @@ Device in a request prompt.
</td></tr>
<tr><td>
<span id="heapsnapshotoptions">[HeapSnapshotOptions](./puppeteer.heapsnapshotoptions.md)</span>
</td><td>
Options for [Page.captureHeapSnapshot()](./puppeteer.page.captureheapsnapshot.md).
</td></tr>
<tr><td>
<span id="interceptresolutionstate">[InterceptResolutionState](./puppeteer.interceptresolutionstate.md)</span>
</td><td>
+55
View File
@@ -0,0 +1,55 @@
---
sidebar_label: HeapSnapshotOptions
---
# HeapSnapshotOptions interface
Options for [Page.captureHeapSnapshot()](./puppeteer.page.captureheapsnapshot.md).
### Signature
```typescript
export interface HeapSnapshotOptions
```
## Properties
<table><thead><tr><th>
Property
</th><th>
Modifiers
</th><th>
Type
</th><th>
Description
</th><th>
Default
</th></tr></thead>
<tbody><tr><td>
<span id="path">path</span>
</td><td>
</td><td>
string
</td><td>
The file path to save the heap snapshot to.
</td><td>
</td></tr>
</tbody></table>
@@ -0,0 +1,47 @@
---
sidebar_label: Page.captureHeapSnapshot
---
# Page.captureHeapSnapshot() method
Captures a snapshot of the JavaScript heap and writes it to a file.
### Signature
```typescript
class Page {
abstract captureHeapSnapshot(options: HeapSnapshotOptions): Promise<void>;
}
```
## Parameters
<table><thead><tr><th>
Parameter
</th><th>
Type
</th><th>
Description
</th></tr></thead>
<tbody><tr><td>
options
</td><td>
[HeapSnapshotOptions](./puppeteer.heapsnapshotoptions.md)
</td><td>
</td></tr>
</tbody></table>
**Returns:**
Promise&lt;void&gt;
+11
View File
@@ -409,6 +409,17 @@ Get the browser context that the page belongs to.
</td></tr>
<tr><td>
<span id="captureheapsnapshot">[captureHeapSnapshot(options)](./puppeteer.page.captureheapsnapshot.md)</span>
</td><td>
</td><td>
Captures a snapshot of the JavaScript heap and writes it to a file.
</td></tr>
<tr><td>
<span id="click">[click(selector, options)](./puppeteer.page.click.md)</span>
</td><td>
+17
View File
@@ -647,6 +647,18 @@ export interface ReloadOptions extends WaitForOptions {
ignoreCache?: boolean;
}
/**
* Options for {@link Page.captureHeapSnapshot}.
*
* @public
*/
export interface HeapSnapshotOptions {
/**
* The file path to save the heap snapshot to.
*/
path: string;
}
/**
* Page provides methods to interact with a single tab or
* {@link https://developer.chrome.com/extensions/background_pages | extension background page}
@@ -1721,6 +1733,11 @@ export abstract class Page extends EventEmitter<PageEvents> {
*/
abstract metrics(): Promise<Metrics>;
/**
* Captures a snapshot of the JavaScript heap and writes it to a file.
*/
abstract captureHeapSnapshot(options: HeapSnapshotOptions): Promise<void>;
/**
* The page's URL.
*
+7
View File
@@ -18,6 +18,7 @@ import type {HTTPResponse} from '../api/HTTPResponse.js';
import type {
Credentials,
GeolocationOptions,
HeapSnapshotOptions,
MediaFeature,
PageEvents,
ReloadOptions,
@@ -952,6 +953,12 @@ export class BidiPage extends Page {
throw new UnsupportedOperation();
}
override async captureHeapSnapshot(
_options: HeapSnapshotOptions,
): Promise<void> {
throw new UnsupportedOperation();
}
override async goBack(
options: WaitForOptions = {},
): Promise<HTTPResponse | null> {
+40 -1
View File
@@ -16,7 +16,11 @@ import type {ElementHandle} from '../api/ElementHandle.js';
import type {Frame, WaitForOptions} from '../api/Frame.js';
import type {HTTPResponse} from '../api/HTTPResponse.js';
import type {JSHandle} from '../api/JSHandle.js';
import type {Credentials, ReloadOptions} from '../api/Page.js';
import type {
Credentials,
HeapSnapshotOptions,
ReloadOptions,
} from '../api/Page.js';
import {
Page,
PageEvent,
@@ -54,6 +58,7 @@ import {
validateDialogType,
} from '../common/util.js';
import type {Viewport} from '../common/Viewport.js';
import {environment} from '../environment.js';
import {assert} from '../util/assert.js';
import {Deferred} from '../util/Deferred.js';
import {AsyncDisposableStack} from '../util/disposable.js';
@@ -812,6 +817,40 @@ export class CdpPage extends Page {
return this.#buildMetricsObject(response.metrics);
}
override async captureHeapSnapshot(
options: HeapSnapshotOptions,
): Promise<void> {
const {createWriteStream} = environment.value.fs;
const stream = createWriteStream(options.path);
const streamPromise = new Promise<void>((resolve, reject) => {
stream.on('error', reject);
stream.on('finish', resolve);
});
const client = this.#primaryTargetClient;
await client.send('HeapProfiler.enable');
await client.send('HeapProfiler.collectGarbage');
const handler = (
event: Protocol.HeapProfiler.AddHeapSnapshotChunkEvent,
) => {
stream.write(event.chunk);
};
client.on('HeapProfiler.addHeapSnapshotChunk', handler);
try {
await client.send('HeapProfiler.takeHeapSnapshot', {
reportProgress: false,
});
} finally {
client.off('HeapProfiler.addHeapSnapshotChunk', handler);
await client.send('HeapProfiler.disable');
}
stream.end();
await streamPromise;
}
#emitMetrics(event: Protocol.Performance.MetricsEvent): void {
this.emit(PageEvent.Metrics, {
title: event.title,
+7
View File
@@ -97,6 +97,13 @@
"expectations": ["SKIP"],
"comment": "Spawns headful browser, needs display or `xvfb` like which is not required for other headless tests"
},
{
"testIdPattern": "[heapSnapshot.spec] *",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "Not supported in WebDriver BiDi"
},
{
"testIdPattern": "[interventionHeaders.spec] *",
"platforms": ["darwin", "linux", "win32"],
+42
View File
@@ -0,0 +1,42 @@
/**
* @license
* Copyright 2026 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import fs from 'node:fs';
import {mkdtemp, rm} from 'node:fs/promises';
import {tmpdir} from 'node:os';
import path from 'node:path';
import expect from 'expect';
import {getTestState, setupTestBrowserHooks} from '../mocha-utils.js';
describe('Heap Snapshot', function () {
setupTestBrowserHooks();
let tempDir: string;
beforeEach(async () => {
tempDir = await mkdtemp(path.join(tmpdir(), 'heap-snapshot-'));
});
afterEach(async () => {
await rm(tempDir, {recursive: true, force: true});
});
it('should capture heap snapshot', async () => {
const {page} = await getTestState();
const filePath = path.join(tempDir, 'heap.heapsnapshot');
await page.captureHeapSnapshot({path: filePath});
expect(fs.existsSync(filePath)).toBe(true);
const content = fs.readFileSync(filePath, 'utf8');
const snapshot = JSON.parse(content);
expect(snapshot.snapshot).toBeDefined();
expect(snapshot.nodes).toBeDefined();
expect(snapshot.edges).toBeDefined();
});
});