Skip to content

Integration Playwright

aryehcitron@gmail.com edited this page Aug 21, 2026 · 4 revisions

The Kronikol.Playwright package is the browser-driven end-to-end client for Kronikol. It mints a per-test identity, stamps the Kronikol test-tracking-* headers (and a W3C traceparent) on every request a Playwright IBrowserContext / IPage makes, and opens the matching in-process identity scope — so page→backend calls are attributed to the running test.

The sink is downstream, not the fixture. Where the calls get recorded depends on your topology:

Backend Sink What to do
Kronikol-instrumented (.NET, TestTrackingContextMiddleware + TestTrackingMessageHandler) the server Nothing else — the middleware reads the headers and the handlers log under that identity.
Not instrumentable (polyglot, third-party, legacy) a [[proxy tap Integration-ProxyTap-Extension]] on each hop

Install

dotnet add package Kronikol.Playwright

Quick Start (xUnit)

using Kronikol.Playwright;
using Microsoft.Playwright;

public class OverviewTests : IAsyncLifetime
{
    private IPlaywright _pw = null!;
    private IBrowser _browser = null!;

    public async ValueTask InitializeAsync()
    {
        _pw = await Playwright.CreateAsync();
        _browser = await _pw.Chromium.LaunchAsync(new() { Headless = true });
    }

    public async ValueTask DisposeAsync() { await _browser.DisposeAsync(); _pw.Dispose(); }

    [Fact]
    public async Task Overview_renders()
    {
        // One identity per test: name for the report, id = the correlation key
        // (defaults to the identity's W3C trace id, so proxy taps can attribute by traceparent too).
        var identity = TestTrackingIdentity.Create("overview › renders");

        await using var context = await _browser.NewTrackedContextAsync(identity);
        var page = await context.NewPageAsync();
        await page.GotoAsync("http://localhost:4000/intelligence");

        // ... assertions ...
        // Report generation: Scenario.Id must equal identity.TestId.
    }
}

If your test framework already runs under a Kronikol adapter (xUnit/NUnit/MSTest/TUnit/… with TestIdentityScope set), use TestTrackingIdentity.FromCurrentScope() so the browser traffic joins the same scenario as your in-process HttpClient calls.


API

Member Purpose
TestTrackingIdentity.Create(testName, testId?, callerName = "Browser", traceId?) Mint an identity. TestId defaults to TraceId.ToString("N") (32-hex = the W3C trace id).
TestTrackingIdentity.FromCurrentScope() Build from the ambient TestIdentityScope.
identity.ToHeaders() The four TestTrackingHttpHeaders (+ traceparent unless IncludeTraceparent = false). Values are ISO-8859-1-safe and ≤ 512 chars.
browser.NewTrackedContextAsync(identity, options?) New context with the headers merged into ExtraHTTPHeaders. The per-test entry point.
context.UseTestTrackingAsync(identity, additionalHeaders?) / page.UseTestTrackingAsync(...) Stamp an existing context/page (SetExtraHTTPHeadersAsync replaces the set — pass other headers you need via additionalHeaders).
identity.BeginScope() Open the matching in-process TestIdentityScope (dispose to close).
identity.Traceparent() A fresh sampled traceparent rooted at the identity's trace id.

How the headers flow

Browser ──(test-tracking-current-test-name / -current-test-id / -caller-name / -trace-id, traceparent)──►
   your web app ──► graphql ──► data-insights ──► …
  • A Kronikol-instrumented hop reads name+id (TestTrackingContextMiddleware) and its outbound handlers re-stamp all four.
  • A ProxyTap does the same from outside: it reads the headers, falls back to the traceparent trace id when a hop dropped them, and re-injects the four headers downstream.
  • Because TestId defaults to the W3C trace id, every hop — instrumented or tapped — lands in the same scenario, and the same id finds the distributed trace in Tempo/Jaeger.

Other languages

The header names and the "test mints the trace, trace id = test id" convention are the contract for the planned @kronikol/playwright (Node) fixture and the Java port — a TypeScript Playwright fixture needs only context.setExtraHTTPHeaders({...}) with the same five headers. See Ingesting External Captures for the capture format those ports share.

Home


Demo


Getting Started

Common Tasks

Integration Guides

Uninstrumentable / polyglot backends

Extensions

Configuration

Features

Reference

Clone this wiki locally