fix(web-modules): I-1 пакет вики инвалидирует весь wiki/<проект>, включая status

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-24 15:13:14 +04:00
parent 8abc390f0e
commit 3a6ec3e602
3 changed files with 24 additions and 2 deletions

View File

@ -246,4 +246,25 @@ describe("страница «Вики»", () => {
open("/wiki?project=AWPCA&page=DOC-2");
expect(await screen.findByText(/Eva не ответила: 502/)).toBeInTheDocument();
});
it("I-1: пакет wiki_sync завершён — подзаголовок «обновлено …» и дерево обновляются без перезагрузки", async () => {
let synced = false;
const fresh = { ...STATUS, synced_at: "2026-10-24T12:00:00+00:00" };
const empty = { ...STATUS, synced_at: null, pages: 0 };
mockFetch((method, url) => {
if (url.pathname === "/wiki/status") return json(200, synced ? fresh : empty);
if (url.pathname === "/wiki/sync" && method === "POST") { synced = true; return json(202, { batch_id: 5 }); }
if (url.pathname === "/batches/5") return json(200, { id: 5, status: "done", module: "wiki", commands: [{ type: "wiki_sync", status: "done" }], author: "viewer", title: "t", created_at: "2026-10-24T12:00:00+00:00" });
if (url.pathname === "/wiki/tree") return json(200, TREE);
if (url.pathname === "/agents") return json(200, { agents: [] });
const m = /^\/wiki\/pages\/([^/]+)$/.exec(url.pathname);
if (m) return json(200, page(decodeURIComponent(m[1])));
return json(404, { error: "not_found" });
});
open("/wiki?project=AWPCA");
await screen.findByText("Вики ещё не синхронизирована. Нажмите «Обновить вики».");
await userEvent.click(screen.getByRole("button", { name: "Обновить вики" }));
expect(await screen.findByText(`Корень · 3 страниц · обновлено ${fmtStamp(fresh.synced_at)}`, {}, { timeout: 8000 })).toBeInTheDocument();
expect(await screen.findByRole("treeitem", { name: /Модель данных/ })).toBeInTheDocument();
}, 15000);
});

View File

@ -19,6 +19,7 @@ export const wikiStatus: ModuleStatus = {
},
invalidate: (qc, project) => {
if (project === null) return;
for (const key of ["tree", "page", "search"]) void qc.invalidateQueries({ queryKey: [...wikiQk.project(project), key] });
// всё про проект разом: status (подзаголовок «обновлено …», появление дерева у проекта без синхронизаций), tree, page, search
void qc.invalidateQueries({ queryKey: wikiQk.project(project) });
},
};

View File

@ -398,7 +398,7 @@ describe("ActivityProvider", () => {
await act(() => m.now().wiki.start());
await waitFor(() => expect(m.now().wiki.event).toEqual({ kind: "batch-done", id: 7 }));
const keys = spy.mock.calls.map(([f]) => (f as { queryKey: unknown[] }).queryKey);
expect(keys).toEqual(expect.arrayContaining([["wiki", "AWPCA", "tree"], ["wiki", "AWPCA", "page"], ["wiki", "AWPCA", "search"], ["projects"]]));
expect(keys).toEqual(expect.arrayContaining([["wiki", "AWPCA"], ["projects"]]));
expect(keys.map((k) => k[0])).not.toContain("board");
expect(keys.map((k) => k[0])).not.toContain("issues");
});