init test suite

This commit is contained in:
geoffsee
2025-05-29 21:31:01 -04:00
committed by Geoff Seemueller
parent 84b0ea0307
commit f07c19dae8
9 changed files with 504 additions and 123 deletions

View File

@@ -15,6 +15,7 @@ export function ThemeSelectionOptions() {
children.push(
<IconButton
as="div"
role="button"
key={theme.name}
onClick={() => userOptionsStore.selectTheme(theme.name)}
size="xs"

View File

@@ -0,0 +1,78 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { ThemeSelectionOptions } from '../ThemeSelection';
import userOptionsStore from '../../stores/UserOptionsStore';
import * as MobileContext from '../contexts/MobileContext';
// Mock dependencies
vi.mock('../../layout/theme/color-themes', () => ({
getColorThemes: () => [
{
name: 'light',
colors: {
background: { primary: '#ffffff', secondary: '#f0f0f0' },
text: { secondary: '#333333' }
}
},
{
name: 'dark',
colors: {
background: { primary: '#121212', secondary: '#1e1e1e' },
text: { secondary: '#e0e0e0' }
}
}
]
}));
vi.mock('../../stores/UserOptionsStore', () => ({
default: {
selectTheme: vi.fn()
}
}));
vi.mock('../toolbar/Toolbar', () => ({
toolbarButtonZIndex: 100
}));
describe('ThemeSelectionOptions', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('renders theme options for desktop view', () => {
// Mock useIsMobile to return false (desktop view)
vi.spyOn(MobileContext, 'useIsMobile').mockReturnValue(false);
render(<ThemeSelectionOptions />);
// Should render 2 theme buttons (from our mock)
const buttons = screen.getAllByRole("button")
expect(buttons).toHaveLength(2);
});
it('renders theme options for mobile view', () => {
// Mock useIsMobile to return true (mobile view)
vi.spyOn(MobileContext, 'useIsMobile').mockReturnValue(true);
render(<ThemeSelectionOptions />);
// Should still render 2 theme buttons
const buttons = screen.getAllByRole('button');
expect(buttons).toHaveLength(2);
});
it('calls selectTheme when a theme button is clicked', () => {
vi.spyOn(MobileContext, 'useIsMobile').mockReturnValue(false);
render(<ThemeSelectionOptions />);
const buttons = screen.getAllByRole('button');
fireEvent.click(buttons[0]); // Click the first theme button (light)
// Verify that selectTheme was called with the correct theme name
expect(userOptionsStore.selectTheme).toHaveBeenCalledWith('light');
fireEvent.click(buttons[1]); // Click the second theme button (dark)
expect(userOptionsStore.selectTheme).toHaveBeenCalledWith('dark');
});
});

View File

@@ -0,0 +1,40 @@
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import WelcomeHomeMessage from '../WelcomeHome';
import { welcome_home_text, welcome_home_tip } from '../../static-data/welcome_home_text';
import { renderMarkdown } from '../markdown/MarkdownComponent';
// Mock the renderMarkdown function
vi.mock('../markdown/MarkdownComponent', () => ({
renderMarkdown: vi.fn((text) => `Rendered: ${text}`),
}));
describe('WelcomeHomeMessage', () => {
it('renders correctly when visible', () => {
render(<WelcomeHomeMessage visible={true} />);
// Check if the rendered markdown content is in the document
expect(screen.getByText(`Rendered: ${welcome_home_text}`)).toBeInTheDocument();
expect(screen.getByText(`Rendered: ${welcome_home_tip}`)).toBeInTheDocument();
// Verify that renderMarkdown was called with the correct arguments
expect(renderMarkdown).toHaveBeenCalledWith(welcome_home_text);
expect(renderMarkdown).toHaveBeenCalledWith(welcome_home_tip);
});
it('applies animation variants based on visible prop', () => {
const { rerender } = render(<WelcomeHomeMessage visible={true} />);
// When visible is true, the component should have the visible animation state
// Since we've mocked framer-motion, we can't directly test the animation state
// But we can verify that the component renders the content
expect(screen.getByText(`Rendered: ${welcome_home_text}`)).toBeInTheDocument();
// Re-render with visible=false
rerender(<WelcomeHomeMessage visible={false} />);
// Content should still be in the document even when not visible
// (since we've mocked the animations)
expect(screen.getByText(`Rendered: ${welcome_home_text}`)).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,99 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook } from '@testing-library/react';
import usePageLoaded from '../usePageLoaded';
describe('usePageLoaded', () => {
const callback = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
// Reset event listeners
vi.stubGlobal('addEventListener', vi.fn());
vi.stubGlobal('removeEventListener', vi.fn());
});
it('calls callback immediately if document is already loaded', () => {
// Mock document.readyState to be "complete"
Object.defineProperty(document, 'readyState', {
configurable: true,
get: () => 'complete'
});
const { result } = renderHook(() => usePageLoaded(callback));
// The hook should return true
expect(result.current).toBe(true);
// Callback should be called immediately
expect(callback).toHaveBeenCalledTimes(1);
// No event listener should be added
expect(window.addEventListener).not.toHaveBeenCalled();
});
it('adds event listener if document is not loaded yet', () => {
// Mock document.readyState to be "loading"
Object.defineProperty(document, 'readyState', {
configurable: true,
get: () => 'loading'
});
const { result } = renderHook(() => usePageLoaded(callback));
// The hook should return false initially
expect(result.current).toBe(false);
// Callback should not be called yet
expect(callback).not.toHaveBeenCalled();
// Event listener should be added
expect(window.addEventListener).toHaveBeenCalledWith('load', expect.any(Function));
});
it('cleans up event listener on unmount', () => {
// Mock document.readyState to be "loading"
Object.defineProperty(document, 'readyState', {
configurable: true,
get: () => 'loading'
});
const { unmount } = renderHook(() => usePageLoaded(callback));
// Unmount the hook
unmount();
// Event listener should be removed
expect(window.removeEventListener).toHaveBeenCalledWith('load', expect.any(Function));
});
it('calls callback and updates state when load event fires', () => {
// Mock document.readyState to be "loading"
Object.defineProperty(document, 'readyState', {
configurable: true,
get: () => 'loading'
});
// Capture the event handler
let loadHandler: Function;
vi.stubGlobal('addEventListener', vi.fn((event, handler) => {
if (event === 'load') {
loadHandler = handler;
}
}));
const { result } = renderHook(() => usePageLoaded(callback));
// Initially, isLoaded should be false
expect(result.current).toBe(false);
// Simulate the load event
loadHandler();
// Now the callback should have been called
expect(callback).toHaveBeenCalledTimes(1);
// And isLoaded should be updated to true
// Note: We need to use rerender or waitFor in a real test to see this update
// For simplicity, we're just testing the callback was called
});
});

18
src/test/setup.ts Normal file
View File

@@ -0,0 +1,18 @@
// Vitest setup file
import '@testing-library/jest-dom';
import { vi } from 'vitest';
import React from 'react';
// Mock for framer-motion to avoid animation-related issues in tests
vi.mock('framer-motion', () => ({
motion: {
div: (props: any) => React.createElement('div', props, props.children),
},
AnimatePresence: (props: any) => React.createElement(React.Fragment, null, props.children),
}));
// Mock for static data if needed
vi.mock('../static-data/welcome_home_text', () => ({
welcome_home_text: 'Welcome home text mock',
welcome_home_tip: 'Welcome home tip mock',
}));