Skip to content

Tooling: introduce a JS unit-test layer for mm web static modules #641

@memtomem

Description

@memtomem

Motivation

`packages/memtomem/src/memtomem/web/static/` ships ~15 hand-rolled JS
modules (`app.js`, `sources-memory-dirs.js`, `settings-*.js`, `i18n.js`,
etc.) carrying real branching logic — view rendering, sub-tab routing,
filter/sort, orphan grouping, locale resolution. Today the only
automated guards on this code are:

  • `test_i18n.py` — locale parity (key set + placeholder shape).
  • `test_no_template_literal_toasts` — i18n-coverage AST-grep guard.

That's it. Every behavioural change to the UI relies on manual
verification or Playwright sessions kicked off ad-hoc. Several recent
PRs (#587, #595, #639) shipped UI-rendering bugs whose minimal
repro would have been a 10-line jsdom assertion. The pattern is
explicit in `feedback_claim_test_parity.md`: behaviour claims need
fail-if-false tests on CI, and the static modules currently violate
that.

What this issue is asking for

Stand up a minimal JS unit-test runner that lives next to the Python
test suite and runs in the same CI lane. Constraints:

  • No build step. The static modules are served verbatim by FastAPI;
    any test layer that requires bundling drifts from production.
  • jsdom or happy-dom. The modules touch `document`, `localStorage`,
    `fetch`. A real browser via Playwright is overkill for unit-level
    rendering assertions.
  • Globals friendly. `app.js` exposes `STATE`, `api()`, `t()`, etc.
    on the global scope; the runner needs to either ingest the module
    as-is (script tag style) or accept a thin shim for those globals.

First three test candidates (concrete, not speculative)

  1. `_renderMemorySourceTree` with orphan rows — assert that a
    `/api/sources` mock returning `memory_dir=null` produces an
    `.source-vendor-orphan` block under the User vendor, and that
    the user sub-tab badge counts the orphans. Direct regression
    guard for fix(web): render orphan-indexed sources in Sources tab #639.
  2. `_renderMemorySourceTree` empty user vendor — assert that
    the empty-state placeholder renders when no dirs and no orphans
    are present, but is replaced by the orphan block when orphans
    exist alone. (Edge case from the fix(web): render orphan-indexed sources in Sources tab #639 review.)
  3. `I18N.applyDOM()` with `data-i18n-placeholder` — assert that
    placeholder attributes refresh on language change without the
    static-key clobber bug from feat(web): namespace cluster — preview echo + honest placeholder + helper text (#581) #595 (`feedback_data_i18n_placeholder_clobber.md`).

Suggested shape

```
packages/memtomem/tests-js/
├── package.json # devDeps: vitest, jsdom (or happy-dom)
├── setup.ts # configure jsdom, expose globals
├── _renderMemorySourceTree.test.ts
├── i18n_apply_dom.test.ts
└── ...
```

CI: add a step before the Python pytest run that does
`npm ci && npm test` inside `tests-js/`. Failure fails the same
build that pytest does.

Out of scope

  • Full DOM E2E (that's Playwright via `reference_playwright_mcp_web_verification.md`)
  • Visual regression
  • Changing the no-build serving model

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions