Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/McpContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,4 +426,8 @@ export class McpContext implements Context {
);
return waitForHelper.waitForEventsAfterAction(action);
}

getNetworkRequestStableId(request: HTTPRequest): number {
return this.#networkCollector.getIdForResource(request);
}
}
9 changes: 7 additions & 2 deletions src/McpResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,12 @@ Call ${handleDialog.name} to handle it before continuing.`);
);
response.push(...data.info);
for (const request of data.items) {
response.push(getShortDescriptionForRequest(request));
response.push(
getShortDescriptionForRequest(
request,
context.getNetworkRequestStableId(request),
),
);
}
} else {
response.push('No requests found.');
Expand Down Expand Up @@ -347,7 +352,7 @@ Call ${handleDialog.name} to handle it before continuing.`);
let indent = 0;
for (const request of redirectChain.reverse()) {
response.push(
`${' '.repeat(indent)}${getShortDescriptionForRequest(request)}`,
`${' '.repeat(indent)}${getShortDescriptionForRequest(request, context.getNetworkRequestStableId(request))}`,
);
indent++;
}
Expand Down
31 changes: 27 additions & 4 deletions src/PageCollector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@ export type ListenerMap<EventMap extends PageEvents = PageEvents> = {
[K in keyof EventMap]?: (event: EventMap[K]) => void;
};

function createIdGenerator() {
let i = 1;
return () => {
if (i === Number.MAX_SAFE_INTEGER) {
i = 0;
}
return i++;
};
}

export const stableIdSymbol = Symbol('stableIdSymbol');
type WithSymbolId<T> = T & {
[stableIdSymbol]?: number;
};

export class PageCollector<T> {
#browser: Browser;
#listenersInitializer: (
Expand All @@ -28,7 +43,8 @@ export class PageCollector<T> {
* As we use the reference to it.
* Use methods that manipulate the array in place.
*/
protected storage = new WeakMap<Page, T[]>();
protected storage = new WeakMap<Page, Array<WithSymbolId<T>>>();
protected idGenerator = new WeakMap<Page, () => number>();

constructor(
browser: Browser,
Expand Down Expand Up @@ -56,7 +72,6 @@ export class PageCollector<T> {
if (!page) {
return;
}
console.log('destro');
this.#cleanupPageDestroyed(page);
});
}
Expand All @@ -70,10 +85,14 @@ export class PageCollector<T> {
return;
}

const stored: T[] = [];
const idGenerator = createIdGenerator();
const stored: Array<WithSymbolId<T>> = [];
this.storage.set(page, stored);

const listeners = this.#listenersInitializer(value => {
stored.push(value);
const withId = value as WithSymbolId<T>;
withId[stableIdSymbol] = idGenerator();
stored.push(withId);
});
listeners['framenavigated'] = (frame: Frame) => {
// Only reset the storage on main frame navigation
Expand Down Expand Up @@ -111,6 +130,10 @@ export class PageCollector<T> {
getData(page: Page): T[] {
return this.storage.get(page) ?? [];
}

getIdForResource(resource: WithSymbolId<T>): number {
return resource[stableIdSymbol] ?? -1;
}
}

export class NetworkCollector extends PageCollector<HTTPRequest> {
Expand Down
8 changes: 6 additions & 2 deletions src/formatters/networkFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ import type {HTTPRequest, HTTPResponse} from 'puppeteer-core';

const BODY_CONTEXT_SIZE_LIMIT = 10000;

export function getShortDescriptionForRequest(request: HTTPRequest): string {
return `${request.url()} ${request.method()} ${getStatusFromRequest(request)}`;
export function getShortDescriptionForRequest(
request: HTTPRequest,
id: number,
): string {
// TODO truncate the URL
return `uid ${id} - ${request.url()} ${request.method()} ${getStatusFromRequest(request)}`;
}

export function getStatusFromRequest(request: HTTPRequest): string {
Expand Down
37 changes: 19 additions & 18 deletions tests/McpResponse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,16 @@ Call handle_dialog to handle it before continuing.`,
await withBrowser(async (response, context) => {
response.setIncludeNetworkRequests(true);
context.getNetworkRequests = () => {
return [getMockRequest()];
return [getMockRequest({stableId: 1}), getMockRequest({stableId: 2})];
};
const result = await response.handle('test', context);
assert.strictEqual(
result[0].text,
`# test response
## Network requests
Showing 1-1 of 1 (Page 1 of 1).
http://example.com GET [pending]`,
Showing 1-2 of 2 (Page 1 of 1).
uid 1 - http://example.com GET [pending]
uid 2 - http://example.com GET [pending]`,
);
});
});
Expand Down Expand Up @@ -266,7 +267,7 @@ ${JSON.stringify({request: 'body'})}
${JSON.stringify({response: 'body'})}
## Network requests
Showing 1-1 of 1 (Page 1 of 1).
http://example.com POST [success - 200]`,
uid 1 - http://example.com POST [success - 200]`,
);
});
});
Expand All @@ -289,7 +290,7 @@ Status: [pending]
- content-size:10
## Network requests
Showing 1-1 of 1 (Page 1 of 1).
http://example.com GET [pending]`,
uid 1 - http://example.com GET [pending]`,
);
});
});
Expand Down Expand Up @@ -354,8 +355,8 @@ describe('McpResponse network request filtering', () => {
`# test response
## Network requests
Showing 1-2 of 2 (Page 1 of 1).
http://example.com GET [pending]
http://example.com GET [pending]`,
uid 1 - http://example.com GET [pending]
uid 1 - http://example.com GET [pending]`,
);
});
});
Expand All @@ -378,7 +379,7 @@ http://example.com GET [pending]`,
`# test response
## Network requests
Showing 1-1 of 1 (Page 1 of 1).
http://example.com GET [pending]`,
uid 1 - http://example.com GET [pending]`,
);
});
});
Expand Down Expand Up @@ -423,11 +424,11 @@ No requests found.`,
`# test response
## Network requests
Showing 1-5 of 5 (Page 1 of 1).
http://example.com GET [pending]
http://example.com GET [pending]
http://example.com GET [pending]
http://example.com GET [pending]
http://example.com GET [pending]`,
uid 1 - http://example.com GET [pending]
uid 1 - http://example.com GET [pending]
uid 1 - http://example.com GET [pending]
uid 1 - http://example.com GET [pending]
uid 1 - http://example.com GET [pending]`,
);
});
});
Expand All @@ -452,11 +453,11 @@ http://example.com GET [pending]`,
`# test response
## Network requests
Showing 1-5 of 5 (Page 1 of 1).
http://example.com GET [pending]
http://example.com GET [pending]
http://example.com GET [pending]
http://example.com GET [pending]
http://example.com GET [pending]`,
uid 1 - http://example.com GET [pending]
uid 1 - http://example.com GET [pending]
uid 1 - http://example.com GET [pending]
uid 1 - http://example.com GET [pending]
uid 1 - http://example.com GET [pending]`,
);
});
});
Expand Down
25 changes: 24 additions & 1 deletion tests/PageCollector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import assert from 'node:assert';
import {describe, it} from 'node:test';

