mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-23 19:54:08 +00:00
Added comprehensive unit test coverage for Container Copy jobs (#2275)
* copy job uts * unit test coverage * lint fix * normalize account dropdown id
This commit is contained in:
@@ -0,0 +1,324 @@
|
||||
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<typeof useCopyJobNavigation>) => void;
|
||||
}> = ({ onHookResult }) => {
|
||||
const hookResult = useCopyJobNavigation();
|
||||
|
||||
React.useEffect(() => {
|
||||
onHookResult?.(hookResult);
|
||||
}, [hookResult, onHookResult]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div data-testid="current-screen">{hookResult.currentScreen?.key}</div>
|
||||
<div data-testid="primary-disabled">{hookResult.isPrimaryDisabled.toString()}</div>
|
||||
<div data-testid="previous-disabled">{hookResult.isPreviousDisabled.toString()}</div>
|
||||
<div data-testid="primary-btn-text">{hookResult.primaryBtnText}</div>
|
||||
<button data-testid="primary-btn" onClick={hookResult.handlePrimary} disabled={hookResult.isPrimaryDisabled}>
|
||||
{hookResult.primaryBtnText}
|
||||
</button>
|
||||
<button data-testid="previous-btn" onClick={hookResult.handlePrevious} disabled={hookResult.isPreviousDisabled}>
|
||||
Previous
|
||||
</button>
|
||||
<button data-testid="cancel-btn" onClick={hookResult.handleCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
{hookResult.currentScreen?.key === SCREEN_KEYS.SelectSourceAndTargetContainers && (
|
||||
<button data-testid="add-collection-btn" onClick={hookResult.showAddCollectionPanel}>
|
||||
Show Collection Panel
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
describe("useCopyJobNavigation", () => {
|
||||
const createMockCopyJobState = (overrides?: Partial<CopyJobContextState>): 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: <div>{key} Screen</div>,
|
||||
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(<TestComponent />);
|
||||
|
||||
expectScreen(SCREEN_KEYS.SelectAccount);
|
||||
expect(screen.getByTestId("previous-disabled")).toHaveTextContent("true");
|
||||
});
|
||||
|
||||
test("should show Next button text by default", () => {
|
||||
render(<TestComponent />);
|
||||
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(<TestComponent />);
|
||||
|
||||
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(<TestComponent />);
|
||||
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(<TestComponent />);
|
||||
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(<TestComponent />);
|
||||
|
||||
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(<TestComponent />);
|
||||
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<void>((resolve) => {
|
||||
resolveSubmission = resolve;
|
||||
});
|
||||
(submitCreateCopyJob as jest.Mock).mockReturnValue(submissionPromise);
|
||||
|
||||
setupToPreviewScreen();
|
||||
|
||||
await waitFor(() => {
|
||||
expectPrimaryDisabled(true);
|
||||
});
|
||||
|
||||
resolveSubmission!();
|
||||
|
||||
await waitFor(() => {
|
||||
expectPrimaryDisabled(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,334 @@
|
||||
import "@testing-library/jest-dom";
|
||||
import { act, render, screen } from "@testing-library/react";
|
||||
import React from "react";
|
||||
import { useCopyJobPrerequisitesCache } from "./useCopyJobPrerequisitesCache";
|
||||
|
||||
describe("useCopyJobPrerequisitesCache", () => {
|
||||
let hookResult: any;
|
||||
|
||||
const TestComponent = ({ onHookUpdate }: { onHookUpdate?: () => void }): JSX.Element => {
|
||||
hookResult = useCopyJobPrerequisitesCache();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (onHookUpdate) {
|
||||
onHookUpdate();
|
||||
}
|
||||
}, [onHookUpdate]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span data-testid="cache-size">{hookResult.validationCache.size}</span>
|
||||
<button
|
||||
data-testid="set-cache-button"
|
||||
onClick={() => {
|
||||
const testCache = new Map<string, boolean>();
|
||||
testCache.set("test-key", true);
|
||||
hookResult.setValidationCache(testCache);
|
||||
}}
|
||||
>
|
||||
Set Cache
|
||||
</button>
|
||||
<button
|
||||
data-testid="clear-cache-button"
|
||||
onClick={() => {
|
||||
hookResult.setValidationCache(new Map<string, boolean>());
|
||||
}}
|
||||
>
|
||||
Clear Cache
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
if (hookResult) {
|
||||
act(() => {
|
||||
hookResult.setValidationCache(new Map<string, boolean>());
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("should initialize with an empty validation cache", () => {
|
||||
render(<TestComponent />);
|
||||
|
||||
expect(hookResult.validationCache).toBeInstanceOf(Map);
|
||||
expect(hookResult.validationCache.size).toBe(0);
|
||||
expect(screen.getByTestId("cache-size")).toHaveTextContent("0");
|
||||
});
|
||||
|
||||
it("should provide a setValidationCache function", () => {
|
||||
render(<TestComponent />);
|
||||
|
||||
expect(typeof hookResult.setValidationCache).toBe("function");
|
||||
});
|
||||
|
||||
it("should update validation cache when setValidationCache is called", () => {
|
||||
render(<TestComponent />);
|
||||
|
||||
const testCache = new Map<string, boolean>();
|
||||
testCache.set("test-key", true);
|
||||
testCache.set("another-key", false);
|
||||
|
||||
act(() => {
|
||||
hookResult.setValidationCache(testCache);
|
||||
});
|
||||
|
||||
expect(hookResult.validationCache).toBe(testCache);
|
||||
expect(hookResult.validationCache.size).toBe(2);
|
||||
expect(hookResult.validationCache.get("test-key")).toBe(true);
|
||||
expect(hookResult.validationCache.get("another-key")).toBe(false);
|
||||
expect(screen.getByTestId("cache-size")).toHaveTextContent("2");
|
||||
});
|
||||
|
||||
it("should replace the entire validation cache when setValidationCache is called", () => {
|
||||
render(<TestComponent />);
|
||||
|
||||
const initialCache = new Map<string, boolean>();
|
||||
initialCache.set("initial-key", true);
|
||||
|
||||
act(() => {
|
||||
hookResult.setValidationCache(initialCache);
|
||||
});
|
||||
|
||||
expect(hookResult.validationCache.get("initial-key")).toBe(true);
|
||||
expect(screen.getByTestId("cache-size")).toHaveTextContent("1");
|
||||
|
||||
const newCache = new Map<string, boolean>();
|
||||
newCache.set("new-key", false);
|
||||
|
||||
act(() => {
|
||||
hookResult.setValidationCache(newCache);
|
||||
});
|
||||
|
||||
expect(hookResult.validationCache.get("initial-key")).toBeUndefined();
|
||||
expect(hookResult.validationCache.get("new-key")).toBe(false);
|
||||
expect(hookResult.validationCache.size).toBe(1);
|
||||
expect(screen.getByTestId("cache-size")).toHaveTextContent("1");
|
||||
});
|
||||
|
||||
it("should handle empty Map updates", () => {
|
||||
render(<TestComponent />);
|
||||
|
||||
const initialCache = new Map<string, boolean>();
|
||||
initialCache.set("test-key", true);
|
||||
|
||||
act(() => {
|
||||
hookResult.setValidationCache(initialCache);
|
||||
});
|
||||
|
||||
expect(hookResult.validationCache.size).toBe(1);
|
||||
expect(screen.getByTestId("cache-size")).toHaveTextContent("1");
|
||||
|
||||
act(() => {
|
||||
screen.getByTestId("clear-cache-button").click();
|
||||
});
|
||||
|
||||
expect(hookResult.validationCache.size).toBe(0);
|
||||
expect(screen.getByTestId("cache-size")).toHaveTextContent("0");
|
||||
});
|
||||
|
||||
it("should maintain state across multiple hook instances (global store behavior)", () => {
|
||||
let firstHookResult: any;
|
||||
let secondHookResult: any;
|
||||
|
||||
const FirstComponent = (): JSX.Element => {
|
||||
firstHookResult = useCopyJobPrerequisitesCache();
|
||||
return <div data-testid="first-component">First</div>;
|
||||
};
|
||||
|
||||
const SecondComponent = (): JSX.Element => {
|
||||
secondHookResult = useCopyJobPrerequisitesCache();
|
||||
return <div data-testid="second-component">Second</div>;
|
||||
};
|
||||
|
||||
render(
|
||||
<div>
|
||||
<FirstComponent />
|
||||
<SecondComponent />
|
||||
</div>,
|
||||
);
|
||||
|
||||
const testCache = new Map<string, boolean>();
|
||||
testCache.set("shared-key", true);
|
||||
|
||||
act(() => {
|
||||
firstHookResult.setValidationCache(testCache);
|
||||
});
|
||||
|
||||
expect(secondHookResult.validationCache.get("shared-key")).toBe(true);
|
||||
expect(secondHookResult.validationCache.size).toBe(1);
|
||||
expect(firstHookResult.validationCache.get("shared-key")).toBe(true);
|
||||
expect(firstHookResult.validationCache.size).toBe(1);
|
||||
});
|
||||
|
||||
it("should allow updates from different hook instances", () => {
|
||||
let firstHookResult: any;
|
||||
let secondHookResult: any;
|
||||
|
||||
const FirstComponent = (): JSX.Element => {
|
||||
firstHookResult = useCopyJobPrerequisitesCache();
|
||||
return (
|
||||
<button
|
||||
data-testid="first-update"
|
||||
onClick={() => {
|
||||
const testCache = new Map<string, boolean>();
|
||||
testCache.set("key-from-first", true);
|
||||
firstHookResult.setValidationCache(testCache);
|
||||
}}
|
||||
>
|
||||
Update from First
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const SecondComponent = (): JSX.Element => {
|
||||
secondHookResult = useCopyJobPrerequisitesCache();
|
||||
return (
|
||||
<button
|
||||
data-testid="second-update"
|
||||
onClick={() => {
|
||||
const testCache = new Map<string, boolean>();
|
||||
testCache.set("key-from-second", false);
|
||||
secondHookResult.setValidationCache(testCache);
|
||||
}}
|
||||
>
|
||||
Update from Second
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
render(
|
||||
<div>
|
||||
<FirstComponent />
|
||||
<SecondComponent />
|
||||
</div>,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
screen.getByTestId("first-update").click();
|
||||
});
|
||||
|
||||
expect(secondHookResult.validationCache.get("key-from-first")).toBe(true);
|
||||
|
||||
act(() => {
|
||||
screen.getByTestId("second-update").click();
|
||||
});
|
||||
|
||||
expect(firstHookResult.validationCache.get("key-from-second")).toBe(false);
|
||||
expect(firstHookResult.validationCache.get("key-from-first")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should handle complex validation scenarios", () => {
|
||||
const ComplexTestComponent = (): JSX.Element => {
|
||||
hookResult = useCopyJobPrerequisitesCache();
|
||||
|
||||
const handleComplexUpdate = () => {
|
||||
const complexCache = new Map<string, boolean>();
|
||||
complexCache.set("database-validation", true);
|
||||
complexCache.set("container-validation", true);
|
||||
complexCache.set("network-validation", false);
|
||||
complexCache.set("authentication-validation", true);
|
||||
complexCache.set("permission-validation", false);
|
||||
hookResult.setValidationCache(complexCache);
|
||||
};
|
||||
|
||||
return (
|
||||
<button data-testid="complex-update" onClick={handleComplexUpdate}>
|
||||
Set Complex Cache
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
render(<ComplexTestComponent />);
|
||||
|
||||
act(() => {
|
||||
screen.getByTestId("complex-update").click();
|
||||
});
|
||||
|
||||
expect(hookResult.validationCache.size).toBe(5);
|
||||
expect(hookResult.validationCache.get("database-validation")).toBe(true);
|
||||
expect(hookResult.validationCache.get("container-validation")).toBe(true);
|
||||
expect(hookResult.validationCache.get("network-validation")).toBe(false);
|
||||
expect(hookResult.validationCache.get("authentication-validation")).toBe(true);
|
||||
expect(hookResult.validationCache.get("permission-validation")).toBe(false);
|
||||
});
|
||||
|
||||
it("should handle edge case keys", () => {
|
||||
const EdgeCaseTestComponent = (): JSX.Element => {
|
||||
hookResult = useCopyJobPrerequisitesCache();
|
||||
|
||||
const handleEdgeCaseUpdate = () => {
|
||||
const edgeCaseCache = new Map<string, boolean>();
|
||||
edgeCaseCache.set("", true);
|
||||
edgeCaseCache.set(" ", false);
|
||||
edgeCaseCache.set("special-chars!@#$%^&*()", true);
|
||||
edgeCaseCache.set("very-long-key-".repeat(10), false);
|
||||
edgeCaseCache.set("unicode-key-🔑", true);
|
||||
hookResult.setValidationCache(edgeCaseCache);
|
||||
};
|
||||
|
||||
return (
|
||||
<button data-testid="edge-case-update" onClick={handleEdgeCaseUpdate}>
|
||||
Set Edge Case Cache
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
render(<EdgeCaseTestComponent />);
|
||||
|
||||
act(() => {
|
||||
screen.getByTestId("edge-case-update").click();
|
||||
});
|
||||
|
||||
expect(hookResult.validationCache.size).toBe(5);
|
||||
expect(hookResult.validationCache.get("")).toBe(true);
|
||||
expect(hookResult.validationCache.get(" ")).toBe(false);
|
||||
expect(hookResult.validationCache.get("special-chars!@#$%^&*()")).toBe(true);
|
||||
expect(hookResult.validationCache.get("very-long-key-".repeat(10))).toBe(false);
|
||||
expect(hookResult.validationCache.get("unicode-key-🔑")).toBe(true);
|
||||
});
|
||||
|
||||
it("should handle setting the same cache reference without errors", () => {
|
||||
let testCache: Map<string, boolean>;
|
||||
|
||||
const SameReferenceTestComponent = (): JSX.Element => {
|
||||
hookResult = useCopyJobPrerequisitesCache();
|
||||
|
||||
const handleFirstUpdate = () => {
|
||||
testCache = new Map<string, boolean>();
|
||||
testCache.set("test-key", true);
|
||||
hookResult.setValidationCache(testCache);
|
||||
};
|
||||
|
||||
const handleSecondUpdate = () => {
|
||||
hookResult.setValidationCache(testCache);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button data-testid="first-update" onClick={handleFirstUpdate}>
|
||||
First Update
|
||||
</button>
|
||||
<button data-testid="second-update" onClick={handleSecondUpdate}>
|
||||
Second Update (Same Reference)
|
||||
</button>
|
||||
<span data-testid="cache-content">{hookResult.validationCache.get("test-key")?.toString()}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render(<SameReferenceTestComponent />);
|
||||
act(() => {
|
||||
screen.getByTestId("first-update").click();
|
||||
});
|
||||
expect(hookResult.validationCache.get("test-key")).toBe(true);
|
||||
expect(screen.getByTestId("cache-content")).toHaveTextContent("true");
|
||||
|
||||
act(() => {
|
||||
screen.getByTestId("second-update").click();
|
||||
});
|
||||
expect(hookResult.validationCache).toBe(testCache);
|
||||
expect(hookResult.validationCache.get("test-key")).toBe(true);
|
||||
expect(screen.getByTestId("cache-content")).toHaveTextContent("true");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,477 @@
|
||||
import "@testing-library/jest-dom";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import React from "react";
|
||||
import Explorer from "../../../Explorer";
|
||||
import CopyJobContextProvider, { useCopyJobContext } from "../../Context/CopyJobContext";
|
||||
import { CopyJobMigrationType } from "../../Enums/CopyJobEnums";
|
||||
import { CopyJobContextState } from "../../Types/CopyJobTypes";
|
||||
import { SCREEN_KEYS, useCreateCopyJobScreensList } from "./useCreateCopyJobScreensList";
|
||||
|
||||
jest.mock("../../Context/CopyJobContext", () => {
|
||||
const actual = jest.requireActual("../../Context/CopyJobContext");
|
||||
return {
|
||||
__esModule: true,
|
||||
...actual,
|
||||
default: actual.default,
|
||||
useCopyJobContext: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock("../Screens/AssignPermissions/AssignPermissions", () => {
|
||||
const MockAssignPermissions = () => {
|
||||
return <div data-testid="assign-permissions">AssignPermissions</div>;
|
||||
};
|
||||
MockAssignPermissions.displayName = "MockAssignPermissions";
|
||||
return MockAssignPermissions;
|
||||
});
|
||||
|
||||
jest.mock("../Screens/CreateContainer/AddCollectionPanelWrapper", () => {
|
||||
const MockAddCollectionPanelWrapper = () => {
|
||||
return <div data-testid="add-collection-panel">AddCollectionPanelWrapper</div>;
|
||||
};
|
||||
MockAddCollectionPanelWrapper.displayName = "MockAddCollectionPanelWrapper";
|
||||
return MockAddCollectionPanelWrapper;
|
||||
});
|
||||
|
||||
jest.mock("../Screens/PreviewCopyJob/PreviewCopyJob", () => {
|
||||
const MockPreviewCopyJob = () => {
|
||||
return <div data-testid="preview-copy-job">PreviewCopyJob</div>;
|
||||
};
|
||||
MockPreviewCopyJob.displayName = "MockPreviewCopyJob";
|
||||
return MockPreviewCopyJob;
|
||||
});
|
||||
|
||||
jest.mock("../Screens/SelectAccount/SelectAccount", () => {
|
||||
const MockSelectAccount = () => {
|
||||
return <div data-testid="select-account">SelectAccount</div>;
|
||||
};
|
||||
MockSelectAccount.displayName = "MockSelectAccount";
|
||||
return MockSelectAccount;
|
||||
});
|
||||
|
||||
jest.mock("../Screens/SelectSourceAndTargetContainers/SelectSourceAndTargetContainers", () => {
|
||||
const MockSelectSourceAndTargetContainers = () => {
|
||||
return <div data-testid="select-source-target">SelectSourceAndTargetContainers</div>;
|
||||
};
|
||||
MockSelectSourceAndTargetContainers.displayName = "MockSelectSourceAndTargetContainers";
|
||||
return MockSelectSourceAndTargetContainers;
|
||||
});
|
||||
|
||||
const TestHookComponent: React.FC<{ goBack: () => void }> = ({ goBack }) => {
|
||||
const screens = useCreateCopyJobScreensList(goBack);
|
||||
|
||||
return (
|
||||
<div data-testid="test-hook-component">
|
||||
{screens.map((screen, index) => (
|
||||
<div key={screen.key} data-testid={`screen-${index}`}>
|
||||
<div data-testid={`screen-key-${index}`}>{screen.key}</div>
|
||||
<div data-testid={`screen-component-${index}`}>{screen.component}</div>
|
||||
<div data-testid={`screen-validations-${index}`}>
|
||||
{JSON.stringify(screen.validations.map((v) => v.message))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
describe("useCreateCopyJobScreensList", () => {
|
||||
const mockExplorer = {} as Explorer;
|
||||
const mockGoBack = jest.fn();
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
(useCopyJobContext as jest.Mock).mockReturnValue({
|
||||
explorer: mockExplorer,
|
||||
});
|
||||
});
|
||||
|
||||
const renderWithContext = (component: React.ReactElement) => {
|
||||
return render(<CopyJobContextProvider explorer={mockExplorer}>{component}</CopyJobContextProvider>);
|
||||
};
|
||||
|
||||
describe("Hook behavior", () => {
|
||||
it("should return screens list with correct keys and components", () => {
|
||||
renderWithContext(<TestHookComponent goBack={mockGoBack} />);
|
||||
|
||||
expect(screen.getByTestId("test-hook-component")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("screen-key-0")).toHaveTextContent(SCREEN_KEYS.SelectAccount);
|
||||
expect(screen.getByTestId("screen-key-1")).toHaveTextContent(SCREEN_KEYS.SelectSourceAndTargetContainers);
|
||||
expect(screen.getByTestId("screen-key-2")).toHaveTextContent(SCREEN_KEYS.CreateCollection);
|
||||
expect(screen.getByTestId("screen-key-3")).toHaveTextContent(SCREEN_KEYS.PreviewCopyJob);
|
||||
expect(screen.getByTestId("screen-key-4")).toHaveTextContent(SCREEN_KEYS.AssignPermissions);
|
||||
|
||||
expect(screen.getByTestId("select-account")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("select-source-target")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("add-collection-panel")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("preview-copy-job")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("assign-permissions")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should return exactly 5 screens in the correct order", () => {
|
||||
renderWithContext(<TestHookComponent goBack={mockGoBack} />);
|
||||
|
||||
const screens = screen.getAllByTestId(/screen-\d+/);
|
||||
expect(screens).toHaveLength(5);
|
||||
});
|
||||
|
||||
it("should memoize results based on explorer dependency", () => {
|
||||
const { rerender } = renderWithContext(<TestHookComponent goBack={mockGoBack} />);
|
||||
const initialScreens = screen.getAllByTestId(/screen-key-\d+/).map((el) => el.textContent);
|
||||
rerender(
|
||||
<CopyJobContextProvider explorer={mockExplorer}>
|
||||
<TestHookComponent goBack={mockGoBack} />
|
||||
</CopyJobContextProvider>,
|
||||
);
|
||||
|
||||
const rerenderScreens = screen.getAllByTestId(/screen-key-\d+/).map((el) => el.textContent);
|
||||
expect(rerenderScreens).toEqual(initialScreens);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Screen validations", () => {
|
||||
describe("SelectAccount screen validation", () => {
|
||||
it("should validate subscription and account presence", () => {
|
||||
renderWithContext(<TestHookComponent goBack={mockGoBack} />);
|
||||
|
||||
const validationMessages = JSON.parse(screen.getByTestId("screen-validations-0").textContent || "[]");
|
||||
expect(validationMessages).toContain("Please select a subscription and account to proceed");
|
||||
});
|
||||
|
||||
it("should pass validation when subscription and account are present", () => {
|
||||
const mockState: CopyJobContextState = {
|
||||
jobName: "",
|
||||
migrationType: CopyJobMigrationType.Offline,
|
||||
source: {
|
||||
subscription: { subscriptionId: "test-sub" } as any,
|
||||
account: { name: "test-account" } as any,
|
||||
databaseId: "",
|
||||
containerId: "",
|
||||
},
|
||||
target: {
|
||||
subscriptionId: "",
|
||||
account: null as any,
|
||||
databaseId: "",
|
||||
containerId: "",
|
||||
},
|
||||
};
|
||||
const ValidationTestComponent = () => {
|
||||
const screens = useCreateCopyJobScreensList(mockGoBack);
|
||||
const selectAccountScreen = screens.find((s) => s.key === SCREEN_KEYS.SelectAccount);
|
||||
const isValid = selectAccountScreen?.validations[0]?.validate(mockState);
|
||||
|
||||
return <div data-testid="validation-result">{isValid ? "valid" : "invalid"}</div>;
|
||||
};
|
||||
|
||||
renderWithContext(<ValidationTestComponent />);
|
||||
expect(screen.getByTestId("validation-result")).toHaveTextContent("valid");
|
||||
});
|
||||
|
||||
it("should fail validation when subscription is missing", () => {
|
||||
const mockState: CopyJobContextState = {
|
||||
jobName: "",
|
||||
migrationType: CopyJobMigrationType.Offline,
|
||||
source: {
|
||||
subscription: null as any,
|
||||
account: { name: "test-account" } as any,
|
||||
databaseId: "",
|
||||
containerId: "",
|
||||
},
|
||||
target: {
|
||||
subscriptionId: "",
|
||||
account: null as any,
|
||||
databaseId: "",
|
||||
containerId: "",
|
||||
},
|
||||
};
|
||||
|
||||
const ValidationTestComponent = () => {
|
||||
const screens = useCreateCopyJobScreensList(mockGoBack);
|
||||
const selectAccountScreen = screens.find((s) => s.key === SCREEN_KEYS.SelectAccount);
|
||||
const isValid = selectAccountScreen?.validations[0]?.validate(mockState);
|
||||
|
||||
return <div data-testid="validation-result">{isValid ? "valid" : "invalid"}</div>;
|
||||
};
|
||||
|
||||
renderWithContext(<ValidationTestComponent />);
|
||||
expect(screen.getByTestId("validation-result")).toHaveTextContent("invalid");
|
||||
});
|
||||
});
|
||||
|
||||
describe("SelectSourceAndTargetContainers screen validation", () => {
|
||||
it("should validate source and target containers", () => {
|
||||
renderWithContext(<TestHookComponent goBack={mockGoBack} />);
|
||||
|
||||
const validationMessages = JSON.parse(screen.getByTestId("screen-validations-1").textContent || "[]");
|
||||
expect(validationMessages).toContain("Please select source and target containers to proceed");
|
||||
});
|
||||
|
||||
it("should pass validation when all required fields are present", () => {
|
||||
const mockState: CopyJobContextState = {
|
||||
jobName: "",
|
||||
migrationType: CopyJobMigrationType.Offline,
|
||||
source: {
|
||||
subscription: null as any,
|
||||
account: null as any,
|
||||
databaseId: "source-db",
|
||||
containerId: "source-container",
|
||||
},
|
||||
target: {
|
||||
subscriptionId: "",
|
||||
account: null as any,
|
||||
databaseId: "target-db",
|
||||
containerId: "target-container",
|
||||
},
|
||||
};
|
||||
|
||||
const ValidationTestComponent = () => {
|
||||
const screens = useCreateCopyJobScreensList(mockGoBack);
|
||||
const screen = screens.find((s) => s.key === SCREEN_KEYS.SelectSourceAndTargetContainers);
|
||||
const isValid = screen?.validations[0]?.validate(mockState);
|
||||
|
||||
return <div data-testid="validation-result">{isValid ? "valid" : "invalid"}</div>;
|
||||
};
|
||||
|
||||
renderWithContext(<ValidationTestComponent />);
|
||||
expect(screen.getByTestId("validation-result")).toHaveTextContent("valid");
|
||||
});
|
||||
|
||||
it("should fail validation when source database is missing", () => {
|
||||
const mockState: CopyJobContextState = {
|
||||
jobName: "",
|
||||
migrationType: CopyJobMigrationType.Offline,
|
||||
source: {
|
||||
subscription: null as any,
|
||||
account: null as any,
|
||||
databaseId: "",
|
||||
containerId: "source-container",
|
||||
},
|
||||
target: {
|
||||
subscriptionId: "",
|
||||
account: null as any,
|
||||
databaseId: "target-db",
|
||||
containerId: "target-container",
|
||||
},
|
||||
};
|
||||
|
||||
const ValidationTestComponent = () => {
|
||||
const screens = useCreateCopyJobScreensList(mockGoBack);
|
||||
const screen = screens.find((s) => s.key === SCREEN_KEYS.SelectSourceAndTargetContainers);
|
||||
const isValid = screen?.validations[0]?.validate(mockState);
|
||||
|
||||
return <div data-testid="validation-result">{isValid ? "valid" : "invalid"}</div>;
|
||||
};
|
||||
|
||||
renderWithContext(<ValidationTestComponent />);
|
||||
expect(screen.getByTestId("validation-result")).toHaveTextContent("invalid");
|
||||
});
|
||||
});
|
||||
|
||||
describe("CreateCollection screen", () => {
|
||||
it("should have no validations", () => {
|
||||
renderWithContext(<TestHookComponent goBack={mockGoBack} />);
|
||||
|
||||
const validationMessages = JSON.parse(screen.getByTestId("screen-validations-2").textContent || "[]");
|
||||
expect(validationMessages).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("PreviewCopyJob screen validation", () => {
|
||||
it("should validate job name format", () => {
|
||||
renderWithContext(<TestHookComponent goBack={mockGoBack} />);
|
||||
|
||||
const validationMessages = JSON.parse(screen.getByTestId("screen-validations-3").textContent || "[]");
|
||||
expect(validationMessages).toContain("Please enter a job name to proceed");
|
||||
});
|
||||
|
||||
it("should pass validation with valid job name", () => {
|
||||
const mockState: CopyJobContextState = {
|
||||
jobName: "valid-job-name_123",
|
||||
migrationType: CopyJobMigrationType.Offline,
|
||||
source: {
|
||||
subscription: null as any,
|
||||
account: null as any,
|
||||
databaseId: "",
|
||||
containerId: "",
|
||||
},
|
||||
target: {
|
||||
subscriptionId: "",
|
||||
account: null as any,
|
||||
databaseId: "",
|
||||
containerId: "",
|
||||
},
|
||||
};
|
||||
|
||||
const ValidationTestComponent = () => {
|
||||
const screens = useCreateCopyJobScreensList(mockGoBack);
|
||||
const screen = screens.find((s) => s.key === SCREEN_KEYS.PreviewCopyJob);
|
||||
const isValid = screen?.validations[0]?.validate(mockState);
|
||||
|
||||
return <div data-testid="validation-result">{isValid ? "valid" : "invalid"}</div>;
|
||||
};
|
||||
|
||||
renderWithContext(<ValidationTestComponent />);
|
||||
expect(screen.getByTestId("validation-result")).toHaveTextContent("valid");
|
||||
});
|
||||
|
||||
it("should fail validation with invalid job name characters", () => {
|
||||
const mockState: CopyJobContextState = {
|
||||
jobName: "invalid job name with spaces!",
|
||||
migrationType: CopyJobMigrationType.Offline,
|
||||
source: {
|
||||
subscription: null as any,
|
||||
account: null as any,
|
||||
databaseId: "",
|
||||
containerId: "",
|
||||
},
|
||||
target: {
|
||||
subscriptionId: "",
|
||||
account: null as any,
|
||||
databaseId: "",
|
||||
containerId: "",
|
||||
},
|
||||
};
|
||||
|
||||
const ValidationTestComponent = () => {
|
||||
const screens = useCreateCopyJobScreensList(mockGoBack);
|
||||
const screen = screens.find((s) => s.key === SCREEN_KEYS.PreviewCopyJob);
|
||||
const isValid = screen?.validations[0]?.validate(mockState);
|
||||
|
||||
return <div data-testid="validation-result">{isValid ? "valid" : "invalid"}</div>;
|
||||
};
|
||||
|
||||
renderWithContext(<ValidationTestComponent />);
|
||||
expect(screen.getByTestId("validation-result")).toHaveTextContent("invalid");
|
||||
});
|
||||
|
||||
it("should fail validation with empty job name", () => {
|
||||
const mockState: CopyJobContextState = {
|
||||
jobName: "",
|
||||
migrationType: CopyJobMigrationType.Offline,
|
||||
source: {
|
||||
subscription: null as any,
|
||||
account: null as any,
|
||||
databaseId: "",
|
||||
containerId: "",
|
||||
},
|
||||
target: {
|
||||
subscriptionId: "",
|
||||
account: null as any,
|
||||
databaseId: "",
|
||||
containerId: "",
|
||||
},
|
||||
};
|
||||
|
||||
const ValidationTestComponent = () => {
|
||||
const screens = useCreateCopyJobScreensList(mockGoBack);
|
||||
const screen = screens.find((s) => s.key === SCREEN_KEYS.PreviewCopyJob);
|
||||
const isValid = screen?.validations[0]?.validate(mockState);
|
||||
|
||||
return <div data-testid="validation-result">{isValid ? "valid" : "invalid"}</div>;
|
||||
};
|
||||
|
||||
renderWithContext(<ValidationTestComponent />);
|
||||
expect(screen.getByTestId("validation-result")).toHaveTextContent("invalid");
|
||||
});
|
||||
});
|
||||
|
||||
describe("AssignPermissions screen validation", () => {
|
||||
it("should validate cache values", () => {
|
||||
renderWithContext(<TestHookComponent goBack={mockGoBack} />);
|
||||
|
||||
const validationMessages = JSON.parse(screen.getByTestId("screen-validations-4").textContent || "[]");
|
||||
expect(validationMessages).toContain("Please ensure all previous steps are valid to proceed");
|
||||
});
|
||||
|
||||
it("should pass validation when all cache values are true", () => {
|
||||
const mockCache = new Map([
|
||||
["step1", true],
|
||||
["step2", true],
|
||||
["step3", true],
|
||||
]);
|
||||
|
||||
const ValidationTestComponent = () => {
|
||||
const screens = useCreateCopyJobScreensList(mockGoBack);
|
||||
const screen = screens.find((s) => s.key === SCREEN_KEYS.AssignPermissions);
|
||||
const isValid = screen?.validations[0]?.validate(mockCache);
|
||||
|
||||
return <div data-testid="validation-result">{isValid ? "valid" : "invalid"}</div>;
|
||||
};
|
||||
|
||||
renderWithContext(<ValidationTestComponent />);
|
||||
expect(screen.getByTestId("validation-result")).toHaveTextContent("valid");
|
||||
});
|
||||
|
||||
it("should fail validation when cache is empty", () => {
|
||||
const mockCache = new Map();
|
||||
|
||||
const ValidationTestComponent = () => {
|
||||
const screens = useCreateCopyJobScreensList(mockGoBack);
|
||||
const screen = screens.find((s) => s.key === SCREEN_KEYS.AssignPermissions);
|
||||
const isValid = screen?.validations[0]?.validate(mockCache);
|
||||
|
||||
return <div data-testid="validation-result">{isValid ? "valid" : "invalid"}</div>;
|
||||
};
|
||||
|
||||
renderWithContext(<ValidationTestComponent />);
|
||||
expect(screen.getByTestId("validation-result")).toHaveTextContent("invalid");
|
||||
});
|
||||
|
||||
it("should fail validation when any cache value is false", () => {
|
||||
const mockCache = new Map([
|
||||
["step1", true],
|
||||
["step2", false],
|
||||
["step3", true],
|
||||
]);
|
||||
|
||||
const ValidationTestComponent = () => {
|
||||
const screens = useCreateCopyJobScreensList(mockGoBack);
|
||||
const screen = screens.find((s) => s.key === SCREEN_KEYS.AssignPermissions);
|
||||
const isValid = screen?.validations[0]?.validate(mockCache);
|
||||
|
||||
return <div data-testid="validation-result">{isValid ? "valid" : "invalid"}</div>;
|
||||
};
|
||||
|
||||
renderWithContext(<ValidationTestComponent />);
|
||||
expect(screen.getByTestId("validation-result")).toHaveTextContent("invalid");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("SCREEN_KEYS constant", () => {
|
||||
it("should export correct screen keys", () => {
|
||||
expect(SCREEN_KEYS.CreateCollection).toBe("CreateCollection");
|
||||
expect(SCREEN_KEYS.SelectAccount).toBe("SelectAccount");
|
||||
expect(SCREEN_KEYS.SelectSourceAndTargetContainers).toBe("SelectSourceAndTargetContainers");
|
||||
expect(SCREEN_KEYS.PreviewCopyJob).toBe("PreviewCopyJob");
|
||||
expect(SCREEN_KEYS.AssignPermissions).toBe("AssignPermissions");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Component props", () => {
|
||||
it("should pass explorer to AddCollectionPanelWrapper", () => {
|
||||
renderWithContext(<TestHookComponent goBack={mockGoBack} />);
|
||||
expect(screen.getByTestId("add-collection-panel")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should pass goBack function to AddCollectionPanelWrapper", () => {
|
||||
renderWithContext(<TestHookComponent goBack={mockGoBack} />);
|
||||
expect(screen.getByTestId("add-collection-panel")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Error handling", () => {
|
||||
it("should handle context provider error gracefully", () => {
|
||||
const consoleError = jest.spyOn(console, "error").mockImplementation(() => {});
|
||||
|
||||
(useCopyJobContext as jest.Mock).mockImplementation(() => {
|
||||
throw new Error("Context not found");
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
render(<TestHookComponent goBack={mockGoBack} />);
|
||||
}).toThrow("Context not found");
|
||||
|
||||
consoleError.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -8,11 +8,11 @@ import SelectAccount from "../Screens/SelectAccount/SelectAccount";
|
||||
import SelectSourceAndTargetContainers from "../Screens/SelectSourceAndTargetContainers/SelectSourceAndTargetContainers";
|
||||
|
||||
const SCREEN_KEYS = {
|
||||
CreateCollection: "CreateCollection",
|
||||
SelectAccount: "SelectAccount",
|
||||
SelectSourceAndTargetContainers: "SelectSourceAndTargetContainers",
|
||||
PreviewCopyJob: "PreviewCopyJob",
|
||||
AssignPermissions: "AssignPermissions",
|
||||
SelectSourceAndTargetContainers: "SelectSourceAndTargetContainers",
|
||||
CreateCollection: "CreateCollection",
|
||||
PreviewCopyJob: "PreviewCopyJob",
|
||||
};
|
||||
|
||||
type Validation = {
|
||||
|
||||
Reference in New Issue
Block a user