import type { ComponentProps, ReactNode } from 'react'; import { cleanup, fireEvent, render, screen } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { api } from '@spoon/backend/convex/_generated/api.js'; import { NotificationBell } from '../../src/components/layout/header/controls/notification-bell'; // Stable sentinels: the real Convex `api` proxy hands back a fresh reference on // every access, so `===` comparisons in the mocks below would never match. vi.mock('@spoon/backend/convex/_generated/api.js', () => ({ api: { notifications: { unreadCount: 'notifications:unreadCount', listMine: 'notifications:listMine', markRead: 'notifications:markRead', markAllRead: 'notifications:markAllRead', }, }, })); const { mockMarkAllRead, mockMarkRead, mockUseConvexAuth, mockUseQuery } = vi.hoisted(() => ({ mockMarkAllRead: vi.fn(), mockMarkRead: vi.fn(), mockUseConvexAuth: vi.fn(), mockUseQuery: vi.fn(), })); vi.mock('convex/react', () => ({ useConvexAuth: mockUseConvexAuth, useMutation: (reference: unknown) => reference === api.notifications.markAllRead ? mockMarkAllRead : mockMarkRead, useQuery: mockUseQuery, })); vi.mock('next/navigation', () => ({ useRouter: () => ({ push: vi.fn(), replace: vi.fn() }), })); vi.mock('sonner', () => ({ toast: { error: vi.fn(), success: vi.fn() }, })); // Render the dropdown primitives as pass-through wrappers so the menu content // is deterministically present under jsdom (Radix pointer behavior is flaky // here). We are exercising NotificationBell's logic, not Radix. vi.mock('@spoon/ui', () => ({ Button: (props: ComponentProps<'button'>) =>