Testing
A form changes when its site does, and a label that matched last month may not match today. Check a script’s targets two ways: with Check Custom Parts on the live page, before anything is typed, and offline in a Deno test against saved copies of the page, with no browser at all.
Check Custom Parts
One question, before a form script runs for real: does every target it uses, and every part fill and clear need, still match the live page? Run SDK: Check Custom Parts with the
script open. The script runs, but each fill, clear, upload, click and press checks its
target on the live page and logs what it would do, instead of doing it:
[check] would fill { label: "Name" } ("Name", text) ← "Ada Lovelace"
[check] would fill { label: "Email" } ("Email", text) ← "ada@example.com"
[check] would fill { label: "Location" } ("Location", select) ← "Lisbon, Portugal" - Only those five SDK form methods change. Everything else in the script runs as usual: reads
(
fields,text,attr,waitFor), navigation and opening tabs, page code (tab.evaluate),ui.confirm, files and network. Use it on a script that fills a form; a script that does more does that too. - A missing target fails the same way it would for real, naming the closest label, which is usually all you need to fix it.
- A dropdown’s parts are checked too: its shown value, what opens it, and its clear button when
fillorclearwould empty it, through the script’s custom parts map if it has one. A partfillorclearwon’t find is listed as missing, with the field’s markup to write a selector from. On a form built to web standards there’s nothing to list. - It checks the page as it is now. A step that needs an earlier write to have happened (the next page of a form, after Next) isn’t reached.
- The action log records each of those five calls with the outcome
checked.
Save the page
Offline tests run against the page’s HTML. Save it before any input, plus any other state of the
form worth testing, such as the page after a failed submit (apply.errors.html). This script saves
the current page (it needs page-js to read the page’s markup):
// Tranquil automation. Rules for writing and changing it: ~/.tranquil/llm/writing-automations.md
// @permissions page-js write=apply.html sites=jobs.example.com
import { files, tabs } from "tranquil/automation";
const tab = await tabs.active();
files.write("apply.html", await tab.evaluate((page) => page.document.documentElement.outerHTML)); Test targets offline
tranquil/automation/testing runs the SDK’s own field resolver over saved HTML, in an ordinary
Deno test. These are its three helpers:
| Function | Returns | Use it to |
|---|---|---|
fieldsOf(html, { within?, customParts? }) | Field[] | List the fields tab.fields() would report for this HTML. |
fieldOf(html, target, { customParts? }) | Field | Resolve one target to its field, or throw the FieldError fill would throw. |
checkTargets(targets, pages, { customParts? }) | one result per target and page | Check a script’s targets against several saved states at once; each result says whether it matched (ok), and why not. |
customParts takes the script’s custom parts map (one, or a list), so a
test reads a form that isn’t built to standards the way the script does.
import { checkTargets, fieldOf, fieldsOf } from "tranquil/automation/testing";
const html = await Deno.readTextFile(new URL("apply.html", import.meta.url));
console.log(fieldsOf(html).map((f) => `${f.label} (${f.kind})`));
console.log(fieldOf(html, { label: "Email" }).kind);
const errors = await Deno.readTextFile(new URL("apply.errors.html", import.meta.url));
const problems = checkTargets([{ label: "Name" }, { label: "Email" }], { clean: html, errors }).filter((c) => !c.ok);
console.log(problems); Offline resolution is structural, so it differs from the live page in two ways:
- No layout. Visibility comes only from the
hiddenattribute and an inlinedisplay: none, so an element hidden by a stylesheet still counts as visible. - Values are what the HTML says. A value a page’s script would fill in after load isn’t there.
A test file isn’t an automation: it runs with deno test, needs no @permissions header, and
doesn’t start with the rules line. Point deno test at Tranquil’s config so the short names resolve:
deno test --config ~/.tranquil/automations/deno.json --allow-read apply_test.ts