==========================================================================
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
--------------------------------------------------------------------------
