Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:Microsoft Playwright TraceModel Loader

From Leeroopedia
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 .trace files to identify trace ordinals.
  • For each ordinal, reads the .trace, .network, and .stacks files.
  • 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 .stacks files 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 errorDescriptors from 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 iframe sources.
  • 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);
  }
}

Related Pages

Implements Principle

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment