Files
med-notes/.pnpm-store/v10/files/cf/1597be4fc465543b97020b3ecc6df329d25f91f5bba782f538408f123619dc61962a5444c8d5d00bcf230ab992795f96fea8d2d64b6b93ba442414a86d4011
2025-05-09 05:30:08 +02:00

275 lines
7.9 KiB
Plaintext

import { beforeAll, describe, expect, it, vi } from 'vitest';
import { render } from '@testing-library/react';
import { pdfjs } from '../index.test.js';
import TextLayer from './TextLayer.js';
import failingPage from '../../../../__mocks__/_failing_page.js';
import { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../../test-utils.js';
import PageContext from '../PageContext.js';
import type { PDFPageProxy } from 'pdfjs-dist';
import type { TextContent } from 'pdfjs-dist/types/src/display/api.js';
import type { PageContextType } from '../shared/types.js';
const pdfFile = loadPDF('./../../__mocks__/_pdf.pdf');
const untaggedPdfFile = loadPDF('./../../__mocks__/_untagged.pdf');
function renderWithContext(children: React.ReactNode, context: Partial<PageContextType>) {
const { rerender, ...otherResult } = render(
<PageContext.Provider value={context as PageContextType}>{children}</PageContext.Provider>,
);
return {
...otherResult,
rerender: (nextChildren: React.ReactNode, nextContext: Partial<PageContextType> = context) =>
rerender(
<PageContext.Provider value={nextContext as PageContextType}>
{nextChildren}
</PageContext.Provider>,
),
};
}
function getTextItems(container: HTMLElement) {
const wrapper = container.firstElementChild as HTMLDivElement;
return wrapper.querySelectorAll('[role="presentation"]');
}
describe('TextLayer', () => {
// Loaded page
let page: PDFPageProxy;
let page2: PDFPageProxy;
// Loaded page text items
let desiredTextItems: TextContent['items'];
let desiredTextItems2: TextContent['items'];
beforeAll(async () => {
const pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;
page = await pdf.getPage(1);
const textContent = await page.getTextContent();
desiredTextItems = textContent.items;
page2 = await pdf.getPage(2);
const textContent2 = await page2.getTextContent();
desiredTextItems2 = textContent2.items;
});
describe('loading', () => {
it('loads text content and calls onGetTextSuccess callback properly', async () => {
const { func: onGetTextSuccess, promise: onGetTextSuccessPromise } = makeAsyncCallback();
renderWithContext(<TextLayer />, {
onGetTextSuccess,
page,
});
expect.assertions(1);
await expect(onGetTextSuccessPromise).resolves.toMatchObject([{ items: desiredTextItems }]);
});
it('calls onGetTextError when failed to load text content', async () => {
const { func: onGetTextError, promise: onGetTextErrorPromise } = makeAsyncCallback();
muteConsole();
renderWithContext(<TextLayer />, {
onGetTextError,
page: failingPage,
});
expect.assertions(1);
await expect(onGetTextErrorPromise).resolves.toMatchObject([expect.any(Error)]);
restoreConsole();
});
it('replaces text content properly', async () => {
const { func: onGetTextSuccess, promise: onGetTextSuccessPromise } = makeAsyncCallback();
const { rerender } = renderWithContext(<TextLayer />, {
onGetTextSuccess,
page,
});
expect.assertions(2);
await expect(onGetTextSuccessPromise).resolves.toMatchObject([
{
items: desiredTextItems,
},
]);
const { func: onGetTextSuccess2, promise: onGetTextSuccessPromise2 } = makeAsyncCallback();
rerender(<TextLayer />, {
onGetTextSuccess: onGetTextSuccess2,
page: page2,
});
await expect(onGetTextSuccessPromise2).resolves.toMatchObject([
{
items: desiredTextItems2,
},
]);
});
it('throws an error when placed outside Page', () => {
muteConsole();
expect(() => render(<TextLayer />)).toThrow();
restoreConsole();
});
});
describe('rendering', () => {
it('renders text content properly', async () => {
const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =
makeAsyncCallback();
const { container } = renderWithContext(<TextLayer />, { onRenderTextLayerSuccess, page });
expect.assertions(1);
await onRenderTextLayerSuccessPromise;
const textItems = getTextItems(container);
expect(textItems).toHaveLength(desiredTextItems.length);
});
it('renders text content properly given customTextRenderer', async () => {
const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =
makeAsyncCallback();
const customTextRenderer = vi.fn();
const { container } = renderWithContext(<TextLayer />, {
customTextRenderer,
onRenderTextLayerSuccess,
page,
});
expect.assertions(1);
await onRenderTextLayerSuccessPromise;
const textItems = getTextItems(container);
expect(textItems).toHaveLength(desiredTextItems.length);
});
it('maps textContent items to actual TextLayer children properly', async () => {
const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =
makeAsyncCallback();
const { container, rerender } = renderWithContext(<TextLayer />, {
onRenderTextLayerSuccess,
page,
});
expect.assertions(1);
await onRenderTextLayerSuccessPromise;
const textItems = getTextItems(container);
const { func: onRenderTextLayerSuccess2, promise: onRenderTextLayerSuccessPromise2 } =
makeAsyncCallback();
const customTextRenderer = (item: { str: string }) => item.str;
rerender(<TextLayer />, {
customTextRenderer,
onRenderTextLayerSuccess: onRenderTextLayerSuccess2,
page,
});
await onRenderTextLayerSuccessPromise2;
const textItems2 = getTextItems(container);
expect(textItems).toEqual(textItems2);
});
it('calls customTextRenderer with necessary arguments', async () => {
const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =
makeAsyncCallback();
const customTextRenderer = vi.fn();
const { container } = renderWithContext(<TextLayer />, {
customTextRenderer,
onRenderTextLayerSuccess,
page,
});
expect.assertions(3);
await onRenderTextLayerSuccessPromise;
const textItems = getTextItems(container);
expect(textItems).toHaveLength(desiredTextItems.length);
expect(customTextRenderer).toHaveBeenCalledTimes(desiredTextItems.length);
expect(customTextRenderer).toHaveBeenCalledWith(
expect.objectContaining({
str: expect.any(String),
itemIndex: expect.any(Number),
}),
);
});
it('renders text content properly given customTextRenderer', async () => {
const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =
makeAsyncCallback();
const customTextRenderer = () => 'Test value';
const { container } = renderWithContext(<TextLayer />, {
customTextRenderer,
onRenderTextLayerSuccess,
page,
});
expect.assertions(1);
await onRenderTextLayerSuccessPromise;
expect(container).toHaveTextContent('Test value');
});
it('renders text content properly given customTextRenderer and untagged document', async () => {
const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =
makeAsyncCallback();
const customTextRenderer = () => 'Test value';
const untaggedDoc = await pdfjs.getDocument({ data: untaggedPdfFile.arrayBuffer }).promise;
const untaggedPage = await untaggedDoc.getPage(1);
const { container } = renderWithContext(<TextLayer />, {
customTextRenderer,
onRenderTextLayerSuccess,
page: untaggedPage,
});
expect.assertions(1);
await onRenderTextLayerSuccessPromise;
expect(container).toHaveTextContent('Test value');
});
});
});