Files
breakpilot-lehrer/website/tests/structure.test.ts
Benjamin Boenisch 5a31f52310 Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:26 +01:00

318 lines
9.0 KiB
TypeScript

/**
* 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')
})
})