import SettingsPage from "@/features/Settings/SettingsPage"; import settingsReducer from "@/features/Settings/SettingsSlice"; import userReducer, { getOpenPaasUserDataAsync, } from "@/features/User/userSlice"; import { api } from "@/utils/apiUtils"; import { configureStore } from "@reduxjs/toolkit"; import "@testing-library/jest-dom"; import { fireEvent, screen, waitFor } from "@testing-library/react"; import { renderWithProviders } from "../../utils/Renderwithproviders"; jest.mock("@/utils/apiUtils"); const basePreloadedState = { user: { userData: { sub: "test", email: "test@test.com", family_name: "Doe", name: "John", sid: "mockSid", openpaasId: "667037022b752d0026472254", }, organiserData: null, tokens: null, coreConfig: { language: "en", datetime: { timeZone: "UTC" }, }, alarmEmailsEnabled: false, loading: false, error: null, }, settings: { language: "en", timeZone: "UTC", isBrowserDefaultTimeZone: false, view: "settings", businessHours: { start: "8:0", end: "19:0", daysOfWeek: [1, 2, 3, 4, 5], }, workingDays: false, }, }; describe("Working Days and Business Hours Settings", () => { beforeEach(() => { jest.clearAllMocks(); (api.patch as jest.Mock).mockResolvedValue({ status: 204 }); }); describe("Initial rendering", () => { it("renders the working days section with day selector", () => { renderWithProviders(, basePreloadedState); expect( screen.getByText("settings.chooseWorkingDays") ).toBeInTheDocument(); }); it("renders the show only working days toggle", () => { renderWithProviders(, basePreloadedState); expect( screen.getByRole("switch", { name: /settings.showOnlyWorkingDays/i }) ).toBeInTheDocument(); }); it("shows working days toggle as unchecked when workingDays is false", () => { renderWithProviders(, basePreloadedState); const toggle = screen.getByRole("switch", { name: /settings.showOnlyWorkingDays/i, }) as HTMLInputElement; expect(toggle.checked).toBe(false); }); it("shows working days toggle as checked when workingDays is true", () => { renderWithProviders(, { ...basePreloadedState, settings: { ...basePreloadedState.settings, workingDays: true }, }); const toggle = screen.getByRole("switch", { name: /settings.showOnlyWorkingDays/i, }) as HTMLInputElement; expect(toggle.checked).toBe(true); }); it("shows working days toggle as unchecked when workingDays is null", () => { renderWithProviders(, { ...basePreloadedState, settings: { ...basePreloadedState.settings, workingDays: null }, }); const toggle = screen.getByRole("switch", { name: /settings.showOnlyWorkingDays/i, }) as HTMLInputElement; expect(toggle.checked).toBe(false); }); }); describe("workingDays toggle", () => { it("updates workingDays immediately (optimistic update) and calls API", async () => { const { store } = renderWithProviders( , basePreloadedState ); const toggle = screen.getByRole("switch", { name: /settings.showOnlyWorkingDays/i, }); fireEvent.click(toggle); await waitFor(() => { expect(store.getState().settings.workingDays).toBe(true); }); await waitFor(() => { expect(api.patch).toHaveBeenCalledWith( "api/configurations?scope=user", expect.objectContaining({ json: expect.arrayContaining([ expect.objectContaining({ name: "linagora.esn.calendar", configurations: expect.arrayContaining([ expect.objectContaining({ name: "workingDays", value: true, }), ]), }), ]), }) ); }); }); it("rolls back workingDays if API call fails", async () => { (api.patch as jest.Mock).mockRejectedValue(new Error("API Error")); const { store } = renderWithProviders( , basePreloadedState ); const toggle = screen.getByRole("switch", { name: /settings.showOnlyWorkingDays/i, }); fireEvent.click(toggle); await waitFor( () => { expect(store.getState().settings.workingDays).toBe(false); }, { timeout: 3000 } ); await waitFor(() => { expect( screen.getByText("settings.workingDaysUpdateError") ).toBeInTheDocument(); }); }); it("toggles workingDays from true to false", async () => { const { store } = renderWithProviders(, { ...basePreloadedState, settings: { ...basePreloadedState.settings, workingDays: true }, }); const toggle = screen.getByRole("switch", { name: /settings.showOnlyWorkingDays/i, }); fireEvent.click(toggle); await waitFor(() => { expect(store.getState().settings.workingDays).toBe(false); }); await waitFor(() => { expect(api.patch).toHaveBeenCalledWith( "api/configurations?scope=user", expect.objectContaining({ json: expect.arrayContaining([ expect.objectContaining({ name: "linagora.esn.calendar", configurations: expect.arrayContaining([ expect.objectContaining({ name: "workingDays", value: false, }), ]), }), ]), }) ); }); }); }); describe("businessHours day selector", () => { it("updates businessHours in Redux immediately when a day is clicked", async () => { const { store } = renderWithProviders( , basePreloadedState ); // Sunday (0) is not in initial daysOfWeek [1,2,3,4,5], click it to add const sundayButton = screen.getByRole("button", { name: /sunday/i, }); fireEvent.click(sundayButton); await waitFor(() => { expect(store.getState().settings.businessHours?.daysOfWeek).toContain( 0 ); }); }); it("removes a day from businessHours when an already selected day is clicked", async () => { const { store } = renderWithProviders( , basePreloadedState ); // Monday (1) is in initial daysOfWeek, click to remove const mondayButton = screen.getByRole("button", { name: /monday/i }); fireEvent.click(mondayButton); await waitFor(() => { expect( store.getState().settings.businessHours?.daysOfWeek ).not.toContain(1); }); }); it("debounces API call when multiple days are clicked rapidly", async () => { jest.useFakeTimers(); renderWithProviders(, basePreloadedState); const sundayButton = screen.getByRole("button", { name: /sunday/i }); const saturdayButton = screen.getByRole("button", { name: /saturday/i }); fireEvent.click(sundayButton); fireEvent.click(saturdayButton); fireEvent.click(sundayButton); // API should not have been called yet expect(api.patch).not.toHaveBeenCalled(); // Advance timers past debounce threshold jest.runAllTimers(); await waitFor(() => { expect(api.patch).toHaveBeenCalledTimes(1); }); jest.useRealTimers(); }); it("sends businessHours as array to API", async () => { jest.useFakeTimers(); renderWithProviders(, basePreloadedState); const sundayButton = screen.getByRole("button", { name: /sunday/i }); fireEvent.click(sundayButton); jest.runAllTimers(); await waitFor(() => { expect(api.patch).toHaveBeenCalledWith( "api/configurations?scope=user", expect.objectContaining({ json: expect.arrayContaining([ expect.objectContaining({ name: "core", configurations: expect.arrayContaining([ expect.objectContaining({ name: "businessHours", value: expect.arrayContaining([ expect.objectContaining({ start: "8:0", end: "19:0", daysOfWeek: expect.arrayContaining([0]), }), ]), }), ]), }), ]), }) ); }); jest.useRealTimers(); }); it("rolls back businessHours if API call fails", async () => { jest.useFakeTimers(); (api.patch as jest.Mock).mockRejectedValue(new Error("API Error")); const { store } = renderWithProviders( , basePreloadedState ); const sundayButton = screen.getByRole("button", { name: /sunday/i }); fireEvent.click(sundayButton); jest.runAllTimers(); await waitFor( () => { expect( store.getState().settings.businessHours?.daysOfWeek ).not.toContain(0); }, { timeout: 3000 } ); await waitFor(() => { expect( screen.getByText("settings.workingDaysUpdateError") ).toBeInTheDocument(); }); jest.useRealTimers(); }); }); describe("getOpenPaasUserDataAsync integration", () => { it("populates businessHours from core module configurations", async () => { const store = configureStore({ reducer: { user: userReducer, settings: settingsReducer }, preloadedState: { user: { userData: { sub: "test", email: "test@test.com", family_name: "Doe", name: "John", sid: "mockSid", openpaasId: "667037022b752d0026472254", }, organiserData: null, tokens: null, coreConfig: { language: "en", datetime: { timeZone: "UTC" } }, alarmEmailsEnabled: null, loading: false, error: null, }, settings: { language: "en", timeZone: "UTC", isBrowserDefaultTimeZone: false, view: "calendar", businessHours: null, workingDays: null, }, }, }); const mockResponse = { id: "667037022b752d0026472254", firstname: "John", lastname: "Doe", preferredEmail: "test@test.com", configurations: { modules: [ { name: "core", configurations: [ { name: "businessHours", value: [ { start: "8:0", end: "19:0", daysOfWeek: [1, 2, 3, 4, 5] }, ], }, ], }, { name: "linagora.esn.calendar", configurations: [{ name: "workingDays", value: true }], }, ], }, }; await store.dispatch( getOpenPaasUserDataAsync.fulfilled(mockResponse, "", undefined) ); const state = store.getState(); expect(state.settings.businessHours).toEqual({ start: "8:0", end: "19:0", daysOfWeek: [1, 2, 3, 4, 5], }); expect(state.settings.workingDays).toBe(true); }); it("sets businessHours to null when not present in API response", async () => { const store = configureStore({ reducer: { user: userReducer, settings: settingsReducer }, preloadedState: { user: { userData: { sub: "test", email: "test@test.com", family_name: "Doe", name: "John", sid: "mockSid", openpaasId: "667037022b752d0026472254", }, organiserData: null, tokens: null, coreConfig: { language: "en", datetime: { timeZone: "UTC" } }, alarmEmailsEnabled: null, loading: false, error: null, }, settings: { language: "en", timeZone: "UTC", isBrowserDefaultTimeZone: false, view: "calendar", businessHours: null, workingDays: null, }, }, }); const mockResponse = { id: "667037022b752d0026472254", firstname: "John", lastname: "Doe", preferredEmail: "test@test.com", configurations: { modules: [ { name: "core", configurations: [], }, { name: "linagora.esn.calendar", configurations: [{ name: "workingDays", value: null }], }, ], }, }; await store.dispatch( getOpenPaasUserDataAsync.fulfilled(mockResponse, "", undefined) ); const state = store.getState(); expect(state.settings.businessHours).toBeNull(); expect(state.settings.workingDays).toBeNull(); }); }); });