feat: add --maxNavigationSaved flag to configure retained navigation history (#3018)

Network requests and console messages were only retained for the last 3
navigations per page. Flows that bounce through many pages, such as
sign-in flows, OAuth handshakes, SSO, or corporate networks with proxies
and captive portals, can easily exceed that, so earlier hops were
dropped before an agent could inspect them. The new
`--maxNavigationSaved` flag (default: 3) lets users raise the limit so
agents can trace the full redirect chain end to end.

For earlier navigations, request URLs, status codes, and request and
response headers (including Location and Set-Cookie) are retained, but
response bodies are generally no longer available. This is sufficient
for network analysis of redirect hops.

Each retained navigation can hold up to 1,000 network requests and
10,000 console messages, so the value is capped at 25 to avoid running
out of memory. Values outside 1-25 are rejected at startup, whether
passed on the command line or in a config file.

The value is passed from the server args through `McpContext` and
`McpPage` to the network and console collectors. The
`includePreservedRequests` and `includePreservedMessages` tool
descriptions now show the configured value instead of a hardcoded 3.
This commit is contained in:
BumaldaOverTheWater94 authored and GitHub committed 2026-10-21 06:17:36 +00:00
1 parent bf8c6d9bc7
commit a580d1320e
16 files changed
+383 -31

No files matched your search

+5
View File
@@ -219,6 +219,11 @@ The Chrome DevTools MCP server supports the following configuration option:
- **Type:** boolean
- **Default:** `true`
- **`--maxNavigationSaved`/ `--max-navigation-saved`**
Number of navigations (including the current one) for which network requests and console messages are retained per page. Must be between 1 and 25. For earlier navigations, request URLs, status codes, and request and response headers are retained, which is sufficient to trace redirect hops, but response bodies are generally no longer available.
- **Type:** number
- **Default:** `3`
- **`--slim`**
Exposes a "slim" set of 3 tools covering navigation, script execution and screenshots only. Useful for basic browser tasks.
- **Type:** boolean
+1
View File
@@ -410,6 +410,7 @@ async function generateToolDocumentation(): Promise<void> {
const tools = createTools({
pageIdRouting: true,
categoryExtensions: true,
maxNavigationSaved: mcpOptions.maxNavigationSaved.default,
} as ParsedArguments);
{
+20 -14
View File
@@ -329,20 +329,26 @@ export class McpPage implements ContextPage {
}
this.#pptrPage = page;
page.on('dialog', this.#dialogHandler);
this.#networkCollector = new NetworkCollector(page);
this.#consoleCollector = new ConsoleCollector(page, collect => {
return {
console: event => {
collect(event);
},
uncaughtError: event => {
collect(event);
},
devtoolsAggregatedIssue: event => {
collect(event);
},
};
this.#networkCollector = new NetworkCollector(page, {
maxNavigationSaved: this.#config.resolved.maxNavigationSaved,
});
this.#consoleCollector = new ConsoleCollector(
page,
collect => {
return {
console: event => {
collect(event);
},
uncaughtError: event => {
collect(event);
},
devtoolsAggregatedIssue: event => {
collect(event);
},
};
},
{maxNavigationSaved: this.#config.resolved.maxNavigationSaved},
);
}
this.updateTimeouts();
await this.#initDevToolsUniverseNoThrow();
@@ -1436,7 +1442,7 @@ export class McpPage implements ContextPage {
this.networkCollector.dispose();
this.networkCollector = new NetworkCollector(
this.pptrPage,
undefined,
{maxNavigationSaved: this.#config.resolved.maxNavigationSaved},
collect => {
return {
request: req => {
+23 -6
View File
@@ -24,6 +24,8 @@ import {
} from '../utils/id.js';
import {logger} from '../utils/logger.js';
import {DEFAULT_MAX_NAVIGATION_SAVED} from './constants.js';
export class UncaughtError {
readonly details: Protocol.Runtime.ExceptionDetails;
readonly targetId: string;
@@ -57,12 +59,17 @@ export type ListenerMap<EventMap extends PageEvents = PageEvents> = {
]?: (event: EventMap[K]) => void;
};
export interface PageCollectorOptions {
maxResourcesPerNavigation?: number;
maxNavigationSaved?: number;
}
export class PageCollector<T> {
protected pptrPage: Page;
#listeners?: ListenerMap<PageEvents>;
#pendingSameDocumentNavigation = false;
#frameNavigatedWithinDocumentTarget?: Frame;
protected maxNavigationSaved = 3;
protected readonly maxNavigationSaved: number;
/**
* This maps a Page to a list of navigations with a sub-list
@@ -74,9 +81,13 @@ export class PageCollector<T> {
constructor(
page: Page,
listeners: (collector: (item: T) => void) => ListenerMap<PageEvents>,
maxResourcesPerNavigation?: number,
{
maxResourcesPerNavigation,
maxNavigationSaved = DEFAULT_MAX_NAVIGATION_SAVED,
}: PageCollectorOptions = {},
) {
this.pptrPage = page;
this.maxNavigationSaved = maxNavigationSaved;
this.#attachFrameNavigatedWithinDocumentListener();
const idGenerator = createIdGenerator();
@@ -219,9 +230,12 @@ export class ConsoleCollector extends PageCollector<
item: ConsoleMessage | Error | DevTools.AggregatedIssue | UncaughtError,
) => void,
) => ListenerMap<PageEvents>,
maxMessagesPerNavigation = ConsoleCollector.MAX_MESSAGES_PER_NAVIGATION,
{
maxResourcesPerNavigation = ConsoleCollector.MAX_MESSAGES_PER_NAVIGATION,
maxNavigationSaved,
}: PageCollectorOptions = {},
) {
super(page, listeners, maxMessagesPerNavigation);
super(page, listeners, {maxResourcesPerNavigation, maxNavigationSaved});
this.#subscriber = new PageEventSubscriber(this.pptrPage);
this.#subscriber.subscribe();
}
@@ -392,7 +406,10 @@ export class NetworkCollector extends PageCollector<HTTPRequest> {
constructor(
page: Page,
maxRequestsPerNavigation = NetworkCollector.MAX_REQUESTS_PER_NAVIGATION,
{
maxResourcesPerNavigation = NetworkCollector.MAX_REQUESTS_PER_NAVIGATION,
maxNavigationSaved,
}: PageCollectorOptions = {},
listeners: (
collector: (item: HTTPRequest) => void,
) => ListenerMap<PageEvents> = collect => {
@@ -403,7 +420,7 @@ export class NetworkCollector extends PageCollector<HTTPRequest> {
} as ListenerMap;
},
) {
super(page, listeners, maxRequestsPerNavigation);
super(page, listeners, {maxResourcesPerNavigation, maxNavigationSaved});
}
override splitAfterNavigation(): void {
const requests = this.storage[0];
+7
View File
@@ -0,0 +1,7 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
export const DEFAULT_MAX_NAVIGATION_SAVED = 3;
+24
View File
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {DEFAULT_MAX_NAVIGATION_SAVED} from '../collectors/constants.js';
import type {YargsOptions} from '../third_party/index.js';
import os from 'node:os';
import path from 'node:path';
@@ -22,6 +23,9 @@ export interface McpOption extends YargsOptions {
}
export const DEFAULT_FILESYSTEM_ROOT = [os.tmpdir()];
// Each retained navigation can hold up to 1,000 network requests and 10,000
// console messages, so the history is capped to avoid running out of memory.
export const MAX_NAVIGATION_SAVED_LIMIT = 25;
export const mcpOptions = {
...getCategoryOptions(),
@@ -170,6 +174,26 @@ export const mcpOptions = {
'Whether to enable source maps in DevTools. Use --no-source-maps to disable.',
reloadable: true,
},
maxNavigationSaved: {
type: 'number',
default: DEFAULT_MAX_NAVIGATION_SAVED,
describe: `Number of navigations (including the current one) for which network requests and console messages are retained per page. Must be between 1 and ${MAX_NAVIGATION_SAVED_LIMIT}. For earlier navigations, request URLs, status codes, and request and response headers are retained, which is sufficient to trace redirect hops, but response bodies are generally no longer available.`,
coerce: (value: number | undefined) => {
if (value === undefined) {
return;
}
if (
!Number.isInteger(value) ||
value <= 0 ||
value > MAX_NAVIGATION_SAVED_LIMIT
) {
throw new Error(
`Invalid maxNavigationSaved ${value}. Expected an integer between 1 and ${MAX_NAVIGATION_SAVED_LIMIT}.`,
);
}
return value;
},
},
clearcutEndpoint: {
type: 'string',
hidden: true,
+4
View File
@@ -478,5 +478,9 @@
{
"name": "watch_config",
"flagType": "boolean"
},
{
"name": "max_navigation_saved_present",
"flagType": "boolean"
}
]
+2 -2
View File
@@ -44,7 +44,7 @@ export const listConsoleMessages = definePageTool(
(cliArgs: ParsedArguments) => {
return {
name: LIST_CONSOLE_MESSAGES_TOOL_NAME,
description: `List all console messages for the target page since the last navigation.${cliArgs?.categoryExtensions ? ' This includes console messages originating from extensions content scripts.' : ''}`,
description: `List all console messages for the target page since the last navigation.${cliArgs.categoryExtensions ? ' This includes console messages originating from extensions content scripts.' : ''}`,
annotations: {
category: ToolCategory.DEBUGGING,
readOnlyHint: true,
@@ -77,7 +77,7 @@ export const listConsoleMessages = definePageTool(
.default(false)
.optional()
.describe(
'Set to true to return the preserved messages over the last 3 navigations.',
`Set to true to return the preserved messages over the last ${cliArgs.maxNavigationSaved} navigations.`,
),
includeStackTraces: zod
.boolean()
+3 -2
View File
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import type {ParsedArguments} from '../config/ConfigParser.js';
import {zod} from '../third_party/index.js';
import type {ResourceType} from '../third_party/index.js';
@@ -32,7 +33,7 @@ const FILTERABLE_RESOURCE_TYPES: readonly [ResourceType, ...ResourceType[]] = [
'other',
];
export const listNetworkRequests = definePageTool(() => ({
export const listNetworkRequests = definePageTool((args: ParsedArguments) => ({
name: 'list_network_requests',
description: `Lists the most recent requests for the target page since the last navigation.`,
annotations: {
@@ -67,7 +68,7 @@ export const listNetworkRequests = definePageTool(() => ({
.default(false)
.optional()
.describe(
'Set to true to return the preserved requests over the last 3 navigations.',
`Set to true to return the preserved requests over the last ${args.maxNavigationSaved} navigations.`,
),
},
blockedByDialog: false,
+48
View File
@@ -9,6 +9,7 @@ import {describe, it} from 'node:test';
import sinon from 'sinon';
import {UncaughtError} from '../src/collectors/PageCollector.js';
import type {ReadonlyServerConfig} from '../src/config/ServerConfig.js';
import type {TargetUniverse} from '../src/devtools/DevtoolsUtils.js';
import {McpPage, replaceHtmlElementsWithUids} from '../src/McpPage.js';
@@ -314,6 +315,53 @@ describe('McpPage', () => {
}
describe('lazy initialization', () => {
for (const replaceNetworkCollector of [false, true]) {
it(`forwards the navigation limit to collectors (replacement: ${replaceNetworkCollector})`, async () => {
const pptrPage = createMockPuppeteerPage();
const target = createMockPuppeteerTarget({page: pptrPage});
const mcpPage = new McpPage(target, 1, {
config: createMockServerConfig({maxNavigationSaved: 5}),
locatorClass: Locator,
});
await mcpPage.init();
if (replaceNetworkCollector) {
await mcpPage.setUpNetworkCollectorForTesting();
}
const requests = [];
const errors = [];
for (let navigation = 0; navigation < 7; navigation++) {
const request = getMockRequest({
url: `http://example.com/nav${navigation}`,
navigationRequest: true,
frame: pptrPage.mainFrame(),
});
requests.push(request);
pptrPage.emit('request', request);
pptrPage.emit('framenavigated', pptrPage.mainFrame());
const error = new UncaughtError(
{
exceptionId: navigation,
text: `error-${navigation}`,
lineNumber: 0,
columnNumber: 0,
},
'<mock>',
);
errors.push(error);
pptrPage.emit('uncaughtError', error);
}
assert.deepStrictEqual(
mcpPage.networkCollector.getData(true),
requests.slice(-5),
);
assert.deepStrictEqual(
mcpPage.consoleCollector.getData(true),
errors.slice(-5),
);
mcpPage.dispose();
});
}
it('does not initialize Puppeteer Page in constructor and throws if pptrPage is accessed before init()', async () => {
const pptrPage = createMockPuppeteerPage();
pptrPage.emulateFocusedPage.resolves();
+30
View File
@@ -97,6 +97,24 @@ describe('McpServer', () => {
return tools.map(tool => tool.name);
}
it('passes the navigation limit to the lazily connected context', async () => {
const context = createMockMcpContext();
const createContext = sinon.stub(McpContext, 'create').returns(context);
const {server, browserManager, config} = await createTestServer(
['--maxNavigationSaved=5'],
{mockContext: false},
);
sinon.assert.calledOnceWithExactly(
createContext,
sinon.match.any,
sinon.match({config: sinon.match.same(config)}),
);
assert.strictEqual(config.resolved.maxNavigationSaved, 5);
sinon.assert.notCalled(browserManager.ensureBrowser);
await server.close();
});
describe('callTool', () => {
it('returns an error for unknown tool names', async () => {
const {server, browserManager} = await createTestServer();
@@ -351,6 +369,18 @@ describe('McpServer', () => {
});
describe('applyConfig', () => {
it('keeps maxNavigationSaved unchanged when configuration is reloaded', async () => {
const {server, config, browserManager} = await createTestServer([
'--maxNavigationSaved=5',
]);
await server.applyConfig(parseConfig(['--maxNavigationSaved=10']));
assert.strictEqual(config.resolved.maxNavigationSaved, 5);
sinon.assert.notCalled(browserManager.ensureBrowser);
await server.close();
});
it('enables tools that were disabled', async () => {
const {server} = await createTestServer();
assert.ok(
+49
View File
@@ -10,12 +10,14 @@ import {describe, it} from 'node:test';
import sinon from 'sinon';
import {DEFAULT_MAX_NAVIGATION_SAVED} from '../src/collectors/constants.js';
import {ConfigLocator} from '../src/config/ConfigLocator.js';
import {ConfigParser} from '../src/config/ConfigParser.js';
import {
getCliOptions,
mcpOptions,
DEFAULT_FILESYSTEM_ROOT,
MAX_NAVIGATION_SAVED_LIMIT,
} from '../src/config/mcp-options.js';
import {
buildCommand,
@@ -70,6 +72,7 @@ describe('cli args parsing', () => {
experimentalStructuredContent: false,
pageIdRouting: true,
sourceMaps: true,
maxNavigationSaved: 3,
clearcutIncludePidHeader: false,
screenshotFormat: 'png',
slim: false,
@@ -701,6 +704,52 @@ describe('cli args parsing', () => {
assert.strictEqual(explicitTrueArgs.sourceMaps, true);
});
it('parses maxNavigationSaved flag', async () => {
const defaultParsed = parseConfig(['main.js']);
assert.strictEqual(defaultParsed.maxNavigationSaved, 3);
assert.strictEqual(
defaultParsed.maxNavigationSaved,
DEFAULT_MAX_NAVIGATION_SAVED,
);
const camelCaseArgs = parseConfig(['--maxNavigationSaved', '5']);
assert.strictEqual(camelCaseArgs.maxNavigationSaved, 5);
const kebabCaseArgs = parseConfig(['--max-navigation-saved=1']);
assert.strictEqual(kebabCaseArgs.maxNavigationSaved, 1);
assert.throws(
() => parseConfig(['--maxNavigationSaved', '26']),
/Invalid maxNavigationSaved 26/,
);
});
it('rejects invalid maxNavigationSaved values', async () => {
const coerce = mcpOptions.maxNavigationSaved.coerce;
assert.strictEqual(coerce(undefined), undefined);
assert.strictEqual(coerce(1), 1);
assert.strictEqual(
coerce(MAX_NAVIGATION_SAVED_LIMIT),
MAX_NAVIGATION_SAVED_LIMIT,
);
for (const value of [
0,
-1,
2.5,
Number.NaN,
MAX_NAVIGATION_SAVED_LIMIT + 1,
Number.MAX_SAFE_INTEGER,
Number.POSITIVE_INFINITY,
]) {
assert.throws(
() => coerce(value),
/Invalid maxNavigationSaved .* Expected an integer between 1 and 25\./,
);
}
});
it('parses config option', async () => {
using testConfig = createTempFile(
JSON.stringify({
+125 -2
View File
@@ -23,6 +23,7 @@ import {
} from '../../src/collectors/PageCollector.js';
import {DevTools, FrameEvent} from '../../src/third_party/index.js';
import {createMockPuppeteerPage} from '../mocks.js';
import {getMockRequest, getMockBrowser} from '../utils.js';
function createMockConsoleMessage(text: string): ConsoleMessage {
@@ -34,6 +35,35 @@ function createMockConsoleMessage(text: string): ConsoleMessage {
}
describe('PageCollector', () => {
it('applies resource and navigation limits independently', () => {
const pptrPage = createMockPuppeteerPage();
const collector = new PageCollector<Error>(
pptrPage,
collect => ({
pageerror: error => {
if (error instanceof Error) {
collect(error);
}
},
}),
{maxResourcesPerNavigation: 2, maxNavigationSaved: 4},
);
const errors: Error[] = [];
for (let navigation = 0; navigation < 6; navigation++) {
pptrPage.emit('framenavigated', pptrPage.mainFrame());
for (let resource = 0; resource < 3; resource++) {
const error = new Error(`${navigation}-${resource}`);
pptrPage.emit('pageerror', error);
if (resource > 0) {
errors.push(error);
}
}
}
assert.deepStrictEqual(collector.getData(), errors.slice(-2));
assert.deepStrictEqual(collector.getData(true), errors.slice(-8));
collector.dispose();
});
it('works', async () => {
const browser = getMockBrowser();
const page = (await browser.pages())[0];
@@ -189,6 +219,40 @@ describe('PageCollector', () => {
});
describe('NetworkCollector', () => {
it('applies both limits while retaining navigation requests', () => {
const pptrPage = createMockPuppeteerPage();
const collector = new NetworkCollector(pptrPage, {
maxResourcesPerNavigation: 2,
maxNavigationSaved: 4,
});
const requests: HTTPRequest[] = [];
for (let navigation = 0; navigation < 6; navigation++) {
const request = getMockRequest({
navigationRequest: true,
frame: pptrPage.mainFrame(),
});
pptrPage.emit('request', request);
pptrPage.emit('framenavigated', pptrPage.mainFrame());
for (let resource = 0; resource < 3; resource++) {
const request = getMockRequest();
pptrPage.emit('request', request);
if (resource > 0) {
requests.push(request);
}
}
}
assert.deepStrictEqual(collector.getData(), requests.slice(-2));
// The next navigation request arrives before framenavigated, evicting
// the oldest resource in each completed navigation.
assert.deepStrictEqual(collector.getData(true), [
requests[5],
requests[7],
requests[9],
...requests.slice(-2),
]);
collector.dispose();
});
it('retains only the newest requests without navigation', async () => {
const browser = getMockBrowser();
const page = (await browser.pages())[0];
@@ -337,6 +401,25 @@ describe('NetworkCollector', () => {
assert.equal(collector.getData(true).length, 3);
});
it('respects a custom maxNavigationSaved', async () => {
const browser = getMockBrowser();
const page = (await browser.pages())[0];
const mainFrame = page.mainFrame();
const collector = new NetworkCollector(page, {maxNavigationSaved: 5});
for (let i = 0; i < 7; i++) {
const req = getMockRequest({
url: `http://example.com/nav${i}`,
navigationRequest: true,
frame: mainFrame,
});
page.emit('request', req);
page.emit('framenavigated', mainFrame);
}
assert.equal(collector.getData(true).length, 5);
});
it('bounds retained navigation buckets with redirects and subframes', async () => {
class TestNetworkCollector extends NetworkCollector {
get navigationSizes(): number[] {
@@ -347,7 +430,9 @@ describe('NetworkCollector', () => {
const browser = getMockBrowser();
const page = (await browser.pages())[0];
const mainFrame = page.mainFrame();
const collector = new TestNetworkCollector(page, 3);
const collector = new TestNetworkCollector(page, {
maxResourcesPerNavigation: 3,
});
const firstNavigation = getMockRequest({
url: 'http://example.com/first',
@@ -431,6 +516,44 @@ describe('ConsoleCollector', () => {
};
});
for (const maxResourcesPerNavigation of [undefined, 2]) {
it(`retains a custom navigation history with resource limit ${maxResourcesPerNavigation}`, () => {
const pptrPage = createMockPuppeteerPage();
const collector = new ConsoleCollector(
pptrPage,
collect => ({
pageerror: error => {
if (error instanceof Error) {
collect(error);
}
},
}),
{maxResourcesPerNavigation, maxNavigationSaved: 4},
);
const errors: Error[] = [];
for (let navigation = 0; navigation < 6; navigation++) {
pptrPage.emit('framenavigated', pptrPage.mainFrame());
for (let resource = 0; resource < 3; resource++) {
const error = new Error(`${navigation}-${resource}`);
pptrPage.emit('pageerror', error);
if (maxResourcesPerNavigation === undefined || resource > 0) {
errors.push(error);
}
}
}
const retainedPerNavigation = maxResourcesPerNavigation ?? 3;
assert.deepStrictEqual(
collector.getData(),
errors.slice(-retainedPerNavigation),
);
assert.deepStrictEqual(
collector.getData(true),
errors.slice(-4 * retainedPerNavigation),
);
collector.dispose();
});
}
it('collects issues', async () => {
const browser = getMockBrowser();
const page = (await browser.pages())[0];
@@ -554,7 +677,7 @@ describe('ConsoleCollector', () => {
},
} as ListenerMap;
},
cap,
{maxResourcesPerNavigation: cap},
);
const messages = Array.from({length: cap + 3}, (_, i) =>
+3 -3
View File
@@ -268,9 +268,8 @@ export function createMockCDPSession(): sinon.SinonStubbedInstance<MockCDPSessio
}
export function createMockPuppeteerPage(): MockPuppeteerPage {
const page = sinon.createStubInstance(
CdpPage,
) as unknown as sinon.SinonStubbedInstance<Page>;
const pptrPage = sinon.createStubInstance(CdpPage);
const page = pptrPage as unknown as sinon.SinonStubbedInstance<Page>;
const pageListener = mockListener();
page.listenerCount.callsFake(pageListener.listenerCount);
page.on.callsFake((eventName, handler) => {
@@ -292,6 +291,7 @@ export function createMockPuppeteerPage(): MockPuppeteerPage {
// PageCollector can subscribe to FrameNavigatedWithinDocument and tests
// can trigger it.
const mainFrameStub = sinon.createStubInstance(CdpFrame);
mainFrameStub.page.returns(pptrPage);
const mainFrameListener = mockListener();
mainFrameStub.on.callsFake((eventName, handler) => {
mainFrameListener.on(eventName, handler);
+19 -1
View File
@@ -21,7 +21,7 @@ import {
listConsoleMessages,
} from '../../src/tools/console.js';
import {installExtension} from '../../src/tools/extensions.js';
import {createMockServerConfig} from '../mocks.js';
import {createMockParsedArguments, createMockServerConfig} from '../mocks.js';
import {serverHooks} from '../server.js';
import {
getTextContent,
@@ -143,6 +143,24 @@ describe('console', () => {
});
describe('list_console_messages', () => {
it('describes the default preserved navigation history', () => {
const tool = listConsoleMessages(createMockParsedArguments());
assert.strictEqual(
tool.schema.includePreservedMessages.description,
'Set to true to return the preserved messages over the last 3 navigations.',
);
});
it('describes preserved messages using maxNavigationSaved', () => {
const tool = listConsoleMessages(
createMockParsedArguments({maxNavigationSaved: 5}),
);
assert.strictEqual(
tool.schema.includePreservedMessages.description,
'Set to true to return the preserved messages over the last 5 navigations.',
);
});
it('list messages', async () => {
await withMcpContext(async (response, context, args) => {
await listConsoleMessages(args).handler(
+20 -1
View File
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import {describe, it} from 'node:test';
import sinon from 'sinon';
@@ -12,10 +13,28 @@ import {
getNetworkRequest,
listNetworkRequests,
} from '../../src/tools/network.js';
import {createHandlerMocks} from '../mocks.js';
import {createHandlerMocks, createMockParsedArguments} from '../mocks.js';
describe('network', () => {
describe('list_network_requests', () => {
it('describes the default preserved navigation history', () => {
const tool = listNetworkRequests(createMockParsedArguments());
assert.strictEqual(
tool.schema.includePreservedRequests.description,
'Set to true to return the preserved requests over the last 3 navigations.',
);
});
it('describes preserved requests using maxNavigationSaved', () => {
const tool = listNetworkRequests(
createMockParsedArguments({maxNavigationSaved: 5}),
);
assert.strictEqual(
tool.schema.includePreservedRequests.description,
'Set to true to return the preserved requests over the last 5 navigations.',
);
});
it('handles default parameters', async () => {
const {page, context, response, args} = createHandlerMocks();
page.getDevToolsData.resolves({});