feat: add testing library dependencies and implement component tests
This commit is contained in:
committed by
Loic Coenen (aider)
parent
826069e8dd
commit
46168a70a8
@@ -1,11 +1,74 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { BerlinClock } from '../containers/BerlinClock';
|
||||
import { LampState } from '../types';
|
||||
import { useCurrentTime } from '../hooks/useCurrentTime';
|
||||
import * as utils from '../utils';
|
||||
|
||||
// Mock the hook and the converter
|
||||
vi.mock('../hooks/useCurrentTime');
|
||||
vi.mock('../utils');
|
||||
|
||||
const mockedUseCurrentTime = vi.mocked(useCurrentTime);
|
||||
const mockedDateToBerlin = vi.mocked(utils.dateToBerlin);
|
||||
|
||||
describe('BerlinClock', () => {
|
||||
it('renders when enabled', () => {
|
||||
render(<BerlinClock enabled={true} />);
|
||||
beforeEach(() => {
|
||||
// Set a fixed date for the hook
|
||||
mockedUseCurrentTime.mockReturnValue({
|
||||
currentTime: new Date(2025, 0, 1, 14, 30, 0),
|
||||
setCurrentTime: vi.fn(),
|
||||
});
|
||||
|
||||
// Provide a controlled BerlinClock output for that time
|
||||
mockedDateToBerlin.mockReturnValue({
|
||||
secondsRow: [LampState.Y],
|
||||
fiveHours: [LampState.R, LampState.R, LampState.R, LampState.O],
|
||||
oneHour: [LampState.R, LampState.R, LampState.R, LampState.R],
|
||||
fiveMinutes: [
|
||||
LampState.Y, LampState.Y, LampState.R,
|
||||
LampState.Y, LampState.Y, LampState.R,
|
||||
LampState.O, LampState.O, LampState.O,
|
||||
LampState.O, LampState.O,
|
||||
],
|
||||
oneMinute: [LampState.O, LampState.O, LampState.O, LampState.O],
|
||||
});
|
||||
});
|
||||
it('does not render when disabled', () => {
|
||||
render(<BerlinClock enabled={false} />);
|
||||
|
||||
it('renders nothing when disabled', () => {
|
||||
const { container } = render(<BerlinClock enabled={false} />);
|
||||
expect(container.innerHTML).toBe('');
|
||||
});
|
||||
|
||||
it('renders all five rows when enabled', () => {
|
||||
render(<BerlinClock enabled={true} />);
|
||||
|
||||
expect(screen.getByTestId('seconds-lamp')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('five-hours-row')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('single-hours-row')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('five-minutes-row')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('single-minutes-row')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the seconds lamp with correct aria-label', () => {
|
||||
render(<BerlinClock enabled={true} />);
|
||||
const secLamp = screen.getByTestId('seconds-lamp').querySelector('[data-testid="lamp-0"]');
|
||||
expect(secLamp).toHaveAttribute('aria-label', 'Y');
|
||||
});
|
||||
|
||||
it('renders the correct number of lamps in five-hours-row', () => {
|
||||
render(<BerlinClock enabled={true} />);
|
||||
const container = screen.getByTestId('five-hours-row').querySelector('[data-testid="lamp-line"]');
|
||||
expect(container?.children).toHaveLength(4);
|
||||
});
|
||||
|
||||
it('renders correct aria-labels in five-hours-row', () => {
|
||||
render(<BerlinClock enabled={true} />);
|
||||
const container = screen.getByTestId('five-hours-row').querySelector('[data-testid="lamp-line"]');
|
||||
const spans = container?.querySelectorAll('span');
|
||||
expect(spans).toHaveLength(4);
|
||||
const expected = ['R', 'R', 'R', 'O'];
|
||||
spans?.forEach((span, i) => {
|
||||
expect(span).toHaveAttribute('aria-label', expected[i]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { Lamp } from '../components/Lamp';
|
||||
import { LampState } from '../types';
|
||||
|
||||
describe('Lamp', () => {
|
||||
it('renders without crashing', () => {
|
||||
render(<Lamp state={LampState.Y} />);
|
||||
// If it doesn't throw, we're good.
|
||||
});
|
||||
|
||||
it.each([
|
||||
['Y' as LampState, 'Y'],
|
||||
['R' as LampState, 'R'],
|
||||
['O' as LampState, 'O'],
|
||||
['N' as LampState, 'N'],
|
||||
])('displays aria-label "%s" for state %s', (state, expectedLabel) => {
|
||||
const { container } = render(<Lamp state={state} />);
|
||||
// The root element should have the correct aria-label
|
||||
const lampElement = container.firstChild as HTMLElement;
|
||||
expect(lampElement).toHaveAttribute('aria-label', expectedLabel);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,33 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { LampLine } from '../components/LampLine';
|
||||
import { LampState } from '../types';
|
||||
|
||||
describe('LampLine', () => {
|
||||
it('renders correct number of lamps', () => {
|
||||
it('renders without crashing', () => {
|
||||
render(<LampLine states={[]} />);
|
||||
});
|
||||
|
||||
it('renders the correct number of lamps', () => {
|
||||
const states = [LampState.Y, LampState.R, LampState.O];
|
||||
render(<LampLine states={states} />);
|
||||
// TODO: assert number of child lamps
|
||||
const container = screen.getByTestId('lamp-line');
|
||||
expect(container.children).toHaveLength(states.length);
|
||||
});
|
||||
|
||||
it('displays correct aria-labels for each lamp', () => {
|
||||
const states = [LampState.Y, LampState.R, LampState.O, LampState.N];
|
||||
render(<LampLine states={states} />);
|
||||
const container = screen.getByTestId('lamp-line');
|
||||
const lampSpans = container.querySelectorAll('span');
|
||||
expect(lampSpans).toHaveLength(states.length);
|
||||
lampSpans.forEach((span, index) => {
|
||||
expect(span).toHaveAttribute('aria-label', states[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders no lamps when given an empty array', () => {
|
||||
render(<LampLine states={[]} />);
|
||||
const container = screen.getByTestId('lamp-line');
|
||||
expect(container.children).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,5 +5,5 @@ interface LampProps {
|
||||
}
|
||||
|
||||
export function Lamp({ state }: LampProps) {
|
||||
return null;
|
||||
return <span aria-label={state} />;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { LampState } from '../types';
|
||||
import { Lamp } from './Lamp';
|
||||
|
||||
interface LampLineProps {
|
||||
states: LampState[];
|
||||
}
|
||||
|
||||
export function LampLine({ states }: LampLineProps) {
|
||||
return null;
|
||||
return (
|
||||
<div data-testid="lamp-line">
|
||||
{states.map((state, index) => (
|
||||
<Lamp key={index} state={state} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { Lamp } from '../components/Lamp';
|
||||
import { LampLine } from '../components/LampLine';
|
||||
import { useCurrentTime } from '../hooks/useCurrentTime';
|
||||
import { dateToBerlin } from '../utils';
|
||||
@@ -10,6 +11,39 @@ interface BerlinClockProps {
|
||||
export function BerlinClock({ enabled }: BerlinClockProps) {
|
||||
const { currentTime } = useCurrentTime();
|
||||
const berlin = dateToBerlin(currentTime);
|
||||
// Will render four LampLine components
|
||||
return null;
|
||||
|
||||
if (!enabled) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Seconds lamp */}
|
||||
<div data-testid="seconds-lamp">
|
||||
{berlin.secondsRow.length > 0 && (
|
||||
<span data-testid="lamp-0" aria-label={berlin.secondsRow[0]}>
|
||||
{/* optional visual */}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Five hours row */}
|
||||
<div data-testid="five-hours-row">
|
||||
<LampLine states={berlin.fiveHours} />
|
||||
</div>
|
||||
|
||||
{/* Single hours row */}
|
||||
<div data-testid="single-hours-row">
|
||||
<LampLine states={berlin.oneHour} />
|
||||
</div>
|
||||
|
||||
{/* Five minutes row */}
|
||||
<div data-testid="five-minutes-row">
|
||||
<LampLine states={berlin.fiveMinutes} />
|
||||
</div>
|
||||
|
||||
{/* Single minutes row */}
|
||||
<div data-testid="single-minutes-row">
|
||||
<LampLine states={berlin.oneMinute} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
1
src/setupTests.ts
Normal file
1
src/setupTests.ts
Normal file
@@ -0,0 +1 @@
|
||||
import '@testing-library/jest-dom';
|
||||
Reference in New Issue
Block a user