From 7c3569d6b64f39023a85b2abb239c81e62558466 Mon Sep 17 00:00:00 2001 From: VinuB-Dev Date: Fri, 13 Jun 2025 22:24:58 +0530 Subject: [PATCH] feat: Added integration test cases for home screen. --- __tests__/HomeScreen.test.tsx | 230 ++++++++++++++++++++++++++++++++++ app/(tabs)/home/index.tsx | 12 +- 2 files changed, 235 insertions(+), 7 deletions(-) create mode 100644 __tests__/HomeScreen.test.tsx diff --git a/__tests__/HomeScreen.test.tsx b/__tests__/HomeScreen.test.tsx new file mode 100644 index 00000000..001e94d8 --- /dev/null +++ b/__tests__/HomeScreen.test.tsx @@ -0,0 +1,230 @@ +import HomeScreen from "@/app/(tabs)/home/index"; +import { act, fireEvent, render, waitFor } from "@testing-library/react-native"; +import React from "react"; +import { Alert } from "react-native"; + +const mockOnConfirm = jest.fn(); +jest.mock("react-native-date-picker", () => { + const MockDatePicker = (props: any) => { + if (props.open) { + mockOnConfirm.mockImplementation(props.onConfirm); + } + return null; + }; + return MockDatePicker; +}); + +let mockToken: string | null = null; +let mockLoading = false; +let mockUserStatus: any = {}; + +const mockFetchUserStatus = jest.fn(); +const mockSubmitOOOForm = jest.fn(); +const mockCancelOOO = jest.fn(); + +jest.mock("@/hooks/getUserToken", () => ({ + __esModule: true, + default: () => ({ token: mockToken }), +})); + +jest.mock("@/store/store", () => ({ + useUserStore: () => ({ + loading: mockLoading, + userStatus: mockUserStatus, + fetchUserStatus: mockFetchUserStatus, + submitOOOForm: mockSubmitOOOForm, + cancelOOO: mockCancelOOO, + }), +})); + +jest.spyOn(Alert, "alert"); + +describe("HomeScreen Integration Test", () => { + beforeEach(() => { + jest.clearAllMocks(); + mockOnConfirm.mockClear(); + + mockToken = "mock-token"; + mockLoading = false; + mockUserStatus = { data: { currentStatus: { state: "ACTIVE" } } }; + + mockSubmitOOOForm.mockResolvedValue({ success: true }); + mockCancelOOO.mockResolvedValue({ success: true }); + }); + + it("renders loading state when token is missing", () => { + mockToken = null; + const { getByText } = render(); + expect(getByText("Loading...")).toBeTruthy(); + }); + + it("renders loading state when store loading is true", () => { + mockLoading = true; + const { getByText } = render(); + expect(getByText("Loading...")).toBeTruthy(); + }); + + it("fetches user status on mount if token exists", async () => { + render(); + await waitFor(() => { + expect(mockFetchUserStatus).toHaveBeenCalledWith("mock-token"); + }); + }); + + it("displays UNKNOWN status if userStatus is missing", () => { + mockUserStatus = null; + const { getByText } = render(); + expect(getByText("UNKNOWN")).toBeVisible(); + }); + + describe("StatusUpdateForm Interaction", () => { + it("closes the form when the close icon is pressed", async () => { + const { getByText, getByTestId, queryByText } = render(); + + fireEvent.press(getByText("Submit OOO")); + expect(getByText("Update Status")).toBeVisible(); + fireEvent.press(getByTestId("close-button")); + + expect(queryByText("Update Status")).toBeNull(); + expect(getByText("Submit OOO")).toBeVisible(); + }); + + it("allows a user to fill out and submit the form", async () => { + mockSubmitOOOForm.mockResolvedValueOnce({ success: true }); + const { getByText, findByPlaceholderText, queryByText } = render( + + ); + + fireEvent.press(getByText("Submit OOO")); + + const fromDateButton = getByText("Select From Date"); + fireEvent.press(fromDateButton); + const fromDate = new Date("2024-01-10"); + await act(async () => { + mockOnConfirm(fromDate); + }); + + const toDateButton = getByText("Select To Date"); + fireEvent.press(toDateButton); + const toDate = new Date("2024-01-12"); + await act(async () => { + mockOnConfirm(toDate); + }); + + const descriptionInput = await findByPlaceholderText("Add description"); + fireEvent.changeText(descriptionInput, "Team offsite"); + + await act(async () => { + fireEvent.press(getByText("Submit")); + await mockSubmitOOOForm(); + }); + + expect(queryByText("Update Status")).toBeNull(); + expect(mockSubmitOOOForm).toHaveBeenCalledWith( + expect.objectContaining({ description: "Team offsite" }), + "mock-token" + ); + expect(Alert.alert).toHaveBeenCalledWith( + "Success", + "Your status has been updated to OOO." + ); + }); + + it("shows error alert on failed submission", async () => { + const consoleErrorSpy = jest + .spyOn(console, "error") + .mockImplementation(() => {}); + mockSubmitOOOForm.mockRejectedValueOnce(new Error("Submit failed")); + + const { getByText, findByPlaceholderText } = render(); + + fireEvent.press(getByText("Submit OOO")); + + fireEvent.press(getByText("Select From Date")); + await act(async () => { + mockOnConfirm(new Date("2024-02-01")); + }); + fireEvent.press(getByText("Select To Date")); + await act(async () => { + mockOnConfirm(new Date("2024-02-05")); + }); + const descriptionInput = await findByPlaceholderText("Add description"); + fireEvent.changeText(descriptionInput, "Test"); + + fireEvent.press(getByText("Submit")); + + await waitFor(() => { + expect(Alert.alert).toHaveBeenCalledWith( + "Error", + "Failed to update your status. Please try again." + ); + }); + consoleErrorSpy.mockRestore(); + }); + }); + + describe("Cancel OOO Flow", () => { + beforeEach(() => { + mockUserStatus = { data: { currentStatus: { state: "OOO" } } }; + }); + + it("displays Cancel OOO button when status is OOO", () => { + const { getByText, queryByText } = render(); + expect(getByText("Cancel OOO")).toBeTruthy(); + expect(queryByText("Submit OOO")).toBeNull(); + }); + + it("calls cancelOOO and shows success alert on successful cancellation", async () => { + const { getByText } = render(); + fireEvent.press(getByText("Cancel OOO")); + + await waitFor(() => { + expect(mockCancelOOO).toHaveBeenCalledWith("mock-token"); + expect(Alert.alert).toHaveBeenCalledWith( + "Success", + "Your status has been updated to ACTIVE." + ); + }); + }); + + it("shows loading indicator on Cancel OOO button while cancelling", async () => { + let resolveCancel: (value: unknown) => void; + mockCancelOOO.mockImplementationOnce( + () => new Promise((res) => (resolveCancel = res)) + ); + + const { getByText, getByTestId, queryByText } = render(); + fireEvent.press(getByText("Cancel OOO")); + + await waitFor(() => { + expect(getByTestId("loading-indicator")).toBeTruthy(); + expect(queryByText("Cancel OOO")).toBeNull(); + }); + + await act(async () => { + resolveCancel({ success: true }); + }); + + await waitFor(() => { + expect(getByText("Cancel OOO")).toBeTruthy(); + }); + }); + it("shows an error alert if cancelling OOO fails", async () => { + const consoleErrorSpy = jest + .spyOn(console, "error") + .mockImplementation(() => {}); + mockCancelOOO.mockRejectedValueOnce(new Error("API Error")); + + const { getByText } = render(); + fireEvent.press(getByText("Cancel OOO")); + + await waitFor(() => { + expect(Alert.alert).toHaveBeenCalledWith( + "Error", + "Failed to cancel your OOO status. Please try again." + ); + }); + consoleErrorSpy.mockRestore(); + }); + }); +}); diff --git a/app/(tabs)/home/index.tsx b/app/(tabs)/home/index.tsx index 2ff55989..e45a7eac 100644 --- a/app/(tabs)/home/index.tsx +++ b/app/(tabs)/home/index.tsx @@ -57,12 +57,10 @@ export default function ProfileScreen() { }; try { - const response = await submitOOOForm(formData, token); - if (response) { - Alert.alert("Success", "Your status has been updated to OOO."); - fetchUserStatus(token); // Refresh the user status - setShowForm(false); // Close the form - } + await submitOOOForm(formData, token); + Alert.alert("Success", "Your status has been updated to OOO."); + fetchUserStatus(token); // Refresh the user status + setShowForm(false); // Close the form } catch (error) { Alert.alert("Error", "Failed to update your status. Please try again."); console.error("Error submitting OOO form:", error); @@ -96,7 +94,7 @@ export default function ProfileScreen() { disabled={isLoading} // Disable button while loading > {isLoading ? ( - + ) : ( Cancel OOO )}