Saffron for VS Code
Saffron is an end-to-end test runner for the web where you write
tests in plain language and nothing else. There are no step
definitions and no glue code. The first time a scenario runs, an AI agent drives a real browser
and records what it did into a small JSON cache. Every run after that
is replayed by Saffron in a real browser: zero AI calls, zero tokens,
at the speed of the browser and your app. When the UI changes and a step
fails, the agent heals it mid-run and files a proposal you review and
accept, like a snapshot update. Then steps are never healed: an
assertion passes because the app is right, or it fails.
Feature: Checkout
Scenario: Guest can place an order
Given I am on the products page
When I add "Blue Widget" to the cart
And I place the order and wait for the order API to return 201
Then I should see "Order placed"
.saffron is Saffron's own test language, and it goes further than
Gherkin: StepSet: for reusable step groups such as a login flow,
{env:VAR} tokens that keep secrets out of recordings and reports, and
{data:...} references to your data files. Standard .feature files
run too.
Get the runner with npm i -D saffron-ai, then npx saffron init.
Docs and the full story: saffron-ai.io.
Package: saffron-ai on npm.
This extension brings Saffron into the editor: it knows your project's
step vocabulary, runs your feature files, and shows what needs review.
What you get
- Syntax highlighting for
.saffron: every keyword, first-class
StepSet: definitions and StepSet <name> invocations, {env:...},
{data:...} and {unique:...} tokens, <param> placeholders, tables,
doc strings.
- Step completion with status badges. Type a step keyword
(
Given , When , …) and every step the project knows is suggested,
ranked by usage, badged from the replay cache:
● recorded: replays at zero tokens
● divergent: same text, different recordings
○ written, not recorded yet
- StepSet completion after typing
StepSet , with the set's steps
in the documentation popup.
- Go to definition on a
StepSet <name> invocation jumps to the
StepSet: block, wherever it lives.
- Hover on steps (status, usage, files) and on invocations (the
expanded steps with badges).
- Run from the editor or the Explorer. Right-click a
.saffron file
or a folder of them and choose Saffron: Run, Run (replay only,
no AI), Run (headed browser) or Re-record. The play button in
the editor title runs the open file. Commands run as a VS Code task in
the project that owns the file, the folder of the nearest
saffron.config.json above it, using that project's own saffron-ai
install (npx otherwise). A folder above several projects runs each of
them, from its own root. The Command Palette also has
Saffron: Run all feature files, Open latest report and
Accept all pending proposals.
- Run one scenario. Above each
Scenario: line, ▶ Run scenario,
Replay only and Headed run that scenario alone, by name,
saffron run <file>:<name> (an outline runs every row), saving the file
first. They show in .saffron files and in the .feature files of a
project with a saffron.config.json, in the file's own Gherkin language
(# language:). Needs saffron-ai 0.9.8 or later.
- The Saffron side panel. Click the Saffron icon in the activity
bar: Feature files lists every
.saffron file with its scenarios
as children (click a scenario to jump to it), inline Run and Replay-only
buttons per file, checkboxes plus Run selected in the title, and
Run all. Run options and status holds the replay-only and
headed toggles (applied to every run started from the extension), the
last run's totals with the report one click away, and the number of
proposals pending review with Accept all. It refreshes as files,
reports and proposals change.
- Tags, Proposals, Health and Orphaned recordings sections in the same panel, read from
saffron status --json (saffron-ai 0.5.4 or later). Tick tags and
Run tagged. Tick proposals, hover for the narrative, proof-replay
verdict and cost, then Accept selected, Reject selected or
Accept all. Clicking one opens the diff of what it changes, action
by action, so you approve the recording rather than the description of it.
Accept and Reject selected are bound to the revision you saw: a proposal a
later run replaced comes back unticked, and the runner refuses one that
changed before the command ran. That needs saffron-ai 0.9.0 or later; an
older runner gets the plain file, as before. Accept all runs saffron accept --all, which takes whatever is pending when it runs, not what the
panel shows, skips UNVERIFIED proposals and refuses STALE ones. Health shows the
vocabulary counts, divergent steps, duplicate wordings proven by identical recordings, the
effective config (click to open the file) and the pass rate of recent
runs. Orphaned recordings lists caches and proposals whose scenario
no longer exists, says why (the scenario is gone from the file, or the
file itself is gone), and removes them with one action after a
confirmation. It needs the saffron-ai release that added saffron prune.
- Test data,
{data:...}: completion of data files and keys with their
values, the current value on hover (a list's values, or an Examples file's
columns), go-to-definition into the JSON, and an error underline for a
reference that does not resolve. {data:...} and {unique:...} are
highlighted like {env:VAR}.
- Last run: the scenarios that failed or healed, with the step that failed.
Its title bar has Open Replay (every scenario the run traced, also
when everything passed) and Open Report (the HTML report in your browser).
Click one to open the screenshot of the page at the moment it went wrong,
in VS Code's image preview; the inline action jumps to the scenario.
Open Replay (on a scenario with an execution trace) runs
saffron trace for it and opens the replay in your browser: the steps as written,
the page at every action, the network alongside. A row acts on the
project it came from, and a screenshot a later run replaced is refused
rather than shown as this run's. The preview is a copy of the checked
picture, named after the scenario and kept in the extension's own storage
until the window closes; the row's tooltip gives the file in the project,
for attaching it to a bug. Open Replay and the screenshot check need
saffron-ai 0.9.0 or later, and Open Replay needs "trace": "retain-on-failure" (or "on") in the config.
- CI runs: the CI runs of your current branch, GitHub Actions or Azure
Pipelines, from
saffron runs. The list reloads when you switch branches
or press its refresh button, not as files change: each load asks the CI
over the network (a branch switch while the view is collapsed reloads it
when you open it). The runner reaches the CI through its own
tool, which keeps the credentials: the GitHub CLI (gh auth login) or
the Azure CLI with its azure-devops extension (az login); the extension
never sees a token. Expand a run to see what it found without
changing your checkout: saffron report --run downloads its results
under .saffron/ci/ (git ignores them) and checks them as an import
would, filing nothing. Under the run: the totals, each failed and healed
scenario with its failing step and the first line of its error (the
tooltip has what the agent changed), and "N proposals: M importable",
each one saying why it would not import. Open Report shows the
run's report in your browser; Open Trace replays a traced scenario,
view only. A run with no results says why (still running, expired, or
none uploaded). Results over saffron.ciMaxDownloadMB (default 100)
ask before downloading. Viewing needs saffron-ai 0.9.7 or later.
Show Results (the graph icon on a run) opens the run as one page in
an editor tab: the totals, each failed and pending-review scenario as a
card with its screenshot, failing step, error and what the agent said,
what passed, and the proposals, with buttons for Open trace, Open
scenario, Open report, Open run page and Import. On GitHub the row also
says what the run left before you open it (results and their size, a
summary only, results expired, no results). Both need saffron-ai 1.0.0
or later.
A heal made in CI is reviewed here instead of being
made again: Import (on a run) runs saffron import --run, which
downloads the run's Saffron bundle (the saffron-bundle* artifact its
pipeline uploads after saffron export) and imports it. The run then
lists what happened to each proposal: imported (click to open its diff),
already here, or not imported with the reason (a scenario or recording
that changed since, for one), and warns when your checkout is not at the
commit the run tested. Imported proposals appear under Proposals
marked with the run they came from, linked in the tooltip when the link
is on github.com or Azure DevOps; the run's
report, screenshots and traces appear under Last run. Accept as
usual, then commit and push the new recordings with the Source Control
view. CI runs need saffron-ai 0.9.3 or later.
The full guide, with the pipeline setup:
CI runs in your IDE.
- Multi-root workspaces: a selection that spans projects becomes one run
per project, each from its own root with its own config and install. The
status-backed sections show which project they describe, and Saffron:
Select project switches it.
- Dashboard in an editor tab: Saffron: Open dashboard (or click
Last run) shows the suite's quality in the run report's design: tests,
passing share, failing (and for how many runs in a row), flaky
scenarios, scenarios healed again and again, never run and left out
of the last run, slowest, pass rate and execution time over the last
20 runs, and results per tag. Click a scenario's name to open it.
Refreshed after each run; needs saffron-ai 0.9.4 or later.
- Diagnostics: unknown StepSet names, duplicate set definitions
(with the "remove the colon to invoke" hint), and near-duplicate
wording warnings: writing "I visit the home page" when
"I navigate to the home page" is already recorded gets flagged before
it costs a recording.
The vocabulary is derived live from your workspace: .feature/.saffron
files contribute the wording, .saffron/cache/**.json contributes the
status. No configuration.
Getting started
- Install this extension, then in your project:
npm i -D saffron-ai
and npx saffron init (add --examples for a ready-made suite
against saucedemo.com).
- Open a
.saffron file. Completion, navigation and diagnostics work
straight away; they read your feature files and .saffron/cache.
- Right-click a feature file and choose Saffron: Run, or use the
Saffron icon in the activity bar. The first run records with AI and
needs Claude credentials (a Claude Code login or
ANTHROPIC_API_KEY);
later runs replay for free.
- Review what was recorded in the Proposals section, accept it, and
commit
.saffron/cache/ like snapshots.
The panel's Tags, Proposals and Health sections need saffron-ai 0.5.4
or later in the project.
Zero-extension fallback
Without this extension, saffron steps --snippets writes
.vscode/saffron.code-snippets for native completion, and one settings
line gives .saffron files Gherkin highlighting:
{ "files.associations": { "*.saffron": "feature" } }
| |