Files
fusion/packages/dashboard/app/components/__tests__/TaskCard-TaskChangesTab-parity.test.tsx
gsxdsm 478f226a54 test: green full-suite CI after main drift (#2229)
## Summary
Restores green **Full Suite (non-blocking)** runs on `main`. Recent main
merges left i18n key parity, schema baseline bookkeeping (0011→0012),
heartbeat tool inventory (FN-8058 `fn_task_logs_read`), and merger
whitespace-classification mocks (execFile `git diff -p -w :2: :3:`) out
of date, so all four test shards failed.

## Root causes observed on main
- **Shard 4 / `@fusion/i18n`**: missing `skipConfirmationDialogs*` +
`reviewBudgetExhausted` in non-en locales; orphan
`awaitingApprovalPlanReviewReplanCap`
- **Shard 3 / `@fusion/core`**: `SCHEMA_BASELINE_VERSION` advanced to
`0012` while tests still equated it with
`OWNER_PROJECT_ID_SPLIT_VERSION` (`0011`) and omitted `0012` from
applied-migration lists
- **Shards 1–2 / `@fusion/engine`**: tool count/snapshot drift for
`fn_task_logs_read`; merger tests still mocked `git diff-tree` for
trivial classification after the execFile `:2:`/`:3:` cutover; mock
provider `updateTask` arity drift

## Changes
- Locale catalogs: add missing keys, drop orphan key
- Schema applier tests: immutable 0011 identity + baseline 0012 lists
- Heartbeat + gating snapshots: include `fn_task_logs_read`
- Merger unit mocks: recognize `git diff -p -w :2:path :3:path`
- Mock provider: accept optional third `updateTask` arg

## Test plan
- [x] `pnpm --filter @fusion/i18n exec vitest run` — 23/23
- [x] `pnpm --filter @fusion/core exec vitest run
src/__tests__/postgres/schema-applier.test.ts` (immutable + automation
upgrade) — pass
- [x] `pnpm --filter @fusion/core exec vitest run` project-identity +
satellite-fusiondir — pass
- [x] Engine suites from failed CI shards (file-scoped,
hermes/openclaw/paperclip/grok, reliability post-finalize/mission,
heartbeat, gating, merger recovery/prompt, mock-provider, etc.) — pass
- [ ] Full Suite workflow green on merge to main

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
  - Improved project data isolation across backend operations.
- Added safer optional toast handling when UI components render outside
the full application shell.
  - Added support for reading task logs during agent heartbeat sessions.

- **Bug Fixes**
- Prevented runtime probes from hanging and avoided scanning large
binary files.
  - Improved path handling for workspaces with missing descendants.
- Corrected task retry state resets and GitHub import/issue-close
behavior.

- **Style**
  - Improved chat, terminal, and settings spacing.
  - Added clearer accessibility labeling for the auto-merge control.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-16 16:01:29 -07:00

133 lines
4.5 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import type { Task } from "@fusion/core";
import { TaskCard } from "../TaskCard";
import { TaskChangesTab } from "../TaskChangesTab";
const useTaskDiffStatsMock = vi.fn();
const fetchTaskDiffMock = vi.fn();
vi.mock("../../hooks/useTaskDiffStats", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../hooks/useTaskDiffStats")>();
return {
...actual,
useTaskDiffStats: (...args: unknown[]) => useTaskDiffStatsMock(...args),
};
});
vi.mock("../../api", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../api")>();
return {
...actual,
fetchTaskDiff: (...args: unknown[]) => fetchTaskDiffMock(...args),
// FNXC:DashboardTests 2026-07-14-21:50: TaskCard loads oversight workflow settings; complete the mock surface so parity tests collect.
fetchWorkflowSettingValues: vi.fn(async () => ({ stored: {}, effective: {}, orphaned: [] })),
};
});
vi.mock("lucide-react", async (importOriginal) => {
const actual = await importOriginal<typeof import("lucide-react")>();
return {
...actual,
Link: () => null,
GitBranch: () => null,
Clock: () => null,
Pencil: () => null,
Layers: () => null,
ChevronDown: () => null,
Folder: () => null,
GitPullRequest: () => null,
CircleDot: () => null,
Target: () => null,
Bot: () => null,
Trash2: () => null,
RotateCw: () => null,
Zap: () => null,
FileCode: () => null,
ChevronRight: () => null,
ChevronLeft: () => null,
AlertCircle: () => null,
GitCommit: () => null,
WrapText: () => null,
Maximize2: () => null,
};
});
vi.mock("../ProviderIcon", () => ({ ProviderIcon: () => null }));
vi.mock("../ChangesDiffModal", () => ({ ChangesDiffModal: () => null }));
vi.mock("../../hooks/useBadgeWebSocket", () => ({
useBadgeWebSocket: () => ({ badgeUpdates: new Map(), isConnected: true, subscribeToBadge: vi.fn(), unsubscribeFromBadge: vi.fn() }),
}));
vi.mock("../../hooks/useBatchBadgeFetch", () => ({ getFreshBatchData: vi.fn(() => null) }));
vi.mock("../../utils/highlightDiff", () => ({ highlightDiff: (diff: string) => diff }));
vi.mock("../../hooks/useConfirm", () => ({ useConfirm: () => ({ confirm: vi.fn() }) }));
/*
FNXC:RuntimeFallbackUI 2026-07-11-00:00:
RuntimeFallbackBadge (commit 0bed997af / FUX-022) calls the shared useToast() hook directly. TaskCard
embeds RuntimeFallbackBadge and this file renders <TaskCard> outside a ToastProvider, so mock the hook
to avoid "useToast must be used within ToastProvider", matching the TaskCard.test.tsx pattern.
*/
vi.mock("../../hooks/useToast", () => ({
useOptionalToast: () => null,
useToast: () => ({
addToast: vi.fn(),
removeToast: vi.fn(),
toasts: [],
}),
}));
function makeTask(overrides: Partial<Task>): Task {
return {
id: "FN-4521",
title: "Parity",
description: "",
column: "in-progress",
dependencies: [],
steps: [],
currentStep: 0,
log: [],
createdAt: "2026-05-14T00:00:00.000Z",
updatedAt: "2026-05-14T00:00:00.000Z",
columnMovedAt: "2026-05-14T00:00:00.000Z",
...overrides,
} as Task;
}
describe("TaskCard/TaskChangesTab files-changed parity", () => {
beforeEach(() => {
useTaskDiffStatsMock.mockReset();
fetchTaskDiffMock.mockReset();
});
it.each([
["in-progress", undefined, undefined],
["in-review", undefined, undefined],
["done", undefined, { commitSha: "abc123" }],
] as const)("keeps counts aligned for %s", async (column, worktree, mergeDetails) => {
const stats = { filesChanged: 3, additions: 5, deletions: 2 };
useTaskDiffStatsMock.mockReturnValue({ stats, loading: false });
fetchTaskDiffMock.mockResolvedValue({
files: [
{ path: "a.ts", status: "modified", additions: 1, deletions: 0, patch: "@@" },
{ path: "b.ts", status: "modified", additions: 1, deletions: 1, patch: "@@" },
{ path: "c.ts", status: "added", additions: 3, deletions: 1, patch: "@@" },
],
stats,
});
const task = makeTask({ column, worktree, mergeDetails });
const { container } = render(
<>
<TaskCard task={task} onOpenDetail={() => {}} addToast={() => {}} />
<TaskChangesTab taskId={task.id} worktree={worktree} column={column} mergeDetails={mergeDetails} />
</>,
);
await waitFor(() => {
expect(screen.getByText(/Files Changed \(3\)/)).toBeTruthy();
});
expect(container.textContent).toContain("3 files changed");
});
});