test(pdf-viewer): port PdfViewer.svelte.test.ts to libLoader prop injection — remove vi.mock
Removes both vi.mock('pdfjs-dist', …) calls that caused the birpc teardown
race (ADR 012). Replaces with static import + makeFakeLibLoader() helper
injected via the libLoader prop on every render() call.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,18 @@
|
|||||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||||
import { cleanup, render } from 'vitest-browser-svelte';
|
import { cleanup, render } from 'vitest-browser-svelte';
|
||||||
import { page } from 'vitest/browser';
|
import { page } from 'vitest/browser';
|
||||||
|
import type { LibLoader } from '$lib/document/viewer/usePdfRenderer.svelte';
|
||||||
|
import PdfViewer from './PdfViewer.svelte';
|
||||||
|
|
||||||
vi.mock('pdfjs-dist', () => {
|
afterEach(cleanup);
|
||||||
function TextLayerMock() {}
|
|
||||||
TextLayerMock.prototype.render = () => Promise.resolve();
|
function makeFakePdfjsLib() {
|
||||||
TextLayerMock.prototype.cancel = () => {};
|
class TextLayerMock {
|
||||||
|
render() {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
cancel() {}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
GlobalWorkerOptions: { workerSrc: '' },
|
GlobalWorkerOptions: { workerSrc: '' },
|
||||||
@@ -20,24 +27,23 @@ vi.mock('pdfjs-dist', () => {
|
|||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
TextLayer: TextLayerMock
|
TextLayer: TextLayerMock
|
||||||
};
|
} as unknown as typeof import('pdfjs-dist');
|
||||||
});
|
}
|
||||||
|
|
||||||
vi.mock('pdfjs-dist/build/pdf.worker.min.mjs?url', () => ({ default: '' }));
|
function makeFakeLibLoader(): LibLoader {
|
||||||
|
const fakePdfjs = makeFakePdfjsLib();
|
||||||
const { default: PdfViewer } = await import('./PdfViewer.svelte');
|
return vi.fn().mockResolvedValue([fakePdfjs, { default: '' }] as const);
|
||||||
|
}
|
||||||
afterEach(cleanup);
|
|
||||||
|
|
||||||
describe('PdfViewer — empty / error states', () => {
|
describe('PdfViewer — empty / error states', () => {
|
||||||
it('renders the no-file placeholder when url is empty', async () => {
|
it('renders the no-file placeholder when url is empty', async () => {
|
||||||
render(PdfViewer, { url: '' });
|
render(PdfViewer, { url: '', libLoader: makeFakeLibLoader() });
|
||||||
|
|
||||||
await expect.element(page.getByText('Keine Datei vorhanden')).toBeVisible();
|
await expect.element(page.getByText('Keine Datei vorhanden')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not render the controls when url is empty', async () => {
|
it('does not render the controls when url is empty', async () => {
|
||||||
render(PdfViewer, { url: '' });
|
render(PdfViewer, { url: '', libLoader: makeFakeLibLoader() });
|
||||||
|
|
||||||
const buttons = document.querySelectorAll('button');
|
const buttons = document.querySelectorAll('button');
|
||||||
expect(buttons.length).toBe(0);
|
expect(buttons.length).toBe(0);
|
||||||
@@ -49,10 +55,10 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
render(PdfViewer, {
|
render(PdfViewer, {
|
||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test',
|
documentId: 'test',
|
||||||
annotationReloadKey: 0
|
annotationReloadKey: 0,
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
// PdfControls renders its nav + zoom buttons once the document.promise resolves.
|
|
||||||
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
||||||
await expect.element(page.getByRole('button', { name: 'Weiter' })).toBeVisible();
|
await expect.element(page.getByRole('button', { name: 'Weiter' })).toBeVisible();
|
||||||
await expect.element(page.getByRole('button', { name: 'Vergrößern' })).toBeVisible();
|
await expect.element(page.getByRole('button', { name: 'Vergrößern' })).toBeVisible();
|
||||||
@@ -63,7 +69,8 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
render(PdfViewer, {
|
render(PdfViewer, {
|
||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test',
|
documentId: 'test',
|
||||||
annotationsDimmed: true
|
annotationsDimmed: true,
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
await vi.waitFor(() => {
|
await vi.waitFor(() => {
|
||||||
@@ -96,11 +103,10 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test',
|
documentId: 'test',
|
||||||
transcribeMode: true,
|
transcribeMode: true,
|
||||||
documentFileHash: 'match'
|
documentFileHash: 'match',
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
// transcribeMode forces showAnnotations=true; toggle button surfaces with "hide" label
|
|
||||||
// (only when annotationCount > 0).
|
|
||||||
await expect
|
await expect
|
||||||
.element(page.getByRole('button', { name: /annotierungen verbergen/i }))
|
.element(page.getByRole('button', { name: /annotierungen verbergen/i }))
|
||||||
.toBeVisible();
|
.toBeVisible();
|
||||||
@@ -113,7 +119,8 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
render(PdfViewer, {
|
render(PdfViewer, {
|
||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test',
|
documentId: 'test',
|
||||||
documentFileHash: 'abc123'
|
documentFileHash: 'abc123',
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
await vi.waitFor(() => {
|
await vi.waitFor(() => {
|
||||||
@@ -125,7 +132,8 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
render(PdfViewer, {
|
render(PdfViewer, {
|
||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test',
|
documentId: 'test',
|
||||||
flashAnnotationId: 'ann-flashing'
|
flashAnnotationId: 'ann-flashing',
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
||||||
@@ -135,7 +143,8 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
render(PdfViewer, {
|
render(PdfViewer, {
|
||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test',
|
documentId: 'test',
|
||||||
blockNumbers: { 'ann-1': 1, 'ann-2': 2 }
|
blockNumbers: { 'ann-1': 1, 'ann-2': 2 },
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
||||||
@@ -145,7 +154,8 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
render(PdfViewer, {
|
render(PdfViewer, {
|
||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test',
|
documentId: 'test',
|
||||||
activeAnnotationId: 'ann-1'
|
activeAnnotationId: 'ann-1',
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
||||||
@@ -156,10 +166,10 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test',
|
documentId: 'test',
|
||||||
transcribeMode: true,
|
transcribeMode: true,
|
||||||
activeAnnotationId: 'ann-1'
|
activeAnnotationId: 'ann-1',
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Without an annotations fetch, the visibility toggle is hidden — just assert the always-on nav.
|
|
||||||
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
||||||
await expect.element(page.getByRole('button', { name: 'Weiter' })).toBeVisible();
|
await expect.element(page.getByRole('button', { name: 'Weiter' })).toBeVisible();
|
||||||
});
|
});
|
||||||
@@ -169,7 +179,8 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
render(PdfViewer, {
|
render(PdfViewer, {
|
||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test',
|
documentId: 'test',
|
||||||
onAnnotationClick
|
onAnnotationClick,
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
||||||
@@ -199,7 +210,8 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
render(PdfViewer, {
|
render(PdfViewer, {
|
||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test',
|
documentId: 'test',
|
||||||
documentFileHash: 'new-hash'
|
documentFileHash: 'new-hash',
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
await vi.waitFor(() => {
|
await vi.waitFor(() => {
|
||||||
@@ -234,10 +246,10 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
render(PdfViewer, {
|
render(PdfViewer, {
|
||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test',
|
documentId: 'test',
|
||||||
documentFileHash: 'matching-hash'
|
documentFileHash: 'matching-hash',
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Controls finish mounting, and the outdated notice stays absent.
|
|
||||||
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
||||||
expect(document.querySelector('[data-testid="annotation-outdated-notice"]')).toBeNull();
|
expect(document.querySelector('[data-testid="annotation-outdated-notice"]')).toBeNull();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -250,10 +262,10 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
try {
|
try {
|
||||||
render(PdfViewer, {
|
render(PdfViewer, {
|
||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test'
|
documentId: 'test',
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
// PDF rendering does not depend on the annotations fetch — controls still appear.
|
|
||||||
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
||||||
expect(document.querySelector('[data-testid="annotation-outdated-notice"]')).toBeNull();
|
expect(document.querySelector('[data-testid="annotation-outdated-notice"]')).toBeNull();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -268,7 +280,8 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
try {
|
try {
|
||||||
render(PdfViewer, {
|
render(PdfViewer, {
|
||||||
url: '/api/documents/test/file',
|
url: '/api/documents/test/file',
|
||||||
documentId: 'test'
|
documentId: 'test',
|
||||||
|
libLoader: makeFakeLibLoader()
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
await expect.element(page.getByRole('button', { name: 'Zurück' })).toBeVisible();
|
||||||
@@ -277,4 +290,21 @@ describe('PdfViewer — loaded state', () => {
|
|||||||
fetchSpy.mockRestore();
|
fetchSpy.mockRestore();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shows previous and next page navigation buttons', async () => {
|
||||||
|
render(PdfViewer, { url: '/api/documents/test-id/file', libLoader: makeFakeLibLoader() });
|
||||||
|
await expect.element(page.getByRole('button', { name: /zurück/i })).toBeInTheDocument();
|
||||||
|
await expect.element(page.getByRole('button', { name: /weiter/i })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows zoom controls', async () => {
|
||||||
|
render(PdfViewer, { url: '/api/documents/test-id/file', libLoader: makeFakeLibLoader() });
|
||||||
|
await expect.element(page.getByRole('button', { name: /vergrößern/i })).toBeInTheDocument();
|
||||||
|
await expect.element(page.getByRole('button', { name: /verkleinern/i })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the page counter once the PDF has loaded', async () => {
|
||||||
|
render(PdfViewer, { url: '/api/documents/test-id/file', libLoader: makeFakeLibLoader() });
|
||||||
|
await expect.element(page.getByText(/1\s*\/\s*2/)).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user