Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions docs/src/api/class-apirequestcontext.md
Original file line number Diff line number Diff line change
Expand Up @@ -918,3 +918,5 @@ Set to `true` to include IndexedDB in the storage state snapshot.
## property: APIRequestContext.tracing
* since: v1.60
- type: <[Tracing]>

Tracing recorder for requests made through this API request context.
2 changes: 1 addition & 1 deletion docs/src/api/class-tracing.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ To specify the final trace zip file name, you need to pass `path` option to

Start recording a HAR (HTTP Archive) of network activity in this context. The HAR file is written to disk when [`method: Tracing.stopHar`] is called, or when the returned [Disposable] is disposed.

Only one HAR recording can be active at a time per [BrowserContext].
Only one HAR recording can be active at a time per [Tracing] instance.

**Usage**

Expand Down
4 changes: 2 additions & 2 deletions packages/isomorphic/trace/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type ContextEntry = {
platform?: string;
playwrightVersion?: string;
wallTime: number;
monotonicTime: number;
sdkLanguage?: Language;
testIdAttributeName?: string;
title?: string;
Expand All @@ -40,15 +41,14 @@ export type ContextEntry = {
stdio: trace.StdioTraceEvent[];
errors: trace.ErrorTraceEvent[];
hasSource: boolean;
contextId: string;
testTimeout?: number;
annotations?: trace.TraceEventAnnotation[];
};

export type PageEntry = {
pageId: string,
screencastFrames: {
sha1: string,
file: string,
timestamp: number,
frameSwapWallTime?: number,
width: number,
Expand Down
6 changes: 3 additions & 3 deletions packages/isomorphic/trace/snapshotRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class SnapshotRenderer {
const closestFrame = (wallTime && this._screencastFrames[0]?.frameSwapWallTime)
? findClosest(this._screencastFrames, frame => frame.frameSwapWallTime!, wallTime)
: findClosest(this._screencastFrames, frame => frame.timestamp, timestamp);
return closestFrame?.sha1;
return closestFrame?.file;
}

render(): RenderedFrameSnapshot {
Expand Down Expand Up @@ -234,14 +234,14 @@ export class SnapshotRenderer {
if (index >= 0 && index < this._snapshots.length)
override = this._snapshots[index].resourceOverrides.find(o => o.url === url);
}
if (override?.sha1) {
if (override?.file) {
result = {
...result,
response: {
...result.response,
content: {
...result.response.content,
_sha1: override.sha1,
_file: override.file,
}
},
};
Expand Down
14 changes: 7 additions & 7 deletions packages/isomorphic/trace/snapshotServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import type { ResourceSnapshot } from '@trace/snapshot';

export class SnapshotServer {
private _snapshotStorage: SnapshotStorage;
private _resourceLoader: (sha1: string) => Promise<Blob | undefined>;
private _resourceLoader: (file: string) => Promise<Blob | undefined>;
private _snapshotIds = new Map<string, SnapshotRenderer>();

constructor(snapshotStorage: SnapshotStorage, resourceLoader: (sha1: string) => Promise<Blob | undefined>) {
constructor(snapshotStorage: SnapshotStorage, resourceLoader: (file: string) => Promise<Blob | undefined>) {
this._snapshotStorage = snapshotStorage;
this._resourceLoader = resourceLoader;
}
Expand All @@ -41,10 +41,10 @@ export class SnapshotServer {

async serveClosestScreenshot(pageOrFrameId: string, searchParams: URLSearchParams): Promise<Response> {
const snapshot = this._snapshot(pageOrFrameId, searchParams);
const sha1 = snapshot?.closestScreenshot();
if (!sha1)
const file = snapshot?.closestScreenshot();
if (!file)
return new Response(null, { status: 404 });
return new Response(await this._resourceLoader(sha1));
return new Response(await this._resourceLoader(file));
}

serveSnapshotInfo(pageOrFrameId: string, searchParams: URLSearchParams): Response {
Expand Down Expand Up @@ -85,8 +85,8 @@ export class SnapshotServer {
if (!resource)
return new Response(null, { status: 404 });

const sha1 = resource.response.content._sha1;
const content = sha1 ? await this._resourceLoader(sha1) || new Blob([]) : new Blob([]);
const file = resource.response.content._file;
const content = file ? await this._resourceLoader(file) || new Blob([]) : new Blob([]);

let contentType = resource.response.content.mimeType;
const isTextEncoding = /^text\/|^application\/(javascript|json)/.test(contentType);
Expand Down
23 changes: 6 additions & 17 deletions packages/isomorphic/trace/snapshotStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ export class SnapshotStorage {
renderers: SnapshotRenderer[],
}>();
private _cache = new LRUCache<SnapshotRenderer, string>(100_000_000); // 100MB per each trace
private _contextToResources = new Map<string, ResourceSnapshot[]>();
private _resources: ResourceSnapshot[] = [];
private _resourceUrlsWithOverrides = new Set<string>();

addResource(contextId: string, resource: ResourceSnapshot): void {
addResource(resource: ResourceSnapshot): void {
resource.request.url = rewriteURLForCustomProtocol(resource.request.url);
this._ensureResourcesForContext(contextId).push(resource);
this._resources.push(resource);
}

addFrameSnapshot(contextId: string, snapshot: FrameSnapshot, screencastFrames: PageEntry['screencastFrames']) {
addFrameSnapshot(snapshot: FrameSnapshot, screencastFrames: PageEntry['screencastFrames']) {
for (const override of snapshot.resourceOverrides)
override.url = rewriteURLForCustomProtocol(override.url);
let frameSnapshots = this._frameSnapshots.get(snapshot.frameId);
Expand All @@ -49,8 +49,7 @@ export class SnapshotStorage {
this._frameSnapshots.set(snapshot.pageId, frameSnapshots);
}
frameSnapshots.raw.push(snapshot);
const resources = this._ensureResourcesForContext(contextId);
const renderer = new SnapshotRenderer(this._cache, resources, frameSnapshots.raw, screencastFrames, frameSnapshots.raw.length - 1);
const renderer = new SnapshotRenderer(this._cache, this._resources, frameSnapshots.raw, screencastFrames, frameSnapshots.raw.length - 1);
frameSnapshots.renderers.push(renderer);
return renderer;
}
Expand All @@ -66,8 +65,7 @@ export class SnapshotStorage {

finalize() {
// Resources are not necessarily sorted in the trace file, so sort them now.
for (const resources of this._contextToResources.values())
resources.sort((a, b) => (a._monotonicTime || 0) - (b._monotonicTime || 0));
this._resources.sort((a, b) => (a._monotonicTime || 0) - (b._monotonicTime || 0));
// Resources that have overrides should not be cached, otherwise we might get stale content
// while serving snapshots with different override values.
for (const frameSnapshots of this._frameSnapshots.values()) {
Expand All @@ -81,13 +79,4 @@ export class SnapshotStorage {
hasResourceOverride(url: string) {
return this._resourceUrlsWithOverrides.has(url);
}

private _ensureResourcesForContext(contextId: string): ResourceSnapshot[] {
let resources = this._contextToResources.get(contextId);
if (!resources) {
resources = [];
this._contextToResources.set(contextId, resources);
}
return resources;
}
}
18 changes: 9 additions & 9 deletions packages/isomorphic/trace/traceLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class TraceLoader {
const match = entryName.match(/(.+)\.trace$/);
if (match && (!prefix || prefix === match[1]))
prefixes.push(match[1] || '');
if (entryName.includes('src@'))
if (entryName.startsWith('src/') || entryName.includes('src@'))
hasSource = true;
}
if (!prefixes.length)
Expand Down Expand Up @@ -97,10 +97,10 @@ export class TraceLoader {
unzipProgress?.(++done, total);

for (const resource of contextEntry.resources) {
if (resource.request.postData?._sha1)
this._resourceToContentType.set(resource.request.postData._sha1, stripEncodingFromContentType(resource.request.postData.mimeType));
if (resource.response.content?._sha1)
this._resourceToContentType.set(resource.response.content._sha1, stripEncodingFromContentType(resource.response.content.mimeType));
if (resource.request.postData?._file)
this._resourceToContentType.set(resource.request.postData._file, stripEncodingFromContentType(resource.request.postData.mimeType));
if (resource.response.content?._file)
this._resourceToContentType.set(resource.response.content._file, stripEncodingFromContentType(resource.response.content.mimeType));
}

this.contextEntries.push(contextEntry);
Expand All @@ -113,9 +113,9 @@ export class TraceLoader {
return this._backend.hasEntry(filename);
}

async resourceForSha1(sha1: string): Promise<Blob | undefined> {
const blob = await this._backend.readBlob('resources/' + sha1);
const contentType = this._resourceToContentType.get(sha1);
async resourceEntry(file: string): Promise<Blob | undefined> {
const blob = await this._backend.readBlob(file);
const contentType = this._resourceToContentType.get(file);
// "x-unknown" in the har means "no content type".
if (!blob || contentType === undefined || contentType === 'x-unknown')
return blob;
Expand All @@ -139,6 +139,7 @@ function createEmptyContext(): ContextEntry {
origin: 'testRunner',
startTime: Number.MAX_SAFE_INTEGER,
wallTime: Number.MAX_SAFE_INTEGER,
monotonicTime: 0,
endTime: 0,
browserName: '',
options: {
Expand All @@ -153,6 +154,5 @@ function createEmptyContext(): ContextEntry {
errors: [],
stdio: [],
hasSource: false,
contextId: '',
};
}
Loading
Loading