Skip to content

Commit

Permalink
feat(fetch): expose .timing()
Browse files Browse the repository at this point in the history
  • Loading branch information
Skn0tt committed Sep 17, 2024
1 parent 9bb91cc commit a7ea946
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 16 deletions.
4 changes: 4 additions & 0 deletions packages/playwright-core/src/client/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,10 @@ export class APIResponse implements api.APIResponse {
return this._headers.headersArray();
}

timing() {
return this._initializer.timing;
}

async body(): Promise<Buffer> {
try {
const result = await this._request._channel.fetchResponseBody({ fetchUid: this._fetchUid() });
Expand Down
16 changes: 2 additions & 14 deletions packages/playwright-core/src/client/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
_failureText: string | null = null;
private _provisionalHeaders: RawHeaders;
private _actualHeadersPromise: Promise<RawHeaders> | undefined;
_timing: ResourceTiming;
_timing: channels.ResourceTiming;
private _fallbackOverrides: SerializedFallbackOverrides = {};

static from(request: channels.RequestChannel): Request {
Expand Down Expand Up @@ -242,7 +242,7 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
};
}

timing(): ResourceTiming {
timing(): channels.ResourceTiming {
return this._timing;
}

Expand Down Expand Up @@ -453,18 +453,6 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro

export type RouteHandlerCallback = (route: Route, request: Request) => Promise<any> | void;

export type ResourceTiming = {
startTime: number;
domainLookupStart: number;
domainLookupEnd: number;
connectStart: number;
secureConnectionStart: number;
connectEnd: number;
requestStart: number;
responseStart: number;
responseEnd: number;
};

export type RequestSizes = {
requestBodySize: number;
requestHeadersSize: number;
Expand Down
2 changes: 2 additions & 0 deletions packages/playwright-core/src/protocol/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ scheme.APIResponse = tObject({
status: tNumber,
statusText: tString,
headers: tArray(tType('NameValue')),
timing: tType('ResourceTiming'),
});
scheme.LifecycleEvent = tEnum(['load', 'domcontentloaded', 'networkidle', 'commit']);
scheme.LocalUtilsInitializer = tObject({
Expand Down Expand Up @@ -2121,6 +2122,7 @@ scheme.ResourceTiming = tObject({
connectEnd: tNumber,
requestStart: tNumber,
responseStart: tNumber,
responseEnd: tNumber,
});
scheme.ResponseInitializer = tObject({
request: tChannel(['Request']),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ export class APIRequestContextDispatcher extends Dispatcher<APIRequestContext, c
status: fetchResponse.status,
statusText: fetchResponse.statusText,
headers: fetchResponse.headers,
fetchUid: fetchResponse.fetchUid
fetchUid: fetchResponse.fetchUid,
timing: fetchResponse.timing,
}
};
}
Expand Down
17 changes: 16 additions & 1 deletion packages/playwright-core/src/server/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,14 +435,29 @@ export abstract class APIRequestContext extends SdkObject {

const chunks: Buffer[] = [];
const notifyBodyFinished = () => {
const endAt = monotonicTime();
// spec: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming
const timing: channels.ResourceTiming = {
startTime: startAt,
domainLookupStart: dnsLookupAt ? 0 : -1,
domainLookupEnd: dnsLookupAt ? dnsLookupAt! - startAt : -1,
connectStart: dnsLookupAt ? dnsLookupAt! - startAt : 0,
secureConnectionStart: dnsLookupAt ? dnsLookupAt! - startAt : 0,
connectEnd: (tlsHandshakeAt ?? tcpConnectionAt!) - startAt,
requestStart: (tlsHandshakeAt ?? tcpConnectionAt!) - startAt,
responseStart: responseAt - startAt,
responseEnd: endAt - startAt,
};

const body = Buffer.concat(chunks);
notifyRequestFinished(body);
fulfill({
url: response.url || url.toString(),
status: response.statusCode || 0,
statusText: response.statusMessage || '',
headers: toHeadersArray(response.rawHeaders),
body
body,
timing,
});
};

Expand Down
2 changes: 2 additions & 0 deletions packages/protocol/src/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ export type APIResponse = {
status: number,
statusText: string,
headers: NameValue[],
timing: ResourceTiming,
};

export type LifecycleEvent = 'load' | 'domcontentloaded' | 'networkidle' | 'commit';
Expand Down Expand Up @@ -3778,6 +3779,7 @@ export type ResourceTiming = {
connectEnd: number,
requestStart: number,
responseStart: number,
responseEnd: number,
};

// ----------- Response -----------
Expand Down
2 changes: 2 additions & 0 deletions packages/protocol/src/protocol.yml
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ APIResponse:
headers:
type: array
items: NameValue
timing: ResourceTiming


LifecycleEvent:
Expand Down Expand Up @@ -2945,6 +2946,7 @@ ResourceTiming:
connectEnd: number
requestStart: number
responseStart: number
responseEnd: number

Response:
type: interface
Expand Down
11 changes: 11 additions & 0 deletions tests/library/browsercontext-fetch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ it('fetch should work', async ({ context, server }) => {
expect(response.ok()).toBeTruthy();
expect(response.headers()['content-type']).toBe('application/json; charset=utf-8');
expect(response.headersArray()).toContainEqual({ name: 'Content-Type', value: 'application/json; charset=utf-8' });
expect(response.timing()).toEqual({
connectEnd: expect.any(Number),
connectStart: expect.any(Number),
domainLookupEnd: expect.any(Number),
domainLookupStart: expect.any(Number),
requestStart: expect.any(Number),
responseStart: expect.any(Number),
responseEnd: expect.any(Number),
secureConnectionStart: expect.any(Number),
startTime: expect.any(Number),
});
expect(await response.text()).toBe('{"foo": "bar"}\n');
});

Expand Down

0 comments on commit a7ea946

Please sign in to comment.