Commit Graph
7 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 4.8 edb96e9ae3 feat(dashboard): make keycloak-js login work over Tor onion
Chromium never treats plaintext .onion origins as a secure context, so
keycloak-js (PKCE via crypto.subtle) could not run. A custom Playwright
browser fixture launches Firefox with the Tor-Browser prefs
(dom.securecontext.allowlist_onions, origin/mixed-content handling) for
onion targets and keeps Chromium for clearnet; all dashboard specs share
it via the register pattern.

keycloak-js is now vendored from the deployment's own CDN as an ES
module (jsdelivr is unreachable over Tor) with CORS opened on the CDN
proxy. Personas and the oidc-login spec wait for adapter readiness and
navbar settle before driving login/logout, which the async code->token
exchange needs over Tor latency.

Verified: full deploy matrix green (onion 14/14 twice, clearnet pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 01:55:16 +02:00
kevinveenbirkenbachandClaude Opus 4.7 fd785f13d7 fix(web-app-dashboard): route Simple Icons card sources through the public TLS URL
Dashboard card icons were rendered with `http://172.17.0.1:8044/<slug>.svg`,
the in-cluster sync URL that only the Ansible runner can reach. The
browser saw `NS_ERROR_CONNECTION_REFUSED` for every Simple Icons card.

The `add_simpleicon_source` filter now takes two URLs: it probes the
local sync URL (plain HTTP, no TLS, no redirect chain — reachable
from the deploy container's `requests` without a CA bundle) and
writes the public `lookup('tls', 'web-svc-simpleicons', 'url.base')`
URL into `icon.source`. Cards render `<img src="https://icon.<DOMAIN>/<slug>.svg">`,
which is reachable from the browser and CSP-permitted (`img-src *`).

Drops the dead local-cache scaffolding (static-subdir vars, volume
mount, `Ensure directory` task, refresh-download step) since the
browser now fetches Simple Icons directly. Renames the
previously-static `templates/style.css.j2` to `files/style.css`
(no Jinja markers, matches the new template-uses-jinja lint warning).

Adds a Playwright spec `test-icons.js` that asserts the regression:
no card may still reference `172.17.0.1`, at least one card must
resolve under `icon.<DOMAIN>`, with a favicon fallback path when the
Simple Icons service is disabled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 02:53:05 +02:00
kevinveenbirkenbachandClaude Opus 4.7 adb901c7ce refactor(web-app-dashboard/playwright): declutter _shared.js + split out console-cleanliness check
Console cleanliness moved to its own scenario
---------------------------------------------

``test-header-navbar-logos.js`` failed in CI when port-ui's Keycloak
silent-check-sso iframe was blocked by frame-ancestor CSP — the
``expectNoUnexpectedDiagnostics`` call at the test's end caught a
console.error logged by chrome for the blocked iframe (chrome-error://
chromewebdata + ``Framing 'https://auth.…'``) and failed every retry.
That diagnostic was a generic "no surprises" tail, unrelated to the
logo assertions the test is named after.

- Drop the diagnostic-tail call from the logo test; it now does
  exactly what its name says.
- New ``test-no-console-noise.js`` runs the diagnostic check as its
  own scenario, with two noise filters enabled
  (``ignoreMatomoConsoleNoise`` + ``ignoreOidcSilentCheckNoise``).
- New ``isOidcSilentCheckNoise`` helper in ``_shared.js`` matches the
  ``chrome-error://chromewebdata`` URL plus the ``Framing '…'`` /
  ``silent-check-sso`` text patterns. These are test-env config
  artefacts (Keycloak ``frame-ancestors`` not whitelisted), not
  dashboard regressions.
- ``expectNoUnexpectedDiagnostics`` learns a corresponding
  ``ignoreOidcSilentCheckNoise`` option.

_shared.js no longer hides single-consumer code
-----------------------------------------------

Audited every export of ``_shared.js`` against every ``test-*.js``
consumer; ten symbols had exactly one caller and were therefore not
"shared". Moved each to the test file that owns it:

- ``runGuestFlow`` / ``runBiberFlow`` / ``runAdminFlow`` →
  matching ``test-*-persona.js`` (now ``require("./personas")`` direct)
- ``assertCspMetaParity`` / ``assertCspResponseHeader`` /
  ``expectNoCspViolations`` / ``installCspViolationObserver`` plus
  ``canonicalDomain`` → ``test-csp-canonical-domain.js`` (CSP observer
  install is now a local ``test.beforeEach`` in that file)
- ``dashboardTargetRoles`` parser → ``test-tile-reachability.js``
- ``cdnBaseUrl`` + each prefix (sharedCss / roleCss / sharedJs) →
  ``test-shared-css.js`` / ``test-cdn-role-stylesheet.js`` /
  ``test-logout-js-injection.js``
- ``platformLogoUrl`` + ``platformFaviconUrl`` →
  ``test-header-navbar-logos.js``
- ``appBaseUrl`` / ``oidcIssuerUrl`` / ``loginUsername`` /
  ``loginPassword`` / ``expectedOidcAuthUrl`` → ``test-oidc-login.js``

Dropped completely as dead test code: ``companyLogoUrl`` (no
spec ever read it) and its ``COMPANY_LOGO_URL`` env render in
``playwright.env.j2``. ``DASHBOARD_COMPANY_LOGO_URL`` in
``vars/main.yml`` stays — ``config.yaml.j2`` still consumes it for
the port-ui company.logo.source field.

``_shared.js`` is down to genuinely-shared state: ``dashboardJsBaseUrl``
+ ``matomoBaseUrl`` (each ≥ 2 callers), the diagnostic plumbing
(``attachDiagnostics`` / ``expectNoUnexpectedDiagnostics`` /
``waitForDashboardReady`` / ``waitForResourceResponse``), the noise
predicates, ``beforeEach``, and the ``skipUnlessServiceEnabled``
re-export. 116 LOC of state/exports collapse to 63.

Hitchhikers
-----------

- ``vars/main.yml`` loses an outdated docstring above the branding
  SPOTs (the SPOTs themselves stay; ``config.yaml.j2`` consumes them).
- ``sys-svc-compose/defaults/main.yml``: linter re-joined the
  multi-line ``compose_repository_pull`` default onto one line; pure
  formatting, no semantic change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 22:36:05 +02:00
kevinveenbirkenbachandClaude Opus 4.7 e97b1095e6 refactor(web-app-dashboard): split playwright spec, SPOT-ify branding, bump port-ui 2.0.0
Playwright spec split
---------------------

The 747-line ``playwright.spec.js`` monolith is split into a thin
orchestrator + ``_shared.js`` (env, ``beforeEach``, common helpers) +
one ``test-*.js`` per scenario, matching the convention used by
web-app-fediwall/friendica/nextcloud. Each scenario file holds its own
specialised helpers (e.g. the auth/dropdown helpers live in
``test-oidc-login.js``, image-loaded assertions live in
``test-header-navbar-logos.js``); only helpers used by ≥ 2 tests stay
in ``_shared.js``. ``dashboard-card-flow.js`` becomes
``test-tile-reachability.js`` (git-detected rename) — the
``runDashboardCardScenario`` helper had a single consumer, so the
indirection is gone.

SPOT for branding URLs
----------------------

The ``'https:' ~ source if source.startswith('//') else source``
transformation lived inline in ``config.yaml.j2`` and was duplicated in
``playwright.env.j2`` when the logo assertion was added. Three new
SPOTs in ``vars/main.yml``:

- ``DASHBOARD_PLATFORM_LOGO_URL``
- ``DASHBOARD_PLATFORM_FAVICON_URL``
- ``DASHBOARD_COMPANY_LOGO_URL``

Both templates now reference these vars; the inline transformations
and the now-redundant ``{% set ... %}`` lines in ``config.yaml.j2``
are removed. The slurp + parse of the rendered config in
``tasks/01_core.yml`` stays because it covers the host-override case
(``DASHBOARD_CONFIG_INV_PATH`` template may use different branding),
but is now documented as the override-discovery path rather than the
default source of truth.

port-ui 2.0.0 + test alignment
------------------------------

- ``meta/services.yml``: port-ui pinned ``1.2.0 → 2.0.0`` (probe-first
  asset resolver, see kevinveenbirkenbach/port-ui#2.0.0).
- ``test-header-navbar-logos.js``: drop the ``asset`` skip-gate (port-ui
  >= 2.0.0 embeds the URL directly instead of producing the broken
  ``/static/<absolute-url>``), replace the shape regex with an
  exact-match against the SPOT ``PLATFORM_LOGO_URL`` env var, and add
  a favicon assertion that checks the rendered ``<link rel="icon">``
  href against the SPOT ``PLATFORM_FAVICON_URL``.
- ``test-cdn-role-stylesheet.js``: keep the ``asset`` skip-gate — that
  test exercises ``web-svc-cdn`` which depends on ``web-svc-asset`` as
  its file backend, unrelated to the port-ui bug.
- ``playwright.env.j2``: rendered ``ASSET_SERVICE_ENABLED``,
  ``PLATFORM_LOGO_URL``, ``PLATFORM_FAVICON_URL``, ``COMPANY_LOGO_URL``.
- ``meta/services.yml``: drop the now-stale
  ``# nocheck: playwright-service-flag`` annotation on the ``asset``
  service block.

.claude/settings.json
---------------------

Resort ``permissions.allow`` after the runtime appended
``WebFetch(domain:cloud.veen.world)`` at the end of the array during
the asset-resolver work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 13:43:43 +02:00
kevinveenbirkenbachandClaude Opus 4.7 9e163f6dd6 fix(web-app-dashboard): gate variant-1 CSS/JS/iframe tests cleanly
CI run 25973408947's `test-development` matrix failed on dashboard
variant 1 (all shared services off): three tests ran and failed
because they either had no service gate or their gate pointed at a
flag that stayed literal `true`. Switching dashboard onto the
web-svc-css opt-in pattern plus adding the missing cdn/matomo gates
makes variant 1 skip cleanly while variant 0 keeps running the full
assertion path.

- `meta/services.yml`: migrate the `css` block from hardcoded
  `{ enabled: true, shared: true }` to dynamic
  `{ "{{ 'web-svc-css' in group_names }}" }` so variant pinning takes
  effect on the gate.
- `meta/variants.yml`: add a `css: { enabled: true/false, shared: ... }`
  entry to both variant blocks to give the dynamic flag the
  two-polarity matrix coverage the lint requires.
- `files/playwright/playwright.spec.js`: add the missing
  `skipUnlessServiceEnabled` calls to test 546 (`cdn`) and test 570
  (`cdn` + `matomo`). Both tests were ungated; variant 1 ran them
  and failed because the resources they depend on were not served.

Verified that variant 0 keeps running every assertion (the gates only
fire when the upstream service is pinned `false`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 02:20:37 +02:00
kevinveenbirkenbachandClaude Opus 4.7 ff7da26d30 refactor(playwright): migrate role specs to service-gating helper; split dashboard asset test per service
Removes direct process.env.<X>_SERVICE_ENABLED reads (and bespoke
per-role <ROLE>_OIDC_ENABLED / SERVICE_* / OIDC_ENABLED env flags) in
favor of skipUnlessServiceEnabled/isServiceEnabled from
roles/test-e2e-playwright/files/service-gating.js, per
docs/contributing/artefact/files/role/playwright.specs.js.md service-
gating contract.

Per-role changes:
- discourse, matrix, dashboard: replace process.env.<X>_SERVICE_ENABLED
  with the helper; drop now-redundant nocheck markers on the canonical
  env flags.
- taiga: drop TAIGA_OAUTH2_ENABLED / TAIGA_OIDC_ENABLED env lines;
  consume OAUTH2_SERVICE_ENABLED / OIDC_SERVICE_ENABLED via the helper.
- opentalk, opencloud, nextcloud: drop <ROLE>_OIDC_ENABLED (and
  NEXTCLOUD_LDAP_ENABLED) env lines; spec switches to
  isServiceEnabled('oidc'|'ldap').
- moodle: drop SERVICE_OIDC / SERVICE_LDAP / SERVICE_LAM in favor of
  the canonical OIDC_SERVICE_ENABLED / LDAP_SERVICE_ENABLED plus a new
  LAM_SERVICE_ENABLED rendered from 'web-app-lam' group membership.
- decidim: drop OIDC_ENABLED env line; SSO-button-visible scenario
  uses skipUnlessServiceEnabled('oidc').

Dashboard asset test split:
The previously bundled "dashboard loads core css, javascript,
simpleicons, and logo assets" test triple-gated (css/cdn/logout)
skipped the entire surface when any single shared service was off.
Split into six focused tests so disabling one service skips only its
specific assertion:
  - shared CSS injection (gate: css)
  - CDN-served role-local stylesheet (gate: cdn)
  - logout.js injection (gate: logout)
  - simpleicon-backed cards (gate: simpleicons; nocheck dropped)
  - role-core JS modules + logos (ungated, role-core)
  - iframe-sync postMessage handler (ungated, role-core)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 23:24:56 +02:00
kevinveenbirkenbachandClaude Opus 4.7 c2c532c8e6 refactor(playwright): centralize dashboard-tile mechanics in web-app-dashboard SPOT; move specs into files/playwright/ subdir
Architecture
============
- New SPOT helper `roles/web-app-dashboard/files/playwright/dashboard-card-flow.js` (`runDashboardCardScenario(page, context, target)`): per `roles_with_service('dashboard')` target, asserts tile visible → click loads in `#main iframe` (outer URL gets `?iframe=` + iframe src matches canonical_domain) → header "Tab" button pops the embed into a fresh browser tab.
- `web-app-dashboard/files/playwright/playwright.spec.js` parametrised loop calls the helper for every `DASHBOARD_TARGET_ROLES_JSON` entry — the single owner of dashboard-tile mechanics across the whole codebase.

Spec relocation
===============
- 71 `roles/<role>/files/playwright.spec.js` → `roles/<role>/files/playwright/playwright.spec.js` (`git mv`, history preserved). odoo + pixelfed show as D+A in `git status` because of substantial content rewrites; coverage retained.
- New SPOT constant `ROLE_FILE_PLAYWRIGHT_SPEC` in `utils/roles/mapping.py`, registered as optional on `ROLE_TYPE_APPLICATION` (disallowed elsewhere, mirrors `ROLE_FILE_META_VARIANTS` pattern).
- `roles/test-e2e-playwright/tasks/run_one.yml`: staging task replaced with `with_fileglob` over `files/playwright/*.js` — roles can ship companion helpers (like `dashboard-card-flow.js`) next to the spec without further wiring.

Spec migrations (no more dashboard-iframe path in app specs)
============================================================
13 specs rewritten so the persona/SSO journey starts at the app URL directly, never through the dashboard tile or iframe:
* fider, mailu, mattermost, nextcloud, odoo, taiga: iframe-wrap removed (no more `frameLocator("#main iframe")`)
* bigbluebutton, discourse, kix, matrix, openwebui, peertube: dashboard goto preamble removed
* pixelfed: ~270 LOC of iframe-aware helpers/login/logout collapsed to direct-URL flow

Title cleanup
=============
- 64 specs: `"<persona>: dashboard → app → universal logout"` / `"administrator: dashboard → prometheus → app → universal logout"` → `"<persona>: app → universal logout"`.
- 13 specs: `"<persona>: dashboard to <app> ..."` → `"<persona>: <app> ..."`.

Dead-code cleanup
=================
- `dashboardBaseUrl` consts + beforeEach assertions removed from 11 specs (no functional uses; personas helper handles env validation).
- `DASHBOARD_BASE_URL=` env-template lines removed from fider, mattermost, odoo, taiga, pixelfed, kix.
- `DASHBOARD_SERVICE_ENABLED=` env-template lines removed (pixelfed retained for the still-gated spec).
- Stale "loads inside the iframe" / "FrameLocator parameter" comments cleaned in fider, odoo, prometheus, nextcloud.

Tooling
=======
- 5 lint Python files + 1 integration test now use `from utils.roles.mapping import ROLE_FILE_PLAYWRIGHT_SPEC`.
- `eslint.config.js` glob pattern + `roles/test-e2e-playwright/meta/main.yml` description + `roles/test-e2e-playwright/README.md` updated to new path.
- 25 markdown/docstring references in `docs/`, `roles/**/README.md`, `roles/**/TODO.md`, persona-helper docstrings updated to new path.
- `docs/contributing/artefact/files/role/playwright.specs.js.md`: file-placement section now documents the `files/playwright/` convention + `ROLE_FILE_PLAYWRIGHT_SPEC` SPOT constant; dashboard SPOT delegation explicit at 3 places.

Net diff: 124 files, +1142/-1564 (−422 LOC).
Integration suite: 183/183 green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 23:24:55 +02:00