Rewrite the constraint specs and docs for the service-led information architecture.
Assertions now follow the eighteen prerenderable paths, the gist dialog, and the six-case evidence set. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { expect, test, type Page } from '@playwright/test';
|
||||
import { SITE_CONTENT_DATA } from '../src/app/core/content/site-content';
|
||||
import { prerenderablePaths } from '../src/app/core/routing/route-paths';
|
||||
import { expectHead, jsonLdGraph, pagePath } from './helpers';
|
||||
|
||||
async function ensurePrimaryNavOpen(page: Page): Promise<void> {
|
||||
@@ -28,10 +29,10 @@ test.describe('server-rendered metadata', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('projects, AI service, contact, legal and 404 keep locale metadata', async ({ request }) => {
|
||||
test('projects, services, 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, 'services', locale);
|
||||
await expectHead(request, 'contact', locale);
|
||||
await expectHead(request, 'imprint', locale);
|
||||
await expectHead(request, 'notFound', locale);
|
||||
@@ -57,6 +58,15 @@ test.describe('server-rendered metadata', () => {
|
||||
await expect(nav.locator(`a[href="${pagePath('home', 'de')}"]`)).toHaveCount(1);
|
||||
});
|
||||
|
||||
test('every prerenderable path returns 200', async ({ request }) => {
|
||||
const paths = prerenderablePaths();
|
||||
expect(paths).toHaveLength(18);
|
||||
for (const path of paths) {
|
||||
const response = await request.get(path);
|
||||
expect(response.status(), path).toBe(200);
|
||||
}
|
||||
});
|
||||
|
||||
test('unmatched URLs return 404 and a real route stays 200', async ({ request }) => {
|
||||
const home = await request.get('/');
|
||||
expect(home.status(), 'GET /').toBe(200);
|
||||
@@ -68,7 +78,7 @@ test.describe('server-rendered metadata', () => {
|
||||
test('client navigation does not duplicate head tags', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await ensurePrimaryNavOpen(page);
|
||||
await page.locator('a.contact-cta').first().waitFor();
|
||||
await page.locator('.language-switch').first().waitFor();
|
||||
|
||||
const assertUnique = async (canonical: string, description: string) => {
|
||||
await expect(page.locator('link[rel="canonical"]')).toHaveCount(1);
|
||||
@@ -97,23 +107,34 @@ test.describe('server-rendered metadata', () => {
|
||||
);
|
||||
|
||||
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 page.locator(`.primary-nav a[href="${pagePath('services', 'de')}"]`).click();
|
||||
await page.waitForURL('**/leistungen');
|
||||
await expect(page.locator('link[rel="canonical"]')).toHaveCount(1);
|
||||
await expect(page.locator('link[rel="canonical"]')).toHaveAttribute(
|
||||
'href',
|
||||
'https://antoniolede.de/leistungen/ai-integration',
|
||||
'https://antoniolede.de/leistungen',
|
||||
);
|
||||
await expect(page.locator('script[type="application/ld+json"]')).toHaveCount(1);
|
||||
});
|
||||
|
||||
test('legacy service URLs return HTTP 308 with the fragment Location', async ({ request }) => {
|
||||
const redirects = [
|
||||
['/leistungen/software', '/leistungen#software-data'],
|
||||
['/leistungen/software/', '/leistungen#software-data'],
|
||||
['/leistungen/hardware-netzwerk', '/leistungen#infrastructure-network'],
|
||||
['/leistungen/cluster', '/leistungen#platform-operations'],
|
||||
['/leistungen/ai-integration', '/leistungen#ai-workflows'],
|
||||
['/en/services/software', '/en/services#software-data'],
|
||||
['/en/services/hardware-network', '/en/services#infrastructure-network'],
|
||||
['/en/services/clusters', '/en/services#platform-operations'],
|
||||
['/en/services/ai-integration', '/en/services#ai-workflows'],
|
||||
['/en/services/ai-integration/', '/en/services#ai-workflows'],
|
||||
] as const;
|
||||
|
||||
for (const [from, to] of redirects) {
|
||||
const response = await request.get(from, { maxRedirects: 0 });
|
||||
expect(response.status(), from).toBe(308);
|
||||
expect(response.headers().location, from).toBe(to);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user