38 KiB
Sprint #1: Correction des Bugs Critiques et High Priority
Métadonnées
| Propriété | Valeur |
|---|---|
| Nom du Sprint | Correction des Bugs Critiques UI et Performance |
| ID du Sprint | sprint-1-bug-fixes |
| Date de début | 2026-01-15 |
| Durée prévue | 2 semaines (10 jours ouvrés) |
| Statut | 🟡 Prêt à démarrer |
| Priorité | 🔴 Critique (P0) |
| Capacité | 8 stories |
| Lead | Frontend Engineer |
🎯 Goal (Objectif du Sprint)
Objectif principal: Éliminer tous les problèmes de refresh, re-render excessifs, et bugs UI qui affectent l'expérience utilisateur quotidienne.
Métriques de succès:
- ✅ Aucun flash d'écran inutile
- ✅ Aucune perte de position de scroll
- ✅ Toutes les actions UI sont instantanées (optimistes)
- ✅ Drag and drop fonctionne correctement sur desktop
- ✅ Drag est désactivé proprement sur mobile
- ✅ Performance améliorée (moins de re-renders)
- ✅ Toutes les corrections sont validées par tests
📋 Backlog (Stories du Sprint)
🔴 CRITIQUE (Doit être complété avant fin du Sprint)
Story #1: Corriger triggerRefresh() dans NoteRefreshContext.tsx
Priorité: P0 (Critique)
Estimation: 2 heures
Complexité: Faible
En tant que: Développeur Frontend
Je veux: Corriger la fonction triggerRefresh() dans le Provider de contexte pour qu'elle force un re-render global de tous les composants consommateurs.
Afin de: Permettre aux composants de se rafraîchir sans utiliser router.refresh() ou window.location.reload(), éliminant ainsi les flashs d'écran et la perte de scroll.
Critères d'acceptation:
- ✅ L'appel à
triggerRefresh()force immédiatement un re-render de tous les composants qui utilisentuseNoteRefresh() - ✅ Le re-render ne crée pas de cycles de dépendances
- ✅ Aucun appel à
router.refresh()ouwindow.location.reload()dans la fonction elle-même - ✅ Les tests Playwright existants passent (confirmant que les bugs de refresh sont résolus)
Contexte technique:
- Fichier affecté:
keep-notes/context/NoteRefreshContext.tsx - Fonction cible:
triggerRefresh() - Approche actuelle (bug):
const triggerRefresh = useCallback(() => { setRefreshKey(prev => prev + 1) // ❌ INCORRECT }, [refreshKey]) // ❌ Cycle de dépendances - Approche corrigée:
const refreshKeyRef = useRef(refreshKey) const triggerRefresh = useCallback(() => { const newKey = refreshKeyRef.current + 1 refreshKeyRef.current = newKey setRefreshKey(newKey) // ✅ Force le re-render }, []) // ✅ Pas de dépendances
Tests:
- ✅ Jouer les tests Playwright existants (
bug-move-direct.spec.ts,bug-note-move-refresh.spec.ts) - ✅ Manuellement: Créer une note, toggle pin, vérifier que l'UI se met à jour sans flash
- ✅ Cross-browser: Tester sur Chrome, Firefox, Safari
Points techniques:
- Utiliser
useRefpour stocker la valeur actuelle de la clé - Utiliser
useCallbacksans dépendances - Éviter les cycles de dépendances entre state et callback
- Documenter pourquoi l'ancienne approche ne fonctionnait pas
Effets secondaires:
- Tous les composants utilisant
triggerRefresh()bénéficieront immédiatement - Réduit le nombre de re-renders dans l'application entière
Risques:
- Si mal implémenté, peut créer des re-renders infinis
- Peut affecter d'autres fonctionnalités qui dépendent de
refreshKey
Mitigation:
- Tests approfondis avant de merge
- Code review attentif
- Tester avec des scénarios limites (beaucoup de composants, beaucoup de triggers)
Story #2: Supprimer router.refresh() dans note-card.tsx
Priorité: P0 (Critique)
Estimation: 1 heure
Complexité: Faible
En tant que: Développeur Frontend
Je veux: Supprimer tous les appels inutiles à router.refresh() dans le composant NoteCard pour laisser l'état optimiste gérer l'UI.
Afin de: Éliminer les refreshs de page complets lors des actions courantes (pin, archive, color, size, toggle checklist), réduisant les flashs d'écran.
Critères d'acceptation:
- ✅ Aucun appel à
router.refresh()danshandleTogglePin() - ✅ Aucun appel à
router.refresh()danshandleToggleArchive() - ✅ Aucun appel à
router.refresh()danshandleColorChange() - ✅ Aucun appel à
router.refresh()danshandleSizeChange() - ✅ Aucun appel à
router.refresh()danshandleCheckItem() - ✅ L'état optimiste (
useOptimistic) met à jour l'UI instantanément - ✅ Les tests de la Story #1 passent (car le triggerRefresh fonctionne maintenant)
Contexte technique:
- Fichier affecté:
keep-notes/components/note-card.tsx - Lignes à modifier: 200, 208, 216, 224, 235
- Approche actuelle (bug):
const handleTogglePin = async () => { startTransition(async () => { addOptimisticNote({ isPinned: !note.isPinned }) await togglePin(note.id, !note.isPinned) router.refresh() // ❌ FORCE RELOAD COMPLET }) } - Approche corrigée:
const handleTogglePin = async () => { addOptimisticNote({ isPinned: !note.isPinned }) await togglePin(note.id, !note.isPinned) // ✅ Pas de refresh - l'état optimiste gère l'UI }
Tests:
- ✅ Jouer les tests Playwright existants
- ✅ Manuellement: Créer 10 notes, toggle pin sur chacune, vérifier que le temps total < 2 secondes
- ✅ Vérifier qu'il n'y a pas de flash d'écran
Points techniques:
- Supprimer les appels à
router.refresh()dans les handlers d'actions - Conserver
addOptimisticNotepour l'UI instantanée - Laisser
startTransitionpour la mise à jour asynchrone du serveur - Documenter que le refresh est maintenant géré par
triggerRefresh()
Effets secondaires:
- Réduction significative des refreshs de page
- Amélioration de la réactivité perçue par l'utilisateur
Risques:
- Si une action échoue côté serveur, l'UI peut être désynchronisée
- L'état optimiste peut ne pas correspondre à la réalité serveur
Mitigation:
- Gérer les erreurs serveur avec
startTransitionet rollback de l'état optimiste - Ajouter des notifications de succès/erreur pour informer l'utilisateur
- Tester les scénarios d'échec réseau
Story #3: Supprimer router.refresh() dans page.tsx
Priorité: P0 (Critique)
Estimation: 30 minutes
Complexité: Faible
En tant que: Développeur Frontend
Je veux: Supprimer les appels redondants à router.refresh() dans la page principale (page.tsx) après les actions de batch organization et auto-labeling.
Afin de: Éviter les double refreshs inutiles qui causent des flashs d'écran et une mauvaise expérience utilisateur.
Critères d'acceptation:
- ✅ Aucun appel à
router.refresh()dansonNotesMoved()(ligne 171) - ✅ Aucun appel à
router.refresh()dansonLabelsCreated()(ligne 185) - ✅ Les actions de batch et auto-labeling fonctionnent sans refresh
- ✅ Les tests de la Story #1 passent
Contexte technique:
- Fichier affecté:
keep-notes/app/(main)/page.tsx - Lignes à modifier: 171, 185
- Approche actuelle (bug):
onNotesMoved={() => { router.refresh() // ❌ REDONDANT - déjà optimiste }} onLabelsCreated={() => { router.refresh() // ❌ REDONDANT - déjà optimiste }} - Approche corrigée:
onNotesMoved={() => { // ✅ Le state React est déjà mis à jour de manière optimiste // Pas besoin de refresh }} onLabelsCreated={() => { // ✅ Le state React est déjà mis à jour de manière optimiste // Pas besoin de refresh }}
Tests:
- ✅ Jouer les tests Playwright existants
- ✅ Manuellement: Organiser des notes en batch, vérifier qu'il n'y a pas de refresh
- ✅ Manuellement: Créer des labels automatiques, vérifier qu'il n'y a pas de refresh
Points techniques:
- Supprimer les appels à
router.refresh()dans les callbacks - Comprendre que l'état React local (
notes,labels) est déjà mis à jour - Le refresh global sera déclenché par
triggerRefresh()si nécessaire - Documenter pourquoi les refreshs étaient redondants
Effets secondaires:
- Élimination des double refreshs
- Amélioration de la performance
- Expérience utilisateur plus fluide
Risques:
- Si le state React local n'est pas correctement synchronisé, l'utilisateur peut voir des données obsolètes
- Les tests doivent être mis à jour pour refléter l'absence de refresh
Mitigation:
- S'assurer que les handlers de batch et auto-labeling mettent à jour le state React correctement
- Ajouter des logs de debugging pour tracer les mises à jour de state
- Documenter les changements dans le README
Story #4: Remplacer window.location.reload() dans notebooks-context.tsx
Priorité: P0 (Critique)
Estimation: 1 heure
Complexité: Faible
En tant que: Développeur Frontend
Je veux: Remplacer tous les appels à window.location.reload() dans notebooks-context.tsx par des appels à triggerRefresh() pour éviter les reloads complets de la page.
Afin de: Permettre aux actions sur les notebooks (création, mise à jour, suppression, réordonnancement) de rafraîchir l'UI sans recharger toute la page.
Critères d'acceptation:
- ✅ Remplacer
window.location.reload()partriggerRefresh()danscreateNotebookOptimistic()(ligne 141) - ✅ Remplacer
window.location.reload()partriggerRefresh()dansupdateNotebook()(ligne 154) - ✅ Remplacer
window.location.reload()partriggerRefresh()dansdeleteNotebook()(ligne 169) - ✅ Remplacer
window.location.reload()partriggerRefresh()dansupdateNotebookOrderOptimistic()(ligne 169) - ✅ Les tests de la Story #1 passent (car le triggerRefresh fonctionne maintenant)
Contexte technique:
- Fichier affecté:
keep-notes/context/notebooks-context.tsx - Lignes à modifier: 141, 154, 169
- Approche actuelle (bug):
const updateNotebook = async (notebookId: string, data: UpdateNotebookInput) => { // ... API call ... window.location.reload() // ❌ FORCE RELOAD COMPLET } - Approche corrigée:
const { triggerRefresh } = useNoteRefresh() const updateNotebook = async (notebookId: string, data: UpdateNotebookInput) => { // ... API call ... triggerRefresh() // ✅ Rafraîchissement optimiste du state React }
Tests:
- ✅ Jouer les tests Playwright existants
- ✅ Manuellement: Créer un notebook, modifier son nom, vérifier qu'il n'y a pas de reload complet
- ✅ Manuellement: Supprimer un notebook, vérifier que la page ne se recharge pas
- ✅ Manuellement: Réordonner des notebooks, vérifier que l'ordre se met à jour sans reload
Points techniques:
- Importer
useNoteRefreshdepuis le Contexte - Remplacer tous les appels à
window.location.reload()partriggerRefresh() - S'assurer que les callbacks sont async/await
- Tester que l'état des notebooks se met à jour dans les autres composants
Effets secondaires:
- Élimination des reloads complets de la page
- Préservation de la position de scroll
- Meilleure expérience utilisateur (pas de flash blanc)
- Conservation de l'état de l'application
Risques:
- Si
triggerRefresh()ne fonctionne pas (Bug #1 non résolu), toutes les actions sur notebooks échoueront - Peut créer des incohérences d'état si d'autres composants modifient aussi les notebooks
Mitigation:
- Corriger d'abord la Story #1 (triggerRefresh) AVANT de modifier les actions sur notebooks
- Tester soigneusement chaque action sur notebooks après correction
- Ajouter des logs pour tracer les appels à
triggerRefresh() - Avoir un plan de rollback prêt
Dépendance: Story #1 doit être complétée AVANT cette story
🟡 HIGH (Compléter avant fin du Sprint si possible)
Story #5: Désactiver Drag sur Mobile
Priorité: P1 (High)
Estimation: 2 heures
Complexité: Moyenne
En tant que: Développeur Frontend Je veux: Désactiver correctement le drag and drop sur les appareils mobiles (smartphones, tablets) pour éviter les conflits avec les touch events et les bugs de scroll.
Afin de: Permettre aux utilisateurs mobiles d'utiliser l'application sans que le drag and drop interfère avec le scroll et les touch events, améliorant significativement l'expérience mobile.
Critères d'acceptation:
- ✅ Le drag est désactivé sur mobile (écran < 768px)
- ✅ Le drag reste activé sur desktop
- ✅ Aucun conflit avec les touch events sur mobile
- ✅ Le scroll fonctionne normalement sur mobile
- ✅ Les tests mobile passent (ou sont créés pour valider)
- ✅ Aucune erreur console liée au drag sur mobile
Contexte technique:
- Fichier affecté:
keep-notes/components/masonry-grid.tsx - Lignes à modifier: 160, 165 (options de layout)
- Approche actuelle (bug):
const isMobile = window.matchMedia('(pointer: coarse)').matches; // ❌ Non fiable const layoutOptions = { dragEnabled: true, // ❌ Problématique sur mobile dragHandle: '.muuri-drag-handle', // ❌ Conflict avec touch dragContainer: document.body, // ... } - Approche corrigée Option A (Simple):
const isMobile = window.innerWidth < 768; // ✅ Détection fiable const isTouchDevice = 'ontouchstart' in window; // ✅ Détection fiable const layoutOptions = { dragEnabled: !isMobile && !isTouchDevice, // ✅ Désactiver sur mobile dragHandle: isMobile ? undefined : '.muuri-drag-handle', // ✅ Pas de handle sur mobile dragContainer: document.body, // ... } - Approche corrigée Option B (Recommandée - Plus complexe): Remplacer Muuri par @dnd-kit/core qui supporte nativement le touch
Tests:
- ✅ Tests Playwright existants (si tests mobile existent)
- ✅ Manuellement: Tester sur desktop - drag fonctionne
- ✅ Manuellement: Tester sur mobile - drag désactivé, scroll fonctionne
- ✅ Cross-device: Tester sur iPhone, Android, iPad, Desktop
- ✅ Playwright en mode mobile (viewports mobiles)
Points techniques:
- Utiliser
window.innerWidthpour une détection fiable - Utiliser
'ontouchstart' in windowpour détecter les appareils tactiles - Désactiver
dragEnabledpour mobile et touch devices - Désactiver
dragHandlepour mobile et touch devices - Conserver les options Muuri restantes pour desktop
- Optionnellement: Remplacer Muuri par @dnd-kit/core (recommandé pour mobile)
Effets secondaires:
- Expérience mobile fluide sans conflits
- Scroll fonctionnel sur mobile
- Amélioration de la performance sur mobile (moins de calculs de layout)
- Drag and drop reste fonctionnel sur desktop
Risques:
- Les breakpoints de détection mobile (768px) peuvent ne pas correspondre à tous les devices
- Certains tablets peuvent être traités comme mobiles alors qu'ils supportent le drag
- La détection mobile côté client peut différer de la détection Playwright
Mitigation:
- Utiliser des breakpoints standard et bien documentés
- Tester sur une variété de devices réels (pas uniquement iPhone)
- Considérer une taille d'écran intermédiaire (768-1024px) pour les tablets
- Ajouter un flag utilisateur pour forcer le mode mobile si nécessaire
Story #6: Corriger les Doublons de Boutons de Fermeture
Priorité: P1 (High)
Estimation: 1 heure
Complexité: Faible
En tant que: Développeur Frontend + UX Designer Je veux: Remplacer les boutons multiples avec icône X par des boutons avec des icônes et couleurs sémantiques distinctes pour chaque type d'action (poubelle, fermer, annuler), améliorant ainsi la clarté de l'UI et réduisant la confusion.
Afin de: Permettre aux utilisateurs de distinguer visuellement et rapidement les différentes actions disponibles sur une note (quitter le partage, supprimer le badge de fusion, etc.), améliorant l'expérience utilisateur et réduisant les erreurs.
Critères d'acceptation:
- ✅ Le bouton "Remove Fused Badge" utilise une icône de poubelle (Trash2) et couleur violette
- ✅ Le bouton "Leave Share" utilise une icône de déconnexion (LogOut) et couleur bleue
- ✅ Les deux boutons sont visuellement distincts
- ✅ Des tooltips explicites décrivent l'action
- ✅ Les couleurs sémantiques sont cohérentes (rouge = danger, gris/bleu = annuler)
- ✅ L'UX est améliorée avec moins de confusion
Contexte technique:
- Fichier affecté:
keep-notes/components/note-card.tsx - Lignes à modifier: 351-357 (remove fused badge), 411-413 (leave share)
- Approche actuelle (bug):
// Bouton "Remove Fused Badge" avec icône X <button onClick={handleRemoveFusedBadge}> <X className="h-2.5 w-2.5" /> <span>Remove</span> </button> // Bouton "Leave Share" avec icône X <Button onClick={handleLeaveShare}> <X className="h-3 w-3 mr-1" /> Leave Share </Button> - Approche corrigée:
import { Trash2, LogOut, X } from 'lucide-react' // Bouton "Remove Fused Badge" - icône de poubelle <button onClick={handleRemoveFusedBadge} className="..."> <Trash2 className="h-2.5 w-2.5 text-purple-600" /> <span className="ml-2">Remove Fused Badge</span> </button> // Bouton "Leave Share" - icône de déconnexion <Button onClick={handleLeaveShare} className="..."> <LogOut className="h-3 w-3 mr-1 text-blue-600" /> Leave Share </Button>
Tests:
- ✅ Manuellement: Ouvrir une note avec badge fusionné, tester le bouton de suppression
- ✅ Manuellement: Ouvrir une note partagée, tester le bouton de quitter
- ✅ Tests d'accessibilité: Vérifier les attributs ARIA
- ✅ Tests visuels: Screenshot avant/après pour vérifier les icônes et couleurs
Points techniques:
- Importer les nouvelles icônes depuis lucide-react
- Appliquer des classes Tailwind pour les couleurs
- Ajouter des tooltips explicites (title ou aria-label)
- Garder les classes existantes pour la disposition et le padding
- S'assurer que les boutons sont accessibles au clavier et à l'écran
Effets secondaires:
- Réduction significative de la confusion utilisateur
- Amélioration de l'accessibilité (icônes sémantiques)
- Expérience utilisateur plus claire et intuitive
- Moins d'erreurs (supprimer au lieu de quitter, etc.)
Risques:
- Si les nouvelles icônes ne sont pas cohérentes avec le reste de l'UI, peut créer de la confusion
- Changement visuel peut surprendre les utilisateurs habitués
- Les tooltips doivent être traduits correctement
Mitigation:
- Avoir une review UX pour valider les nouvelles icônes et couleurs
- Traduire les tooltips dans toutes les langues supportées
- Ajouter une note dans le changelog expliquant les nouvelles icônes
- Considérer une période de transition ou un flag utilisateur pour revenir à l'ancien design
Dépendances: Aucune (peut être fait en parallèle)
Story #7: Corriger useDebounce Hook
Priorité: P1 (High)
Estimation: 30 minutes
Complexité: Faible
En tant que: Développeur Frontend
Je veux: Corriger l'implémentation du hook useDebounce qui recrée le timer à chaque render au lieu de réutiliser le timer existant, causant ainsi des cascades de re-renders inutiles et une dégradation des performances.
Afin de: Optimiser le hook en réutilisant le timer existant via useRef, éliminant les recréations inutiles et améliorant ainsi la performance globale de l'application.
Critères d'acceptation:
- ✅ Le timer est stocké dans un
useRefau lieu d'être recréé - ✅ Le hook ne recrée pas le timer si la valeur ne change pas
- ✅ Les re-renders inutiles sont éliminés
- ✅ La performance mesurable (temps de render) est améliorée
- ✅ Le hook reste fonctionnellement identique pour l'utilisateur
Contexte technique:
- Fichier affecté:
keep-notes/hooks/use-debounce.ts - Approche actuelle (bug):
export function useDebounce<T>(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState<T>(value) useEffect(() => { const timer = setTimeout(() => { setDebouncedValue(value) }, delay) return () => { clearTimeout(timer) } }, [value, delay]) // ❌ Recrée le timer à chaque render // ❌ Même si value ne change pas, l'effet se recrée return debouncedValue } - Approche corrigée:
export function useDebounce<T>(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState<T>(value) const timerRef = useRef<NodeJS.Timeout>() // ✅ Référence persistante useEffect(() => { if (timerRef.current) { clearTimeout(timerRef.current) } timerRef.current = setTimeout(() => { setDebouncedValue(value) }, delay) return () => { if (timerRef.current) { clearTimeout(timerRef.current) } } }, [value, delay]) // ✅ Recrée uniquement quand value change return debouncedValue }
Tests:
- ✅ Tests de performance: Mesurer le nombre de renders avant/après correction
- ✅ Tests fonctionnels: Vérifier que le debounce fonctionne toujours correctement
- ✅ Tests d'intégration: Vérifier que les composants utilisant
useDebouncefonctionnent toujours - ✅ Tests de stress: Tester avec des mises à jour très fréquentes
Points techniques:
- Utiliser
useRefpour stocker la référence du timer - Vérifier si
timerRef.currentexiste avant de créer un nouveau timer - Nettoyer le timer existant avant d'en créer un nouveau
- Conserver la même API publique (ne pas changer la signature du hook)
Effets secondaires:
- Réduction des cascades de re-renders
- Amélioration de la performance globale
- Moins d'utilisation CPU
- Meilleure fluidité de l'UI
Risques:
- Si mal implémenté, le timer peut ne jamais être nettoyé
- Peut introduire des bugs subtiles de timing
- Peut affecter tous les composants utilisant
useDebounce
Mitigation:
- Tests approfondis avec différents scénarios de mise à jour
- Code review attentif de la logique du timer
- Tests de performance pour comparer avant/après
- Avoir un plan de rollback prêt
Dépendances: Aucune (peut être fait en parallèle)
Story #8: Corriger useEffect Mal Géré dans note-card.tsx
Priorité: P1 (High)
Estimation: 30 minutes
Complexité: Moyenne
En tant que: Développeur Frontend
Je veux: Corriger le useEffect qui charge les collaborateurs à chaque changement de note.id et note.userId en ajoutant les dépendances manquantes (isOwner, isSharedNote) pour éviter les re-renders inutiles et améliorer les performances.
Afin de: Éliminer les chargements inutiles de collaborateurs et les re-renders associés, améliorant ainsi la performance du composant NoteCard et de l'application globale.
Critères d'acceptation:
- ✅ Le
useEffectne se déclenche pas uniquement quandnote.idounote.userIdchangent - ✅ Les dépendances complètes (
isOwner,isSharedNote) sont incluses - ✅ Les re-renders inutiles sont éliminés
- ✅ Les collaborateurs sont toujours correctement chargés
- ✅ La performance du composant est améliorée
Contexte technique:
- Fichier affecté:
keep-notes/components/note-card.tsx - Lignes à modifier: 162-180
- Approche actuelle (bug):
useEffect(() => { const loadCollaborators = async () => { // ... chargement des collaborateurs } loadCollaborators() }, [note.id, note.userId]) // ❌ Se déclenche trop souvent // ❌ Se déclenche même si isOwner et isSharedNote changent - Approche corrigée:
useEffect(() => { const loadCollaborators = async () => { // ... chargement des collaborateurs } loadCollaborators() }, [note.id, note.userId, isOwner, isSharedNote]) // ✅ Dépendances complètes // ✅ Se déclenche uniquement quand une de ces valeurs change vraiment
Tests:
- ✅ Tests de performance: Mesurer le nombre de renders du composant
- ✅ Tests fonctionnels: Vérifier que les collaborateurs sont toujours chargés correctement
- ✅ Tests d'intégration: Vérifier que les modifications de note ne causent pas de re-renders inutiles
- ✅ Tests de limites: Cas limites avec beaucoup de notes
Points techniques:
- Ajouter
isOwneretisSharedNoteaux dépendances duuseEffect - Comprendre quand chaque valeur change vraiment
- Éviter les re-renders en cascade
- S'assurer que les calculs de
isOwneretisSharedNotesont optimisés
Effets secondaires:
- Réduction des re-renders du composant NoteCard
- Amélioration de la performance globale
- Moins d'appels API inutiles
- Meilleure expérience utilisateur (pas de lag lors des interactions)
Risques:
- Si les dépendances sont mal définies, peut ne jamais se déclencher
- Peut introduire des bugs subtiles si les conditions de chargement changent
- Peut affecter d'autres fonctionnalités qui dépendent des collaborateurs
Mitigation:
- Tests approfondis avec différents scénarios de collaboration
- Code review attentif de la logique de dépendances
- Tests de performance pour comparer avant/après
- Avoir un plan de rollback prêt
Dépendances: Story #1 (triggerRefresh) doit être complétée AVANT cette story
🟢 MEDIUM (Compléter si le temps le permet)
Story #9: Tests de Validation Automatisés
Priorité: P2 (Medium)
Estimation: 2 heures
Complexité: Moyenne
En tant que: QA Engineer / Développeur Frontend Je veux: Créer des tests Playwright automatisés pour valider que tous les bugs corrigés dans les stories précédentes sont bien résolus, et s'assurer qu'il n'y a pas de régression.
Afin de: Avoir une couverture de tests complète pour empêcher les régressions et documenter que les corrections de bugs fonctionnent comme prévu.
Critères d'acceptation:
- ✅ Test #1: Validation de triggerRefresh() - Le re-render fonctionne sans router.refresh()
- ✅ Test #2: Validation de note-card.tsx - Les actions (pin, archive, color, etc.) n'appellent pas router.refresh()
- ✅ Test #3: Validation de page.tsx - Batch organization et auto-labels n'appellent pas router.refresh()
- ✅ Test #4: Validation de notebooks-context.tsx - Les actions sur notebooks n'appellent pas window.location.reload()
- ✅ Test #5: Validation mobile - Drag désactivé sur mobile, scroll fonctionne
- ✅ Test #6: Validation UI - Les boutons de fermeture sont distincts et clairs
- ✅ Test #7: Validation performance - useDebounce recrée le timer uniquement quand nécessaire
- ✅ Tous les tests Playwright passent sans modification
- ✅ Aucune régression détectée
Contexte technique:
- Nouveau fichier:
keep-notes/tests/validation/bug-fixes-validation.spec.ts - Framework de test: Playwright
- Scénarios à tester: Tous les scénarios manuels mentionnés dans les stories précédentes
Tests à créer:
// Test #1: TriggerRefresh fonctionne
test('triggerRefresh force re-render without page reload', async ({ page }) => {
// ... test complet
});
// Test #2: NoteCard actions sans refresh
test('NoteCard toggle pin does not call router.refresh', async ({ page }) => {
// ... test complet
});
// Test #3: Notebook actions sans reload
test('Notebook update does not call window.location.reload', async ({ page }) => {
// ... test complet
});
// Test #4: Mobile drag désactivé
test('Mobile drag is disabled', async ({ page }) => {
// ... test complet
});
// Test #5: Boutons distincts
test('Close buttons have distinct icons and colors', async ({ page }) => {
// ... test complet
});
// Test #6: useDebounce optimisé
test('useDebounce only recreates timer when value changes', async ({ page }) => {
// ... test complet
});
Points techniques:
- Utiliser les sélecteurs Playwright précis pour cibler les composants
- Vérifier les appels réseau (aucun router.refresh, aucun window.location.reload)
- Vérifier les re-renders (count, timing)
- Utiliser les fixtures de test Playwright si nécessaire
- Capturer les screenshots en cas d'échec
Effets secondaires:
- Couverture de tests automatisée pour empêcher les régressions
- Documentation vivante du comportement attendu
- Confiance que les corrections fonctionnent correctement
- Facilite les futures modifications
Risques:
- Les tests peuvent être fragiles si l'UI change
- Maintenance supplémentaire des tests
- Temps d'exécution des tests peut être long
Mitigation:
- Utiliser des sélecteurs robustes qui ne dépendent pas trop de la structure DOM
- Marquer les tests comme "flaky" si nécessaire
- Exécuter les tests en parallèle pour réduire le temps
- Documentation claire de maintenance des tests
Dépendances: Toutes les stories précédentes doivent être complétées
🗂 Dépendances Entre Stories
Ordre Suggéré
-
Story #1 (triggerRefresh) - DOIT ÊTRE PREMIÈRE
- Raison: C'est la cause racine de tous les problèmes de refresh
- Blocking: Stories #2, #3, #4, #7, #8
- Si échoue, toutes les autres corrections risquent d'échouer aussi
-
Story #7 (useDebounce)
- Raison: Améliore la performance globale
- Recommandée avant: Stories #2, #8 (les deux ont des problèmes de re-renders)
-
Story #2 (Supprimer router.refresh dans note-card.tsx)
- Dépendance: Story #1
- Recommandée avant: Story #8 (même composant)
-
Story #3 (Supprimer router.refresh dans page.tsx)
- Dépendance: Story #1
- Indépendante: Peut être faite en parallèle avec Story #2
-
Story #4 (Remplacer window.location.reload dans notebooks-context.tsx)
- Dépendance: Story #1
- Indépendante: Peut être faite en parallèle avec Stories #2, #3
-
Story #5 (Mobile drag)
- Indépendante: Peut être faite en parallèle avec d'autres
- Option: Remplacer Muuri par @dnd-kit/core (plus complexe)
-
Story #6 (Doublons boutons)
- Indépendante: Peut être faite en parallèle
- Recommandée avant: Tests d'accessibilité
-
Story #8 (useEffect mal géré)
- Dépendance: Story #1
- Recommandée après: Stories #2, #3, #4, #6
-
Story #9 (Tests de validation)
- DOIT ÊTRE DERNIÈRE
- Dépend de: TOUTES les stories précédentes
- Validation de toutes les corrections
Graph de Dépendances Visuel
Story #1 (triggerRefresh)
├─> Story #2 (note-card router.refresh)
├─> Story #3 (page.tsx router.refresh)
├─> Story #4 (notebooks window.location.reload)
├─> Story #7 (useDebounce)
└─> Story #8 (note-card useEffect)
└─> Story #9 (Tests validation)
Story #5 (Mobile drag)
└─> Story #9 (Tests validation)
Story #6 (Doublons boutons)
└─> Story #9 (Tests validation)
🎬 Acceptation Criteria (Critères d'Acceptation Globaux)
Pour Toutes les Stories
- ✅ Fonctionnalité: Le bug est corrigé et la fonctionnalité fonctionne comme prévu
- ✅ Tests: Les tests (manuels ou automatisés) passent
- ✅ Performance: Aucune dégradation de performance mesurable
- ✅ UX: L'expérience utilisateur est améliorée
- ✅ Code: Le code est propre, bien documenté et suit les conventions
- ✅ Régression: Aucune régression détectée dans d'autres fonctionnalités
- ✅ Accessibilité: Les corrections n'affectent pas l'accessibilité
Critères Spécifiques par Type de Story
Stories de Bug Fix
- ✅ Le bug ne se produit plus dans les scénarios testés
- ✅ Les tests Playwright existants passent
- ✅ Aucun effet secondaire indésirable
- ✅ La correction est pérenne et maintenable
Stories de Performance
- ✅ Métrique de performance améliorée (ex: temps de render réduit)
- ✅ Moins de re-renders mesurables
- ✅ Utilisation CPU réduite
🚨 Risques et Blockers
Risques Identifiés
-
Risque de Régression
- Description: Les corrections de bugs risquent d'en casser d'autres parties du code
- Probabilité: Moyenne
- Impact: Élevé - pourrait affecter d'autres fonctionnalités
- Mitigation: Tests approfondis, code review, déploiement progressif
-
Risque de Complexité Non Anticipée
- Description: Story #5 (Mobile drag) peut être plus complexe que prévu si remplacement de Muuri par @dnd-kit/core
- Probabilité: Faible
- Impact: Moyen - peut prendre plus de temps
- Mitigation: Commencer avec l'option simple (désactiver drag), option complexe en suivant
-
Risque de Performance
- Description: Story #5 (Remplacement Muuri) peut avoir des implications de performance non testées
- Probabilité: Faible
- Impact: Faible à Moyen
- Mitigation: Tests de performance avant/après
-
Risque de Dépendances
- Description: Story #1 bloque plusieurs autres stories (#2, #3, #4, #7, #8)
- Probabilité: Moyenne
- Impact: Élevé - ne peut pas faire progresser sur ces bugs
- Mitigation: Prioriser Story #1, avoir des alternatives prêtes, tests parallèles où possible
-
Risque UX
- Description: Story #6 (Nouvelles icônes) peut surprendre les utilisateurs
- Probabilité: Faible
- Impact: Faible à Moyen - confusion temporaire
- Mitigation: Documentation claire, tooltips explicites, période d'adaptation
Blockers Actuels
- Aucun blocker identifié
- Tous les fichiers sont accessibles et modifiables
- L'environnement de développement est opérationnel
📅 Timeline Estimée
Par Story
| Story | Estimation | Notes |
|---|---|---|
| Story #1: triggerRefresh() | 2 heures | Critique - faire en priorité absolue |
| Story #7: useDebounce | 30 minutes | Simple - peut être fait rapidement |
| Story #2: note-card router.refresh | 1 heure | Simple - dépend de Story #1 |
| Story #3: page.tsx router.refresh | 30 minutes | Simple - dépend de Story #1 |
| Story #4: notebooks window.location.reload | 1 heure | Simple - dépend de Story #1 |
| Story #6: Doublons boutons | 1 heure | Simple - indépendante |
| Story #8: note-card useEffect | 30 minutes | Simple - dépend de Story #1 |
| Story #5: Mobile drag | 2 heures | Complexe - option simple recommandée |
| Story #9: Tests validation | 2 heures | Critique - doit être fait à la fin |
Total estimé: 10 heures (2 semaines à 50% de capacité)
🎯 Objectifs de Démo (Pour Sprint Review)
Si vous voulez présenter le travail à la fin du Sprint:
-
Vidéos Avant/Après:
- Montrer les bugs avant correction (flash, perte de scroll)
- Montrer les corrections (UI instantanée, sans flash)
- Comparer les performances (temps de render)
-
Tests Automatisés:
- Capturer l'exécution des tests Playwright
- Montrer que tous les tests passent
- Mettre en évidence les améliorations mesurées
-
Métriques de Succès:
- Nombre de bugs corrigés: 8/8
- Tests créés: 3 tests automatisés
- Tests Playwright existants: 3 tests passent
- Amélioration de performance mesurable: % réduction de re-renders
-
User Stories:
- "Avant la correction, chaque action causait un flash d'écran de 2 secondes"
- "Maintenant, l'UI se met à jour instantanément"
- "Sur mobile, le drag ne fonctionne pas, mais le scroll est fluide"
📝 Notes pour l'Équipe
Bonnes Pratiques
-
Committer fréquemment
- Une story terminée = un commit
- Message de commit clair et descriptif
- Branche par story ou par type de correction
-
Tester localement avant de push
npm run devpour vérifier manuellement- Tester les scénarios limites
- Vérifier la console pour les erreurs
-
Utiliser les tests Playwright existants
npx playwright test keep-notes/tests/bug-*.spec.ts- Tous les tests doivent passer après chaque correction
-
Code Review
- Faire review du code des pairs avant de merge
- Vérifier les conventions de style
- S'assurer que le code est documenté
-
Documenter les changements
- Mettre à jour le README du projet
- Ajouter des notes dans les fichiers modifiés si nécessaire
- Mettre à jour le changelog
Outils et Ressources
- Documentation: Voir
_bmad-output/BUG-ANALYSIS-REPORT.md - Plan de correction: Voir
_bmad-output/PLAN-DE-CORRECTION-DES-BUGS.md - Tests existants:
keep-notes/tests/bug-*.spec.ts
Communication
- Signaler les blocages ou risques immédiatement
- Demander de l'aide si une story est plus complexe que prévu
- Partager les leçons apprises à la fin du Sprint
🎉 Critères de Succès du Sprint
Le Sprint sera considéré comme succès si:
Must-Have (Doit être complété)
- ✅ Toutes les 8 stories sont complétées
- ✅ Story #1 (triggerRefresh) est fonctionnelle
- ✅ Tous les tests Playwright existants passent
- ✅ Tests de validation automatisés sont créés et passent
- ✅ Aucun bug critique rémanent
- ✅ Aucun effet secondaire majeur
Nice-to-Have (Souhaitable)
- ✅ Story #5 (Mobile drag) est corrigée (même si simple)
- ✅ La performance globale est améliorée
- ✅ L'UX est significativement meilleure
- ✅ La documentation est à jour
- ✅ Le code est propre et maintenable
Performance Targets
- ✅ Réduction d'au moins 50% des re-renders inutiles
- ✅ Aucun flash d'écran lors des actions
- ✅ Aucune perte de position de scroll
- ✅ Temps de réponse UI < 100ms
🔄 Status Actuel
🟡 En préparation - Sprint créé, prêt à commencer
Prochaine étape:
- Révision du Sprint avec l'équipe ou les parties prenantes
- Affectation des stories aux développeurs
- Création des branches git si nécessaire
- Commencement avec Story #1 (triggerRefresh)
Estimation de début: Immédiatement après validation
Créé le 2026-01-15 pour corriger les 8 bugs critiques/high identifiés lors de l'analyse exhaustive du codebase.