test(frontend): add specs for Tabs, AlertBanner, BackLink, useHashTab
4 new spec files adding 21 tests (7+6+4+4). Each component spec includes brutalism invariant assertions (no rounded-md, no transition-*, no shadow-*). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
60
frontend/tests/components/Tabs.test.tsx
Normal file
60
frontend/tests/components/Tabs.test.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
|
||||
const ITEMS = [
|
||||
{ id: 'schedule', label: 'Schedule' },
|
||||
{ id: 'description', label: 'Description' },
|
||||
{ id: 'simulations', label: 'Simulations', count: 5 },
|
||||
];
|
||||
|
||||
describe('Tabs', () => {
|
||||
it('renders all tab labels', () => {
|
||||
render(<Tabs items={ITEMS} activeId="schedule" onChange={vi.fn()} />);
|
||||
expect(screen.getByText('Schedule')).toBeInTheDocument();
|
||||
expect(screen.getByText('Description')).toBeInTheDocument();
|
||||
expect(screen.getByText('Simulations')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('marks the active tab with aria-selected=true', () => {
|
||||
render(<Tabs items={ITEMS} activeId="description" onChange={vi.fn()} />);
|
||||
const descBtn = screen.getByRole('tab', { name: /Description/i });
|
||||
expect(descBtn).toHaveAttribute('aria-selected', 'true');
|
||||
const schedBtn = screen.getByRole('tab', { name: /Schedule/i });
|
||||
expect(schedBtn).toHaveAttribute('aria-selected', 'false');
|
||||
});
|
||||
|
||||
it('applies tab-underline-active class to the active tab', () => {
|
||||
render(<Tabs items={ITEMS} activeId="schedule" onChange={vi.fn()} />);
|
||||
const activeBtn = screen.getByRole('tab', { name: /Schedule/i });
|
||||
expect(activeBtn).toHaveClass('tab-underline-active');
|
||||
});
|
||||
|
||||
it('renders count pill for the simulations tab', () => {
|
||||
render(<Tabs items={ITEMS} activeId="schedule" onChange={vi.fn()} />);
|
||||
const pill = screen.getByText('5');
|
||||
expect(pill).toHaveClass('tab-count-pill');
|
||||
});
|
||||
|
||||
it('applies tab-count-pill-active when the tab with count is active', () => {
|
||||
render(<Tabs items={ITEMS} activeId="simulations" onChange={vi.fn()} />);
|
||||
const pill = screen.getByText('5');
|
||||
expect(pill).toHaveClass('tab-count-pill-active');
|
||||
});
|
||||
|
||||
it('calls onChange with the tab id when clicked', () => {
|
||||
const onChange = vi.fn();
|
||||
render(<Tabs items={ITEMS} activeId="schedule" onChange={onChange} />);
|
||||
fireEvent.click(screen.getByRole('tab', { name: /Description/i }));
|
||||
expect(onChange).toHaveBeenCalledWith('description');
|
||||
});
|
||||
|
||||
it('has no rounded-md, transition-*, or shadow-* on tab buttons (brutalism)', () => {
|
||||
render(<Tabs items={ITEMS} activeId="schedule" onChange={vi.fn()} />);
|
||||
for (const btn of screen.getAllByRole('tab')) {
|
||||
expect(btn).not.toHaveClass('rounded-md');
|
||||
expect(btn.className).not.toMatch(/transition-/);
|
||||
expect(btn.className).not.toMatch(/shadow-/);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user