The case format
A case is a data file. Any file ending .cases.json, anywhere under the directory a run
points at, read recursively in path order.
This page is the whole format, and it is not a summary of it. The tables are generated from
ScenarioSchema on every build — the same declaration the loader enforces and the same one
winwright_format serves to an agent — so a field renamed in the engine is renamed here, and a
field the engine does not have cannot appear.
A scenario file
Section titled “A scenario file”| Field | Holds | What it is for |
|---|---|---|
casesrequired | Cases | the cases the file holds, as an array |
fixtures | Fixtures | what to launch them against, as an array |
A case
Section titled “A case”| Field | Holds | What it is for |
|---|---|---|
namerequired | Text | what the case is called, and what a run of it is reported under |
stepsrequired | Steps | its steps, in the order they are performed |
tags | Words | what selects it besides its name, as an array of words |
needs | Words | what this machine has to have before it can observe anything, as an array of names |
catches | Text | the defect it exists to catch — what went wrong without it |
filed | Text | the task it was filed under |
fixture | Text | which of this file's 'fixtures' to launch it against |
forEach | Text | the key whose every declared string this case runs once for — derived from the project's own strings, with the member reaching a locator through '{}' |
onlyReads | Truth | that it leaves the window as it found it, so a window may be lent to it |
name has to be unique across the whole suite, not just its file: a name declared twice is
refused, naming both files, before anything runs.
catches is optional and worth writing anyway. It is what the case exists to catch, and a run
counts the cases that passed without saying — a check nobody can justify is one nobody dares
delete and nobody dares change.
A step
Section titled “A step”Every step says what to do and what to do it to, and at most one thing it claims about the result.
actis required. What it needs written beside it is the verb’s own business:withis required exactly where the act takes something and refused where it does not.- Exactly one of
locatorandtray. A tray icon is named by the shell rather than located in a window’s tree, so it is a second kind of subject. Naming both addresses two things and is refused; naming neither is refused too. - At most one claim. The fields marked claim below are alternatives — a step makes one of
them, or none and simply acts.
readsis not a claim: it says which reading the claim is about.
| Field | Holds | What it is for |
|---|---|---|
locatorone of the subject | Text | what to act on, in the locator grammar |
trayone of the subject | Text | the notification-area icon to act on, by the name the shell gives it — a tooltip, matched on its first line, for the surface no locator reaches |
fileone of the subject | Text | a file on disk to read, relative to the project's root or naming the fixture's staged directory with '{files}' — for what an act leaves outside any window, such as a file dropped onto an Explorer folder; it takes 'read' and claims 'sameBytesAs' or 'image' |
processone of the subject | Text | what the application's process does next, waited for over the launch timeout and claimed by the step: 'exits', by its own choice, or 'relaunches', a new process of the same executable whose window the steps after it then read — for an application that exits to update and is started again; or 'starts', a program it starts whose file name and arguments, read as 'blender.exe --python cube.glb', match the step's 'matches', which it then requires; or 'launched again', the run starting the application the way its fixture did, after an earlier step claimed 'exits', without staging its files again, so the steps after it read what the closed copy left; it takes 'read' and claims nothing else One of: exits, relaunches, starts, launched again |
actrequired | Text | what to do to it One of: read, invoke, toggle, set value, set range, select, add to selection, remove from selection, choose file, expand, collapse, focus, resize, type, click, drag, drop file, nudge, press, pick, pick at, open submenu, open tray menu, click tray icon, capture |
with | Text | what the act needs said, where it needs anything — '{root}' is filled with the project's root, '{root/}' with the same path as Windows OpenSSH spells it (/C:/src/app), '{files}' with the fixture's staged directory, and '{configuration}' with the build the project declaration names; a brace shaped like a token that is none of those is refused |
holding | Text | a key a 'click' or a 'drag' holds down across it — ctrl adds a row to a list's selection and shift takes the range to it; across a drag, an editor reads ctrl, alt or ctrl+alt as insert, duplicate or swap — for an application that answers the gesture and offers no pattern One of: ctrl, shift, alt, ctrl+alt |
button | Text | which button a 'click' presses, left where the step names none — middle closes a tab in a browser or a terminal, and right opens a context menu One of: left, right, middle |
at | Text | where inside its element a 'click' presses, as 'x,y' — each a percentage of the element ('8.9%') or device-independent pixels from its top-left corner ('6') — rather than its centre, for a spot drawn on a canvas; a point outside the element is refused and nothing is pressed |
clicks | Whole | how many times a 'click' presses, 1 where the step names none — 2 is a double click, sent as one batch at one point, for what opens on a double click and on nothing else |
to | Text | where a 'drag' ends: a second locator, in the same window unless 'toDesktop' says otherwise, that the pointer is released over — for the gesture no pattern expresses, such as a tab dragged across its neighbours; beside 'from', the point of the same element it is released at |
from | Text | where a 'drag' presses inside its own element, as 'x,y' — each a percentage of the element ('12%') or device-independent pixels from its top-left corner ('40') — with 'to' the point of the same element it is released at, for a handle drawn on a canvas |
toDesktop | Truth | that 'to' is looked for from the desktop rather than in the window under test, so its first step names another top-level window — for a drop onto another application, such as an Explorer folder |
expectclaim | Text | what the reading should be once the act has landed |
reads | Text | which reading the expectation is about One of: anything, value, range, toggle, selected, picked, expanded, text, name, description, status, enabled, focused |
movesclaim | Truth | that the reading should end up different, where the case cannot know what it will be |
answersclaim | Truth | that the reading it names should say something rather than nothing, where the case cannot know what |
emptyclaim | Truth | that the element is there and the reading it names says nothing — a field left blank, a tab with no state to report; unlike 'absent', a locator that matches nothing fails it |
matchesclaim | Text | the regular expression the reading should match, where the case cannot name the value but can name its shape |
disclosesclaim | Truth | that the act put something under the locator that was not in the tree before it |
sameAsclaim | Text | the 'named' of an earlier step in this case whose reading this one claims to be back to, for the round trip whose value no case can name |
unlikeclaim | Text | the 'named' of an earlier step in this case whose reading this one claims to differ from, for the change whose value no case can name at either end |
sameCountdownAsclaim | Text | the same claim as 'sameAs' for a reading that counts down while the case runs — the numbers in it must match, except the last, which may have ticked by one |
containsclaim | Text | the 'named' of an earlier step in this case whose reading this one claims to hold inside its own — for the dialog that quotes the thing it opened for, where neither string is one a case can type |
labelclaim | Text | the key whose declared string the reading should be — the label itself, derived from the project's own strings and never typed here |
expectReportedclaim | Text | the name whose value the application reports and the reading should be — declared in the project's reportedValues, for a fact about this machine that no case may type |
notLabelclaim | Text | the key whose declared string the reading should not be, for the state an application has a word for and must not be showing |
beginsWithLabelclaim | Text | the key whose declared string the reading should begin with, for a state announced as a word in front of a sentence — a prefix and never a containment, because the sentence behind may hold the word too |
endsWithLabelclaim | Text | the key whose declared string the reading should end with, for a state an application appends rather than announces in front — the same precision as the prefix and for the same reason, since the name before it is free text that can hold the word |
notEndsWithLabelclaim | Text | the key whose declared string the reading must NOT end with, for a state an application appends and must not be showing — the mirror of 'endsWithLabel', because a mark that can only be claimed present is one a window drawing it always would pass |
absentclaim | Truth | that this step's locator matches nothing — the claim an application makes by what its window does NOT hold, refused where the region the last step is looked for under is not there either |
ownHeaderclaim | Truth | that no control inside a row this locator matches announces a different row's header — the pairing a check for whether a name exists is blind to |
eachSpokenclaim | Truth | that every element this step's locator matches announces a name — a sweep over elements, where 'covers' is a sweep over the strings a key declares |
spokenclaim | Truth | that everything under the locator which announces anything announces a name — never a glyph, a template or an id handed back — and that something does |
neverclaim | Text | the key whose string must not be showing anywhere in the window at any moment while this step waits for its locator — a key and never the text, like the project's own loading strings |
passesclaim | Text | the key whose string must show anywhere in the window after this step's act, watched from before the act so a line that comes and goes is still seen — for an act whose handler runs after it returns, such as a rescan showing 'Scanning…'; a key and never the text |
coversclaim | Text | the key whose every string must be read somewhere the locator matches, and nothing else — derived from the project's own strings and never listed here |
coversAtLeastclaim | Text | the same set, claiming only that every declared string is read here and allowing values that are not in it — for a container the locator cannot separate from its neighbours |
coversWithinclaim | Text | the same set, claiming each declared string appears inside the name of something read rather than equalling it — for an entry that decorates what it is about |
withinclaim | Text | a second locator, in the same window, that this step's element claims to be drawn inside — its rectangle inside the other's, and not empty, for a control the layout clipped or left no room; waited for over the resolve timeout, and a failure names both rectangles |
fillsMonitorclaim | Truth | that this step's element covers the whole of the monitor it is on — for a full-screen mode, where a border or a strip left uncovered is the defect; waited for over the resolve timeout, and a failure names both rectangles and each edge left uncovered |
imageclaim | Text | on a 'file' step, what the file is as an image, decoded rather than compared byte for byte — facts joined by commas: 'width N', 'height N', 'longSide N', 'shortSide N', 'transparent' or 'opaque', such as 'longSide 2048, transparent' — waited for over the resolve timeout, and a failure names the size and transparency it read |
sameBytesAsclaim | Text | on a 'file' step, the file whose bytes it should hold, written the way 'file' is — waited for over the resolve timeout, and a file still missing or different then is a failure naming the path, the lengths and the first byte that differs |
meansIt | Truth | that this step means a destructive entry it names |
popup | Text | the popup inside this step's window whose tree the picture is of, by the name the application's own author gave it — for a capture, and the one surface no copy of the screen can ever take |
waits | Text | a timeout the project declares under 'timeouts', by name, that this step waits instead of 'resolve' and 'act' — for a result that takes minutes, such as a cloud job, without every absent step in the gate waiting as long; a name the project does not declare is refused before the launch |
named | Text | what a report should call it, where the act and the locator will not do |
Which reading reads is about
Section titled “Which reading reads is about”A claim is about one reading, and the wrong one does not fail in a way that says so: a reading the element does not offer answers nothing at all, forever, and the failure sentence says nothing answered to it rather than naming the reading that was never there.
| Reading | What it is about |
|---|---|
anything | the one value worth showing, whichever pattern the element happens to offer. |
value | what a control holding a value says it holds, through its ValuePattern. |
range | where a slider or a progress bar sits, as a number. |
toggle | whether a checkbox or a switch is on, off, or neither. |
selected | whether this element is the chosen one, which is a claim about the element. |
picked | which one a container chose, which is a claim about the container and the pair to selected. |
expanded | whether something that opens and closes is open. |
text | what a text-editing control holds, through its TextPattern; a label offers none and answers nothing. |
name | what the element announces itself as, which for a label is the words on it. |
description | what the element says about itself beyond its name. |
status | the state the element publishes in words for a screen reader, such as Connected; empty where it publishes none. |
enabled | whether the element would take an act at all. |
focused | whether the keyboard is in this element, which is the one reading that answers for anything that resolved. |
selected and picked are the pair worth reading twice. One asks whether this element is the
chosen one; the other asks which one a container chose. A picker offering no value pattern
answers nothing to value and answers its own label to name, so a round trip comparing
either would hold on every machine whatever the picker did.
A fixture
Section titled “A fixture”A fixture says what the application is launched with. Any case in the suite may name one declared in any file.
| Field | Holds | What it is for |
|---|---|---|
namerequired | Text | what to call it, and what a case names to be launched against it |
environment | Text | the sampled environment it is — the one field both the launch and the expectations read |
flag | Text | the argument the environment reaches the application through, without its value |
arguments | Words | everything else the launch carries, as an array — '{files}' is filled with the fixture's staged directory and '{root}' with the project's root, for an application whose paths come in as flags |
variables | Pairs | the environment variables it sets, as an object — APPDATA, LOCALAPPDATA, USERPROFILE and HOME reach only an application that reads the variable, and one asking Windows for that known folder still reads the real profile, which a report says |
shareable | Truth | that this window may be lent to a case that only reads it |
language | Text | the language tag the window it launches is in, so a derived set reads the strings that window is actually showing |
resident | Truth | that this launch draws no window of its own — a tray — so the run holds it as a process and its locators resolve against the desktop |
workingDirectory | Text | the directory to start the application in, resolved against the project's root — the default, so that an argument naming a file in the project means the same thing under every runner |
files | Placed | the files to put in a directory of this launch's own, each relative to the project's root, as an array — a variable or the working directory names that directory with '{files}', which is how a case gives the application a store of its own without the application growing a flag, or a step of a case in the same file does, such as a 'drop file' whose 'with' is '{files}/photo.png'; an entry may instead be an object with 'from' and 'to', where 'to' is a path under that directory such as quickshell/sessions.json, for an application whose store sits one folder down, and 'fill' set to true to have {reach0} and the staged directory's own token filled in its text, JSON-escaped where the file lands as .json and raw anywhere else; every staged copy is written now, as a file the application was just given, unless 'modified' names an age such as '-1d' to stage a stale one on purpose |
opens | Text | a folder to open in Explorer beside the window under test before the first step, relative to the project's root or naming '{files}' — created where it is not there, the two windows put side by side so a drop released over the folder lands in it, and the folder's window closed again when the case gives its window back; one still standing then is named as a survivor. Not beside 'shareable' |
reaches | Words | what the application connects to outside itself, each a host:port or a %VARIABLE% naming one, as an array — probed before every launch and never started; unreachable, every check on the fixture is a hole naming the address, and reachable, the arguments and variables name each as {reach0}, {reach1} and on |
before | Commands | commands run once the files are staged and before the application launches, each an object: 'run', the program, written the way 'application' is; 'arguments', with the fixture's tokens filled; 'input', what it reads on its standard input; and 'expectExit', 0 unless said — each with the fixture's variables and working directory, and one that exits otherwise or does not finish within the launch timeout leaves every check of the case a hole naming it and the end of what it wrote |
application | Text | the executable this launch starts instead of the one winwright.json names, such as the copy an installer put in %LOCALAPPDATA%\Programs — the environment expanded and the tokens filled, and relative to the project's root |
unset | Words | the variables the run inherited that the launch removes, by name, as an array — ELECTRON_RUN_AS_NODE, say, which VS Code exports and which makes an Electron window run as plain Node; a name also under 'variables' is refused |
activates | Text | a file to start the application through, the way Open with or a double-click in Explorer does, instead of an executable — relative to the project's root or naming '{files}', and activated through the ProgID 'through' names; the process the shell starts is held and stopped with the case, and what it starts in turn is not tracked. Not beside 'application', 'arguments', 'variables', 'unset', an environment or 'resident' |
through | Text | the ProgID 'activates' goes through, never the desk's default handler, or a packaged application's user model ID (family name, '!', app id), activated through the ProgID its package registered for the type — refused where the extension has no association, the ProgID is not registered, or it does not claim the type; an activation that starts no process of its own, handed to a copy already running, is said as that |
windowOf | Text | the image whose window the case is about, such as Modelado.exe, where what is launched is a launcher that starts it — a CLI alias or a stub — so the wait for a window reads that image among the processes the launch started rather than the launched one; the launcher leaving while it lives is not an early exit. Never a default, so a launcher's splash is not read instead. Not beside 'resident' |
environment is the machine setting the launch samples and flag is the argument it reaches
the application through; language says which language the resulting window is in, so
derived expectations read the strings that window is actually showing. A fixture naming an
environment nothing carries to the launch is refused, and so is one that names it twice — two
places deciding one thing means whichever the application reads last wins while the
expectations still describe the other.
What a value may be
Section titled “What a value may be”The Holds column above is one of these. It is data rather than prose because the loader
asks it before it reads a field, so the kind published here is the kind the run enforces: a
schema saying tags is text where the loader reads an array is a tool that accepts what the
run refuses.
| Holds | What a value may be |
|---|---|
| Text | Text. |
| Truth | True or false. |
| Whole | A whole number, written as a number and not as text. |
| Words | An array of text. |
| Pairs | An object whose every value is text. |
| Cases | An array of cases. |
| Steps | An array of steps. |
| Fixtures | An array of fixtures. |
| Placed | An array of files, each a path or an object naming where under the staged directory it lands. |
| Commands | An array of commands, each an object naming the program to run, what to give it and the exit that says it worked. |
A key nobody recognises is refused
Section titled “A key nobody recognises is refused”Every level of the format is closed. "expects" beside "expect" is a check the author wrote
and the run never made, and a loader that shrugged at it would hand that green back — so an
unrecognised key is refused, listing the keys there are.
That is the same list as the tables above, because there is only one. It is also why
winwright_check cannot be sent a misspelled key at all: the tool’s input schema is this
schema, so the refusal arrives before the file exists rather than on the first run.
Where to go next
Section titled “Where to go next”- The verbs — everything
actaccepts, and what each one needs of the desk before it can answer. - Addressing an element — the grammar every
locatorfield is written in. - Your first case — the same fields as a file you can copy, and the four calls that run it.
- The README — every verb
actaccepts, what each needs of the desk, and the reasoning behind each refusal.