Skip to content

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.

FieldHoldsWhat it is for
casesrequiredCasesthe cases the file holds, as an array
fixturesFixtureswhat to launch them against, as an array
FieldHoldsWhat it is for
namerequiredTextwhat the case is called, and what a run of it is reported under
stepsrequiredStepsits steps, in the order they are performed
tagsWordswhat selects it besides its name, as an array of words
needsWordswhat this machine has to have before it can observe anything, as an array of names
catchesTextthe defect it exists to catch — what went wrong without it
filedTextthe task it was filed under
fixtureTextwhich of this file's 'fixtures' to launch it against
forEachTextthe key whose every declared string this case runs once for — derived from the project's own strings, with the member reaching a locator through '{}'
onlyReadsTruththat 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.

Every step says what to do and what to do it to, and at most one thing it claims about the result.

  • act is required. What it needs written beside it is the verb’s own business: with is required exactly where the act takes something and refused where it does not.
  • Exactly one of locator and tray. 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. reads is not a claim: it says which reading the claim is about.
FieldHoldsWhat it is for
locatorone of the subjectTextwhat to act on, in the locator grammar
trayone of the subjectTextthe 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 subjectTexta 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 subjectTextwhat 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
actrequiredTextwhat 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
withTextwhat 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
holdingTexta 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
buttonTextwhich 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
atTextwhere 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
clicksWholehow 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
toTextwhere 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
fromTextwhere 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
toDesktopTruththat '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
expectclaimTextwhat the reading should be once the act has landed
readsTextwhich reading the expectation is about
One of: anything, value, range, toggle, selected, picked, expanded, text, name, description, status, enabled, focused
movesclaimTruththat the reading should end up different, where the case cannot know what it will be
answersclaimTruththat the reading it names should say something rather than nothing, where the case cannot know what
emptyclaimTruththat 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
matchesclaimTextthe regular expression the reading should match, where the case cannot name the value but can name its shape
disclosesclaimTruththat the act put something under the locator that was not in the tree before it
sameAsclaimTextthe '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
unlikeclaimTextthe '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
sameCountdownAsclaimTextthe 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
containsclaimTextthe '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
labelclaimTextthe key whose declared string the reading should be — the label itself, derived from the project's own strings and never typed here
expectReportedclaimTextthe 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
notLabelclaimTextthe key whose declared string the reading should not be, for the state an application has a word for and must not be showing
beginsWithLabelclaimTextthe 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
endsWithLabelclaimTextthe 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
notEndsWithLabelclaimTextthe 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
absentclaimTruththat 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
ownHeaderclaimTruththat 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
eachSpokenclaimTruththat 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
spokenclaimTruththat everything under the locator which announces anything announces a name — never a glyph, a template or an id handed back — and that something does
neverclaimTextthe 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
passesclaimTextthe 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
coversclaimTextthe 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
coversAtLeastclaimTextthe 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
coversWithinclaimTextthe 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
withinclaimTexta 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
fillsMonitorclaimTruththat 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
imageclaimTexton 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
sameBytesAsclaimTexton 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
meansItTruththat this step means a destructive entry it names
popupTextthe 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
waitsTexta 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
namedTextwhat a report should call it, where the act and the locator will not do

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.

ReadingWhat it is about
anythingthe one value worth showing, whichever pattern the element happens to offer.
valuewhat a control holding a value says it holds, through its ValuePattern.
rangewhere a slider or a progress bar sits, as a number.
togglewhether a checkbox or a switch is on, off, or neither.
selectedwhether this element is the chosen one, which is a claim about the element.
pickedwhich one a container chose, which is a claim about the container and the pair to selected.
expandedwhether something that opens and closes is open.
textwhat a text-editing control holds, through its TextPattern; a label offers none and answers nothing.
namewhat the element announces itself as, which for a label is the words on it.
descriptionwhat the element says about itself beyond its name.
statusthe state the element publishes in words for a screen reader, such as Connected; empty where it publishes none.
enabledwhether the element would take an act at all.
focusedwhether 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 says what the application is launched with. Any case in the suite may name one declared in any file.

FieldHoldsWhat it is for
namerequiredTextwhat to call it, and what a case names to be launched against it
environmentTextthe sampled environment it is — the one field both the launch and the expectations read
flagTextthe argument the environment reaches the application through, without its value
argumentsWordseverything 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
variablesPairsthe 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
shareableTruththat this window may be lent to a case that only reads it
languageTextthe language tag the window it launches is in, so a derived set reads the strings that window is actually showing
residentTruththat 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
workingDirectoryTextthe 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
filesPlacedthe 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
opensTexta 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'
reachesWordswhat 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
beforeCommandscommands 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
applicationTextthe 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
unsetWordsthe 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
activatesTexta 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'
throughTextthe 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
windowOfTextthe 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.

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.

HoldsWhat a value may be
TextText.
TruthTrue or false.
WholeA whole number, written as a number and not as text.
WordsAn array of text.
PairsAn object whose every value is text.
CasesAn array of cases.
StepsAn array of steps.
FixturesAn array of fixtures.
PlacedAn array of files, each a path or an object naming where under the staged directory it lands.
CommandsAn array of commands, each an object naming the program to run, what to give it and the exit that says it worked.

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.

  • The verbs — everything act accepts, and what each one needs of the desk before it can answer.
  • Addressing an element — the grammar every locator field 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 act accepts, what each needs of the desk, and the reasoning behind each refusal.