92 lines
3.9 KiB
TypeScript
92 lines
3.9 KiB
TypeScript
import { render } from '@testing-library/react';
|
|
import { describe, it, expect, afterEach } from 'vitest';
|
|
import { HiloConductor } from '../HiloConductor';
|
|
|
|
describe('<HiloConductor />', () => {
|
|
const matchMediaOriginal = window.matchMedia;
|
|
afterEach(() => {
|
|
window.matchMedia = matchMediaOriginal;
|
|
});
|
|
|
|
it('is decorative: hidden from assistive tech', () => {
|
|
const { container } = render(<HiloConductor />);
|
|
const svg = container.querySelector('svg');
|
|
expect(svg).toHaveAttribute('aria-hidden', 'true');
|
|
expect(svg).toHaveAttribute('focusable', 'false');
|
|
});
|
|
|
|
it('draws a single continuous thread', () => {
|
|
const { container } = render(<HiloConductor />);
|
|
expect(container.querySelectorAll('path')).toHaveLength(1);
|
|
});
|
|
|
|
it('travels with the document instead of sticking to the viewport', () => {
|
|
const { container } = render(<HiloConductor />);
|
|
// `fixed` would read as an ornament pinned to the window; the thread
|
|
// belongs to the page and must scroll with it.
|
|
expect(container.firstElementChild?.className).toContain('absolute');
|
|
expect(container.firstElementChild?.className).not.toContain('fixed');
|
|
});
|
|
|
|
it('stays out of the way of pointer input', () => {
|
|
const { container } = render(<HiloConductor />);
|
|
expect(container.firstElementChild?.className).toContain('pointer-events-none');
|
|
});
|
|
|
|
it('runs down the centre of the column, not in a side gutter', () => {
|
|
const { container } = render(<HiloConductor />);
|
|
const cls = container.querySelector('svg')?.getAttribute('class') ?? '';
|
|
// `left-1/2 -translate-x-1/2` anchors the thread to the column's centre;
|
|
// the old `right-full` banished it to the left gutter, which is why the
|
|
// thread read as off to the side instead of as the page's spine.
|
|
expect(cls).toContain('left-1/2');
|
|
expect(cls).toContain('-translate-x-1/2');
|
|
expect(cls).not.toContain('right-full');
|
|
});
|
|
|
|
it('declares a stacking level that beats section roots but stays under the header', () => {
|
|
const { container } = render(<HiloConductor />);
|
|
const wrapperClass = container.firstElementChild?.className ?? '';
|
|
// Section roots never set z-index (auto), so any explicit positive value
|
|
// paints above them regardless of whether the section itself happens to
|
|
// be `position: relative` or `static` — that inconsistency was the bug.
|
|
// The sticky header is z-50 and must still win.
|
|
expect(wrapperClass).toMatch(/\bz-\d+\b/);
|
|
expect(wrapperClass).not.toContain('z-50');
|
|
});
|
|
|
|
it('renders on every breakpoint, not only on very wide screens', () => {
|
|
const { container } = render(<HiloConductor />);
|
|
const wrapperClass = container.firstElementChild?.className ?? '';
|
|
// The thread is the brand's through-line; it belongs in the middle on a
|
|
// phone too. Legibility is handled by layering it behind the text, not by
|
|
// hiding it below a breakpoint.
|
|
expect(wrapperClass).not.toContain('hidden');
|
|
expect(wrapperClass).not.toContain('2xl:block');
|
|
});
|
|
|
|
it('renders a genuine plain path under reduced motion, not a neutralised motion.path', () => {
|
|
window.matchMedia = (query: string) =>
|
|
({
|
|
matches: true,
|
|
media: query,
|
|
onchange: null,
|
|
addEventListener: () => {},
|
|
removeEventListener: () => {},
|
|
addListener: () => {},
|
|
removeListener: () => {},
|
|
dispatchEvent: () => false,
|
|
}) as unknown as MediaQueryList;
|
|
|
|
const { container } = render(<HiloConductor />);
|
|
const path = container.querySelector('path');
|
|
// motion.path stamps stroke-dasharray/stroke-dashoffset onto the DOM even
|
|
// without initial/animate — the dash-scaffolding trap that leaves the
|
|
// thread invisible for reduced-motion users. A real plain <path> never
|
|
// gets them, so their absence proves the branches weren't collapsed back
|
|
// into one.
|
|
expect(path).not.toHaveAttribute('stroke-dasharray');
|
|
expect(path).not.toHaveAttribute('stroke-dashoffset');
|
|
});
|
|
});
|