import "@testing-library/jest-dom"; import React from "react"; import CookieBanner from "./index"; import { act, fireEvent, render, screen } from "@testing-library/react"; // Mock cookie utilities const mockGetCookie = jest.fn(); const mockSetCookie = jest.fn(); jest.mock("../../../utils/cookie", () => ({ getCookie: (...args: any[]) => mockGetCookie(...args), setCookie: (...args: any[]) => mockSetCookie(...args), })); jest.mock("@shared/components/material-icon", () => ({ MaterialIcon: ({ name }: any) => ( {name} ), })); jest.mock("@shared/contentful/blocks/button", () => ({ Button: (props: any) => ( ), })); describe("CookieBanner", () => { const defaultContent = { entryName: "Cookie Notice", anchorId: "cookie-banner", title: "Cookies", richText:

We use cookies to improve your experience.

, }; beforeEach(() => { jest.useFakeTimers(); mockGetCookie.mockReset(); mockSetCookie.mockReset(); // Default: cookie not set (banner should show) mockGetCookie.mockReturnValue(null); // Reset body classes document.body.classList.remove("cookie-banner-visible"); // Reset CSS custom property document.documentElement.style.removeProperty("--cookie-banner-height"); }); afterEach(() => { jest.useRealTimers(); }); describe("cookie management", () => { it("shows the banner when cookieBannerClosed cookie is not set", () => { mockGetCookie.mockReturnValue(null); render(); expect( screen.getByLabelText("Cookie usage notification") ).toBeInTheDocument(); }); it("hides the banner when cookieBannerClosed cookie is true", () => { mockGetCookie.mockReturnValue("true"); const { container } = render(); expect(container.firstChild).toBeNull(); }); it("calls getCookie with cookieBannerClosed key", () => { render(); expect(mockGetCookie).toHaveBeenCalledWith("cookieBannerClosed"); }); it("sets cookieBannerClosed cookie on close", () => { mockGetCookie.mockReturnValue(null); render(); fireEvent.click(screen.getByTestId("cookie-button")); expect(mockSetCookie).toHaveBeenCalledWith( "cookieBannerClosed", true, expect.objectContaining({ expires: expect.any(Date) }) ); }); it("sets cookie with correct expiration (30 days)", () => { mockGetCookie.mockReturnValue(null); render(); const now = Date.now(); fireEvent.click(screen.getByTestId("cookie-button")); const callArgs = mockSetCookie.mock.calls[0]; const expirationDate = callArgs[2].expires; // 43200 minutes * 60 * 1000 = 30 days in milliseconds const expectedMs = 43200 * 60 * 1000; const diff = expirationDate.getTime() - now; expect(diff).toBeGreaterThanOrEqual(expectedMs - 1000); expect(diff).toBeLessThanOrEqual(expectedMs + 1000); }); }); describe("rendering", () => { it("renders with fixed positioning", () => { render(); const banner = screen.getByLabelText("Cookie usage notification"); expect(banner.className).toContain("fixed"); }); it("renders the rich text content", () => { render(); expect( screen.getByText("We use cookies to improve your experience.") ).toBeInTheDocument(); }); it("does not render rich text when richText is falsy", () => { const content = { ...defaultContent, richText: null as any }; render(); const banner = screen.getByLabelText("Cookie usage notification"); // Banner is shown but no rich text inside expect(banner.querySelector(".mx-auto")).toBeNull(); }); it("renders close button with close icon", () => { render(); expect(screen.getByTestId("icon-close")).toBeInTheDocument(); }); it("has correct z-index class", () => { render(); const banner = screen.getByLabelText("Cookie usage notification"); expect(banner.className).toContain("z-[1000]"); }); it("renders with correct max width", () => { render(); const banner = screen.getByLabelText("Cookie usage notification"); expect(banner.className).toContain("max-w-[350px]"); }); it("renders with correct positioning styles", () => { render(); const banner = screen.getByLabelText("Cookie usage notification"); expect(banner.style.bottom).toBe("0px"); expect(banner.style.right).toBe("20px"); }); it("has id cookie-banner", () => { render(); expect(document.getElementById("cookie-banner")).toBeInTheDocument(); }); }); describe("close behavior", () => { it("hides banner on close button click", () => { mockGetCookie.mockReturnValue(null); render(); expect( screen.getByLabelText("Cookie usage notification") ).toBeInTheDocument(); fireEvent.click(screen.getByTestId("cookie-button")); expect( screen.queryByLabelText("Cookie usage notification") ).not.toBeInTheDocument(); }); }); describe("body class management", () => { it("adds cookie-banner-visible class to body when visible", () => { mockGetCookie.mockReturnValue(null); render(); expect(document.body.classList.contains("cookie-banner-visible")).toBe( true ); }); it("removes cookie-banner-visible class when banner is hidden", () => { mockGetCookie.mockReturnValue(null); render(); fireEvent.click(screen.getByTestId("cookie-button")); expect(document.body.classList.contains("cookie-banner-visible")).toBe( false ); }); it("removes cookie-banner-visible class on unmount", () => { mockGetCookie.mockReturnValue(null); const { unmount } = render(); expect(document.body.classList.contains("cookie-banner-visible")).toBe( true ); unmount(); expect(document.body.classList.contains("cookie-banner-visible")).toBe( false ); }); }); describe("sticky footer detection", () => { it("uses larger margin when anchored-banner element exists", () => { const anchoredBanner = document.createElement("div"); anchoredBanner.id = "anchored-banner"; document.body.appendChild(anchoredBanner); mockGetCookie.mockReturnValue(null); render(); const banner = screen.getByLabelText("Cookie usage notification"); // marginBottom = 14 * 4 = 56px when anchored-banner exists expect(banner.style.marginBottom).toBe("56px"); document.body.removeChild(anchoredBanner); }); it("uses smaller margin when anchored-banner does not exist", () => { mockGetCookie.mockReturnValue(null); render(); const banner = screen.getByLabelText("Cookie usage notification"); // marginBottom = 3 * 4 = 12px when no anchored-banner expect(banner.style.marginBottom).toBe("12px"); }); }); describe("CSS custom property for height", () => { it("calculates and sets --cookie-banner-height property", () => { mockGetCookie.mockReturnValue(null); render(); act(() => { jest.advanceTimersByTime(350); }); // The property should be set (value depends on getBoundingClientRect mock) // In jsdom, getBoundingClientRect returns zeros, so spaceFromBottom = innerHeight - 0 const value = document.documentElement.style.getPropertyValue( "--cookie-banner-height" ); // Should be set (even though value may be 0px in jsdom) expect(value).toBeDefined(); }); it("removes --cookie-banner-height on unmount", () => { mockGetCookie.mockReturnValue(null); const { unmount } = render(); act(() => { jest.advanceTimersByTime(350); }); unmount(); const value = document.documentElement.style.getPropertyValue( "--cookie-banner-height" ); expect(value).toBe(""); }); it("recalculates height on window resize", () => { mockGetCookie.mockReturnValue(null); render(); act(() => { jest.advanceTimersByTime(350); }); act(() => { window.dispatchEvent(new Event("resize")); jest.advanceTimersByTime(200); }); // Should not throw and property remains set const value = document.documentElement.style.getPropertyValue( "--cookie-banner-height" ); expect(value).toBeDefined(); }); }); });