53 lines
2.7 KiB
TypeScript
53 lines
2.7 KiB
TypeScript
import { cleanup, render, screen } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { afterEach, expect, it, vi } from "vitest";
|
|
import { toast } from "sonner";
|
|
import { dismissMapNotice, showMapNotice } from "./notice-actions";
|
|
import type { MapNoticeTone } from "./notice-types";
|
|
|
|
vi.mock("sonner", () => ({ toast: { custom: vi.fn(), dismiss: vi.fn() } }));
|
|
afterEach(() => { cleanup(); vi.clearAllMocks(); });
|
|
|
|
it.each<MapNoticeTone>(["info", "success", "warning", "error", "loading"])(
|
|
"keeps actionable %s notices until manual dismissal even with an explicit timeout", async (tone) => {
|
|
const onAction = vi.fn();
|
|
const id = showMapNotice({ tone, title: "报告已更新", message: "查看分析结果", duration: 10_000,
|
|
dismissible: false, actionLabel: "查看报告", onAction });
|
|
await Promise.resolve();
|
|
const [content, options] = vi.mocked(toast.custom).mock.calls[0];
|
|
expect(options).toMatchObject({ duration: Infinity, dismissible: true });
|
|
render(content(id));
|
|
const user = userEvent.setup();
|
|
await user.click(screen.getByRole("button", { name: "查看报告" }));
|
|
expect(onAction).toHaveBeenCalledOnce();
|
|
expect(toast.dismiss).not.toHaveBeenCalled();
|
|
expect(options?.cancel).toMatchObject({ label: expect.anything(), onClick: expect.any(Function) });
|
|
}
|
|
);
|
|
|
|
it("preserves transient feedback and the explicit lifecycle of non-actionable notices", async () => {
|
|
showMapNotice({ tone: "success", message: "已保存" });
|
|
await Promise.resolve();
|
|
expect(vi.mocked(toast.custom).mock.lastCall?.[1]).toMatchObject({ duration: 3200 });
|
|
showMapNotice({ tone: "warning", message: "稍后重试", duration: 8000, actionLabel: "缺少回调" });
|
|
await Promise.resolve();
|
|
expect(vi.mocked(toast.custom).mock.lastCall?.[1]).toMatchObject({ duration: 8000 });
|
|
showMapNotice({ tone: "loading", message: "正在加载" });
|
|
await Promise.resolve();
|
|
const [content, options] = vi.mocked(toast.custom).mock.lastCall!;
|
|
expect(options).toMatchObject({ duration: Infinity, dismissible: false });
|
|
render(content("loading"));
|
|
expect(screen.queryByRole("button", { name: "关闭通知" })).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("publishes after effects and cancels notices dismissed before publication", async () => {
|
|
showMapNotice({ id: "source", tone: "loading", message: "加载中" });
|
|
showMapNotice({ id: "source", tone: "success", message: "已完成" });
|
|
const cancelled = showMapNotice({ tone: "info", message: "已取消" });
|
|
dismissMapNotice(cancelled);
|
|
expect(toast.custom).not.toHaveBeenCalled();
|
|
await Promise.resolve();
|
|
expect(toast.custom).toHaveBeenCalledOnce();
|
|
expect(vi.mocked(toast.custom).mock.lastCall?.[1]).toMatchObject({ id: "source", duration: 3200 });
|
|
});
|