Add an endBeforeStart $derived to WhoWhenSection (lexicographic ISO compare, no Date object) that renders an inline error on the end-date field — border-red-400, aria-invalid, aria-describedby, and a #end-date-error <p> inside the existing aria-live region — with a ⚠ glyph so the cue is not colour-alone (WCAG 1.4.1). Save is not disabled; the server stays the gate. Wire ErrorCode INVALID_DATE_RANGE through errors.ts getErrorMessage and add the single key error_invalid_date_range to de/en/es, so the same translated string is used inline (client) and via getErrorMessage (server fallback). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
151 lines
5.1 KiB
TypeScript
151 lines
5.1 KiB
TypeScript
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
import { cleanup, render } from 'vitest-browser-svelte';
|
|
import WhoWhenSection from './WhoWhenSection.svelte';
|
|
|
|
afterEach(cleanup);
|
|
|
|
describe('WhoWhenSection — date input behavior', () => {
|
|
it('marks the date input as invalid when input has text but no valid ISO', async () => {
|
|
render(WhoWhenSection, {});
|
|
|
|
const dateInput = document.querySelector('input#documentDate') as HTMLInputElement;
|
|
dateInput.value = '32.13';
|
|
dateInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
|
|
await vi.waitFor(() => {
|
|
// Invalid → border-red-400 class
|
|
expect(dateInput.className).toContain('border-red-400');
|
|
expect(document.querySelector('#date-error')).not.toBeNull();
|
|
});
|
|
});
|
|
|
|
it('does not show the error before the user has typed', async () => {
|
|
render(WhoWhenSection, {});
|
|
|
|
const error = document.querySelector('#date-error');
|
|
expect(error).toBeNull();
|
|
});
|
|
|
|
it('updates the hidden ISO input when typing a valid German date', async () => {
|
|
render(WhoWhenSection, {});
|
|
|
|
const dateInput = document.querySelector('input#documentDate') as HTMLInputElement;
|
|
dateInput.value = '15.03.2024';
|
|
dateInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
|
|
await vi.waitFor(() => {
|
|
const hidden = document.querySelector(
|
|
'input[name="documentDate"][type="hidden"]'
|
|
) as HTMLInputElement;
|
|
expect(hidden.value).toBe('2024-03-15');
|
|
});
|
|
});
|
|
|
|
it('renders the location input outside editMode with initialLocation', async () => {
|
|
render(WhoWhenSection, { editMode: false, initialLocation: 'Hamburg' });
|
|
|
|
const loc = document.querySelector('input#location') as HTMLInputElement;
|
|
expect(loc.value).toBe('Hamburg');
|
|
});
|
|
|
|
it('hides the location input in editMode', async () => {
|
|
render(WhoWhenSection, { editMode: true });
|
|
|
|
const loc = document.querySelector('input#location');
|
|
expect(loc).toBeNull();
|
|
});
|
|
|
|
it('shows the FieldLabelBadge for receivers in editMode', async () => {
|
|
render(WhoWhenSection, { editMode: true });
|
|
|
|
// FieldLabelBadge with variant=additive is rendered (just check the heading area)
|
|
const labels = Array.from(document.querySelectorAll('p, label')).filter((el) =>
|
|
/empfänger/i.test(el.textContent ?? '')
|
|
);
|
|
expect(labels.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('renders the date asterisk indicator (required field)', async () => {
|
|
render(WhoWhenSection, {});
|
|
|
|
const label = document.querySelector('label[for="documentDate"]');
|
|
expect(label?.textContent).toContain('*');
|
|
});
|
|
});
|
|
|
|
describe('WhoWhenSection — precision controls', () => {
|
|
it('renders a labelled precision select', async () => {
|
|
render(WhoWhenSection, {});
|
|
|
|
const label = document.querySelector('label[for="metaDatePrecision"]');
|
|
const select = document.querySelector('select#metaDatePrecision[name="metaDatePrecision"]');
|
|
expect(label).not.toBeNull();
|
|
expect(select).not.toBeNull();
|
|
});
|
|
|
|
it('hides the end-date field unless precision is RANGE', async () => {
|
|
render(WhoWhenSection, { precision: 'DAY' });
|
|
expect(document.querySelector('input#metaDateEnd')).toBeNull();
|
|
});
|
|
|
|
it('reveals the end-date field when precision is RANGE', async () => {
|
|
render(WhoWhenSection, { precision: 'RANGE' });
|
|
expect(document.querySelector('input#metaDateEnd')).not.toBeNull();
|
|
});
|
|
|
|
it('renders the raw cell as static text (not an editable input) and escapes it', async () => {
|
|
render(WhoWhenSection, { rawDate: '<b>Sommer</b> 1916' });
|
|
const raw = document.querySelector('[data-testid="who-when-raw"]');
|
|
expect(raw).not.toBeNull();
|
|
// Verbatim shown as escaped text; no injected <b> element.
|
|
expect(raw?.textContent).toContain('<b>Sommer</b> 1916');
|
|
expect(raw?.querySelector('b')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('WhoWhenSection — end-before-start inline validation (#678)', () => {
|
|
it('shows an inline error on the end-date field when end is before start (AC1)', async () => {
|
|
render(WhoWhenSection, {
|
|
precision: 'RANGE',
|
|
dateIso: '1917-01-11',
|
|
endDateIso: '1917-01-10'
|
|
});
|
|
|
|
const end = document.querySelector('input#metaDateEnd') as HTMLInputElement;
|
|
await vi.waitFor(() => {
|
|
expect(document.querySelector('#end-date-error')).not.toBeNull();
|
|
expect(end.getAttribute('aria-invalid')).toBe('true');
|
|
expect(end.className).toContain('border-red-400');
|
|
});
|
|
});
|
|
|
|
it('clears the inline error once the end date is corrected, without reload (AC5)', async () => {
|
|
render(WhoWhenSection, {
|
|
precision: 'RANGE',
|
|
dateIso: '1917-01-11',
|
|
endDateIso: '1917-01-10'
|
|
});
|
|
|
|
await vi.waitFor(() => expect(document.querySelector('#end-date-error')).not.toBeNull());
|
|
|
|
const end = document.querySelector('input#metaDateEnd') as HTMLInputElement;
|
|
end.value = '12.01.1917'; // now after the start
|
|
end.dispatchEvent(new Event('input', { bubbles: true }));
|
|
|
|
await vi.waitFor(() => {
|
|
expect(document.querySelector('#end-date-error')).toBeNull();
|
|
expect(end.getAttribute('aria-invalid')).not.toBe('true');
|
|
});
|
|
});
|
|
|
|
it('does not show the inline error when precision is not RANGE', async () => {
|
|
render(WhoWhenSection, {
|
|
precision: 'DAY',
|
|
dateIso: '1917-01-11',
|
|
endDateIso: '1917-01-10'
|
|
});
|
|
|
|
expect(document.querySelector('#end-date-error')).toBeNull();
|
|
});
|
|
});
|