The Playwright suite only checked that pages render. It now performs the
administrator's work against a live test panel and asserts the panel's own
data after each step:
- catalogue: a stream category, a bouquet and a reseller package — created,
renamed / edited, reopened, deleted;
- subscribers: a line with a bouquet (search, edit in the modal, disable /
enable, ban / unban, delete), a MAG and an Enigma2 device;
- bulk: two lines selected with the header checkbox, disabled and deleted;
- resellers: created with credits, topped up, edited, disabled, deleted;
- block lists: an IP (RFC 5737 address — blocking adds an iptables rule), a
user agent and an ISP;
- streams: a live stream added with a source and a server, started, running
with codecs and the Resources column filled in, stopped, renamed, deleted;
- sign-in: a second administrator refused with a wrong password, signing in
and out — a separate account, because every admin login re-hashes the
password and ends that account's other sessions.
Records are named `e2e-<run>-…`; a teardown project sweeps whatever a run
leaves behind and nothing else. tools/create-admin.php provisions the
dedicated test administrator on the panel host.
The first runs found three save paths that answered an empty page (fixed in
f7bba5cb, fd13c940, ee2f586a). Against the test panel: 82 passed, 1 skipped
(no series to select).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WDhDajBPziJwjWZcXnh6R
Rename the last lowercase "vuexy" identifiers left after the de-brand:
- views {header,footer,menu,vendors,topbar}.vuexy.php -> .newui.php
- functions xc_admin_use_vuexy / xc_vuexy_vendor_* / _xc_vuexy_icon -> *_newui*
- constant XC_VUEXY_PAGES -> XC_NEWUI_PAGES
- E2E spec vuexy-tables.spec.ts -> newui-tables.spec.ts + drop the brand
name from test/Makefile prose
Also fix two identifiers the earlier brand scrub corrupted (a bare "Vuexy"
in a name became "Bootstrap 5", inserting a space): class
XcVuexyMenuBuilder -> XcNewuiMenuBuilder (was a parse error in the menu view)
and global key xmVuexyVendors -> xmNewuiVendors.
Refs: #148#178
Rebuild the panel's most complex serverSide table on the Bootstrap 5 stack.
handleStreams now emits structured rows (StatusBadge::stream code, live uptime,
convert-to-channel encode %, restart-fails indicator, current server/source,
client count, EPG availability, player-codec compatibility, archive/adaptive/
title-sync flags, codec stream-info) instead of building HTML; the view renders
every cell and the per-row action dropdown client-side via datatables-bs5. The
$rActualStatus computation is preserved verbatim; the data-gathering (grouping,
per-server fails, redis connection counts) is untouched.
Features: category / server / status / codec filters, bulk-select toolbar
(start/stop/restart/kill/delete via action=multi&type=stream), per-row edit +
fingerprint iframe modals, start/stop/restart/purge/delete via action=stream,
player, live-connections link, CSV export. $rOrder gains the control + checkbox
offset. Add streams to the allowlist + E2E suite and the `filter` i18n key.
Verified on canary: 200, correct row shape, renders.
Refs: #148#178
Rebuild the panel's largest management table on the Bootstrap 5 stack. handleLines
now emits structured rows (status code, online/trial/restreamer flags, live
connection count, current stream, owner, formatted expiration + last-connection,
notes, ban/enable state, contact) instead of HTML; the view renders every cell
and the per-row action dropdown client-side via datatables-bs5.
Features: reseller (select2-ajax) + status + search filters, bulk-select
toolbar (enable/disable/ban/unban/kill/delete via action=multi&type=line),
per-row edit + fingerprint iframe modals, native Download-Playlist modal
(output_devices) and WhatsApp-renewal modal, kill/ban/unban/enable/disable/
delete via action=line, and CSV export. $rOrder gains the control + checkbox
offset. Add lines to the allowlist + E2E suite (ajax wait widened to 40s for
this heavy handler) and the `all` / `download_playlist` i18n keys.
Refs: #148#178
Rebuild the Backups page on the Bootstrap 5 stack: the Dropbox / automatic-backup
settings form (native Bootstrap 5 fields + form-switch) posting to
post.php?action=backups, plus the backups list as a clean-JSON table.
handleBackups now emits structured rows (date, filename, size, local flag,
remote state yes/no/error/uploading + error message) and the view renders
status dots, size and restore/delete actions client-side; Create Backup Now
and restore/delete hit ./api?action=backup.
Add backups to the Bootstrap 5 allowlist and the E2E smoke suite (new noControl
flag for tables without a Responsive control column).
Refs: #148#178
Convert handleRadios to the clean-JSON serverSide pattern and rebuild the
radios view on the Bootstrap 5 datatables-bs5 stack (twin of movies, audio
live-streams): icon column, StatusBadge::stream badges with live uptime for
ONLINE, start/stop/restart/kill + edit-modal + delete row actions via
action=stream, bulk delete (action=multi&type=radio), category/server
filters, and a codec/bitrate/speed stream-info column.
Add the missing `restart` i18n key (guards Translator self-poisoning),
add radios to the Bootstrap 5 allowlist and the E2E MANAGEMENT smoke array.
Refs: #148#178
First stream-class content table on the JSON-first pattern:
- TableController::handleMovies: the transcode status code (StatusBadge::vod),
category, per-server info (name/url/count/offline), client count and codec
stream-info are resolved server-side; the render branch now emits a structured
JSON object instead of positional HTML. Two leading false entries prepended to
$rOrder for the Bootstrap 5 control + bulk-select columns.
- movies.php rewritten as a Bootstrap 5 datatables-bs5 serverSide table with client-side
render (cover, title+year+category, server link + "+N" servers + offline flag,
client count linking to live connections, VOD status badge, TMDB, a codec
stream-info badge row, player). Checkbox column + select-all + bulk delete
(action=multi&type=movie); row actions: encode start/stop + edit iframe modal +
delete (action=movie). Category / server / resolution / video / audio filters.
- movies added to the allowlist + the E2E smoke suite; encode/start_encoding/
stop_encoding keys added.
Refs: #148#178
Adds tests/e2e/tests/vuexy-tables.spec.ts — a data-driven Playwright suite over
every admin page migrated to the Bootstrap 5 clean-JSON / client-side datatables-bs5
pattern (30 pages: logs, security blocklists, reference lists, management).
For each page it asserts the page renders inside the Bootstrap 5 shell (#layout-menu —
proving the xc_admin_use_vuexy allowlist routed it to the new layout, not legacy
chrome), its Bootstrap 5-specific table id is present, a leading Responsive control
column exists, and — for serverSide tables — the DataTables ./table ajax answers
200. Three interaction checks cover series bulk-select, panel_logs server-side
search, and the users row-action dropdown.
All 34 tests (incl. the auth setup project) pass against the canary.
Refs: #148#178
Add a browser E2E project under tests/e2e/ that runs against a live panel
(there is no built-in server). Verified green (7/7) against a canary instance.
- playwright.config.ts: loads tests/e2e/.env (dotenv); a `setup` project logs in
once (auth.setup.ts — form #username/#password/#login_button -> ./login) and
stores the session, the chromium project reuses it via storageState. baseURL is
normalized to a trailing slash so relative gotos keep the access-code segment.
- Smoke specs: shell renders (sidebar/navbar, server-stamped data-bs-theme);
Dashboard group + Home link; six stat tiles + a live ./api?action=stats 200;
charts + server selector; customizer theme change persists across reload and
reset (opening the customizer panel) clears it.
- Config via env: XC_E2E_BASE_URL (admin base incl. access code), XC_E2E_USER,
XC_E2E_PASS (see .env.example / README). Makefile: `make e2e` / `e2e-ui` /
`e2e-install`. node_modules/.auth/results/.env gitignored; @playwright/test
pinned ~1.49 (Node 18+).