Compare commits

...

5 Commits

Author SHA1 Message Date
Marcel
9aed929b67 fix(bulk-upload): raise discard button touch target to 44px for WCAG compliance
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m59s
CI / OCR Service Tests (push) Successful in 35s
CI / Backend Unit Tests (push) Failing after 2m56s
CI / Unit & Component Tests (pull_request) Failing after 2m59s
CI / OCR Service Tests (pull_request) Successful in 43s
CI / Backend Unit Tests (pull_request) Failing after 3m2s
Senior users on tablets need at least 44×44px touch targets (WCAG 2.2).
Added min-h-[44px] flex items-center px-2 to the discard button.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 09:13:55 +02:00
Marcel
cb9962f0c2 test(bulk-upload): add positive navigation assertion for successful save
The error-path test (goto not called on failure) had no matching positive
assertion. Added: save() navigates to /documents when all chunks succeed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 09:12:21 +02:00
Marcel
262c792654 fix(bulk-upload): correct stale DocumentBatchMetadataDTO type in api.ts
Generated type had tags?: string but Java DTO declares List<String> tagNames.
Corrected to tagNames?: string[] to match the backend contract.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 09:10:29 +02:00
Marcel
60f1db1f99 fix(bulk-upload): announce error chip status to screen readers
The ! indicator was aria-hidden with no sr-only fallback, making failed
uploads invisible to assistive technology. Added sr-only span with
bulk_file_error_chip_label before the visual indicator.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 09:08:10 +02:00
Marcel
8cf4f7c2e4 test(bulk-upload): fix ScopeCard spec assertions to match actual component classes
/brand-mint/ never matched (component uses border-accent bg-accent-bg);
companion test also updated to assert the meaningful negative.
getByText('5') fixed to exact:true to avoid strict-mode ambiguity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 09:03:57 +02:00
6 changed files with 52 additions and 7 deletions

View File

@@ -140,6 +140,25 @@ describe('BulkDocumentEditLayout', () => {
expect(metadataJson).toHaveProperty('tagNames');
});
it('save() navigates to /documents when all chunks succeed', async () => {
const mockFetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ created: [], updated: [], errors: [] })
});
vi.stubGlobal('fetch', mockFetch);
const { container } = render(BulkDocumentEditLayout, {});
await addFilesViaInput(container, [makeFile('doc.pdf')]);
const saveBtn = container.querySelector(
'button[data-testid="bulk-save-btn"]'
) as HTMLButtonElement;
saveBtn.click();
await vi.waitFor(() => expect(mockFetch).toHaveBeenCalledTimes(1), { timeout: 3000 });
await vi.waitFor(() => expect(goto).toHaveBeenCalledWith('/documents'), { timeout: 3000 });
});
it('save() does not navigate when chunk returns non-ok response', async () => {
const mockFetch = vi.fn().mockResolvedValue({
ok: false,

View File

@@ -103,6 +103,7 @@ $effect(() => {
>
<span class="max-w-[8rem] truncate" title={entry.title}>{entry.title}</span>
{#if entry.status === 'error'}
<span class="sr-only">{m.bulk_file_error_chip_label()}</span>
<span aria-hidden="true" class="ml-0.5 font-extrabold text-red-600">!</span>
{/if}
</button>

View File

@@ -78,6 +78,27 @@ describe('FileSwitcherStrip', () => {
expect(errBtn).not.toBeNull();
});
it('error chip contains a screen-reader-only error label', async () => {
const files: FileEntry[] = [
{
id: 'e1',
file: new File([''], 'bad.pdf'),
title: 'Bad file',
status: 'error',
previewUrl: ''
}
];
const { container } = render(FileSwitcherStrip, {
files,
activeId: 'e1',
onSelect: vi.fn(),
onRemove: vi.fn()
});
const errBtn = container.querySelector('[data-status="error"]');
const srOnly = errBtn?.querySelector('.sr-only');
expect(srOnly).not.toBeNull();
});
it('ArrowRight moves focus to next chip without leaving strip', async () => {
const files = makeFiles(3);
const { container } = render(FileSwitcherStrip, {

View File

@@ -6,21 +6,21 @@ import ScopeCard from './ScopeCard.svelte';
afterEach(cleanup);
describe('ScopeCard', () => {
it('per-file variant has mint background class', async () => {
it('per-file variant has accent background class', async () => {
const { container } = render(ScopeCard, { variant: 'per-file', count: 1 });
const card = container.querySelector('[data-testid="scope-card"]');
expect(card?.className).toMatch(/brand-mint/);
expect(card?.className).toMatch(/bg-accent-bg/);
});
it('shared variant does not have mint background', async () => {
it('shared variant does not have accent background', async () => {
const { container } = render(ScopeCard, { variant: 'shared', count: 3 });
const card = container.querySelector('[data-testid="scope-card"]');
expect(card?.className).not.toMatch(/bg-brand-mint/);
expect(card?.className).not.toMatch(/bg-accent-bg/);
});
it('shared variant renders count badge with file count', async () => {
render(ScopeCard, { variant: 'shared', count: 5 });
await expect.element(page.getByText('5')).toBeInTheDocument();
await expect.element(page.getByText('5', { exact: true })).toBeInTheDocument();
});
it('per-file variant renders slot content', async () => {

View File

@@ -27,7 +27,11 @@ let {
></progress>
{/if}
<div class="flex items-center justify-between gap-3">
<button type="button" onclick={onDiscard} class="text-sm text-red-600/70 hover:text-red-700">
<button
type="button"
onclick={onDiscard}
class="flex min-h-[44px] items-center px-2 text-sm text-red-600/70 hover:text-red-700"
>
{m.bulk_discard_all()}
</button>
<button

View File

@@ -1695,7 +1695,7 @@ export interface components {
/** Format: date */
documentDate?: string;
location?: string;
tags?: string;
tagNames?: string[];
metadataComplete?: boolean;
};
QuickUploadResult: {