replaytest

package
v0.13.21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 18, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package replaytest implements a record→worker-mock→replay pipeline for deterministic, fully-local front-end testing.

The pipeline captures live proxy traffic into a scenario, serves the recorded API responses back from an in-page web worker during replay, and drives a headless browser against the mocked app so automation runs without touching any real backend. Fuzz mutators perturb the recorded responses to probe front-end resilience, and a breadth-exploration step partitions seeds for fan-out across browser-debugger subagents.

Units:

  • scenario: on-disk scenario model (.agnt/replaytests/<name>.json)
  • match: request matching of live calls against recorded entries
  • fuzz: response mutators (empty_array, http_error, ...)
  • domsig: DOM signature capture/diff for pass/fail assertions
  • worker_bundle: the in-page web worker that serves recorded responses
  • recorder: assembles scenarios from captured proxy traffic
  • report: scenario run report model (<name>.report.json)
  • driver: headless-chrome replay driver with in-page network mock
  • refine: LLM-assisted scenario cleanup (needs an API key)
  • explore: seed partitioning for subagent breadth exploration
  • store: scenario/report persistence

All record/refine/replay/explore actions are license-gated behind license.CapAdvancedTesting (Pro: advanced_testing).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DOMSignature

func DOMSignature(html string, volatileAttrs []string) string

DOMSignature returns a stable hash of an HTML fragment with volatile attributes removed and whitespace collapsed, so cosmetic noise does not register as a regression.

func Explore

func Explore(ctx context.Context, sc *Scenario, rep *Report, runner BreadthRunner, agents int, preset string) error

Explore fans out `agents` seeds through the runner and merges findings into the report; newly discovered stable assertions are promoted onto the report.

func GenerateBundle

func GenerateBundle(s *Scenario, preset string) (string, error)

GenerateBundle returns the full JavaScript (shim + worker) to inject for a replay session. Pure function: same inputs -> same output.

The main-thread shim overrides window.fetch and window.XMLHttpRequest to delegate every request to a Web Worker (created from a Blob) that holds the recordings, the matcher queues, and the fuzz mutator. The worker source is embedded as a JSON-quoted JS string literal so it can be carried inside a Go raw-string template without backtick/escape collisions.

func PresetNames

func PresetNames() []string

func Refine

func Refine(ctx context.Context, sc *Scenario, client LLMClient) error

Refine mutates the Scenario's steps in place using the provided client.

func TemplatePath

func TemplatePath(path string) string

TemplatePath replaces identifier-looking path segments with ":id" so that recordings match across differing ids at replay time.

Types

type AssertType

type AssertType string
const (
	AssertText    AssertType = "text"
	AssertPresent AssertType = "present"
)

type Assertion

type Assertion struct {
	Selector string     `json:"selector"`
	Type     AssertType `json:"type"`
	Expect   string     `json:"expect"`
	Mask     bool       `json:"mask"`
}

type BreadthFinding

type BreadthFinding struct {
	StatesVisited int         `json:"states_visited"`
	Crashes       []Crash     `json:"crashes"`
	NewAssertions []Assertion `json:"new_assertions"`
}

BreadthFinding holds the output of a single exploration seed.

type BreadthRunner

type BreadthRunner interface {
	Run(ctx context.Context, seed ExploreSeed) (BreadthFinding, error)
}

BreadthRunner runs one exploration seed (in production: dispatch a browser-debugger subagent against an isolated worker-mocked context).

type Crash

type Crash struct {
	Route    string `json:"route"`
	Selector string `json:"selector"`
	Error    string `json:"error"`
}

type Driver

type Driver struct {
	// contains filtered or unexported fields
}

Driver runs the chromedp seed lane: it injects the worker bundle (which mocks the network entirely in-page) before navigation, drives a Scenario's Steps, evaluates Assertions, captures JS errors, and returns a *Report. It is THIN — all request matching and response mutation lives in the worker bundle.

func NewDriver

func NewDriver(proxyID string) *Driver

NewDriver returns a seed-lane driver. proxyID may be "" when the bundle fully mocks the network in-page (the default); when set, the chromedp session is routed through that proxy.

func (*Driver) RunSeed

func (d *Driver) RunSeed(ctx context.Context, sc *Scenario, preset string) (*Report, error)

RunSeed injects the worker bundle for the given preset, drives the scenario's steps once, and records a single SeedResult labeled by preset ("baseline" when preset==""). It fails fast on bundle generation or browser errors.

type ExploreSeed

type ExploreSeed struct {
	Index int
	Route string
}

