51ded06fde
- F5 [stability/regression]: the round-1 F2 fix re-rendered the root with <PostHogProvider><App/></PostHogProvider> after the analytics chunk loaded. In the ChunkLoadErrorBoundary child slot the element TYPE changes App -> PostHogProvider, so React does NOT reconcile in place — it REMOUNTS the whole App: every mount effect runs twice (websocket connect/disconnect, origin tracking, subscriptions) and local state / focus / scroll / in-progress input is lost on cloud cold-load (e.g. typing in /login before analytics loads). And it was USELESS: the app has ZERO consumers of the PostHog React context (no usePostHog / useFeatureFlag* / PostHogFeature), and PostHogProvider given an initialized client is a no-op — all capture goes through the posthog singleton. Fix: initAnalytics now inits the posthog SINGLETON only (no posthog-js/react import, no second render); renderApp() renders <App/> once. First paint stays instant, cloud analytics behavior unchanged, no remount. - F6 [test]: exported isChunkLoadError + chunk-load-error-boundary.test.ts — pins the detector (ChunkLoadError name + the 3 dynamic-import failure messages, case-insensitive → true; null/undefined/ordinary errors → false) so a false-negative that re-blanks the app on a real chunk-404 is caught. Gate: client tsc 0, chunk-load + sanitize tests 14 passed. Entry chunk unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
38 lines
1.4 KiB
TypeScript
38 lines
1.4 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { isChunkLoadError } from "./chunk-load-error-boundary";
|
|
|
|
// The detector decides whether a caught render error is a stale-deploy chunk-404
|
|
// (→ auto-reload to fetch the new manifest) vs a genuine app error (→ generic
|
|
// recovery UI, no reload). A false negative on a real chunk failure re-blanks the
|
|
// app; a false positive would auto-reload on an ordinary error. Pin both sides.
|
|
describe("isChunkLoadError", () => {
|
|
it("detects the ChunkLoadError name", () => {
|
|
expect(isChunkLoadError({ name: "ChunkLoadError", message: "x" })).toBe(true);
|
|
});
|
|
|
|
it.each([
|
|
"Failed to fetch dynamically imported module: https://x/assets/index-abc.js",
|
|
"error loading dynamically imported module",
|
|
"Importing a module script failed.",
|
|
])("detects the dynamic-import failure message %#", (message) => {
|
|
expect(isChunkLoadError({ name: "TypeError", message })).toBe(true);
|
|
});
|
|
|
|
it("is case-insensitive on the message", () => {
|
|
expect(
|
|
isChunkLoadError({ message: "FAILED TO FETCH DYNAMICALLY IMPORTED MODULE" }),
|
|
).toBe(true);
|
|
});
|
|
|
|
it.each([
|
|
null,
|
|
undefined,
|
|
{},
|
|
{ name: "TypeError", message: "Cannot read properties of undefined" },
|
|
{ message: "Network request failed" },
|
|
new Error("some ordinary render error"),
|
|
])("returns false for a non-chunk error %#", (err) => {
|
|
expect(isChunkLoadError(err)).toBe(false);
|
|
});
|
|
});
|