verify_feature
Every option and step the verify_feature tool accepts, what each expect asserts, and what the tool returns. Formerly record_demo, and the old name still works.
This page lists what your agent can send when it writes the steps of a test
itself. Everything on it works on every version of tada-mcp unless a line
says “from 0.1.62”. From 0.1.62 the same tool is also listed as
check_feature, and verify_feature keeps working with every option below.
verify_feature is the tool your coding agent calls to check a feature. A
check is one go at one feature, and the tests of that feature are what the
agent sends: steps for one test, journeys for several. Tada walks them in a
real browser on its own machines, checks each expect, and returns a verdict.
A pass has one film, with a chapter per test. It was called record_demo until
2026-08-24, and that name still works.
Options
| Option | Type | Default | What it does |
|---|---|---|---|
title | string, 1 to 200 characters | required | What the check is called, shown on the watch page. It names the feature when feature is left out. |
steps | array, at least one | required unless journeys or a feature alone is sent | The ordered steps of one test. |
journeys | up to 24 of { name, steps } | none | The tests of a planned feature, named as the plan names them, each with its steps. Sent with feature set to the id plan_feature returned. The parameter keeps the name journeys from earlier versions; each entry is one test. |
feature | string | the title | The feature the check is for, named as the ticket names it. With no steps and no journeys, it checks that feature’s saved tests again. |
repo | owner/name | the git remote of the folder the server started in | The app the check is for. |
device | desktop, tablet or mobile | desktop | The screen size: 1920×1080, 820×1180 or 390×844. |
initScript | string | none | JavaScript that runs in every page before the app’s own scripts, to set a locale or a feature flag. |
hideSelectors | up to 20 CSS selectors | none | Hides your app’s own floating widgets, such as a chat bubble or cookie banner, for the whole film. A step that touches a hidden element fails the check and names the selector. |
captureScale | 1 or 2 | 1 | Use 2 only when the film will be re-cut as marketing video. It costs four times the pixels and cannot be changed after the check. |
From 0.1.62, three more options ask with a goal in place of steps. See MCP server.
| Option | Type | Default | What it does |
|---|---|---|---|
goal | string, up to 240 characters | none | What should work, in a sentence or two. Tada writes the test. Sent with repo, and without steps or title. |
role | string | none | With goal: the role to sign in as, in your app’s own word. |
address | URL | the app’s usual address | With goal: where to check. An address on localhost is reached through a temporary tunnel. |
Use mobile only for features built for phones. A desktop app filmed 390
pixels wide shows your breakpoints, not your feature.
Options that refuse the check
storageState, width, height and taskId are still in the schema so older
agent setups do not break. Passing one refuses the check before anything starts
or counts, and the answer says what to use instead:
| Option | Use instead |
|---|---|
storageState | Sign in inside the steps. See Keep a sign-in off camera. |
width, height | device. |
taskId | Leave it out. It is not supported. |
dryRun is accepted and ignored. The check happens on Tada and is published
like any other, and the answer says so.
Steps
Every step is { "action": ..., ...fields }. Steps may also carry a role, to put several people from the app’s cast in one test.
Move and act
| Action | Fields | Notes |
|---|---|---|
goto | url | Opens a page. Accepts ${magicLink} from an earlier awaitEmail. |
click | text or selector | Prefer text. Visible labels survive a refactor that breaks CSS selectors. |
fill | text (the label) or selector, value | value accepts ${inbox}. |
select | text or selector, value | Picks an option from a dropdown, including custom ones. |
upload | text or selector, value | Attaches a sample file. See Attach a file. |
press | key | For example "Enter". |
hover | text or selector | |
scroll | value or selector, ms | value is "top", "bottom" or a pixel offset. ms sets how long the scroll takes. |
drag | text or selector, then toText or toSelector, native | See Drag and drop. |
wait | ms | Holds still. |
waitForText | text | Waits for words to appear. It is not a check. |
Check
| Action | Fields | Passes when |
|---|---|---|
expect | text | Those words become visible. |
expect | urlIncludes | The address contains that text, for example "/library". |
expect | selector | An element matching the selector becomes visible. |
expect | signedIn: true | The page is past the sign-in: not a sign-in address, and no password box showing. |
An expect waits up to 8 seconds. Give a slow step more with ms:
{ "action": "expect", "text": "Export complete", "ms": 30000 }
Only expect decides the verdict. wait and waitForText set the pace and
prove nothing. Put an expect after every step that matters.
Narrate
| Action | Fields | Notes |
|---|---|---|
caption | text, ms | A line of narration for the next action. |
A caption holds the frame for as long as it takes to say, up to 12 seconds, so keep each one to a short sentence. Whether captions show and whether they are spoken come from Settings > Narration and captions, and the voice from the actor in the app’s Settings > Cast, not from the agent.
Keep a sign-in off camera
pause and resume take no fields. Between them the browser performs every
step, and nothing is filmed.
[
{ "action": "pause" },
{ "action": "goto", "url": "https://staging.example.com/login" },
{ "action": "fill", "text": "Email", "value": "{{secret.TADA_TEST_EMAIL}}" },
{ "action": "fill", "text": "Password", "value": "{{secret.TADA_TEST_PASSWORD}}" },
{ "action": "click", "text": "Sign in" },
{ "action": "expect", "signedIn": true },
{ "action": "resume" }
]
Email steps
| Action | Fields | Notes |
|---|---|---|
createInbox | none | Makes a throwaway address. Use it as ${inbox} in a later fill. |
awaitEmail | text, ms, link | Waits for an email. text matches part of the subject. link picks which link becomes ${magicLink}: words on its button, such as Review and sign, or part of its address. Waits up to 20 seconds by default. |
After awaitEmail, the link in the email is ${magicLink}:
[
{ "action": "createInbox" },
{ "action": "fill", "text": "Email", "value": "${inbox}" },
{ "action": "click", "text": "Send me a link" },
{ "action": "awaitEmail", "text": "Sign in to" },
{ "action": "goto", "url": "${magicLink}" },
{ "action": "expect", "urlIncludes": "/dashboard" }
]
Without link, Tada takes the sign-in link, then an invitation’s link, then a signing link, and skips logos, unsubscribe links and social links. A link that matches nothing in the email fails the check.
An email that never arrives fails the check.
Attach a file
{ "action": "upload", "text": "Attach invoice", "value": "invoice.pdf" }
text or selector names the file field, or the button or drop area that
takes the file. value names the file. Tada finds the file field behind a
label or a drop area and sets it. When there is no field, it presses the
control and answers the file picker that opens.
The file is always one of Tada’s own sample files, named exactly:
value | What it is |
|---|---|
invoice.pdf | A one-page sample invoice. |
picture.png | A small picture. |
photo.jpg | The same picture as a photo. |
contacts.csv | A table with four made-up rows. |
notes.txt | A few lines of plain text. |
letter.docx | A short Word letter. |
Everything in them is made up. A path or an address is refused, because the check happens on Tada, which cannot see the files on your machine. Any other name fails the step and lists the names that work.
A press that opens no file picker, on a page with no file field, blocks the
step and says so. Always follow an upload with an expect for what your app
shows once it has the file, such as the file’s name.
Drag and drop
{ "action": "drag", "text": "My card", "toText": "In Progress" }
Tada drags with the pointer first, which is what dnd-kit and
react-beautiful-dnd listen for. When the source element is
draggable="true", it uses the browser’s own drag events instead. Set
native: true only if that detection misses. Always follow a drag with an
expect that the item landed.
What comes back
Up to 0.1.61 the answer is as below. From 0.1.62 it is the check’s report in the order the app shows it: the result, one line that sums it up, what failed, what was not covered, the link to the report, and the film’s link last. See MCP server.
Passed. The watch link to the film, and a line saying it passed. Notes follow when something needs your attention, such as a tunnel Tada opened. See Check what your coding agent builds.
Failed. No link and no film. Every step that failed the check, by step
number and action, with what the screen showed, and the check’s page, where the
failing frame is. The agent receives it as a tool error. A planned feature
answers Passed: <feature>. Every test passed. or Failed: <feature>. with
the same page, and does not list the tests one by one.
Not started. Not signed in, a refused option, or a check the server would not start. Each says why, and nothing is checked.
If Tada is still working after 16 minutes, the tool stops waiting and returns the check’s page. The check finishes on its own.