Integrate Prisma for content; enhance SEO, i18n, and deployment workflows
Co-authored-by: dennis <dennis@konkol.net>
This commit is contained in:
27
e2e/consent.spec.ts
Normal file
27
e2e/consent.spec.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test.describe("Consent banner", () => {
|
||||
test("banner shows and can be accepted", async ({ page, context }) => {
|
||||
// Start clean
|
||||
await context.clearCookies();
|
||||
|
||||
await page.goto("/en", { waitUntil: "domcontentloaded" });
|
||||
|
||||
// Banner should appear on public pages when no consent is set yet
|
||||
const bannerTitle = page.getByText(/Privacy settings|Datenschutz-Einstellungen/i);
|
||||
await expect(bannerTitle).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Accept all
|
||||
const acceptAll = page.getByRole("button", { name: /Accept all|Alles akzeptieren/i });
|
||||
await acceptAll.click();
|
||||
|
||||
// Banner disappears
|
||||
await expect(bannerTitle).toBeHidden({ timeout: 10000 });
|
||||
|
||||
// Cookie is written
|
||||
const cookies = await context.cookies();
|
||||
const consentCookie = cookies.find((c) => c.name === "dk0_consent_v1");
|
||||
expect(consentCookie).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import { test, expect } from '@playwright/test';
|
||||
*/
|
||||
test.describe('Critical Paths', () => {
|
||||
test('Home page loads and displays correctly', async ({ page }) => {
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
await page.goto('/en', { waitUntil: 'networkidle' });
|
||||
|
||||
// Wait for page to be fully loaded
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
@@ -25,7 +25,7 @@ test.describe('Critical Paths', () => {
|
||||
});
|
||||
|
||||
test('Projects page loads and displays projects', async ({ page }) => {
|
||||
await page.goto('/projects', { waitUntil: 'networkidle' });
|
||||
await page.goto('/en/projects', { waitUntil: 'networkidle' });
|
||||
|
||||
// Wait for projects to load
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
@@ -45,7 +45,7 @@ test.describe('Critical Paths', () => {
|
||||
|
||||
test('Individual project page loads', async ({ page }) => {
|
||||
// First, get a project slug from the projects page
|
||||
await page.goto('/projects', { waitUntil: 'networkidle' });
|
||||
await page.goto('/en/projects', { waitUntil: 'networkidle' });
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
// Try to find a project link
|
||||
|
||||
@@ -20,7 +20,7 @@ test.describe('Hydration Tests', () => {
|
||||
});
|
||||
|
||||
// Navigate to home page
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
await page.goto('/en', { waitUntil: 'networkidle' });
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
// Check for hydration errors
|
||||
@@ -51,7 +51,7 @@ test.describe('Hydration Tests', () => {
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Check for duplicate key warnings
|
||||
@@ -71,11 +71,11 @@ test.describe('Hydration Tests', () => {
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
await page.goto('/en', { waitUntil: 'networkidle' });
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
// Navigate to projects page via link
|
||||
const projectsLink = page.locator('a[href="/projects"], a[href*="projects"]').first();
|
||||
const projectsLink = page.locator('a[href*="/projects"]').first();
|
||||
if (await projectsLink.count() > 0) {
|
||||
await projectsLink.click();
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
@@ -90,7 +90,7 @@ test.describe('Hydration Tests', () => {
|
||||
});
|
||||
|
||||
test('Server and client HTML match', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
|
||||
// Get initial HTML
|
||||
const initialHTML = await page.content();
|
||||
@@ -108,7 +108,7 @@ test.describe('Hydration Tests', () => {
|
||||
});
|
||||
|
||||
test('Interactive elements work after hydration', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Try to find and click interactive elements
|
||||
|
||||
17
e2e/i18n.spec.ts
Normal file
17
e2e/i18n.spec.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test.describe("i18n routing", () => {
|
||||
test("language switcher navigates between locales", async ({ page }) => {
|
||||
await page.goto("/en", { waitUntil: "domcontentloaded" });
|
||||
|
||||
// Buttons are "EN"/"DE" in the header
|
||||
const deButton = page.getByRole("button", { name: "DE" });
|
||||
if (await deButton.count()) {
|
||||
await deButton.click();
|
||||
await expect(page).toHaveURL(/\/de(\/|$)/);
|
||||
} else {
|
||||
test.skip();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
22
e2e/seo.spec.ts
Normal file
22
e2e/seo.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test.describe("SEO endpoints", () => {
|
||||
test("robots.txt is served and contains sitemap", async ({ request }) => {
|
||||
const res = await request.get("/robots.txt");
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const txt = await res.text();
|
||||
expect(txt).toContain("User-agent:");
|
||||
expect(txt).toContain("Sitemap:");
|
||||
});
|
||||
|
||||
test("sitemap.xml is served and contains locale routes", async ({ request }) => {
|
||||
const res = await request.get("/sitemap.xml");
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const xml = await res.text();
|
||||
expect(xml).toContain('<urlset xmlns="https://www.sitemaps.org/schemas/sitemap/0.9">');
|
||||
// At least the localized home routes should exist
|
||||
expect(xml).toMatch(/\/en<\/loc>/);
|
||||
expect(xml).toMatch(/\/de<\/loc>/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user