Files
med-notes/.pnpm-store/v10/files/94/4b55e554c863fe49575e33af6b0a9c10ea1eb2993e1b24b871139d33de06be17aaa375ac930373d1b23ec1697a483ae9ce9e218f28776e12534be7d257ccc8
2025-05-09 05:30:08 +02:00

361 lines
10 KiB
Plaintext

import { beforeAll, describe, expect, it } from 'vitest';
import { render } from '@testing-library/react';
import { pdfjs } from '../index.test.js';
import AnnotationLayer from './AnnotationLayer.js';
import LinkService from '../LinkService.js';
import failingPage from '../../../../__mocks__/_failing_page.js';
import { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../../test-utils.js';
import DocumentContext from '../DocumentContext.js';
import PageContext from '../PageContext.js';
import type { RenderResult } from '@testing-library/react';
import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';
import type { Annotations, DocumentContextType, PageContextType } from '../shared/types.js';
const pdfFile = loadPDF('./../../__mocks__/_pdf.pdf');
const annotatedPdfFile = loadPDF('./../../__mocks__/_pdf3.pdf');
function renderWithContext(
children: React.ReactNode,
documentContext: Partial<DocumentContextType>,
pageContext: Partial<PageContextType>,
) {
const { rerender, ...otherResult } = render(
<DocumentContext.Provider value={documentContext as DocumentContextType}>
<PageContext.Provider value={pageContext as PageContextType}>{children}</PageContext.Provider>
</DocumentContext.Provider>,
);
const customRerender = (
nextChildren: React.ReactNode,
nextDocumentContext: Partial<DocumentContextType> = documentContext,
nextPageContext: Partial<PageContextType> = pageContext,
) =>
rerender(
<DocumentContext.Provider value={nextDocumentContext as DocumentContextType}>
<PageContext.Provider value={nextPageContext as PageContextType}>
{nextChildren}
</PageContext.Provider>
</DocumentContext.Provider>,
);
return {
...otherResult,
rerender: customRerender,
} as RenderResult & { rerender: typeof customRerender };
}
describe('AnnotationLayer', () => {
const linkService = new LinkService();
// Loaded PDF file
let pdf: PDFDocumentProxy;
// Loaded page
let page: PDFPageProxy;
let page2: PDFPageProxy;
// Loaded page text items
let desiredAnnotations: Annotations;
let desiredAnnotations2: Annotations;
beforeAll(async () => {
pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;
page = await pdf.getPage(1);
desiredAnnotations = await page.getAnnotations();
page2 = await pdf.getPage(2);
desiredAnnotations2 = await page2.getAnnotations();
});
describe('loading', () => {
it('loads annotations and calls onGetAnnotationsSuccess callback properly', async () => {
const { func: onGetAnnotationsSuccess, promise: onGetAnnotationsSuccessPromise } =
makeAsyncCallback();
renderWithContext(
<AnnotationLayer />,
{
linkService,
pdf,
},
{
onGetAnnotationsSuccess,
page,
},
);
expect.assertions(1);
await expect(onGetAnnotationsSuccessPromise).resolves.toMatchObject([desiredAnnotations]);
});
it('calls onGetAnnotationsError when failed to load annotations', async () => {
const { func: onGetAnnotationsError, promise: onGetAnnotationsErrorPromise } =
makeAsyncCallback();
muteConsole();
renderWithContext(
<AnnotationLayer />,
{
linkService,
pdf,
},
{
onGetAnnotationsError,
page: failingPage,
},
);
expect.assertions(1);
await expect(onGetAnnotationsErrorPromise).resolves.toMatchObject([expect.any(Error)]);
restoreConsole();
});
it('replaces annotations properly when page is changed', async () => {
const { func: onGetAnnotationsSuccess, promise: onGetAnnotationsSuccessPromise } =
makeAsyncCallback();
const { rerender } = renderWithContext(
<AnnotationLayer />,
{
linkService,
pdf,
},
{
onGetAnnotationsSuccess,
page,
},
);
expect.assertions(2);
await expect(onGetAnnotationsSuccessPromise).resolves.toMatchObject([desiredAnnotations]);
const { func: onGetAnnotationsSuccess2, promise: onGetAnnotationsSuccessPromise2 } =
makeAsyncCallback();
rerender(
<AnnotationLayer />,
{
linkService,
pdf,
},
{
onGetAnnotationsSuccess: onGetAnnotationsSuccess2,
page: page2,
},
);
await expect(onGetAnnotationsSuccessPromise2).resolves.toMatchObject([desiredAnnotations2]);
});
it('throws an error when placed outside Page', () => {
muteConsole();
expect(() => render(<AnnotationLayer />)).toThrow();
restoreConsole();
});
});
describe('rendering', () => {
it('renders annotations properly', async () => {
const {
func: onRenderAnnotationLayerSuccess,
promise: onRenderAnnotationLayerSuccessPromise,
} = makeAsyncCallback();
const { container } = renderWithContext(
<AnnotationLayer />,
{
linkService,
pdf,
},
{
onRenderAnnotationLayerSuccess,
page,
},
);
expect.assertions(1);
await onRenderAnnotationLayerSuccessPromise;
const wrapper = container.firstElementChild as HTMLDivElement;
const annotationItems = Array.from(wrapper.children);
expect(annotationItems).toHaveLength(desiredAnnotations.length);
});
it.each`
externalLinkTarget | target
${null} | ${''}
${'_self'} | ${'_self'}
${'_blank'} | ${'_blank'}
${'_parent'} | ${'_parent'}
${'_top'} | ${'_top'}
`(
'renders all links with target $target given externalLinkTarget = $externalLinkTarget',
async ({ externalLinkTarget, target }) => {
const {
func: onRenderAnnotationLayerSuccess,
promise: onRenderAnnotationLayerSuccessPromise,
} = makeAsyncCallback();
const customLinkService = new LinkService();
if (externalLinkTarget) {
customLinkService.setExternalLinkTarget(externalLinkTarget);
}
const { container } = renderWithContext(
<AnnotationLayer />,
{
linkService: customLinkService,
pdf,
},
{
onRenderAnnotationLayerSuccess,
page,
},
);
expect.assertions(desiredAnnotations.length);
await onRenderAnnotationLayerSuccessPromise;
const wrapper = container.firstElementChild as HTMLDivElement;
const annotationItems = Array.from(wrapper.children);
const annotationLinkItems = annotationItems
.map((item) => item.firstChild as HTMLElement)
.filter((item) => item.tagName === 'A');
for (const link of annotationLinkItems) {
expect(link.getAttribute('target')).toBe(target);
}
},
);
it.each`
externalLinkRel | rel
${null} | ${'noopener noreferrer nofollow'}
${'noopener'} | ${'noopener'}
`(
'renders all links with rel $rel given externalLinkRel = $externalLinkRel',
async ({ externalLinkRel, rel }) => {
const {
func: onRenderAnnotationLayerSuccess,
promise: onRenderAnnotationLayerSuccessPromise,
} = makeAsyncCallback();
const customLinkService = new LinkService();
if (externalLinkRel) {
customLinkService.setExternalLinkRel(externalLinkRel);
}
const { container } = renderWithContext(
<AnnotationLayer />,
{
linkService: customLinkService,
pdf,
},
{
onRenderAnnotationLayerSuccess,
page,
},
);
expect.assertions(desiredAnnotations.length);
await onRenderAnnotationLayerSuccessPromise;
const wrapper = container.firstElementChild as HTMLDivElement;
const annotationItems = Array.from(wrapper.children);
const annotationLinkItems = annotationItems
.map((item) => item.firstChild as HTMLElement)
.filter((item) => item.tagName === 'A');
for (const link of annotationLinkItems) {
expect(link.getAttribute('rel')).toBe(rel);
}
},
);
it('renders annotations with the default imageResourcesPath given no imageResourcesPath', async () => {
const pdf = await pdfjs.getDocument({ data: annotatedPdfFile.arrayBuffer }).promise;
const annotatedPage = await pdf.getPage(1);
const {
func: onRenderAnnotationLayerSuccess,
promise: onRenderAnnotationLayerSuccessPromise,
} = makeAsyncCallback();
const imageResourcesPath = '';
const desiredImageTagRegExp = new RegExp(
`<img[^>]+src="${imageResourcesPath}annotation-note.svg"`,
);
const { container } = renderWithContext(
<AnnotationLayer />,
{
linkService,
pdf,
},
{
onRenderAnnotationLayerSuccess,
page: annotatedPage,
},
);
expect.assertions(1);
await onRenderAnnotationLayerSuccessPromise;
const stringifiedAnnotationLayerNode = container.outerHTML;
expect(stringifiedAnnotationLayerNode).toMatch(desiredImageTagRegExp);
});
it('renders annotations with the specified imageResourcesPath given imageResourcesPath', async () => {
const pdf = await pdfjs.getDocument({ data: annotatedPdfFile.arrayBuffer }).promise;
const annotatedPage = await pdf.getPage(1);
const {
func: onRenderAnnotationLayerSuccess,
promise: onRenderAnnotationLayerSuccessPromise,
} = makeAsyncCallback();
const imageResourcesPath = '/public/images/';
const desiredImageTagRegExp = new RegExp(
`<img[^>]+src="${imageResourcesPath}annotation-note.svg"`,
);
const { container } = renderWithContext(
<AnnotationLayer />,
{
imageResourcesPath,
linkService,
pdf,
},
{
onRenderAnnotationLayerSuccess,
page: annotatedPage,
},
);
expect.assertions(1);
await onRenderAnnotationLayerSuccessPromise;
const stringifiedAnnotationLayerNode = container.outerHTML;
expect(stringifiedAnnotationLayerNode).toMatch(desiredImageTagRegExp);
});
});
});