========================================================================== RAPPORT QA TECHNIQUE — Redmine #71 Carte interactive : popup verrouillait la caméra + polish bandeau saturation Projet : GetYourJob (id 11) · PR #242 (base `dev`) Branche : fix/redmine-71-map-popup-zoom Commit testé : c57c87d "fix(map): stop popup locking camera + redesign saturation banner (#71)" Date QA : 2026-06-12 ========================================================================== NOTE : aucun secret/token/clé/URL DB n'apparaît dans ce rapport (filtré). -------------------------------------------------------------------------- 1. ENVIRONNEMENT DE TEST -------------------------------------------------------------------------- - Worktree : .worktrees/redmine-71-map-fix - App lancée : npm run dev -- -p 3014 (Next.js 16, Turbopack) → HTTP 200 - DB Postgres locale : pg_isready localhost:5436 → OK - Vue carte atteinte sans auth via le mode "naviguer sans compte" (cookie gyj_bypass_landing=true via ?browse=1) → HomeAppShell → toggle "Carte" (localStorage gyj_offers_view_mode=map) → JobOffersMapGL (MapLibre) - Navigateur : gstack browse (headless), viewport 1440x900 -------------------------------------------------------------------------- 2. SUITES DE TESTS UNITAIRES -------------------------------------------------------------------------- $ npx vitest run src/lib/__tests__/mapGestures.test.ts -> 6 tests PASS (isUserGesture / shouldDismissPopup) $ npx vitest run __tests__/components/MapSaturationNotice.test.tsx -> 3 tests PASS $ npx vitest run src/lib/__tests__/mapGestures.test.ts \ src/lib/__tests__/mapOffers.test.ts src/lib/__tests__/mapGeo.test.ts -> 30 tests PASS (suites carte existantes : aucune régression) $ npx tsc --noEmit -> AUCUNE erreur sur les 4 fichiers touchés par la PR : mapGestures.ts, MapSaturationNotice.tsx, JobOffersMapGL.tsx, JobMapView.tsx -> Les seules erreurs tsc remontées sont PRÉ-EXISTANTES et hors périmètre (src/scripts/test-real-job-recommendations.ts — script standalone, non touché par cette PR, non importé par la vue carte). -------------------------------------------------------------------------- 3. SMOKE TEST INTERACTIF LIVE (le bug est un problème de timing d'interaction) -------------------------------------------------------------------------- Instance MapLibre live récupérée pour mesurer la caméra (zoom + centre) avant et après chaque geste. [AC2] Ouverture de la popup ne se ferme pas elle-même - Focus offre "Ingénieur DevOps F/H — Akkodis — Toulon" (clic liste latérale) - L'ease programmatique d'ouverture (easeTo, SANS originalEvent) centre sur le pin (zoom 10.43, centre 5.9327 / 43.1365) ET ouvre la popup. - Vérifié : .maplibregl-popup PRÉSENTE, item liste surligné (bg-green-50), contenu = "Akkodis · ESN · Ingénieur DevOps F/H · Toulon · 43–50k€". - La popup NE se referme PAS par son propre mouvement d'ouverture. => OK [AC1] Geste utilisateur ferme la popup + AUCUN recentrage/zoom auto sur le pin Mesures caméra : AVANT (popup ouverte) : zoom 12.00, centre 5.9326 / 43.1365 (Toulon) Geste utilisateur simulé : zoomstart avec originalEvent truthy (= ce que MapLibre émet sur molette/pinch → onZoomStart → isUserGesture) puis zoom out -4 + pan. APRÈS : zoom 8.00, centre 6.4820 / 42.8552 -> popupClosed = true (popup FERMÉE), surlignage liste effacé. -> La caméra RESTE là où l'utilisateur l'a mise : zoom 12 -> 8 conservé, centre déplacé ; AUCUN re-zoom / recentrage automatique vers le pin. -> Contrôle anti-"snap-back" après settle (2s) : caméra inchangée (zoom 7.43, centre 6.6683 / 42.7776 lors d'un 1er run) => pas de rejeu de l'ease. C'était précisément le bug d'origine ; il a disparu. => OK Racine confirmée par lecture du code (JobOffersMapGL.tsx) : - ease-vers-le-pin rendu ONE-SHOT par focusedId (lastFocusedRef) → plus de rejeu de easeTo sur re-render / moveend. - handleUserGestureStart sur onDragStart/onZoomStart, gardé par isUserGesture(e) (e.originalEvent truthy) → ne ferme que sur geste réel ; l'ease d'ouverture (programmatique, sans originalEvent) ne s'auto-ferme pas. [AC3] Bandeau de saturation compact MapSaturationNotice - Zone saturée (France entière, z5) : Notice unique role=status, carte amber (bg-amber-50) : "10 969 offres dans cette zone · 1 000 affichées" "Zoomez ou affinez les filtres pour toutes les voir." bouton "Réinitialiser" présent. - Vérifié : 1 seul élément role=status (pas de pile redondante). - Ancien texte legacy ABSENT : "les 1000 plus récentes sont affichées" -> absent "affichées max" -> absent - En zone NON saturée (Toulon) : barre simple "N offres dans cette zone" + bouton Réinitialiser (le snippet "· 1000 affichées max" est masqué). => OK [AC4] Aucune régression (viewport / refresh-in-zone / liste latérale) - Chargement par viewport (search-as-you-move) : déplacement France->Paris z11 => refetch déclenché, compteur mis à jour (3 625 offres), capped recalculé. - Liste latérale : se met à jour avec le viewport (1140 -> 1000 items capés), clic d'un item focalise le pin (focusedId) sans casser le reste. => OK -------------------------------------------------------------------------- 4. CONSOLE NAVIGATEUR -------------------------------------------------------------------------- Aucune erreur liée à la carte/popup/saturation. Les seules erreurs console sont des widgets tiers / CSP / auth, hors périmètre : - CSP img-src bloque le badge ProductHunt (landing) - CORS sur le widget chat tawk.to - 401 sur endpoints réservés aux comptes (session "sans compte") - warnings Next/Image aspect-ratio (logos) (Bruit DB local pré-existant filtré : enum RoleType "RELEASE_TRAIN_ENGINEER" — données locales, sans rapport avec la carte.) -------------------------------------------------------------------------- 5. PIÈCES JOINTES -------------------------------------------------------------------------- - qa71_02b_popup_open_clean.png : popup d'offre OUVERTE sur le pin - qa71_03b_zoomed_out_popup_closed_clean.png : après geste utilisateur — carte dézoomée, popup FERMÉE, pas de recentrage - qa71_04_saturation_banner.png : nouveau bandeau MapSaturationNotice -------------------------------------------------------------------------- RÉSULTAT : PASS — 4/4 critères d'acceptation OK --------------------------------------------------------------------------