From 4431d533ce0e12857c71e9c3b7021e153e49d831 Mon Sep 17 00:00:00 2001 From: Ekong Jemimah Date: Thu, 30 Jul 2026 11:38:01 +0100 Subject: [PATCH] test: add third-party script error handling and duplicate prevention coverage - Add onError callback to ThirdPartyScript interface - Add dedup-by-id guard in getThirdPartyScripts() - Wire onError prop in ThirdPartyScripts component - Add handleScriptError() helper - Add tests for error event handling and duplicate script IDs --- src/__tests__/lib/thirdParty.test.ts | 30 ++++++++++++++++++++++++++++ src/components/ThirdPartyScripts.tsx | 3 ++- src/lib/thirdParty.ts | 17 ++++++++++++++-- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/__tests__/lib/thirdParty.test.ts b/src/__tests__/lib/thirdParty.test.ts index 1c2e054f..c432fe75 100644 --- a/src/__tests__/lib/thirdParty.test.ts +++ b/src/__tests__/lib/thirdParty.test.ts @@ -134,6 +134,36 @@ describe("thirdParty script registry", () => { } }); + it("handles script load errors via onError callback", () => { + const onError = jest.fn(); + const mod = loadWithEnv({ + NEXT_PUBLIC_ANALYTICS_PROVIDER: "plausible", + NEXT_PUBLIC_ANALYTICS_DOMAIN: "proofofheart.xyz", + }); + + // Script configs returned by the registry have no onError by default. + for (const s of mod.getThirdPartyScripts()) { + mod.handleScriptError(s); + } + expect(onError).not.toHaveBeenCalled(); + + // When a consumer wires onError, handleScriptError invokes it. + const script = mod.getThirdPartyScripts()[0]; + mod.handleScriptError({ ...script, onError }); + expect(onError).toHaveBeenCalledTimes(1); + }); + + it("does not return duplicate script ids", () => { + const mod = loadWithEnv({ + NEXT_PUBLIC_ANALYTICS_PROVIDER: "plausible", + NEXT_PUBLIC_ANALYTICS_DOMAIN: "proofofheart.xyz", + NEXT_PUBLIC_SUPPORT_WIDGET_SRC: "https://widget.example.com/w.js", + }); + + const ids = mod.getThirdPartyScripts().map((s) => s.id); + expect(new Set(ids).size).toBe(ids.length); + }); + it("derives CSP origins from the configured scripts", () => { const mod = loadWithEnv({ NEXT_PUBLIC_ANALYTICS_PROVIDER: "plausible", diff --git a/src/components/ThirdPartyScripts.tsx b/src/components/ThirdPartyScripts.tsx index 3ed025c9..2b212fd3 100644 --- a/src/components/ThirdPartyScripts.tsx +++ b/src/components/ThirdPartyScripts.tsx @@ -38,12 +38,13 @@ export default function ThirdPartyScripts() { return ( <> - {scripts.map(({ id, src, strategy, attributes }) => ( + {scripts.map(({ id, src, strategy, attributes, onError }) => (