Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#637] Create atoms components ui tests #668

Merged
merged 1 commit into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions govtool/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"test": "vitest",
"test:ui": "vitest --ui",
"test-storybook": "test-storybook",
"test:watch": "vitest watch",
"tsc": "npx tsc --noEmit --skipLibCheck"
Expand Down
90 changes: 90 additions & 0 deletions govtool/frontend/src/components/atoms/ActionRadio.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { describe, expect, it, vi } from "vitest";
import { fireEvent, render, screen } from "@testing-library/react";
import { ActionRadio } from "@atoms";

describe("ActionRadio", () => {
it("should execute onChange with the correct value on click", () => {
const handleChange = vi.fn();
render(
<ActionRadio
title="Test Radio"
value="test-value"
onChange={handleChange}
dataTestId="action-radio"
/>,
);

const radio = screen.getByTestId("action-radio");
fireEvent.click(radio);

expect(handleChange).toHaveBeenCalledTimes(1);
expect(handleChange).toHaveBeenCalledWith("test-value");
});

it("should change styles based on isChecked change", () => {
const { rerender } = render(
<ActionRadio
title="Test Radio"
value="unchecked-value"
isChecked={false}
onChange={() => {}}
dataTestId="action-radio"
/>,
);
let radio = screen.getByTestId("action-radio");

expect(radio).toHaveStyle("borderColor: white");
expect(radio).toHaveStyle("backgroundColor: rgb(255, 255, 255)");

rerender(
<ActionRadio
title="Test Radio"
value="checked-value"
isChecked
onChange={() => {}}
dataTestId="action-radio"
/>,
);
radio = screen.getByTestId("action-radio");

expect(radio).toHaveStyle("borderColor: specialCyanBorder");
expect(radio).toHaveStyle("backgroundColor: specialCyan");
});

it("should display correct title and optional subtitle", () => {
render(
<ActionRadio
title="Main Title"
subtitle="Sub Title"
value="any-value"
onChange={() => {}}
dataTestId="action-radio"
/>,
);

const title = screen.getByText("Main Title");
const subtitle = screen.getByText("Sub Title");

expect(title).toBeInTheDocument();
expect(subtitle).toBeInTheDocument();
});

it("should display tooltip text when InfoOutlinedIcon is hovered over", async () => {
render(
<ActionRadio
title="With Tooltip"
tooltipText="Info Here"
tooltipTitle="Tooltip"
value="tooltip-present-value"
onChange={() => {}}
dataTestId="action-radio"
/>,
);

const icon = screen.getByTestId("InfoOutlinedIcon");
fireEvent.mouseOver(icon);

const tooltip = await screen.findByText("Info Here", {}, { timeout: 500 });
expect(tooltip).toBeInTheDocument();
});
});
69 changes: 69 additions & 0 deletions govtool/frontend/src/components/atoms/CopyButton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { SnackbarProvider } from "@context";
import { CopyButton } from "@atoms";

Object.defineProperty(global.navigator, "clipboard", {
value: {
writeText: vi.fn(),
},
writable: true,
});

vi.mock("@hooks", () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
useScreenDimension: () => ({
isMobile: false,
}),
}));

const writeTextMock = navigator.clipboard.writeText as unknown as {
mockClear: () => void;
};

describe("CopyButton", () => {
beforeEach(() => {
writeTextMock.mockClear();
});

it("renders correctly with the default icon", () => {
render(<CopyButton text="Example Text" />);
const image = screen.getByRole("img");
expect(image).toHaveAttribute("src", "/icons/Copy.svg");
});

it("renders the blue icon when variant is 'blue'", () => {
render(<CopyButton text="Example Text" variant="blue" />);
const image = screen.getByRole("img");
expect(image).toHaveAttribute("src", "/icons/CopyBlue.svg");
});

it("renders the blue thin icon when variant is 'blueThin'", () => {
render(<CopyButton text="Example Text" variant="blueThin" />);
const image = screen.getByRole("img");
expect(image).toHaveAttribute("src", "/icons/CopyBlueThin.svg");
});

it("renders the white icon when isChecked prop is true", () => {
render(<CopyButton text="Example Text" isChecked />);
const image = screen.getByRole("img");
expect(image).toHaveAttribute("src", "/icons/CopyWhite.svg");
});

it("copies text to clipboard and shows success alert on click", async () => {
render(
<SnackbarProvider>
<CopyButton text="Example Text" />,
</SnackbarProvider>,
);

const copyButton = screen.getByTestId("copy-button");
await userEvent.click(copyButton);
expect(navigator.clipboard.writeText).toHaveBeenCalledWith("Example Text");

expect(screen.getByText("alerts.copiedToClipboard")).toBeInTheDocument();
});
});
90 changes: 90 additions & 0 deletions govtool/frontend/src/components/atoms/DrawerLink.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { MemoryRouter, Route, Routes } from "react-router-dom";
import { DrawerLink } from "@atoms";
import { theme } from "@/theme";

