diff --git a/src/components/insights/sleep/__tests__/chronotype-section.test.tsx b/src/components/insights/sleep/__tests__/chronotype-section.test.tsx index a96866b28..a0cc000da 100644 --- a/src/components/insights/sleep/__tests__/chronotype-section.test.tsx +++ b/src/components/insights/sleep/__tests__/chronotype-section.test.tsx @@ -65,14 +65,18 @@ beforeEach(() => { }); describe("", () => { - it("renders the quiet inline notice on a query error", () => { + it("renders nothing on a query error — the page owns the one notice", () => { + // All three rhythm cards resolve the same query, so a per-card notice + // printed the identical sentence three times. /insights/sleep now renders + // a single QueryErrorCard with a retry in place of the row. The card must + // still not fall through to its skeleton, which would spin forever. rhythmMock.mockReturnValue({ data: undefined, isLoading: false, isError: true, }); const html = render(); - expect(html).toContain('data-slot="chronotype-error"'); + expect(html).toBe(""); expect(html).not.toContain('data-slot="chronotype-loading"'); }); diff --git a/src/components/insights/sleep/__tests__/sleep-rhythm-section.test.tsx b/src/components/insights/sleep/__tests__/sleep-rhythm-section.test.tsx index a5ff44d3c..13aa523b2 100644 --- a/src/components/insights/sleep/__tests__/sleep-rhythm-section.test.tsx +++ b/src/components/insights/sleep/__tests__/sleep-rhythm-section.test.tsx @@ -73,16 +73,17 @@ beforeEach(() => { }); describe("", () => { - it("renders the quiet inline notice on a query error, not an infinite skeleton", () => { + it("renders nothing on a query error, and never an infinite skeleton", () => { + // The shared-read notice moved to /insights/sleep so one failure prints + // one message instead of three. The card must still bail rather than + // fall through to a skeleton that can never resolve. rhythmMock.mockReturnValue({ data: undefined, isLoading: false, isError: true, }); const html = render(); - expect(html).toContain('data-slot="sleep-rhythm-error"'); - expect(html).toContain("Your sleep rhythm"); - // No skeletons while errored. + expect(html).toBe(""); expect(html).not.toContain('data-slot="sleep-rhythm-loading"'); }); diff --git a/src/components/insights/sleep/average-sleep-section.tsx b/src/components/insights/sleep/average-sleep-section.tsx index f708ac0cf..87de5b1c0 100644 --- a/src/components/insights/sleep/average-sleep-section.tsx +++ b/src/components/insights/sleep/average-sleep-section.tsx @@ -20,10 +20,18 @@ import { AverageSleepCard } from "./average-sleep-card"; */ export function AverageSleepSection({ enabled }: { enabled: boolean }) { const { isAuthenticated } = useAuth(); - const { data, isLoading } = useSleepRhythm(isAuthenticated && enabled); + const { data, isLoading, isError } = useSleepRhythm( + isAuthenticated && enabled, + ); if (!enabled) return null; + // The page owns the single error notice for this shared read (all three + // cards resolve the same query, so a per-card notice printed it three + // times). Bail out here rather than falling through to the skeleton below, + // which would otherwise spin forever on a failed read. + if (isError) return null; + if (isLoading || !data) { return (