fix(demo): add HOMEPAGE_ALLOWED_HOSTS, harden Playwright tests
- 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 <crush@charm.land>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -23,6 +23,9 @@ tmp_template.yml
|
|||||||
# Homepage auto-generated files
|
# Homepage auto-generated files
|
||||||
demo/config/homepage/logs/
|
demo/config/homepage/logs/
|
||||||
demo/config/homepage/kubernetes.yaml
|
demo/config/homepage/kubernetes.yaml
|
||||||
|
demo/config/homepage/custom.css
|
||||||
|
demo/config/homepage/custom.js
|
||||||
|
demo/config/homepage/proxmox.yaml
|
||||||
|
|
||||||
# Playwright
|
# Playwright
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ DEMO_DOCKER_GID=986
|
|||||||
|
|
||||||
# Port Assignments (4000-4099 range)
|
# Port Assignments (4000-4099 range)
|
||||||
HOMEPAGE_PORT=4000
|
HOMEPAGE_PORT=4000
|
||||||
|
HOMEPAGE_ALLOWED_HOSTS=*
|
||||||
DOCKER_SOCKET_PROXY_PORT=4005
|
DOCKER_SOCKET_PROXY_PORT=4005
|
||||||
PIHOLE_PORT=4006
|
PIHOLE_PORT=4006
|
||||||
DOCKHAND_PORT=4007
|
DOCKHAND_PORT=4007
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./config/homepage:/app/config
|
- ./config/homepage:/app/config
|
||||||
environment:
|
environment:
|
||||||
|
- HOMEPAGE_ALLOWED_HOSTS=${HOMEPAGE_ALLOWED_HOSTS}
|
||||||
- PUID=${DEMO_UID}
|
- PUID=${DEMO_UID}
|
||||||
- PGID=${DEMO_GID}
|
- PGID=${DEMO_GID}
|
||||||
labels:
|
labels:
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ ensure_env() {
|
|||||||
fi
|
fi
|
||||||
# Ensure new variables exist in older env files
|
# Ensure new variables exist in older env files
|
||||||
grep -q '^MAILHOG_SMTP_PORT=' "$ENV_FILE" || echo "MAILHOG_SMTP_PORT=4019" >> "$ENV_FILE"
|
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() {
|
detect_user() {
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.52.0"
|
"@playwright/test": "1.52.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,105 @@
|
|||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
const services = [
|
const services = [
|
||||||
{ name: 'Homepage', url: 'http://localhost:4000', contentCheck: 'homepage' },
|
{
|
||||||
{ name: 'Pi-hole', url: 'http://localhost:4006/admin', contentCheck: 'pihole' },
|
name: 'Homepage',
|
||||||
{ name: 'Dockhand', url: 'http://localhost:4007', contentCheck: 'sveltekit' },
|
url: 'http://localhost:4000',
|
||||||
{ name: 'InfluxDB', url: 'http://localhost:4008', contentCheck: 'influxdb' },
|
contentCheck: 'tsys developer support stack',
|
||||||
{ name: 'Grafana', url: 'http://localhost:4009', contentCheck: 'grafana' },
|
titleCheck: 'TSYS Developer Support Stack',
|
||||||
{ 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: 'Pi-hole',
|
||||||
{ name: 'ArchiveBox', url: 'http://localhost:4013', contentCheck: 'archive' },
|
url: 'http://localhost:4006/admin',
|
||||||
{ name: 'Tube Archivist', url: 'http://localhost:4014', contentCheck: 'tube' },
|
contentCheck: 'pihole',
|
||||||
{ 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: '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) {
|
for (const svc of services) {
|
||||||
test(`${svc.name} (${svc.url}) loads successfully`, async ({ page }) => {
|
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).not.toBeNull();
|
||||||
expect(response!.status()).toBeLessThan(400);
|
expect(response!.status()).toBeLessThan(400);
|
||||||
|
|
||||||
const body = await page.textContent('body').catch(() => '');
|
const body = await page.textContent('body').catch(() => '');
|
||||||
const title = await page.title().catch(() => '');
|
const title = await page.title().catch(() => '');
|
||||||
const combined = (body + ' ' + title).toLowerCase();
|
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());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user