Commit Graph

5 Commits

Author SHA1 Message Date
simoleo89 b1729d8ddc Vitest: cover dedupeBadges with 6 cases
The badge-deduplication helper was extracted from
InfoStandWidgetUserView in the prior commit; it's a pure (badges[]) =>
badges[] function that keeps slot indices stable by replacing duplicate
codes with empty strings. Coverage:

- empty input
- unique-only passthrough
- duplicate-replaced-with-empty
- falsy entries (null / undefined / '') normalized to ''
- first-occurrence-wins semantics
- order sensitivity (same multiset, different order -> different output)
2026-05-11 21:11:02 +02:00
simoleo89 dbafc97e89 Drop unused login dialogs (dead code) + Vitest coverage on FriendlyTime
Two unrelated cleanups grouped because they're both small and safe.

Dead code removal
- src/components/login/components/RegisterDialog.tsx
- src/components/login/components/ForgotDialog.tsx
- src/components/login/components/shared.ts (only consumed by the two
  dialogs above)

These were the older non-Form-Actions versions of the register and
forgot-password dialogs. LoginView.tsx defines its own inline versions
that use `useActionState` + `useFormStatus` (Phase 3 of the React 19
modernization), which are the ones actually rendered. The legacy
files were already documented as dead in docs/ARCHITECTURE.md.

NewsWindow.tsx and the `components/` directory itself stay — NewsWindow
is still imported by LoginView at the bottom of the login flow.

Vitest coverage on FriendlyTime (+12 cases)
- 65 -> 77 passing tests, 5 -> 6 test files.
- LocalizeText is mocked with a deterministic stub
  (`${ key }|${ amount }`) so each assertion can verify both the bucket
  chosen and the rounded amount. The mock also short-circuits the
  transitive renderer-SDK import, which keeps the test runner
  decoupled from the renderer install state.
- Buckets covered: seconds / minutes / hours / days / months / years
  for both `format` and `shortFormat`. Plus: threshold override,
  key-suffix concatenation, half-hour rounding, the raw
  `getLocalization` helper.

Verification
- yarn test: 6 files / 77 cases / ~2s.
- yarn eslint on the new test file: 0 errors / 0 warnings.
- yarn tsc: clean on touched files.
2026-05-11 17:59:46 +00:00
simoleo89 bb28d252d8 Vitest: +16 cases on ColorUtils, FixedSizeStack, LocalizeFormattedNumber
49 -> 65 passing tests, 4 -> 5 test files.

New file: tests/api-utils-extra.test.ts (16 cases)
- LocalizeFormattedNumber (3): zero/NaN/null guard, sub-1000 stays,
  >=1000 inserts thin-space group separators.
- ColorUtils (8): makeColorHex, makeColorNumberHex (with zero-pad),
  convertFromHex (with/without #), int_to_8BitVals/eight_bitVals_to_int
  roundtrip, int2rgb pure-RGB output, zero-input edge cases.
- FixedSizeStack (4): grow then overwrite oldest (ring-buffer
  semantics), reset clears state, partial-fill behavior of getMax,
  empty-stack returns Number.MIN_VALUE. The "partial-fill" case
  documents a subtle quirk: getMax iterates the whole maxSize window
  including undefined slots, but `undefined > X` is false in JS so
  the inserted value wins — the test pins that behavior.

Note on `usePetPackageWidget` and `useWordQuizWidget`
- They were both considered for a state/actions split this turn but
  their actions mutate internal state (`onClose` resets 5 useState,
  `vote` reads pollId/question/answerSent). A clean split would
  require either passing args to the action or hoisting the state
  to a shared store first. Deferred as follow-up.

Verification
- yarn test: 5 files / 65 cases / ~1.9s.
- yarn eslint on the new test file: 0 errors / 0 warnings.
2026-05-11 17:50:47 +00:00
simoleo89 9d2e4a7324 Expand Vitest coverage on the pure helpers in src/api/{utils,wired}
22 -> 49 passing tests, 2 -> 3 test files.

Targets are functions with zero external dependencies (no renderer SDK,
no network, no DOM). They were picked because:
- they're easy to break by accident in a refactor (rounding edge cases,
  zero-padding rules);
