Files
computer-playbook/roles/web-app-dashboard/files/playwright/test-icons.js
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

43 lines
1.8 KiB
JavaScript

const { test, expect } = require("./fixtures/onion-test");
const { resolveTimeout } = require("./timeouts");
const { gotoOnion } = require("./personas");
exports.register = function (shared) {
test("dashboard card icons resolve to the public Simple Icons URL when enabled, and to a Font Awesome icon otherwise", async ({ page }) => {
await gotoOnion(page,"/");
await shared.waitForDashboardReady(page);
await expect(
page.locator(".card-img-top").first(),
"Expected at least one dashboard card with a rendered icon"
).toBeVisible({ timeout: resolveTimeout(60_000) });
// Regression guard for the original bug: no card may still reference the
// internal Docker bridge IP that the buggy DASHBOARD_SIMPLEICONS_SYNC_URL_BASE
// used to be rendered with.
await expect(
page.locator(".card-img-top img[src*='172.17.0.1']"),
"No card icon may point at the internal Docker bridge IP"
).toHaveCount(0);
if (shared.isServiceEnabled("simpleicons")) {
const simpleiconsImg = page.locator(".card-img-top img[src*='//icon.']").first();
await expect(
simpleiconsImg,
"Expected at least one card icon to load from the public Simple Icons domain (icon.<DOMAIN>)"
).toBeVisible({ timeout: resolveTimeout(60_000) });
const src = await simpleiconsImg.getAttribute("src");
expect(
src,
"Expected the Simple Icons URL to be an absolute SVG under icon.<DOMAIN>"
).toMatch(/^https?:\/\/icon\.[^/]+\/[^/]+\.svg$/);
} else {
await expect(
page.locator(".card-img-top i[class*='fa']:visible").first(),
"Expected card icons to fall back to a Font Awesome icon when the Simple Icons service is disabled"
).toBeVisible({ timeout: resolveTimeout(60_000) });
}
});
};