[ AxiomCore · Documentation ]
Frontend tests
Test authored Acore UI behavior with axiom ui test, typed JSON suites, scripted operations, virtual time and explicit evidence levels.
axiom ui test checks an Acore UI module. Without a suite it runs a compiler
session smoke test. With --suite it runs scripted clicks, typing, navigation
and assertions against the application's semantics, with scripted backend
operations and virtual time.
Smoke test or application suite
| Command | What runs | Assertions |
|---|---|---|
axiom ui test main.acore | Compiler session smoke: initial load, a compatible edit, and keeping the last good build after an invalid edit | 3 session checks, 0 interaction assertions |
axiom ui test main.acore --suite FILE --layer reference | The resolved owner, data, component and routing semantics | Your suite's assertions |
axiom ui test main.acore --suite FILE --layer adapter | The maintained web input and button bindings, or the emitted native owner hook and runtime facade, on controlled targets | Your suite's assertions |
--layer needs --suite and defaults to reference. --target defaults to
ios, so pass --target web for the web adapter. --report FILE writes a
machine-readable report. A suite needs Node.js 24 or newer and a compiler and
runtime that support acore-application-test/v1 and acore-pure/v2.
Mounted interactions, OS keyboard behavior, accessibility trees and paint need separate target tests.
An executable counter
Save this as main.acore:
module examples.testing.counter.ui
page Home {
state count: Int = 0
action increment() {count = count + 1}
view {
Text(to_string(count))
Button(label: "Increment",on_press: increment)
}
}Save this as application.test.json:
{
"format": "acore-application-test/v1",
"name": "Counter",
"cases": [{
"name": "increment and cleanup",
"steps": [
{"do":"click","query":{"role":"button","name":"Increment"}},
{"do":"expect","query":{"role":"text","text":"1"},"count":1},
{"do":"dispose"},
{"do":"expect_clean"}
]
}]
}Run both layers:
axiom ui test main.acore --suite application.test.json --target web --layer reference --report reference.json
axiom ui test main.acore --suite application.test.json --target web --layer adapter --report adapter.jsonThe report names the suite kind, target, layer, assertion count, skipped and unsupported cases, failures, the visible semantic tree, requests and owner traces. Compilation alone does not satisfy the counter or cleanup assertions.
Suite format
A suite has format, name and 1 to 256 cases; the file is at most 4 MiB. A
case has a name, optional targets, a start path (default /), an
optional session, mocks and up to 4,096 steps.
Queries
Queries select nodes by role, name, label, text, id and within.
id accepts a compiler source semantic ID or a complete runtime instance ID;
within selects an ancestor instance. Repeated nodes need an unambiguous name
or instance, and a control step needs exactly one match. This controlled
semantic projection is not a native OS accessibility tree.
Steps
Step (do) | Fields | Effect |
|---|---|---|
click, blur, submit | query | Interact with one control. Clicks on disabled controls do not invoke actions. |
type | query, value | Enter text |
navigate | path | Go to a route |
back | None | Go back one route |
advance | ms | Run due callbacks in chronological order on the virtual clock |
respond | request, result or error | Answer a manual mock by zero-based request index |
publish | request, value | Send a typed stream value |
expect | query, optional count, text, value, disabled, invalid | Assert on the semantic tree |
expect_state | owner, path, value | Assert owned state |
expect_route | path | Assert the current route |
expect_requests | operation, count, optional args, cancelled | Assert operation calls |
expect_trace | kind, count | Assert trace events |
dispose, expect_clean | None | Dispose the app, then assert no active owner, request or timer remains |
Unsupported adapter controls or capabilities fail explicitly; they never count as coverage.
Scripted operations
Each case may declare up to 4,096 mocks. A mock names a qualified locked
operation, optional exact args, and exactly one typed result or error.
- A legacy error has a declared
codeand a Stringmessage. - For an operation with selected failure cases, use
"error": {"code": "titleTaken", "payload": {"message": "Choose another title"}}. The runner validates the payload against that public case and rejects unknown cases, fields or malformed values. delayMsschedules the response in virtual time.manual: truewaits for arespondstep.- Unexpected calls, undeclared errors, wrong arguments or results, and unused mocks fail the case.
- Scripts never grant transport or extension authority to the application.
Time and failure rules
Real sleep never advances the controlled clock. A failed setup, a failed assertion, an unsupported case, a suite with no executed cases or a case with no assertions cannot pass. Disposal must leave no active owner, request or timer. The worker has a 60-second limit, including synchronously blocked adapters.
Logical-submission suites use a secure host key hook in the reference and generated-adapter runners. Business randomness and credentials are not replaced by seeded test values, and the transport stays scripted, so this does not show server admission durability.
Evidence in the report
Application reports include per-case cleanup counts and an executionEvidence
envelope in axiom-development-execution-evidence/v1. It binds the canonical
frontend IR, the suite, runner, assertion API, adapter and facade hashes, and
records nonzero assertions and cleanup. Native scenario
reports use the same envelope with their contract, plan, schema and scenario
identities.
| Level | Shows | Does not show |
|---|---|---|
| Compiler smoke | The module loads, accepts a compatible edit and keeps the last good build | Any interaction |
Application suite, reference or adapter | Scripted behavior and cleanup at compiler-host level | Mounted browser, emulator or simulator behavior |
Scenario (compiler-host) | Compiled native managed queries with strict synthetic read replies and logical policy time | Frontend interactions (it rejects interaction and state controls) |
| Mounted target, durable provider or actual guest | Real execution | Must keep evidence from its own executing adapter |
Owned-operation tests use controlled adapter futures to prove overlap and drain, and a virtual clock to prove timer ownership, foreground pause, session invalidation and suppression of stale completion. Durable job recovery needs separate Redis and worker evidence. See Managed logic and concurrency.
Limits and compatibility
- Existing CI can keep the smoke command. To claim behavior, add a suite and keep its JSON report. Do not label adapter results as mounted native qualification.
- The web binding adapter covers buttons and text inputs; forms can submit through an authored submit button. Element methods, extensions and other OS surfaces need an explicit capability or a mounted target adapter.
- Application tests use frontend IR
axiom-ui-ir/v10andacore-ui/v6, and needacore-pure/v2alongside the reactive, component, data and application helper formats. Use a CLI, UI hosts and playground from the same release. - Pure value validation is bounded to 1,000,000 JSON nodes or keys, depth 128 and 10,000 Unicode scalars per String. Expression fuel stays 10,000. Larger transport values do not increase computation fuel or grant custom callbacks.
List.slice(start, end)uses an exclusive end, clamps indices, accepts negative indices from the end and returns an empty list when the end precedes the start.
Next steps
- Learn the UI language in Acore UI.
- Run a UI against a contract mock.
- Add
axiom ui testto the CI gate.