diff --git a/frontend/src/lib/document/ScriptTypeSelect.svelte.test.ts b/frontend/src/lib/document/ScriptTypeSelect.svelte.test.ts new file mode 100644 index 00000000..498b71e9 --- /dev/null +++ b/frontend/src/lib/document/ScriptTypeSelect.svelte.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import ScriptTypeSelect from './ScriptTypeSelect.svelte'; + +afterEach(cleanup); + +describe('ScriptTypeSelect', () => { + it('renders the label and select', async () => { + render(ScriptTypeSelect, { props: { value: '' } }); + + await expect.element(page.getByLabelText(/schrifttyp/i)).toBeVisible(); + }); + + it('renders all four option values', async () => { + render(ScriptTypeSelect, { props: { value: '' } }); + + const options = document.querySelectorAll('option'); + const values = Array.from(options).map((o) => (o as HTMLOptionElement).value); + expect(values).toEqual(['', 'TYPEWRITER', 'HANDWRITING_LATIN', 'HANDWRITING_KURRENT']); + }); + + it('marks the placeholder option as disabled', async () => { + render(ScriptTypeSelect, { props: { value: '' } }); + + const placeholder = document.querySelector('option[value=""]') as HTMLOptionElement; + expect(placeholder.disabled).toBe(true); + }); + + it('initialises the select with the supplied value', async () => { + render(ScriptTypeSelect, { props: { value: 'TYPEWRITER' } }); + + const select = (await page.getByRole('combobox').element()) as HTMLSelectElement; + expect(select.value).toBe('TYPEWRITER'); + }); + + it('disables the select when the disabled prop is true', async () => { + render(ScriptTypeSelect, { props: { value: '', disabled: true } }); + + const select = (await page.getByRole('combobox').element()) as HTMLSelectElement; + expect(select.disabled).toBe(true); + }); +}); diff --git a/frontend/src/lib/user/UserGroupsSection.svelte.test.ts b/frontend/src/lib/user/UserGroupsSection.svelte.test.ts new file mode 100644 index 00000000..841d44f7 --- /dev/null +++ b/frontend/src/lib/user/UserGroupsSection.svelte.test.ts @@ -0,0 +1,54 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import UserGroupsSection from './UserGroupsSection.svelte'; + +afterEach(cleanup); + +const groups = [ + { id: 'g1', name: 'Familie' }, + { id: 'g2', name: 'Admins' }, + { id: 'g3', name: 'Lesetransport' } +]; + +describe('UserGroupsSection', () => { + it('renders one checkbox per group', async () => { + render(UserGroupsSection, { props: { groups } }); + + const checkboxes = document.querySelectorAll('input[name="groupIds"]'); + expect(checkboxes.length).toBe(3); + }); + + it('renders each group label', async () => { + render(UserGroupsSection, { props: { groups } }); + + expect(document.body.textContent).toContain('Familie'); + expect(document.body.textContent).toContain('Admins'); + expect(document.body.textContent).toContain('Lesetransport'); + }); + + it('preselects checkboxes for ids in selectedGroupIds', async () => { + render(UserGroupsSection, { props: { groups, selectedGroupIds: ['g1', 'g3'] } }); + + const checkboxes = Array.from( + document.querySelectorAll('input[name="groupIds"]') + ) as HTMLInputElement[]; + const checkedValues = checkboxes.filter((c) => c.checked).map((c) => c.value); + expect(checkedValues.sort()).toEqual(['g1', 'g3']); + }); + + it('renders nothing when groups is empty', async () => { + render(UserGroupsSection, { props: { groups: [] } }); + + const checkboxes = document.querySelectorAll('input[name="groupIds"]'); + expect(checkboxes.length).toBe(0); + }); + + it('handles a missing selectedGroupIds prop by defaulting to none selected', async () => { + render(UserGroupsSection, { props: { groups } }); + + const checkboxes = Array.from( + document.querySelectorAll('input[name="groupIds"]') + ) as HTMLInputElement[]; + expect(checkboxes.every((c) => !c.checked)).toBe(true); + }); +}); diff --git a/frontend/src/routes/briefwechsel/SinglePersonHintBar.svelte.test.ts b/frontend/src/routes/briefwechsel/SinglePersonHintBar.svelte.test.ts new file mode 100644 index 00000000..20cb9ea9 --- /dev/null +++ b/frontend/src/routes/briefwechsel/SinglePersonHintBar.svelte.test.ts @@ -0,0 +1,53 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import SinglePersonHintBar from './SinglePersonHintBar.svelte'; + +afterEach(cleanup); + +describe('SinglePersonHintBar', () => { + it('renders the no-filter prompt when neither fromDate nor toDate is supplied', async () => { + render(SinglePersonHintBar, { props: { senderName: 'Anna Schmidt' } }); + + await expect.element(page.getByText('Anna Schmidt')).toBeVisible(); + await expect.element(page.getByText(/wähle einen korrespondenten/i)).toBeVisible(); + }); + + it('renders the year range and sort label when fromDate is supplied', async () => { + render(SinglePersonHintBar, { + props: { + senderName: 'Anna Schmidt', + fromDate: '1923-01-01', + toDate: '1925-12-31', + sortDir: 'DESC' + } + }); + + await expect.element(page.getByText('1923–1925')).toBeVisible(); + await expect.element(page.getByText('Neueste')).toBeVisible(); + }); + + it('uses the "Älteste" label when sortDir is ASC', async () => { + render(SinglePersonHintBar, { + props: { senderName: 'Anna Schmidt', fromDate: '1923-01-01', sortDir: 'ASC' } + }); + + await expect.element(page.getByText('Älteste')).toBeVisible(); + }); + + it('hides the no-filter prompt when fromDate alone is set', async () => { + render(SinglePersonHintBar, { + props: { senderName: 'Anna Schmidt', fromDate: '1923-01-01' } + }); + + await expect.element(page.getByText(/wähle einen korrespondenten/i)).not.toBeInTheDocument(); + }); + + it('shows year range using only fromYear when toDate is empty', async () => { + render(SinglePersonHintBar, { + props: { senderName: 'Anna Schmidt', fromDate: '1923-01-01' } + }); + + await expect.element(page.getByText('1923–')).toBeVisible(); + }); +});