import type {Browser, Frame, Page, Target} from 'puppeteer-core';
import type {Browser, Frame, HTTPRequest, Page, Target} from 'puppeteer-core';

import type {ListenerMap} from '../src/PageCollector.js';
import {PageCollector} from '../src/PageCollector.js';
Expand Down Expand Up @@ -196,4 +196,27 @@ describe('PageCollector', () => {

assert.equal(collector.getData(page).length, 0);
});

it('should assign ids to requests', async () => {
const browser = getMockBrowser();
const page = (await browser.pages())[0];
const request1 = getMockRequest();
const request2 = getMockRequest();
const collector = new PageCollector<HTTPRequest>(browser, collect => {
return {
request: req => {
collect(req);
},
} as ListenerMap;
});
await collector.init();

page.emit('request', request1);
page.emit('request', request2);

assert.equal(collector.getData(page).length, 2);

assert.equal(collector.getIdForResource(request1), 1);
assert.equal(collector.getIdForResource(request2), 2);
});
});
24 changes: 12 additions & 12 deletions tests/formatters/networkFormatter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,40 +21,40 @@ describe('networkFormatter', () => {
describe('getShortDescriptionForRequest', () => {
it('works', async () => {
const request = getMockRequest();
const result = getShortDescriptionForRequest(request);
const result = getShortDescriptionForRequest(request, 1);

assert.equal(result, 'http://example.com GET [pending]');
assert.equal(result, 'uid 1 - http://example.com GET [pending]');
});
it('shows correct method', async () => {
const request = getMockRequest({method: 'POST'});
const result = getShortDescriptionForRequest(request);
const result = getShortDescriptionForRequest(request, 1);

assert.equal(result, 'http://example.com POST [pending]');
assert.equal(result, 'uid 1 - http://example.com POST [pending]');
});
it('shows correct status for request with response code in 200', async () => {
const response = getMockResponse();
const request = getMockRequest({response});
const result = getShortDescriptionForRequest(request);
const result = getShortDescriptionForRequest(request, 1);

assert.equal(result, 'http://example.com GET [success - 200]');
assert.equal(result, 'uid 1 - http://example.com GET [success - 200]');
});
it('shows correct status for request with response code in 100', async () => {
const response = getMockResponse({
status: 199,
});
const request = getMockRequest({response});
const result = getShortDescriptionForRequest(request);
const result = getShortDescriptionForRequest(request, 1);

assert.equal(result, 'http://example.com GET [failed - 199]');
assert.equal(result, 'uid 1 - http://example.com GET [failed - 199]');
});
it('shows correct status for request with response code above 200', async () => {
const response = getMockResponse({
status: 300,
});
const request = getMockRequest({response});
const result = getShortDescriptionForRequest(request);
const result = getShortDescriptionForRequest(request, 1);

assert.equal(result, 'http://example.com GET [failed - 300]');
assert.equal(result, 'uid 1 - http://example.com GET [failed - 300]');
});
it('shows correct status for request that failed', async () => {
const request = getMockRequest({
Expand All @@ -64,11 +64,11 @@ describe('networkFormatter', () => {
};
},
});
const result = getShortDescriptionForRequest(request);
const result = getShortDescriptionForRequest(request, 1);

assert.equal(
result,
'http://example.com GET [failed - Error in Network]',
'uid 1 - http://example.com GET [failed - Error in Network]',
);
});
});
Expand Down
5 changes: 4 additions & 1 deletion tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {HTTPRequest, HTTPResponse} from 'puppeteer-core';

import {McpContext} from '../src/McpContext.js';
import {McpResponse} from '../src/McpResponse.js';
import {stableIdSymbol} from '../src/PageCollector.js';

let browser: Browser | undefined;

Expand Down Expand Up @@ -49,6 +50,7 @@ export function getMockRequest(
hasPostData?: boolean;
postData?: string;
fetchPostData?: Promise<string>;
stableId?: number;
} = {},
): HTTPRequest {
return {
Expand Down Expand Up @@ -84,7 +86,8 @@ export function getMockRequest(
redirectChain(): HTTPRequest[] {
return [];
},
} as HTTPRequest;
[stableIdSymbol]: options.stableId ?? 1,
} as unknown as HTTPRequest;
}

export function getMockResponse(
Expand Down