// docs

Writing cases & targets

Author decks in the Case Editor in Gherkin or steps-and-expected. Add machine-addressable targets where you want future agent assistance — verdicts stay human today.

Honest scope: Teasynaer on main is a manual runner. Machine-addressable ⌖ targets prepare cases for a future agent pass; unattended execution is not shipped yet. Verdicts stay human.

A Teasynaer deck (a package row, hence /packages/[id]/… in the URL) is a set of case-cards a tester steps through in the Runner. You get one two ways:

  • Build it in the Case Editor/packages+ New deck creates an empty deck and drops you straight into /packages/[packageId]/manage, where you author cases one at a time. This page covers that path.
  • Bring in a deck already written — upload a Gherkin .feature file or a TestRail CSV export at /packages, or POST one from CI/a coding agent via the machine-import API. Either way you land in the same Case Editor afterwards, so everything below still applies once the deck exists. See importing-tests for the file formats, limits, and the CI/machine-import contract.

Both admin-only. A member can run decks and see results; only an admin sees + New deck, Import from file, or a deck's Manage link.

Two case shapes — set once per deck

Every deck is authored in one of two shapes, chosen with a toggle at the top of the Case Editor (Steps + Expected / Gherkin). It's a deck-level setting, not a per-case one — every case in the deck uses the same shape, and both render identically to the tester in the Runner: a card with steps and an expected result to check by eye.

Steps + Expected

The literal form — a case key, a title, a suite, a step list (one per line), and an expected result:

FieldExample
Case keyCO-H1
TitleApply a valid discount code
Suitecheckout
StepsGiven my basket total is £50.00<br>When I apply the code "SAVE10"
ExpectedThe basket total is £45.00<br>A "code applied" confirmation is shown

Case keys accept letters, numbers and hyphens and must start alphanumeric (CO-H1, not -CO-H1). Limits: title ≤ 500 characters, each step ≤ 2000, expected ≤ 4000 (joined), suite ≤ 64, deck name ≤ 255 — generous for real steps, but if you're tempted to paste a whole spec into one step, split it. There's no cap on how many cases a deck can hold this way (contrast the 200-scenario ceiling on a bulk .feature upload — see importing-tests).

Gherkin

Pick Gherkin and the case form becomes a single textarea — write the scenario in Given/When/Then, same as you would for a .feature file:

Feature: Checkout

  Scenario: Apply a valid discount code
    Given my basket total is £50.00
    When I apply the code "SAVE10"
    Then the basket total is £45.00
    And a "code applied" confirmation is shown

Under the hood this is stored exactly as the Steps + Expected shape above — Given/When lines become Steps, the Then (and any trailing And/But) becomes Expected, and the Scenario: name becomes the title (a case key is derived from it automatically, since Gherkin mode has no case key field). The conversion round-trips cleanly, so switching a case back to Steps + Expected later doesn't lose anything.

One real difference from a bulk .feature import: a missing Then doesn't reject the case here. The importer (importing-tests) is strict — no Then, whole file rejected. The Case Editor's single-scenario box is more forgiving: with no Then at all, it quietly treats your last step as the expected result instead of erroring. Convenient while drafting, easy to misread later — always write an explicit Then so the expected result says what you meant it to, not whatever happened to be your last step.

This box only ever holds one scenario — no Background:, no Scenario Outline:/Examples:, no tags. Those constructs are for the multi-scenario .feature file import, which creates several cases from one upload; a Gherkin-format deck here is one scenario per case, authored and saved one at a time.

⌖ targets — marking a step machine-addressable

Drop a ⌖ kind:value marker into a step's text (Steps + Expected shape only — the Gherkin box doesn't parse or show them) and the Case Editor recognises it as a target: a machine-addressable hook on that step, of one of four kinds.

KindPoints atExample
routea URL path⌖ route:/checkout
selectora DOM element⌖ selector:#apply-code
cookiea cookie name⌖ cookie:session_id
clocka fixed point in time⌖ clock:2026-01-01T00:00:00Z

A step can carry more than one:

Given I am at the payment step ⌖ route:/checkout
When I apply the code "SAVE10" ⌖ selector:#apply-code

