From f7ed154e4dd50c48e46a813efb180daa85c77d2a Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 18 Apr 2026 14:31:05 +0200 Subject: [PATCH] fix(a11y): bump progress bar text to text-xs minimum, add motion-safe to upload animation - text-[9px]/text-[10px] in required-fields bar raised to text-xs (12px), meeting the project minimum for the 60+ audience (WCAG 1.4.4) - Upload animation now uses motion-safe: prefix so it stops for users with prefers-reduced-motion set (WCAG 2.1 SC 2.3.3) - Strengthened UploadZone tests: onCancel uses [role=status] button selector instead of first-button heuristic; added positive file selection test (valid PDF calls onFile), file-too-large test, and MIME rejection now also asserts the error message is visible Co-Authored-By: Claude Sonnet 4.6 --- .../lib/components/document/UploadZone.svelte | 4 ++- .../document/UploadZone.svelte.test.ts | 32 ++++++++++++++++++- frontend/src/routes/enrich/[id]/+page.svelte | 4 +-- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/frontend/src/lib/components/document/UploadZone.svelte b/frontend/src/lib/components/document/UploadZone.svelte index 50aac4eb..62d3c672 100644 --- a/frontend/src/lib/components/document/UploadZone.svelte +++ b/frontend/src/lib/components/document/UploadZone.svelte @@ -55,7 +55,9 @@ function handleDrop(e: DragEvent) { {#if isUploading}
-
+

{filename}

Wird hochgeladen …

diff --git a/frontend/src/lib/components/document/UploadZone.svelte.test.ts b/frontend/src/lib/components/document/UploadZone.svelte.test.ts index a661a2a6..553d8638 100644 --- a/frontend/src/lib/components/document/UploadZone.svelte.test.ts +++ b/frontend/src/lib/components/document/UploadZone.svelte.test.ts @@ -47,7 +47,8 @@ describe('UploadZone', () => { render(UploadZone, { props: { filename: 'scan.pdf', isUploading: true, isDragging: false, error: null, onCancel } }); - const btn = document.querySelector('button')!; + // Click the button inside [role="status"] — more specific than querySelector('button') + const btn = document.querySelector('[role="status"] button') as HTMLButtonElement; btn.dispatchEvent(new MouseEvent('click', { bubbles: true })); expect(onCancel).toHaveBeenCalledOnce(); }); @@ -68,6 +69,18 @@ describe('UploadZone', () => { }); describe('file selection', () => { + it('calls onFile for a valid PDF', () => { + const onFile = vi.fn(); + render(UploadZone, { + props: { filename: 'scan.pdf', isUploading: false, isDragging: false, error: null, onFile } + }); + const input = document.querySelector('input[type="file"]') as HTMLInputElement; + const pdf = new File(['%PDF-1.4'], 'brief.pdf', { type: 'application/pdf' }); + Object.defineProperty(input, 'files', { value: [pdf], writable: false }); + input.dispatchEvent(new Event('change', { bubbles: true })); + expect(onFile).toHaveBeenCalledWith(pdf); + }); + it('does not call onFile for an unsupported MIME type', async () => { const onFile = vi.fn(); render(UploadZone, { @@ -80,6 +93,23 @@ describe('UploadZone', () => { Object.defineProperty(input, 'files', { value: [docxFile], writable: false }); input.dispatchEvent(new Event('change', { bubbles: true })); expect(onFile).not.toHaveBeenCalled(); + await expect + .element(page.getByText('Dieser Dateityp wird nicht unterstützt (PDF, JPG, PNG, TIFF).')) + .toBeVisible(); + }); + + it('does not call onFile when file exceeds 50 MB', async () => { + const onFile = vi.fn(); + render(UploadZone, { + props: { filename: 'scan.pdf', isUploading: false, isDragging: false, error: null, onFile } + }); + const input = document.querySelector('input[type="file"]') as HTMLInputElement; + const bigFile = new File(['x'.repeat(1)], 'huge.pdf', { type: 'application/pdf' }); + Object.defineProperty(bigFile, 'size', { value: 51 * 1024 * 1024 }); + Object.defineProperty(input, 'files', { value: [bigFile], writable: false }); + input.dispatchEvent(new Event('change', { bubbles: true })); + expect(onFile).not.toHaveBeenCalled(); + await expect.element(page.getByText('Die Datei ist zu groß (max. 50 MB).')).toBeVisible(); }); }); }); diff --git a/frontend/src/routes/enrich/[id]/+page.svelte b/frontend/src/routes/enrich/[id]/+page.svelte index 6b1eea0a..08578094 100644 --- a/frontend/src/routes/enrich/[id]/+page.svelte +++ b/frontend/src/routes/enrich/[id]/+page.svelte @@ -120,7 +120,7 @@ async function handleReplaceFile(e: Event) {
- Pflichtfelder + Pflichtfelder
- {requiredFilled} / 3 + {requiredFilled} / 3