Skip to content

Commit 78977d8

Browse files
authored
feat: page.requests() (#37490)
1 parent 2f820cb commit 78977d8

File tree

16 files changed

+128
-6
lines changed

16 files changed

+128
-6
lines changed

docs/src/api/class-page.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3137,6 +3137,13 @@ return value resolves to `[]`.
31373137
* since: v1.9
31383138

31393139

3140+
## async method: Page.requests
3141+
* since: v1.56
3142+
- returns: <[Array]<[Request]>>
3143+
3144+
Returns up to 100 last network request from this page. See [`event: Page.request`] for more details.
3145+
3146+
31403147
## async method: Page.addLocatorHandler
31413148
* since: v1.42
31423149

packages/playwright-client/types/types.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3926,6 +3926,12 @@ export interface Page {
39263926
*/
39273927
requestGC(): Promise<void>;
39283928

3929+
/**
3930+
* Returns up to 100 last network request from this page. See
3931+
* [page.on('request')](https://playwright.dev/docs/api/class-page#page-event-request) for more details.
3932+
*/
3933+
requests(): Promise<Array<Request>>;
3934+
39293935
/**
39303936
* Routing provides the capability to modify network requests that are made by a page.
39313937
*

packages/playwright-core/src/client/page.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { Frame, verifyLoadState } from './frame';
2929
import { HarRouter } from './harRouter';
3030
import { Keyboard, Mouse, Touchscreen } from './input';
3131
import { JSHandle, assertMaxArguments, parseResult, serializeArgument } from './jsHandle';
32-
import { Response, Route, RouteHandler, WebSocket, WebSocketRoute, WebSocketRouteHandler, validateHeaders } from './network';
32+
import { Request, Response, Route, RouteHandler, WebSocket, WebSocketRoute, WebSocketRouteHandler, validateHeaders } from './network';
3333
import { Video } from './video';
3434
import { Waiter } from './waiter';
3535
import { Worker } from './worker';
@@ -48,7 +48,7 @@ import type { Clock } from './clock';
4848
import type { APIRequestContext } from './fetch';
4949
import type { WaitForNavigationOptions } from './frame';
5050
import type { FrameLocator, Locator, LocatorOptions } from './locator';
51-
import type { Request, RouteHandlerCallback, WebSocketRouteHandlerCallback } from './network';
51+
import type { RouteHandlerCallback, WebSocketRouteHandlerCallback } from './network';
5252
import type { FilePayload, Headers, LifecycleEvent, SelectOption, SelectOptionOptions, Size, TimeoutOptions, WaitForEventOptions, WaitForFunctionOptions } from './types';
5353
import type * as structs from '../../types/structs';
5454
import type * as api from '../../types/types';
@@ -804,6 +804,11 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
804804
return await this._mainFrame.waitForFunction(pageFunction, arg, options);
805805
}
806806

807+
async requests() {
808+
const { requests } = await this._channel.requests();
809+
return requests.map(request => Request.from(request));
810+
}
811+
807812
workers(): Worker[] {
808813
return [...this._workers];
809814
}

packages/playwright-core/src/protocol/validator.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,6 +1481,10 @@ scheme.PagePdfParams = tObject({
14811481
scheme.PagePdfResult = tObject({
14821482
pdf: tBinary,
14831483
});
1484+
scheme.PageRequestsParams = tOptional(tObject({}));
1485+
scheme.PageRequestsResult = tObject({
1486+
requests: tArray(tChannel(['Request'])),
1487+
});
14841488
scheme.PageSnapshotForAIParams = tObject({
14851489
timeout: tFloat,
14861490
});

packages/playwright-core/src/server/browserContext.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export abstract class BrowserContext extends SdkObject {
6262
RequestAborted: 'requestaborted',
6363
RequestFulfilled: 'requestfulfilled',
6464
RequestContinued: 'requestcontinued',
65+
RequestCollected: 'requestcollected',
6566
BeforeClose: 'beforeclose',
6667
VideoStarted: 'videostarted',
6768
RecorderEvent: 'recorderevent',

packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
154154
if (!redirectFromDispatcher && !this._shouldDispatchNetworkEvent(request, 'request') && !request.isNavigationRequest())
155155
return;
156156
const requestDispatcher = RequestDispatcher.from(this, request);
157+
requestDispatcher.reportedThroughEvent = true;
157158
this._dispatchEvent('request', {
158159
request: requestDispatcher,
159160
page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined())
@@ -190,6 +191,11 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
190191
page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined()),
191192
});
192193
});
194+
this.addObjectListener(BrowserContext.Events.RequestCollected, (request: Request) => {
195+
const requestDispatcher = this.connection.existingDispatcher<RequestDispatcher>(request);
196+
if (requestDispatcher && !requestDispatcher.reportedThroughEvent)
197+
requestDispatcher._dispose('gc');
198+
});
193199
this.addObjectListener(BrowserContext.Events.RecorderEvent, ({ event, data, page, code }: { event: 'actionAdded' | 'actionUpdated' | 'signalAdded', data: any, page: Page, code: string }) => {
194200
this._dispatchEvent('recorderEvent', { event, data, code, page: PageDispatcher.from(this, page) });
195201
});

packages/playwright-core/src/server/dispatchers/networkDispatchers.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import type { Progress } from '@protocol/progress';
3232
export class RequestDispatcher extends Dispatcher<Request, channels.RequestChannel, BrowserContextDispatcher | PageDispatcher | FrameDispatcher> implements channels.RequestChannel {
3333
_type_Request: boolean;
3434
private _browserContextDispatcher: BrowserContextDispatcher;
35+
reportedThroughEvent = false;
3536

3637
static from(scope: BrowserContextDispatcher, request: Request): RequestDispatcher {
3738
const result = scope.connection.existingDispatcher<RequestDispatcher>(request);
@@ -48,9 +49,9 @@ export class RequestDispatcher extends Dispatcher<Request, channels.RequestChann
4849
const frame = request.frame();
4950
const page = request.frame()?._page;
5051
const pageDispatcher = page ? scope.connection.existingDispatcher<PageDispatcher>(page) : null;
51-
const frameDispatcher = frame ? FrameDispatcher.from(scope, frame) : null;
52+
const frameDispatcher = FrameDispatcher.fromNullable(scope, frame);
5253
super(pageDispatcher || frameDispatcher || scope, request, 'Request', {
53-
frame: FrameDispatcher.fromNullable(scope, request.frame()),
54+
frame: frameDispatcher,
5455
serviceWorker: WorkerDispatcher.fromNullable(scope, request.serviceWorker()),
5556
url: request.url(),
5657
resourceType: request.resourceType(),

packages/playwright-core/src/server/dispatchers/pageDispatcher.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,10 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
339339
return { pdf: buffer };
340340
}
341341

342+
async requests(params: channels.PageRequestsParams, progress: Progress): Promise<channels.PageRequestsResult> {
343+
return { requests: this._page.networkRequests().map(request => RequestDispatcher.from(this.parentScope(), request)) };
344+
}
345+
342346
async snapshotForAI(params: channels.PageSnapshotForAIParams, progress: Progress): Promise<channels.PageSnapshotForAIResult> {
343347
return { snapshot: await this._page.snapshotForAI(progress) };
344348
}

packages/playwright-core/src/server/frames.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ export class FrameManager {
302302
route?.abort('aborted').catch(() => {});
303303
return;
304304
}
305+
this._page.addNetworkRequest(request);
305306
this._page.emitOnContext(BrowserContext.Events.Request, request);
306307
if (route)
307308
new network.Route(request, route).handle([...this._page.requestInterceptors, ...this._page.browserContext.requestInterceptors]);

packages/playwright-core/src/server/page.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ export class Page extends SdkObject {
166166
private _locatorHandlers = new Map<number, { selector: string, noWaitAfter?: boolean, resolved?: ManualPromise<void> }>();
167167
private _lastLocatorHandlerUid = 0;
168168
private _locatorHandlerRunningCounter = 0;
169+
private _networkRequests: network.Request[] = [];
169170

170171
// Aiming at 25 fps by default - each frame is 40ms, but we give some slack with 35ms.
171172
// When throttling for tracing, 200ms between frames, except for 10 frames around the action.
@@ -350,6 +351,16 @@ export class Page extends SdkObject {
350351
return this._extraHTTPHeaders;
351352
}
352353

354+
addNetworkRequest(request: network.Request) {
355+
this._networkRequests.push(request);
356+
for (const collected of ensureArrayLimit(this._networkRequests, 100))
357+
this.emitOnContext(BrowserContext.Events.RequestCollected, collected);
358+
}
359+
360+
networkRequests() {
361+
return this._networkRequests;
362+
}
363+
353364
async onBindingCalled(payload: string, context: dom.FrameExecutionContext) {
354365
if (this._closedState === 'closed')
355366
return;
@@ -1078,7 +1089,8 @@ async function snapshotFrameForAI(progress: Progress, frame: frames.Frame, frame
10781089
return result;
10791090
}
10801091

1081-
function ensureArrayLimit(array: any[], limit: number) {
1092+
function ensureArrayLimit<T>(array: T[], limit: number): T[] {
10821093
if (array.length > limit)
1083-
array.splice(0, limit / 10);
1094+
return array.splice(0, limit / 10);
1095+
return [];
10841096
}

0 commit comments

Comments
 (0)