describe("DrawerLink", () => {
const mockOnClick = vi.fn();

it("renders correctly with mandatory props", () => {
render(
<MemoryRouter initialEntries={["/somepath"]}>
<DrawerLink label="Home" navTo="/home" />
</MemoryRouter>,
);

const linkElement = screen.getByRole("link");
expect(linkElement).toHaveAttribute("href", "/home");
expect(screen.getByText("Home")).toBeInTheDocument();
});

it("applies active styles correctly when active", () => {
render(
<MemoryRouter initialEntries={["/home"]}>
<Routes>
<Route
path="/home"
element={<DrawerLink label="Home" navTo="/home" />}
/>
</Routes>
</MemoryRouter>,
);

const linkElement = screen.getByRole("link");
expect(linkElement).toHaveStyle(
`backgroundColor: ${theme.palette.highlightBlue}`,
);
});

it("does not apply active styles when not active", () => {
render(
<MemoryRouter initialEntries={["/other"]}>
<DrawerLink label="Home" navTo="/home" />
</MemoryRouter>,
);

const linkElement = screen.getByRole("link");
expect(linkElement).not.toHaveStyle(
`backgroundColor: ${theme.palette.highlightBlue}`,
);
});

it("renders with an icon and activeIcon", () => {
const icon = "icon-path.png";
const activeIcon = "active-icon-path.png";

render(
<MemoryRouter initialEntries={["/home"]}>
<Routes>
<Route
path="/home"
element={
<DrawerLink
label="Home"
navTo="/home"
icon={icon}
activeIcon={activeIcon}
/>
}
/>
</Routes>
</MemoryRouter>,
);

const img = screen.getByAltText("icon") as HTMLImageElement;
expect(img.src).toContain("active-icon-path.png");
});

it("executes onClick callback when clicked", () => {
render(
<MemoryRouter initialEntries={["/home"]}>
<DrawerLink label="Clickable" navTo="/click" onClick={mockOnClick} />
</MemoryRouter>,
);

const linkElement = screen.getByRole("link");
fireEvent.click(linkElement);
expect(mockOnClick).toHaveBeenCalled();
});
});
53 changes: 53 additions & 0 deletions govtool/frontend/src/components/atoms/LoadingButton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import { LoadingButton } from "@atoms";

describe("LoadingButton", () => {
it("renders its children", () => {
render(<LoadingButton isLoading={false}>Click me</LoadingButton>);
expect(screen.getByText("Click me")).toBeInTheDocument();
});

it("is disabled when isLoading is true", () => {
render(<LoadingButton isLoading>Loading...</LoadingButton>);
expect(screen.getByRole("button", { name: "Loading..." })).toBeDisabled();
});

it("is disabled when disabled prop is true", () => {
render(<LoadingButton disabled>Disabled</LoadingButton>);
expect(screen.getByRole("button", { name: "Disabled" })).toBeDisabled();
});

it("shows a CircularProgress when isLoading", () => {
render(<LoadingButton isLoading>Loading...</LoadingButton>);
expect(screen.getByRole("progressbar")).toBeInTheDocument();
});

it("applies different heights based on size prop", () => {
const { rerender } = render(
<LoadingButton size="small">Small Button</LoadingButton>,
);

expect(screen.getByText("Small Button")).toHaveStyle({ height: "32px" });

rerender(<LoadingButton size="medium">Medium Button</LoadingButton>);
expect(screen.getByText("Medium Button")).toHaveStyle({ height: "36px" });

rerender(<LoadingButton size="large">Large Button</LoadingButton>);
expect(screen.getByText("Large Button")).toHaveStyle({ height: "40px" });

rerender(
<LoadingButton size="extraLarge">Extra Large Button</LoadingButton>,
);
expect(screen.getByText("Extra Large Button")).toHaveStyle("height: 48px");
});

it("applies custom styles via sx prop", () => {
const customStyles = { backgroundColor: "specialCyan" };
render(<LoadingButton sx={customStyles}>Styled Button</LoadingButton>);

expect(screen.getByText("Styled Button")).toHaveStyle({
backgroundColor: "specialCyan",
});
});
});
55 changes: 55 additions & 0 deletions govtool/frontend/src/components/atoms/VotePill.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { describe, it, expect } from "vitest";
import { render } from "@testing-library/react";
import { VotePill } from "@atoms";

describe("VotePill", () => {
it('renders the VotePill component with "yes" vote correctly', () => {
const { getByText } = render(<VotePill vote="yes" />);
const voteText = getByText("yes");
expect(voteText).toBeInTheDocument();
expect(voteText.parentNode).toHaveStyle({
borderColor: "#C0E4BA",
backgroundColor: "#F0F9EE",
});
});

it('renders the VotePill component with "no" vote correctly', () => {
const { getByText } = render(<VotePill vote="no" />);
const voteText = getByText("no");
expect(voteText).toBeInTheDocument();
expect(voteText.parentNode).toHaveStyle({
borderColor: "#EDACAC",
backgroundColor: "#FBEBEB",
});
});

it('renders the VotePill component with "abstain" vote correctly', () => {
const { getByText } = render(<VotePill vote="abstain" />);
const voteText = getByText("abstain");
expect(voteText).toBeInTheDocument();
expect(voteText.parentNode).toHaveStyle({
borderColor: "#99ADDE",
backgroundColor: "#E6EBF7",
});
});

it("handles custom width and maxWidth props correctly", () => {
const { container } = render(
<VotePill vote="yes" width={100} maxWidth={120} />,
);
const pillBox = container.firstChild;
expect(pillBox).toHaveStyle({
width: "100px",
maxWidth: "120px",
});
});

it("defaults width and maxWidth when not provided", () => {
const { container } = render(<VotePill vote="abstain" />);
const pillBox = container.firstChild;
expect(pillBox).toHaveStyle({
width: "auto",
maxWidth: "auto",
});
});
});
Loading
Loading