feat(T02): Static pages – home, referenzen, kontakt, legal (impressum/datenschutz/agb)

This commit is contained in:
Implementation Engineer
2026-07-10 01:02:27 +02:00
parent e9da21b57e
commit db5080df48
15 changed files with 1618 additions and 109 deletions
+68
View File
@@ -0,0 +1,68 @@
import { test, expect } from "@playwright/test";
test.describe("Home Page", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/");
});
test("should display hero with headline 'Veranstaltungstechnik'", async ({ page }) => {
await expect(page.locator('[data-testid="hero-headline"]')).toContainText("Veranstaltungstechnik");
});
test("should have CTA button linking to /mietkatalog", async ({ page }) => {
const cta = page.locator('[data-testid="hero-cta-mietkatalog"]');
await expect(cta).toBeVisible();
await expect(cta).toHaveAttribute("to", "/mietkatalog");
});
test("should have CTA button linking to /kontakt", async ({ page }) => {
const cta = page.locator('[data-testid="hero-cta-kontakt"]');
await expect(cta).toBeVisible();
await expect(cta).toHaveAttribute("to", "/kontakt");
});
test("should display all 8 services", async ({ page }) => {
const servicesSection = page.locator('[data-testid="services-section"]');
await expect(servicesSection).toBeVisible();
await expect(servicesSection).toContainText("Vermietung");
await expect(servicesSection).toContainText("Verkauf");
await expect(servicesSection).toContainText("Personal");
await expect(servicesSection).toContainText("Transport");
await expect(servicesSection).toContainText("Lagerung");
await expect(servicesSection).toContainText("Werkstatt");
await expect(servicesSection).toContainText("Installation");
await expect(servicesSection).toContainText("Booking");
});
test("should display speaker grid with 6 equipment types", async ({ page }) => {
const grid = page.locator('[data-testid="speaker-grid"]');
await expect(grid).toBeVisible();
await expect(grid).toContainText("Lautsprecher");
await expect(grid).toContainText("Mischpulte");
await expect(grid).toContainText("Lichtanlagen");
await expect(grid).toContainText("Rigging");
await expect(grid).toContainText("Traversen");
await expect(grid).toContainText("Komplett-Setups");
});
test("should display about section with stats", async ({ page }) => {
const about = page.locator('[data-testid="about-section"]');
await expect(about).toBeVisible();
await expect(about).toContainText("20+");
await expect(about).toContainText("1000+");
await expect(about).toContainText("500+");
});
test("should have mietkatalog CTA section", async ({ page }) => {
const cta = page.locator('[data-testid="mietkatalog-cta"]');
await expect(cta).toBeVisible();
const link = page.locator('[data-testid="cta-mietkatalog-link"]');
await expect(link).toBeVisible();
});
test("should be mobile-responsive at 375px width", async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await expect(page.locator('[data-testid="hero-headline"]')).toBeVisible();
await expect(page.locator('[data-testid="services-section"]')).toBeVisible();
});
});
+74
View File
@@ -0,0 +1,74 @@
import { test, expect } from "@playwright/test";
test.describe("Kontakt Page", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/kontakt");
});
test("should display contact page heading", async ({ page }) => {
await expect(page.locator('[data-testid="kontakt-headline"]')).toContainText("Kontakt");
});
test("should show office address card", async ({ page }) => {
const card = page.locator('[data-testid="office-card"]');
await expect(card).toBeVisible();
await expect(card).toContainText("Grockelhofen 10");
await expect(card).toContainText("89340 Leipheim");
});
test("should show warehouse address card", async ({ page }) => {
const card = page.locator('[data-testid="warehouse-card"]');
await expect(card).toBeVisible();
await expect(card).toContainText("Ellzee");
});
test("should show opening hours card", async ({ page }) => {
const card = page.locator('[data-testid="hours-card"]');
await expect(card).toBeVisible();
await expect(card).toContainText("Öffnungszeiten");
});
test("should show 2 contact persons", async ({ page }) => {
await expect(page.locator('[data-testid="contact-person-markus"]')).toBeVisible();
await expect(page.locator('[data-testid="contact-person-thomas"]')).toBeVisible();
});
test("should have form with all required fields", async ({ page }) => {
await expect(page.locator('[data-testid="form-name"]')).toBeVisible();
await expect(page.locator('[data-testid="form-email"]')).toBeVisible();
await expect(page.locator('[data-testid="form-phone"]')).toBeVisible();
await expect(page.locator('[data-testid="form-message"]')).toBeVisible();
await expect(page.locator('[data-testid="form-privacy"]')).toBeVisible();
await expect(page.locator('[data-testid="form-submit"]')).toBeVisible();
});
test("should block submit when email is empty", async ({ page }) => {
await page.locator('[data-testid="form-name"]').fill("Test User");
await page.locator('[data-testid="form-message"]').fill("Test message");
await page.locator('[data-testid="form-privacy"]').check();
await page.locator('[data-testid="form-submit"]').click();
await expect(page.locator('[data-testid="error-email"]')).toBeVisible();
});
test("should block submit when privacy consent unchecked", async ({ page }) => {
await page.locator('[data-testid="form-name"]').fill("Test User");
await page.locator('[data-testid="form-email"]').fill("test@example.com");
await page.locator('[data-testid="form-message"]').fill("Test message");
await page.locator('[data-testid="form-submit"]').click();
await expect(page.locator('[data-testid="error-privacy"]')).toBeVisible();
});
test("should block submit when name is empty", async ({ page }) => {
await page.locator('[data-testid="form-email"]').fill("test@example.com");
await page.locator('[data-testid="form-message"]').fill("Test message");
await page.locator('[data-testid="form-privacy"]').check();
await page.locator('[data-testid="form-submit"]').click();
await expect(page.locator('[data-testid="error-name"]')).toBeVisible();
});
test("should be mobile-responsive at 375px width", async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await expect(page.locator('[data-testid="contact-form"]')).toBeVisible();
await expect(page.locator('[data-testid="office-card"]')).toBeVisible();
});
});
+96
View File
@@ -0,0 +1,96 @@
import { test, expect } from "@playwright/test";
test.describe("Impressum Page", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/impressum");
});
test("should display Impressum heading", async ({ page }) => {
await expect(page.locator("h1")).toContainText("Impressum");
});
test("should contain Hammerschmidt u. Mössle GbR", async ({ page }) => {
await expect(page.locator("body")).toContainText("Hammerschmidt u. Mössle GbR");
});
test("should contain address Grockelhofen 10, 89340 Leipheim", async ({ page }) => {
await expect(page.locator("body")).toContainText("Grockelhofen 10");
await expect(page.locator("body")).toContainText("89340 Leipheim");
});
test("should contain phone number", async ({ page }) => {
await expect(page.locator("body")).toContainText("204433");
});
test("should contain email info@hms-licht-ton.de", async ({ page }) => {
await expect(page.locator('a[href="mailto:info@hms-licht-ton.de"]')).toBeVisible();
});
test("should be mobile-responsive at 375px width", async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await expect(page.locator("h1")).toBeVisible();
});
});
test.describe("Datenschutz Page", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/datenschutz");
});
test("should display Datenschutz heading", async ({ page }) => {
await expect(page.locator("h1")).toContainText("Datenschutz");
});
test("should contain DSGVO-relevant sections", async ({ page }) => {
const body = page.locator("body");
await expect(body).toContainText("Datenerhebung");
await expect(body).toContainText("Cookies");
await expect(body).toContainText("Kontaktformular");
await expect(body).toContainText("Server-Log");
await expect(body).toContainText("Drittanbieter");
});
test("should mention responsible party", async ({ page }) => {
await expect(page.locator("body")).toContainText("Hammerschmidt u. Mössle GbR");
});
test("should be mobile-responsive at 375px width", async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await expect(page.locator("h1")).toBeVisible();
});
});
test.describe("AGB Vermietung Page", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/agb-vermietung");
});
test("should display AGB heading", async ({ page }) => {
await expect(page.locator("h1")).toContainText("AGB");
});
test("should contain Geltungsbereich section", async ({ page }) => {
await expect(page.locator("body")).toContainText("Geltungsbereich");
});
test("should contain Preise section", async ({ page }) => {
await expect(page.locator("body")).toContainText("Preise");
});
test("should contain Zahlungsbedingungen section", async ({ page }) => {
await expect(page.locator("body")).toContainText("Zahlungsbedingungen");
});
test("should contain Haftung section", async ({ page }) => {
await expect(page.locator("body")).toContainText("Haftung");
});
test("should contain Rückgabe section", async ({ page }) => {
await expect(page.locator("body")).toContainText("Rückgabe");
});
test("should be mobile-responsive at 375px width", async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await expect(page.locator("h1")).toBeVisible();
});
});
+80
View File
@@ -0,0 +1,80 @@
import { test, expect } from "@playwright/test";
test.describe("Referenzen Page", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/referenzen");
});
test("should display page heading", async ({ page }) => {
await expect(page).toHaveTitle(/Referenzen/);
});
test("should show 9 gallery images in grid", async ({ page }) => {
const grid = page.locator('[data-testid="gallery-grid"]');
await expect(grid).toBeVisible();
const items = grid.locator('[data-testid^="gallery-item-"]');
await expect(items).toHaveCount(9);
});
test("should have 4 category filter chips", async ({ page }) => {
const chips = page.locator('[data-testid="filter-chips"]');
await expect(chips).toBeVisible();
await expect(chips).toContainText("Alle");
await expect(chips).toContainText("Open-Air");
await expect(chips).toContainText("Indoor");
await expect(chips).toContainText("Rigging");
});
test("should filter images by category", async ({ page }) => {
// Click Open-Air filter
await page.locator('[data-testid="filter-open-air"]').click();
const grid = page.locator('[data-testid="gallery-grid"]');
const items = grid.locator('[data-testid^="gallery-item-"]');
const count = await items.count();
expect(count).toBeLessThan(9);
expect(count).toBeGreaterThan(0);
// Click Alle to reset
await page.locator('[data-testid="filter-alle"]').click();
await expect(items).toHaveCount(9);
});
test("should open lightbox on image click", async ({ page }) => {
const firstItem = page.locator('[data-testid="gallery-item-1"]');
await firstItem.click();
const lightbox = page.locator('[data-testid="lightbox"]');
await expect(lightbox).toBeVisible();
});
test("should close lightbox with close button", async ({ page }) => {
await page.locator('[data-testid="gallery-item-1"]').click();
const lightbox = page.locator('[data-testid="lightbox"]');
await expect(lightbox).toBeVisible();
await page.locator('[data-testid="lightbox-close"]').click();
await expect(lightbox).not.toBeVisible();
});
test("should navigate to next image in lightbox", async ({ page }) => {
await page.locator('[data-testid="gallery-item-1"]').click();
const lightbox = page.locator('[data-testid="lightbox"]');
await expect(lightbox).toBeVisible();
const firstCaption = await lightbox.locator("p").textContent();
await page.locator('[data-testid="lightbox-next"]').click();
const secondCaption = await lightbox.locator("p").textContent();
expect(firstCaption).not.toBe(secondCaption);
});
test("should navigate to prev image in lightbox", async ({ page }) => {
await page.locator('[data-testid="gallery-item-1"]').click();
const lightbox = page.locator('[data-testid="lightbox"]');
await expect(lightbox).toBeVisible();
await page.locator('[data-testid="lightbox-prev"]').click();
await expect(lightbox).toBeVisible();
});
test("should be mobile-responsive at 375px width", async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await expect(page.locator('[data-testid="gallery-grid"]')).toBeVisible();
await expect(page.locator('[data-testid="filter-chips"]')).toBeVisible();
});
});
+76
View File
@@ -0,0 +1,76 @@
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
describe("Home Page", () => {
const pagePath = resolve(__dirname, "../../pages/index.vue");
const content = readFileSync(pagePath, "utf-8");
it("should render hero with headline 'Veranstaltungstechnik'", () => {
expect(content).toContain("Veranstaltungstechnik");
expect(content).toContain('data-testid="hero-headline"');
});
it("should have CTA button linking to /mietkatalog", () => {
expect(content).toContain('to="/mietkatalog"');
expect(content).toContain("Mietkatalog");
});
it("should have CTA button linking to /kontakt", () => {
expect(content).toContain('to="/kontakt"');
expect(content).toContain("Beratung anfragen");
});
it("should display all 8 services", () => {
expect(content).toContain("Vermietung");
expect(content).toContain("Verkauf");
expect(content).toContain("Personal");
expect(content).toContain("Transport");
expect(content).toContain("Lagerung");
expect(content).toContain("Werkstatt");
expect(content).toContain("Installation");
expect(content).toContain("Booking");
});
it("should have services section with data-testid", () => {
expect(content).toContain('data-testid="services-section"');
});
it("should have speaker grid with 6 equipment types", () => {
expect(content).toContain('data-testid="speaker-grid"');
expect(content).toContain("Lautsprecher");
expect(content).toContain("Mischpulte");
expect(content).toContain("Lichtanlagen");
expect(content).toContain("Rigging");
expect(content).toContain("Traversen");
expect(content).toContain("Komplett-Setups");
});
it("should have about section with stats", () => {
expect(content).toContain('data-testid="about-section"');
expect(content).toContain('data-testid="stat-years"');
expect(content).toContain("20+");
expect(content).toContain('data-testid="stat-devices"');
expect(content).toContain("1000+");
expect(content).toContain('data-testid="stat-events"');
expect(content).toContain("500+");
});
it("should have mietkatalog CTA section", () => {
expect(content).toContain('data-testid="mietkatalog-cta"');
expect(content).toContain('data-testid="cta-mietkatalog-link"');
});
it("should use TypeScript with lang=ts", () => {
expect(content).toContain('<script setup lang="ts">');
});
it("should use Tailwind classes, no inline styles", () => {
expect(content).not.toContain('style="');
});
it("should use useHead for page title", () => {
expect(content).toContain("useHead");
expect(content).toContain("HMS Licht");
});
});
+112
View File
@@ -0,0 +1,112 @@
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
describe("Kontakt Page", () => {
const pagePath = resolve(__dirname, "../../pages/kontakt.vue");
const content = readFileSync(pagePath, "utf-8");
it("should have page title Kontakt", () => {
expect(content).toContain("Kontakt");
});
it("should have office address card with Leipheim address", () => {
expect(content).toContain('data-testid="office-card"');
expect(content).toContain("Grockelhofen 10");
expect(content).toContain("89340 Leipheim");
});
it("should have warehouse address card with Ellzee", () => {
expect(content).toContain('data-testid="warehouse-card"');
expect(content).toContain("Ellzee");
});
it("should have opening hours card", () => {
expect(content).toContain('data-testid="hours-card"');
expect(content).toContain("Öffnungszeiten");
});
it("should have 2 contact persons", () => {
expect(content).toContain("Markus Hammerschmidt");
expect(content).toContain("Thomas Mössle");
});
it("should have form with name field (required)", () => {
expect(content).toContain('data-testid="form-name"');
expect(content).toContain("Name *");
});
it("should have form with email field (required)", () => {
expect(content).toContain('data-testid="form-email"');
expect(content).toContain("E-Mail *");
});
it("should have form with phone field (optional)", () => {
expect(content).toContain('data-testid="form-phone"');
expect(content).toContain("Telefon");
});
it("should have form with message field (required)", () => {
expect(content).toContain('data-testid="form-message"');
expect(content).toContain("Nachricht *");
});
it("should have form with privacy consent checkbox (required)", () => {
expect(content).toContain('data-testid="form-privacy"');
expect(content).toContain('type="checkbox"');
expect(content).toContain("Datenschutzerklärung");
});
it("should have submit button", () => {
expect(content).toContain('data-testid="form-submit"');
expect(content).toContain("Anfrage senden");
});
it("should have email validation with regex", () => {
expect(content).toContain("emailRegex");
expect(content).toContain("emailRegex.test");
});
it("should block submit when email is empty (validation error)", () => {
expect(content).toContain('data-testid="error-email"');
expect(content).toContain("Bitte geben Sie Ihre E-Mail-Adresse ein");
});
it("should block submit when privacy consent unchecked (validation error)", () => {
expect(content).toContain('data-testid="error-privacy"');
expect(content).toContain("Bitte stimmen Sie der Datenschutzerklärung zu");
});
it("should have validateField function", () => {
expect(content).toContain("validateField");
expect(content).toContain("validateAll");
});
it("should POST to /api/contact on submit", () => {
expect(content).toContain("$fetch");
expect(content).toContain("/api/contact");
expect(content).toContain("POST");
});
it("should have success state", () => {
expect(content).toContain('data-testid="success-message"');
expect(content).toContain("Vielen Dank");
});
it("should have error state", () => {
expect(content).toContain('data-testid="error-message"');
expect(content).toContain("Fehler");
});
it("should use TypeScript with lang=ts", () => {
expect(content).toContain('<script setup lang="ts">');
});
it("should use Tailwind classes, no inline styles", () => {
expect(content).not.toContain('style="');
});
it("should use useHead for page title", () => {
expect(content).toContain("useHead");
});
});
@@ -0,0 +1,89 @@
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
describe("Referenzen Page", () => {
const pagePath = resolve(__dirname, "../../pages/referenzen.vue");
const content = readFileSync(pagePath, "utf-8");
it("should have page title Referenzen", () => {
expect(content).toContain("Referenzen");
});
it("should have 9 gallery images", () => {
expect(content).toContain('data-testid="gallery-grid"');
const matches = content.match(/picsum\.photos/g);
expect(matches).not.toBeNull();
expect(matches!.length).toBe(9);
});
it("should have 4 category filter chips", () => {
expect(content).toContain('data-testid="filter-chips"');
expect(content).toContain("Alle");
expect(content).toContain("Open-Air");
expect(content).toContain("Indoor");
expect(content).toContain("Rigging");
});
it("should have Lightbox component", () => {
expect(content).toContain("Lightbox");
expect(content).toContain(':is-open="lightboxOpen"');
expect(content).toContain('@close="closeLightbox"');
expect(content).toContain('@prev="prevImage"');
expect(content).toContain('@next="nextImage"');
});
it("should have filter logic with computed filteredImages", () => {
expect(content).toContain("filteredImages");
expect(content).toContain("activeFilter");
expect(content).toContain("computed");
});
it("should have openLightbox function", () => {
expect(content).toContain("openLightbox");
expect(content).toContain("currentIndex");
});
it("should use TypeScript with lang=ts", () => {
expect(content).toContain('<script setup lang="ts">');
});
it("should use Tailwind classes, no inline styles", () => {
expect(content).not.toContain('style="');
});
it("should use useHead for page title", () => {
expect(content).toContain("useHead");
});
});
describe("Lightbox Component", () => {
const componentPath = resolve(__dirname, "../../components/Lightbox.vue");
const content = readFileSync(componentPath, "utf-8");
it("should have close button with data-testid", () => {
expect(content).toContain('data-testid="lightbox-close"');
});
it("should have prev button with data-testid", () => {
expect(content).toContain('data-testid="lightbox-prev"');
});
it("should have next button with data-testid", () => {
expect(content).toContain('data-testid="lightbox-next"');
});
it("should have data-testid lightbox", () => {
expect(content).toContain('data-testid="lightbox"');
});
it("should emit close, prev, next events", () => {
expect(content).toContain("close");
expect(content).toContain("prev");
expect(content).toContain("next");
});
it("should use TypeScript with lang=ts", () => {
expect(content).toContain('<script setup lang="ts">');
});
});