Hooks
Run your own scripts before, during or after a test to prepare data, call APIs and check side effects, inside a sandbox.
Some things are faster and more reliable outside the UI: creating a fresh account, seeding an order, reading a database row, cleaning up afterwards. Hooks are your own scripts that agent-qa runs at fixed points in a test, in a Docker sandbox.
Hooks need Docker running. npx agent-qa doctor checks it in its Docker row.
Define a hook
Hooks live in hooks.yaml, the file workspace.hooksFile names:
hooks:
- id: h_phi-arts-vis-band-aira-streng-twig-roi-mino-fold
name: Make a shipping address
runtime: node
file: scripts/examples/make-shipping-address.mjs
timeout: 30s
network: falseruntime is node, bun, python or bash. The hooks file reference lists every field. New hooks get their ID from the dashboard's hook editor or from your coding agent through agent_qa_change_config.
Pass values to the test
A hook hands values to the test by writing KEY=value lines to /tmp/agent-qa.env. Here is the example init writes, and the same hook in each runtime:
// scripts/examples/make-shipping-address.mjs
import { writeFile } from 'node:fs/promises'
import { randomInt } from 'node:crypto'
const address = `${randomInt(100, 1000)} Cedar Lane`
await writeFile('/tmp/agent-qa.env', `SHIPPING_ADDRESS="${address}"\n`, 'utf-8')// scripts/examples/make-shipping-address.ts
const address = `${100 + Math.floor(Math.random() * 900)} Cedar Lane`
await Bun.write('/tmp/agent-qa.env', `SHIPPING_ADDRESS="${address}"\n`)# scripts/examples/make_shipping_address.py
import random
address = f"{random.randint(100, 999)} Cedar Lane"
with open("/tmp/agent-qa.env", "w") as env:
env.write(f'SHIPPING_ADDRESS="{address}"\n')# scripts/examples/make-shipping-address.sh
echo "SHIPPING_ADDRESS=\"$((RANDOM % 900 + 100)) Cedar Lane\"" > /tmp/agent-qa.envThe test reads them as {{env:KEY}}:
setup:
- h_phi-arts-vis-band-aira-streng-twig-roi-mino-fold
steps:
- Type {{env:SHIPPING_ADDRESS}} into the Street address field, then continue to delivery.When hooks run
| Where | When |
|---|---|
setup: in a test | Before the first step. If it fails, the test fails. |
teardown: in a test | After the last step, whether the test passed or not. A failing teardown hook does not change the verdict. |
setup: and teardown: in a suite | Once around the whole suite. Values from a suite setup hook reach every test. |
{{runHook:"h_…"}} in a step | In the middle of the test, before that step runs. Use it to check a side effect, then verify the value in the next step. |
Setup hooks run in order. When one fails, the rest are skipped and the test, or the suite, fails.
The sandbox
Each hook runs in a fresh container from vostride/agent-qa-hook-runner-<runtime>. The first run pulls the image, so it takes longer.
- 512 MB of memory, one CPU and up to 256 processes, on a read-only file system.
- A working folder at
/workspacethat holds only the hook file, the files listed indepsand thepackageFile, side by side. Your project is not mounted. /tmpis writable.- No network when
network: false. Without the field, the hook has network access. - Nothing is installed. A hook can use its runtime's standard library and the files you copy in, so bundle any packages it needs into those files.
- A hook that runs past its
timeoutfails.
The container receives the run's variables, the secrets from your secrets file except those a remote, plugin or device uses to connect, and the active saved login when there is one. Values a hook writes that match a secret are dropped, and its output is masked like everything else.
Example: check a side effect
steps:
- Place the order and note the order number shown.
- Look up the order through the API {{runHook:"h_shy-oak-tide-rim-ash-fog-ivy-elm-bay-ore"}}.
- Verify ORDER_FOUND is exactly "true"; the current value is "{{env:ORDER_FOUND}}".The hook queries your API and writes ORDER_FOUND=true or false. The next step checks it, so a backend bug fails the test even when the UI looks right.