import { AnchoredBottomBanner } from "./index"; import { act, render, screen } from "@testing-library/react"; // Mock next/link jest.mock("next/link", () => { // eslint-disable-next-line react/display-name return ({ children, href, target, ...rest }: any) => ( {children} ); }); describe("AnchoredBottomBanner", () => { beforeEach(() => { jest.useFakeTimers(); jest.setSystemTime(new Date("2026-05-19T12:00:00Z")); }); afterEach(() => { jest.useRealTimers(); }); it("renders with default anchorId", () => { const { container } = render(); expect(container.querySelector("#anchored-banner")).toBeInTheDocument(); }); it("renders with custom anchorId", () => { const { container } = render( ); expect(container.querySelector("#custom-banner")).toBeInTheDocument(); }); it("renders ctaButtonLabel", () => { render(); expect(screen.getByText("Shop now")).toBeInTheDocument(); }); it("renders ctaSuffixText", () => { render(); expect(screen.getByText("Limited time")).toBeInTheDocument(); }); it("renders iconName", () => { render(); expect(screen.getByText("local_offer")).toBeInTheDocument(); }); it("does not render icon when iconName is not provided", () => { const { container } = render(); expect( container.querySelector(".material-symbols-rounded") ).not.toBeInTheDocument(); }); it("uses ctaButtonLink in the link href", () => { render(); const link = screen.getByRole("link"); expect(link).toHaveAttribute("href", "/offers"); }); it("defaults link href to # when ctaButtonLink is not provided", () => { render(); const link = screen.getByRole("link"); expect(link).toHaveAttribute("href", "#"); }); it("uses ctaButtonTarget for link target", () => { render(); const link = screen.getByRole("link"); expect(link).toHaveAttribute("target", "_blank"); }); it("defaults link target to _self", () => { render(); const link = screen.getByRole("link"); expect(link).toHaveAttribute("target", "_self"); }); // Background colors it("applies yellow background by default (no backgroundColor)", () => { const { container } = render(); const div = container.querySelector(".bg-bg-fill-brand-accent"); expect(div).toBeInTheDocument(); }); it("applies navy background", () => { const { container } = render( ); expect(container.querySelector(".bg-bg-fill-inverse")).toBeInTheDocument(); }); it("applies green background", () => { const { container } = render( ); expect(container.querySelector(".bg-bg-fill-success")).toBeInTheDocument(); }); it("applies blue background", () => { const { container } = render( ); expect( container.querySelector(".bg-bg-fill-brand-supporting") ).toBeInTheDocument(); }); it("applies purple background", () => { const { container } = render( ); expect( container.querySelector(".bg-bg-fill-brand-tertiary") ).toBeInTheDocument(); }); it("applies white background", () => { const { container } = render( ); expect(container.querySelector(".bg-white")).toBeInTheDocument(); }); // Text colors it("uses dark text for yellow/white/no background", () => { const { container } = render( ); expect(container.querySelector(".text-text-primary")).toBeInTheDocument(); }); it("uses white text for dark backgrounds", () => { const { container } = render( ); expect(container.querySelector(".text-white")).toBeInTheDocument(); }); // Box shadow it("uses default shadow when boxShadow is not provided", () => { const { container } = render(); const div = container.querySelector( '[class*="shadow-[0_-4px_10px_rgba(0,0,0,0.1)]"]' ); expect(div).toBeInTheDocument(); }); it("uses boxShadow value when provided", () => { const { container } = render( ); const div = container.querySelector( '[class*="shadow-[0_-4px_10px_rgba(0,0,0,0.1)]"]' ); expect(div).not.toBeInTheDocument(); }); // Countdown timer it("shows countdown when timer is enabled and within range", () => { const end = new Date("2026-05-19T13:00:00Z").toISOString(); render( ); // 1 hour remaining: 01H : 00M : 00 expect(screen.getByText(/01H : 00M : 00/)).toBeInTheDocument(); }); it("does not show countdown when timer is disabled", () => { const end = new Date("2026-05-19T13:00:00Z").toISOString(); render(); expect(screen.queryByText(/H :/)).not.toBeInTheDocument(); }); it("does not show countdown before start time", () => { const start = new Date("2026-05-19T14:00:00Z").toISOString(); const end = new Date("2026-05-19T15:00:00Z").toISOString(); render( ); expect(screen.queryByText(/H :/)).not.toBeInTheDocument(); }); it("does not show countdown after end time", () => { const end = new Date("2026-05-19T11:00:00Z").toISOString(); render( ); expect(screen.queryByText(/H :/)).not.toBeInTheDocument(); }); it("updates countdown every second", () => { const end = new Date("2026-05-19T12:00:10Z").toISOString(); render( ); expect(screen.getByText(/00H : 00M : 10/)).toBeInTheDocument(); act(() => { jest.advanceTimersByTime(1000); }); expect(screen.getByText(/00H : 00M : 09/)).toBeInTheDocument(); }); it("clears interval when countdown reaches zero", () => { const end = new Date("2026-05-19T12:00:01Z").toISOString(); render( ); act(() => { jest.advanceTimersByTime(2000); }); // After expiry, countdown should not show expect(screen.queryByText(/H :/)).not.toBeInTheDocument(); }); it("handles invalid countdownEndDateTime gracefully", () => { const consoleSpy = jest.spyOn(console, "error").mockImplementation(() => { /* noop */ }); render( ); expect(screen.queryByText(/H :/)).not.toBeInTheDocument(); consoleSpy.mockRestore(); }); it("handles invalid countdownStartDateTime gracefully", () => { const consoleSpy = jest.spyOn(console, "error").mockImplementation(() => { /* noop */ }); const end = new Date("2026-05-19T13:00:00Z").toISOString(); render( ); expect(screen.queryByText(/H :/)).not.toBeInTheDocument(); consoleSpy.mockRestore(); }); it("logs error when start is after end", () => { const consoleSpy = jest.spyOn(console, "error").mockImplementation(() => { /* noop */ }); const start = new Date("2026-05-19T15:00:00Z").toISOString(); const end = new Date("2026-05-19T13:00:00Z").toISOString(); render( ); expect(consoleSpy).toHaveBeenCalledWith( "Invalid countdown range: start must be before end", expect.any(Object) ); consoleSpy.mockRestore(); }); });