E2E testing infrastructure, NextGraph connection fixes, and documentation

- Add @e2e test layer: real app in broker iframe via Playwright
- Fix broker redirect: conditional auto-init only when inside iframe
- Fix seed data flash: empty data during 'connecting' phase
- Fix Gallery button in iframe: explicit navigate instead of history.back
- Add auth e2e feature scenarios and step definitions
- Update docs: bdd-testing, data-layer-testing, data-layer, AGENTS.md
- Add decision record for conditional NG init approach

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sylvain Duchesne
2026-03-13 17:51:44 +01:00
parent 6f9b3ece34
commit 708cbeead8
23 changed files with 12033 additions and 361 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ function AppContent() {
return (
<DemoMode
initialScreenId={route.screenId}
onBack={goBack}
onBack={() => navigate({ page: 'gallery' })}
onNavigateToStory={(storyId) => navigate({ page: 'specs', storyId })}
/>
);
+2
View File
@@ -38,6 +38,8 @@ export function DemoMode({ initialScreenId, onBack, onNavigateToStory }: DemoMod
setHistory(newHistory);
setHistoryIndex(newHistory.length - 1);
setCurrentScreenId(screenId);
// Keep URL in sync so refresh preserves the current screen
window.history.replaceState(null, '', `#/demo/${screenId}`);
};
const canGoBack = historyIndex > 0;
+50
View File
@@ -2,6 +2,8 @@ import React, { useState, useEffect } from 'react';
import { PhoneFrame, BrokerBanner } from '../../shared/components/sketchy';
import { screenGroups, type Screen } from '../../screens';
import { ThemeToggle } from './ThemeToggle';
import { useNextGraph } from '../../shared/context/NextGraphContext';
import { useFestipodData } from '../../shared/context/FestipodDataContext';
function useIsMobile(breakpoint = 768) {
const [isMobile, setIsMobile] = useState(window.innerWidth < breakpoint);
@@ -25,6 +27,11 @@ const DEFAULT_SCALE = 0.5;
export function Gallery({ onSelectScreen, onShowSpecs }: GalleryProps) {
const [scale, setScale] = useState(DEFAULT_SCALE);
const isMobile = useIsMobile();
const { status, connect } = useNextGraph();
const { loadTestData } = useFestipodData();
const [loading, setLoading] = useState(false);
const isConnected = status === 'connected';
const isConnecting = status === 'connecting';
return (
<div>
@@ -83,6 +90,49 @@ export function Gallery({ onSelectScreen, onShowSpecs }: GalleryProps) {
Specs BDD
</button>
{/* NextGraph: login or load test data */}
{!isConnected && (
<button
onClick={connect}
disabled={isConnecting}
style={{
background: isConnecting ? 'var(--tool-text-muted)' : '#4CAF50',
color: 'white',
border: '2px solid var(--tool-border)',
borderRadius: '255px 15px 225px 15px/15px 225px 15px 255px',
padding: isMobile ? '6px 12px' : '8px 16px',
fontFamily: 'var(--font-sketch)',
fontSize: isMobile ? 12 : 14,
cursor: isConnecting ? 'wait' : 'pointer',
opacity: isConnecting ? 0.7 : 1,
}}
>
{isConnecting ? 'Connexion...' : 'Se connecter'}
</button>
)}
{isConnected && (
<button
onClick={async () => {
setLoading(true);
try { await loadTestData(); } finally { setLoading(false); }
}}
disabled={loading}
style={{
background: loading ? 'var(--tool-text-muted)' : '#FF9800',
color: 'white',
border: '2px solid var(--tool-border)',
borderRadius: '255px 15px 225px 15px/15px 225px 15px 255px',
padding: isMobile ? '6px 12px' : '8px 16px',
fontFamily: 'var(--font-sketch)',
fontSize: isMobile ? 12 : 14,
cursor: loading ? 'wait' : 'pointer',
opacity: loading ? 0.7 : 1,
}}
>
{loading ? 'Chargement...' : 'Charger données de test'}
</button>
)}
{/* Zoom control - hide on mobile */}
{!isMobile && (
<div style={{