import "@testing-library/jest-dom"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import React from "react"; import { CopyJobMigrationType } from "../../Enums/CopyJobEnums"; import { CopyJobContextState } from "../../Types/CopyJobTypes"; import { useCopyJobNavigation } from "./useCopyJobNavigation"; jest.mock("../../../../hooks/useSidePanel", () => ({ useSidePanel: { getState: jest.fn(() => ({ closeSidePanel: jest.fn(), })), }, })); jest.mock("../../Actions/CopyJobActions", () => ({ submitCreateCopyJob: jest.fn(), })); jest.mock("../../Context/CopyJobContext", () => ({ useCopyJobContext: jest.fn(), })); jest.mock("./useCopyJobPrerequisitesCache", () => ({ useCopyJobPrerequisitesCache: jest.fn(), })); jest.mock("./useCreateCopyJobScreensList", () => ({ SCREEN_KEYS: { SelectAccount: "SelectAccount", AssignPermissions: "AssignPermissions", SelectSourceAndTargetContainers: "SelectSourceAndTargetContainers", CreateCollection: "CreateCollection", PreviewCopyJob: "PreviewCopyJob", }, useCreateCopyJobScreensList: jest.fn(), })); jest.mock("../../CopyJobUtils", () => ({ getContainerIdentifiers: jest.fn(), isIntraAccountCopy: jest.fn(), })); import { useSidePanel } from "../../../../hooks/useSidePanel"; import { submitCreateCopyJob } from "../../Actions/CopyJobActions"; import { useCopyJobContext } from "../../Context/CopyJobContext"; import { getContainerIdentifiers, isIntraAccountCopy } from "../../CopyJobUtils"; import { useCopyJobPrerequisitesCache } from "./useCopyJobPrerequisitesCache"; import { SCREEN_KEYS, useCreateCopyJobScreensList } from "./useCreateCopyJobScreensList"; const TestComponent: React.FC<{ onHookResult?: (result: ReturnType) => void; }> = ({ onHookResult }) => { const hookResult = useCopyJobNavigation(); React.useEffect(() => { onHookResult?.(hookResult); }, [hookResult, onHookResult]); return (
{hookResult.currentScreen?.key}
{hookResult.isPrimaryDisabled.toString()}
{hookResult.isPreviousDisabled.toString()}
{hookResult.primaryBtnText}
{hookResult.currentScreen?.key === SCREEN_KEYS.SelectSourceAndTargetContainers && ( )}
); }; describe("useCopyJobNavigation", () => { const createMockCopyJobState = (overrides?: Partial): CopyJobContextState => ({ jobName: "test-job", migrationType: CopyJobMigrationType.Offline, source: { subscription: { subscriptionId: "source-sub-id" } as any, account: { id: "source-account-id", name: "Account-1" } as any, databaseId: "source-db", containerId: "source-container", }, target: { subscriptionId: "target-sub-id", account: { id: "target-account-id", name: "Account-2" } as any, databaseId: "target-db", containerId: "target-container", }, ...overrides, }); const createMockScreen = (key: string, validations: any[] = []) => ({ key, component:
{key} Screen
, validations, }); const mockResetCopyJobState = jest.fn(); const mockSetContextError = jest.fn(); const mockCloseSidePanel = jest.fn(); const mockCopyJobState = createMockCopyJobState(); const mockValidationCache = new Map([ ["validation1", true], ["validation2", true], ]); const setupMocks = (screensList: any[] = [], isIntraAccount = false) => { (useCopyJobContext as jest.Mock).mockReturnValue({ copyJobState: mockCopyJobState, resetCopyJobState: mockResetCopyJobState, setContextError: mockSetContextError, }); (useCopyJobPrerequisitesCache as unknown as jest.Mock).mockReturnValue({ validationCache: mockValidationCache, }); (useCreateCopyJobScreensList as jest.Mock).mockReturnValue( screensList.length > 0 ? screensList : [createMockScreen(SCREEN_KEYS.SelectAccount)], ); (useSidePanel.getState as jest.Mock).mockReturnValue({ closeSidePanel: mockCloseSidePanel, }); (getContainerIdentifiers as jest.Mock).mockImplementation((container) => ({ accountId: container.account?.id, databaseId: container.databaseId, containerId: container.containerId, })); (isIntraAccountCopy as jest.Mock).mockReturnValue(isIntraAccount); }; const clickPrimaryButton = () => fireEvent.click(screen.getByTestId("primary-btn")); const clickPreviousButton = () => fireEvent.click(screen.getByTestId("previous-btn")); const expectScreen = (screenKey: string) => { expect(screen.getByTestId("current-screen")).toHaveTextContent(screenKey); }; const expectPrimaryButtonText = (text: string) => { expect(screen.getByTestId("primary-btn-text")).toHaveTextContent(text); }; const expectPrimaryDisabled = (disabled: boolean) => { expect(screen.getByTestId("primary-disabled")).toHaveTextContent(disabled.toString()); }; const navigateToScreen = (screenKey: string, clicks: number) => { for (let i = 0; i < clicks; i++) { clickPrimaryButton(); } expectScreen(screenKey); }; beforeEach(() => { jest.clearAllMocks(); setupMocks(); }); describe("Initial state and navigation", () => { test("should start with SelectAccount screen and disable previous button", () => { render(); expectScreen(SCREEN_KEYS.SelectAccount); expect(screen.getByTestId("previous-disabled")).toHaveTextContent("true"); }); test("should show Next button text by default", () => { render(); expectPrimaryButtonText("Next"); }); test("should navigate through screens and show Create button for CreateCollection", () => { const screens = [ createMockScreen(SCREEN_KEYS.SelectAccount), createMockScreen(SCREEN_KEYS.SelectSourceAndTargetContainers), createMockScreen(SCREEN_KEYS.CreateCollection), ]; setupMocks(screens, true); render(); expectScreen(SCREEN_KEYS.SelectAccount); clickPrimaryButton(); expectScreen(SCREEN_KEYS.SelectSourceAndTargetContainers); expectPrimaryButtonText("Next"); fireEvent.click(screen.getByTestId("add-collection-btn")); expectScreen(SCREEN_KEYS.CreateCollection); expectPrimaryButtonText("Create"); clickPreviousButton(); expectScreen(SCREEN_KEYS.SelectSourceAndTargetContainers); expectPrimaryButtonText("Next"); }); }); describe("Validation logic", () => { test("should disable primary button when validations fail", () => { const invalidScreen = createMockScreen(SCREEN_KEYS.SelectAccount, [ { validate: () => false, message: "Invalid state" }, ]); setupMocks([invalidScreen]); render(); expectPrimaryDisabled(true); }); test("should enable primary button when all validations pass", () => { const validScreen = createMockScreen(SCREEN_KEYS.SelectAccount, [ { validate: () => true, message: "Valid state" }, ]); setupMocks([validScreen]); render(); expectPrimaryDisabled(false); }); test("should prevent navigation when source and target containers are identical", () => { const screens = [ createMockScreen(SCREEN_KEYS.SelectAccount), createMockScreen(SCREEN_KEYS.SelectSourceAndTargetContainers, [ { validate: () => true, message: "Valid containers" }, ]), ]; setupMocks(screens, true); (getContainerIdentifiers as jest.Mock).mockImplementation(() => ({ accountId: "same-account", databaseId: "same-db", containerId: "same-container", })); render(); navigateToScreen(SCREEN_KEYS.SelectSourceAndTargetContainers, 1); clickPrimaryButton(); expectScreen(SCREEN_KEYS.SelectSourceAndTargetContainers); expect(mockSetContextError).toHaveBeenCalledWith( "Source and destination containers cannot be the same. Please select different containers to proceed.", ); }); }); describe("Copy job submission", () => { const setupToPreviewScreen = () => { const screens = [ createMockScreen(SCREEN_KEYS.SelectAccount), createMockScreen(SCREEN_KEYS.SelectSourceAndTargetContainers), createMockScreen(SCREEN_KEYS.PreviewCopyJob), ]; setupMocks(screens, true); render(); navigateToScreen(SCREEN_KEYS.PreviewCopyJob, 2); clickPrimaryButton(); }; test("should handle successful copy job submission", async () => { (submitCreateCopyJob as jest.Mock).mockResolvedValue(undefined); setupToPreviewScreen(); await waitFor(() => { expect(submitCreateCopyJob).toHaveBeenCalledWith(mockCopyJobState, expect.any(Function)); }); }); test("should handle copy job submission error", async () => { const error = new Error("Submission failed"); (submitCreateCopyJob as jest.Mock).mockRejectedValue(error); setupToPreviewScreen(); await waitFor(() => { expect(mockSetContextError).toHaveBeenCalledWith("Submission failed"); }); }); test("should handle unknown error during submission", async () => { (submitCreateCopyJob as jest.Mock).mockRejectedValue("Unknown error"); setupToPreviewScreen(); await waitFor(() => { expect(mockSetContextError).toHaveBeenCalledWith("Failed to create copy job. Please try again later."); }); }); test("should disable buttons during loading", async () => { let resolveSubmission: () => void; const submissionPromise = new Promise((resolve) => { resolveSubmission = resolve; }); (submitCreateCopyJob as jest.Mock).mockReturnValue(submissionPromise); setupToPreviewScreen(); await waitFor(() => { expectPrimaryDisabled(true); }); resolveSubmission!(); await waitFor(() => { expectPrimaryDisabled(false); }); }); }); });