/** * Website Structure Tests * * Testet, dass alle wichtigen Seiten und Komponenten existieren. * Diese Tests sind statische Struktur-Tests, keine Runtime-Tests. */ import { existsSync, readFileSync } from 'fs' import { join } from 'path' const ROOT_DIR = join(__dirname, '..') const APP_DIR = join(ROOT_DIR, 'app') const COMPONENTS_DIR = join(ROOT_DIR, 'components') const LIB_DIR = join(ROOT_DIR, 'lib') describe('Website Structure', () => { describe('App Pages', () => { it('has landing page (page.tsx)', () => { expect(existsSync(join(APP_DIR, 'page.tsx'))).toBe(true) }) it('has layout (layout.tsx)', () => { expect(existsSync(join(APP_DIR, 'layout.tsx'))).toBe(true) }) it('has FAQ page', () => { expect(existsSync(join(APP_DIR, 'faq', 'page.tsx'))).toBe(true) }) it('has success page', () => { expect(existsSync(join(APP_DIR, 'success', 'page.tsx'))).toBe(true) }) it('has cancel page', () => { expect(existsSync(join(APP_DIR, 'cancel', 'page.tsx'))).toBe(true) }) it('has admin page', () => { expect(existsSync(join(APP_DIR, 'admin', 'page.tsx'))).toBe(true) }) }) describe('Components', () => { it('has Header component', () => { expect(existsSync(join(COMPONENTS_DIR, 'Header.tsx'))).toBe(true) }) it('has Footer component', () => { expect(existsSync(join(COMPONENTS_DIR, 'Footer.tsx'))).toBe(true) }) it('has PricingSection component', () => { expect(existsSync(join(COMPONENTS_DIR, 'PricingSection.tsx'))).toBe(true) }) }) describe('Configuration', () => { it('has package.json', () => { expect(existsSync(join(ROOT_DIR, 'package.json'))).toBe(true) }) it('has Dockerfile', () => { expect(existsSync(join(ROOT_DIR, 'Dockerfile'))).toBe(true) }) it('has tailwind.config.ts', () => { expect(existsSync(join(ROOT_DIR, 'tailwind.config.ts'))).toBe(true) }) }) describe('Lib', () => { it('has content.ts', () => { expect(existsSync(join(LIB_DIR, 'content.ts'))).toBe(true) }) }) describe('API Routes', () => { it('has content API route', () => { expect(existsSync(join(APP_DIR, 'api', 'content', 'route.ts'))).toBe(true) }) }) }) describe('FAQ Page Content', () => { const faqPagePath = join(APP_DIR, 'faq', 'page.tsx') it('contains FAQ items', () => { const content = readFileSync(faqPagePath, 'utf-8') expect(content).toContain('faqItems') }) it('has question about "Aufgabe"', () => { const content = readFileSync(faqPagePath, 'utf-8') expect(content).toContain('Was ist bei Breakpilot eine') }) it('has question about trial', () => { const content = readFileSync(faqPagePath, 'utf-8') expect(content).toContain('kostenlos testen') }) it('has question about DSGVO/data privacy', () => { const content = readFileSync(faqPagePath, 'utf-8') expect(content).toContain('DSGVO') }) it('has question about cancellation', () => { const content = readFileSync(faqPagePath, 'utf-8') expect(content).toContain('kuendigen') }) it('has at least 10 FAQ items', () => { const content = readFileSync(faqPagePath, 'utf-8') // Zaehle die Fragen (question: Eintraege) const matches = content.match(/question:/g) expect(matches).not.toBeNull() expect(matches!.length).toBeGreaterThanOrEqual(10) }) }) describe('Header Navigation', () => { const headerPath = join(COMPONENTS_DIR, 'Header.tsx') it('has link to FAQ', () => { const content = readFileSync(headerPath, 'utf-8') expect(content).toContain('/faq') }) it('has link to pricing', () => { const content = readFileSync(headerPath, 'utf-8') expect(content).toContain('#pricing') }) it('has link to features', () => { const content = readFileSync(headerPath, 'utf-8') expect(content).toContain('#features') }) it('has mobile menu', () => { const content = readFileSync(headerPath, 'utf-8') expect(content).toContain('mobileMenuOpen') }) }) describe('Landing Page Content', () => { const landingPath = join(APP_DIR, 'page.tsx') it('uses LandingContent component', () => { const content = readFileSync(landingPath, 'utf-8') expect(content).toContain('LandingContent') }) it('imports Header component', () => { const content = readFileSync(landingPath, 'utf-8') expect(content).toContain('Header') }) it('imports Footer component', () => { const content = readFileSync(landingPath, 'utf-8') expect(content).toContain('Footer') }) it('loads content from getContent', () => { const content = readFileSync(landingPath, 'utf-8') expect(content).toContain('getContent') }) it('passes content to LandingContent', () => { const content = readFileSync(landingPath, 'utf-8') expect(content).toContain('content={content}') }) }) describe('PricingSection Content', () => { const pricingPath = join(COMPONENTS_DIR, 'PricingSection.tsx') it('has three plans', () => { const content = readFileSync(pricingPath, 'utf-8') expect(content).toContain('basic') expect(content).toContain('standard') expect(content).toContain('premium') }) it('has trial button', () => { const content = readFileSync(pricingPath, 'utf-8') expect(content).toContain('7 Tage kostenlos') }) it('has email form modal', () => { const content = readFileSync(pricingPath, 'utf-8') expect(content).toContain('showEmailForm') }) it('calls billing service API', () => { const content = readFileSync(pricingPath, 'utf-8') expect(content).toContain('BILLING_API_URL') expect(content).toContain('trial/start') }) }) describe('Admin Page', () => { const adminPath = join(APP_DIR, 'admin', 'page.tsx') it('is a client component', () => { const content = readFileSync(adminPath, 'utf-8') expect(content).toContain("'use client'") }) it('uses AdminLayout', () => { const content = readFileSync(adminPath, 'utf-8') expect(content).toContain('AdminLayout') }) it('has quick actions section', () => { const content = readFileSync(adminPath, 'utf-8') expect(content).toContain('quickActions') }) it('has stats display', () => { const content = readFileSync(adminPath, 'utf-8') expect(content).toContain('Stats') }) it('loads stats from API', () => { const content = readFileSync(adminPath, 'utf-8') expect(content).toContain('loadStats') }) }) describe('Content API Route', () => { const apiPath = join(APP_DIR, 'api', 'content', 'route.ts') it('has GET handler', () => { const content = readFileSync(apiPath, 'utf-8') expect(content).toContain('export async function GET') }) it('has POST handler', () => { const content = readFileSync(apiPath, 'utf-8') expect(content).toContain('export async function POST') }) it('validates admin key', () => { const content = readFileSync(apiPath, 'utf-8') expect(content).toContain('x-admin-key') expect(content).toContain('Unauthorized') }) it('uses content lib', () => { const content = readFileSync(apiPath, 'utf-8') expect(content).toContain('getContent') expect(content).toContain('saveContent') }) }) describe('Content Library', () => { const contentPath = join(LIB_DIR, 'content.ts') it('exports getContent function', () => { const content = readFileSync(contentPath, 'utf-8') expect(content).toContain('export function getContent') }) it('exports saveContent function', () => { const content = readFileSync(contentPath, 'utf-8') expect(content).toContain('export function saveContent') }) it('exports WebsiteContent type', () => { const content = readFileSync(contentPath, 'utf-8') expect(content).toContain('WebsiteContent') }) it('exports HeroContent type', () => { const content = readFileSync(contentPath, 'utf-8') expect(content).toContain('HeroContent') }) it('has default content', () => { const content = readFileSync(contentPath, 'utf-8') expect(content).toContain('defaultContent') }) it('stores content as JSON', () => { const content = readFileSync(contentPath, 'utf-8') expect(content).toContain('website.json') }) }) describe('Landing Page Uses Dynamic Content', () => { const landingPath = join(APP_DIR, 'page.tsx') const landingContentPath = join(COMPONENTS_DIR, 'LandingContent.tsx') it('imports getContent', () => { const content = readFileSync(landingPath, 'utf-8') expect(content).toContain('import { getContent }') }) it('calls getContent', () => { const content = readFileSync(landingPath, 'utf-8') expect(content).toContain('getContent()') }) it('LandingContent component exists', () => { expect(existsSync(landingContentPath)).toBe(true) }) it('LandingContent has hero section', () => { const content = readFileSync(landingContentPath, 'utf-8') expect(content).toContain('Hero') }) it('LandingContent has pricing section', () => { const content = readFileSync(landingContentPath, 'utf-8') expect(content).toContain('PricingSection') }) })