feat(compliance): P-01 Rechtstextseiten technisch anlegen (0.8.69)
All checks were successful
Deploy Development / deploy (push) Successful in 36s
Test Suite / pytest-backend (push) Successful in 32s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 7s
Test Suite / playwright-tests (push) Successful in 32s
All checks were successful
Deploy Development / deploy (push) Successful in 36s
Test Suite / pytest-backend (push) Successful in 32s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 7s
Test Suite / playwright-tests (push) Successful in 32s
Öffentliche Routen /impressum /datenschutz /nutzungsbedingungen /medienrichtlinie ohne Auth erreichbar. LegalPage-Komponente mit deutlichem Platzhalterhinweis und strukturierten Pflichtfeldern je Rechtstext. Links in LoginPage-Footer und DesktopSidebar-Footer. KRIT-01 technischer Teil geschlossen. Juristische Inhalte bleiben offen — Betreiber + Rechtsanwalt erforderlich. - frontend/src/pages/LegalPage.jsx (neu) - frontend/src/App.jsx: 4 öffentliche Routen - frontend/src/pages/LoginPage.jsx: Rechtstext-Links im Footer - frontend/src/components/DesktopSidebar.jsx: Links im Sidebar-Footer - tests/dev-smoke-test.spec.js: 5 neue P-01-Tests version: 0.8.69 (backend + frontend) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d73ed13f87
commit
d7ed0c0e9b
|
|
@ -1,6 +1,6 @@
|
||||||
# Shinkan Jinkendo Version Information
|
# Shinkan Jinkendo Version Information
|
||||||
|
|
||||||
APP_VERSION = "0.8.68"
|
APP_VERSION = "0.8.69"
|
||||||
BUILD_DATE = "2026-05-10"
|
BUILD_DATE = "2026-05-10"
|
||||||
DB_SCHEMA_VERSION = "20260508049"
|
DB_SCHEMA_VERSION = "20260508049"
|
||||||
|
|
||||||
|
|
@ -29,6 +29,14 @@ MODULE_VERSIONS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
CHANGELOG = [
|
CHANGELOG = [
|
||||||
|
{
|
||||||
|
"version": "0.8.69",
|
||||||
|
"date": "2026-05-10",
|
||||||
|
"changes": [
|
||||||
|
"Compliance P-01 (KRIT-01) technischer Teil: Rechtstextseiten /impressum, /datenschutz, /nutzungsbedingungen, /medienrichtlinie als oeffentliche Routen angelegt (Platzhalter, kein Auth erforderlich)",
|
||||||
|
"Login-Seite und Desktop-Sidebar enthalten Links zu allen vier Rechtstextseiten",
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "0.8.68",
|
"version": "0.8.68",
|
||||||
"date": "2026-05-10",
|
"date": "2026-05-10",
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import AdminUsersPage from './pages/AdminUsersPage'
|
||||||
import AdminHomeRedirect from './components/AdminHomeRedirect'
|
import AdminHomeRedirect from './components/AdminHomeRedirect'
|
||||||
import PlatformAdminRoute from './components/PlatformAdminRoute'
|
import PlatformAdminRoute from './components/PlatformAdminRoute'
|
||||||
import MediaLibraryPage from './pages/MediaLibraryPage'
|
import MediaLibraryPage from './pages/MediaLibraryPage'
|
||||||
|
import LegalPage from './pages/LegalPage'
|
||||||
import ActiveClubSwitcher from './components/ActiveClubSwitcher'
|
import ActiveClubSwitcher from './components/ActiveClubSwitcher'
|
||||||
import InactiveMembershipBanner from './components/InactiveMembershipBanner'
|
import InactiveMembershipBanner from './components/InactiveMembershipBanner'
|
||||||
import './app.css'
|
import './app.css'
|
||||||
|
|
@ -171,6 +172,12 @@ function AppRoutes() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* P-01: Öffentliche Rechtstextseiten — kein Auth erforderlich */}
|
||||||
|
<Route path="/impressum" element={<LegalPage type="impressum" />} />
|
||||||
|
<Route path="/datenschutz" element={<LegalPage type="datenschutz" />} />
|
||||||
|
<Route path="/nutzungsbedingungen" element={<LegalPage type="nutzungsbedingungen" />} />
|
||||||
|
<Route path="/medienrichtlinie" element={<LegalPage type="medienrichtlinie" />} />
|
||||||
|
|
||||||
<Route element={<ProtectedLayout />}>
|
<Route element={<ProtectedLayout />}>
|
||||||
<Route index element={<Dashboard />} />
|
<Route index element={<Dashboard />} />
|
||||||
<Route path="profile" element={<Navigate to="/settings" replace />} />
|
<Route path="profile" element={<Navigate to="/settings" replace />} />
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { NavLink, useLocation } from 'react-router-dom'
|
import { NavLink, Link, useLocation } from 'react-router-dom'
|
||||||
import { LogOut } from 'lucide-react'
|
import { LogOut } from 'lucide-react'
|
||||||
import { getMainNavItems } from '../config/appNav'
|
import { getMainNavItems } from '../config/appNav'
|
||||||
import { useOrgInbox } from '../context/OrgInboxContext'
|
import { useOrgInbox } from '../context/OrgInboxContext'
|
||||||
|
|
@ -92,6 +92,20 @@ export default function DesktopSidebar({
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '0.5rem 1rem 0.75rem',
|
||||||
|
display: 'flex',
|
||||||
|
gap: '0.6rem',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
fontSize: '0.7rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Link to="/impressum" style={{ color: 'var(--text3)' }}>Impressum</Link>
|
||||||
|
<Link to="/datenschutz" style={{ color: 'var(--text3)' }}>Datenschutz</Link>
|
||||||
|
<Link to="/nutzungsbedingungen" style={{ color: 'var(--text3)' }}>Nutzungsbedingungen</Link>
|
||||||
|
<Link to="/medienrichtlinie" style={{ color: 'var(--text3)' }}>Medienrichtlinie</Link>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
212
frontend/src/pages/LegalPage.jsx
Normal file
212
frontend/src/pages/LegalPage.jsx
Normal file
|
|
@ -0,0 +1,212 @@
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
const PAGES = {
|
||||||
|
impressum: {
|
||||||
|
title: 'Impressum',
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
heading: 'Betreiber / Verantwortlicher',
|
||||||
|
placeholder: '[Name und Rechtsform des Betreibers — vom Betreiber einzutragen]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Anschrift',
|
||||||
|
placeholder: '[Straße, Hausnummer, PLZ, Ort — vom Betreiber einzutragen]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Vertretungsberechtigte Person',
|
||||||
|
placeholder: '[Name der vertretungsberechtigten Person — vom Betreiber einzutragen]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Kontakt',
|
||||||
|
placeholder: '[E-Mail-Adresse, ggf. Telefonnummer — vom Betreiber einzutragen]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Registerangaben (falls relevant)',
|
||||||
|
placeholder: '[Vereinsregister, Handelsregister o. ä. — vom Rechtsanwalt prüfen lassen]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
datenschutz: {
|
||||||
|
title: 'Datenschutzerklärung',
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
heading: 'Verantwortlicher',
|
||||||
|
placeholder: '[Name, Anschrift und Kontakt des Verantwortlichen — vom Betreiber einzutragen]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Zwecke der Verarbeitung',
|
||||||
|
placeholder: '[Welche Daten werden zu welchem Zweck verarbeitet? — vom Rechtsanwalt zu formulieren]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Rechtsgrundlagen',
|
||||||
|
placeholder: '[Art. 6 DSGVO: Einwilligung, Vertrag, berechtigtes Interesse — vom Rechtsanwalt zu bestimmen]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Empfänger und Dienstleister',
|
||||||
|
placeholder: '[SMTP-Anbieter, Hosting, ggf. weitere — vom Rechtsanwalt und Betreiber zu listen]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Speicherdauern',
|
||||||
|
placeholder: '[Wie lange werden welche Daten gespeichert? — vom Rechtsanwalt zu bestimmen]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Betroffenenrechte',
|
||||||
|
placeholder: '[Auskunft, Berichtigung, Löschung, Widerspruch, Datenübertragbarkeit — vom Rechtsanwalt zu formulieren]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Browser-Speicher (localStorage, sessionStorage)',
|
||||||
|
placeholder: '[Technisch notwendige Speicherung des Auth-Tokens und Sitzungsdaten. Nach TDDDG §25 ggf. ohne Einwilligung zulässig — vom Rechtsanwalt zu prüfen]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Kontakt für Datenschutzanfragen',
|
||||||
|
placeholder: '[E-Mail oder Kontaktweg für Datenschutzanfragen — vom Betreiber einzutragen]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Zuständige Aufsichtsbehörde',
|
||||||
|
placeholder: '[Zuständige Datenschutzbehörde je nach Bundesland — vom Rechtsanwalt zu bestimmen]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
nutzungsbedingungen: {
|
||||||
|
title: 'Nutzungsbedingungen',
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
heading: 'Nutzungsumfang',
|
||||||
|
placeholder: '[Für welche Nutzergruppen und Zwecke darf die Plattform genutzt werden? — vom Rechtsanwalt zu formulieren]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Registrierung',
|
||||||
|
placeholder: '[Voraussetzungen für die Registrierung, Wahrheitspflicht der Angaben — vom Rechtsanwalt zu formulieren]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Zulässige und unzulässige Inhalte',
|
||||||
|
placeholder: '[Was darf hochgeladen und veröffentlicht werden? Was ist verboten? — vom Rechtsanwalt zu formulieren]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Verantwortlichkeit der Nutzer',
|
||||||
|
placeholder: '[Nutzer sind für ihre hochgeladenen Inhalte selbst verantwortlich — vom Rechtsanwalt zu formulieren]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Sperrung und Löschung',
|
||||||
|
placeholder: '[Unter welchen Bedingungen können Konten oder Inhalte gesperrt oder gelöscht werden? — vom Rechtsanwalt zu formulieren]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Haftungshinweise',
|
||||||
|
placeholder: '[Haftungsausschluss für Nutzerinhalte, externe Links, Systemausfälle — vom Rechtsanwalt zu formulieren]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Geltungsbereich und anwendbares Recht',
|
||||||
|
placeholder: '[Welches Recht ist anwendbar? Welcher Gerichtsstand gilt? — vom Rechtsanwalt zu bestimmen]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
medienrichtlinie: {
|
||||||
|
title: 'Medienrichtlinie',
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
heading: 'Urheberrechte',
|
||||||
|
placeholder: '[Nur eigene oder ausdrücklich lizenzierte Inhalte hochladen — vom Rechtsanwalt zu formulieren]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Rechte am eigenen Bild (§ 22 KUG)',
|
||||||
|
placeholder: '[Erkennbare Personen müssen eingewilligt haben; besondere Regeln für Minderjährige — juristisch zu prüfen und zu formulieren]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Minderjährige',
|
||||||
|
placeholder: '[Besondere Schutzpflichten bei Aufnahmen von Minderjährigen — vom Rechtsanwalt zu formulieren]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Musik und sonstige Fremdinhalte',
|
||||||
|
placeholder: '[Keine Hintergrundmusik oder andere Fremdinhalte ohne gültige Lizenz — vom Rechtsanwalt zu formulieren]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Sichtbarkeitsstufen',
|
||||||
|
placeholder: '[Erläuterung der Stufen: privat, vereinsintern, öffentlich — vom Betreiber zu beschreiben]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Meldewege für rechtsverletzende Inhalte',
|
||||||
|
placeholder: '[Wie können Inhalte gemeldet werden? — wird nach Umsetzung von P-13 (Content-Melde-Backend) ergänzt]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Lösch- und Sperrlogik',
|
||||||
|
placeholder: '[Wann und wie werden gemeldete Inhalte entfernt oder gesperrt? — wird nach Umsetzung von P-11 und P-13 ergänzt]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const LEGAL_LINKS = [
|
||||||
|
{ to: '/impressum', label: 'Impressum' },
|
||||||
|
{ to: '/datenschutz', label: 'Datenschutz' },
|
||||||
|
{ to: '/nutzungsbedingungen', label: 'Nutzungsbedingungen' },
|
||||||
|
{ to: '/medienrichtlinie', label: 'Medienrichtlinie' },
|
||||||
|
]
|
||||||
|
|
||||||
|
function LegalPage({ type }) {
|
||||||
|
const page = PAGES[type]
|
||||||
|
if (!page) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ minHeight: '100vh', background: 'var(--bg)', padding: '2rem 1rem' }}>
|
||||||
|
<div style={{ maxWidth: '720px', margin: '0 auto' }}>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: '1.5rem' }}>
|
||||||
|
<Link to="/login" style={{ color: 'var(--accent)', textDecoration: 'none', fontSize: '0.9rem' }}>
|
||||||
|
← Zurück zur Anmeldung
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="card"
|
||||||
|
style={{
|
||||||
|
marginBottom: '1.5rem',
|
||||||
|
borderLeft: '4px solid var(--danger)',
|
||||||
|
background: 'var(--surface)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<strong style={{ color: 'var(--danger)' }}>⚠ MUSTER / PLATZHALTER</strong>
|
||||||
|
<p style={{ margin: '0.5rem 0 0', color: 'var(--text2)', fontSize: '0.9rem' }}>
|
||||||
|
Inhalt wird vor Produktivbetrieb juristisch geprüft und durch den Betreiber ergänzt.
|
||||||
|
Diese Seite hat keinen rechtlich verbindlichen Charakter.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 style={{ marginBottom: '2rem', color: 'var(--text1)' }}>{page.title}</h1>
|
||||||
|
|
||||||
|
{page.sections.map((section) => (
|
||||||
|
<div key={section.heading} style={{ marginBottom: '1.75rem' }}>
|
||||||
|
<h2 style={{ fontSize: '1.05rem', marginBottom: '0.4rem', color: 'var(--text1)' }}>
|
||||||
|
{section.heading}
|
||||||
|
</h2>
|
||||||
|
<p style={{ color: 'var(--text3)', fontStyle: 'italic', margin: 0 }}>
|
||||||
|
{section.placeholder}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: '3rem',
|
||||||
|
paddingTop: '1rem',
|
||||||
|
borderTop: '1px solid var(--border)',
|
||||||
|
fontSize: '0.82rem',
|
||||||
|
color: 'var(--text3)',
|
||||||
|
textAlign: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
gap: '1.25rem',
|
||||||
|
justifyContent: 'center',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{LEGAL_LINKS.map((l) => (
|
||||||
|
<Link key={l.to} to={l.to} style={{ color: 'var(--text3)' }}>
|
||||||
|
{l.label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LegalPage
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate, Link } from 'react-router-dom'
|
||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import api from '../utils/api'
|
import api from '../utils/api'
|
||||||
|
|
||||||
|
|
@ -238,6 +238,25 @@ function LoginPage() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: '1.5rem',
|
||||||
|
paddingTop: '1rem',
|
||||||
|
borderTop: '1px solid var(--border)',
|
||||||
|
fontSize: '0.78rem',
|
||||||
|
color: 'var(--text3)',
|
||||||
|
textAlign: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
gap: '1rem',
|
||||||
|
justifyContent: 'center',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Link to="/impressum" style={{ color: 'var(--text3)' }}>Impressum</Link>
|
||||||
|
<Link to="/datenschutz" style={{ color: 'var(--text3)' }}>Datenschutz</Link>
|
||||||
|
<Link to="/nutzungsbedingungen" style={{ color: 'var(--text3)' }}>Nutzungsbedingungen</Link>
|
||||||
|
<Link to="/medienrichtlinie" style={{ color: 'var(--text3)' }}>Medienrichtlinie</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
// Shinkan Jinkendo Frontend Version
|
// Shinkan Jinkendo Frontend Version
|
||||||
|
|
||||||
export const APP_VERSION = "0.8.68"
|
export const APP_VERSION = "0.8.69"
|
||||||
export const BUILD_DATE = "2026-05-10"
|
export const BUILD_DATE = "2026-05-10"
|
||||||
|
|
||||||
export const PAGE_VERSIONS = {
|
export const PAGE_VERSIONS = {
|
||||||
LoginPage: "1.0.1",
|
LoginPage: "1.0.2",
|
||||||
|
LegalPage: "1.0.0",
|
||||||
Dashboard: "1.0.0",
|
Dashboard: "1.0.0",
|
||||||
AccountSettingsPage: "1.0.1",
|
AccountSettingsPage: "1.0.1",
|
||||||
ExercisesPage: "1.5.0", // Fokus +/- Regeln, nur ohne Fokusbereich; Filterprefs
|
ExercisesPage: "1.5.0", // Fokus +/- Regeln, nur ohne Fokusbereich; Filterprefs
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,52 @@ test('P-12: sessionStorage wird bei Logout bereinigt (sj_coach_* Schlüssel)', a
|
||||||
console.log('✓ P-12: sj_coach_* entfernt, Fremd-Key erhalten, authToken entfernt');
|
console.log('✓ P-12: sj_coach_* entfernt, Fremd-Key erhalten, authToken entfernt');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// P-01: Rechtstextseiten – öffentliche Routen ohne Auth
|
||||||
|
|
||||||
|
const LEGAL_ROUTES = [
|
||||||
|
{ path: '/impressum', label: 'Impressum' },
|
||||||
|
{ path: '/datenschutz', label: 'Datenschutz' },
|
||||||
|
{ path: '/nutzungsbedingungen', label: 'Nutzungsbedingungen' },
|
||||||
|
{ path: '/medienrichtlinie', label: 'Medienrichtlinie' },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const route of LEGAL_ROUTES) {
|
||||||
|
test(`P-01: ${route.label} ohne Auth erreichbar und enthält Platzhalterhinweis`, async ({ page }) => {
|
||||||
|
// Direkt aufrufen ohne Login
|
||||||
|
await page.goto(route.path);
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
// Seite ist erreichbar (kein Redirect zur Login-Seite)
|
||||||
|
expect(page.url()).toContain(route.path);
|
||||||
|
|
||||||
|
// Platzhalterhinweis sichtbar
|
||||||
|
const hinweis = await page.getByText('MUSTER / PLATZHALTER').first();
|
||||||
|
await expect(hinweis).toBeVisible();
|
||||||
|
|
||||||
|
// Seitentitel korrekt
|
||||||
|
await expect(page.getByRole('heading', { level: 1 })).toContainText(route.label);
|
||||||
|
|
||||||
|
// Reload funktioniert
|
||||||
|
await page.reload();
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
expect(page.url()).toContain(route.path);
|
||||||
|
|
||||||
|
console.log(`✓ P-01: ${route.label} – ohne Auth erreichbar, Platzhalter sichtbar, Reload OK`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test('P-01: Login-Seite enthält Links zu allen vier Rechtstextseiten', async ({ page }) => {
|
||||||
|
await page.goto('/login');
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
for (const route of LEGAL_ROUTES) {
|
||||||
|
const link = page.locator(`a[href="${route.path}"]`);
|
||||||
|
await expect(link).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✓ P-01: Login-Seite – alle vier Rechtstext-Links vorhanden');
|
||||||
|
});
|
||||||
|
|
||||||
test('8. Keine kritischen Console-Fehler', async ({ page }) => {
|
test('8. Keine kritischen Console-Fehler', async ({ page }) => {
|
||||||
const errors = [];
|
const errors = [];
|
||||||
page.on('console', msg => {
|
page.on('console', msg => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user