Files
mimic/frontend/tests/components/Tabs.test.tsx
Knacky 790ced4204 fix(frontend): design-review polish — table-compact height, header type-scale, tabs a11y
Fix 1 (A): move row height constraint from min-height on tbody tr
(CSS no-op on table-row) to height: 32px on td (works on table-cell).
Fix 2 (A): table header text-[11px] -> text-[12px] to align with
documented caption scale.
Fix 3: add aria-controls + id to Tabs buttons; wrap active tab content
in role="tabpanel" + aria-labelledby in EngagementDetailPage.
Test: add aria-controls/id assertion to Tabs.test.tsx (234 total).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 22:18:09 +02:00

71 lines
3.0 KiB
TypeScript

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('sets aria-controls and id on each tab button', () => {
render(<Tabs items={ITEMS} activeId="schedule" onChange={vi.fn()} />);
const schedBtn = screen.getByRole('tab', { name: /Schedule/i });
expect(schedBtn).toHaveAttribute('id', 'tab-schedule');
expect(schedBtn).toHaveAttribute('aria-controls', 'tabpanel-schedule');
const simsBtn = screen.getByRole('tab', { name: /Simulations/i });
expect(simsBtn).toHaveAttribute('id', 'tab-simulations');
expect(simsBtn).toHaveAttribute('aria-controls', 'tabpanel-simulations');
});
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-/);
}
});
});