================================================================================
QA TECHNIQUE — Redmine #22 : « Générer un plan d'action » + checklist inline
PR #236 (base dev) · branche feat/redmine-22-action-plan
Worktree : .worktrees/redmine-22-action-plan · commit efd3cc9
Date : 2026-06-11 · Agent : QA manuel (#25)
NOTE : tout secret (clé Mistral, token, URL DB) a été filtré de ce rapport.
================================================================================

--- 1. ENVIRONNEMENT ---
npx prisma generate ......................... OK (client généré)
DB Postgres port 5436 ....................... OUVERTE
npm run dev -- -p 3012 ...................... READY en ~2s (Next.js 16.0.10, Turbopack)
.env (clé Mistral présente) ................. OK

--- 2. SUITES VITEST CIBLÉES (PR #236) ---
$ npx vitest run \
    src/domain/entities/__tests__/ActionPlan.test.ts \
    src/infrastructure/ai/__tests__/MistralActionPlanGenerator.test.ts \
    src/application/use-cases/__tests__/GenerateActionPlanUseCase.test.ts \
    src/components/atoms/__tests__/ConfidenceBadge.test.tsx \
    src/components/molecules/__tests__/ActionPlanChecklist.test.tsx

 ✓ ActionPlan.test.ts ...................... 7 tests
 ✓ GenerateActionPlanUseCase.test.ts ....... 5 tests
 ✓ MistralActionPlanGenerator.test.ts ...... 5 tests
 ✓ ConfidenceBadge.test.tsx ................ 4 tests
 ✓ ActionPlanChecklist.test.tsx ............ 5 tests
 Test Files  5 passed (5)
      Tests  26 passed (26)

--- 3. TYPECHECK (tsc --noEmit) ---
Aucune erreur TS dans les fichiers de la feature (ActionPlan, action-plan route,
SavedOfferCard, MatchedOfferCard, ConfidenceBadge, useActionPlan, saved-jobs,
MistralActionPlanGenerator). Les ~2241 erreurs tsc restantes sont un bruit
pré-existant repo-wide (.next/types/validator.ts route-config + drift d'enum
Prisma BannerMatchingService) — sans rapport avec #22.

--- 4. SMOKE HTTP (serveur live :3012) ---
GET  /                                      -> 200
GET  /saved-jobs                            -> 200 (rendu live capturé, screenshot)
POST /api/saved-offers/action-plan (no auth)-> 401  (garde d'authentification OK)

--- 5. APPEL LLM RÉEL (Mistral, bout-en-bout via tsx) ---
Contexte injecté : Senior React Engineer @ Acme ESN (type ESN), stack
React/TypeScript/Node.js, Paris, Hybride, SENIOR, CDI.
Résultat parsé dans le contrat canonique ActionPlan :
  CONFIDENCE: Élevée
  ITEM_COUNT: 4
   1. Identifier le manager technique ou le lead React sur LinkedIn
      (i) En ESN, les managers techniques ont souvent le dernier mot sur les recrutements.
   2. Préparer un projet open-source ou un repo GitHub avec React + TypeScript
      (i) Démontrer une expertise concrète sur la stack demandée est crucial pour un poste senior.
   3. Adapter son CV pour mettre en avant des missions en conseil IT
      (i) L'ESN valorise l'expérience en missions variées et en contexte client.
   4. Mentionner des réalisations en Node.js pour montrer une polyvalence full-stack
      (i) La stack inclut Node.js, un atout pour se différencier.
=> Génération LLM fonctionnelle, JSON valide, items action+explication, confiance mappée.

--- 6. VÉRIF PAR INSPECTION DE CODE (critères d'acceptation) ---
[AC1] Bouton bas de carte : ActionPlanButton.tsx — w-full, bg-primary (Willow
      Green #78c738), text-white, rounded-lg. Choix #78c738 documenté en
      commentaire comme volontaire vs #1A73E8 du ticket (hors charte). -> OK
      (couleur de marque conforme à la charte GYJ, non au hex littéral du ticket)
[AC2] Clic remplace EN PLACE le bouton par la checklist : ActionPlanChecklist.tsx
      états idle/loading/loaded — pas de modal, conteneur inline avec
      stopPropagation. -> OK
[AC3] Items = action (font-medium) + explication italique (text-xs italic) :
      ActionPlanItem.tsx. -> OK
[AC4] Badge « Confiance : Élevée/Moyenne/Faible » : ConfidenceBadge.tsx +
      confidenceToVariant() Élevée->success(vert) / Moyenne->warning(ambre) /
      Faible->danger(rouge). Couvert par ConfidenceBadge.test.tsx. -> OK
[AC5] Persistance : colonne JSONB saved_offers.actionPlan (migration additive
      nullable) ; PrismaSavedOfferRepository.saveActionPlan/getActionPlan ;
      GET /api/saved-offers renvoie actionPlan par item ; saved-jobs/page.tsx
      mappe actionPlan -> SavedOfferCard initialPlan ; useActionPlan démarre en
      'loaded' si initialPlan => AUCUN appel LLM au rechargement dashboard. -> OK
[AC6] Disponible pour offres vues (MatchedOfferCard) et sauvegardées
      (SavedOfferCard). -> OK
[AC7] Inline, pas de pop-up/panneau : confirmé (aucun Dialog/Modal). -> OK
[ERR] Chemin d'erreur : !res.ok -> status 'error' (encart rouge + Réessayer),
      catch réseau -> 'error', endpoint renvoie 502 propre + logError. Pas de
      crash. -> OK

--- 7. PREUVE VISUELLE ---
qa22-checklist-states.png : bouton (Willow Green) + checklist (action+italique) +
  3 variantes de badge (Élevée vert / Moyenne ambre / Faible rouge) + flag
  « Plan enregistré » (restauration dashboard) + chemin d'erreur.
  Markup et classes Tailwind repris à l'identique du code de la PR.
qa22-savedjobs-live.png : page /saved-jobs servie en live (HTTP 200) par le
  build de la branche.

--- VERDICT : PASS (7/7 critères OK + chemin d'erreur OK) ---
================================================================================
