|
| 1 | +// @vitest-environment jsdom |
| 2 | +/** |
| 3 | + * NotesPage.groupFilter.test.tsx — 页面「组过滤接线」的行为级判据(批 5 T4 收口 W1 缺口)。 |
| 4 | + * |
| 5 | + * @ai-context: 为什么必须单独钉——`visibleNotes`(组过滤 + SE 封存)的派生在批 5 T4 从 |
| 6 | + * `NotesPage` 搬进了 `utils/colorPalette.visibleNotesOf`,页面只剩「接线」 |
| 7 | + * (把 `groupFilter` 作为实参传给派生函数)。T4 的探针 W1 实测:把那个实参 |
| 8 | + * 换成常量 `null`(真实缺陷:组过滤静默失效)后,既有 |
| 9 | + * `NotesPage.test.tsx` 的 2 条用例**仍 2/2 全绿** ⇒ 接线当时没有判据。 |
| 10 | + * 本文件补上这条判据:用**既有的公共注入面** `focusGroupId` prop |
| 11 | + * (`useNotesDeepLink` → `setGroupFilter`)注入组过滤,断言**渲染出来的 |
| 12 | + * 列表条目**随注入变化;再用反向对照排除「夹具里本来就只有一条」的空真。 |
| 13 | + * @ai-context: 判据强度的来源(写清以防被弱化)——渲染层的组过滤**只有页面这一处**: |
| 14 | + * 实测 `utils/noteSectionModel.buildSections` 的 `groupFilter` 只决定 |
| 15 | + * 「未分组」组头是否常驻(不删条目),`components/NoteListBody` 只把它用于 |
| 16 | + * 组头 `active` 态 ⇒ 列表条目数完全由页面传下去的 `visibleNotes` 决定。 |
| 17 | + * @ai-context: 夹具为什么必须给两个组——`treeMode`(无关键词/无标签/默认排序)为真时 |
| 18 | + * `buildSections` **按组建节**,组不在 `list_note_groups` 里的笔记整节不渲染 |
| 19 | + * ⇒ 只给笔记不给组会让两条判据同时失真(空真)。 |
| 20 | + */ |
| 21 | +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; |
| 22 | +import { cleanup, render, screen, waitFor } from "@testing-library/react"; |
| 23 | +import type { Note, NoteGroup } from "../types"; |
| 24 | + |
| 25 | +const { invokeMock } = vi.hoisted(() => ({ invokeMock: vi.fn() })); |
| 26 | +const { confirmMock } = vi.hoisted(() => ({ confirmMock: vi.fn() })); |
| 27 | +vi.mock("@tauri-apps/api/core", () => ({ invoke: invokeMock })); |
| 28 | +vi.mock("@tauri-apps/plugin-dialog", () => ({ confirm: confirmMock })); |
| 29 | +// EnrichPanel 挂载期 useAiTaskPolling 会 listen 事件——mock 返回解绑函数 |
| 30 | +vi.mock("@tauri-apps/api/event", () => ({ listen: vi.fn(() => Promise.resolve(() => {})) })); |
| 31 | + |
| 32 | +import NotesPage from "./NotesPage"; |
| 33 | + |
| 34 | +const noteIn = (id: number, title: string, groupId: number | null): Note => ({ |
| 35 | + id, title, content: "正文", source: "manual", tags: "[]", properties: null, |
| 36 | + pin: 0, group_id: groupId, created_at: 1, updated_at: 2, |
| 37 | +}); |
| 38 | + |
| 39 | +const mkGroup = (id: number, name: string): NoteGroup => ({ |
| 40 | + id, name, terrain: "container", kind: "topic", domainTag: null, source: "manual", |
| 41 | + seriesKey: null, routeReason: null, routeOverridden: 0, color: null, noteCount: 1, createdAt: 1, updatedAt: 2, |
| 42 | +}); |
| 43 | + |
| 44 | +/** 夹具:两条笔记分属两个组(组表必须同时给出两组,见文件头第三条 @ai-context) */ |
| 45 | +const NOTES = [noteIn(1, "组7笔记", 7), noteIn(2, "组8笔记", 8)]; |
| 46 | +const GROUPS = [mkGroup(7, "第七组"), mkGroup(8, "第八组")]; |
| 47 | + |
| 48 | +describe("NotesPage 组过滤接线(批 5 T4 收口:派生搬走后页面实参确被用上)", () => { |
| 49 | + beforeEach(() => { |
| 50 | + invokeMock.mockReset(); |
| 51 | + confirmMock.mockReset(); |
| 52 | + invokeMock.mockImplementation(async (cmd: string) => { |
| 53 | + switch (cmd) { |
| 54 | + case "list_notes": |
| 55 | + case "search_notes": |
| 56 | + return NOTES; |
| 57 | + case "list_note_groups": |
| 58 | + return GROUPS; |
| 59 | + case "list_fragments": |
| 60 | + return []; |
| 61 | + case "get_feature_flags": |
| 62 | + return { feedCapture: true }; |
| 63 | + default: |
| 64 | + // 子面板(EnrichPanel/VersionPanel/NoteLinkToSystem 等)挂载期查询——空值兜底不抛错 |
| 65 | + if (cmd === "note_versions_list") return []; |
| 66 | + if (cmd === "note_versions_usage") return []; |
| 67 | + if (cmd === "list_knowledge_systems") return []; |
| 68 | + if (cmd === "list_knowledge_links") return []; |
| 69 | + if (cmd === "list_tag_colors") return []; |
| 70 | + if (cmd === "list_links_by_target") return []; |
| 71 | + return null; |
| 72 | + } |
| 73 | + }); |
| 74 | + }); |
| 75 | + |
| 76 | + afterEach(() => cleanup()); |
| 77 | + |
| 78 | + it("注入 focusGroupId=7 ⇒ 列表只渲染第 7 组条目(页面把 groupFilter 真的接进派生)", async () => { |
| 79 | + render(<NotesPage focusGroupId={7} />); |
| 80 | + expect(await screen.findByText("组7笔记")).toBeTruthy(); |
| 81 | + await waitFor(() => expect(screen.queryByText("组8笔记")).toBeNull()); |
| 82 | + }); |
| 83 | + |
| 84 | + it("反向对照:不注入 ⇒ 两条都渲染(排除「夹具本来就只有一条」的空真)", async () => { |
| 85 | + render(<NotesPage />); |
| 86 | + expect(await screen.findByText("组7笔记")).toBeTruthy(); |
| 87 | + expect(await screen.findByText("组8笔记")).toBeTruthy(); |
| 88 | + }); |
| 89 | +}); |
0 commit comments