import { describe, it, expect, vi, beforeEach } from 'vitest' import { render, screen, waitFor } from '@testing-library/react' import * as api from '@/services/api' import { App } from './App' // Mock API service - AuthProvider calls refreshToken on mount vi.mock('@/services/api') // Mock WorkspaceProvider to avoid workspace loading logic vi.mock('@/context/WorkspaceContext', () => ({ WorkspaceProvider: ({ children }: { children: React.ReactNode }) => (
{children}
), })) // Mock all page components to verify routing vi.mock('@/pages/Auth', () => ({ Auth: () =>
Auth Page
, })) vi.mock('@/pages/Calendar', () => ({ Calendar: () =>
Calendar Page
, })) vi.mock('@/pages/Dashboard', () => ({ Dashboard: () =>
Dashboard Page
, })) vi.mock('@/pages/Settings', () => ({ Settings: () =>
Settings Page
, })) vi.mock('@/pages/Tips', () => ({ Tips: () =>
Tips Page
, })) vi.mock('@/pages/DevFetch', () => ({ DevFetch: () =>
DevFetch Page
, })) // App supplies its own BrowserRouter. Replace it with a pass-through // so MemoryRouter can control routing in tests. vi.mock('react-router-dom', async () => { const actual = await vi.importActual('react-router-dom') return { ...actual, BrowserRouter: ({ children }: { children: React.ReactNode }) => <>{children}, } }) // Import MemoryRouter from the mocked module (gets the real one via spread) import { MemoryRouter } from 'react-router-dom' describe('App', () => { beforeEach(() => { vi.clearAllMocks() }) const renderWithRoute = (route: string = '/') => { return render( ) } describe('auth state handling', () => { it('shows loading state when auth is initializing', () => { // Mock refreshToken to never resolve (simulates loading state) vi.mocked(api.refreshToken).mockImplementation(() => new Promise(() => {})) renderWithRoute('/') expect(screen.getByText('Laddar…')).toBeInTheDocument() }) it('navigates to auth page when not authenticated', async () => { // Mock refreshToken to reject (no valid session) vi.mocked(api.refreshToken).mockRejectedValue(new Error('Unauthorized')) renderWithRoute('/') await waitFor(() => { expect(screen.getByTestId('auth-page')).toBeInTheDocument() }) }) it('shows dashboard when authenticated', async () => { // Mock refreshToken to return a token (user is authenticated) vi.mocked(api.refreshToken).mockResolvedValue({ access_token: 'test-token', token_type: 'bearer', expires_in: 3600, }) vi.mocked(api.setAccessToken).mockImplementation(() => {}) renderWithRoute('/') await waitFor(() => { expect(screen.getByTestId('dashboard-page')).toBeInTheDocument() }) }) }) describe('protected routes', () => { it('renders protected route when authenticated', async () => { // Test that calendar (another protected route) also works when authenticated vi.mocked(api.refreshToken).mockResolvedValue({ access_token: 'test-token', token_type: 'bearer', expires_in: 3600, }) vi.mocked(api.setAccessToken).mockImplementation(() => {}) renderWithRoute('/calendar') await waitFor(() => { expect(screen.getByTestId('calendar-page')).toBeInTheDocument() }) }) it('redirects to auth page when accessing protected route without authentication', async () => { // Even on a protected route like /calendar, unauthenticated users are sent to auth vi.mocked(api.refreshToken).mockRejectedValue(new Error('Unauthorized')) renderWithRoute('/calendar') await waitFor(() => { expect(screen.getByTestId('auth-page')).toBeInTheDocument() }) }) }) describe('app structure', () => { it('wraps app with AuthProvider and WorkspaceProvider', async () => { // Verify the provider wrappers are in place by checking that auth flow works // (both depend on these providers existing) vi.mocked(api.refreshToken).mockRejectedValue(new Error('Unauthorized')) renderWithRoute('/') await waitFor(() => { expect(screen.getByTestId('workspace-provider')).toBeInTheDocument() }) }) }) })