import { expect, test, type Page } from '@playwright/test'; import { SITE_CONTENT_DATA } from '../src/app/core/content/site-content'; import { expectHead, jsonLdGraph, pagePath } from './helpers'; async function ensurePrimaryNavOpen(page: Page): Promise { const toggle = page.locator('.nav-toggle'); if ((await toggle.isVisible()) && (await toggle.getAttribute('aria-expanded')) === 'false') { await toggle.click(); await expect(toggle).toHaveAttribute('aria-expanded', 'true'); } } test.describe('server-rendered metadata', () => { test('German and English Home include the full head contract', async ({ request, page }) => { await expectHead(request, 'home', 'de'); await expectHead(request, 'home', 'en'); await page.goto('/'); const graph = (await jsonLdGraph(page)) as { '@graph': Array<{ '@type': string }> }; const types = graph['@graph'].map((node) => node['@type']); expect(types).toContain('Person'); expect(types).toContain('ProfessionalService'); await page.goto('/en'); const english = (await jsonLdGraph(page)) as { '@graph': Array<{ '@type': string }> }; expect(english['@graph'].map((node) => node['@type'])).toEqual( expect.arrayContaining(['Person', 'ProfessionalService']), ); }); test('projects, AI service, contact, legal and 404 keep locale metadata', async ({ request }) => { for (const locale of ['de', 'en'] as const) { await expectHead(request, 'projects', locale); await expectHead(request, 'servicesAi', locale); await expectHead(request, 'contact', locale); await expectHead(request, 'imprint', locale); await expectHead(request, 'notFound', locale); } }); test('unmatched URLs return 404 and a real route stays 200', async ({ request }) => { const home = await request.get('/'); expect(home.status(), 'GET /').toBe(200); await expectHead(request, 'notFound', 'de'); await expectHead(request, 'notFound', 'en'); }); test('client navigation does not duplicate head tags', async ({ page }) => { await page.goto('/'); await ensurePrimaryNavOpen(page); await page.locator('a.contact-cta').first().waitFor(); const assertUnique = async (canonical: string, description: string) => { await expect(page.locator('link[rel="canonical"]')).toHaveCount(1); await expect(page.locator('link[rel="alternate"][hreflang="de-DE"]')).toHaveCount(1); await expect(page.locator('link[rel="alternate"][hreflang="en"]')).toHaveCount(1); await expect(page.locator('link[rel="alternate"][hreflang="x-default"]')).toHaveCount(1); await expect(page.locator('meta[name="description"]')).toHaveCount(1); await expect(page.locator('meta[name="robots"]')).toHaveCount(1); await expect(page.locator('script[type="application/ld+json"]')).toHaveCount(1); await expect(page.locator('link[rel="canonical"]')).toHaveAttribute('href', canonical); await expect(page.locator('meta[name="description"]')).toHaveAttribute( 'content', description, ); }; await ensurePrimaryNavOpen(page); await page .locator(`a[href="${pagePath('projects', 'de')}"]`) .first() .click(); await page.waitForURL('**/projekte'); await assertUnique( 'https://antoniolede.de/projekte', SITE_CONTENT_DATA.de.pages.projects.description, ); await ensurePrimaryNavOpen(page); const servicesParent = page.locator( `.primary-nav > li > a[href="${pagePath('services', 'de')}"]`, ); const servicesAiLink = page .locator(`.primary-nav a[href="${pagePath('servicesAi', 'de')}"]`) .first(); if (!(await servicesAiLink.isVisible())) { await servicesParent.hover(); await expect(servicesAiLink).toBeVisible(); } await servicesAiLink.click(); await page.waitForURL('**/leistungen/ai-integration'); await expect(page.locator('link[rel="canonical"]')).toHaveCount(1); await expect(page.locator('link[rel="canonical"]')).toHaveAttribute( 'href', 'https://antoniolede.de/leistungen/ai-integration', ); await expect(page.locator('script[type="application/ld+json"]')).toHaveCount(1); }); });