As you type, a small mono line appears under the Steps box echoing what was parsed — Step 2: ⌖ selector:#apply-code — and a "Tester sees" preview shows the same steps with every ⌖ … token stripped out and the whitespace tidied, e.g. Open the page ⌖ route:/packages ⌖ selector:#main previews as "Open the page". Targets are metadata for tooling, never something a human tester has to read past.

What "agent-addressable" means today — and what it doesn't

Be precise about this, because it's easy to over-read: adding ⌖ targets does not make a case run itself. A case whose steps all carry targets is agent-addressable in principle — structured so that an automated actor could, in future, drive it against a live target — but nothing in Teasynaer executes a step today. There is no browser automation, no scheduler, no "run this suite unattended." That's a deliberate, permanent boundary (see agent-runs): Teasynaer is the human-attestation half of QA, and whatever an agent-run engine eventually does, it will only ever assist a human and reconcile against their verdict — never replace it, never file anything on its own.

What exists right now is a Dry-run ▶ button, next to Save, on any Steps + Expected case. Click it and it builds a plan from your current draft and logs it — nothing more:

[agent-run] case CO-H1 — Apply a valid discount code
[agent-run] step 1: Given I am at the payment step ⌖ route:/checkout
[agent-run]   ⌖ route:/checkout
[agent-run] step 2: When I apply the code "SAVE10" ⌖ selector:#apply-code
[agent-run]   ⌖ selector:#apply-code
[agent-run]   expect: The basket total is £45.00
[agent-run] coverage 100% (2/2 steps with targets)

That's the whole "engine": a deterministic planner that counts targets and prints what would be attempted, so you can see your coverage while authoring. No network call, no browser window, no target actually touched. It updates live from whatever's in the Steps box — including an unsaved draft — so it's a good way to sanity-check your ⌖ markup before saving.

If you don't add any ⌖ targets, nothing about the case changes for the tester — it's just a normal manual case, and that's a completely reasonable choice for most cases. Reach for targets when you specifically want a case ready for the day agent-runs ships, not as a required step.

Writing a case that's actually good to run

The shape is mechanical; the craft isn't. A few things that make a big difference to whoever steps through the card later:

  • One behaviour per case. If the title needs "and", it's probably two cases — Apply a valid discount code and Reject an expired discount code read and fail independently; a combined case doesn't tell you which half broke.
  • A concrete, checkable expected result. "The basket total is £45.00" is something a tester can look at and answer yes/no in a second. "It works" or "the right thing happens" isn't — it pushes the judgement call onto whoever runs the case, which is exactly what a written case exists to avoid.
  • Describe what, not how. "Apply the code SAVE10" survives a UI redesign; "click the promo field, type SAVE10, click Apply" breaks the moment a button moves. Let the tester choose the mechanics.
  • Steps stand alone. Each case should make sense read cold — don't lean on "as in the previous case." If several cases genuinely share setup, that's what the suite field is for grouping, not a substitute for self-contained steps.
  • Business language. Write for the person running the test, in the product's own vocabulary — not internal component or endpoint names they'd have to translate.

Editing, archiving, and keeping a deck tidy

  • Archive, don't delete. There's no hard delete on a case — Archive case retires it from the live list (and from what a tester sees), and it's listed under Show archived on the deck with a one-click Restore. Nothing is destroyed by mistake.
  • Deck details (name, description) edit separately via Edit deck details, without touching any case.
  • Per-deck redaction rules — literal or regex patterns masked out of this deck's evidence text on a fail — live in the same Case Editor under Redaction rules. That's a big enough topic on its own; see Evidence & redaction for what's masked and what isn't.
  • A "draft" badge on a case means it was promoted from a failed run's evidence trail (an admin action on that run, not something you do from here) — treat it as a first cut to review and finish, not a finished case.
  • Unsaved edits are guarded — leaving mid-edit (closing the tab, switching cases) asks first, and ⌘S/Ctrl-S saves the case you're on without reaching for the mouse.

Where to next

  • importing-tests — bulk-author a deck as a .feature file (with Background, Scenario Outline/Examples, and @suite: tags) or a TestRail CSV, including the CI/machine-import API for a coding agent to import one without a human upload.
  • agent-runs — the honest, current state of the ⌖-target engine: what a dry-run plan is for, what "agent-addressable in principle" does and doesn't promise, and why unattended execution isn't — and won't become — a Teasynaer feature.
  • Evidence & redaction — what a fail actually captures, and how a deck's redaction rules mask text before it's stored.
← All docs