slimcore-website/e2e/smoke.spec.ts
Pascal Oelmann 3c79b63db5
Some checks failed
Deploy Marketing-Site / Lint + Smoke-Tests (push) Failing after 1m9s
Deploy Marketing-Site / Deploy auf Marketing-VPS (push) Failing after 0s
Deploy Marketing-Site / Deploy-Notification (push) Successful in 9s
Initial Astro-Build, Deployment-Setup und Forgejo-Workflow
- Astro 6 + React + Tailwind v4 Projekt-Skelett mit allen Marketing-Seiten
  (Home, Module, Tester, Souveränität, Roadmap, Kontakt, Impressum, Datenschutz)
- Self-hosted Outfit + JetBrains Mono Fonts (DSGVO)
- Marketing-Komponenten gemäss CLAUDE.md §5.6 (NumberedItem, ModuleCard,
  StatusDot, TechStrip, SovereigntyBlock, RoadmapTimeline, etc.)
- Module-Daten in src/content/module.ts als Single Source of Truth
- E2E Smoke-Tests via Playwright
- OG-Image-Generator
- Forgejo Workflow .forgejo/workflows/deploy.yml für Tier-2 Static Deploy
- Infra-as-Code Snapshot in infra/marketing-vps/
- Brand-System Submodule auf Forgejo umgezogen (war GitHub)
- Deployment- und Handoff-Dokumentation
- .DS_Store aus Tracking entfernt, .gitignore um Test-Artefakte ergaenzt
2026-05-05 01:59:35 +02:00

96 lines
4.1 KiB
TypeScript

import { test, expect } from '@playwright/test';
const routes = [
{ path: '/', lang: 'de', h1Contains: 'Schlank starten' },
{ path: '/module', lang: 'de', h1Contains: 'Module' },
{ path: '/tester', lang: 'de', h1Contains: 'Lücken' },
{ path: '/souveraenitaet', lang: 'de', h1Contains: 'Ihre Daten' },
{ path: '/roadmap', lang: 'de', h1Contains: 'Heute' },
{ path: '/kontakt', lang: 'de', h1Contains: 'erreichen' },
{ path: '/impressum', lang: 'de', h1Contains: 'Impressum' },
{ path: '/datenschutz', lang: 'de', h1Contains: 'Datenschutz' },
{ path: '/en/', lang: 'en', h1Contains: 'Start lean' },
{ path: '/en/module', lang: 'en', h1Contains: 'Modules' },
{ path: '/en/tester', lang: 'en', h1Contains: 'gaps' },
{ path: '/en/sovereignty', lang: 'en', h1Contains: 'Your data' },
{ path: '/en/roadmap', lang: 'en', h1Contains: 'Today' },
{ path: '/en/contact', lang: 'en', h1Contains: 'reach' },
{ path: '/en/imprint', lang: 'en', h1Contains: 'Imprint' },
{ path: '/en/privacy', lang: 'en', h1Contains: 'Privacy' },
];
test.describe('All routes return 200 with correct lang and headline', () => {
for (const r of routes) {
test(`${r.path} renders`, async ({ page }) => {
const response = await page.goto(r.path);
expect(response?.status()).toBe(200);
await expect(page.locator('html')).toHaveAttribute('lang', r.lang);
await expect(page.locator('h1')).toContainText(r.h1Contains);
await expect(page.locator('#nav-bar a[href="/"], #nav-bar a[href="/en/"]').first()).toContainText('SlimCore');
});
}
});
test.describe('NavBar dual-mode toggles on dark-hero pages', () => {
test('Home dark hero: nav dark at top, light after scroll past hero', async ({ page }) => {
await page.goto('/');
await page.waitForFunction(() => document.getElementById('nav-bar')?.dataset.mode === 'dark');
await page.evaluate(() => window.scrollTo(0, 2000));
await page.waitForFunction(() => document.getElementById('nav-bar')?.dataset.mode === 'light');
});
test('Module page also has dark hero', async ({ page }) => {
await page.goto('/module');
await page.waitForFunction(() => document.getElementById('nav-bar')?.dataset.mode === 'dark');
});
});
test.describe('Module filter island', () => {
test('Filter pills hydrate and filter the list', async ({ page }) => {
await page.goto('/module');
await expect(page.locator('main ul li')).toHaveCount(19);
await page.locator('button[aria-pressed]', { hasText: /^Verfügbar$/ }).click();
await expect(page.locator('main ul li')).toHaveCount(6);
await expect(page.locator('[aria-live="polite"]')).toHaveText(/6 Module/);
});
});
test.describe('Language switcher', () => {
test('From DE home, EN button leads to /en/', async ({ page }) => {
await page.goto('/');
await page.locator('.nav-lang-link', { hasText: 'EN' }).click();
await expect(page).toHaveURL('/en/');
await expect(page.locator('html')).toHaveAttribute('lang', 'en');
});
test('From EN home, DE button leads to /', async ({ page }) => {
await page.goto('/en/');
await page.locator('.nav-lang-link', { hasText: 'DE' }).click();
await expect(page).toHaveURL('/');
await expect(page.locator('html')).toHaveAttribute('lang', 'de');
});
});
test.describe('Footer + sitemap + robots', () => {
test('Sitemap-index exists and links to slimcore.io', async ({ request }) => {
const res = await request.get('/sitemap-index.xml');
expect(res.status()).toBe(200);
const body = await res.text();
expect(body).toContain('https://slimcore.io/sitemap-0.xml');
});
test('robots.txt exists and disallows /dev/', async ({ request }) => {
const res = await request.get('/robots.txt');
expect(res.status()).toBe(200);
const body = await res.text();
expect(body).toContain('Disallow: /dev/');
expect(body).toContain('Sitemap: https://slimcore.io/sitemap-index.xml');
});
test('Footer Impressum link works', async ({ page }) => {
await page.goto('/');
await page.locator('footer a[href="/impressum"]').click();
await expect(page).toHaveURL('/impressum');
await expect(page.locator('h1')).toContainText('Impressum');
});
});