Skip to content

Commit

Permalink
test: add unit tests for LibraryPage and shared components
Browse files Browse the repository at this point in the history
  • Loading branch information
amalv committed Dec 27, 2023
1 parent 55e5958 commit 7b8df4a
Show file tree
Hide file tree
Showing 9 changed files with 238 additions and 0 deletions.
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@semantic-release/git": "^10.0.1",
"@testing-library/jest-dom": "^6.1.4",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.5.1",
"@types/jest": "^29.5.11",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { render, screen } from "@testing-library/react";
import { BooksView } from "./BooksView";

describe("BooksView", () => {
it("renders a list of books when provided", () => {
const books = [
{
id: "1",
title: "Book 1",
author: "Author 1",
publicationDate: new Date().toLocaleDateString("en-CA"), // 'en-CA' locale results in 'yyyy-mm-dd' format
image: "image-url-1",
rating: 4.5,
ratingsCount: 100,
},
{
id: "2",
title: "Book 2",
author: "Author 2",
publicationDate: new Date().toLocaleDateString("en-CA"),
image: "image-url-2",
rating: 4.0,
ratingsCount: 200,
},
];

render(<BooksView books={books} />);

books.forEach((book) => {
expect(screen.getByText(book.title)).toBeInTheDocument();
});
});

it("renders a message when no books are provided", () => {
render(<BooksView books={[]} />);
expect(screen.getByText("No books available")).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { render, screen } from "@testing-library/react";
import { userEvent } from "@testing-library/user-event";
import { ApolloError } from "@apollo/client";
import { ErrorView } from "./ErrorView";
import { vi } from "vitest";

describe("ErrorView", () => {
it("renders an error message when provided", () => {
const error = new ApolloError({ errorMessage: "Test error" });

render(
<ErrorView
error={error}
isErrorSnackbarOpen={true}
handleCloseSnackbar={vi.fn()}
/>
);

const errorMessages = screen.getAllByText("Test error");
expect(errorMessages).toHaveLength(2);
});

it("calls handleCloseSnackbar when the snackbar is closed", async () => {
const handleCloseSnackbar = vi.fn();
const error = new ApolloError({ errorMessage: "Test error" });

render(
<ErrorView
error={error}
isErrorSnackbarOpen={true}
handleCloseSnackbar={handleCloseSnackbar}
/>
);

const user = userEvent.setup();
await user.click(screen.getByRole("button", { name: /close/i }));

expect(handleCloseSnackbar).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { render, screen } from "@testing-library/react";
import { LoadingView } from "./LoadingView";

describe("LoadingView", () => {
it("renders a CircularProgress component", () => {
render(<LoadingView />);

expect(screen.getByRole("progressbar")).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { render, screen } from "@testing-library/react";
import { Message } from "./Message";

describe("Message", () => {
it("renders the provided text", () => {
const testMessage = "Test message";
render(<Message text={testMessage} />);

expect(screen.getByText(testMessage)).toBeInTheDocument();
});
});
18 changes: 18 additions & 0 deletions src/components/LibraryPage/components/Search/Search.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { render, screen } from "@testing-library/react";
import { userEvent } from "@testing-library/user-event";
import { Search } from "./Search";
import { vi } from "vitest";

describe("Search", () => {
it("updates the search value when typed into", async () => {
const setSearch = vi.fn();
render(<Search search="" setSearch={setSearch} />);

const input = screen.getByLabelText("Search by title or author");
const user = userEvent.setup();
await user.type(input, "Test search");

const calls = setSearch.mock.calls.map((call) => call[0]).join("");
expect(calls).toEqual("Test search");
});
});
66 changes: 66 additions & 0 deletions src/components/LibraryPage/hooks/useLibraryPage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { renderHook, act, waitFor } from "@testing-library/react";
import { useLibraryPage } from "./useLibraryPage";

describe("useLibraryPage", () => {
it("should update search and debouncedSearch after debounce delay", async () => {
const { result } = renderHook(() => useLibraryPage(100));

// Update search
act(() => {
result.current.setSearch("Test search");
});

expect(result.current.search).toBe("Test search");
expect(result.current.debouncedSearch).toBe("");

// Wait for debounce and check debouncedSearch
await waitFor(() =>
expect(result.current.debouncedSearch).toBe("Test search")
);
});

it("should update debouncedSearch once after multiple updates to search", async () => {
const { result } = renderHook(() => useLibraryPage(100));

// Update search multiple times
act(() => {
result.current.setSearch("Test");
result.current.setSearch("Test search");
});

expect(result.current.search).toBe("Test search");

// Wait for debounce and check debouncedSearch
await waitFor(() =>
expect(result.current.debouncedSearch).toBe("Test search")
);
});

it("should clear debouncedSearch when search is cleared", async () => {
const { result } = renderHook(() => useLibraryPage(100));

// Set and clear search
act(() => {
result.current.setSearch("Test search");
result.current.setSearch("");
});

expect(result.current.search).toBe("");

// Wait for debounce and check debouncedSearch
await waitFor(() => expect(result.current.debouncedSearch).toBe(""));
});

it("should update and clear error", () => {
const { result } = renderHook(() => useLibraryPage(500));

// Set and clear error
const error = new Error("Test error");
act(() => {
result.current.setError(error);
result.current.setError(null);
});

expect(result.current.error).toBeNull();
});
});
40 changes: 40 additions & 0 deletions src/components/shared/ErrorSnackbar/ErrorSnackbar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { render, screen } from "@testing-library/react";
import { userEvent } from "@testing-library/user-event";
import { ErrorSnackbar } from "./ErrorSnackbar";
import { vi } from "vitest";

describe("ErrorSnackbar", () => {
const user = userEvent.setup();

it("should be visible and display the correct error message when open is true", () => {
const handleClose = vi.fn();
const errorMessage = "Test error message";

render(
<ErrorSnackbar
open={true}
handleClose={handleClose}
errorMessage={errorMessage}
/>
);

expect(screen.getByText(errorMessage)).toBeInTheDocument();
});

it("should call handleClose when the close button is clicked", async () => {
const handleClose = vi.fn();
const errorMessage = "Test error message";

render(
<ErrorSnackbar
open={true}
handleClose={handleClose}
errorMessage={errorMessage}
/>
);

await user.click(screen.getByRole("button"));

expect(handleClose).toHaveBeenCalled();
});
});

0 comments on commit 7b8df4a

Please sign in to comment.