Back to Aionui

Handoff — T4 Frontend unit tests + type + lint (frontend-tester → team-lead + e2e-tester)

docs/backend-migration/handoffs/frontend-tester-assistant-user-data-2026-04-23.md

1.9.26-dev-e9bbc439.3 KB
Original Source

Handoff — T4 Frontend unit tests + type + lint (frontend-tester → team-lead + e2e-tester)

Date: 2026-04-23 Branch: feat/backend-migration-assistant-user-data Owner (outgoing): frontend-tester (Task #7 / T4) Next owners: team-lead (coordinator closure), e2e-tester (Task #8 / T5)

Status

All T4 gates satisfied.

GateResult
Scoped Vitest (plan §4.3)70 / 70 passed
Full Vitest (plan §4.4)4309 / 4479 — 103 pre-existing failures across 37 files; zero new, two fixed
bunx tsc --noEmitclean
bun run lint --quiet1800 warnings + 1 error — identical to T3a baseline; zero new

Scoped suite breakdown

bun run test --run \
  tests/unit/assistantsBridge.test.ts \
  tests/unit/assistantUtils.test.ts \
  tests/unit/assistantHooks.dom.test.ts \
  tests/unit/migrateAssistants.test.ts
FileTestsOwner
tests/unit/assistantsBridge.test.ts15 newfrontend-tester (this task)
tests/unit/assistantUtils.test.ts23 (rewritten)frontend-tester (this task)
tests/unit/assistantHooks.dom.test.ts23 (rewritten)frontend-tester (this task)
tests/unit/migrateAssistants.test.ts9 (landed with T3b @ 26cccd2b9)frontend-dev
Total70

New file: tests/unit/assistantsBridge.test.ts

Stubs globalThis.fetch and exercises each of the 6 ipcBridge.assistants.* entries directly through the real factory in src/common/adapter/ipcBridge.ts. Each method has one happy-path assertion (HTTP verb + path + body shape + response unwrapping) and one error-propagation assertion (4xx/5xx → thrown Error with the "<METHOD> <path> failed (<status>)" message shape produced by httpBridge.ts:57-65).

  • list — GET /api/assistants, envelope unwrap, 500 → throw
  • create — POST /api/assistants, body is full CreateAssistantRequest, 400 → throw
  • update — PUT /api/assistants/:id, id carried in path, body passed through as-is, 404 → throw
  • delete — DELETE /api/assistants/:id, no body, 409 → throw
  • setState — PATCH /api/assistants/:id/state, id stripped from body by adapter mapper, 400 → throw
  • import — POST /api/assistants/import, typed ImportAssistantsResult returned, per-row errors surfaced, 500 → throw
  • Transport decoupling: URL assertions capture only the /api/... path (not host:port), and one dedicated case verifies that window.__backendPort injected by preload overrides the httpBridge.ts fallback. Per docs/development-workflow.md §"仓库关系", the backend listens on a random port selected by findAvailablePort, so binding tests to the fallback 13400 would break in any environment where preload has done its job.

Updated file: tests/unit/assistantHooks.dom.test.ts

Swapped the legacy ConfigStorage mocks (used by the pre-T3a useAssistantList) for direct ipcBridge.assistants.* / ipcBridge.fs.* / ipcBridge.acpConversation.* mocks.

Sections and their counts:

  • useAssistantList (6) — load via assistants.list, sort-order wins, first-assistant-active default, reload preserves active id when still present, falls back when active id disappears, isExtensionAssistant predicate, error-path keeps list empty.
  • useAssistantEditor (11) — create opens drawer + loads skills, save in create mode calls assistants.create + writes rule md + reloads, save in update mode calls assistants.update, validation skip on empty name, delete-click on user opens dialog, delete-click on builtin + extension warns (source-based gating), delete-confirm calls assistants.delete, toggle-enabled on user calls assistants.setState, toggle-enabled on builtin also calls setState (override path — per useAssistantEditor.ts:373-391), toggle-enabled on extension skips backend + warns.
  • useDetectedAgents (3) — unchanged semantically, rewired to ipcBridge.acpConversation.refreshCustomAgents only.
  • useAssistantSkills (3) — kept from prior coverage (this hook still uses ipcBridge.fs.* and was not touched by T3a).

Updated file: tests/unit/assistantUtils.test.ts

Pruned per plan §4.2. Removed suites referenced already-deleted helpers: normalizeExtensionAssistants, isExtensionAssistant, getAssistantSource, hasBuiltinSkills. Kept and extended coverage for surviving helpers:

  • isEmoji (4) — empty / single emoji / plain text / mixed.
  • resolveAvatarImageSrc (7) — missing / mapped / http / data uri / ext:// via resolveExtensionAssetUrl / emoji-not-image / bare word non-image.
  • sortAssistants (4) — empty / ascending sortOrder / non-mutating / stable on ties. (Simplified per plan — the backend already sorts, so the helper is now just a deterministic fallback.)
  • filterAssistants (6) — all / enabled / disabled / builtin+user+extension by source / combined source+query / localized name & description match. Note AssistantListFilter now includes 'user' (replaces legacy 'custom').
  • groupAssistantsByEnabled (2) — standard split + undefined-enabled treated as enabled.

Lint diff

  • Baseline (T3a tip 4a2d73da2): 1800 warnings + 1 error.
  • After T4 (pre-fix): transient 1801 warnings because a useAssistantEditor test helper was defined inside a describe without capturing parent scope, tripping eslint-plugin-unicorn(consistent-function-scoping).
  • After T4 (fix applied — extensionCheck lifted to module scope): 1800 warnings + 1 error. Zero new.

The pre-existing error is unchanged: tests/e2e/helpers/httpBridge.ts:41:9 — owned by the e2e-coverage track, flagged in the T3a handoff as the 1800-warnings + 1-error baseline.

Full-suite regression analysis

bun run test --run with my changes: 37 file failed · 402 passed · 6 skipped and 103 tests failed / 4309 passed.

With my changes git stash-ed (baseline at 26cccd2b9): 39 file failed · 399 passed · 6 skipped and 132 tests failed / 4269 passed.

diff of failing file sets:

  • Only in baseline (fixed by T4): tests/unit/assistantHooks.dom.test.ts, tests/unit/assistantUtils.test.ts
  • Only in after (regressions): none.

The remaining 37 failing files are unrelated to the Assistant user-data migration — they are the pre-existing backend-migration collateral that T3a's handoff already called out (presetAssistantResources.test.ts, configMigration.test.ts, assorted .dom.test.tsx platform tests, etc.). Not in T4 scope.

Non-obvious notes for e2e-tester

  • useAssistantEditor.handleToggleEnabled — builtin assistants go through assistants.setState (the assistant_overrides row path). Only source === 'extension' is hard-blocked in the frontend. If a future e2e test asserts "builtin toggle is disabled in UI" it must exercise the AssistantSettings button rendering, not the hook — the hook itself allows builtins and relies on the backend override mechanism to persist the flip.
  • useAssistantEditor.handleSave always calls writeAssistantRule when editContext.trim() is non-empty, regardless of create/update mode (see useAssistantEditor.ts:283-289 and 310-317). Empty rule body skips the rule write.
  • ipcBridge.assistants.* is currently the sole bridge-level contract for user-data operations. The extensions.getAssistants route is unchanged and still used for extension-contributed assistants — e2e fixtures for "Reject extension edit" scenarios should seed via the extension loader, not via POST /api/assistants/import.
  • migrateAssistantsToBackend is wired in src/index.ts after backendManager.start() succeeds (T3b commit 26cccd2b9). E2E fixtures that seed assistant rows directly through the backend API must set AIONUI_SKIP_ELECTRON_MIGRATION=1 to prevent the main process from re-importing any seed legacy file, or the import will run once but be a no-op on the second boot because the flag migration.electronConfigImported gates it.

Commits pushed by this agent

  • test(assistant): unit coverage for bridge + hooks + migration (b0658d79e)
    • New tests/unit/assistantsBridge.test.ts
    • Updated tests/unit/assistantHooks.dom.test.ts
    • Updated tests/unit/assistantUtils.test.ts
    • This handoff document.
  • test(assistant): decouple bridge tests from fallback port
    • Re-read docs/development-workflow.md after T4 was committed.
    • Replaced 13400-bound URL assertions with /api/... path assertions, and added one window.__backendPort override case against the real httpBridge.getBaseUrl() contract (+1 test).

Recommendation

Mark Task #7 / T4 completed. T5 (e2e, Task #8) is unblocked as soon as Task #4 (backend HTTP integration) also completes.