Skip to content
Merged
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
104 changes: 104 additions & 0 deletions frontend/src/tests/ExplorePage.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import ExplorePage from "../pages/explorePage/ExplorePage";

// mock i18next to return the key as the translation
vi.mock("react-i18next", () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}));

// set up the page and user interactions for each test
function setup() {
const user = userEvent.setup({ delay: null });
render(<ExplorePage />);
return user;
}

// get all plant cards displayed on the page
const cards = () => screen.getAllByRole("listitem");

describe("ExplorePage", () => {
// clear stored filter and search values before each test,
// and use fake timers to control the search debounce
beforeEach(() => {
sessionStorage.clear();
// shouldAdvanceTime keeps userEvent from hanging while fake timers are on
vi.useFakeTimers({ shouldAdvanceTime: true });
});

// restore real timers after each test
afterEach(() => {
vi.useRealTimers();
});

// test that all plants are displayed when no filters are selected
it("displays all plants when no filters are selected", () => {
setup();
expect(cards()).toHaveLength(15);
expect(
screen.getByRole("button", { name: "Monstera" }),
).toBeInTheDocument();
});

// test that clicking a plant card displays its information
it("opens and closes plant card information", async () => {
const user = setup();

const monstera = screen.getByRole("button", { name: "Monstera" });

// --- Closed state ---
expect(monstera).toHaveAttribute("aria-pressed", "false");
expect(
within(monstera).queryByText("explorePage.average"),
).not.toBeInTheDocument();

// --- Open the card ---
await user.click(monstera);

// the card now reports that it is pressed (open)
expect(monstera).toHaveAttribute("aria-pressed", "true");

expect(
within(monstera).getByText("explorePage.average"),
).toBeInTheDocument();
expect(
within(monstera).getByText("explorePage.part_shade"),
).toBeInTheDocument();
expect(
within(monstera).getByText("explorePage.poison"),
).toBeInTheDocument();
expect(
within(monstera).getByText("explorePage.indoor"),
).toBeInTheDocument();

// --- Close the card by clicking it again ---
await user.click(monstera);

expect(monstera).toHaveAttribute("aria-pressed", "false");
expect(
within(monstera).queryByText("explorePage.average"),
).not.toBeInTheDocument();
});

// --- Moves to backend (the resolver/database decides which plants match) ---
it.todo("filters plants by search"); // -> backend (incl. case-insensitive)
it.todo("filters plants by watering needs"); // -> backend
it.todo("filters plants by sunlight requirements"); // -> backend
it.todo("filters plants by location"); // -> backend
it.todo("filters plants by edibility"); // -> backend
it.todo("combines multiple filters"); // -> backend
it.todo("returns an empty list when nothing matches"); // -> backend

// --- Stays in frontend, written after Apollo (MockedProvider) ---
it.todo("sends the search text in the query after the debounce delay");
it.todo("sends the selected filter values in the query");
it.todo("resets the search and filters");
it.todo("shows a loading state");
it.todo("shows an error message when the query fails");
it.todo("shows a message when the result is empty");

// Snapshot tests ?
});