mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-28 13:21:42 +00:00
427 lines
14 KiB
TypeScript
427 lines
14 KiB
TypeScript
import "@testing-library/jest-dom";
|
|
import { fireEvent, render, screen } from "@testing-library/react";
|
|
import React from "react";
|
|
import CreateCopyJobScreens from "./CreateCopyJobScreens";
|
|
|
|
jest.mock("../../Context/CopyJobContext", () => ({
|
|
useCopyJobContext: jest.fn(),
|
|
}));
|
|
|
|
jest.mock("../Utils/useCopyJobNavigation", () => ({
|
|
useCopyJobNavigation: jest.fn(),
|
|
}));
|
|
|
|
jest.mock("./Components/NavigationControls", () => {
|
|
const MockedNavigationControls = ({
|
|
primaryBtnText,
|
|
onPrimary,
|
|
onPrevious,
|
|
onCancel,
|
|
isPrimaryDisabled,
|
|
isPreviousDisabled,
|
|
}: {
|
|
primaryBtnText: string;
|
|
onPrimary: () => void;
|
|
onPrevious: () => void;
|
|
onCancel: () => void;
|
|
isPrimaryDisabled: boolean;
|
|
isPreviousDisabled: boolean;
|
|
}) => (
|
|
<div data-testid="navigation-controls">
|
|
<button data-testid="primary-button" onClick={onPrimary} disabled={isPrimaryDisabled}>
|
|
{primaryBtnText}
|
|
</button>
|
|
<button data-testid="previous-button" onClick={onPrevious} disabled={isPreviousDisabled}>
|
|
Previous
|
|
</button>
|
|
<button data-testid="cancel-button" onClick={onCancel}>
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
);
|
|
return MockedNavigationControls;
|
|
});
|
|
|
|
import { useCopyJobContext } from "../../Context/CopyJobContext";
|
|
import { useCopyJobNavigation } from "../Utils/useCopyJobNavigation";
|
|
|
|
const createMockNavigationHook = (overrides = {}) => ({
|
|
currentScreen: {
|
|
key: "SelectAccount",
|
|
component: <div data-testid="mock-screen">Mock Screen Component</div>,
|
|
},
|
|
isPrimaryDisabled: false,
|
|
isPreviousDisabled: true,
|
|
handlePrimary: jest.fn(),
|
|
handlePrevious: jest.fn(),
|
|
handleCancel: jest.fn(),
|
|
primaryBtnText: "Next",
|
|
showAddCollectionPanel: jest.fn(),
|
|
...overrides,
|
|
});
|
|
|
|
const createMockContext = (overrides = {}) => ({
|
|
contextError: "",
|
|
setContextError: jest.fn(),
|
|
copyJobState: {},
|
|
setCopyJobState: jest.fn(),
|
|
flow: {},
|
|
setFlow: jest.fn(),
|
|
resetCopyJobState: jest.fn(),
|
|
explorer: {},
|
|
...overrides,
|
|
});
|
|
|
|
describe("CreateCopyJobScreens", () => {
|
|
const mockNavigationHook = createMockNavigationHook();
|
|
const mockContext = createMockContext();
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
(useCopyJobNavigation as jest.Mock).mockReturnValue(mockNavigationHook);
|
|
(useCopyJobContext as jest.Mock).mockReturnValue(mockContext);
|
|
});
|
|
|
|
describe("Rendering", () => {
|
|
test("should render without error", () => {
|
|
render(<CreateCopyJobScreens />);
|
|
expect(screen.getByTestId("mock-screen")).toBeInTheDocument();
|
|
expect(screen.getByTestId("navigation-controls")).toBeInTheDocument();
|
|
});
|
|
|
|
test("should render current screen component", () => {
|
|
const customScreen = <div data-testid="custom-screen">Custom Screen Content</div>;
|
|
(useCopyJobNavigation as jest.Mock).mockReturnValue(
|
|
createMockNavigationHook({
|
|
currentScreen: { component: customScreen },
|
|
}),
|
|
);
|
|
|
|
render(<CreateCopyJobScreens />);
|
|
expect(screen.getByTestId("custom-screen")).toBeInTheDocument();
|
|
expect(screen.getByText("Custom Screen Content")).toBeInTheDocument();
|
|
});
|
|
|
|
test("should have correct CSS classes", () => {
|
|
const { container } = render(<CreateCopyJobScreens />);
|
|
const mainContainer = container.querySelector(".createCopyJobScreensContainer");
|
|
const contentContainer = container.querySelector(".createCopyJobScreensContent");
|
|
const footerContainer = container.querySelector(".createCopyJobScreensFooter");
|
|
|
|
expect(mainContainer).toBeInTheDocument();
|
|
expect(contentContainer).toBeInTheDocument();
|
|
expect(footerContainer).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("Error Message Bar", () => {
|
|
test("should not show error message bar when no error", () => {
|
|
render(<CreateCopyJobScreens />);
|
|
expect(screen.queryByRole("region")).not.toBeInTheDocument();
|
|
});
|
|
|
|
test("should show error message bar when context error exists", () => {
|
|
const errorMessage = "Something went wrong";
|
|
(useCopyJobContext as jest.Mock).mockReturnValue(
|
|
createMockContext({
|
|
contextError: errorMessage,
|
|
}),
|
|
);
|
|
|
|
render(<CreateCopyJobScreens />);
|
|
const messageBar = screen.getByRole("region");
|
|
expect(messageBar).toBeInTheDocument();
|
|
expect(messageBar).toHaveClass("createCopyJobErrorMessageBar");
|
|
});
|
|
|
|
test("should have correct error message bar properties", () => {
|
|
const errorMessage = "Test error message";
|
|
(useCopyJobContext as jest.Mock).mockReturnValue(
|
|
createMockContext({
|
|
contextError: errorMessage,
|
|
}),
|
|
);
|
|
|
|
render(<CreateCopyJobScreens />);
|
|
const messageBar = screen.getByRole("region");
|
|
|
|
expect(messageBar).toHaveClass("createCopyJobErrorMessageBar");
|
|
});
|
|
|
|
test("should call setContextError when dismiss button is clicked", () => {
|
|
const mockSetContextError = jest.fn();
|
|
(useCopyJobContext as jest.Mock).mockReturnValue(
|
|
createMockContext({
|
|
contextError: "Test error",
|
|
setContextError: mockSetContextError,
|
|
}),
|
|
);
|
|
|
|
render(<CreateCopyJobScreens />);
|
|
|
|
const dismissButton = screen.getByLabelText("Close");
|
|
fireEvent.click(dismissButton);
|
|
|
|
expect(mockSetContextError).toHaveBeenCalledWith(null);
|
|
});
|
|
|
|
test("should show overflow button with correct aria label", () => {
|
|
(useCopyJobContext as jest.Mock).mockReturnValue(
|
|
createMockContext({
|
|
contextError: "A very long error message that should trigger overflow behavior",
|
|
}),
|
|
);
|
|
|
|
render(<CreateCopyJobScreens />);
|
|
const overflowButton = screen.getByLabelText("See more");
|
|
expect(overflowButton).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("Navigation Controls Integration", () => {
|
|
test("should pass correct props to NavigationControls", () => {
|
|
const mockHook = createMockNavigationHook({
|
|
primaryBtnText: "Create",
|
|
isPrimaryDisabled: true,
|
|
isPreviousDisabled: false,
|
|
});
|
|
(useCopyJobNavigation as jest.Mock).mockReturnValue(mockHook);
|
|
|
|
render(<CreateCopyJobScreens />);
|
|
|
|
const primaryButton = screen.getByTestId("primary-button");
|
|
const previousButton = screen.getByTestId("previous-button");
|
|
|
|
expect(primaryButton).toHaveTextContent("Create");
|
|
expect(primaryButton).toBeDisabled();
|
|
expect(previousButton).not.toBeDisabled();
|
|
});
|
|
|
|
test("should call navigation handlers when buttons are clicked", () => {
|
|
const mockHandlePrimary = jest.fn();
|
|
const mockHandlePrevious = jest.fn();
|
|
const mockHandleCancel = jest.fn();
|
|
|
|
(useCopyJobNavigation as jest.Mock).mockReturnValue(
|
|
createMockNavigationHook({
|
|
handlePrimary: mockHandlePrimary,
|
|
handlePrevious: mockHandlePrevious,
|
|
handleCancel: mockHandleCancel,
|
|
isPrimaryDisabled: false,
|
|
isPreviousDisabled: false,
|
|
}),
|
|
);
|
|
|
|
render(<CreateCopyJobScreens />);
|
|
|
|
fireEvent.click(screen.getByTestId("primary-button"));
|
|
fireEvent.click(screen.getByTestId("previous-button"));
|
|
fireEvent.click(screen.getByTestId("cancel-button"));
|
|
|
|
expect(mockHandlePrimary).toHaveBeenCalledTimes(1);
|
|
expect(mockHandlePrevious).toHaveBeenCalledTimes(1);
|
|
expect(mockHandleCancel).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe("Screen Component Props", () => {
|
|
test("should pass showAddCollectionPanel prop to screen component", () => {
|
|
const mockShowAddCollectionPanel = jest.fn();
|
|
const TestScreen = ({ showAddCollectionPanel }: { showAddCollectionPanel: () => void }) => (
|
|
<div>
|
|
<button data-testid="add-collection-btn" onClick={showAddCollectionPanel}>
|
|
Add Collection
|
|
</button>
|
|
</div>
|
|
);
|
|
|
|
(useCopyJobNavigation as jest.Mock).mockReturnValue(
|
|
createMockNavigationHook({
|
|
currentScreen: { component: <TestScreen showAddCollectionPanel={() => {}} /> },
|
|
showAddCollectionPanel: mockShowAddCollectionPanel,
|
|
}),
|
|
);
|
|
|
|
render(<CreateCopyJobScreens />);
|
|
|
|
const addButton = screen.getByTestId("add-collection-btn");
|
|
expect(addButton).toBeInTheDocument();
|
|
});
|
|
|
|
test("should handle screen component without props", () => {
|
|
const SimpleScreen = () => <div data-testid="simple-screen">Simple Screen</div>;
|
|
|
|
(useCopyJobNavigation as jest.Mock).mockReturnValue(
|
|
createMockNavigationHook({
|
|
currentScreen: { component: <SimpleScreen /> },
|
|
}),
|
|
);
|
|
|
|
expect(() => render(<CreateCopyJobScreens />)).not.toThrow();
|
|
expect(screen.getByTestId("simple-screen")).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("Layout and Structure", () => {
|
|
test("should maintain vertical layout with space-between alignment", () => {
|
|
const { container } = render(<CreateCopyJobScreens />);
|
|
const stackContainer = container.querySelector(".createCopyJobScreensContainer");
|
|
|
|
expect(stackContainer).toBeInTheDocument();
|
|
});
|
|
|
|
test("should have content area above navigation controls", () => {
|
|
const { container } = render(<CreateCopyJobScreens />);
|
|
|
|
const content = container.querySelector(".createCopyJobScreensContent");
|
|
const footer = container.querySelector(".createCopyJobScreensFooter");
|
|
|
|
expect(content).toBeInTheDocument();
|
|
expect(footer).toBeInTheDocument();
|
|
|
|
const contentIndex = Array.from(container.querySelectorAll("*")).indexOf(content!);
|
|
const footerIndex = Array.from(container.querySelectorAll("*")).indexOf(footer!);
|
|
expect(contentIndex).toBeLessThan(footerIndex);
|
|
});
|
|
});
|
|
|
|
describe("Error Scenarios", () => {
|
|
test("should handle missing current screen gracefully", () => {
|
|
(useCopyJobNavigation as jest.Mock).mockReturnValue(
|
|
createMockNavigationHook({
|
|
currentScreen: null,
|
|
}),
|
|
);
|
|
expect(() => render(<CreateCopyJobScreens />)).toThrow();
|
|
});
|
|
|
|
test("should handle missing screen component", () => {
|
|
(useCopyJobNavigation as jest.Mock).mockReturnValue(
|
|
createMockNavigationHook({
|
|
currentScreen: { key: "test", component: null },
|
|
}),
|
|
);
|
|
expect(() => render(<CreateCopyJobScreens />)).toThrow();
|
|
});
|
|
|
|
test("should render with valid screen component", () => {
|
|
(useCopyJobNavigation as jest.Mock).mockReturnValue(
|
|
createMockNavigationHook({
|
|
currentScreen: {
|
|
key: "test",
|
|
component: <div data-testid="valid-screen">Valid Screen</div>,
|
|
},
|
|
}),
|
|
);
|
|
|
|
expect(() => render(<CreateCopyJobScreens />)).not.toThrow();
|
|
expect(screen.getByTestId("valid-screen")).toBeInTheDocument();
|
|
});
|
|
|
|
test("should handle context hook throwing error", () => {
|
|
(useCopyJobContext as jest.Mock).mockImplementation(() => {
|
|
throw new Error("Context not available");
|
|
});
|
|
|
|
expect(() => render(<CreateCopyJobScreens />)).toThrow("Context not available");
|
|
});
|
|
|
|
test("should handle navigation hook throwing error", () => {
|
|
(useCopyJobNavigation as jest.Mock).mockImplementation(() => {
|
|
throw new Error("Navigation not available");
|
|
});
|
|
|
|
expect(() => render(<CreateCopyJobScreens />)).toThrow("Navigation not available");
|
|
});
|
|
});
|
|
|
|
describe("Multiple Error States", () => {
|
|
test("should handle error message changes", () => {
|
|
const mockSetContextError = jest.fn();
|
|
const { rerender } = render(<CreateCopyJobScreens />);
|
|
|
|
expect(screen.queryByRole("region")).not.toBeInTheDocument();
|
|
|
|
(useCopyJobContext as jest.Mock).mockReturnValue(
|
|
createMockContext({
|
|
contextError: "First error",
|
|
setContextError: mockSetContextError,
|
|
}),
|
|
);
|
|
rerender(<CreateCopyJobScreens />);
|
|
expect(screen.getByRole("region")).toBeInTheDocument();
|
|
|
|
(useCopyJobContext as jest.Mock).mockReturnValue(
|
|
createMockContext({
|
|
contextError: "Second error",
|
|
setContextError: mockSetContextError,
|
|
}),
|
|
);
|
|
rerender(<CreateCopyJobScreens />);
|
|
expect(screen.getByRole("region")).toBeInTheDocument();
|
|
|
|
(useCopyJobContext as jest.Mock).mockReturnValue(
|
|
createMockContext({
|
|
contextError: null,
|
|
setContextError: mockSetContextError,
|
|
}),
|
|
);
|
|
rerender(<CreateCopyJobScreens />);
|
|
expect(screen.queryByRole("region")).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("Accessibility", () => {
|
|
test("should have proper ARIA labels for message bar", () => {
|
|
(useCopyJobContext as jest.Mock).mockReturnValue(
|
|
createMockContext({
|
|
contextError: "Test error",
|
|
}),
|
|
);
|
|
|
|
render(<CreateCopyJobScreens />);
|
|
|
|
const dismissButton = screen.getByLabelText("Close");
|
|
const overflowButton = screen.getByLabelText("See more");
|
|
|
|
expect(dismissButton).toBeInTheDocument();
|
|
expect(overflowButton).toBeInTheDocument();
|
|
});
|
|
|
|
test("should have proper region role for message bar", () => {
|
|
(useCopyJobContext as jest.Mock).mockReturnValue(
|
|
createMockContext({
|
|
contextError: "Test error",
|
|
}),
|
|
);
|
|
|
|
render(<CreateCopyJobScreens />);
|
|
const messageRegion = screen.getByRole("region");
|
|
expect(messageRegion).toBeInTheDocument();
|
|
|
|
const alert = screen.getByRole("alert");
|
|
expect(alert).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("Component Integration", () => {
|
|
test("should integrate with both context and navigation hooks", () => {
|
|
const mockContext = createMockContext({
|
|
contextError: "Integration test error",
|
|
});
|
|
const mockNavigation = createMockNavigationHook({
|
|
primaryBtnText: "Integration Test",
|
|
isPrimaryDisabled: true,
|
|
});
|
|
|
|
(useCopyJobContext as jest.Mock).mockReturnValue(mockContext);
|
|
(useCopyJobNavigation as jest.Mock).mockReturnValue(mockNavigation);
|
|
|
|
render(<CreateCopyJobScreens />);
|
|
|
|
expect(screen.getByRole("region")).toBeInTheDocument();
|
|
expect(screen.getByText("Integration Test")).toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|