| 1 | import assert from "node:assert/strict"; |
| 2 | import React, { act, StrictMode, useLayoutEffect } from "react"; |
| 3 | import { createRoot } from "react-dom/client"; |
| 4 | import { JSDOM } from "jsdom"; |
| 5 | import { useRecoverableErrorToasts } from "../app-runtime/useAppEffectHosts"; |
| 6 | import { RECOVERABLE_ERROR_EVENT } from "../lib/globalCrashHandlers"; |
| 7 | |
| 8 | const dom = new JSDOM("<div id='root'></div>", { url: "http://localhost", pretendToBeVisual: true }); |
| 9 | Object.assign(globalThis, { window: dom.window, document: dom.window.document, |
| 10 | Node: dom.window.Node, HTMLElement: dom.window.HTMLElement, IS_REACT_ACT_ENVIRONMENT: true }); |
| 11 | const root = createRoot(document.getElementById("root")!); |
| 12 | const delivered: string[] = []; |
| 13 | const fire = (message: string) => window.dispatchEvent(new dom.window.CustomEvent(RECOVERABLE_ERROR_EVENT, { detail: { message } })); |
| 14 | function Fixture({ owner }: { owner: string }) { |
| 15 | useRecoverableErrorToasts((message, level, options) => { |
| 16 | assert.equal(level, "warn"); assert.equal(options?.durationMs, 6000); |
| 17 | delivered.push(`${owner}:${message}`); |
| 18 | }); |
| 19 | useLayoutEffect(() => () => { fire("layout-cleanup"); }, []); |
| 20 | return null; |
| 21 | } |
| 22 | try { |
| 23 | for (let index = 0; index < 32; index++) { |
| 24 | await act(async () => root.render(<StrictMode><Fixture owner={String(index)} /></StrictMode>)); |
| 25 | await act(async () => { fire("backend-error"); }); |
| 26 | assert.equal(delivered.length, index + 1, "one subscription survives StrictMode and callback replacement"); |
| 27 | assert.equal(delivered.at(-1), `${index}:backend-error`, "delivery uses the latest committed toast sink"); |
| 28 | } |
| 29 | await act(async () => root.unmount()); |
| 30 | fire("after-unmount"); |
| 31 | assert.equal(delivered.length, 32, "layout teardown revokes delivery before passive unsubscribe, with no late toast"); |
| 32 | console.log("recoverable toast lifecycle: current sink, single subscription and synchronous teardown passed"); |
| 33 | } finally { dom.window.close(); } |
| 34 |