test(coverage): drive browser tests to 80% on all metrics (#496) #505

Merged
marcel merged 189 commits from feat/issue-496-browser-coverage-tests into main 2026-05-11 21:50:39 +02:00
2 changed files with 122 additions and 0 deletions
Showing only changes of commit 2578da50f9 - Show all commits

View File

@@ -0,0 +1,68 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import FileSectionNew from './FileSectionNew.svelte';
afterEach(cleanup);
describe('FileSectionNew', () => {
it('renders the upload prompt and section heading when no file is selected', async () => {
render(FileSectionNew, { props: {} });
await expect.element(page.getByRole('heading', { name: /datei/i })).toBeVisible();
await expect.element(page.getByText('Datei hochladen')).toBeVisible();
await expect.element(page.getByText('(optional)')).toBeVisible();
});
it('replaces the prompt with the selected filename after a file is chosen', async () => {
render(FileSectionNew, { props: {} });
const input = document.querySelector('input[type="file"]') as HTMLInputElement;
const file = new File(['%PDF'], 'brief_1920.pdf', { type: 'application/pdf' });
Object.defineProperty(input, 'files', { value: [file], writable: false });
input.dispatchEvent(new Event('change', { bubbles: true }));
await expect.element(page.getByText('brief_1920.pdf')).toBeVisible();
await expect.element(page.getByText('Datei hochladen')).not.toBeInTheDocument();
});
it('invokes onfileParsed with the parsed filename result', async () => {
const onfileParsed = vi.fn();
render(FileSectionNew, { props: { onfileParsed } });
const input = document.querySelector('input[type="file"]') as HTMLInputElement;
const file = new File(['%PDF'], 'Sender_Receiver_2024-05-01.pdf', { type: 'application/pdf' });
Object.defineProperty(input, 'files', { value: [file], writable: false });
input.dispatchEvent(new Event('change', { bubbles: true }));
expect(onfileParsed).toHaveBeenCalledOnce();
const result = onfileParsed.mock.calls[0][0];
expect(result).toHaveProperty('suggestedTitle');
});
it('does nothing when the change event fires with no file selected', async () => {
const onfileParsed = vi.fn();
render(FileSectionNew, { props: { onfileParsed } });
const input = document.querySelector('input[type="file"]') as HTMLInputElement;
Object.defineProperty(input, 'files', { value: [], writable: false });
input.dispatchEvent(new Event('change', { bubbles: true }));
expect(onfileParsed).not.toHaveBeenCalled();
await expect.element(page.getByText('Datei hochladen')).toBeVisible();
});
it('falls back to the bare stripped filename when the parser provides no suggested title', async () => {
const onfileParsed = vi.fn();
render(FileSectionNew, { props: { onfileParsed } });
const input = document.querySelector('input[type="file"]') as HTMLInputElement;
const file = new File(['%PDF'], 'plain.pdf', { type: 'application/pdf' });
Object.defineProperty(input, 'files', { value: [file], writable: false });
input.dispatchEvent(new Event('change', { bubbles: true }));
const result = onfileParsed.mock.calls[0][0];
expect(typeof result.suggestedTitle).toBe('string');
expect(result.suggestedTitle.length).toBeGreaterThan(0);
});
});

View File

@@ -0,0 +1,54 @@
import { describe, it, expect, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import PasswordChangeForm from './PasswordChangeForm.svelte';
afterEach(cleanup);
describe('PasswordChangeForm', () => {
it('renders the three password inputs and a save button by default', async () => {
render(PasswordChangeForm, { props: { form: null } });
await expect.element(page.getByRole('heading', { name: /passwort ändern/i })).toBeVisible();
await expect.element(page.getByRole('button', { name: /speichern/i })).toBeVisible();
});
it('does not render any banner when form is null', async () => {
render(PasswordChangeForm, { props: { form: null } });
await expect.element(page.getByText(/erfolgreich geändert/i)).not.toBeInTheDocument();
await expect.element(page.getByText(/stimmen nicht überein/i)).not.toBeInTheDocument();
});
it('shows the success banner when form.passwordSuccess is true', async () => {
render(PasswordChangeForm, { props: { form: { passwordSuccess: true } } });
await expect.element(page.getByText('Passwort erfolgreich geändert.')).toBeVisible();
});
it('shows the localised mismatch message for the PASSWORDS_DO_NOT_MATCH error code', async () => {
render(PasswordChangeForm, {
props: { form: { passwordError: 'PASSWORDS_DO_NOT_MATCH' } }
});
await expect
.element(page.getByText('Die neuen Passwörter stimmen nicht überein.'))
.toBeVisible();
});
it('shows the raw error message for any non-matching error code', async () => {
render(PasswordChangeForm, {
props: { form: { passwordError: 'Server-side error message' } }
});
await expect.element(page.getByText('Server-side error message')).toBeVisible();
});
it('declares POST as the form method and routes to the changePassword action', async () => {
render(PasswordChangeForm, { props: { form: null } });
const form = document.querySelector('form');
expect(form?.getAttribute('method')).toBe('POST');
expect(form?.getAttribute('action')).toBe('?/changePassword');
});
});