From 60a14c676366a6a9d429f07afcc867026d73d82c Mon Sep 17 00:00:00 2001 From: Helene Lotsberg Date: Tue, 6 Oct 2026 13:10:35 +0200 Subject: [PATCH 1/4] add Sidebar rendering test --- frontend/src/tests/Sidebar.test.tsx | 76 +++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 frontend/src/tests/Sidebar.test.tsx diff --git a/frontend/src/tests/Sidebar.test.tsx b/frontend/src/tests/Sidebar.test.tsx new file mode 100644 index 00000000..4e7d478b --- /dev/null +++ b/frontend/src/tests/Sidebar.test.tsx @@ -0,0 +1,76 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { MemoryRouter } from "react-router-dom"; +import { describe, it, expect, vi } from "vitest"; +import Sidebar from "../components/sidebar/Sidebar"; +import { ROUTES } from "../AppRoutes"; + +// mock i18next to return the key as the translation +vi.mock("react-i18next", () => ({ + useTranslation: () => ({ + t: (key: string) => key, + }), +})); + +// replace the child components with simple stand-ins. They have their own +// tests (or are not part of what we test here), and this keeps the Sidebar +// test focused on the Sidebar itself +vi.mock("../components/languageSwitcher/LanguageSwitcher", () => ({ + default: () =>
, +})); + +vi.mock("../components/ThemeToggle", () => ({ + default: () =>
, +})); + +// render the Sidebar inside a router, as NavLink needs one. +// "route" is the page the router starts on, which decides the active link +function renderSidebar({ isOpen = false, route = ROUTES.plantMatch }: { isOpen?: boolean; route?: string } = {}) { + const user = userEvent.setup(); + const onCloseSidebar = vi.fn(); // a spy that records how many times it is called + + render( + + + , + ); + + return { user, onCloseSidebar }; +} + +describe("Sidebar", () => { + // sidebar displays the title, links to all pages, language switcher, and theme toggle + it("displays the title", () => { + renderSidebar(); + + expect(screen.getByText("sidebar.title")).toBeInTheDocument(); + }); + + it("displays navigation links to all pages", () => { + renderSidebar(); + + expect(screen.getByRole("link", { name: "sidebar.plantMatch" })).toHaveAttribute( + "href", + ROUTES.plantMatch, + ); + expect(screen.getByRole("link", { name: "sidebar.explorePlants" })).toHaveAttribute( + "href", + ROUTES.explore, + ); + expect(screen.getByRole("link", { name: "sidebar.myMatches" })).toHaveAttribute( + "href", + ROUTES.myMatches, + ); + expect(screen.getByRole("link", { name: "sidebar.profile" })).toHaveAttribute( + "href", + ROUTES.profile, + ); + }); + + it("displays the language switcher and theme toggle", () => { + renderSidebar(); + + expect(screen.getByTestId("language-switcher")).toBeInTheDocument(); + expect(screen.getByTestId("theme-toggle")).toBeInTheDocument(); + }); +}); \ No newline at end of file From cc217d1a1f64b587acde6679129a96129a4e460a Mon Sep 17 00:00:00 2001 From: Helene Lotsberg Date: Tue, 6 Oct 2026 13:11:41 +0200 Subject: [PATCH 2/4] add Sidebar tests for mobile --- frontend/src/tests/Sidebar.test.tsx | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/frontend/src/tests/Sidebar.test.tsx b/frontend/src/tests/Sidebar.test.tsx index 4e7d478b..8937610c 100644 --- a/frontend/src/tests/Sidebar.test.tsx +++ b/frontend/src/tests/Sidebar.test.tsx @@ -73,4 +73,41 @@ describe("Sidebar", () => { expect(screen.getByTestId("language-switcher")).toBeInTheDocument(); expect(screen.getByTestId("theme-toggle")).toBeInTheDocument(); }); + + // on mobile the sidebar is closed after the user picks a page or clicks the close button. The onCloseSidebar prop is called in both cases. + it("closes the sidebar when a link is clicked", async () => { + const { user, onCloseSidebar } = renderSidebar(); + + await user.click(screen.getByRole("link", { name: "sidebar.profile" })); + + expect(onCloseSidebar).toHaveBeenCalledTimes(1); + }); + + it("closes the sidebar when the close button is clicked", async () => { + const { user, onCloseSidebar } = renderSidebar(); + + await user.click(screen.getByRole("button", { name: "Close menu" })); + + expect(onCloseSidebar).toHaveBeenCalledTimes(1); + }); + + // data-open controls whether the sidebar is shown or hidden on mobile, + // so it should be "true" when isOpen is true and "false" when it is false. + // The