import { cleanup, render, screen } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; import DashboardPage from '../../src/app/(app)/dashboard/page'; const { mockUseQuery } = vi.hoisted(() => ({ mockUseQuery: vi.fn(), })); vi.mock('convex/react', () => ({ useConvexAuth: () => ({ isAuthenticated: false }), useMutation: vi.fn(), useQuery: mockUseQuery, })); vi.mock('next/navigation', () => ({ useParams: vi.fn(), useRouter: () => ({ push: vi.fn(), replace: vi.fn() }), useSearchParams: () => new URLSearchParams(), })); describe('dashboard loading vs empty state', () => { afterEach(() => { cleanup(); mockUseQuery.mockReset(); }); it('shows skeletons while queries are loading (undefined)', () => { mockUseQuery.mockReturnValue(undefined); render(); expect(screen.getAllByTestId('list-skeleton').length).toBeGreaterThan(0); expect(screen.queryByText('No Spoons yet')).not.toBeInTheDocument(); }); it('shows empty states once loaded with no data ([])', () => { mockUseQuery.mockReturnValue([]); render(); expect(screen.queryByTestId('list-skeleton')).not.toBeInTheDocument(); expect(screen.getByText('No Spoons yet')).toBeInTheDocument(); }); it('renders data once loaded with items', () => { mockUseQuery.mockImplementation((_query, args) => { // spoons list has an empty args object ({}); recent syncRuns/threads pass limits. if (args && 'limit' in args) return []; return [ { _id: 'spoon-1', name: 'useSend', upstreamOwner: 'acme', upstreamRepo: 'app', status: 'active', syncCadence: 'daily', effectiveUpstreamAheadBy: 0, rawUpstreamAheadBy: 0, forkAheadBy: 0, ignoredUpstreamCount: 0, }, ]; }); render(); expect(screen.getByText('useSend')).toBeInTheDocument(); expect(screen.queryByTestId('list-skeleton')).not.toBeInTheDocument(); expect(screen.queryByText('No Spoons yet')).not.toBeInTheDocument(); }); });