import reducer, { addEvent, removeEvent, createCalendar, updateEventLocal, removeTempCal, getCalendarsListAsync, getTempCalendarsListAsync, getCalendarDetailAsync, putEventAsync, getEventAsync, patchCalendarAsync, removeCalendarAsync, moveEventAsync, patchACLCalendarAsync, createCalendarAsync, addSharedCalendarAsync, deleteEventAsync, } from "../../../src/features/Calendars/CalendarSlice"; import * as calAPI from "../../../src/features/Calendars/CalendarApi"; import * as userAPI from "../../../src/features/User/userAPI"; import { configureStore } from "@reduxjs/toolkit"; import { Calendars } from "../../../src/features/Calendars/CalendarTypes"; import { CalendarEvent } from "../../../src/features/Events/EventsTypes"; jest.mock("../../../src/features/Calendars/CalendarApi"); jest.mock("../../../src/features/User/userAPI"); jest.mock("../../../src/features/Events/EventApi"); jest.mock("../../../src/features/Events/eventUtils"); jest.mock("../../../src/utils/apiUtils"); describe("CalendarSlice", () => { const initialState = { list: {}, templist: {}, pending: false, }; beforeEach(() => { jest.clearAllMocks(); }); describe("reducers", () => { it("createCalendar adds new calendar", () => { const action = createCalendar({ name: "Test Cal", color: "#ff0000", description: "desc", }); const state = reducer(initialState, action); const values = Object.values(state.list); expect(values[0].name).toBe("Test Cal"); expect(values[0].color).toBe("#ff0000"); }); it("addEvent adds event to calendar", () => { const calId = "user/cal"; const event = { uid: "event1", title: "My Event" } as any; const stateWithCal = { ...initialState, list: { [calId]: { id: calId, events: {} } as any }, }; const state = reducer( stateWithCal, addEvent({ calendarUid: calId, event }) ); expect(state.list[calId].events["event1"]).toEqual( expect.objectContaining({ uid: "event1" }) ); }); it("removeEvent deletes event", () => { const calId = "user/cal"; const stateWithEvent = { ...initialState, list: { [calId]: { id: calId, events: { e1: { uid: "e1" } }, } as unknown as Calendars, }, }; const state = reducer( stateWithEvent, removeEvent({ calendarUid: calId, eventUid: "e1" }) ); expect(state.list[calId].events).toEqual({}); }); it("updateEventLocal updates an event", () => { const calId = "user/cal"; const stateWithEvent = { ...initialState, list: { [calId]: { id: calId, events: { e1: { uid: "e1", title: "Old" } }, } as unknown as Calendars, }, }; const state = reducer( stateWithEvent, updateEventLocal({ calId, event: { uid: "e1", title: "New" } as any }) ); expect(state.list[calId].events.e1.title).toBe("New"); }); it("removeTempCal deletes temp calendar", () => { const stateWithTemp = { ...initialState, templist: { temp1: { id: "temp1" } as any }, }; const state = reducer(stateWithTemp, removeTempCal("temp1")); expect(state.templist).toEqual({}); }); }); describe("extraReducers (thunks)", () => { const storeFactory = () => configureStore({ reducer: { calendars: reducer }, }); it("getCalendarsListAsync.fulfilled replaces list", async () => { (userAPI.getOpenPaasUser as jest.Mock).mockResolvedValue({ id: "u1" }); (calAPI.getCalendars as jest.Mock).mockResolvedValue({ _embedded: { "dav:calendar": [] }, }); (userAPI.getUserDetails as jest.Mock).mockResolvedValue({ firstname: "Alice", lastname: "Smith", emails: ["a@b.com"], }); const store = storeFactory(); await store.dispatch(getCalendarsListAsync() as any); const state = store.getState().calendars; expect(state.list).toEqual({}); }); it("patchCalendarAsync.fulfilled updates calendar fields", () => { const calId = "c1"; const prev = { ...initialState, list: { c1: { id: calId, events: { e1: { uid: "e1" } } } as any }, }; const patch = { name: "N", desc: "D", color: "#00f" }; const state = reducer( prev, patchCalendarAsync.fulfilled( { calId, calLink: "link", patch }, "req1", { calId, calLink: "link", patch, } ) ); expect(state.list[calId].name).toBe("N"); expect(state.list[calId].description).toBe("D"); expect(state.list[calId].color).toBe("#00f"); }); it("removeCalendarAsync.fulfilled deletes calendar", () => { const prev = { ...initialState, list: { c1: { id: "c1" } as any }, }; const state = reducer( prev, removeCalendarAsync.fulfilled({ calId: "c1" }, "req2", { calId: "c1", calLink: "l", }) ); expect(state.list).toEqual({}); }); it("patchACLCalendarAsync.fulfilled sets visibility", () => { const prev = { ...initialState, list: { c1: { id: "c1", visibility: "public" } as any }, }; const state = reducer( prev, patchACLCalendarAsync.fulfilled( { calId: "c1", calLink: "l", request: "" }, "req3", { calId: "c1", calLink: "l", request: "" } ) ); expect(state.list.c1.visibility).toBe("private"); }); it("createCalendarAsync.fulfilled adds a new calendar", () => { const payload = { userId: "u1", calId: "cal1", color: "#f00", name: "Test", desc: "Desc", owner: "Owner", ownerEmails: ["o@example.com"], }; const state = reducer( initialState, createCalendarAsync.fulfilled(payload, "req4", payload) ); expect(state.list["u1/cal1"].name).toBe("Test"); expect(state.list["u1/cal1"].color).toBe("#f00"); }); it("addSharedCalendarAsync.fulfilled adds shared calendar", () => { const payload = { calId: "c1", color: "#0f0", link: "/calendars/u1/c1.json", name: "Shared", desc: "Shared Desc", owner: "O", ownerEmails: ["o@example.com"], }; const mockCal = { cal: { _links: { self: { href: "/calendars/u1/c1.json" } }, "apple:color": "#0f0", "caldav:description": "Shared Desc", "dav:name": "Shared", }, }; const state = reducer( initialState, addSharedCalendarAsync.fulfilled(payload, "req5", { userId: "u1", calId: "c1", cal: mockCal, }) ); expect(state.list["c1"].name).toBe("Shared"); }); it("deleteEventAsync.fulfilled removes event", () => { const calId = "c1"; const prev = { ...initialState, list: { [calId]: { id: calId, events: { e1: { uid: "e1" } }, } as unknown as Calendars, }, }; const state = reducer( prev, deleteEventAsync.fulfilled({ calId, eventId: "e1" }, "req6", { calId, eventId: "e1", eventURL: "e1.ics", }) ); expect(state.list[calId].events).toEqual({}); }); it("getTempCalendarsListAsync.fulfilled updates templist", () => { const payload = { t1: { id: "t1", name: "Temp", color: "#aaa", events: {}, visibility: "public", owner: "O", ownerEmails: ["o@o.com"], link: "/calendars/t1.json", description: "desc", } as Calendars, }; const state = reducer( initialState, getTempCalendarsListAsync.fulfilled(payload, "req7", { openpaasId: "u1", color: "#aaa", displayName: "test", avatarUrl: "", email: "test@test.com", }) ); expect(state.templist.t1.name).toBe("Temp"); }); it("putEventAsync.fulfilled updates calendar events", () => { const cal = { id: "c1", events: {} } as any; const payload = { calId: "c1", events: [{ uid: "e1" }] as any[] }; const state = reducer( initialState, putEventAsync.fulfilled(payload, "req8", { cal, newEvent: { uid: "e1" } as CalendarEvent, }) ); expect(state.list.c1.events.e1.uid).toBe("e1"); }); it("getEventAsync.fulfilled adds single event", () => { const payload = { calId: "c1", event: { uid: "e1" } as any }; const state = reducer( initialState, getEventAsync.fulfilled(payload, "req9", { uid: "e1" } as any) ); expect(state.list.c1.events.e1.uid).toBe("e1"); }); it("moveEventAsync.fulfilled updates events after move", () => { const cal = { id: "c1", events: {} } as any; const payload = { calId: "c1", events: [{ uid: "e1" }] as any[] }; const state = reducer( initialState, moveEventAsync.fulfilled(payload, "req10", { cal, newEvent: { uid: "e1" } as CalendarEvent, newURL: "e1.ics", }) ); expect(state.list.c1.events.e1.uid).toBe("e1"); }); it("getCalendarDetailAsync.fulfilled adds calendar events", () => { const payload = { calId: "c1", events: [{ uid: "e1" }] as any[] }; const state = reducer( initialState, getCalendarDetailAsync.fulfilled(payload, "req11", { calId: "c1", match: { start: "", end: "" }, }) ); expect(state.list.c1.events.e1.uid).toBe("e1"); }); }); });