All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m46s
CI / OCR Service Tests (pull_request) Successful in 20s
CI / Backend Unit Tests (pull_request) Successful in 3m50s
CI / fail2ban Regex (pull_request) Successful in 44s
CI / Semgrep Security Scan (pull_request) Successful in 20s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m1s
The top bar now renders document dates through formatDocumentDate, so a DAY-precision date like 1923-04-15 renders as "15. April 1923" (de) via Intl.DateTimeFormat — no longer the old short "15.04.1923". These two browser-project specs still asserted the old short form and were never updated (CI-only, not run locally by prior agents). Refs #666 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
196 lines
6.3 KiB
TypeScript
196 lines
6.3 KiB
TypeScript
import { describe, it, expect, afterEach } from 'vitest';
|
|
import { cleanup, render } from 'vitest-browser-svelte';
|
|
import { page } from 'vitest/browser';
|
|
import DocumentTopBar from './DocumentTopBar.svelte';
|
|
|
|
afterEach(cleanup);
|
|
|
|
const sender = { id: 's1', firstName: 'Anna', lastName: 'Schmidt', displayName: 'Anna Schmidt' };
|
|
const receiver = { id: 'r1', firstName: 'Bert', lastName: 'Meier', displayName: 'Bert Meier' };
|
|
|
|
const baseDoc = {
|
|
id: 'd1',
|
|
title: 'Brief an Helene',
|
|
originalFilename: 'brief.pdf',
|
|
documentDate: '1923-04-15',
|
|
sender,
|
|
receivers: [receiver],
|
|
filePath: null as string | null,
|
|
contentType: null as string | null,
|
|
location: null,
|
|
status: 'UPLOADED',
|
|
tags: [] as { id: string; name: string }[]
|
|
};
|
|
|
|
const baseProps = (overrides: Record<string, unknown> = {}) => ({
|
|
doc: baseDoc,
|
|
canWrite: false,
|
|
fileUrl: '',
|
|
transcribeMode: false,
|
|
inferredRelationship: null,
|
|
geschichten: [],
|
|
canBlogWrite: false,
|
|
...overrides
|
|
});
|
|
|
|
describe('DocumentTopBar', () => {
|
|
it('renders the document title as the main heading', async () => {
|
|
render(DocumentTopBar, { props: baseProps() });
|
|
|
|
await expect.element(page.getByRole('heading', { name: 'Brief an Helene' })).toBeVisible();
|
|
});
|
|
|
|
it('falls back to originalFilename when title is missing', async () => {
|
|
render(DocumentTopBar, { props: baseProps({ doc: { ...baseDoc, title: null } }) });
|
|
|
|
await expect.element(page.getByRole('heading', { name: 'brief.pdf' })).toBeVisible();
|
|
});
|
|
|
|
it('renders the precision-aware long documentDate when one is present', async () => {
|
|
render(DocumentTopBar, { props: baseProps() });
|
|
|
|
// documentDate '1923-04-15' with default DAY precision renders the honest
|
|
// long German label via formatDocumentDate (Refs #666), not the old short form.
|
|
await expect.element(page.getByText('15. April 1923')).toBeVisible();
|
|
});
|
|
|
|
it('omits the date paragraph entirely when documentDate is null', async () => {
|
|
render(DocumentTopBar, { props: baseProps({ doc: { ...baseDoc, documentDate: null } }) });
|
|
|
|
await expect.element(page.getByText(/^\d{2}\.\d{2}\.\d{4}$/)).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('does not render the transcribe button when canWrite is false', async () => {
|
|
render(DocumentTopBar, {
|
|
props: baseProps({ doc: { ...baseDoc, filePath: 'x', contentType: 'application/pdf' } })
|
|
});
|
|
|
|
await expect
|
|
.element(page.getByRole('button', { name: /transkribieren/i }))
|
|
.not.toBeInTheDocument();
|
|
});
|
|
|
|
it('does not render the transcribe button when contentType is not PDF', async () => {
|
|
render(DocumentTopBar, {
|
|
props: baseProps({
|
|
canWrite: true,
|
|
doc: { ...baseDoc, filePath: 'x', contentType: 'image/jpeg' }
|
|
})
|
|
});
|
|
|
|
await expect
|
|
.element(page.getByRole('button', { name: /transkribieren/i }))
|
|
.not.toBeInTheDocument();
|
|
});
|
|
|
|
it('renders the transcribe button when canWrite is true and the file is a PDF', async () => {
|
|
render(DocumentTopBar, {
|
|
props: baseProps({
|
|
canWrite: true,
|
|
doc: { ...baseDoc, filePath: 'x', contentType: 'application/pdf' }
|
|
})
|
|
});
|
|
|
|
await expect.element(page.getByRole('button', { name: /transkribieren/i })).toBeVisible();
|
|
});
|
|
|
|
it('renders the stop-transcribe button when transcribeMode is true', async () => {
|
|
render(DocumentTopBar, {
|
|
props: baseProps({
|
|
canWrite: true,
|
|
transcribeMode: true,
|
|
doc: { ...baseDoc, filePath: 'x', contentType: 'application/pdf' }
|
|
})
|
|
});
|
|
|
|
await expect.element(page.getByRole('button', { name: /fertig/i })).toBeVisible();
|
|
});
|
|
|
|
it('hides the edit link when transcribeMode is true', async () => {
|
|
render(DocumentTopBar, {
|
|
props: baseProps({
|
|
canWrite: true,
|
|
transcribeMode: true,
|
|
doc: { ...baseDoc, filePath: 'x', contentType: 'application/pdf' }
|
|
})
|
|
});
|
|
|
|
await expect.element(page.getByRole('link', { name: /bearbeiten/i })).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('renders the edit link when canWrite is true and not in transcribeMode', async () => {
|
|
render(DocumentTopBar, { props: baseProps({ canWrite: true }) });
|
|
|
|
await expect
|
|
.element(page.getByRole('link', { name: /bearbeiten/i }))
|
|
.toHaveAttribute('href', '/documents/d1/edit');
|
|
});
|
|
|
|
it('does not render the edit link when canWrite is false', async () => {
|
|
render(DocumentTopBar, { props: baseProps() });
|
|
|
|
await expect.element(page.getByRole('link', { name: /bearbeiten/i })).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('renders the download link when filePath is present and not in transcribe mode', async () => {
|
|
render(DocumentTopBar, {
|
|
props: baseProps({ doc: { ...baseDoc, filePath: 'docs/x.pdf' }, fileUrl: '/api/docs/x' })
|
|
});
|
|
|
|
await expect.element(page.getByTitle('Herunterladen')).toBeVisible();
|
|
});
|
|
|
|
it('does not render the download link when filePath is null', async () => {
|
|
render(DocumentTopBar, { props: baseProps() });
|
|
|
|
await expect.element(page.getByTitle('Herunterladen')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('opens the metadata drawer when the details toggle is clicked', async () => {
|
|
render(DocumentTopBar, { props: baseProps() });
|
|
|
|
await page.getByRole('button', { name: /^details$/i }).click();
|
|
|
|
await expect
|
|
.element(page.getByRole('button', { name: /^details$/i }))
|
|
.toHaveAttribute('aria-expanded', 'true');
|
|
});
|
|
|
|
it('renders the mobile kebab menu trigger when filePath is present', async () => {
|
|
render(DocumentTopBar, {
|
|
props: baseProps({ doc: { ...baseDoc, filePath: 'docs/x.pdf' } })
|
|
});
|
|
|
|
await expect.element(page.getByRole('button', { name: /weitere aktionen/i })).toBeVisible();
|
|
});
|
|
|
|
it('does not render the mobile kebab menu when there is no filePath and no canWrite/PDF combo', async () => {
|
|
render(DocumentTopBar, { props: baseProps() });
|
|
|
|
await expect
|
|
.element(page.getByRole('button', { name: /weitere aktionen/i }))
|
|
.not.toBeInTheDocument();
|
|
});
|
|
|
|
it('opens the mobile kebab menu when the trigger is clicked', async () => {
|
|
render(DocumentTopBar, {
|
|
props: baseProps({ doc: { ...baseDoc, filePath: 'docs/x.pdf' } })
|
|
});
|
|
|
|
await page.getByRole('button', { name: /weitere aktionen/i }).click();
|
|
|
|
await expect
|
|
.element(page.getByRole('button', { name: /weitere aktionen/i }))
|
|
.toHaveAttribute('aria-expanded', 'true');
|
|
});
|
|
|
|
it('renders the metadata drawer content when detailsOpen is toggled on', async () => {
|
|
render(DocumentTopBar, { props: baseProps() });
|
|
|
|
await page.getByRole('button', { name: /^details$/i }).click();
|
|
|
|
const drawer = document.querySelector('[data-topbar] > div:nth-child(2)');
|
|
expect(drawer).not.toBeNull();
|
|
});
|
|
});
|