diff --git a/src/server/frames.ts b/src/server/frames.ts index ffe6443083..3a8b3291e5 100644 --- a/src/server/frames.ts +++ b/src/server/frames.ts @@ -411,11 +411,11 @@ export class Frame extends SdkObject { private _setContentCounter = 0; readonly _detachedPromise: Promise; private _detachedCallback = () => {}; - readonly traceId: string; + readonly idInSnapshot: string; constructor(page: Page, id: string, parentFrame: Frame | null) { super(page); - this.traceId = parentFrame ? `frame@${id}` : page.traceId; + this.idInSnapshot = parentFrame ? `frame@${id}` : page.idInSnapshot; this.attribution.frame = this; this._id = id; this._page = page; diff --git a/src/server/page.ts b/src/server/page.ts index 5ec18f95e1..54c8f9d392 100644 --- a/src/server/page.ts +++ b/src/server/page.ts @@ -147,11 +147,11 @@ export class Page extends SdkObject { _ownedContext: BrowserContext | undefined; readonly selectors: Selectors; _video: Video | null = null; - readonly traceId: string; + readonly idInSnapshot: string; constructor(delegate: PageDelegate, browserContext: BrowserContext) { super(browserContext); - this.traceId = 'page@' + createGuid(); + this.idInSnapshot = 'page@' + createGuid(); this.attribution.page = this; this._delegate = delegate; this._closedCallback = () => {}; diff --git a/src/server/snapshot/inMemorySnapshotter.ts b/src/server/snapshot/inMemorySnapshotter.ts new file mode 100644 index 0000000000..7158b0fea9 --- /dev/null +++ b/src/server/snapshot/inMemorySnapshotter.ts @@ -0,0 +1,71 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BrowserContext } from '../browserContext'; +import { ContextResources, FrameSnapshot } from './snapshot'; +import { SnapshotRenderer } from './snapshotRenderer'; +import { NetworkResponse, SnapshotStorage } from './snapshotServer'; +import { Snapshotter, SnapshotterBlob, SnapshotterDelegate, SnapshotterResource } from './snapshotter'; + +export class InMemorySnapshotter implements SnapshotStorage, SnapshotterDelegate { + private _blobs = new Map(); + private _resources = new Map(); + private _frameSnapshots = new Map(); + private _snapshots = new Map(); + private _contextResources: ContextResources = new Map(); + private _snapshotter: Snapshotter; + + constructor(context: BrowserContext) { + this._snapshotter = new Snapshotter(context, this); + } + + onBlob(blob: SnapshotterBlob): void { + this._blobs.set(blob.sha1, blob.buffer); + } + + onResource(resource: SnapshotterResource): void { + this._resources.set(resource.resourceId, resource); + let resources = this._contextResources.get(resource.url); + if (!resources) { + resources = []; + this._contextResources.set(resource.url, resources); + } + resources.push({ frameId: resource.frameId, resourceId: resource.resourceId }); + } + + onFrameSnapshot(snapshot: FrameSnapshot): void { + const key = snapshot.pageId + '/' + snapshot.frameId; + let frameSnapshots = this._frameSnapshots.get(key); + if (!frameSnapshots) { + frameSnapshots = []; + this._frameSnapshots.set(key, frameSnapshots); + } + frameSnapshots.push(snapshot); + this._snapshots.set(snapshot.snapshotId, new SnapshotRenderer(new Map(this._contextResources), frameSnapshots, frameSnapshots.length - 1)); + } + + resourceContent(sha1: string): Buffer | undefined { + return this._blobs.get(sha1); + } + + resourceById(resourceId: string): NetworkResponse | undefined { + return this._resources.get(resourceId)!; + } + + snapshotById(snapshotId: string): SnapshotRenderer | undefined { + return this._snapshots.get(snapshotId); + } +} diff --git a/src/server/snapshot/snapshot.ts b/src/server/snapshot/snapshot.ts index 5e1fda86e8..251b6904a4 100644 --- a/src/server/snapshot/snapshot.ts +++ b/src/server/snapshot/snapshot.ts @@ -34,8 +34,19 @@ export type ResourceOverride = { }; export type FrameSnapshot = { + snapshotId: string, + pageId: string, + frameId: string, + frameUrl: string, doctype?: string, html: NodeSnapshot, resourceOverrides: ResourceOverride[], viewport: { width: number, height: number }, }; + +export type ContextResources = Map; + +export type RenderedFrameSnapshot = { + html: string; + resources: { [key: string]: { resourceId: string, sha1?: string } }; +}; diff --git a/src/server/snapshot/snapshotRenderer.ts b/src/server/snapshot/snapshotRenderer.ts index 3abc3036d5..9c1c0f52c7 100644 --- a/src/server/snapshot/snapshotRenderer.ts +++ b/src/server/snapshot/snapshotRenderer.ts @@ -14,23 +14,14 @@ * limitations under the License. */ -import { FrameSnapshot, NodeSnapshot } from './snapshot'; - -export type ContextResources = Map; - -export type RenderedFrameSnapshot = { - html: string; - resources: { [key: string]: { resourceId: string, sha1?: string } }; -}; +import { ContextResources, FrameSnapshot, NodeSnapshot, RenderedFrameSnapshot } from './snapshot'; export class SnapshotRenderer { private _snapshots: FrameSnapshot[]; private _index: number; private _contextResources: ContextResources; - private _frameId: string; - constructor(frameId: string, contextResources: ContextResources, snapshots: FrameSnapshot[], index: number) { - this._frameId = frameId; + constructor(contextResources: ContextResources, snapshots: FrameSnapshot[], index: number) { this._contextResources = contextResources; this._snapshots = snapshots; this._index = index; @@ -80,7 +71,7 @@ export class SnapshotRenderer { const resources: { [key: string]: { resourceId: string, sha1?: string } } = {}; for (const [url, contextResources] of this._contextResources) { - const contextResource = contextResources.find(r => r.frameId === this._frameId) || contextResources[0]; + const contextResource = contextResources.find(r => r.frameId === snapshot.frameId) || contextResources[0]; if (contextResource) resources[url] = { resourceId: contextResource.resourceId }; } diff --git a/src/server/snapshot/snapshotServer.ts b/src/server/snapshot/snapshotServer.ts index 57dfab7f77..479f9cd1eb 100644 --- a/src/server/snapshot/snapshotServer.ts +++ b/src/server/snapshot/snapshotServer.ts @@ -16,8 +16,9 @@ import * as http from 'http'; import querystring from 'querystring'; -import { SnapshotRenderer, RenderedFrameSnapshot } from './snapshotRenderer'; +import { SnapshotRenderer } from './snapshotRenderer'; import { HttpServer } from '../../utils/httpServer'; +import type { RenderedFrameSnapshot } from './snapshot'; export type NetworkResponse = { contentType: string; @@ -26,9 +27,9 @@ export type NetworkResponse = { }; export interface SnapshotStorage { - resourceContent(sha1: string): Buffer; - resourceById(resourceId: string): NetworkResponse; - snapshotByName(snapshotName: string): SnapshotRenderer | undefined; + resourceContent(sha1: string): Buffer | undefined; + resourceById(resourceId: string): NetworkResponse | undefined; + snapshotById(snapshotId: string): SnapshotRenderer | undefined; } export class SnapshotServer { @@ -207,7 +208,7 @@ export class SnapshotServer { response.setHeader('Cache-Control', 'public, max-age=31536000'); response.setHeader('Content-Type', 'application/json'); const parsed: any = querystring.parse(request.url!.substring(request.url!.indexOf('?') + 1)); - const snapshot = this._snapshotStorage.snapshotByName(parsed.snapshotName); + const snapshot = this._snapshotStorage.snapshotById(parsed.snapshotName); const snapshotData: any = snapshot ? snapshot.render() : { html: '' }; response.end(JSON.stringify(snapshotData)); return true; @@ -236,9 +237,14 @@ export class SnapshotServer { } const resource = this._snapshotStorage.resourceById(resourceId); + if (!resource) + return false; + const sha1 = overrideSha1 || resource.responseSha1; try { const content = this._snapshotStorage.resourceContent(sha1); + if (!content) + return false; response.statusCode = 200; let contentType = resource.contentType; const isTextEncoding = /^text\/|^application\/(javascript|json)/.test(contentType); diff --git a/src/server/snapshot/snapshotter.ts b/src/server/snapshot/snapshotter.ts index 87c97c687a..bc580f8717 100644 --- a/src/server/snapshot/snapshotter.ts +++ b/src/server/snapshot/snapshotter.ts @@ -21,12 +21,13 @@ import { helper, RegisteredListener } from '../helper'; import { debugLogger } from '../../utils/debugLogger'; import { Frame } from '../frames'; import { SnapshotData, frameSnapshotStreamer, kSnapshotBinding, kSnapshotStreamer } from './snapshotterInjected'; -import { calculateSha1 } from '../../utils/utils'; +import { calculateSha1, createGuid } from '../../utils/utils'; import { FrameSnapshot } from './snapshot'; export type SnapshotterResource = { + resourceId: string, pageId: string, - frameId: string, // Empty means main frame + frameId: string, url: string, contentType: string, responseHeaders: { name: string, value: string }[], @@ -45,7 +46,7 @@ export type SnapshotterBlob = { export interface SnapshotterDelegate { onBlob(blob: SnapshotterBlob): void; onResource(resource: SnapshotterResource): void; - onFrameSnapshot(frame: Frame, frameUrl: string, snapshot: FrameSnapshot, snapshotId?: string): void; + onFrameSnapshot(snapshot: FrameSnapshot): void; } export class Snapshotter { @@ -61,6 +62,10 @@ export class Snapshotter { ]; this._context.exposeBinding(kSnapshotBinding, false, (source, data: SnapshotData) => { const snapshot: FrameSnapshot = { + snapshotId: data.snapshotId, + pageId: source.page.idInSnapshot, + frameId: source.frame.idInSnapshot, + frameUrl: data.url, doctype: data.doctype, html: data.html, viewport: data.viewport, @@ -76,7 +81,7 @@ export class Snapshotter { snapshot.resourceOverrides.push({ url, ref: content }); } } - this._delegate.onFrameSnapshot(source.frame, data.url, snapshot, data.snapshotId); + this._delegate.onFrameSnapshot(snapshot); }); this._context._doAddInitScript('(' + frameSnapshotStreamer.toString() + ')()'); } @@ -114,7 +119,7 @@ export class Snapshotter { const context = await parent._mainContext(); await context.evaluateInternal(({ kSnapshotStreamer, frameElement, frameId }) => { (window as any)[kSnapshotStreamer].markIframe(frameElement, frameId); - }, { kSnapshotStreamer, frameElement, frameId: frame.traceId }); + }, { kSnapshotStreamer, frameElement, frameId: frame.idInSnapshot }); frameElement.dispose(); } catch (e) { // Ignore @@ -147,8 +152,9 @@ export class Snapshotter { const body = await response.body().catch(e => debugLogger.log('error', e)); const responseSha1 = body ? calculateSha1(body) : 'none'; const resource: SnapshotterResource = { - pageId: page.traceId, - frameId: response.frame().traceId, + pageId: page.idInSnapshot, + frameId: response.frame().idInSnapshot, + resourceId: 'resource@' + createGuid(), url, contentType, responseHeaders: response.headers(), diff --git a/src/server/snapshot/snapshotterInjected.ts b/src/server/snapshot/snapshotterInjected.ts index 903b8eb67c..429e527d6b 100644 --- a/src/server/snapshot/snapshotterInjected.ts +++ b/src/server/snapshot/snapshotterInjected.ts @@ -26,7 +26,7 @@ export type SnapshotData = { }[], viewport: { width: number, height: number }, url: string, - snapshotId?: string, + snapshotId: string, }; export const kSnapshotStreamer = '__playwright_snapshot_streamer_'; @@ -92,7 +92,7 @@ export function frameSnapshotStreamer() { const observerConfig = { attributes: true, subtree: true }; this._observer.observe(document, observerConfig); - this._streamSnapshot(); + this._streamSnapshot('snapshot@initial'); } private _interceptNativeMethod(obj: any, method: string, cb: (thisObj: any, result: any) => void) { @@ -168,7 +168,7 @@ export function frameSnapshotStreamer() { this._streamSnapshot(snapshotId); } - private _streamSnapshot(snapshotId?: string) { + private _streamSnapshot(snapshotId: string) { if (this._timer) { clearTimeout(this._timer); this._timer = undefined; @@ -178,7 +178,7 @@ export function frameSnapshotStreamer() { (window as any)[kSnapshotBinding](snapshot).catch((e: any) => {}); } catch (e) { } - this._timer = setTimeout(() => this._streamSnapshot(), 100); + this._timer = setTimeout(() => this._streamSnapshot(`snapshot@${performance.now()}`), 100); } private _sanitizeUrl(url: string): string { @@ -228,7 +228,7 @@ export function frameSnapshotStreamer() { } } - private _captureSnapshot(snapshotId?: string): SnapshotData { + private _captureSnapshot(snapshotId: string): SnapshotData { const snapshotNumber = ++this._lastSnapshotNumber; let nodeCounter = 0; let shadowDomNesting = 0; diff --git a/src/server/trace/common/traceEvents.ts b/src/server/trace/common/traceEvents.ts index c47f4927da..4c4bacc0cf 100644 --- a/src/server/trace/common/traceEvents.ts +++ b/src/server/trace/common/traceEvents.ts @@ -129,8 +129,6 @@ export type FrameSnapshotTraceEvent = { pageId: string, frameId: string, snapshot: FrameSnapshot, - frameUrl: string, - snapshotId?: string, }; export type TraceEvent = diff --git a/src/server/trace/recorder/tracer.ts b/src/server/trace/recorder/tracer.ts index a7bb4ef26a..5622e60ef8 100644 --- a/src/server/trace/recorder/tracer.ts +++ b/src/server/trace/recorder/tracer.ts @@ -121,7 +121,7 @@ class ContextTracer implements SnapshotterDelegate { contextId: this._contextId, pageId: resource.pageId, frameId: resource.frameId, - resourceId: 'resource@' + createGuid(), + resourceId: resource.resourceId, url: resource.url, contentType: resource.contentType, responseHeaders: resource.responseHeaders, @@ -134,16 +134,14 @@ class ContextTracer implements SnapshotterDelegate { this._appendTraceEvent(event); } - onFrameSnapshot(frame: Frame, frameUrl: string, snapshot: FrameSnapshot, snapshotId?: string): void { + onFrameSnapshot(snapshot: FrameSnapshot): void { const event: trace.FrameSnapshotTraceEvent = { timestamp: monotonicTime(), type: 'snapshot', contextId: this._contextId, - pageId: frame._page.traceId, - frameId: frame.traceId, + pageId: snapshot.pageId, + frameId: snapshot.frameId, snapshot: snapshot, - frameUrl, - snapshotId, }; this._appendTraceEvent(event); } @@ -163,7 +161,7 @@ class ContextTracer implements SnapshotterDelegate { timestamp: monotonicTime(), type: 'action', contextId: this._contextId, - pageId: sdkObject.attribution.page.traceId, + pageId: sdkObject.attribution.page.idInSnapshot, objectType: metadata.type, method: metadata.method, // FIXME: filter out evaluation snippets, binary @@ -179,7 +177,7 @@ class ContextTracer implements SnapshotterDelegate { } private _onPage(page: Page) { - const pageId = page.traceId; + const pageId = page.idInSnapshot; const event: trace.PageCreatedTraceEvent = { timestamp: monotonicTime(), diff --git a/src/server/trace/viewer/screenshotGenerator.ts b/src/server/trace/viewer/screenshotGenerator.ts deleted file mode 100644 index 2b9d7ac4d6..0000000000 --- a/src/server/trace/viewer/screenshotGenerator.ts +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs'; -import path from 'path'; -import * as playwright from '../../../..'; -import * as util from 'util'; -import { ActionEntry, ContextEntry, TraceModel } from './traceModel'; -import { SnapshotServer } from '../../snapshot/snapshotServer'; - -const fsReadFileAsync = util.promisify(fs.readFile.bind(fs)); -const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs)); - -export class ScreenshotGenerator { - private _resourcesDir: string; - private _browserPromise: Promise; - private _snapshotServer: SnapshotServer; - private _traceModel: TraceModel; - private _rendering = new Map>(); - private _lock = new Lock(3); - - constructor(snapshotServer: SnapshotServer, resourcesDir: string, traceModel: TraceModel) { - this._snapshotServer = snapshotServer; - this._resourcesDir = resourcesDir; - this._traceModel = traceModel; - this._browserPromise = playwright.chromium.launch(); - } - - generateScreenshot(actionId: string): Promise { - const { context, action } = this._traceModel.actionById(actionId); - if (!this._rendering.has(action)) { - this._rendering.set(action, this._render(context, action).then(body => { - this._rendering.delete(action); - return body; - })); - } - return this._rendering.get(action)!; - } - - private async _render(contextEntry: ContextEntry, actionEntry: ActionEntry): Promise { - const imageFileName = path.join(this._resourcesDir, actionEntry.action.timestamp + '-screenshot.png'); - try { - return await fsReadFileAsync(imageFileName); - } catch (e) { - // fall through - } - - const { action } = actionEntry; - const browser = await this._browserPromise; - - await this._lock.obtain(); - - const page = await browser.newPage({ - viewport: contextEntry.created.viewportSize, - deviceScaleFactor: contextEntry.created.deviceScaleFactor - }); - - try { - await page.goto(this._snapshotServer.snapshotRootUrl()); - - const snapshots = action.snapshots || []; - const snapshotId = snapshots.length ? snapshots[0].snapshotId : undefined; - const snapshotUrl = this._snapshotServer.snapshotUrl(action.pageId!, snapshotId, action.endTime); - console.log('Generating screenshot for ' + action.method); // eslint-disable-line no-console - await page.evaluate(snapshotUrl => (window as any).showSnapshot(snapshotUrl), snapshotUrl); - - try { - const element = await page.$(action.params.selector || '*[__playwright_target__]'); - if (element) { - await element.evaluate(e => { - e.style.backgroundColor = '#ff69b460'; - }); - } - } catch (e) { - console.log(e); // eslint-disable-line no-console - } - const imageData = await page.screenshot(); - await fsWriteFileAsync(imageFileName, imageData); - return imageData; - } catch (e) { - console.log(e); // eslint-disable-line no-console - } finally { - await page.close(); - this._lock.release(); - } - } -} - -class Lock { - private _maxWorkers: number; - private _callbacks: (() => void)[] = []; - private _workers = 0; - - constructor(maxWorkers: number) { - this._maxWorkers = maxWorkers; - } - - async obtain() { - while (this._workers === this._maxWorkers) - await new Promise(f => this._callbacks.push(f)); - ++this._workers; - } - - release() { - --this._workers; - const callbacks = this._callbacks; - this._callbacks = []; - for (const callback of callbacks) - callback(); - } -} diff --git a/src/server/trace/viewer/traceModel.ts b/src/server/trace/viewer/traceModel.ts index 02a8693783..4165f63108 100644 --- a/src/server/trace/viewer/traceModel.ts +++ b/src/server/trace/viewer/traceModel.ts @@ -16,7 +16,8 @@ import { createGuid } from '../../../utils/utils'; import * as trace from '../common/traceEvents'; -import { ContextResources, SnapshotRenderer } from '../../snapshot/snapshotRenderer'; +import { SnapshotRenderer } from '../../snapshot/snapshotRenderer'; +import { ContextResources } from '../../snapshot/snapshot'; export * as trace from '../common/traceEvents'; export class TraceModel { @@ -74,7 +75,6 @@ export class TraceModel { const action: ActionEntry = { actionId, action: event, - thumbnailUrl: `/action-preview/${actionId}.png`, resources: pageEntry.resources, }; pageEntry.resources = []; @@ -156,8 +156,8 @@ export class TraceModel { const { pageEntry, contextEntry } = this.pageEntries.get(pageId)!; const frameSnapshots = pageEntry.snapshotsByFrameId[frameId]; for (let index = 0; index < frameSnapshots.length; index++) { - if (frameSnapshots[index].snapshotId === snapshotId) - return new SnapshotRenderer(frameId, this.contextResources.get(contextEntry.created.contextId)!, frameSnapshots.map(fs => fs.snapshot), index); + if (frameSnapshots[index].snapshot.snapshotId === snapshotId) + return new SnapshotRenderer(this.contextResources.get(contextEntry.created.contextId)!, frameSnapshots.map(fs => fs.snapshot), index); } } @@ -170,7 +170,7 @@ export class TraceModel { if (timestamp && snapshot.timestamp <= timestamp) snapshotIndex = index; } - return snapshotIndex >= 0 ? new SnapshotRenderer(frameId, this.contextResources.get(contextEntry.created.contextId)!, frameSnapshots.map(fs => fs.snapshot), snapshotIndex) : undefined; + return snapshotIndex >= 0 ? new SnapshotRenderer(this.contextResources.get(contextEntry.created.contextId)!, frameSnapshots.map(fs => fs.snapshot), snapshotIndex) : undefined; } } @@ -197,7 +197,6 @@ export type PageEntry = { export type ActionEntry = { actionId: string; action: trace.ActionTraceEvent; - thumbnailUrl: string; resources: trace.NetworkResourceTraceEvent[]; }; diff --git a/src/server/trace/viewer/traceViewer.ts b/src/server/trace/viewer/traceViewer.ts index 03e20ddfa4..64377eccdc 100644 --- a/src/server/trace/viewer/traceViewer.ts +++ b/src/server/trace/viewer/traceViewer.ts @@ -18,7 +18,6 @@ import fs from 'fs'; import path from 'path'; import * as playwright from '../../../..'; import * as util from 'util'; -import { ScreenshotGenerator } from './screenshotGenerator'; import { TraceModel } from './traceModel'; import { NetworkResourceTraceEvent, TraceEvent } from '../common/traceEvents'; import { ServerRouteHandler, HttpServer } from '../../../utils/httpServer'; @@ -62,7 +61,6 @@ class TraceViewer implements SnapshotStorage { // Served by TraceViewer // - "/traceviewer/..." - our frontend. // - "/file?filePath" - local files, used by sources tab. - // - "/action-preview/..." - lazily generated action previews. // - "/sha1/" - trace resource bodies, used by network previews. // // Served by SnapshotServer @@ -73,6 +71,7 @@ class TraceViewer implements SnapshotStorage { // and translates them into "/resources/". const server = new HttpServer(); + new SnapshotServer(server, this); const traceModelHandler: ServerRouteHandler = (request, response) => { response.statusCode = 200; @@ -82,9 +81,6 @@ class TraceViewer implements SnapshotStorage { }; server.routePath('/contexts', traceModelHandler); - const snapshotServer = new SnapshotServer(server, this); - const screenshotGenerator = this._document ? new ScreenshotGenerator(snapshotServer, this._document.resourcesDir, this._document.model) : undefined; - const traceViewerHandler: ServerRouteHandler = (request, response) => { const relativePath = request.url!.substring('/traceviewer/'.length); const absolutePath = path.join(__dirname, '..', '..', '..', 'web', ...relativePath.split('/')); @@ -92,26 +88,6 @@ class TraceViewer implements SnapshotStorage { }; server.routePrefix('/traceviewer/', traceViewerHandler, true); - const actionPreviewHandler: ServerRouteHandler = (request, response) => { - if (!screenshotGenerator) - return false; - const fullPath = request.url!.substring('/action-preview/'.length); - const actionId = fullPath.substring(0, fullPath.indexOf('.png')); - screenshotGenerator.generateScreenshot(actionId).then(body => { - if (!body) { - response.statusCode = 404; - response.end(); - } else { - response.statusCode = 200; - response.setHeader('Content-Type', 'image/png'); - response.setHeader('Content-Length', body.byteLength); - response.end(body); - } - }); - return true; - }; - server.routePrefix('/action-preview/', actionPreviewHandler); - const fileHandler: ServerRouteHandler = (request, response) => { try { const url = new URL('http://localhost' + request.url!); @@ -141,19 +117,19 @@ class TraceViewer implements SnapshotStorage { await uiPage.goto(urlPrefix + '/traceviewer/traceViewer/index.html'); } - resourceById(resourceId: string): NetworkResourceTraceEvent { + resourceById(resourceId: string): NetworkResourceTraceEvent | undefined { const traceModel = this._document!.model; return traceModel.resourceById.get(resourceId)!; } - snapshotByName(snapshotName: string): SnapshotRenderer | undefined { + snapshotById(snapshotName: string): SnapshotRenderer | undefined { const traceModel = this._document!.model; const parsed = parseSnapshotName(snapshotName); const snapshot = parsed.snapshotId ? traceModel.findSnapshotById(parsed.pageId, parsed.frameId, parsed.snapshotId) : traceModel.findSnapshotByTime(parsed.pageId, parsed.frameId, parsed.timestamp!); return snapshot; } - resourceContent(sha1: string): Buffer { + resourceContent(sha1: string): Buffer | undefined { return fs.readFileSync(path.join(this._document!.resourcesDir, sha1)); } } diff --git a/src/web/traceViewer/ui/actionList.css b/src/web/traceViewer/ui/actionList.css index 4d11f38657..97fc1a4cbc 100644 --- a/src/web/traceViewer/ui/actionList.css +++ b/src/web/traceViewer/ui/actionList.css @@ -80,18 +80,3 @@ display: inline; padding-left: 5px; } - -.action-thumbnail { - flex: none; - display: flex; - align-items: center; - justify-content: center; - width: 200px; - height: 100px; - box-shadow: var(--box-shadow); -} - -.action-thumbnail img { - max-width: 200px; - max-height: 100px; -} diff --git a/src/web/traceViewer/ui/actionList.stories.tsx b/src/web/traceViewer/ui/actionList.stories.tsx index 0f68228a4c..a55721e55b 100644 --- a/src/web/traceViewer/ui/actionList.stories.tsx +++ b/src/web/traceViewer/ui/actionList.stories.tsx @@ -16,8 +16,6 @@ import { Story, Meta } from '@storybook/react/types-6-0'; import { ActionList, ActionListProps } from './actionList'; -import gotoThumbnailUrl from './assets/action-thumbnail-goto.png'; -import clickThumbnailUrl from './assets/action-thumbnail-click.png'; export default { title: 'TraceViewer/ActionList', @@ -43,7 +41,6 @@ Primary.args = { startTime: Date.now(), endTime: Date.now(), }, - thumbnailUrl: gotoThumbnailUrl, resources: [], }, { @@ -57,7 +54,6 @@ Primary.args = { startTime: Date.now(), endTime: Date.now(), }, - thumbnailUrl: clickThumbnailUrl, resources: [], } ] diff --git a/src/web/traceViewer/ui/actionList.tsx b/src/web/traceViewer/ui/actionList.tsx index 987fc09886..d578f139eb 100644 --- a/src/web/traceViewer/ui/actionList.tsx +++ b/src/web/traceViewer/ui/actionList.tsx @@ -35,8 +35,7 @@ export const ActionList: React.FC = ({ }) => { const targetAction = highlightedAction || selectedAction; return
{actions.map(actionEntry => { - const { action, actionId, thumbnailUrl } = actionEntry; - const selector = action.params.selector; + const { action, actionId } = actionEntry; return
= ({ {action.params.selector &&
{action.params.selector}
} {action.method === 'goto' && action.params.url &&
{action.params.url}
}
-
- -
; })}; }; diff --git a/src/web/traceViewer/ui/assets/action-thumbnail-click.png b/src/web/traceViewer/ui/assets/action-thumbnail-click.png deleted file mode 100644 index 4cef69f929..0000000000 Binary files a/src/web/traceViewer/ui/assets/action-thumbnail-click.png and /dev/null differ diff --git a/src/web/traceViewer/ui/assets/action-thumbnail-goto.png b/src/web/traceViewer/ui/assets/action-thumbnail-goto.png deleted file mode 100644 index f75a37eacb..0000000000 Binary files a/src/web/traceViewer/ui/assets/action-thumbnail-goto.png and /dev/null differ diff --git a/test/trace.spec.ts b/test/trace.spec.ts index 3d248c64c0..249813d210 100644 --- a/test/trace.spec.ts +++ b/test/trace.spec.ts @@ -63,7 +63,7 @@ it('should record trace', (test, { browserName, platform }) => { expect(clickEvent).toBeTruthy(); expect(clickEvent.snapshots.length).toBe(2); const snapshotId = clickEvent.snapshots[0].snapshotId; - const snapshotEvent = traceEvents.find(event => event.type === 'snapshot' && event.snapshotId === snapshotId) as trace.FrameSnapshotTraceEvent; + const snapshotEvent = traceEvents.find(event => event.type === 'snapshot' && event.snapshot.snapshotId === snapshotId) as trace.FrameSnapshotTraceEvent; expect(snapshotEvent).toBeTruthy(); }); diff --git a/utils/check_deps.js b/utils/check_deps.js index 6e4cb70ccd..4f64e39c39 100644 --- a/utils/check_deps.js +++ b/utils/check_deps.js @@ -155,6 +155,7 @@ DEPS['src/service.ts'] = ['src/remote/']; DEPS['src/cli/'] = ['src/cli/**', 'src/client/**', 'src/install/**', 'src/generated/', 'src/server/injected/', 'src/debug/injected/', 'src/server/trace/**', 'src/utils/**']; DEPS['src/server/supplements/recorder/recorderApp.ts'] = ['src/common/', 'src/utils/', 'src/server/', 'src/server/chromium/']; +DEPS['src/server/supplements/recorderSupplement.ts'] = ['src/server/snapshot/', ...DEPS['src/server/']]; DEPS['src/utils/'] = ['src/common/']; // Trace viewer