Files
Password-Manager/js/tests
r-zakarya 63e532d5bb feat(entries): Precedent / Suivant parmi les entrees ouvertes
Fleches dans l'en-tete du panneau et Alt+fleches, comme un navigateur :
un clic par erreur dans une liste filtree ne force plus a refaire la
recherche. Historique en memoire (50), vide au verrouillage ; la navigation
repasse par openSlideOver et sa confirmation de modifications non
enregistrees. Logique pure testee (history.test.js).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 17:39:24 +01:00
..

Frontend unit tests

Regression net for the highest-risk pure/near-pure logic in js/app.js: crypto round-trip, KDF/verifier derivation, CSV import parsing, and the sync-merge / tombstone-resurrection arbitration. Addresses CODE_AUDIT.md §3.2 (aucun test automatisé).

Running

npm test
# or directly:
node --test "js/tests/**/*.test.js"

Zero dependencies — uses the Node built-in test runner (node:test) and webcrypto. Requires Node ≥ 18 (developed on v24). Runs in ~1.7 s.

How it works — harness.js

The frontend is a large browser script with no module exports and one top-level side effect (a DOMContentLoaded listener). It's being split into ordered classic-script files (§3.1); the harness concatenates the app parts in load order (APP_PARTS = app.crypto.js + app.js) into one source — node:vm doesn't share top-level const/let across separate runInContext calls the way the browser shares them across <script> tags. argon2.js is a self-contained IIFE and loads separately first. The concatenated source runs in a node:vm context with browser globals stubbed (crypto, localStorage, document, location, …) so init() never fires, then an export epilogue surfaces the internals on globalThis.__test.

Two gotchas the harness works around, both documented inline:

  • const/let don't attach to the vm global. Top-level function/var declarations become properties of the context global, but const state, const HASH_ALGO_V2, etc. do not — hence the explicit export epilogue.
  • Cross-realm prototypes. Values returned from the sandbox carry the sandbox realm's prototypes, so assert.deepStrictEqual trips on the prototype check. Structural comparisons normalize through JSON first (see eqDeep in csv.test.js).

The merge tests stub only the api() seam (a function declaration → overridable global property) with an in-memory fake server; loadEntries, loadFolders, and encryptImportEntry all run for real against it — so the tests exercise the actual pull→merge path, not a re-implementation.

Suites

File Covers
crypto.test.js deriveKeyAndVerifier (AES key == raw PBKDF2, cross-checked vs Node's pbkdf2Sync), legacy vs -v2 verifier decoupling, encryptPwd/decryptPwd round-trip, IV uniqueness, AEAD tamper/wrong-key → [ERROR]
csv.test.js parseCSV tokenizer (quotes, escaped "", CRLF, trailing field), findColumn header heuristics, parseEntriesFromCSV for Bitwarden/KeePass shapes, note-vs-login classification
merge.test.js applyRemoteSnapshot: add/update/skip (last-write-wins), tombstone delete, resurrection arbitration (both NaN branches), local-tombstone veto, additive folder merge

Notes on encoded behavior

merge.test.js locks in one deliberately-asymmetric behavior: an unparseable local updated_at favours KEEP (resurrection wins), but an unparseable remote deleted_at still applies the delete. In production deleted_at is always server-formatted (parseable), so this only matters for a corrupted remote snapshot. If that arbitration is ever changed, the two unparseable … tests are where to update the expectation.