All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 13m12s
174 lines
5.9 KiB
TypeScript
174 lines
5.9 KiB
TypeScript
// src/hooks/useAppInitialization.test.tsx
|
|
import { renderHook, waitFor } from '@testing-library/react';
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { MemoryRouter, useNavigate } from 'react-router-dom';
|
|
import { useAppInitialization } from './useAppInitialization';
|
|
import { useAuth } from './useAuth';
|
|
import { useModal } from './useModal';
|
|
import { createMockUserProfile } from '../tests/utils/mockFactories';
|
|
|
|
// Mock dependencies
|
|
vi.mock('./useAuth');
|
|
vi.mock('./useModal');
|
|
vi.mock('react-router-dom', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('react-router-dom')>();
|
|
return {
|
|
...actual,
|
|
useNavigate: vi.fn(),
|
|
};
|
|
});
|
|
vi.mock('../services/logger.client');
|
|
vi.mock('../config', () => ({
|
|
default: {
|
|
app: { version: '1.0.1' },
|
|
},
|
|
}));
|
|
|
|
const mockedUseAuth = vi.mocked(useAuth);
|
|
const mockedUseModal = vi.mocked(useModal);
|
|
const mockedUseNavigate = vi.mocked(useNavigate);
|
|
|
|
const mockLogin = vi.fn().mockResolvedValue(undefined);
|
|
const mockNavigate = vi.fn();
|
|
const mockOpenModal = vi.fn();
|
|
|
|
// Wrapper with MemoryRouter is needed because the hook uses useLocation and useNavigate
|
|
const wrapper = ({
|
|
children,
|
|
initialEntries = ['/'],
|
|
}: {
|
|
children: React.ReactNode;
|
|
initialEntries?: string[];
|
|
}) => <MemoryRouter initialEntries={initialEntries}>{children}</MemoryRouter>;
|
|
|
|
describe('useAppInitialization Hook', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockedUseNavigate.mockReturnValue(mockNavigate);
|
|
mockedUseAuth.mockReturnValue({
|
|
userProfile: null,
|
|
login: mockLogin,
|
|
authStatus: 'SIGNED_OUT',
|
|
isLoading: false,
|
|
logout: vi.fn(),
|
|
updateProfile: vi.fn(),
|
|
});
|
|
mockedUseModal.mockReturnValue({
|
|
openModal: mockOpenModal,
|
|
closeModal: vi.fn(),
|
|
isModalOpen: vi.fn(),
|
|
});
|
|
// Mock localStorage
|
|
Object.defineProperty(window, 'localStorage', {
|
|
value: {
|
|
getItem: vi.fn(),
|
|
setItem: vi.fn(),
|
|
removeItem: vi.fn(),
|
|
clear: vi.fn(),
|
|
},
|
|
writable: true,
|
|
});
|
|
// Mock matchMedia
|
|
Object.defineProperty(window, 'matchMedia', {
|
|
value: vi.fn().mockImplementation((query) => ({
|
|
matches: false, // default to light mode
|
|
})),
|
|
writable: true,
|
|
});
|
|
});
|
|
|
|
it('should call login when googleAuthToken is in URL', async () => {
|
|
renderHook(() => useAppInitialization(), {
|
|
wrapper: (props) => wrapper({ ...props, initialEntries: ['/?googleAuthToken=test-token'] }),
|
|
});
|
|
await waitFor(() => {
|
|
expect(mockLogin).toHaveBeenCalledWith('test-token');
|
|
});
|
|
});
|
|
|
|
it('should call login when githubAuthToken is in URL', async () => {
|
|
renderHook(() => useAppInitialization(), {
|
|
wrapper: (props) => wrapper({ ...props, initialEntries: ['/?githubAuthToken=test-token'] }),
|
|
});
|
|
await waitFor(() => {
|
|
expect(mockLogin).toHaveBeenCalledWith('test-token');
|
|
});
|
|
});
|
|
|
|
it('should call navigate to clean the URL after processing a token', async () => {
|
|
renderHook(() => useAppInitialization(), {
|
|
wrapper: (props) => wrapper({ ...props, initialEntries: ['/some/path?googleAuthToken=test-token'] }),
|
|
});
|
|
await waitFor(() => {
|
|
expect(mockLogin).toHaveBeenCalledWith('test-token');
|
|
});
|
|
expect(mockNavigate).toHaveBeenCalledWith('/some/path', { replace: true });
|
|
});
|
|
|
|
it("should open \"What's New\" modal if version is new", () => {
|
|
vi.spyOn(window.localStorage, 'getItem').mockReturnValue('1.0.0');
|
|
renderHook(() => useAppInitialization(), { wrapper });
|
|
expect(mockOpenModal).toHaveBeenCalledWith('whatsNew');
|
|
expect(window.localStorage.setItem).toHaveBeenCalledWith('lastSeenVersion', '1.0.1');
|
|
});
|
|
|
|
it("should not open \"What's New\" modal if version is the same", () => {
|
|
vi.spyOn(window.localStorage, 'getItem').mockReturnValue('1.0.1');
|
|
renderHook(() => useAppInitialization(), { wrapper });
|
|
expect(mockOpenModal).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should set dark mode from user profile', async () => {
|
|
mockedUseAuth.mockReturnValue({
|
|
...mockedUseAuth(),
|
|
userProfile: createMockUserProfile({ preferences: { darkMode: true } }),
|
|
});
|
|
const { result } = renderHook(() => useAppInitialization(), { wrapper });
|
|
await waitFor(() => {
|
|
expect(result.current.isDarkMode).toBe(true);
|
|
expect(document.documentElement.classList.contains('dark')).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('should set dark mode from localStorage', async () => {
|
|
vi.spyOn(window.localStorage, 'getItem').mockImplementation((key) =>
|
|
key === 'darkMode' ? 'true' : null,
|
|
);
|
|
const { result } = renderHook(() => useAppInitialization(), { wrapper });
|
|
await waitFor(() => {
|
|
expect(result.current.isDarkMode).toBe(true);
|
|
expect(document.documentElement.classList.contains('dark')).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('should set dark mode from system preference', async () => {
|
|
vi.spyOn(window, 'matchMedia').mockReturnValue({ matches: true } as any);
|
|
const { result } = renderHook(() => useAppInitialization(), { wrapper });
|
|
await waitFor(() => {
|
|
expect(result.current.isDarkMode).toBe(true);
|
|
expect(document.documentElement.classList.contains('dark')).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('should set unit system from user profile', async () => {
|
|
mockedUseAuth.mockReturnValue({
|
|
...mockedUseAuth(),
|
|
userProfile: createMockUserProfile({ preferences: { unitSystem: 'metric' } }),
|
|
});
|
|
const { result } = renderHook(() => useAppInitialization(), { wrapper });
|
|
await waitFor(() => {
|
|
expect(result.current.unitSystem).toBe('metric');
|
|
});
|
|
});
|
|
|
|
it('should set unit system from localStorage', async () => {
|
|
vi.spyOn(window.localStorage, 'getItem').mockImplementation((key) =>
|
|
key === 'unitSystem' ? 'metric' : null,
|
|
);
|
|
const { result } = renderHook(() => useAppInitialization(), { wrapper });
|
|
await waitFor(() => {
|
|
expect(result.current.unitSystem).toBe('metric');
|
|
});
|
|
});
|
|
});
|