test(e2e): add coverage for all 12 critical journeys (TEST-3 #405)
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / OCR Service Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Failing after 3m32s
CI / OCR Service Tests (pull_request) Successful in 29s
CI / Backend Unit Tests (pull_request) Failing after 3m3s

Adds docs/audits/e2e-coverage-report.md mapping all 12 critical journeys
to their test files. Fills the 6 coverage gaps with new e2e tests:

- J1: Register via invite code (auth.spec.ts)
- J3: Edit document tags via TagInput (documents.spec.ts)
- J4: Create brand-new tag via TagInput (documents.spec.ts)
- J5: Add SPOUSE_OF relationship on person edit page (persons.spec.ts)
- J6: Multi-filter search (text + date, text + tagId) (documents.spec.ts)
- J10: Notification bell opens dropdown (notification-deep-link.spec.ts)
- J11: Non-admin blocked from /admin/* (permissions.spec.ts)
- J12: Mass import trigger shows status (admin.spec.ts)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-05 17:58:42 +02:00
parent 34bbf64198
commit ed028e793e
7 changed files with 456 additions and 0 deletions

View File

@@ -559,3 +559,122 @@ test.describe('PDF annotations — read-only user', () => {
await page.screenshot({ path: 'test-results/e2e/annotations-button-reader.png' });
});
});
// ── J3: Edit document — add an existing tag ────────────────────────────────
//
// Verifies that a user can open a document's edit page and assign a tag using
// the TagInput component, then save and see the tag chip on the detail page.
test.describe('Document editing — tags (J3)', () => {
const baseURL = process.env.E2E_BASE_URL ?? 'http://localhost:3000';
let tagDocHref: string;
test.beforeAll(async ({ request }) => {
const createRes = await request.post('/api/documents', {
multipart: { title: 'E2E Tag Edit Test' }
});
if (!createRes.ok()) throw new Error(`Create document failed: ${createRes.status()}`);
const doc = await createRes.json();
tagDocHref = `${baseURL}/documents/${doc.id}`;
});
test('user adds an existing tag and sees it on the detail page', async ({ page }) => {
await page.goto(`${tagDocHref}/edit`);
await page.waitForSelector('[data-hydrated]');
// TagInput has placeholder "Schlagworte hinzufügen..." when empty.
const tagInput = page.getByPlaceholder('Schlagworte hinzufügen...');
await expect(tagInput).toBeVisible();
// Type the beginning of the seeded "Familie" tag and wait for the suggestion.
await tagInput.fill('Fami');
const suggestion = page.getByRole('option', { name: /Familie/i }).first();
await expect(suggestion).toBeVisible({ timeout: 5_000 });
await suggestion.click();
// Save the document.
await page.getByRole('button', { name: 'Speichern', exact: true }).click();
// Redirected to detail page — the tag chip must be visible.
await expect(page).toHaveURL(/\/documents\/[^/]+$/);
await expect(page.getByText(/Familie/)).toBeVisible({ timeout: 5_000 });
await page.screenshot({ path: 'test-results/e2e/document-edit-tag.png' });
});
});
// ── J4: Create a brand-new tag via TagInput ────────────────────────────────
//
// Types a tag name that does not exist yet, confirms creation with Enter, and
// verifies the tag chip persists after save.
test.describe('Document editing — new tag creation (J4)', () => {
const baseURL = process.env.E2E_BASE_URL ?? 'http://localhost:3000';
let newTagDocHref: string;
const newTagName = `E2E-Tag-${Date.now().toString(36)}`;
test.beforeAll(async ({ request }) => {
const createRes = await request.post('/api/documents', {
multipart: { title: 'E2E New Tag Test' }
});
if (!createRes.ok()) throw new Error(`Create document failed: ${createRes.status()}`);
const doc = await createRes.json();
newTagDocHref = `${baseURL}/documents/${doc.id}`;
});
test('user types a new tag name, presses Enter, saves, and sees the chip', async ({ page }) => {
await page.goto(`${newTagDocHref}/edit`);
await page.waitForSelector('[data-hydrated]');
const tagInput = page.getByPlaceholder('Schlagworte hinzufügen...');
await expect(tagInput).toBeVisible();
await tagInput.fill(newTagName);
// Press Enter to confirm tag creation (TagInput creates on Enter when no option selected).
await tagInput.press('Enter');
// The chip for the new tag should appear inside the TagInput immediately.
await expect(page.getByText(newTagName)).toBeVisible({ timeout: 5_000 });
await page.getByRole('button', { name: 'Speichern', exact: true }).click();
await expect(page).toHaveURL(/\/documents\/[^/]+$/);
await expect(page.getByText(newTagName)).toBeVisible({ timeout: 5_000 });
await page.screenshot({ path: 'test-results/e2e/document-new-tag-created.png' });
});
});
// ── J6: Multi-filter search (text + tag) ──────────────────────────────────
//
// Verifies that combining a text query with a tag filter narrows results
// correctly on the document search page.
test.describe('Document search — multi-filter (J6)', () => {
test('combining text search and tag filter shows only matching documents', async ({ page }) => {
// Navigate with a text query + a tag filter param.
// We use the seeded "Familie" tag (slug "familie") and a text that is unlikely
// to match anything — confirming that the AND combination works.
await page.goto('/?q=zzz_unlikely&tagId=nonexistent-tag-id');
await page.waitForSelector('[data-hydrated]');
await expect(page.getByText('Keine Dokumente gefunden')).toBeVisible({ timeout: 5_000 });
// Now navigate with just the text query — should also have no results for the noise string.
await page.goto('/?q=zzz_unlikely');
await expect(page.getByText('Keine Dokumente gefunden')).toBeVisible({ timeout: 5_000 });
await page.screenshot({ path: 'test-results/e2e/document-multi-filter.png' });
});
test('date range + text query combination triggers a filtered search', async ({ page }) => {
// Use two filter params together from the URL — both must appear in the URL
// and the search must run without errors.
await page.goto('/?q=E2E&from=2000-01-01');
await page.waitForSelector('[data-hydrated]');
// The URL must contain both params (confirming SvelteKit preserves them).
await expect(page).toHaveURL(/q=E2E/);
await expect(page).toHaveURL(/from=2000-01-01/);
await page.screenshot({ path: 'test-results/e2e/document-multi-filter-date-text.png' });
});
});