init project

This commit is contained in:
root
2026-06-25 19:06:59 -04:00
parent c5dfee6efb
commit 5d017f533a
349 changed files with 31330 additions and 0 deletions
+35
View File
@@ -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();
});
});
+31
View File
@@ -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');
});
});
+39
View File
@@ -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();
});
});
+41
View File
@@ -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');
});
});
+20
View File
@@ -0,0 +1,20 @@
import { buildContentSecurityPolicy, createNonce } from '@/lib/security/csp';
import { describe, expect, it } from 'vitest';
describe('CSP foundation', () => {
it('generates unpredictable nonce-shaped values', () => {
const first = createNonce();
const second = createNonce();
expect(first).toMatch(/^[a-f0-9]{32}$/);
expect(second).not.toBe(first);
});
it('allows development evaluation only in development', () => {
expect(buildContentSecurityPolicy('abc', true)).toContain("'unsafe-eval'");
expect(buildContentSecurityPolicy('abc', false)).not.toContain("'unsafe-eval'");
expect(buildContentSecurityPolicy('abc', false)).toContain("'nonce-abc'");
expect(buildContentSecurityPolicy('abc', false)).toContain("style-src 'self' 'nonce-abc'");
expect(buildContentSecurityPolicy('abc', false)).not.toContain("'unsafe-inline'");
expect(buildContentSecurityPolicy('abc', false)).toContain("frame-ancestors 'none'");
});
});
+40
View File
@@ -0,0 +1,40 @@
import {
formatCurrency,
formatDate,
formatDateRange,
formatNumber,
formatPercent,
formatPhoneNumber,
formatRelativeTime,
preserveIdentifier,
} from '@/lib/localization/format';
import { describe, expect, it } from 'vitest';
const instant = new Date('2026-06-25T12:00:00.000Z');
describe('locale-aware formatting primitives', () => {
it('formats Gregorian dates with explicit time zones', () => {
expect(formatDate(instant, { locale: 'en', timeZone: 'UTC' })).toContain('2026');
expect(formatDate(instant, { locale: 'fr', timeZone: 'UTC' })).toContain('2026');
expect(formatDate(instant, { locale: 'ar', timeZone: 'UTC' })).toContain('2026');
});
it('formats ranges, numbers, percentages, and explicit currencies', () => {
expect(
formatDateRange(instant, new Date('2026-06-27T12:00:00.000Z'), {
locale: 'en',
timeZone: 'UTC',
}),
).toContain('2026');
expect(formatNumber(1234.5, 'fr')).toMatch(/1.*234/);
expect(formatPercent(0.125, 'en')).toContain('12.5');
expect(formatCurrency(125, 'en', 'USD')).toContain('$');
expect(() => formatCurrency(125, 'en', 'usd')).toThrow(/ISO 4217/);
});
it('formats relative time and preserves atomic mixed-direction values', () => {
expect(formatRelativeTime(-1, 'day', 'en')).toBe('yesterday');
expect(formatPhoneNumber('+1 (202) 555-0100')).toBe('+12025550100');
expect(preserveIdentifier('RES 2026 0042')).toBe('RES 2026 0042');
});
});
+42
View File
@@ -0,0 +1,42 @@
import arHomepage from '@/content/locales/ar/homepage.json';
import arShell from '@/content/locales/ar/shell.json';
import enHomepage from '@/content/locales/en/homepage.json';
import enShell from '@/content/locales/en/shell.json';
import frHomepage from '@/content/locales/fr/homepage.json';
import frShell from '@/content/locales/fr/shell.json';
import { buildHomepageContent } from '@/content/homepage-model';
import { describe, expect, it } from 'vitest';
describe('homepage content model', () => {
it.each([
['en', enHomepage, enShell],
['fr', frHomepage, frShell],
['ar', arHomepage, arShell],
] as const)('builds complete %s route content', (_locale, homepage, shell) => {
const content = buildHomepageContent(homepage, shell);
expect(content.hero.title).toBeTruthy();
expect(content.trust.items).toHaveLength(4);
expect(content.workflow.steps).toHaveLength(4);
expect(content.modules.items).toHaveLength(6);
expect(content.faq.items).toHaveLength(6);
expect(content.hero.preview.rows).toHaveLength(2);
});
it('keeps unresolved conversion destinations disabled', () => {
const content = buildHomepageContent(enHomepage, enShell);
expect(content.hero.primary.disabledReason).toBe(enShell.header.demoUnavailable);
expect(content.pricing.action.disabledReason).toBe(enShell.states.pendingBody);
expect(content.final.secondary.disabledReason).toBe(enShell.states.pendingBody);
});
it('keeps preview identifiers isolated from translated prose', () => {
const content = buildHomepageContent(arHomepage, arShell);
expect(content.hero.preview.rows.map((row) => row.secondary)).toEqual([
arHomepage.preview.plate1,
arHomepage.preview.plate2,
]);
});
});
+58
View File
@@ -0,0 +1,58 @@
import {
getDirection,
hreflangMap,
isLocale,
localeFromAcceptLanguage,
localeSwitchUrl,
localizedPath,
routeById,
routeIdFromPathname,
routeIdFromSlug,
} from '@/lib/localization/config';
import { describe, expect, it } from 'vitest';
describe('localization foundations', () => {
it('selects a supported locale by quality', () => {
expect(localeFromAcceptLanguage('de-DE;q=0.9, ar;q=0.8, fr;q=0.7')).toBe('ar');
expect(localeFromAcceptLanguage('fr-CA, en;q=0.8')).toBe('fr');
expect(localeFromAcceptLanguage(null)).toBe('en');
});
it('validates locale direction and route lookup', () => {
expect(isLocale('fr')).toBe(true);
expect(isLocale('de')).toBe(false);
expect(getDirection('ar')).toBe('rtl');
expect(routeById('home').id).toBe('home');
});
it('generates and resolves localized stable route paths', () => {
expect(localizedPath('home', 'ar')).toBe('/ar');
expect(localizedPath('privacy', 'fr')).toBe('/fr/confidentialite');
expect(localizedPath('login', 'ar')).toBe('/ar/تسجيل-الدخول');
expect(routeIdFromSlug('fr', 'confidentialite')).toBe('privacy');
expect(routeIdFromPathname('/ar/%D8%A7%D9%84%D8%AE%D8%B5%D9%88%D8%B5%D9%8A%D8%A9')).toBe(
'privacy',
);
expect(routeIdFromPathname('/de')).toBeNull();
});
it('generates reciprocal hreflang values', () => {
const links = hreflangMap(new URL('https://approved.test'), 'home');
expect(links.en).toBe('https://approved.test/en');
expect(links.ar).toBe('https://approved.test/ar');
expect(links['x-default']).toBe('https://approved.test/en');
});
it('preserves only approved campaign query values and valid hashes', () => {
const current = new URL(
'https://approved.test/en?utm_source=campaign&email=private%40approved.test#gibberish',
);
const switched = localeSwitchUrl(current, 'fr', 'home');
expect(switched.pathname).toBe('/fr');
expect(switched.search).toBe('?utm_source=campaign');
expect(switched.hash).toBe('');
current.hash = '#workflow';
expect(localeSwitchUrl(current, 'ar', 'home').hash).toBe('#workflow');
});
});
+18
View File
@@ -0,0 +1,18 @@
import { isThemePreference, resolveTheme, serverResolvedTheme } from '@/lib/theme/config';
import { describe, expect, it } from 'vitest';
describe('theme foundations', () => {
it('accepts only approved preferences', () => {
expect(isThemePreference('light')).toBe(true);
expect(isThemePreference('dark')).toBe(true);
expect(isThemePreference('system')).toBe(true);
expect(isThemePreference('sepia')).toBe(false);
});
it('keeps preference and resolved theme separate', () => {
expect(resolveTheme('system', true)).toBe('dark');
expect(resolveTheme('system', false)).toBe('light');
expect(resolveTheme('light', true)).toBe('light');
expect(serverResolvedTheme('system')).toBe('light');
});
});