test(coverage): drive browser tests to 80% on all metrics (#496) #505
@@ -0,0 +1,106 @@
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
import { cleanup, render } from 'vitest-browser-svelte';
|
||||
import { page } from 'vitest/browser';
|
||||
import MentionDropdown from './MentionDropdown.svelte';
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
const makePerson = (id: string, name: string, overrides: Record<string, unknown> = {}) => ({
|
||||
id,
|
||||
firstName: name.split(' ')[0] ?? null,
|
||||
lastName: name.split(' ').slice(1).join(' ') || name,
|
||||
displayName: name,
|
||||
birthYear: null as number | null,
|
||||
deathYear: null as number | null,
|
||||
...overrides
|
||||
});
|
||||
|
||||
const baseModel = (overrides: Record<string, unknown> = {}) => ({
|
||||
items: [] as ReturnType<typeof makePerson>[],
|
||||
command: vi.fn(),
|
||||
clientRect: () => new DOMRect(100, 100, 0, 24),
|
||||
...overrides
|
||||
});
|
||||
|
||||
describe('MentionDropdown', () => {
|
||||
it('renders the listbox with the mention label', async () => {
|
||||
render(MentionDropdown, { props: { model: baseModel() } });
|
||||
|
||||
await expect.element(page.getByRole('listbox', { name: /person verlinken/i })).toBeVisible();
|
||||
});
|
||||
|
||||
it('renders the empty placeholder when items is empty', async () => {
|
||||
render(MentionDropdown, { props: { model: baseModel() } });
|
||||
|
||||
await expect.element(page.getByText('Keine Personen gefunden')).toBeVisible();
|
||||
});
|
||||
|
||||
it('shows the create-new escape hatch link in the empty state', async () => {
|
||||
render(MentionDropdown, { props: { model: baseModel() } });
|
||||
|
||||
const link = (await page
|
||||
.getByRole('link', { name: /neue person anlegen/i })
|
||||
.element()) as HTMLAnchorElement;
|
||||
expect(link.href).toContain('/persons/new');
|
||||
expect(link.target).toBe('_blank');
|
||||
expect(link.rel).toContain('noopener');
|
||||
});
|
||||
|
||||
it('renders one option per item when populated', async () => {
|
||||
render(MentionDropdown, {
|
||||
props: {
|
||||
model: baseModel({
|
||||
items: [makePerson('p1', 'Anna Schmidt'), makePerson('p2', 'Bert Meier')]
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
await expect.element(page.getByText('Anna Schmidt')).toBeVisible();
|
||||
await expect.element(page.getByText('Bert Meier')).toBeVisible();
|
||||
});
|
||||
|
||||
it('marks the first item as aria-selected by default', async () => {
|
||||
render(MentionDropdown, {
|
||||
props: { model: baseModel({ items: [makePerson('p1', 'Anna Schmidt')] }) }
|
||||
});
|
||||
|
||||
const option = document.querySelector('[role="option"]');
|
||||
expect(option?.getAttribute('aria-selected')).toBe('true');
|
||||
});
|
||||
|
||||
it('renders the life-date range when birthYear or deathYear is present', async () => {
|
||||
render(MentionDropdown, {
|
||||
props: {
|
||||
model: baseModel({
|
||||
items: [makePerson('p1', 'Anna', { birthYear: 1899, deathYear: 1972 })]
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
await expect.element(page.getByText(/1899/)).toBeVisible();
|
||||
});
|
||||
|
||||
it('falls back to a default position when clientRect returns null', async () => {
|
||||
render(MentionDropdown, {
|
||||
props: {
|
||||
model: baseModel({ clientRect: () => null })
|
||||
}
|
||||
});
|
||||
|
||||
const dropdown = document.querySelector('[role="listbox"]') as HTMLElement;
|
||||
expect(dropdown.style.left).toBe('0px');
|
||||
});
|
||||
|
||||
it('positions itself based on the clientRect callback', async () => {
|
||||
render(MentionDropdown, {
|
||||
props: {
|
||||
model: baseModel({
|
||||
clientRect: () => new DOMRect(123, 200, 50, 24)
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const dropdown = document.querySelector('[role="listbox"]') as HTMLElement;
|
||||
expect(dropdown.style.left).toBe('123px');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user