Implementation:Microsoft Playwright TraceModel Loader
| Knowledge Sources | |
|---|---|
| Domains | Debugging, Testing |
| Last Updated | 2026-02-11 00:00 GMT |
Overview
Concrete APIs for parsing trace files, building structured trace models, and rendering DOM snapshots, provided by the Playwright library.
Description
The trace analysis pipeline consists of three primary classes that transform raw trace data into a queryable, renderable model:
1. TraceLoader (traceLoader.ts, Lines 32-126) is responsible for reading and parsing raw trace files. Its load() method accepts a TraceLoaderBackend (abstracting ZIP or directory access) and performs these steps:
- Scans entry names for
.tracefiles to identify trace ordinals. - For each ordinal, reads the
.trace,.network, and.stacksfiles. - Uses a TraceModernizer to parse JSON events and populate a ContextEntry with actions, events, pages, resources, errors, and stdio.
- Applies client-side call stacks from
.stacksfiles to actions. - Sorts actions by start time and terminates incomplete actions gracefully (for non-live traces).
- Initializes a SnapshotStorage for snapshot lookup during rendering.
2. TraceModel (traceModel.ts, Lines 66-163) is the high-level model that consumers interact with. Its constructor accepts a trace URI and an array of ContextEntry[] (produced by TraceLoader) and performs:
- Context indexing: Associates actions, pages, events, and resources with their parent context via symbols.
- Action merging: Calls
mergeActionsAndUpdateTiming()to combine test runner and library context actions, aligning their timestamps using a computed time delta. - Error analysis: Builds
errorDescriptorsfrom either test runner errors (when step data is present) or action errors (for library-only traces). Each descriptor includes the error message, stack trace, and associated action. - Source collection: Gathers all unique source file paths from action stacks and annotates them with error locations.
- Action grouping: Counts actions by group (navigation, click, fill, etc.) for the viewer's filter UI.
Key TraceModel methods:
- failedAction(): Returns the innermost (last) action with an error, useful for jumping to the failure point.
- filteredActions(filter): Returns actions matching the specified action groups.
- renderActionTree(filter): Builds a textual representation of the action hierarchy with indentation.
3. SnapshotRenderer (snapshotRenderer.ts, Lines 40-218) reconstructs DOM snapshots into renderable HTML. Its render() method (Lines 77-160):
- Traverses the snapshot's node tree, which uses a compact array format:
[tagName, attributes, ...children]. - Resolves subtree references that point to nodes in previous snapshots (for deduplication).
- Rewrites URLs for custom protocols (Electron support) and
iframesources. - Escapes HTML content and attributes properly.
- Injects a script that handles shadow DOM reconstruction, scroll position restoration, element highlighting, and canvas content rendering.
- Uses an LRU cache to avoid re-rendering previously computed snapshots.
- Returns
{ html, pageId, frameId, index }for the viewer to display.
The TraceLoaderBackend interface abstracts the storage layer:
entryNames(): Lists all file entries in the trace archive.hasEntry(name): Checks if a specific entry exists.readText(name): Reads a text entry.readBlob(name): Reads a binary entry.isLive(): Indicates whether the trace is from an active test run.
Usage
Use these APIs when:
- Loading traces in the viewer: The trace viewer SPA uses TraceLoader to parse traces and TraceModel to power its UI.
- Programmatic trace analysis: Build custom tools that parse and analyze trace data.
- Rendering snapshots: Use SnapshotRenderer to generate HTML from trace snapshots for display or comparison.
- Custom trace backends: Implement TraceLoaderBackend for non-standard trace sources (databases, cloud storage).
Code Reference
Source Location
- Repository: playwright
- TraceModel:
packages/playwright-core/src/utils/isomorphic/trace/traceModel.ts(Lines 66-163) - TraceLoader:
packages/playwright-core/src/utils/isomorphic/trace/traceLoader.ts(Lines 32-126) - SnapshotRenderer:
packages/playwright-core/src/utils/isomorphic/trace/snapshotRenderer.ts(Lines 40-218)
Signature
// TraceLoaderBackend interface
interface TraceLoaderBackend {
entryNames(): Promise<string[]>;
hasEntry(entryName: string): Promise<boolean>;
readText(entryName: string): Promise<string | undefined>;
readBlob(entryName: string): Promise<Blob | undefined>;
isLive(): boolean;
}
// TraceLoader class
class TraceLoader {
contextEntries: ContextEntry[];
async load(backend: TraceLoaderBackend,
unzipProgress: (done: number, total: number) => void): Promise<void>;
async hasEntry(filename: string): Promise<boolean>;
async resourceForSha1(sha1: string): Promise<Blob | undefined>;
storage(): SnapshotStorage;
}
// TraceModel class
class TraceModel {
readonly traceUri: string;
readonly startTime: number;
readonly endTime: number;
readonly browserName: string;
readonly channel?: string;
readonly platform?: string;
readonly playwrightVersion?: string;
readonly wallTime?: number;
readonly title?: string;
readonly options: BrowserContextEventOptions;
readonly pages: PageEntry[];
readonly actions: ActionTraceEventInContext[];
readonly attachments: Attachment[];
readonly events: (EventTraceEvent | ConsoleMessageTraceEvent)[];
readonly stdio: StdioTraceEvent[];
readonly errors: ErrorTraceEvent[];
readonly errorDescriptors: ErrorDescription[];
readonly hasSource: boolean;
readonly hasStepData: boolean;
readonly sdkLanguage: Language | undefined;
readonly sources: Map<string, SourceModel>;
resources: ResourceEntry[];
constructor(traceUri: string, contexts: ContextEntry[]);
failedAction(): ActionTraceEventInContext | undefined;
filteredActions(actionsFilter: ActionGroup[]): ActionTraceEventInContext[];
renderActionTree(filter?: ActionGroup[]): string[];
}
// SnapshotRenderer class
class SnapshotRenderer {
readonly snapshotName: string | undefined;
constructor(
htmlCache: LRUCache<SnapshotRenderer, string>,
resources: ResourceSnapshot[],
snapshots: FrameSnapshot[],
screencastFrames: PageEntry['screencastFrames'],
index: number
);
snapshot(): FrameSnapshot;
viewport(): { width: number; height: number };
closestScreenshot(): string | undefined;
render(): RenderedFrameSnapshot;
resourceByUrl(url: string, method: string): ResourceSnapshot | undefined;
}
// RenderedFrameSnapshot type
type RenderedFrameSnapshot = {
html: string;
pageId: string;
frameId: string;
index: number;
};
Import
// These are isomorphic modules used by the trace viewer SPA:
import { TraceModel } from '@isomorphic/trace/traceModel';
import { TraceLoader } from '@isomorphic/trace/traceLoader';
import { SnapshotRenderer } from '@isomorphic/trace/snapshotRenderer';
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| backend | TraceLoaderBackend |
Yes | Abstraction for reading trace archive entries. Implementations include ZIP-based and directory-based backends. |
| unzipProgress | (done: number, total: number) => void |
Yes | Progress callback invoked during trace loading. Total is 3 * numOrdinals (trace + network + stacks per ordinal).
|
| traceUri | string |
Yes | URI identifying the trace source. Stored in the model and used for creating relative URLs. |
| contexts | ContextEntry[] |
Yes | Array of parsed context entries from TraceLoader, passed to TraceModel constructor. |
| htmlCache | LRUCache<SnapshotRenderer, string> |
Yes | Cache for rendered HTML strings, preventing redundant snapshot rendering. |
| resources | ResourceSnapshot[] |
Yes | Network resources for the SnapshotRenderer to resolve URLs and apply overrides. |
| snapshots | FrameSnapshot[] |
Yes | Array of frame snapshots for the SnapshotRenderer. Indexed by position. |
| index | number |
Yes | Index into the snapshots array selecting the specific snapshot to render. |
Outputs
| Name | Type | Description |
|---|---|---|
| TraceModel | object |
Fully indexed model with actions, pages, events, errors, sources, attachments, and computed properties. |
| contextEntries | ContextEntry[] |
Parsed context entries from TraceLoader, ready for TraceModel construction. |
| RenderedFrameSnapshot | object |
Object with html (rendered HTML string), pageId, frameId, and index.
|
| errorDescriptors | ErrorDescription[] |
Array of error descriptions with message, stack trace, and associated action reference. |
| actionTree | string[] |
Textual representation of the action hierarchy from renderActionTree().
|
| failedAction | undefined | The innermost action that failed, if any. |
Usage Examples
Basic Example
// Load a trace and build the model
const loader = new TraceLoader();
await loader.load(zipBackend, (done, total) => {
console.log(`Loading: ${Math.round(done / total * 100)}%`);
});
const model = new TraceModel('trace.zip', loader.contextEntries);
// Inspect the model
console.log('Browser:', model.browserName);
console.log('Actions:', model.actions.length);
console.log('Pages:', model.pages.length);
console.log('Errors:', model.errors.length);
// Find the failed action
const failed = model.failedAction();
if (failed) {
console.log('Failed at:', failed.class + '.' + failed.method);
console.log('Error:', failed.error?.message);
}
// Render action tree
const tree = model.renderActionTree();
tree.forEach(line => console.log(line));
// Output:
// page.goto(https://example.com)
// page.click(#button)
// locator.waitFor
// page.fill(#input, "hello")
Snapshot Rendering Example
import { LRUCache } from '@isomorphic/lruCache';
// Create cache and renderer
const htmlCache = new LRUCache<SnapshotRenderer, string>(50);
const renderer = new SnapshotRenderer(
htmlCache,
model.resources,
snapshotStorage.snapshotsForPage(pageId),
page.screencastFrames,
snapshotIndex
);
// Render the snapshot
const { html, pageId, frameId, index } = renderer.render();
// The html string contains a complete HTML document with:
// - DOCTYPE declaration
// - Injected script for shadow DOM, scroll restoration, element highlighting
// - The serialized DOM tree with rewritten URLs
// - Hidden visibility (unhidden by the injected script after processing)
// Get the closest screenshot for canvas rendering
const screenshotSha1 = renderer.closestScreenshot();
Error Analysis Example
// Analyze errors from the trace model
for (const error of model.errorDescriptors) {
console.log('Error:', error.message);
if (error.stack && error.stack.length > 0) {
const frame = error.stack[0];
console.log(' at', frame.file + ':' + frame.line);
// Look up source content
const source = model.sources.get(frame.file);
if (source?.content) {
const lines = source.content.split('\n');
console.log(' Code:', lines[frame.line - 1]?.trim());
}
}
if (error.action) {
console.log(' During:', error.action.class + '.' + error.action.method);
}
}