Files
SRCmail/hooks/__tests__/use-shortened-text.test.tsx
T
Mathy Vanvoorden ca0ba818b7 feat: Add nesting of tags in a tree
- levels are joined by forward slashes in the keywords
- behaviour is opt-in for now
- long paths are shortened if there is not enough display room

Closes #687.
2026-07-29 17:18:38 +02:00

71 lines
2.4 KiB
TypeScript

import { render, screen } from '@testing-library/react';
import { describe, it, expect, afterEach, vi } from 'vitest';
import { useShortenedText } from '../use-shortened-text';
const CANDIDATES = ['Work/Clients/Acme/Sales', 'Work/../Acme/Sales', 'Work/.../Sales'];
/**
* Reports `width` for the observed element and measures text at 10px per
* character, so a width of N*10 fits any candidate of N characters or fewer.
*/
function stubMeasurement(width: number) {
// Implementing the interface rather than passing an anonymous class keeps the
// members the hook never calls from reading as dead code.
class StubResizeObserver implements ResizeObserver {
constructor(private readonly callback: ResizeObserverCallback) {}
/** The hook observes once on mount; hand it `width` straight back. */
observe(target: Element) {
this.callback([{ target, contentRect: { width } } as unknown as ResizeObserverEntry], this);
}
unobserve() {}
disconnect() {}
}
vi.stubGlobal('ResizeObserver', StubResizeObserver);
vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue({
font: '',
measureText: (text: string) => ({ width: text.length * 10 }),
} as unknown as CanvasRenderingContext2D);
}
function Probe({ candidates }: { candidates: string[] }) {
const [ref, text] = useShortenedText(candidates);
return <span ref={ref} data-testid="probe">{text}</span>;
}
function renderProbe(candidates: string[]): string {
render(<Probe candidates={candidates} />);
return screen.getByTestId('probe').textContent ?? '';
}
describe('useShortenedText', () => {
afterEach(() => {
vi.unstubAllGlobals();
vi.restoreAllMocks();
});
it('returns the longest candidate where the DOM cannot be measured', () => {
// No ResizeObserver: server rendering, and jsdom by default. Showing the
// whole path beats shortening it on a guess.
expect(renderProbe(CANDIDATES)).toBe('Work/Clients/Acme/Sales');
});
it('keeps the full path when the element is wide enough', () => {
stubMeasurement(230);
expect(renderProbe(CANDIDATES)).toBe('Work/Clients/Acme/Sales');
});
it('steps down only as far as the width requires', () => {
stubMeasurement(200);
expect(renderProbe(CANDIDATES)).toBe('Work/../Acme/Sales');
});
it('falls back to the shortest candidate when none of them fit', () => {
stubMeasurement(40);
expect(renderProbe(CANDIDATES)).toBe('Work/.../Sales');
});
});