import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; vi.mock('./api/sessionApi', () => ({ getSessionMe: vi.fn(), loginSession: vi.fn(), refreshSession: vi.fn(), logoutSession: vi.fn() })); vi.mock('./api/dashboardApi', () => ({ loadDashboard: vi.fn(), loadRecentChanges: vi.fn(), setServiceWindow: vi.fn() })); import { loadDashboard, loadRecentChanges, setServiceWindow } from './api/dashboardApi'; import { ApiError } from './api/client'; import { getSessionMe } from './api/sessionApi'; import App from './App'; describe('Restaurant Admin App', () => { beforeEach(() => { vi.mocked(loadDashboard).mockReset(); vi.mocked(loadRecentChanges).mockReset(); vi.mocked(setServiceWindow).mockReset(); vi.mocked(getSessionMe).mockReset(); window.__APP_CONFIG__ = { API_BASE_URL: 'http://localhost:8080', THALOS_AUTH_BASE_URL: 'https://auth.dream-views.com', THALOS_DEFAULT_RETURN_URL: 'https://restaurant-admin-demo.dream-views.com/config', THALOS_DEFAULT_TENANT_ID: 'demo-tenant' }; window.history.pushState({}, '', '/config'); }); it('shows central login action when session is missing', async () => { vi.mocked(getSessionMe).mockResolvedValue({ isAuthenticated: false, subjectId: '', tenantId: '', provider: 0 }); render(); await waitFor(() => expect(screen.getByRole('link', { name: 'Continue with Google' })).toBeInTheDocument()); const link = screen.getByRole('link', { name: 'Continue with Google' }) as HTMLAnchorElement; expect(link.href).toContain('/api/identity/oidc/google/start'); expect(link.href).toContain('tenantId=demo-tenant'); }); it('loads admin config and recent changes for authenticated users', async () => { vi.mocked(getSessionMe).mockResolvedValue({ isAuthenticated: true, subjectId: 'demo-user', tenantId: 'demo-tenant', provider: 2 }); vi.mocked(loadDashboard).mockResolvedValue({ contextId: 'demo-context', summary: 'configured', version: 'v2', featureFlags: [{ key: 'pos.closeout.preview', enabled: true }], serviceWindows: [{ day: 1, openAt: '08:00:00', closeAt: '22:00:00', isClosed: false }], recentChanges: [] }); vi.mocked(loadRecentChanges).mockResolvedValue({ contextId: 'demo-context', summary: 'changes', version: 'v2', recentChanges: [ { changeId: 'CFG-100', category: 'feature-flag', description: 'Enabled POS closeout preview mode.', updatedBy: 'ops-lead', updatedAtUtc: '2026-03-31T12:00:00Z' } ] }); render(); await waitFor(() => expect(screen.getByRole('button', { name: /Load Config/ })).toBeInTheDocument()); fireEvent.click(screen.getByRole('button', { name: /Load Config/ })); fireEvent.click(screen.getByRole('button', { name: /Load Recent Changes/ })); await waitFor(() => expect(loadDashboard).toHaveBeenCalledWith('demo-context')); await waitFor(() => expect(loadRecentChanges).toHaveBeenCalledWith('demo-context')); expect(await screen.findByText('pos.closeout.preview')).toBeInTheDocument(); expect(await screen.findByText('CFG-100')).toBeInTheDocument(); }); it('applies service window and shows the latest applied snapshot', async () => { vi.mocked(getSessionMe).mockResolvedValue({ isAuthenticated: true, subjectId: 'demo-user', tenantId: 'demo-tenant', provider: 2 }); vi.mocked(loadDashboard).mockResolvedValue({ contextId: 'demo-context', summary: 'configured', version: 'v2', featureFlags: [], serviceWindows: [], recentChanges: [] }); vi.mocked(loadRecentChanges).mockResolvedValue({ contextId: 'demo-context', summary: 'changes', version: 'v2', recentChanges: [] }); vi.mocked(setServiceWindow).mockResolvedValue({ contextId: 'demo-context', applied: true, message: 'Service window updated by admin-operator.', serviceWindow: { day: 1, openAt: '08:00:00', closeAt: '22:00:00', isClosed: false } }); render(); await waitFor(() => expect(screen.getByText('Service Window')).toBeInTheDocument()); fireEvent.click(screen.getByText('Service Window')); fireEvent.click(screen.getByRole('button', { name: 'Apply Service Window' })); await waitFor(() => expect(setServiceWindow).toHaveBeenCalledTimes(1)); expect(await screen.findAllByText('Service window updated by admin-operator.')).toHaveLength(2); expect((await screen.findAllByText('Monday')).length).toBeGreaterThanOrEqual(2); }); it('shows reauthentication guidance when the session expires', async () => { vi.mocked(getSessionMe) .mockResolvedValueOnce({ isAuthenticated: true, subjectId: 'demo-user', tenantId: 'demo-tenant', provider: 2 }) .mockResolvedValueOnce({ isAuthenticated: false, subjectId: '', tenantId: '', provider: 2 }); vi.mocked(loadDashboard).mockRejectedValue(new ApiError(401, 'Unauthorized request.', 'unauthorized', 'corr-401')); render(); await waitFor(() => expect(screen.getByRole('button', { name: /Load Config/ })).toBeInTheDocument()); fireEvent.click(screen.getByRole('button', { name: /Load Config/ })); await waitFor(() => expect(screen.getByText('Authentication Required')).toBeInTheDocument()); expect(screen.getByText('Session expired')).toBeInTheDocument(); }); });