init project
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { Button } from '@/components/actions/Button';
|
||||
import { ActionLink } from '@/components/actions/ActionLink';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
describe('action components', () => {
|
||||
it('prevents duplicate interaction while loading and preserves the accessible name', () => {
|
||||
const onClick = vi.fn();
|
||||
render(
|
||||
<Button loading onClick={onClick}>
|
||||
Save reservation
|
||||
</Button>,
|
||||
);
|
||||
const button = screen.getByRole('button', { name: 'Save reservation' });
|
||||
expect(button).toBeDisabled();
|
||||
expect(button).toHaveAttribute('aria-busy', 'true');
|
||||
fireEvent.click(button);
|
||||
expect(onClick).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('requires an accessible label for icon-only use through its public contract', () => {
|
||||
render(<Button icon="close" aria-label="Close panel" />);
|
||||
expect(screen.getByRole('button', { name: 'Close panel' })).toBeVisible();
|
||||
});
|
||||
|
||||
it('renders unresolved navigation as a disabled non-link', () => {
|
||||
render(
|
||||
<ActionLink href="/en" disabledReason="Destination pending approval">
|
||||
Book a demo
|
||||
</ActionLink>,
|
||||
);
|
||||
expect(screen.queryByRole('link')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Book a demo').closest('[aria-disabled="true"]')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Accordion } from '@/components/controls/Accordion';
|
||||
import { Tabs } from '@/components/controls/Tabs';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('selection and disclosure controls', () => {
|
||||
it('moves tab focus and selection with arrow keys', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<Tabs
|
||||
label="Views"
|
||||
items={[
|
||||
{ id: 'one', label: 'One', content: 'Panel one' },
|
||||
{ id: 'two', label: 'Two', content: 'Panel two' },
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
const first = screen.getByRole('tab', { name: 'One' });
|
||||
await user.click(first);
|
||||
await user.keyboard('{ArrowRight}');
|
||||
expect(screen.getByRole('tab', { name: 'Two' })).toHaveAttribute('aria-selected', 'true');
|
||||
expect(screen.getByRole('tabpanel', { name: 'Two' })).toHaveTextContent('Panel two');
|
||||
});
|
||||
|
||||
it('uses native disclosure semantics for accordion items', () => {
|
||||
render(<Accordion items={[{ id: 'faq', title: 'Question', content: 'Answer' }]} />);
|
||||
expect(screen.getByText('Question').closest('summary')).toBeInTheDocument();
|
||||
expect(screen.getByText('Answer').closest('details')).not.toHaveAttribute('open');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import { canRenderProductionEvidence, Metric } from '@/components/marketing/Evidence';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('evidence governance', () => {
|
||||
it('allows only approved evidence in production mode', () => {
|
||||
expect(canRenderProductionEvidence('approved')).toBe(true);
|
||||
expect(canRenderProductionEvidence('pending-review')).toBe(false);
|
||||
expect(canRenderProductionEvidence('research-only')).toBe(false);
|
||||
expect(canRenderProductionEvidence('prohibited')).toBe(false);
|
||||
});
|
||||
|
||||
it('suppresses research-only metrics in production mode', () => {
|
||||
const { container } = render(
|
||||
<Metric
|
||||
production
|
||||
statusLabel="Research only"
|
||||
content={{ value: '24', label: 'Fixture', status: 'research-only' }}
|
||||
/>,
|
||||
);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('renders approved evidence with its source reference', () => {
|
||||
render(
|
||||
<Metric
|
||||
production
|
||||
statusLabel="Approved"
|
||||
content={{
|
||||
value: '1',
|
||||
label: 'Approved fixture',
|
||||
status: 'approved',
|
||||
sourceReference: 'SRC-001',
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText('SRC-001')).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { ErrorSummary } from '@/components/forms/ErrorSummary';
|
||||
import { FormField } from '@/components/forms/FormField';
|
||||
import { TextInput } from '@/components/forms/TextInput';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('form primitives', () => {
|
||||
it('associates labels, help, required state, and errors', () => {
|
||||
render(
|
||||
<FormField
|
||||
id="work-email"
|
||||
label="Work email"
|
||||
required
|
||||
supportingText="Use work email"
|
||||
error="Enter a valid address"
|
||||
>
|
||||
<TextInput
|
||||
id="work-email"
|
||||
type="email"
|
||||
required
|
||||
invalid
|
||||
describedBy="work-email-help work-email-error"
|
||||
/>
|
||||
</FormField>,
|
||||
);
|
||||
const input = screen.getByLabelText(/Work email/);
|
||||
expect(input).toHaveAttribute('required');
|
||||
expect(input).toHaveAttribute('aria-invalid', 'true');
|
||||
expect(input).toHaveAccessibleDescription('Use work email Enter a valid address');
|
||||
});
|
||||
|
||||
it('links summary errors back to fields', () => {
|
||||
render(
|
||||
<ErrorSummary
|
||||
title="Correct the fields"
|
||||
errors={[{ fieldId: 'work-email', message: 'Enter a valid address' }]}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByRole('alert').querySelector('a')).toHaveAttribute('href', '#work-email');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user