ExploreSeed identifies one exploration run within a breadth fan-out.

type FuzzPreset

type FuzzPreset struct {
	Name  string
	Apply func(status int, body string) FuzzResult
}

func Preset

func Preset(name string) (FuzzPreset, bool)

type FuzzResult

type FuzzResult struct {
	Status int
	Body   string
}

type LLMClient

type LLMClient interface {
	RefineAssertions(ctx context.Context, steps []Step) ([]Step, error)
}

LLMClient refines auto-captured assertions: masking volatile content and keeping high-signal checks. Injected so CI runs against a stub.

type MatchKey

type MatchKey struct {
	Method    string   `json:"method"`
	Path      string   `json:"path"`
	QueryKeys []string `json:"query_keys"`
}

type Matcher

type Matcher struct {
	// contains filtered or unexported fields
}

Matcher resolves live requests to recordings, advancing an ordered queue per key so repeated identical calls return successive recordings.

func NewMatcher

func NewMatcher(recs []Recording) *Matcher

func (*Matcher) Match

func (m *Matcher) Match(method, rawURL, bodySig string) (Recording, bool)

Match returns the next recording for the request, or ok=false on a miss. It does a single exact templated-key lookup against the queue map. Recordings store already-templated paths, so a live request templates to the same key.

type Recording

type Recording struct {
	Match          MatchKey          `json:"match"`
	RequestBodySig string            `json:"request_body_sig,omitempty"`
	Status         int               `json:"status"`
	Headers        map[string]string `json:"headers"`
	BodyRef        string            `json:"body_ref"`
	Hits           int               `json:"hits"`
}

type Report

type Report struct {
	Scenario   string       `json:"scenario"`
	Seeds      []SeedResult `json:"seeds"`
	Crashes    []Crash      `json:"crashes"`
	NewAsserts []Assertion  `json:"new_assertions,omitempty"`
}

func NewReport

func NewReport(scenario string) *Report

func (*Report) AddCrash

func (r *Report) AddCrash(route, selector, errMsg string)

func (*Report) AddSeedResult

func (r *Report) AddSeedResult(preset string, passed bool, failures []string)

func (*Report) CrashCount

func (r *Report) CrashCount() int

func (*Report) JSON

func (r *Report) JSON() ([]byte, error)

func (*Report) Passed

func (r *Report) Passed() bool

type Scenario

type Scenario struct {
	Name       string            `json:"name"`
	Version    int               `json:"version"`
	RecordedAt string            `json:"recorded_at"`
	BaseURL    string            `json:"base_url"`
	Steps      []Step            `json:"steps"`
	Recordings []Recording       `json:"recordings"`
	Blobs      map[string]string `json:"blobs"`
}

func AssembleScenario

func AssembleScenario(name, baseURL string, entries []proxy.LogEntry) *Scenario

AssembleScenario builds a Scenario from a window of proxy log entries. Interaction entries become ordered Steps; HTTP entries become Recordings with response bodies out-of-lined into Blobs (blob:0, blob:1, ...). Paths are templated via TemplatePath, query keys are extracted (dropping the cache-buster "_"), and consecutive identical recordings are coalesced into one with an incremented Hits count.

func UnmarshalScenario

func UnmarshalScenario(data []byte) (*Scenario, error)

func (*Scenario) MarshalJSON

func (s *Scenario) MarshalJSON() ([]byte, error)

type SeedResult

type SeedResult struct {
	Preset   string   `json:"preset"`
	Passed   bool     `json:"passed"`
	Failures []string `json:"failures,omitempty"`
}

type Step

type Step struct {
	Index        int         `json:"index"`
	Kind         StepKind    `json:"kind"`
	Selector     string      `json:"selector"`
	Value        string      `json:"value,omitempty"`
	DOMSignature string      `json:"dom_signature"`
	Assertions   []Assertion `json:"assertions"`
}

type StepKind

type StepKind string
const (
	StepNavigate StepKind = "navigate"
	StepClick    StepKind = "click"
	StepInput    StepKind = "input"
	StepSubmit   StepKind = "submit"
)

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store persists Scenarios and reports under <projectDir>/.agnt/replaytests/.

func NewStore

func NewStore(projectDir string) *Store

func (*Store) List

func (s *Store) List() ([]string, error)

func (*Store) LoadScenario

func (s *Store) LoadScenario(name string) (*Scenario, error)

func (*Store) SaveReport

func (s *Store) SaveReport(name string, data []byte) error

func (*Store) SaveScenario

func (s *Store) SaveScenario(sc *Scenario) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL