Files
familienarchiv/frontend/src/lib/document/BulkSelectionBar.svelte.spec.ts
Marcel e7f8aa5894 refactor: move document domain core to lib/document/
Moves ~25 components, utils (search, filename, groupDocuments,
documentStatusLabel, validateFile), bulkSelection store, and
TranscriptionSection sub-component. Fixes broken relative imports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 13:56:36 +02:00

123 lines
4.2 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import { goto } from '$app/navigation';
import BulkSelectionBar from './BulkSelectionBar.svelte';
import { bulkSelectionStore } from '$lib/document/bulkSelection.svelte';
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
afterEach(() => {
cleanup();
vi.mocked(goto).mockClear();
bulkSelectionStore.clear();
});
describe('BulkSelectionBar', () => {
it('does not render when canWrite is false', async () => {
bulkSelectionStore.add('a');
render(BulkSelectionBar, { canWrite: false });
await expect.element(page.getByTestId('bulk-selection-bar')).not.toBeInTheDocument();
});
it('does not render when selection is empty', async () => {
render(BulkSelectionBar, { canWrite: true });
await expect.element(page.getByTestId('bulk-selection-bar')).not.toBeInTheDocument();
});
it('renders with the current selection count', async () => {
bulkSelectionStore.add('a');
bulkSelectionStore.add('b');
render(BulkSelectionBar, { canWrite: true });
await expect.element(page.getByTestId('bulk-selection-count')).toHaveTextContent('2');
});
it('uses the singular plural form for count=1 (not "1 Dokumente")', async () => {
bulkSelectionStore.add('only');
render(BulkSelectionBar, { canWrite: true });
await expect
.element(page.getByTestId('bulk-selection-count'))
.toHaveTextContent('1 Dokument ausgewählt');
});
it('uses the plural form for count=2', async () => {
bulkSelectionStore.add('a');
bulkSelectionStore.add('b');
render(BulkSelectionBar, { canWrite: true });
await expect
.element(page.getByTestId('bulk-selection-count'))
.toHaveTextContent('2 Dokumente ausgewählt');
});
it('clear button empties the store', async () => {
bulkSelectionStore.add('a');
bulkSelectionStore.add('b');
render(BulkSelectionBar, { canWrite: true });
await page.getByTestId('bulk-clear-all').click();
expect(bulkSelectionStore.size).toBe(0);
});
it('Massenbearbeitung navigates to /documents/bulk-edit', async () => {
bulkSelectionStore.add('a');
render(BulkSelectionBar, { canWrite: true });
await page.getByTestId('bulk-edit-open').click();
expect(vi.mocked(goto)).toHaveBeenCalledWith('/documents/bulk-edit');
});
it('selection count region announces via aria-live=polite', async () => {
bulkSelectionStore.add('a');
render(BulkSelectionBar, { canWrite: true });
await expect
.element(page.getByTestId('bulk-selection-count'))
.toHaveAttribute('aria-live', 'polite');
});
it('Escape clears the selection while the bar is visible', async () => {
bulkSelectionStore.add('a');
bulkSelectionStore.add('b');
render(BulkSelectionBar, { canWrite: true });
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
await expect.poll(() => bulkSelectionStore.size).toBe(0);
});
it('Escape is a no-op when the bar is hidden (no selection)', async () => {
render(BulkSelectionBar, { canWrite: true });
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
// Nothing to clear, no error.
expect(bulkSelectionStore.size).toBe(0);
});
it('Escape does not clear when an open <dialog> is present (Leonie B6 scope guard)', async () => {
bulkSelectionStore.add('a');
bulkSelectionStore.add('b');
render(BulkSelectionBar, { canWrite: true });
// Simulate a ConfirmDialog being open in front of the bar.
const overlay = document.createElement('dialog');
overlay.setAttribute('open', '');
document.body.appendChild(overlay);
try {
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
// Escape is captured by the dialog, not the bar — selection survives.
expect(bulkSelectionStore.size).toBe(2);
} finally {
overlay.remove();
}
});
it('Escape does not clear when an aria-expanded popover is present', async () => {
bulkSelectionStore.add('a');
render(BulkSelectionBar, { canWrite: true });
const trigger = document.createElement('button');
trigger.setAttribute('aria-expanded', 'true');
document.body.appendChild(trigger);
try {
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
expect(bulkSelectionStore.size).toBe(1);
} finally {
trigger.remove();
}
});
});