feat(trace): allow multiple traces in a single app, gc traces (#9478)

This commit is contained in:
Pavel Feldman 2021-10-13 12:31:54 -08:00 committed by GitHub
parent fc54f1937a
commit cd99ad0da2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 24 deletions

View File

@ -218,7 +218,7 @@ program
options.browser = 'firefox';
if (options.browser === 'wk')
options.browser = 'webkit';
showTraceViewer(trace, options.browser).catch(logErrorAndExit);
showTraceViewer(trace, options.browser, false, 9322).catch(logErrorAndExit);
}).addHelpText('afterAll', `
Examples:

View File

@ -25,7 +25,7 @@ import { internalCallMetadata } from '../../instrumentation';
import { createPlaywright } from '../../playwright';
import { ProgressController } from '../../progress';
export async function showTraceViewer(traceUrl: string, browserName: string, headless = false): Promise<BrowserContext | undefined> {
export async function showTraceViewer(traceUrl: string, browserName: string, headless = false, port?: number): Promise<BrowserContext | undefined> {
const server = new HttpServer();
server.routePath('/file', (request, response) => {
try {
@ -42,7 +42,7 @@ export async function showTraceViewer(traceUrl: string, browserName: string, hea
return server.serveFile(response, absolutePath);
});
const urlPrefix = await server.start();
const urlPrefix = await server.start(port);
const traceViewerPlaywright = createPlaywright('javascript', true);
const traceViewerBrowser = isUnderTest() ? 'chromium' : browserName;

View File

@ -28,53 +28,86 @@ self.addEventListener('activate', function(event: any) {
event.waitUntil(self.clients.claim());
});
let traceModel: TraceModel | undefined;
let snapshotServer: SnapshotServer | undefined;
const scopePath = new URL(self.registration.scope).pathname;
async function loadTrace(trace: string): Promise<TraceModel> {
const loadedTraces = new Map<string, { traceModel: TraceModel, snapshotServer: SnapshotServer, clientId: string }>();
async function loadTrace(trace: string, clientId: string): Promise<TraceModel> {
const entry = loadedTraces.get(trace);
if (entry)
return entry.traceModel;
const traceModel = new TraceModel();
const url = trace.startsWith('http') || trace.startsWith('blob') ? trace : `/file?path=${trace}`;
await traceModel.load(url);
const snapshotServer = new SnapshotServer(traceModel.storage());
loadedTraces.set(trace, { traceModel, snapshotServer, clientId });
return traceModel;
}
// @ts-ignore
async function doFetch(event: FetchEvent): Promise<Response> {
const request = event.request;
const url = new URL(request.url);
const snapshotUrl = request.mode === 'navigate' ?
request.url : (await self.clients.get(event.clientId))!.url;
const traceUrl = new URL(snapshotUrl).searchParams.get('trace')!;
const { snapshotServer } = loadedTraces.get(traceUrl) || {};
if (request.url.startsWith(self.registration.scope)) {
const url = new URL(request.url);
const relativePath = url.pathname.substring(scopePath.length - 1);
if (relativePath === '/context') {
const trace = url.searchParams.get('trace')!;
traceModel = await loadTrace(trace);
snapshotServer = new SnapshotServer(traceModel.storage());
await gc();
const traceModel = await loadTrace(traceUrl, event.clientId);
return new Response(JSON.stringify(traceModel!.contextEntry), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
}
if (relativePath.startsWith('/snapshotSize/'))
return snapshotServer!.serveSnapshotSize(relativePath, url.searchParams);
if (relativePath.startsWith('/snapshot/'))
return snapshotServer!.serveSnapshot(relativePath, url.searchParams, snapshotUrl);
if (relativePath.startsWith('/sha1/')) {
const blob = await traceModel!.resourceForSha1(relativePath.slice('/sha1/'.length));
if (blob)
return new Response(blob, { status: 200 });
else
if (relativePath.startsWith('/snapshotSize/')) {
if (!snapshotServer)
return new Response(null, { status: 404 });
return snapshotServer.serveSnapshotSize(relativePath, url.searchParams);
}
if (relativePath.startsWith('/snapshot/')) {
if (!snapshotServer)
return new Response(null, { status: 404 });
return snapshotServer.serveSnapshot(relativePath, url.searchParams, snapshotUrl);
}
if (relativePath.startsWith('/sha1/')) {
// Sha1 is unique, load it from either of the models for simplicity.
for (const { traceModel } of loadedTraces.values()) {
const blob = await traceModel!.resourceForSha1(relativePath.slice('/sha1/'.length));
if (blob)
return new Response(blob, { status: 200 });
}
return new Response(null, { status: 404 });
}
// Fallback to network.
return fetch(event.request);
}
if (!snapshotServer)
return new Response(null, { status: 404 });
return snapshotServer!.serveResource(request.url, snapshotUrl);
return snapshotServer.serveResource(request.url, snapshotUrl);
}
async function gc() {
const usedTraces = new Set<string>();
for (const [traceUrl, entry] of loadedTraces) {
const client = await self.clients.get(entry.clientId);
if (client)
usedTraces.add(traceUrl);
}
for (const traceUrl of loadedTraces.keys()) {
if (!usedTraces.has(traceUrl))
loadedTraces.delete(traceUrl);
}
}
// @ts-ignore

View File

@ -41,8 +41,9 @@ export const SnapshotTab: React.FunctionComponent<{
if (action) {
const snapshot = snapshots[snapshotIndex];
if (snapshot && snapshot.snapshotName) {
snapshotUrl = new URL(`snapshot/${action.metadata.pageId}?name=${snapshot.snapshotName}`, window.location.href).toString();
snapshotSizeUrl = new URL(`snapshotSize/${action.metadata.pageId}?name=${snapshot.snapshotName}`, window.location.href).toString();
const traceUrl = new URL(window.location.href).searchParams.get('trace');
snapshotUrl = new URL(`snapshot/${action.metadata.pageId}?trace=${traceUrl}&name=${snapshot.snapshotName}`, window.location.href).toString();
snapshotSizeUrl = new URL(`snapshotSize/${action.metadata.pageId}?trace=${traceUrl}&name=${snapshot.snapshotName}`, window.location.href).toString();
if (snapshot.snapshotName.includes('action')) {
pointX = action.metadata.point?.x;
pointY = action.metadata.point?.y;

View File

@ -127,7 +127,7 @@ class HtmlReporter {
const absolutePath = path.join(reportFolder, ...relativePath.split('/'));
return server.serveFile(response, absolutePath);
});
const url = await server.start();
const url = await server.start(9323);
console.log('');
console.log(colors.cyan(` Serving HTML report at ${url}. Press Ctrl+C to quit.`));
console.log('');