This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/website/tests/structure.test.ts
Benjamin Admin 21a844cb8a fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:51:32 +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')
})
})