diff --git a/__test__/components/EventErrorHandling.test.tsx b/__test__/components/EventErrorHandling.test.tsx new file mode 100644 index 0000000..f08e8d1 --- /dev/null +++ b/__test__/components/EventErrorHandling.test.tsx @@ -0,0 +1,96 @@ +import { act, fireEvent, screen, waitFor } from "@testing-library/react"; +import { renderWithProviders } from "../utils/Renderwithproviders"; +import CalendarLayout from "../../src/components/Calendar/CalendarLayout"; + +describe("Event Error Handling", () => { + beforeEach(() => { + localStorage.clear(); + jest.clearAllMocks(); + }); + + const today = new Date(); + const start = new Date(today); + start.setHours(10, 0, 0, 0); + const end = new Date(today); + end.setHours(11, 0, 0, 0); + + const erroredState = { + user: { + userData: { + sub: "test", + email: "test@test.com", + sid: "mockSid", + openpaasId: "user1", + }, + tokens: { accessToken: "token" }, + }, + calendars: { + list: { + "user1/cal1": { + name: "Calendar personal", + id: "user1/cal1", + color: { light: "#FF0000", dark: "#000" }, + ownerEmails: ["alice@example.com"], + events: { + event1: { + id: "event1", + calId: "user1/cal1", + uid: "event1", + title: "Test Event", + start: start.toISOString(), + end: start.toISOString(), + partstat: "ACCEPTED", + organizer: { + cn: "Alice", + cal_address: "alice@example.com", + }, + attendee: [ + { + cn: "Alice", + partstat: "ACCEPTED", + rsvp: "TRUE", + role: "REQ-PARTICIPANT", + cutype: "INDIVIDUAL", + cal_address: "alice@example.com", + }, + ], + }, + }, + }, + }, + pending: false, + }, + }; + + it("BUGFIX: does not re-report errors after clearing error snackbar", async () => { + const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(); + + await act(async () => + renderWithProviders(, erroredState) + ); + + await waitFor(() => { + expect(screen.getByText("Test Event")); + expect(screen.getByRole("alert")).toBeInTheDocument(); + }); + const closeButton = screen.queryByRole("button", { name: "common.ok" }); + + if (closeButton) { + const initialWarnCount = consoleWarnSpy.mock.calls.length; + + await act(async () => { + fireEvent.click(closeButton); + }); + + await waitFor(() => { + expect(screen.queryByRole("alert")).not.toBeInTheDocument(); + }); + + const afterCloseWarnCount = consoleWarnSpy.mock.calls.length; + + expect(afterCloseWarnCount).toBe(initialWarnCount); + } + + consoleWarnSpy.mockRestore(); + }); +}); diff --git a/src/components/Error/EventErrorHandler.ts b/src/components/Error/EventErrorHandler.ts index f2f2082..fd47839 100644 --- a/src/components/Error/EventErrorHandler.ts +++ b/src/components/Error/EventErrorHandler.ts @@ -1,5 +1,6 @@ export class EventErrorHandler { private errors = new Map(); + private reportedEvents = new Set(); private onErrorCallback?: (messages: string[]) => void; setErrorCallback(callback: (messages: string[]) => void) { @@ -7,7 +8,8 @@ export class EventErrorHandler { } reportError(eventId: string, message: string) { - if (!this.errors.has(eventId)) { + if (!this.reportedEvents.has(eventId)) { + this.reportedEvents.add(eventId); this.errors.set(eventId, message); console.warn(`[EventErrorHandler] ${eventId}: ${message}`); this.emit(); diff --git a/src/components/Event/EventChip/ErrorEventChip.tsx b/src/components/Event/EventChip/ErrorEventChip.tsx index 58219f7..144ea0a 100644 --- a/src/components/Event/EventChip/ErrorEventChip.tsx +++ b/src/components/Event/EventChip/ErrorEventChip.tsx @@ -1,5 +1,5 @@ -import React from "react"; import { Card, Typography } from "@mui/material"; +import { useRef, useEffect } from "react"; import { EventErrorHandler } from "../../Error/EventErrorHandler"; export function ErrorEventChip({ @@ -11,9 +11,21 @@ export function ErrorEventChip({ errorHandler: EventErrorHandler; error: any; }) { - const message = - error instanceof Error ? error.message : "Unknown error during rendering"; - errorHandler.reportError(event._def.extendedProps.uid || event.id, message); + const hasReported = useRef(false); + + useEffect(() => { + if (!hasReported.current) { + const message = + error instanceof Error + ? error.message + : `${error.class} error during rendering ${event._def.extendedProps.uid || event.id}`; + errorHandler.reportError( + event._def.extendedProps.uid || event.id, + message + ); + hasReported.current = true; + } + }, [event, errorHandler, error]); return (