// ABOUTME: Tests for OnboardingBanner component that prompts new users to complete setup. // ABOUTME: Covers Garmin connection, period date, and notification time banners. import { fireEvent, render, screen } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import { OnboardingBanner, type OnboardingStatus } from "./onboarding-banner"; // Mock next/link vi.mock("next/link", () => ({ default: ({ children, href, }: { children: React.ReactNode; href: string; }) => {children}, })); describe("OnboardingBanner", () => { const defaultStatus: OnboardingStatus = { garminConnected: true, lastPeriodDate: "2024-01-15", notificationTime: "07:00", }; describe("rendering conditions", () => { it("renders nothing when all setup is complete", () => { const { container } = render(); expect(container.firstChild).toBeNull(); }); it("renders Garmin banner when not connected", () => { render( , ); expect( screen.getByText(/Connect your Garmin to get started/i), ).toBeInTheDocument(); }); it("renders period banner when lastPeriodDate is null", () => { render( , ); expect( screen.getByText(/Set your last period date for accurate tracking/i), ).toBeInTheDocument(); }); it("renders period banner when lastPeriodDate is empty string", () => { render( , ); expect( screen.getByText(/Set your last period date for accurate tracking/i), ).toBeInTheDocument(); }); it("renders notification banner when notificationTime is missing", () => { render( , ); expect( screen.getByText(/Set your preferred notification time/i), ).toBeInTheDocument(); }); it("renders multiple banners when multiple items are missing", () => { render( , ); expect( screen.getByText(/Connect your Garmin to get started/i), ).toBeInTheDocument(); expect( screen.getByText(/Set your last period date for accurate tracking/i), ).toBeInTheDocument(); expect( screen.getByText(/Set your preferred notification time/i), ).toBeInTheDocument(); }); }); describe("Garmin banner", () => { it("links to /settings/garmin", () => { render( , ); const link = screen.getByRole("link", { name: /Connect/i }); expect(link).toHaveAttribute("href", "/settings/garmin"); }); it("has proper accessibility attributes", () => { render( , ); const banner = screen.getByRole("alert"); expect(banner).toBeInTheDocument(); }); }); describe("period date banner", () => { it("calls onSetPeriodDate callback when button is clicked", () => { const onSetPeriodDate = vi.fn(); render( , ); const button = screen.getByRole("button", { name: /Set date/i }); fireEvent.click(button); expect(onSetPeriodDate).toHaveBeenCalledTimes(1); }); it("shows 'Set date' button for period banner", () => { render( , ); expect( screen.getByRole("button", { name: /Set date/i }), ).toBeInTheDocument(); }); }); describe("notification time banner", () => { it("links to /settings", () => { render( , ); const link = screen.getByRole("link", { name: /Configure/i }); expect(link).toHaveAttribute("href", "/settings"); }); }); describe("styling", () => { it("uses alert styling with appropriate colors", () => { render( , ); const banner = screen.getByRole("alert"); expect(banner.className).toContain("border"); expect(banner.className).toContain("rounded"); }); it("has proper spacing between multiple banners", () => { const { container } = render( , ); const wrapper = container.firstChild as HTMLElement; expect(wrapper.className).toContain("space-y"); }); }); describe("icons", () => { it("shows watch icon for Garmin banner", () => { render( , ); expect(screen.getByText("⌚")).toBeInTheDocument(); }); it("shows calendar icon for period banner", () => { render( , ); expect(screen.getByText("📅")).toBeInTheDocument(); }); it("shows bell icon for notification banner", () => { render( , ); expect(screen.getByText("🔔")).toBeInTheDocument(); }); }); });