🧪 Test Hub
testing: Demo target v3.0.0 · built-in demo site

Settings

Test Hub v2.21.0

System check is this machine healthy? — the same checks as testhub doctor on the command line

ok Python 3.9.25 at /opt/pw-offline/venv/bin/python
ok Test Hub version 2.21.0
ok Playwright library version unknown
ok Bundled browsers 6 browser build(s) in /opt/pw-offline/browsers
ok Database SQLite 3.26.0, 484 run(s) recorded
ok Database schema up to date
ok Tests folder /opt/pw-testhub/data/tests — 25 test file(s)
ok Results folder /opt/pw-testhub/data/results
ok Disk space 17.9 GB free where runs are stored
ok Schedule timezone America/Los_Angeles
ok File permissions secret key and config are not world-readable
warn Sleep prevention this machine cannot take a sleep-inhibitor lock
→ Run 'caffeinate --status' to see whether it can suspend at all. On a server that must run overnight: sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
ok OpenCode (AI agent) installed at /opt/pw-offline/bin/opencode — shells see it after: source /opt/pw-offline/env.sh (add to ~/.bashrc, copied exactly)
ok Runner active — tests can run and schedules can fire
ok Target reachable HTTP 200 from http://127.0.0.1:8000/demo/

This machine's config /opt/pw-testhub/config.json

Target under test

While the URL is empty the hub tests its built-in demo site, which reports its OWN version (now 3.0.0) — switch it in the demo's Release console. This field applies once you point the hub at your own site.


Runner


What to record each one costs disk on every run — turn off what you do not need






Your own ctx.screenshot("name") calls inside tests are always saved; these switches control what the hub records automatically.


Scheduler


Maintenance

Where things live

Hub URL (site.host / site.port)https://test-hub.us
Target under testhttp://127.0.0.1:8000/demo/
Data directory/opt/pw-testhub/data
Tests folder (source of truth)/opt/pw-testhub/data/tests
Results / artifacts/opt/pw-testhub/data/results
Database/opt/pw-testhub/data/db.sqlite3

To point this hub at a different machine's target (local vs office network vs AWS), edit target.url above — nothing else changes. To move the hub itself, copy the data directory and start manage.py serve there.

Machine clock air-gapped boxes have no NTP — a wrong clock fires schedules at surreal hours and scrambles run history

This machine thinks it is 2026-09-25 08:58:51
Your device says …
Fix it or pick one:

Needs the hub to be running as root (the systemd service does). The timezone name used for schedules is separate — set it under Scheduler above.

Disk & connectivity

Run artifacts on disk 0 bytes across 0 run folder(s)
Target reachability
Free space now days

Free up space deleting artifacts keeps every chart — only the videos/screenshots/traces go

Delete artifacts, keep history
reclaims essentially all the space; pass rates, durations and timings survive
keep newest run(s) per test
Delete only passed runs' artifacts
failures are usually the ones worth keeping
Delete run history too
removes the rows as well — charts lose those runs

Per-test and per-run delete buttons live on those pages. Automatic cleanup: set retention days above.

Artifact storage where videos/traces/screenshots live

Local disk is the default and the only option offline. On AWS you can push finished runs to a bucket and (optionally) drop the local copy — the database always stays local. Credentials come from the instance role or the environment, never from config.json.

Backup & transfer upgrades are painful here — take a backup first

Backup everything
tests + run history + config, as one zip
⬇ Download backup
restore with: testhub restore <file>
Export tests
test definitions only — the format to carry between machines
⬇ Download tests
Import tests
upload a tests zip from another machine; run history here is never touched

A safety backup is taken automatically before an import or a restore, and anything replaced is moved aside — never deleted.

Archived tests

Nothing archived.

Credentials for tests that have to log in

Test files travel — laptop, zip, disc, git. A password written into one travels with it. So credentials live only on this machine in a 0600 file, and a test refers to them by name: ctx.secret("target_password"). Move the test anywhere and it works as soon as that machine has its own value for the name — which is also right, because the lab password should not be the laptop password.

Values are never shown again after saving, never included in a backup or a tests export, and are redacted from run logs, error messages and results even if a test prints one by accident.

NameUse it as
No credentials stored on this machine.