Saved tests
A flow an agent has tested can be kept as a saved test: a script plus a few details (name, ticket, environment, credentials it uses, record mode). Any agent can replay it, you can watch it, and CI can run it.
Where tests live
- Project tests are saved in
.astral/tests/<id>.astral.jsinside your project and versioned with your repository. The id is a slug of the test's name. - Temporary tests are kept in Astral's data folder and expire after a number of days (7 by default).
The agent saves one with test_save and can link it to a ticket. A saved test replays the script, not the model, so a replay spends no model tokens and runs the same steps every time.
return await astral.test('Login', async () => {
await astral.step('Open the login page', () => astral.navigate({url: astral.url('/login')}));
await astral.step('Sign in', async () => {
await astral.fill({locator: {role: 'textbox', name: 'Email'}, credential: 'shop.demo', field: 'username'});
await astral.fill({locator: {role: 'textbox', name: 'Password', exact: true}, credential: 'shop.demo', field: 'password'});
await astral.click({locator: {role: 'button', name: 'Sign in'}});
await astral.expect.url('/account');
});
}, {record: 'actions'}); .astral/config.json
The project's .astral/config.json declares the environments your tests run against and the credentials they may use, by name. Tests navigate with astral.url('/login'), so the same test runs against local, staging or production.
{
"project": "shop",
"envs": {
"local": { "baseUrl": "http://localhost:3000" },
"staging": { "baseUrl": "https://staging.example.com" },
"prod": { "baseUrl": "https://www.example.com", "protected": true }
},
"defaultEnv": "local",
"collections": {
"smoke": { "name": "Smoke", "tests": ["login", "checkout"] }
},
"credentials": {
"shop.demo": {
"username": "[email protected]",
"password": { "keychain": "shop.demo.password" }
}
}
} | Key | What it is |
|---|---|
envs | Named base URLs. protected: true marks an environment such as production. No user name or password in the URL. |
defaultEnv | The environment used when none is given. |
collections | Tests run in order as one group, with a pass or fail summary and a report linking each test's. |
credentials | Named logins: a username, a password, a one-time-code seed and extra fields. Secrets are references, never values. |
layout | Selectors the layout check ignores and its thresholds. |
Credentials and the keychain
A secret in config.json is a reference to where it lives, never the value. A plain-text password in the file is rejected. A reference is one of:
{ "keychain": "<id>" }
{ "env": "ASTRAL_TEST_<NAME>" }
{ "local": "<key>" } | Reference | Where the value is |
|---|---|
keychain | Your system keychain. You set it in Astral; an agent can never read it back. |
env | An environment variable whose name starts with ASTRAL_TEST_. |
local | .astral/secrets.local.json, which must be in .gitignore. Astral refuses to create it otherwise and warns if it already exists unignored. |
- The agent only ever sees the credential's name and username.
astral.fillresolves the value itself and types it with real key strokes; the agent only learns that it was typed and masked. - A secret is typed only into a sensitive field (password, one-time code, card) on the environment's own origin, so it cannot be read back or sent to another site. Reports show •••••• in its place.
- A keychain secret is stored together with the environments' origins at that moment and only typed into those, even if
config.jsonis edited later. For sensitive logins, prefer the keychain.
Protected environments
On an environment marked protected (production, say), only tests declared readOnly run, and only after a person approves that exact version of the test in Astral. An agent can ask for readOnly: true, but that is a request, not an approval: you approve it with Approve as read-only in the Tests panel, the approval is kept outside the project, and it is lost if the test is edited. In a collection, tests that are not approved are skipped with the reason.
Viewports
A test or a collection can run at one or several viewports, and Astral runs every test at each one.
| Preset | Size (CSS px) | Pixel ratio | Touch |
|---|---|---|---|
mobile | 390 × 844 | 3 | yes |
tablet | 820 × 1180 | 2 | yes |
desktop | 1440 × 900 | 1 | no |
A custom viewport has a width and a height from 200 to 4096 px, and optionally a pixel ratio, touch, a user agent and a name. Inside Astral the tab's panel is resized to the viewport for the run and given back its own size afterwards; pixel ratio, touch and user agent are not emulated there, and the run says so. The headless runner with Chrome emulates all of them.
The layout check
The layout check looks at the page as it is rendered and reports findings, each with a rule, a severity, a selector, a rectangle, a message and a suggested CSS fix.
| Rule | What it finds |
|---|---|
horizontal-overflow | The page is wider than the viewport; it names the element that escapes. |
overlap | Visible text or controls on top of each other. |
text-clipped | Text cut by overflow, or a long word spilling out of its box. |
offscreen | A control outside the horizontal viewport. |
tap-target | A touch target under 24 × 24 px is an error; under 44 × 44, a warning. Touch viewports only. |
small-text | Text under 12 px on screens up to 600 px wide. |
Three ways to use it:
- The
layout_checktool: the agent finds a problem, fixes the CSS and checks again. astral.expect.layout()as a step of a test.layoutCheck: trueon a test or collection, to add a check at the end of each viewport run.
{
"collections": {
"rwd": { "name": "Responsive", "tests": ["home", "checkout"], "viewports": ["mobile", "tablet", "desktop"], "layoutCheck": true }
},
"layout": { "ignore": [".chat-widget", "#cookie-banner"] }
} With a screenshot, the page is captured with every finding outlined. On an engine that does not lay pages out the check is reported as skipped, never as passed.
Replay with Play
The agent calls test_open_in_astral and Astral opens its Tests panel with the test loaded and paused. You press Play to watch it run: slow motion, pause, step by step and stop. By default the agent cannot start it: Play is also a safety control. For demos and reviews you asked it to drive, the agent can start it with play: true; that needs the same permission as test_run, works only for tests saved in a project you have allowed, and you keep Pause, Step and Stop.