// AuthProvider.test.tsx import React, { useContext, act } from 'react'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { render, waitFor, cleanup } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import AuthProvider from '../../context/AuthProvider'; import AuthContext, { AuthContextData } from '../../context/AuthContext'; import api from '../../api-service/api'; import { API_TOKEN, USER_DATA } from '../../app-constants/app-constants'; // Mock the API service methods. vi.mock('../../api-service/api'); // Create a helper component to consume AuthContext for testing. const ConsumerComponent: React.FC = () => { const { signed, loading, user, signIn, register, signOut, updateUser, checkCurrentAuthUser } = useContext(AuthContext); return (
{signed ? 'true' : 'false'}
{loading ? 'true' : 'false'}
{user ? user.name : 'none'}
); }; describe('AuthProvider', () => { // Reset DOM and mocks for each test. beforeEach(() => { localStorage.clear(); vi.clearAllMocks(); }); afterEach(() => { cleanup(); }); it('should render the default context values', async () => { const { getByTestId } = render( ); await waitFor(() => { expect(getByTestId('signed').textContent).toBe('false'); expect(getByTestId('user').textContent).toBe('none'); }); }); it('should set loading to false after initial auth check with no token', async () => { // No token in localStorage, so fetchCurrentSession returns immediately. const { getByTestId } = render( ); await waitFor(() => expect(getByTestId('loading').textContent).toBe('false') ); expect(getByTestId('signed').textContent).toBe('false'); }); it('should set loading to false and signed to true after successful initial auth check', async () => { const fakeResponse = { token: 'refresh-token', userId: '789', name: 'Refreshed User', email: 'refreshed@example.com', admin: false, createdAt: new Date().toISOString(), gravatarImageUrl: 'http://dummyimage.com' }; vi.spyOn(api, 'getJSON').mockResolvedValue(fakeResponse); localStorage.setItem(API_TOKEN, 'dummy'); const { getByTestId } = render( ); await waitFor(() => expect(getByTestId('loading').textContent).toBe('false') ); expect(getByTestId('signed').textContent).toBe('true'); }); it('should sign in a user successfully', async () => { // Create a fake sign-in response. const fakeResponse = { userId: '123', name: 'Test User', email: 'test@example.com', admin: false, createdAt: new Date(), token: 'dummy-token', gravatarImageUrl: 'http://dummyimage.com' }; vi.spyOn(api, 'postJSON').mockResolvedValue(fakeResponse); const { getByTestId } = render( ); await userEvent.click(getByTestId('signIn')); await waitFor(() => expect(getByTestId('signed').textContent).toBe('true') ); expect(getByTestId('user').textContent).toBe('Test User'); // LocalStorage should have API_TOKEN and USER_DATA set. expect(localStorage.getItem(API_TOKEN)).toBe('dummy-token'); expect(localStorage.getItem(USER_DATA)).not.toBeNull(); }); it('should sign out a user', async () => { // Pre-populate localStorage to simulate a signed-in state. localStorage.setItem(API_TOKEN, 'dummy-token'); localStorage.setItem( USER_DATA, JSON.stringify({ userId: '123', name: 'Test User', email: 'test@example.com', admin: false, createdAt: new Date(), gravatarImageUrl: 'http://dummyimage.com' }) ); const fakeResponse = { userId: '123', name: 'Test User', email: 'test@example.com', admin: false, createdAt: new Date(), token: 'dummy-token', gravatarImageUrl: 'http://dummyimage.com' }; vi.spyOn(api, 'postJSON').mockResolvedValue(fakeResponse); const { getByTestId } = render( ); // Make sure that the context initially renders with the signed user by triggering a signIn. await userEvent.click(getByTestId('signIn')); await waitFor(() => expect(getByTestId('signed').textContent).toBe('true') ); // Now sign out await userEvent.click(getByTestId('sign-out-btn')); await waitFor(() => expect(getByTestId('signed').textContent).toBe('false') ); expect(getByTestId('user').textContent).toBe('none'); // LocalStorage items should be removed. expect(localStorage.getItem(API_TOKEN)).toBeNull(); expect(localStorage.getItem(USER_DATA)).toBeNull(); }); it('should update user in context and localStorage', async () => { const { getByTestId } = render( ); await userEvent.click(getByTestId('updateUser')); await waitFor(() => { expect(getByTestId('user').textContent).toBe('Updated User') const savedUser = localStorage.getItem(USER_DATA); expect(savedUser).not.toBeNull(); if (savedUser) { const parsedUser = JSON.parse(savedUser); expect(parsedUser.name).toBe('Updated User'); } }); }); it('should call fetchCurrentSession when checking current auth user', async () => { const fakeResponse = { token: 'refresh-token', userId: '789', name: 'Refreshed User', email: 'refreshed@example.com', admin: false, createdAt: new Date().toISOString(), gravatarImageUrl: 'http://dummyimage.com' }; vi.spyOn(api, 'getJSON').mockResolvedValue(fakeResponse); // Store API_TOKEN so that fetchCurrentSession runs the refresh logic. localStorage.setItem(API_TOKEN, 'dummy'); const user = userEvent.setup(); let getByTestIdFunction; await act(async () => { const { getByTestId } = render( ); getByTestIdFunction = getByTestId; }); // Wait for any initial renders to complete await waitFor(() => expect(getByTestIdFunction('checkCurrentAuthUser')).toBeDefined()); await user.click(getByTestIdFunction('checkCurrentAuthUser')); await waitFor(() => expect(localStorage.getItem(API_TOKEN)).toBe('refresh-token') ); }); });