molecule-core/canvas
Hongming Wang 679e30538a test(canvas): cover store/classNames helpers (17% → 100%) (#1815)
[Molecule-Platform-Evolvement-Manager]

Continues the #1815 coverage rollup. classNames.ts was at 17%
in the baseline; this PR brings it to full coverage.

16 cases across 3 helpers:

**appendClass (6):**
- undefined / empty existing → just `cls`
- single-class → "a b" join
- DEDUP: existing already contains `cls` → existing unchanged.
  This is the load-bearing reason classNames.ts exists. Pre-helper
  the call sites inlined `${existing} ${cls}` with no dedup, so a
  tick that fired the same class twice produced "a a" and React
  Flow's className-equality diff saw it as a change every render.
- whitespace normalization (multi-space, leading/trailing)

**removeClass (7):**
- undefined / empty existing → ""
- removes named class
- exact match only ("spawn" must NOT match "spawn-fast")
- removing the only class → ""
- no-op when class absent
- whitespace normalization

**scheduleNodeClassRemoval (3):**
- after delayMs: calls set() with className-removed on target node;
  OTHER nodes untouched (the per-id pruning is the contract — pin
  it so a future refactor that maps over all nodes doesn't silently
  strip classes from siblings)
- does NOT fire before the delay elapses (vi.useFakeTimers + advance)
- SSR safety: when window is undefined, function is a no-op
  (neither get nor set fires)

## Note on test environment

Added `// @vitest-environment jsdom` directive — the file's
default `node` environment leaves `window` undefined, which would
make the SSR-guard happy-path test pass for the wrong reason
(every test would short-circuit). With jsdom, the SSR test
explicitly stubs `window` to undefined to exercise the guard.

## Test plan

- [x] All 16 cases pass locally (~1.1s with jsdom env spin-up)
- [x] No SUT changes
- [ ] CI green

## #1815 progress

- [x] Step 1+2: instrumentation (#2147)
- [x] utils.ts + runtime-names.ts (#2148)
- [x] canvas-actions.ts (#2149)
- [x] store/classNames.ts (this PR)
- [ ] store/canvas.ts (73% — biggest absolute gap; bigger surface,
      separate cycle)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 23:50:00 -07:00
..
e2e chore(simplify): trim SHA-rot comments + harden TENANT_HOST scheme/port stripping 2026-04-26 11:44:54 -07:00
public chore: replace brand icon and add HANDOFF.md 2026-04-13 13:03:40 -07:00
src test(canvas): cover store/classNames helpers (17% → 100%) (#1815) 2026-04-26 23:50:00 -07:00
.env.example fix(canvas): close 4 gaps in WS status indicator (env, toast, tests) 2026-04-14 08:26:38 +00:00
.gitignore feat(canvas): SaaS cross-origin — slug header + cookie credentials (Phase F) 2026-04-14 20:08:39 -07:00
components.json chore(canvas): initialize shadcn/ui — components.json + cn utility 2026-04-18 07:57:17 -07:00
Dockerfile chore(canvas): upgrade node:20-alpine → node:22-alpine 2026-04-24 18:54:30 +00:00
next.config.ts fix(canvas,dotenv): review-driven hardening of fit gate + parser parity 2026-04-24 22:23:51 -07:00
package-lock.json fix(canvas): cascade-delete UX — require checkbox before Delete All (#1314) 2026-04-21 07:06:45 +00:00
package.json fix(quickstart): make README cp-paste flow bugless end-to-end (#1871) 2026-04-23 19:53:43 +00:00
playwright.config.ts initial commit — Molecule AI platform 2026-04-13 11:55:37 -07:00
playwright.staging.config.ts feat(e2e): canary + canvas Playwright workflows; delegation mechanics 2026-04-21 04:15:10 -07:00
postcss.config.js initial commit — Molecule AI platform 2026-04-13 11:55:37 -07:00
tailwind.config.ts initial commit — Molecule AI platform 2026-04-13 11:55:37 -07:00
tsconfig.json initial commit — Molecule AI platform 2026-04-13 11:55:37 -07:00
vitest.config.ts initial commit — Molecule AI platform 2026-04-13 11:55:37 -07:00