Files
Password-Manager/TEST_SCENARIOS.md
T
r-zakarya 6f4363bde6 fix(batch): move the selection bar into the title row
Selecting an entry inserted the batch bar as a block above the grid, pushing
every row down ~60px — the list jumped under the cursor mid-click. The bar
now renders inside .content-header, to the right of "All items", and the
header reserves its height (min-height) so appearing and disappearing shifts
nothing at all.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 15:59:53 +01:00

9.4 KiB

Scénarios de test — écriture concurrente & fermeture de l'éditeur

Sessions du 2026-07-22. Tous les tests sont manuels (ils portent sur des interactions UI que le harness node:test ne couvre pas). Rebuild obligatoire avant de commencer : delphi-backend\assets\BuildAssets.cmd puis build Delphi.

Convention : PRÉ = état de départ, ACTION, ATTENDU. Un test qui échoue = régression, noter le commit courant.


Les 4 classes de bug

# Classe Cause racine Corrigé par
A Écriture extérieure écrasée au Save L'éditeur garde le payload chargé à l'ouverture et le re-poste entier ; toute écriture faite ailleurs entre-temps est perdue editorAbsorbChange (d146fbe)
B Éditeur fermé sans garde-fou closeSlideOver() appelé sans condition + click-outside non filtré closeEditorForDeleted (3dbce78), whitelist (e1ddc65)
C PUT /entries sans withEncryptedMeta Les colonnes en clair sont droppées côté serveur → le body ne contient rien de stockable et blanchit les *_enc d146fbe
D UI construite sans regarder la vue Le menu kebab est partagé entre vues ; la corbeille n'était pas testée d146fbe

A — Éditeur ouvert vs écriture extérieure

Préparer : 3 entrées A, B, C, dont A dans le dossier Work avec le tag old.

A1 · batch Add tag sur l'entrée éditée

  • PRÉ : A ouverte dans l'éditeur, taper du texte dans NOTE/PASSWORD (ne pas sauver)
  • ACTION : cocher A + B → barre de sélection → Add tag → shared → puis Save dans l'éditeur
  • ATTENDU : le chip shared apparaît immédiatement dans l'éditeur ; après Save, A porte old, shared et la modif tapée. Le bouton Save reste visible (l'entrée était déjà dirty) mais le tag seul ne rend pas dirty.✅

A2 · batch Add tag sur une autre entrée

  • PRÉ : A ouverte + dirty
  • ACTION : cocher B + C seulement → Add tag → x → Save dans l'éditeur
  • ATTENDU : l'éditeur ne bouge pas (aucun chip ajouté), A conserve ses tags, B/C taguées.✅

A3 · batch Move to folder sur l'entrée éditée

  • ACTION : A ouverte + dirty → cocher A → Move to folder… → Perso → Save
  • ATTENDU : le select Folder de l'éditeur bascule sur Perso tout seul ; après Save l'entrée est bien dans Perso.✅

A4 · dossier choisi dans l'éditeur puis move extérieur

  • ACTION : A ouverte, changer Folder → Archive (ne pas sauver) → cocher A → Move to folder… → Perso → Save
  • ATTENDU : Perso gagne (action extérieure = intention la plus récente). Comportement documenté, pas un bug. ✅

A5 · glisser une entrée sur un dossier (moveEntryToFolder)

  • ACTION : A ouverte + dirty → glisser la ligne A sur Perso dans la sidebar → Save
  • ATTENDU : identique à A3.✅

A6 · glisser une multi-sélection sur un dossier (moveEntriesToFolder) ⚠️

  • ACTION : cocher A + B → glisser sur Perso → recharger (F5 impossible : relancer l'app)
  • ATTENDU : dossier changé et nom / username / site / tags intacts.
  • Historique : ce chemin n'appelait pas withEncryptedMeta → il blanchissait les métadonnées chiffrées. Si des entrées ont été déplacées ainsi avant le fix, elles sont peut-être déjà vides — vérifier.

A7 · suppression du dossier de l'entrée éditée

  • ACTION : A (dossier Work) ouverte + dirty → sidebar → supprimer le dossier Work → Save
  • ATTENDU : le select de l'éditeur passe à All ; après Save l'entrée est dans All, le dossier supprimé ne réapparaît pas.

A8 · tags édités des deux côtés

  • ACTION : A ouverte, ajouter le chip mine dans l'éditeur (ne pas sauver) → batch Add tag shared sur A → Save
  • ATTENDU : old, mine, shared — aucun des trois perdu.

A9 · favori / épingle pendant l'édition (non-régression)

  • ACTION : A ouverte + dirty → cliquer ★ et 📌 sur la ligne A → Save
  • ATTENDU : l'entrée reste favorite et épinglée (ces colonnes ne sont pas dans le PUT de l'éditeur).

A10 · icône / pièce jointe pendant l'édition (non-régression)

  • ACTION : A ouverte + dirty → Settings → Fetch missing favicons (ou upload d'icône) → Save
  • ATTENDU : l'icône survit au Save (endpoint dédié, hors payload éditeur).

A11 · sync / import pendant que l'éditeur est ouvert — trou connu

  • ACTION : A ouverte + dirty → lancer Sync now (ou importer un JSON en mode overwrite) qui met A à jour → Save
  • ATTENDU AUJOURD'HUI : le Save de l'éditeur écrase la version venue du sync/import. Non corrigé — voir « Reste à traiter » plus bas.

B — Fermeture de l'éditeur & message de discard

Le message n'apparaît que si confirmOnUnsaved est actif et que l'entrée est dirty. Tester avec askBeforeDelete ON puis OFF (Settings → Security).

B1 · ✕ sur une autre ligne

  • PRÉ : A ouverte + dirty
  • ACTION : cliquer ✕ (Move to trash) sur la ligne B
  • ATTENDU : B part à la corbeille, l'éditeur de A reste ouvert avec les modifs. Vrai dans les deux réglages de askBeforeDelete.

B2 · ✕ sur la ligne éditée, askBeforeDelete = OFF

  • ACTION : A ouverte + dirty → ✕ sur A
  • ATTENDU : modale « Discard unsaved changes? ». Annuler ⇒ rien n'est supprimé et l'éditeur reste ouvert. Confirmer ⇒ A à la corbeille + éditeur fermé.

B3 · ✕ sur la ligne éditée, askBeforeDelete = ON

  • ATTENDU : une seule modale (« Move to trash »). Pas de double prompt.

B4 · batch Delete incluant l'entrée éditée

  • ACTION : A ouverte + dirty → cocher A + B → Delete
  • ATTENDU : une seule modale (« 2 entries will be moved to trash »), puis l'éditeur se ferme.

B5 · batch Delete excluant l'entrée éditée

  • ATTENDU : l'éditeur reste ouvert, modifs intactes.

B6 · corbeille

  • ACTION : ouvrir une entrée de la corbeille via Trash actions, puis : (a) Delete forever dessus, (b) batch purge la contenant, (c) Empty trash
  • ATTENDU : dans les trois cas l'éditeur se ferme s'il portait une entrée purgée, sinon il reste.

B7 · boutons de la barre de sélection

  • ACTION : A ouverte + dirty → cocher B → cliquer successivement Move to folder…, Add tag, Clear
  • ATTENDU : aucun de ces clics ne ferme l'éditeur ni n'affiche de discard.

B8 · bouton Empty trash (barre de titre)

  • ATTENDU : idem, pas de fermeture parasite de l'éditeur.

B9 · chemins de fermeture légitimes (non-régression)

  • Esc, ✕ de l'éditeur, clic sur le fond de la grille, ouverture d'une autre entrée
  • ATTENDU : message de discard à chaque fois si dirty ; annuler garde l'éditeur ouvert sur la bonne entrée.

B10 · verrouillage (comportement assumé)

  • ACTION : A ouverte + dirty → auto-lock ou Lock vault
  • ATTENDU : fermeture sans message, modifs perdues. Volontaire : la sécurité prime.

C — withEncryptedMeta sur tout write /entries

Test générique, à rejouer après toute modif touchant un body POST/PUT /entries : faire l'action, relancer l'app (pour relire depuis SQLite), vérifier que nom / site / username / tags s'affichent encore.

Chemins à couvrir : Save éditeur (neuf + existant), duplicate, import (bulk + overwrite), sync (applyRemoteSnapshot), addTagToEntry, batchAddTag, moveEntryToFolder, moveEntriesToFolder, batchMoveToFolder, migrateMetadataAtRest (au unlock), rotation du master password.

Symptôme du bug : après relance, lignes au nom vide / (no name).


D — Vue corbeille

D1 · menu kebab en table view

  • ACTION : corbeille + vue table → kebab d'une ligne
  • ATTENDU : Restore / Copy… / Delete forever uniquement. Pas de Pin / Favorite / Edit / Duplicate / Move to trash.

D2 · vues cartes et liste

  • ATTENDU : boutons restore + purge (déjà le cas avant), pas de drag possible.

D3 · clic sur une ligne de corbeille

  • ATTENDU : ouvre Trash actions, jamais l'éditeur (une entrée supprimée ne doit pas pouvoir être sauvegardée).

Reste à traiter (constaté pendant l'audit, non corrigé)

  1. Sync / import pendant l'édition (scénario A11). editorAbsorbChange ne couvre que les écritures locales tag/dossier. Un vrai filet serait de mémoriser updated_at à l'ouverture de l'éditeur et, au Save, de comparer avec la valeur courante en mémoire : si elle a bougé, demander « cette entrée a changé ailleurs — écraser ? ». Ça couvre d'un coup sync, import, et tout futur chemin d'écriture.

  2. Modale d'entrée héritée = code mort. openEntryModal n'a aucun appelant ; saveEntry, closeEntryModal, le markup #entryModal et leurs listeners ne sont plus atteignables. Danger : saveEntry fait un PUT partiel (ni kind, ni totp_secret, ni custom_fields, ni template) — exactement le bug que moveEntryToFolder documente comme à ne jamais refaire. À supprimer (JS + HTML) tant qu'il n'est pas recâblé par erreur.

  3. Click-outside : #busyOverlay et .toast ne sont pas dans la liste blanche. Cliquer un toast ou l'overlay d'attente pendant une opération longue ferme l'éditeur. Impact faible, correctif = 2 lignes dans la même liste.