AI prompt examples
Seven worked examples of prompts that produce good AutoTestX tests. Each one shows the prompt a tester writes, the test that came out, and what to notice. Every resulting test passes validation and passes when run. Use them as models for your own prompts.
How to write a prompt and which option to choose is explained in the Prompt guide. In short:
- Option A: Write with AI: plain-language steps typed into the editor. Examples 1 and 2.
- Option B: an AI chat (Claude or similar), using the guide’s master prompt. It writes a complete test file you import. Examples 3 to 7.
| # | Option | What it shows |
|---|---|---|
| 1 | A | A negative check: sign-in refused |
| 2 | A | A validation message, and a check that nothing was created |
| 3 | B | A loop over order lines, a remembered value, a check on a list row |
| 4 | B | A manual test case converted, rows found by PO number |
| 5 | B | Data-driven: six rows of good and bad credentials |
| 6 | B | Waiting for the page state instead of a fixed wait |
| 7 | B | An API test with a negative case and cleanup |
The examples use the bundled fixture application (a small purchase-order screen) and
the demo API, so you can try them yourself. The test files are in the repository under
examples/ai-prompts/; see Trying the examples.
Example 1: a refused sign-in
Section titled “Example 1: a refused sign-in”Option A. Typed into Write with AI → Describe the test:
Open the application.Type {{secret.APP_USER}} into the "Username" field.Type wrong-password into the "Password" field.Click the "Sign In" button.Check that the alert shows exactly "Invalid credentials".Check that the "Sign In" button is still visible.Check that the heading "Purchase Orders" is not visible.Result: 7 steps.
| Step | Type | Target |
|---|---|---|
| Open the application | Open | {{baseUrl}} |
| Type the test user into Username | Type (sensitive) | label Username |
| Type a wrong password into Password | Type (sensitive) | label Password |
| Click Sign In | Click | test id login, then button Sign In |
| The alert says exactly Invalid credentials | Text | role alert |
| The Sign In button is still visible | Visible | test id login |
| The Purchase Orders heading is not visible | Visible, negated | heading Purchase Orders |
Notice: the expected message is given exactly, and two more checks prove the user was not let in. “Check that login fails” would produce a weak test.
Example 2: a validation message
Section titled “Example 2: a validation message”Option A:
Open the application and sign in with APP_USER and APP_PASSWORD.Click the "New Purchase Order" button.Select "Globex" in the "Vendor" dropdown.Type "Order with no lines" into the "Description" field.Click "Save" without adding any line.Check that the status message says exactly "Add at least one line before saving".Remember the text of the PO number (test id po-number) as poNumber.Check that poNumber is empty.Take a screenshot named "save-refused".Result: 13 steps. The last checks are:
| Step | Type | Detail |
|---|---|---|
| The message says a line is needed | Text | role status, expected Add at least one line before saving |
| Read the PO number field | Extract | test id po-number, saved as poNumber |
| No PO number was generated | Expression | LENGTH(poNumber) == 0 |
| Screenshot of the refused save | Screenshot | save-refused |
Notice: “check that nothing was created” is the half of a negative test people forget. A Text check cannot expect an empty value, so the prompt says remember … then check it is empty, which becomes an Extract and an Expression step.
Example 3: a loop and a list row
Section titled “Example 3: a loop and a list row”Option B. The guide’s master prompt, with its The test to write section filled in like this:
## The test to writeApplication / screen: Fixture ERP - Purchase OrdersEnvironment variables available: baseUrlSecret names available: APP_USER, APP_PASSWORDPreconditions: none; the test signs in itself.
Requirement:As a buyer I can create a purchase order with several lines. On save the systemshows "Saved", generates a number PO-<digits>, sets status Approved, and theorder appears in the Purchase Orders list with status Approved.
Screen details:<section aria-label="Create Purchase Order"> <label for="vendor_7731">Vendor</label> <select id="vendor_7731"> Acme Ltd | Globex <label for="desc_7732">Description</label> <input id="desc_7732"> <table> Lines: rows with cells Item | Quantity </table> <label>Item</label> <input> <label>Quantity</label> <input type="number"> <button data-testid="add-line">Add Item</button> <button data-testid="save-po">Save</button> <div role="status"> (shows "Saved") </div> <span data-testid="po-number"></span> <span data-testid="po-status"></span></section>List: <table role="grid" aria-label="Purchase order list">, one row per order:PO Number | Vendor | Status | Actions
Test data: vendor Acme Ltd; lines 1234 x 2, 5678 x 1, 7002 x 4 - put the lines in variables and add them with control.forEach. - after each Add Item, check the line's row shows the quantity.Expected results: as in the requirement. Status check is soft.Values to remember: the PO number as poNumber.Cleanup needed: none (the fixture resets on reload).Result: a test with the lines as a variable and a For each loop. Its key parts:
"variables": { "lines": [ { "item": "1234", "qty": "2" }, { "item": "5678", "qty": "1" }, { "item": "7002", "qty": "4" } ]},...{ "id": "line-listed", "type": "assert.text", "label": "The line's row shows the quantity", "target": { "scope": { "within": { "role": "row", "key": "{{line.item}}" } }, "strategies": [{ "kind": "css", "value": "td:nth-child(2)" }] }, "params": { "expected": "{{line.qty}}" }},...{ "id": "in-list", "type": "assert.exists", "label": "The new order's row in the list says Approved", "target": { "scope": { "within": { "role": "row", "key": "{{poNumber}}" } }, "strategies": [{ "kind": "text", "value": "Approved" }] }}Notice: the pasted HTML gives the AI real test ids and labels, so no target is a guess. The list check finds the row by its PO number, never “the last row”.
Example 4: converting a manual test case
Section titled “Example 4: converting a manual test case”Option B. The master prompt plus the guide’s convert manual test cases variant, with the manual case pasted in:
TC-2001 Approve and delete purchase orders from the listPrecondition: orders PO-100001 (Acme Ltd, Draft) and PO-100002 (Globex, Draft) exist.| # | Step | Expected result ||---|---------------------------------------|-----------------------------------------|| 1 | Open the application | Sign-in screen is shown || 2 | Sign in as the test user | Purchase order list is shown || 3 | Click Approve on PO-100001 | PO-100001 status becomes Approved || 4 | Click Delete on PO-100002 | PO-100002 is removed from the list || 5 | - | PO-100001 is still listed, Approved |
Screen details: each list row is <tr role="row"> with cellsPO Number | Vendor | <td class="status"> | <button class="row-approve">Approve</button><button class="row-delete">Delete</button>.Find rows by PO number, never by position. Check each precondition before acting on it.Result: a test named TC-2001 Approve and delete purchase orders from the list,
tagged TC-2001, whose step labels keep the manual numbering:
Step 1: Open the applicationStep 2: Enter username / Enter password / Sign inExpected 2: The Purchase Orders list is shownPrecondition: the order to approve is DraftStep 3: Click Approve on that order's rowExpected 3: Its status is ApprovedPrecondition: the order to delete is listedStep 4: Click Delete on the other order's rowExpected 4: That order is no longer in the listExpected 5: The approved order is still listed as ApprovedNotice: the run report maps straight back to TC-2001. Every negative check (“is no longer in the list”) has a positive check before it (“the order to delete is listed”). Without that, a wrong target would make the negative check pass for the wrong reason.
Example 5: data-driven
Section titled “Example 5: data-driven”Option B. The master prompt plus the data-driven variant:
## The test to writeApplication / screen: Fixture ERP - Sign inRequirement: only user "tester" with password "correct horse" can sign in.Any other combination shows the alert "Invalid credentials" and stays on Sign in.Screen: <label>Username</label><input> <label>Password</label><input type=password><button data-testid="login">Sign In</button> <p role="alert">Invalid credentials</p>After sign-in: heading "Purchase Orders".
Run once per row (inline data, independent rows, password column sensitive).Columns: case, username, password, should_pass (yes/no).Rows: the valid user; wrong password; unknown user; password in different case;empty password; username with surrounding spaces.Use control.if on data.should_pass to check success or the error.Result: one set of steps, six rows, and an If / Else on the outcome:
"data": { "source": { "kind": "inline", "columns": ["case", "username", "password", "should_pass"], "rows": [ { "case": "valid user", "username": "tester", "password": "correct horse", "should_pass": "yes" }, { "case": "wrong password", "username": "tester", "password": "wrong", "should_pass": "no" }, ... ] }, "independent": true, "sensitiveColumns": ["password"]},...{ "id": "outcome", "type": "control.if", "label": "Check the outcome the row expects", "params": { "condition": "data.should_pass == 'yes'" }, "children": [ /* the Purchase Orders list is shown */ ], "orElse": [ /* the alert says Invalid credentials; the list is not shown */ ]}Notice: the case column names each row in the run report. These passwords sit in
the data table only because they are deliberately fake; real credentials always stay in
secrets. See Data-driven testing.
Example 6: waiting without a fixed wait
Section titled “Example 6: waiting without a fixed wait”Option B:
## The test to writeOpen {{baseUrl}}#slow. The "Processing" heading appears with the text "Loading...".After a variable time (up to ~10 s), "Loading..." disappears and "Completed" is shown.Check Completed is shown. Do NOT use a fixed wait: wait for "Loading..." to be hidden,with a step timeout of 15000 ms.Result:
| Step | Type | Detail |
|---|---|---|
| Open the processing panel | Open | {{baseUrl}}#slow |
| The Processing heading is shown | Visible | heading Processing |
| Wait until Loading… disappears | Wait for element | text Loading…, state hidden, timeout 15000 ms |
| Completed is shown | Visible | text Completed |
Notice: naming the signal to wait for (“Loading…” disappears) is what lets the AI choose Wait for element instead of Wait. The test waits as long as the page needs and no longer.
Example 7: an API test with cleanup
Section titled “Example 7: an API test with cleanup”Option B. The master prompt plus the API variant:
## The test to writeAPI base: {{apiUrl}}. Secrets: API_USER, API_PASSWORD.Auth: POST {{apiUrl}}/auth/token {"username","password"} -> 200 {"token","expiresIn"}.Send "Authorization: Bearer <token>" on every other call.
Endpoints:POST /vendors {"name","email"} -> 201 {"id":"V-1001","name","email","status":"Active"}POST /orders {"vendorId","lines":[{"item","qty"}]} -> 201 {"id":"PO-1002","vendorId","lines":[{"item","qty","unitPrice","amount"}],"total","status":"Draft"} 422 {"error":"qty must be a positive whole number"} when qty <= 0 or not wholeGET /orders/{id} -> 200; 401 without a tokenDELETE /orders/{id}, DELETE /vendors/{id} -> 204Catalog prices: 1234 = 24.50, 5678 = 89.00
Cover: create a vendor with a unique name; create an order with 1234 x 2 and 5678 x 1;check id prefix, status Draft, vendor, 2 lines and total 138; response under 800 ms(soft); quantity 0 is rejected with the exact error; reading without a token gives 401.Cleanup: delete the order and the vendor, accept 204 or 404, continue on error.Result: 11 steps and 2 cleanup steps.
| Step | Type | Detail |
|---|---|---|
| Give this run’s vendor a unique name | Set variable | CONCAT('AI Vendor ', NOW()) |
| Sign in to the API | HTTP request (sensitive) | POST /auth/token, expect 200, saved as auth |
| Create the vendor the order needs | HTTP request | POST /vendors, expect 201 |
| Create an order with two lines | HTTP request | POST /orders, expect 201, saved as order |
| The order is a Draft PO for this vendor | Expression | STARTS_WITH(order.body.id, 'PO-') AND order.body.status == 'Draft' AND … |
| The order has 2 lines | Expression | COUNT(order.body.lines) == 2 |
| The total is 2 x 24.50 + 1 x 89.00 = 138.00 | Expression | order.body.total == 138 |
| Creating the order took under 800 ms | Expression (soft) | order.durationMs < 800 |
| Try an order line with quantity 0 | HTTP request | expect 422 |
| The error explains the quantity rule | Expression | badQty.body.error == 'qty must be a positive whole number' |
| Reading the order without a token is refused | HTTP request | GET, expect 401 |
| Cleanup: Delete the order / Delete the vendor | HTTP request | expect 204,404, on error Continue |
Notice: the expected total is worked out in the prompt (2 × 24.50 + 89.00 = 138), so the AI asserts a number instead of “a total exists”. Cleanup runs even when a check fails. See API testing and Error handling.
What validating and running caught
Section titled “What validating and running caught”The first drafts of these tests had four mistakes, and they are typical of what an AI
writes. All four were caught by atx validate or by the first run, which is why you
should always do both before saving a test:
| Mistake in the draft | Caught by | Fix |
|---|---|---|
| A Text check with an empty expected value | atx validate: expected must contain at least 1 character |
Extract the value, then Expression LENGTH(x) == 0 |
{{line.qty}} inside a target strategy’s value |
Run: Tried text=“{{line.qty}}” -> 0 | Strategy values are literal text. Templates work in step parameters and in the row scope’s key. |
| A “no longer exists” check passed with a wrong target | Review: the target never matched anything | Check the element exists first, then that it is gone |
css: td inside a row matched 4 cells |
Run: css=td -> 4 | A target must match exactly one element: td:first-child, td.status |
The guide’s master prompt now tells the AI each of these rules, so you should not meet them. They are still worth knowing when you review AI-written tests.
Trying the examples
Section titled “Trying the examples”The seven test files are in the repository under examples/ai-prompts/:
| # | File | Environment |
|---|---|---|
| 1 | 01-login-wrong-password.json |
examples/fixture.env.json |
| 2 | 02-save-without-lines.json |
examples/fixture.env.json |
| 3 | 03-create-po-with-lines.json |
examples/fixture.env.json |
| 4 | 04-manual-case-row-actions.json |
examples/fixture.env.json |
| 5 | 05-login-data-driven.json |
examples/fixture.env.json |
| 6 | 06-wait-for-processing.json |
examples/fixture.env.json |
| 7 | 07-api-create-order.json |
examples/api-demo/environment.json, after npm run demo:api |
Run one from the command line:
npx atx run examples/ai-prompts/03-create-po-with-lines.json --env examples/fixture.env.json --headedexamples/fixture.env.json holds an absolute file:// path to the fixture application;
change its baseUrl to match where the repository is on your machine. To work with an
example in the editor, import it (see Importing a test file)
and create an environment with the same baseUrl and secrets.