test(coverage): drive browser tests to 80% on all metrics (#496) #505
@@ -5,6 +5,7 @@ import PersonChipRow from '$lib/person/PersonChipRow.svelte';
|
||||
import OverflowPillButton from '$lib/shared/primitives/OverflowPillButton.svelte';
|
||||
import DocumentMetadataDrawer from './DocumentMetadataDrawer.svelte';
|
||||
import DocumentTopBarTitle from './DocumentTopBarTitle.svelte';
|
||||
import DocumentTopBarActions from './DocumentTopBarActions.svelte';
|
||||
import DocumentMobileMenu from './DocumentMobileMenu.svelte';
|
||||
import BackButton from '$lib/shared/primitives/BackButton.svelte';
|
||||
|
||||
@@ -120,85 +121,15 @@ const overflowPersons = $derived(receivers.slice(2));
|
||||
|
||||
<!-- Action buttons -->
|
||||
<div class="flex shrink-0 items-center gap-1.5 font-sans">
|
||||
{#if canWrite && isPdf && !transcribeMode}
|
||||
<button
|
||||
onclick={() => (transcribeMode = true)}
|
||||
aria-label={m.transcription_mode_label()}
|
||||
aria-pressed={false}
|
||||
class="hidden items-center gap-1.5 rounded border border-primary px-3 py-1.5 font-sans text-[16px] font-medium text-ink transition hover:bg-primary hover:text-primary-fg focus-visible:ring-2 focus-visible:ring-primary md:flex"
|
||||
>
|
||||
<svg
|
||||
class="h-5 w-5 shrink-0"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
|
||||
/>
|
||||
</svg>
|
||||
{m.transcription_mode_label()}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if transcribeMode}
|
||||
<button
|
||||
onclick={() => (transcribeMode = false)}
|
||||
aria-label={m.transcription_mode_stop()}
|
||||
aria-pressed={true}
|
||||
class="flex items-center gap-1.5 rounded bg-primary px-3 py-1.5 font-sans text-[16px] font-medium text-primary-fg transition focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
<svg
|
||||
class="h-5 w-5 shrink-0"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
|
||||
/>
|
||||
</svg>
|
||||
{m.transcription_mode_stop()}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if canWrite && !transcribeMode}
|
||||
<a
|
||||
href="/documents/{doc.id}/edit"
|
||||
aria-label={m.btn_edit()}
|
||||
class="flex items-center gap-1.5 rounded border border-primary bg-transparent px-3 py-1.5 text-[16px] font-medium text-ink transition hover:bg-primary hover:text-primary-fg focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
<img
|
||||
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Edit-Content-MD.svg"
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
class="h-5 w-5"
|
||||
/>
|
||||
<span class="hidden sm:inline">{m.btn_edit()}</span>
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
{#if doc.filePath && !transcribeMode}
|
||||
<a
|
||||
href={fileUrl}
|
||||
download={doc.originalFilename}
|
||||
class="hidden rounded border border-transparent bg-muted p-1.5 text-ink transition hover:bg-accent focus-visible:ring-2 focus-visible:ring-primary md:block"
|
||||
title={m.doc_download_title()}
|
||||
>
|
||||
<img
|
||||
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Download-MD.svg"
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
class="h-5 w-5 shrink-0"
|
||||
/>
|
||||
</a>
|
||||
{/if}
|
||||
<DocumentTopBarActions
|
||||
documentId={doc.id}
|
||||
canWrite={canWrite}
|
||||
isPdf={!!isPdf}
|
||||
bind:transcribeMode={transcribeMode}
|
||||
filePath={doc.filePath}
|
||||
originalFilename={doc.originalFilename}
|
||||
fileUrl={fileUrl}
|
||||
/>
|
||||
|
||||
{#if (canWrite && isPdf) || doc.filePath}
|
||||
<div class="md:hidden">
|
||||
|
||||
103
frontend/src/lib/document/DocumentTopBarActions.svelte
Normal file
103
frontend/src/lib/document/DocumentTopBarActions.svelte
Normal file
@@ -0,0 +1,103 @@
|
||||
<script lang="ts">
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
|
||||
type Props = {
|
||||
documentId: string;
|
||||
canWrite: boolean;
|
||||
isPdf: boolean;
|
||||
transcribeMode: boolean;
|
||||
filePath?: string | null;
|
||||
originalFilename?: string | null;
|
||||
fileUrl: string;
|
||||
};
|
||||
|
||||
let {
|
||||
documentId,
|
||||
canWrite,
|
||||
isPdf,
|
||||
transcribeMode = $bindable(),
|
||||
filePath = null,
|
||||
originalFilename = null,
|
||||
fileUrl
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if canWrite && isPdf && !transcribeMode}
|
||||
<button
|
||||
onclick={() => (transcribeMode = true)}
|
||||
aria-label={m.transcription_mode_label()}
|
||||
aria-pressed={false}
|
||||
class="hidden items-center gap-1.5 rounded border border-primary px-3 py-1.5 font-sans text-[16px] font-medium text-ink transition hover:bg-primary hover:text-primary-fg focus-visible:ring-2 focus-visible:ring-primary md:flex"
|
||||
>
|
||||
<svg
|
||||
class="h-5 w-5 shrink-0"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
|
||||
/>
|
||||
</svg>
|
||||
{m.transcription_mode_label()}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if transcribeMode}
|
||||
<button
|
||||
onclick={() => (transcribeMode = false)}
|
||||
aria-label={m.transcription_mode_stop()}
|
||||
aria-pressed={true}
|
||||
class="flex items-center gap-1.5 rounded bg-primary px-3 py-1.5 font-sans text-[16px] font-medium text-primary-fg transition focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
<svg
|
||||
class="h-5 w-5 shrink-0"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
|
||||
/>
|
||||
</svg>
|
||||
{m.transcription_mode_stop()}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if canWrite && !transcribeMode}
|
||||
<a
|
||||
href="/documents/{documentId}/edit"
|
||||
aria-label={m.btn_edit()}
|
||||
class="flex items-center gap-1.5 rounded border border-primary bg-transparent px-3 py-1.5 text-[16px] font-medium text-ink transition hover:bg-primary hover:text-primary-fg focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
<img
|
||||
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Edit-Content-MD.svg"
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
class="h-5 w-5"
|
||||
/>
|
||||
<span class="hidden sm:inline">{m.btn_edit()}</span>
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
{#if filePath && !transcribeMode}
|
||||
<a
|
||||
href={fileUrl}
|
||||
download={originalFilename}
|
||||
class="hidden rounded border border-transparent bg-muted p-1.5 text-ink transition hover:bg-accent focus-visible:ring-2 focus-visible:ring-primary md:block"
|
||||
title={m.doc_download_title()}
|
||||
>
|
||||
<img
|
||||
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Download-MD.svg"
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
class="h-5 w-5 shrink-0"
|
||||
/>
|
||||
</a>
|
||||
{/if}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { describe, it, expect, afterEach } from 'vitest';
|
||||
import { cleanup, render } from 'vitest-browser-svelte';
|
||||
import { page } from 'vitest/browser';
|
||||
import DocumentTopBarActions from './DocumentTopBarActions.svelte';
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
const baseProps = {
|
||||
documentId: 'd1',
|
||||
canWrite: false,
|
||||
isPdf: false,
|
||||
transcribeMode: false,
|
||||
filePath: null as string | null,
|
||||
originalFilename: 'brief.pdf' as string | null,
|
||||
fileUrl: ''
|
||||
};
|
||||
|
||||
describe('DocumentTopBarActions', () => {
|
||||
it('renders nothing visible when canWrite is false and no file is present', async () => {
|
||||
render(DocumentTopBarActions, { props: baseProps });
|
||||
|
||||
await expect
|
||||
.element(page.getByRole('button', { name: /transkribieren/i }))
|
||||
.not.toBeInTheDocument();
|
||||
await expect.element(page.getByRole('link', { name: /bearbeiten/i })).not.toBeInTheDocument();
|
||||
await expect.element(page.getByTitle('Herunterladen')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the transcribe button when canWrite, isPdf, and not transcribing', async () => {
|
||||
render(DocumentTopBarActions, {
|
||||
props: { ...baseProps, canWrite: true, isPdf: true, filePath: 'docs/x.pdf' }
|
||||
});
|
||||
|
||||
await expect.element(page.getByRole('button', { name: /transkribieren/i })).toBeVisible();
|
||||
});
|
||||
|
||||
it('omits the transcribe button when not a PDF', async () => {
|
||||
render(DocumentTopBarActions, {
|
||||
props: { ...baseProps, canWrite: true, isPdf: false, filePath: 'docs/x.jpg' }
|
||||
});
|
||||
|
||||
await expect
|
||||
.element(page.getByRole('button', { name: /transkribieren/i }))
|
||||
.not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the stop-transcribe button when transcribeMode is true', async () => {
|
||||
render(DocumentTopBarActions, {
|
||||
props: {
|
||||
...baseProps,
|
||||
canWrite: true,
|
||||
isPdf: true,
|
||||
transcribeMode: true,
|
||||
filePath: 'docs/x.pdf'
|
||||
}
|
||||
});
|
||||
|
||||
await expect.element(page.getByRole('button', { name: /fertig/i })).toBeVisible();
|
||||
});
|
||||
|
||||
it('renders the edit link to the document edit route when canWrite and not transcribing', async () => {
|
||||
render(DocumentTopBarActions, {
|
||||
props: { ...baseProps, canWrite: true, documentId: 'doc-42' }
|
||||
});
|
||||
|
||||
await expect
|
||||
.element(page.getByRole('link', { name: /bearbeiten/i }))
|
||||
.toHaveAttribute('href', '/documents/doc-42/edit');
|
||||
});
|
||||
|
||||
it('hides the edit link when transcribeMode is true', async () => {
|
||||
render(DocumentTopBarActions, {
|
||||
props: { ...baseProps, canWrite: true, transcribeMode: true }
|
||||
});
|
||||
|
||||
await expect.element(page.getByRole('link', { name: /bearbeiten/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the download link when filePath is set and not in transcribe mode', async () => {
|
||||
render(DocumentTopBarActions, {
|
||||
props: { ...baseProps, filePath: 'docs/x.pdf', fileUrl: '/api/docs/x' }
|
||||
});
|
||||
|
||||
await expect.element(page.getByTitle('Herunterladen')).toBeVisible();
|
||||
});
|
||||
|
||||
it('hides the download link when transcribeMode is true', async () => {
|
||||
render(DocumentTopBarActions, {
|
||||
props: { ...baseProps, filePath: 'docs/x.pdf', fileUrl: '/api/docs/x', transcribeMode: true }
|
||||
});
|
||||
|
||||
await expect.element(page.getByTitle('Herunterladen')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user