`the channel is listed under its category` failed intermittently on the second poll: the category never showed up in get_live_categories within 200s. That list is filtered by the line's category_ids, which UserRepository builds from the `category_map` cache file, and only CacheCronJob's heavy pass writes it. That pass returns early while its `heavy_cache_built` marker is under 300s old, and cron:cache runs once a minute, so a category created just after a pass waits up to ~360s. A 200s budget cannot cover that — the test passed only when the category happened to land late in a cycle, which is why it looked flaky rather than broken. The poll now gets 380s and the test 600s, with the 300s throttle named in the comment so the budget is not trimmed back without seeing why it is that size. Nothing in the panel changed; the first poll (get_live_streams, fed by the light pass) was always passing. Forcing the rebuild from the test is not an option: ./api?action=regenerate_cache runs cron:cache_engine, which only reads bouquet_map, and the only way to skip the throttle is `console.php cron:cache 1` — a shell the HTTP-only harness does not have. Verified: the full spec file passes (10/10, 3.5m).
XC_VM admin E2E (Playwright)
Browser tests for the admin panel. They run against a live panel (there is no built-in server), so point them at a test instance, never at production: the admin specs create, edit, start and delete real records.
What runs
| File | Covers |
|---|---|
tests/auth.setup.ts |
Signs in once; the other specs reuse that session (.auth/admin.json) |
tests/dashboard.spec.ts, customizer.spec.ts, newui-tables.spec.ts |
Smoke: the new-UI shell, dashboard tiles, per-user UI prefs, every migrated list page loads |
tests/admin/catalog.spec.ts |
Stream category, bouquet and reseller package: create, rename / edit, reopen, delete |
tests/admin/subscribers.spec.ts |
Line with a bouquet: create, search, edit in the modal, disable / enable, ban / unban, delete; MAG and Enigma2 devices: create, find, delete |
tests/admin/bulk.spec.ts |
Lines bulk bar: select with the header checkbox, bulk disable, bulk delete |
tests/admin/resellers.spec.ts |
Reseller: create with credits, top up from the list, edit in the modal, disable / enable, delete |
tests/admin/blocklists.spec.ts |
Block and unblock an IP, a user agent and an ISP |
tests/admin/streams.spec.ts |
Live stream: add with a source and a server, start, wait until it runs with codecs and the Resources column (producer / CPU / RAM), stop, rename, delete |
tests/admin/signin.spec.ts |
A second administrator: created, refused with a wrong password, signs in and out, deleted |
tests/admin/templates.spec.ts |
Activation code pages render; category template: the menu's create entry, create, rename and make it a system template, clone, delete |
tests/admin/reports.spec.ts |
VOD theft detection and line IP usage keep the chosen time range |
tests/admin/pickers.spec.ts |
Date pickers open: expiry on the line / MAG / Enigma2 forms and their mass-edit pages, and the log pages' date range filters |
tests/admin/player-api.spec.ts |
A line signs in to player_api.php and every action answers JSON; info actions answer only for the line's own content; malformed parameters and a wrong password get JSON too |
tests/admin/cleanup.teardown.ts |
After everything: removes whatever the admin specs left behind (see below) |
Run
# one-time: install deps + the Chromium browser
cd tests/e2e && npm ci && npx playwright install --with-deps chromium
# configure (or copy .env.example -> .env)
export XC_E2E_BASE_URL="http://<host>:<port>/<access-code>"
export XC_E2E_USER="<admin user>"
export XC_E2E_PASS="<admin pass>"
# from the repo root:
make e2e # headless run
make e2e-ui # interactive Playwright UI
# one spec, e.g. while working on it:
cd tests/e2e && npx playwright test tests/admin/streams.spec.ts
XC_E2E_BASE_URL is the admin base including the access-code path segment
(e.g. http://panel.example.com:8080/ACCESS_CODE) — the same prefix as /<code>/login.
Optional:
| Variable | Default | Used by |
|---|---|---|
XC_E2E_STREAM_SOURCE |
Unified Streaming's public 24/7 demo channel | streams.spec.ts — a live source the panel's server can reach |
XC_E2E_SERVER |
Main Server |
streams.spec.ts — the server the stream runs on |
XC_E2E_RUN |
a timestamp | the run tag in every record name (below) |
Traces/screenshots/video for failures land in test-results/, the HTML report in
playwright-report/ (npm run report).
A dedicated admin account
Use an account that exists only for the tests. A successful admin login re-hashes the password, which signs out every other session of that account — each run would log a person out of their own panel.
tools/create-admin.php creates (or re-keys) that account on the panel host, the
same way the first-run setup page does. The password is read from stdin:
scp tests/e2e/tools/create-admin.php root@panel:/tmp/
ssh root@panel 'sudo -u xc_vm /home/xc_vm/bin/php/bin/php /tmp/create-admin.php e2e_admin' < password.txt
Test data
Every record the admin specs create is named after the run: e2e-<run>-<label>
(categories, bouquets, packages, category templates, streams, block lists), e2e<run><label> for
usernames, and devices carry an e2e-<run> tag in their notes. Each spec deletes
what it made; cleanup.teardown.ts runs after the whole suite (also after a
failure) and sweeps anything matching those patterns, from any earlier run. It
never touches other records, nor the XC_E2E_USER account.
Side effects on the panel host worth knowing:
- Blocking an IP adds an iptables DROP rule. The spec uses an address from 198.51.100.0/24 (RFC 5737 documentation range) and lifts the block again.
- The wrong-password test counts once against the login flood limit
(Settings →
login_flood, failures per IP per day). - The stream test pulls its source for a minute or two, and the panel's cron
must be running: codecs and resources are filled in by
cron:streams.