# Test plan — commit `fa7ea19` (native save + auto-backup + folder customization + attachments + UX bundle) Coche au fur et à mesure. Les **🔥 regression** sont les bugs déjà vus à re-vérifier explicitement. ## 0. Setup - [✅] `delphi-backend\assets\BuildAssets.cmd` + F9 (sinon embedded ≠ disque) - [✅] Lance avec `config.txt` présent (debug panel + log) - [✅] Crée 2 users de test ou utilise vault existant non critique ## 1. Native Save As - [✅] Settings → Export encrypted JSON → maître pw OK → backup pw → boîte "Enregistrer sous" Windows native (pas le popup Téléchargements Edge) - [✅] Export CSV → idem natif - [✅] Annule la dialog → toast "save cancelled" attendu, pas d'erreur silencieuse ## 2. Auto-backup - [✅] Settings → Auto-backup → toggle on → prompt pwd (< 6 chars rejeté) - [✅] "Choose…" → folder Windows valide → path s'affiche - [✅] "Backup now" → fichier `vault-autobackup-yyyymmdd-HHmmss.json` créé dans le folder - [✅] Lance plusieurs "Backup now" (interval = 1 jour) → fichiers s'accumulent → après N+1, le plus vieux est purgé (vérifier `keep` retention) - [✅] Toggle off → re-on → prompt pwd à nouveau (confirme que le pwd est effacé) - [ ] Restart app, unlock → 5s plus tard, si `interval` passé, backup auto silencieux (regarde le folder) - [✅] **Restore** : install fresh → import "Encrypted JSON" → choisis un backup → tape le pwd → entries restaurées intactes ## 3. Folder customization - [✅] Sidebar → + Folders → modal s'ouvre → name+color+icon → Create → folder visible avec custom icon + color - [✅] Hover sur folder → pencil apparaît → click → modal pré-rempli → change icon → Save → re-render OK - [✅] **Folder chip sur card** : crée entry dans "Banking" → la chip sous le pwd montre l'icône shield + couleur (pas le default folder gris) 🔥 - [✅] Drag-reorder : grab folder → drag au-dessus d'un autre → ligne d'insertion cyan visible (above/below selon mouse Y) → drop → ordre changé persisté (refresh app) ## 4. Drag entries vers folders 🔥 - [✅] Drag d'une LOGIN entry → folder → toast "Moved to X" → entry change de folder - [✅] Drag d'une NOTE entry → folder → toast "Moved to X" (régression v1, doit marcher maintenant) - [✅] Drag d'une entry avec **TOTP configuré** → folder → ouvre l'entry après → **TOTP toujours là** (pas wipé) - [✅] Drag d'une entry avec **custom_fields** → folder → custom_fields préservés - [✅] Drag d'une entry vers son propre folder → no-op (pas d'erreur) ## 5. Recently used - [✅] Vue vide au start (rien jamais touché) → "No entries" - [✅] Copy password d'une entry → attends 2s → sidebar "Recently used" → entry visible en tête - [✅] Ouvre une entry (slideover) → idem - [✅] Copy 5 entries rapidement → max 10 visibles, ordre = plus récent d'abord - [✅] Lock vault + unlock → liste rechargée du serveur (accessed_at persiste) ## 6. Encrypted attachments - [✅] Crée entry, sauve, ré-ouvre → section "Attachments" visible (pas sur new entry) - [✅] "Attach file" → file picker → upload PDF/PNG < 5 MB → toast OK → liste affiche filename+size+mime - [✅] Upload fichier > 5 MB → toast erreur "File too large" - [✅] Download → boîte Save As → fichier décrypté identique à l'original (hash check si paranoid) - [✅] Delete → confirm modal → row disparaît - [✅] Crée 2nd attachment, supprime entry parent → vérifier que les rows entry_attachments sont aussi delete (CASCADE FK) via `sqlite vault.db "select count(*) from entry_attachments"` - [✅] Master pw rotation avec attachments → toast "Re-encrypting N attachment(s)…" puis "Attachments re-encrypted" → download d'un attachment **après rotation** réussit (re-chiffré client-side dans le flux change-master) ## 7. Password expiry - [✅] Settings → Flag aged → 90 days - [✅] Backdoor : `sqlite vault.db "UPDATE vault_entries SET password_changed_at = '2024-01-01' WHERE id = X"` → recharge → badge "Aged" visible sur la card de X - [✅] Edit l'entry, change le password, Save → badge disparaît (password_changed_at bumpé) - [✅] Edit l'entry, change **uniquement le title**, Save → badge **toujours là** (password_changed_at pas bumpé — le CASE SQL filtre) - [✅] Filters dropdown → "Aged password" → liste filtrée ## 8. Print recovery code - [✅] Settings → Recovery → Generate (ou regenerate) → modal avec code → bouton "Print" - [✅] Click Print → preview Edge → vérifie : code 32px monospace, account name, date, instructions 3 étapes, page A4 propre (le reste de l'app caché) - [✅] Annule la print preview → app revient normalement (cleanup du #printRecoveryArea) ## 9. Audit log - [✅] Sidebar → Tools → Audit log → table visible avec actions chronologiques - [✅] Fais une action (add entry, copy pwd) → refresh audit log → nouvelle ligne en tête - [✅] Search dans audit log → filtre marche ## 10. Pinned entries - [✅] Sur n'importe quelle card, click pushpin → toast "Pinned to top" → entry remonte en tête de la liste - [✅] Re-click → toast "Unpinned" → entry retrouve sa place dans le sort courant - [✅] Change le sort (name asc → date desc) → entries pinned restent en tête, le tri interne s'applique aux deux groupes - [✅] Compact mode (Settings → Compact actions) → kebab menu contient "Pin to top" / "Unpin" - [✅] Pin 3 entries, lock vault, unlock → les 3 sont toujours en tête (persist serveur) - [ ] Master pw rotation → pinned préservé (pas wipé) - [✅] Duplicate d'une entry pinned → la copie est **non pinned** (comportement voulu) ## 11. Search history - [✅] Tape une recherche, attends 1s sans bouger → recherche ajoutée à l'historique (pas besoin d'Enter) - [✅] Vide la searchbar → focus → dropdown "Recent searches" avec les dernières - [✅] Click sur un item → re-filtre et bump en tête - [✅] X individuel sur un item → suppression isolée - [✅] "Clear" → vide tout l'historique - [✅] Dropdown disparaît au click hors searchbar / Escape / blur => un petit probleme > focus search bar > dropdown search affiché > clique à nouveau search (focus non changé) dropdown non affiché ## 12. PIN unlock 🔥 ### 12a. Setup - [✅] Settings → "PIN unlock" section visible (Bridge actif requis) - [✅] Status "No PIN set." + bouton "Set PIN" visible, "Remove PIN" caché - [✅] Click "Set PIN" → prompt → tape **3 chiffres** → erreur inline "PIN must be 4–12 digits" (modal reste ouvert) - [✅] Tape **abcd** → même erreur (digits only) - [✅] Tape **123456** → toast "PIN set" → status passe à "PIN is set on this device." → bouton "Change PIN" + "Remove PIN" visibles - [✅] Vérifie `%LOCALAPPDATA%\PMServer\pin-unlock.bin` existe ### 12b. Mode 'pin' (PIN only) - [✅] Settings → dropdown "Unlock method" = "PIN only" → toast "Unlock method updated" - [✅] Lock vault → auth screen affiche **PIN** input (pas master pw) + lien "Use master password instead" - [✅] Tape bon PIN → unlock instant - [✅] Lock → tape mauvais PIN → "Wrong PIN. Try again..." - [✅] Lock → tape mauvais PIN 5 fois → blob auto-supprimé → écran retombe sur master pw → message "Too many wrong PIN attempts. Sign in with your master password." - [✅] Settings après ça : status "No PIN set." → re-set possible ### 12c. Mode 'both' (master + PIN) - [✅] Re-set PIN, dropdown = "Master password + PIN" - [✅] Lock → auth affiche pw + PIN ensemble - [✅] Tape bon pw + mauvais PIN → unlock initie, puis lockVault avec "Wrong PIN. Try again." - [✅] Tape bon pw + bon PIN → unlock OK - [✅] Pw seul (PIN vide) → submit bloqué (HTML5 required) ### 12d. Escape PIN-only - [✅] Mode 'pin', PIN configuré → auth → click "Use master password instead" → champ pw apparaît, PIN caché → unlock master pw marche - [✅] Lock après → revient en mode PIN (le choix master était one-shot) ### 12e. Quick Unlock + PIN coexistence - [✅] Active Quick Unlock + définis PIN en mode 'both' - [✅] Restart app → Quick Unlock cold-start réussit sans demander PIN (device trusted) - [✅] Lock manuel → auth demande pw + PIN comme prévu ### 12f. Sensitive actions = master pw obligatoire - [ ] Mode 'pin', unlocked → Settings → Export encrypted JSON → askReauth demande **master pw** (pas PIN) - [ ] Idem : Change master password → master pw demandé - [ ] Idem : Generate recovery code → master pw demandé ### 12g. Master pw rotation invalide le PIN - [ ] Mode 'pin', PIN configuré → Change master pw → après rotation, status PIN passe à "No PIN set." (blob wipé) + unlockMode tombe à 'pw' - [ ] Next lock → champ master pw → unlock OK avec nouveau pw ### 12h. Remove PIN - [✅] Click "Remove PIN" → confirm dialog - [✅] Confirm → blob deleted, status repasse à "No PIN set.", mode revert à 'pw' ### 12i. Recovery key continue à marcher - [ ] Avec mode 'pin' actif, ouvre auth → click "Forgot master password? Use a recovery code" → flow recovery normal (le bouton reste visible) ## 13. Regressions générales 🔥 - [✅] Quick unlock encore fonctionnel après update - [✅] Master password rotation OK + entries déchiffrables après - [✅] Autofill (Ctrl+Shift+L/P) toujours OK - [ ] Recovery code redeem fonctionne (sur compte test) - [✅] Lock vault → unlock → toutes entries présentes - [ ] Trash → restore → entry revient avec tous ses champs (kind, custom_fields, TOTP) ## 13. Console / log scrutiny - [ ] Ouvre debug panel → execute scénarios ci-dessus → log Delphi sans `FAILED`/`ERROR` non attendu - [ ] Si tu peux ouvrir console JS (DevTools désactivé en prod, faut hack — sinon skip) → pas d'erreur JS rouge pendant les flows --- **Ordre conseillé** : 0 → 4 → 6 → 7 → 1 → 2 → 3 → 5 → 8 → 9 → 10 → 11 → 12 → 13. Le drag entries (#4) est le plus à risque (régression confirmée hier), le faire tôt + à fond.