6e59793643
- F1 [blocking]: share-modal.test.tsx + comment-content-view.test.tsx mocked page-query without usePageMetaQuery → 3 tests threw (ShareModal uses it directly, comment-content-view via MentionContent). Added usePageMetaQuery to both mocks (the space-tree mocks were already fixed; these two were missed). - F2: restored refetchOnMount:true on useGetSpacesQuery — ["spaces"] is invalidated only by same-tab mutations (no socket path), so a cross-actor change (an admin adding/removing THIS user from a space) left the list stale until a hard reload. The other refetchOnMount removals (favorites/watched — per-user, same-tab-only gap) stay removed. - F3: corrected the trash-list + recent-changes KEEP comments — both keys ARE invalidated (trash-list by 3 mutations, recent-changes by page CRUD), but invalidateQueries only marks an UNMOUNTED query stale without refetching, so the mount refetch closes the gap. The old "never invalidated" wording was wrong and risked a maintainer deleting a live invalidation as dead code. - F4: tests for the two load-bearing pure paths — invalidate-on-update-page (the undefined-guard: a title-only event keeps the icon; sibling/unrelated subtrees untouched) and breadcrumb-path-equal (equal chain → true; any id/slugId/name/ icon change or length diff → false; both-null → true). Exported breadcrumbPathEqual for the test. Gate: client tsc 0; the 4 affected/new test files 33 passed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
2.0 KiB
TypeScript
54 lines
2.0 KiB
TypeScript
import { describe, it, expect, vi } from "vitest";
|
|
import { SpaceTreeNode } from "@/features/page/tree/types";
|
|
|
|
// breadcrumb.tsx transitively imports @/main.tsx (via usePageMetaQuery ->
|
|
// queryClient), whose module body calls ReactDOM.createRoot on a null root in
|
|
// jsdom. Stub it so importing the pure helper under test doesn't run that
|
|
// (breadcrumbPathEqual does not use queryClient, so a dummy is enough).
|
|
vi.mock("@/main.tsx", () => ({ queryClient: {} }));
|
|
|
|
import { breadcrumbPathEqual } from "./breadcrumb";
|
|
|
|
// breadcrumbPathEqual is the ONLY point where a false-positive equality would
|
|
// leave a stale/incorrect breadcrumb trail on screen: it decides whether the
|
|
// selectAtom hands back the same reference (no re-render) for the ancestor chain.
|
|
// Pin both directions — a too-loose equality goes stale on a rename; a too-tight
|
|
// one loses the perf win.
|
|
const node = (over: Partial<SpaceTreeNode>): SpaceTreeNode =>
|
|
({ id: "a", slugId: "sa", name: "A", icon: "📄", ...over }) as SpaceTreeNode;
|
|
|
|
describe("breadcrumbPathEqual", () => {
|
|
it("both null → true", () => {
|
|
expect(breadcrumbPathEqual(null, null)).toBe(true);
|
|
});
|
|
|
|
it("same reference → true", () => {
|
|
const p = [node({})];
|
|
expect(breadcrumbPathEqual(p, p)).toBe(true);
|
|
});
|
|
|
|
it("equal by id/slugId/name/icon (different arrays) → true", () => {
|
|
expect(breadcrumbPathEqual([node({})], [node({})])).toBe(true);
|
|
});
|
|
|
|
it("one side null → false", () => {
|
|
expect(breadcrumbPathEqual([node({})], null)).toBe(false);
|
|
expect(breadcrumbPathEqual(null, [node({})])).toBe(false);
|
|
});
|
|
|
|
it("different length → false", () => {
|
|
expect(
|
|
breadcrumbPathEqual([node({})], [node({}), node({ id: "b" })]),
|
|
).toBe(false);
|
|
});
|
|
|
|
it.each(["name", "icon", "slugId", "id"] as const)(
|
|
"a changed %s → false (breadcrumb must re-render)",
|
|
(field) => {
|
|
expect(
|
|
breadcrumbPathEqual([node({})], [node({ [field]: "CHANGED" })]),
|
|
).toBe(false);
|
|
},
|
|
);
|
|
});
|