chore: clean up docker-compose files and enhance SQL migration for skills #7

Merged
Lars merged 1 commits from develop into main 2026-04-29 13:43:57 +02:00
4 changed files with 30 additions and 16 deletions

View File

@ -2,6 +2,18 @@
-- One-time import of skills with categories from karatetrainer.net
-- Source: https://karatetrainer.net/index.php?title=Fähigkeitsmatrix
-- ON CONFLICT (name) erfordert einen UNIQUE-Constraint auf skills.name (003/007 legten keinen an)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint c
JOIN pg_class t ON c.conrelid = t.oid
WHERE t.relname = 'skills' AND c.contype = 'u' AND c.conname = 'skills_name_unique'
) THEN
ALTER TABLE skills ADD CONSTRAINT skills_name_unique UNIQUE (name);
END IF;
END $$;
-- Create skill categories first
INSERT INTO skill_categories (name, description, sort_order) VALUES
('Kihon', 'Grundtechniken', 1),

View File

@ -1,5 +1,3 @@
version: '3.8'
# Keine festen container_name — Compose-Namen haben Projektprefix (<projekt>-postgres-1).
# Gleiche Variablennamen wie docker-compose.yml; andere Werte in einer eigenen .env neben dieser Datei.

View File

@ -1,5 +1,3 @@
version: '3.8'
services:
postgres:
image: postgres:16-alpine

View File

@ -43,8 +43,10 @@ test('2. Dashboard lädt ohne Fehler', async ({ page }) => {
// Warte bis Spinner verschwunden
await expect(page.locator('.spinner')).toHaveCount(0, { timeout: 10000 });
// Prüfe ob Dashboard-Inhalt da ist
await expect(page.locator('text=Willkommen')).toBeVisible({ timeout: 5000 });
// Zwei verschiedene "Willkommen"-Texte im Dashboard → kein ambiguity locator('text=Willkommen')
await expect(
page.getByRole('heading', { name: /Willkommen bei Shinkan/i }),
).toBeVisible({ timeout: 5000 });
await page.screenshot({ path: 'screenshots/02-dashboard.png' });
console.log('✓ Dashboard OK');
@ -53,8 +55,11 @@ test('2. Dashboard lädt ohne Fehler', async ({ page }) => {
test('3. Navigation zu Übungen', async ({ page }) => {
await login(page);
// Bottom-Nav: Übungen klicken
await page.click('text=Übungen');
// Bei Viewport ≥1024px ist .bottom-nav versteckt — Mobile garantieren wie in playwright.config.js
await page.setViewportSize({ width: 390, height: 844 });
// Desktop-Sidebar enthält ebenfalls Übungen nur Mobile-Bottom-Nav klicken (sichtbarer Link)
await page.locator('.bottom-nav a[href="/exercises"]').click();
await page.waitForLoadState('networkidle');
// Prüfe ob Übungen-Seite geladen
@ -66,9 +71,9 @@ test('3. Navigation zu Übungen', async ({ page }) => {
test('4. Navigation zu Vereine', async ({ page }) => {
await login(page);
await page.setViewportSize({ width: 390, height: 844 });
// Bottom-Nav: Vereine klicken
await page.click('text=Vereine');
await page.locator('.bottom-nav a[href="/clubs"]').click();
await page.waitForLoadState('networkidle');
// Prüfe ob Vereine-Seite geladen
@ -109,16 +114,17 @@ test('6. Bottom-Nav sichtbar (Mobile)', async ({ page }) => {
test('7. Session-Persistenz nach Reload', async ({ page }) => {
await login(page);
// Warte auf Dashboard
await expect(page.locator('.spinner')).toHaveCount(0, { timeout: 10000 });
// Reload
await page.reload();
await page.reload({ waitUntil: 'domcontentloaded' });
await page.waitForLoadState('networkidle');
// Sollte NICHT zur Login-Seite redirecten
const loginButton = page.locator('button:has-text("Login")');
await expect(loginButton).toHaveCount(0, { timeout: 5000 });
// Auth lädt erst nach Spinner nicht auf /login stranden (stabiler als Button „Login“-Tab auf Login-Screen)
await expect(page.locator('.spinner')).toHaveCount(0, { timeout: 20000 });
await expect(page).not.toHaveURL('**/login', { timeout: 20000 });
await expect(page.locator('h1').filter({ hasText: /^Dashboard$/ })).toBeVisible({
timeout: 10000,
});
await page.screenshot({ path: 'screenshots/07-nach-reload.png' });
console.log('✓ Session bleibt nach Reload erhalten');