Skip to main content

Record tests with Capture

shaft-capture defines the provider-neutral intermediate representation used by SHAFT Capture and the managed-browser recorder that produces it. It records browser intent for review, replay, migration, and future Java/JUnit/Cucumber consumers. It generates Java 25 SHAFT/TestNG tests without depending on shaft-ai; provider adapters remain optional.

Capture creation, validation, serialization, and privacy enforcement work with pilot.ai.enabled=false. Optional AI consumers may receive only the already redacted representation after the separate Pilot approval checks succeed.

Managed browser recording

The recorder launches a fresh SHAFT-managed Chrome or Edge session. Firefox is rejected with an explicit unsupported-browser message until equivalent event coverage is available. WebDriver BiDi supplies navigation, browsing-context, prompt, and preload-script signals when available. A JavaScript listener drained through ordinary WebDriver provides deterministic interaction capture and remains the compatibility fallback.

Build the executable MCP JAR, then use its capture subcommand:

mvn -pl shaft-mcp -am package -DskipTests -Dgpg.skip
java -jar shaft-mcp/target/SHAFT_MCP-<version>.jar capture start \
--url https://example.test --browser chrome \
--output recordings/example.json --headless
java -jar shaft-mcp/target/SHAFT_MCP-<version>.jar capture status
java -jar shaft-mcp/target/SHAFT_MCP-<version>.jar capture checkpoint \
--description "Checkout ready"
java -jar shaft-mcp/target/SHAFT_MCP-<version>.jar capture stop

Use --runtime-dir <path> on every command to isolate control files. stop also accepts --discard. Only one recorder may own a runtime directory at a time. The daemon control endpoint is bound to loopback, requires a generated bearer token, and removes its token and descriptor at shutdown. Browser profiles are temporary and removed after normal stop or interruption.

The same lifecycle is exposed by the capture_start, capture_status, and capture_stop MCP tools. Generation is exposed by capture_generate. Status contains safe metadata and counts, never typed values.

All process arguments and filesystem paths are built with Java APIs (ProcessBuilder, Path, and Files). No Windows, POSIX shell, or path separator is assumed; restrictive POSIX permissions are applied when supported and otherwise the host filesystem's inherited permissions are used.

Format

Every session has a schemaVersion, safe session and browser metadata, ordered events and checkpoints, external test-data references, a redaction summary, and explicit extension maps. The current version is 1.0; readers migrate the synthetic 0.9 format and reject unsupported versions with an actionable message.

The event hierarchy covers:

  • navigation, click, type, clear, select, check/uncheck, and upload;
  • keyboard, window/tab, frame, alert, and explicit wait operations;
  • explicit verification events and replay status.

ElementSnapshot retains sanitized role, accessible name, label, normalized attributes, visibility state, and LocatorCandidate evidence. Candidate scores are deterministic inputs based on strategy, uniqueness, visibility, stability, and recorded signals. No model inference is used to rank locators.

The bundled schema is:

shaft-capture/src/main/resources/schema/shaft-capture-session-1.0.schema.json

CaptureJsonCodec validates before read and write, emits stable human-readable JSON, preserves explicit extension fields, and never publishes a partially validated recording.

Privacy boundary

CapturePrivacyClassifier runs before values enter a CaptureSession. Passwords, tokens, configured sensitive fields/selectors/attributes/URL parameters, and configured value patterns produce named environment or secret references with no original value. Ordinary typed data is externalized to capture-data.json by default through ExternalTestDataWriter.

Upload events store a logical fixture reference, sanitized basename, media type, and size. They never retain an arbitrary absolute user path or file contents. Evidence references accept only safe relative paths. Cookies, storage, headers, page source, screenshots, and other evidence are absent unless a later collector explicitly enables a documented category.

The persisted redaction summary contains only counts and rule names. It does not contain removed values.

Lifecycle

CaptureSessionStore provides thread-safe start, append, checkpoint, interruption, stop, and read operations. Each update serializes and validates a complete snapshot before an atomic replacement. In-progress or crashed sessions remain readable with status INCOMPLETE; a normal stop records COMPLETED and an end timestamp.

Example:

var session = CaptureSession.start(
"checkout-recording",
Instant.now(),
browserMetadata);
var store = new CaptureSessionStore(Path.of("recordings/checkout.json"));
store.start(session);
store.append(captureEvent);
store.stop(Instant.now());

Deterministic TestNG generation

Generate a test, SHAFT JSON test data, and a deterministic report:

java -jar shaft-mcp/target/SHAFT_MCP-<version>.jar capture generate \
--session recordings/example.json \
--output-dir generated-tests

The default output layout is:

generated-tests/
src/test/java/generated/capture/<SessionName>Test.java
src/test/resources/testDataFiles/<session-name>-test.json
target/shaft-capture/generation-report.json

Generation selects locators in the accessibility, label, test-ID, stable ID/name, CSS, then XPath family. The report records the score contribution from uniqueness, visibility, interactability, semantic match, volatility, frame/shadow context, and replay evidence, plus ranked fallbacks. Stable user-provided locators can outrank volatile semantic evidence.

Ordinary values are copied from the recording's external JSON into the generated test-data file. Secret and sensitive references become required environment variables and are typed securely. Uploads remain relative fixture references under src/test/resources. Missing data is reported as required user input. Captured credentials, cookies, authorization values, and absolute personal paths fail the privacy scan instead of entering generated artifacts.

Every generated class creates a fresh driver in @BeforeMethod and calls driver.quit() from @AfterMethod(alwaysRun = true). Only explicit VerificationEvent records become assertions. An ASSERTION checkpoint must point at a verification event; unsupported steps fail generation with their event IDs and remediation.

Compilation is enabled by default. Add --replay to run the compiled TestNG class in an isolated process and require populated, passing Allure result files. Existing source, data, report, or preview files are never replaced unless --overwrite is supplied.

AI enrichment is optional and uses two phases:

# Calls the enabled provider only with explicit processing approval.
java -jar shaft-mcp/target/SHAFT_MCP-<version>.jar capture generate \
--session recordings/example.json --output-dir generated-tests \
--ai-preview --allow-local-ai

# Apply the reviewed, fingerprinted preview.
java -jar shaft-mcp/target/SHAFT_MCP-<version>.jar capture generate \
--session recordings/example.json --output-dir generated-tests-enriched \
--apply-enrichment generated-tests/target/shaft-capture/enrichment-preview.json \
--approve-enrichment --replay

The provider may suggest Java names and captured-state assertions only. It cannot replace deterministic locators. Preview output is schema-validated and privacy-scanned; apply rejects stale fingerprints, invalid identifiers, unknown events, and assertions that contradict captured state. Accepted changes are compiled and replayed again.

Run the focused suite with:

mvn -pl shaft-capture -am test

The real-browser recording and generated-replay suites are opt-in:

mvn -pl shaft-capture -am test \
-DincludeCaptureBrowserE2E=true \
-Dtest=ManagedCaptureRecorderBrowserTest,CaptureGeneratedReplayBrowserTest