Skip to content

Commit f53cd19

Browse files
test(webui): pin the toast's documented lifetimes and its unhosted fallback
- Pin the four lifetimes ADR 0030 records, so a number that drifts from the decision is a failing test rather than a quietly different notice - Cover the surface rendered without a queue: the notice is still raised, the render does not fail, and the client is told once that there was nowhere to draw it
1 parent 9b01edf commit f53cd19

1 file changed

Lines changed: 46 additions & 3 deletions

File tree

‎packages/webui/test/unit/webui-toast.test.tsx‎

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,25 @@
1313
// to the browser suite.
1414

1515
import { readFileSync } from "node:fs";
16-
import { createElement } from "react";
16+
import { createElement, type ReactElement } from "react";
1717
import { renderToStaticMarkup } from "react-dom/server";
18-
import { describe, expect, it } from "vitest";
18+
import { describe, expect, it, vi } from "vitest";
1919

2020
import {
2121
createWebuiToastEntry,
2222
reduceWebuiToasts,
2323
webuiToastDurationMs,
2424
webuiToastIdentity,
25+
WEBUI_TOAST_DURATION_MS,
2526
WEBUI_TOAST_MAX_VISIBLE,
2627
type WebuiToastState,
2728
} from "../../src/client/ui/toast-store.js";
28-
import { WebuiToast, WEBUI_TOAST_EXIT_MS } from "../../src/client/ui/toast.js";
29+
import {
30+
useWebuiToast,
31+
WebuiToast,
32+
WEBUI_TOAST_EXIT_MS,
33+
type WebuiToastController,
34+
} from "../../src/client/ui/toast.js";
2935

3036
const clientRoot = new URL("../../src/client/", import.meta.url);
3137
const toastCss = readFileSync(new URL("styles/toast.css", clientRoot), "utf8");
@@ -64,6 +70,18 @@ describe("how long a notice lives", () => {
6470
it("a notice without one is given the time its tone is worth", () => {
6571
expect(webuiToastDurationMs("success", false)).toBe(3000);
6672
expect(webuiToastDurationMs("error", false)).toBe(6000);
73+
expect(webuiToastDurationMs("info", false)).toBe(4000);
74+
expect(webuiToastDurationMs("warning", false)).toBe(5000);
75+
});
76+
77+
it("the family's lifetimes are the ones ADR 0030 records", () => {
78+
// The numbers are the family's, not a caller's, and ADR 0030 states them.
79+
expect(WEBUI_TOAST_DURATION_MS).toEqual({
80+
info: 4000,
81+
success: 3000,
82+
warning: 5000,
83+
error: 6000,
84+
});
6785
});
6886

6987
it("the caller may say otherwise", () => {
@@ -180,4 +198,29 @@ describe("a notice takes no room from the page", () => {
180198
// so the two numbers have to be the same one.
181199
expect(WEBUI_TOAST_EXIT_MS).toBe(120);
182200
});
201+
});
202+
203+
describe("a surface rendered on its own", () => {
204+
it("says its notice anyway, and tells the client once that there was nowhere to draw it", () => {
205+
let controller: WebuiToastController | undefined;
206+
const Probe = (): ReactElement | null => {
207+
controller = useWebuiToast();
208+
return null;
209+
};
210+
// No queue is mounted here, and that must not be the reason the surface
211+
// fails to render: a server render of one surface is a normal thing to do.
212+
const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
213+
try {
214+
expect(() => render(createElement(Probe))).not.toThrow();
215+
expect(controller?.show({ tone: "error", message: "读取失败" })).toBeTruthy();
216+
// The client is told once, not once per notice: the mistake is one, and
217+
// every notice after it would only say the same thing again.
218+
controller?.success("已保存");
219+
controller?.info("提示");
220+
expect(warn).toHaveBeenCalledTimes(1);
221+
expect(String(warn.mock.calls[0]?.[0])).toContain("toast");
222+
} finally {
223+
warn.mockRestore();
224+
}
225+
});
183226
});

0 commit comments

Comments
 (0)