mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(trace): allow multiple traces in a single app, gc traces (#9478)
This commit is contained in:
parent
fc54f1937a
commit
cd99ad0da2
@ -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:
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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('');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user