// @vitest-environment jsdom import { describe, it, expect, vi, beforeEach } from 'vitest' import { render, screen, fireEvent } from '@testing-library/react' import '@testing-library/jest-dom' import React from 'react' const pushMock = vi.fn() const refreshMock = vi.fn() const pathnameMock = vi.fn(() => '/products/p1/sprint') vi.mock('next/navigation', () => ({ useRouter: () => ({ push: pushMock, refresh: refreshMock }), usePathname: () => pathnameMock(), })) vi.mock('@/actions/active-sprint', () => ({ setActiveSprintAction: vi.fn(), })) vi.mock('sonner', () => ({ toast: { error: vi.fn(), success: vi.fn() }, })) const isDemoMock = { value: false } vi.mock('@/stores/user-settings/store', () => ({ useUserSettingsStore: (selector: (s: { context: { isDemo: boolean } }) => unknown) => selector({ context: { isDemo: isDemoMock.value } }), })) vi.mock('@/components/ui/dropdown-menu', () => { type Props = { children?: React.ReactNode; onClick?: () => void; className?: string } const PassThrough = ({ children }: Props) => <>{children} return { DropdownMenu: PassThrough, DropdownMenuTrigger: PassThrough, DropdownMenuContent: PassThrough, DropdownMenuItem: ({ children, onClick, className }: Props) => ( ), DropdownMenuSeparator: () => null, } }) vi.mock('@/components/ui/tooltip', () => { type Props = { children?: React.ReactNode } const PassThrough = ({ children }: Props) => <>{children} return { Tooltip: PassThrough, TooltipContent: PassThrough, TooltipProvider: PassThrough, TooltipTrigger: PassThrough, } }) import { setActiveSprintAction } from '@/actions/active-sprint' import { toast } from 'sonner' import { SprintSwitcher } from '@/components/shared/sprint-switcher' const actionMock = setActiveSprintAction as unknown as ReturnType const toastError = toast.error as unknown as ReturnType const toastSuccess = toast.success as unknown as ReturnType const sprints = [ { id: 's1', code: 'SP-1', sprint_goal: 'Goal 1', status: 'open' as const }, { id: 's2', code: 'SP-2', sprint_goal: 'Goal 2', status: 'open' as const }, ] beforeEach(() => { vi.clearAllMocks() isDemoMock.value = false actionMock.mockResolvedValue({ success: true }) pathnameMock.mockReturnValue('/products/p1/sprint') }) describe('SprintSwitcher', () => { it('demo: clicking another sprint navigates via router.push without calling the action', () => { isDemoMock.value = true render( , ) fireEvent.click(screen.getByText('Goal 2')) expect(pushMock).toHaveBeenCalledWith('/products/p1/sprint/s2') expect(actionMock).not.toHaveBeenCalled() expect(toastError).not.toHaveBeenCalled() expect(toastSuccess).not.toHaveBeenCalled() }) it('non-demo: clicking another sprint calls setActiveSprintAction', async () => { isDemoMock.value = false render( , ) fireEvent.click(screen.getByText('Goal 2')) // Wait microtask for the transition to flush. await Promise.resolve() expect(actionMock).toHaveBeenCalledWith('p1', 's2') }) it('clicking the already-active sprint does nothing', () => { isDemoMock.value = true render( , ) fireEvent.click(screen.getByText('Goal 1')) expect(pushMock).not.toHaveBeenCalled() expect(actionMock).not.toHaveBeenCalled() }) })