diff --git a/web/src/modules/jira/board/useBoardPrefs.test.tsx b/web/src/modules/jira/board/useBoardPrefs.test.tsx index 9d9b71f..dd7f3e7 100644 --- a/web/src/modules/jira/board/useBoardPrefs.test.tsx +++ b/web/src/modules/jira/board/useBoardPrefs.test.tsx @@ -1,5 +1,6 @@ import { act, renderHook } from "@testing-library/react"; import { beforeEach, describe, expect, it } from "vitest"; +import { readPrefs } from "./storage.ts"; import type { BoardModel } from "./types.ts"; import { useBoardPrefs } from "./useBoardPrefs.ts"; @@ -18,10 +19,10 @@ describe("useBoardPrefs: два переключения в одном такт it("два toggle одного флага в одном такте возвращают исходное значение, в хранилище то же", () => { const { result } = renderHook(() => useBoardPrefs(model, "P")); - const before = result.current.snap, stored = JSON.stringify(localStorage); + const before = result.current.snap; act(() => { result.current.toggle("snap"); result.current.toggle("snap"); }); expect(result.current.snap).toBe(before); - expect(JSON.stringify(localStorage)).toBe(stored === "{}" ? JSON.stringify(localStorage) : stored); + expect(readPrefs().snap).toBe(before); }); it("toggleHidden двух значений скрывает оба", () => { diff --git a/web/src/modules/jira/issues/IssueCard.test.tsx b/web/src/modules/jira/issues/IssueCard.test.tsx index 0b90fbe..a342178 100644 --- a/web/src/modules/jira/issues/IssueCard.test.tsx +++ b/web/src/modules/jira/issues/IssueCard.test.tsx @@ -73,7 +73,7 @@ describe("карточка задачи", () => { expect(screen.getByRole("link", { name: "AURORA-9002" }).closest(".issue")).not.toBeNull(); // O-10: цвет темы }); - it("связь на ключ вне зеркала ведёт в Jira, а не в карточку (#103)", async () => { + it("связь на ключ вне зеркала ведёт в Jira, а не в карточку (#42 M-5)", async () => { mockFetch((_m, url) => url.pathname === "/settings" ? json(200, settings) : json(200, issue("AURORA-9001", { links_out: [{ target_key: "AURORA-1999", link_type: "ASUPR_Связанные", summary: null, status: null }], links_in: [], }))); @@ -84,7 +84,7 @@ describe("карточка задачи", () => { expect(link.closest("li")).toHaveTextContent("нет в зеркале"); }); - it("«Прочие поля»: вид работы отдельно от меток (#42 M-5)", async () => { + it("«Прочие поля»: вид работы отдельно от меток (#103)", async () => { mockFetch((_m, url) => url.pathname === "/settings" ? json(200, settings) : json(200, issue("AURORA-9001", { labels: ["AWPCA", "kind-dev"] }))); open("/jira/issues/AURORA-9001"); await screen.findByRole("heading", { level: 2, name: "Тема AURORA-9001" }); diff --git a/web/src/modules/jira/issues/attachments/useAttachmentDownload.test.tsx b/web/src/modules/jira/issues/attachments/useAttachmentDownload.test.tsx index 4abee3d..17a3312 100644 --- a/web/src/modules/jira/issues/attachments/useAttachmentDownload.test.tsx +++ b/web/src/modules/jira/issues/attachments/useAttachmentDownload.test.tsx @@ -117,7 +117,7 @@ describe("useAttachmentDownload", () => { it("failed при опросе — ошибка команды; повторное start сбрасывает ошибку", async () => { let n = 0; - mockFetch((m) => m === "POST" ? json(200, st("fetching")) : json(200, ++n === 1 ? st("failed", { error: "Jira: 404", ok: false }) : st("fetching"))); + mockFetch((m) => m === "POST" ? json(200, st("fetching")) : json(200, ++n === 1 ? st("failed", { error: "Jira: 404" }) : st("fetching"))); const { result } = renderHook(() => useAttachmentDownload("AURORA-1650"), { wrapper }); act(() => result.current.start(att)); await flush(); diff --git a/web/src/shell/activity/ActivityProvider.tsx b/web/src/shell/activity/ActivityProvider.tsx index e668973..eeb0f0b 100644 --- a/web/src/shell/activity/ActivityProvider.tsx +++ b/web/src/shell/activity/ActivityProvider.tsx @@ -124,7 +124,7 @@ function useTracker(m: (typeof TRACKED)[number], { qc, project, pollMs, agents } }, [qc, status, setActiveNow]); useEffect(() => { - // #116: ответ обрабатывается, только если отслеживаемый пакет — тот же объект, что в activeRef (смена проекта обнуляет его синхронно) + // #116: ответ обрабатывается, только если отслеживаемый пакет — тот же объект, что в activeRef (эффект смены проекта объявлен выше и в том же коммите обнуляет activeRef) if (!active || activeRef.current !== active) return; const b = batch.data, e = batch.error; if (b && b.id === active.id && !batch.isError) { diff --git a/web/src/shell/activity/activity.test.tsx b/web/src/shell/activity/activity.test.tsx index d0884b6..0ed37c4 100644 --- a/web/src/shell/activity/activity.test.tsx +++ b/web/src/shell/activity/activity.test.tsx @@ -339,11 +339,11 @@ describe("ActivityProvider", () => { it("#116: ответ опроса прежнего проекта, пришедший в коммите смены проекта, не возвращает событие", async () => { const late = deferred(); - let polls = 0; + let polls = 0, delivered = false; hub({ ...listing((mod, project) => (mod === "wiki" && project === "AWPCA" ? [item(9, "wiki")] : [])), "GET /batches/:id": async () => { - if (++polls > 1) await late.promise; // первый ответ показан сразу, следующий держится до смены проекта + if (++polls > 1) { await late.promise; delivered = true; } // первый ответ показан сразу, следующий держится до смены проекта return batch("running", { type: "wiki_sync" }, { id: 9, module: "wiki" }); }, }); @@ -352,6 +352,7 @@ describe("ActivityProvider", () => { await waitFor(() => expect(polls).toBeGreaterThanOrEqual(2)); // повторный опрос ушёл и висит // смена проекта и ответ опроса — одним коммитом: оба обновления копятся в act и рендерятся вместе на выходе из него await act(async () => { m.setProject("STOCKS_DRRK"); late.resolve(); await sleep(20); }); + expect(delivered).toBe(true); // отложенный ответ действительно ушёл в обработчик await waitFor(() => expect(m.now().wiki.event).toBeNull()); await sleep(50); expect(m.now().wiki.event).toBeNull();