From 1628b1dfeabccd842841b82735356695376ae736 Mon Sep 17 00:00:00 2001 From: reachableceo Date: Fri, 1 May 2026 13:31:42 -0500 Subject: [PATCH] fix(demo): add HOMEPAGE_ALLOWED_HOSTS, harden Playwright tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Set HOMEPAGE_ALLOWED_HOSTS=* so Homepage accepts requests from localhost, LAN IPs, and Tailscale FQDNs (appropriate for demo) - Add host validation to docker-compose.yml.template and demo.env.template - Bootstrap HOMEPAGE_ALLOWED_HOSTS in ensure_env() for existing installs - Harden Playwright tests: check for "host validation failed" and "internal server error" text, verify page titles, use stronger content assertions based on actual rendered content - Pin @playwright/test to exact 1.52.0 (no caret) to prevent npm resolving to a version incompatible with the Docker image - Gitignore additional Homepage auto-generated files (custom.css/js, proxmox.yaml) 💘 Generated with Crush Assisted-by: GLM-5.1 via Crush --- .gitignore | 3 + demo/demo.env.template | 1 + demo/docker-compose.yml.template | 1 + demo/scripts/demo-stack.sh | 1 + demo/tests/e2e/package.json | 2 +- demo/tests/e2e/playwright-services.spec.ts | 105 ++++++++++++++++++--- 6 files changed, 97 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 4f37801..fc2ba9b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,9 @@ tmp_template.yml # Homepage auto-generated files demo/config/homepage/logs/ demo/config/homepage/kubernetes.yaml +demo/config/homepage/custom.css +demo/config/homepage/custom.js +demo/config/homepage/proxmox.yaml # Playwright node_modules/ diff --git a/demo/demo.env.template b/demo/demo.env.template index f669a0d..fdec198 100644 --- a/demo/demo.env.template +++ b/demo/demo.env.template @@ -12,6 +12,7 @@ DEMO_DOCKER_GID=986 # Port Assignments (4000-4099 range) HOMEPAGE_PORT=4000 +HOMEPAGE_ALLOWED_HOSTS=* DOCKER_SOCKET_PROXY_PORT=4005 PIHOLE_PORT=4006 DOCKHAND_PORT=4007 diff --git a/demo/docker-compose.yml.template b/demo/docker-compose.yml.template index f4813f9..d753873 100644 --- a/demo/docker-compose.yml.template +++ b/demo/docker-compose.yml.template @@ -94,6 +94,7 @@ services: volumes: - ./config/homepage:/app/config environment: + - HOMEPAGE_ALLOWED_HOSTS=${HOMEPAGE_ALLOWED_HOSTS} - PUID=${DEMO_UID} - PGID=${DEMO_GID} labels: diff --git a/demo/scripts/demo-stack.sh b/demo/scripts/demo-stack.sh index dfef507..577c269 100755 --- a/demo/scripts/demo-stack.sh +++ b/demo/scripts/demo-stack.sh @@ -30,6 +30,7 @@ ensure_env() { fi # Ensure new variables exist in older env files grep -q '^MAILHOG_SMTP_PORT=' "$ENV_FILE" || echo "MAILHOG_SMTP_PORT=4019" >> "$ENV_FILE" + grep -q '^HOMEPAGE_ALLOWED_HOSTS=' "$ENV_FILE" || echo "HOMEPAGE_ALLOWED_HOSTS=*" >> "$ENV_FILE" } detect_user() { diff --git a/demo/tests/e2e/package.json b/demo/tests/e2e/package.json index 7203768..7f74dfb 100644 --- a/demo/tests/e2e/package.json +++ b/demo/tests/e2e/package.json @@ -3,6 +3,6 @@ "version": "1.0.0", "private": true, "devDependencies": { - "@playwright/test": "^1.52.0" + "@playwright/test": "1.52.0" } } diff --git a/demo/tests/e2e/playwright-services.spec.ts b/demo/tests/e2e/playwright-services.spec.ts index 0407637..1dd102c 100644 --- a/demo/tests/e2e/playwright-services.spec.ts +++ b/demo/tests/e2e/playwright-services.spec.ts @@ -1,30 +1,105 @@ import { test, expect } from '@playwright/test'; const services = [ - { name: 'Homepage', url: 'http://localhost:4000', contentCheck: 'homepage' }, - { name: 'Pi-hole', url: 'http://localhost:4006/admin', contentCheck: 'pihole' }, - { name: 'Dockhand', url: 'http://localhost:4007', contentCheck: 'sveltekit' }, - { name: 'InfluxDB', url: 'http://localhost:4008', contentCheck: 'influxdb' }, - { name: 'Grafana', url: 'http://localhost:4009', contentCheck: 'grafana' }, - { name: 'Draw.io', url: 'http://localhost:4010', contentCheck: 'draw' }, - { name: 'Kroki', url: 'http://localhost:4011/health', contentCheck: 'kroki' }, - { name: 'Atomic Tracker', url: 'http://localhost:4012', contentCheck: 'journal' }, - { name: 'ArchiveBox', url: 'http://localhost:4013', contentCheck: 'archive' }, - { name: 'Tube Archivist', url: 'http://localhost:4014', contentCheck: 'tube' }, - { name: 'Wakapi', url: 'http://localhost:4015', contentCheck: 'wakapi' }, - { name: 'MailHog', url: 'http://localhost:4017', contentCheck: 'mailhog' }, - { name: 'Atuin', url: 'http://localhost:4018', contentCheck: 'version' }, + { + name: 'Homepage', + url: 'http://localhost:4000', + contentCheck: 'tsys developer support stack', + titleCheck: 'TSYS Developer Support Stack', + }, + { + name: 'Pi-hole', + url: 'http://localhost:4006/admin', + contentCheck: 'pihole', + }, + { + name: 'Dockhand', + url: 'http://localhost:4007', + contentCheck: 'sveltekit', + }, + { + name: 'InfluxDB', + url: 'http://localhost:4008', + contentCheck: 'influxdb', + }, + { + name: 'Grafana', + url: 'http://localhost:4009', + contentCheck: 'grafana', + }, + { + name: 'Draw.io', + url: 'http://localhost:4010', + contentCheck: 'diagram', + }, + { + name: 'Kroki', + url: 'http://localhost:4011/health', + contentCheck: 'kroki', + }, + { + name: 'Atomic Tracker', + url: 'http://localhost:4012', + contentCheck: 'journal', + }, + { + name: 'ArchiveBox', + url: 'http://localhost:4013', + contentCheck: 'archive', + }, + { + name: 'Tube Archivist', + url: 'http://localhost:4014', + contentCheck: 'tubearchivist', + }, + { + name: 'Wakapi', + url: 'http://localhost:4015', + contentCheck: 'wakapi', + }, + { + name: 'MailHog', + url: 'http://localhost:4017', + contentCheck: 'mailhog', + }, + { + name: 'Atuin', + url: 'http://localhost:4018', + contentCheck: 'version', + }, ]; for (const svc of services) { test(`${svc.name} (${svc.url}) loads successfully`, async ({ page }) => { - const response = await page.goto(svc.url, { waitUntil: 'domcontentloaded', timeout: 30000 }); + const response = await page.goto(svc.url, { + waitUntil: 'domcontentloaded', + timeout: 30000, + }); expect(response).not.toBeNull(); expect(response!.status()).toBeLessThan(400); const body = await page.textContent('body').catch(() => ''); const title = await page.title().catch(() => ''); const combined = (body + ' ' + title).toLowerCase(); - expect(combined).toContain(svc.contentCheck.toLowerCase()); + + expect( + combined, + `${svc.name} should not show an error page` + ).not.toContain('host validation failed'); + expect( + combined, + `${svc.name} should not show a server error` + ).not.toContain('internal server error'); + expect( + combined, + `${svc.name} should contain expected content` + ).toContain(svc.contentCheck.toLowerCase()); + + if (svc.titleCheck) { + expect( + title.toLowerCase(), + `${svc.name} should have expected title` + ).toContain(svc.titleCheck.toLowerCase()); + } }); }