- their behavior is documented by tests once and for all, including the
  surprising bit about LocalizeShortNumber rounding 950..999 into the
  "1K" bucket (kept as an explicit "documented quirk" assertion rather
  than fixed — the current behavior is what the rest of the app
  expects).

New file: tests/api-utils.test.ts (27 cases)
- ConvertSeconds: zero, 1m / 1h / 1d, mixed, single-digit padding (6).
- LocalizeShortNumber: zero/NaN/null guard, sub-1000 stays as-is, K/M/B
  buckets, negative numbers, the 950..999 rounding quirk (7).
- CloneObject: primitives, identity preservation, key fidelity (3).
- GetWiredTimeLocale: even (whole sec), odd (half sec), zero (3).
- WiredDateToString: zero-pad rules, two-digit values (2).
- PrefixUtils.parsePrefixColors: empty inputs, mapping, color reuse (3).
- PrefixUtils.getPrefixFontStyle: default empty id, known preset,
  unknown id (3).

Verification
- yarn test: 3 files / 49 cases / ~1.1s.
- yarn eslint on tests/: 0 errors / 0 warnings.
- All test targets are stable pure functions; the assertions
  double as documentation for callers.
2026-05-11 16:38:12 +00:00
simoleo89 6793de2106 Set up Vitest + 22 smoke tests on pure modules (proposal #6)
Phase 3 of the refactor plan in docs/ARCHITECTURE.md — the foundation
that unblocks every safe refactor below.

Install
- yarn add -D vitest@3 jsdom @testing-library/dom @testing-library/react
  @testing-library/jest-dom

Note: pinned to vitest@3 (not the latest 4.x) because yarn 1's peer
resolution breaks on vitest@4's peer link to vite. With vitest@3 the
existing Vite 8 install resolves cleanly.

Configuration
- vitest.config.mts (new): separate from vite.config.mjs because the
  dev/build config wires up renderer SDK aliases that point at sibling
  working trees (../renderer, ../Nitro_Render_V3). Tests are written
  against pure modules that don't pull in the renderer, so the test
  runner uses a smaller alias set.
- tests/setup.ts (new): imports @testing-library/jest-dom/vitest so
  custom matchers (toBeInTheDocument, etc.) are available without
  per-file imports.
- tsconfig.json: include "tests" so eslint stops complaining about
  unparseable files; also makes the IDE see the test files.
- package.json scripts: "test" (one-shot) and "test:watch".

Tests
- tests/WiredCreatorTools.helpers.test.ts (18 cases): covers the pure
  helpers extracted in 3c68d97 — createEmptyMonitorSnapshot,
  formatMonitorLatestOccurrence (5 time-bucket branches),
  formatMonitorHistoryOccurrence, formatVariableTimestamp,
  formatMonitorSource (4 branches), normalizeMonitorReason. These are
  the most boring-but-easy-to-break functions; locking them down first
  is high value, near-zero risk.
- tests/navigatorRoomCreatorStore.test.ts (4 cases): exercises the
  Zustand store added in the previous commit — initial state, latch
  semantics, 5s auto-reset (with fake timers), and the
  "second beginCreate restarts the lockout" invariant. Validates that
  the store-based replacement of the let-singleton has the same
  observable behavior, plus the new invariant that wasn't possible
  before (timer composition under StrictMode double-mount).

Side effect: two non-test source files were converted to `import type`
to keep the test bundle from accidentally pulling in the renderer SDK
transitively:
- src/components/wired-tools/WiredCreatorTools.types.ts
  (`import type { AvatarInfoFurni }`)
- src/components/wired-tools/WiredCreatorTools.helpers.ts
  (`import type { HotelDateTimeParts, MonitorSnapshot }`)
This is harmless — TypeScript already treated them as type-only —
and improves tree-shaking on build as a side benefit.

Verification
- yarn test -> 2 files, 22 tests passing in ~1.0s.
- yarn eslint on tests/ + the two type-only-import files: 0 errors,
  0 warnings.

Migration path
- Next adoption targets: cover useDoorbellState reducer (data hook
  split), the new useNitroQuery adapter (timeout/cleanup behavior),
  and the smaller pure formatters under src/api/.
- React component tests (via @testing-library/react) deferred until
  there's a small mock layer for the renderer SDK. The
  @testing-library/* deps are already installed so that PR is
  unblocked.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:53 +00:00