Files
bea-edit/tests/e2e/pagination.spec.ts
T
2026-10-28 09:25:15 +01:00

177 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { expect, test, type Page } from '@playwright/test';
import { PDFDocument } from 'pdf-lib';
import {
createEncryptedPdf,
createPdf,
createPreRotatedPdf,
dropFiles,
expectPdfDownload,
getPageContentText,
getPageText,
pdfDropzone,
trackPageErrors
} from './helpers';
const downloadPaginated = async (page: Page, fileName: RegExp) => {
const downloadPromise = page.waitForEvent('download');
await page.getByRole('button', { name: 'Paginieren & herunterladen' }).click();
return expectPdfDownload(await downloadPromise, fileName);
};
test('pagination stamps every page with an Aktenzeichen and the configured start number', async ({
page
}) => {
const pageErrors = trackPageErrors(page);
await page.goto('/tools/pagination');
await dropFiles(page, pdfDropzone(page), [
{ name: 'akte.pdf', mimeType: 'application/pdf', buffer: await createPdf(3, 'Akte') }
]);
await expect(page.getByText('Seite 1 von 3')).toBeVisible();
await page.locator('#pagination-start').fill('26');
await page.locator('#pagination-text').fill('123 Ja 60/26');
await expect(page.getByTestId('pagination-example')).toContainText(
'Beispiel: 123 Ja 60/26, Bl. 26'
);
await page.getByRole('button', { name: 'Nächste Seite' }).click();
await expect(page.getByText('Seite 2 von 3')).toBeVisible();
await expect(page.getByTestId('pagination-preview')).toContainText('123 Ja 60/26, Bl. 27');
const bytes = await downloadPaginated(page, /^akte_paginiert\.pdf$/);
const output = await PDFDocument.load(bytes);
expect(output.getPageCount()).toBe(3);
expect(getPageText(output, 0)).toContain('123 Ja 60/26, Bl. 26');
expect(getPageText(output, 1)).toContain('123 Ja 60/26, Bl. 27');
expect(getPageText(output, 2)).toContain('123 Ja 60/26, Bl. 28');
expect(pageErrors).toEqual([]);
});
test('pagination can keep the additional text on the first page only', async ({ page }) => {
const pageErrors = trackPageErrors(page);
await page.goto('/tools/pagination');
await dropFiles(page, pdfDropzone(page), [
{ name: 'akte.pdf', mimeType: 'application/pdf', buffer: await createPdf(3, 'Akte') }
]);
await page.locator('#pagination-start').fill('7');
await page.locator('#pagination-text').fill('AZ 7/26');
await page.locator('#pagination-format').selectOption('s');
await page.getByLabel('Nur auf der ersten Seite').check();
await expect(page.getByTestId('pagination-example')).toContainText('AZ 7/26, S. 7');
await page.getByRole('button', { name: 'Nächste Seite' }).click();
await expect(page.getByTestId('pagination-preview')).toHaveText('S. 8');
const bytes = await downloadPaginated(page, /^akte_paginiert\.pdf$/);
const output = await PDFDocument.load(bytes);
expect(output.getPageCount()).toBe(3);
expect(getPageText(output, 0)).toContain('AZ 7/26, S. 7');
expect(getPageText(output, 1)).toContain('S. 8');
expect(getPageText(output, 1)).not.toContain('AZ 7/26');
expect(getPageText(output, 2)).toContain('S. 9');
expect(pageErrors).toEqual([]);
});
test('pagination supports number-only labels and jumps to a page', async ({ page }) => {
const pageErrors = trackPageErrors(page);
await page.goto('/tools/pagination');
await dropFiles(page, pdfDropzone(page), [
{ name: 'nummern.pdf', mimeType: 'application/pdf', buffer: await createPdf(11, 'Nummern') }
]);
await page.locator('#pagination-start').fill('42');
await page.locator('#pagination-format').selectOption('nummer');
await expect(page.getByTestId('pagination-example')).toHaveText('Beispiel: 42');
await page.getByLabel('Springe zu Seite').fill('11');
await expect(page.getByText('Seite 11 von 11')).toBeVisible();
await expect(page.getByTestId('pagination-preview')).toHaveText('52');
const bytes = await downloadPaginated(page, /^nummern_paginiert\.pdf$/);
const output = await PDFDocument.load(bytes);
expect(output.getPageCount()).toBe(11);
expect(getPageText(output, 0)).toContain('42');
expect(getPageText(output, 10)).toContain('52');
expect(pageErrors).toEqual([]);
});
test('pagination keeps the page number upright on rotated scans', async ({ page }) => {
const pageErrors = trackPageErrors(page);
await page.goto('/tools/pagination');
await dropFiles(page, pdfDropzone(page), [
{
name: 'gedreht.pdf',
mimeType: 'application/pdf',
buffer: await createPreRotatedPdf(2, 'Gedreht')
}
]);
const bytes = await downloadPaginated(page, /^gedreht_paginiert\.pdf$/);
const output = await PDFDocument.load(bytes);
expect(output.getPageCount()).toBe(2);
// Page 1 is stored with /Rotate 90: the text is counter-rotated so it reads
// horizontally on screen instead of sideways.
const rotatedContent = getPageContentText(output, 0);
expect(getPageText(output, 0)).toContain('Bl. 1');
const rotatedMatrix =
/([-\d.]+) ([-\d.]+) ([-\d.]+) ([-\d.]+) ([-\d.]+) ([-\d.]+) Tm\n<426C2E2031> Tj/.exec(
rotatedContent
);
expect(rotatedMatrix).not.toBeNull();
const [a, b, c, d, x, y] = rotatedMatrix!.slice(1).map(Number);
// 90° counter-clockwise in user space cancels the viewer's page rotation.
expect(Math.abs(a)).toBeLessThan(0.001);
expect(b).toBeCloseTo(1, 3);
expect(c).toBeCloseTo(-1, 3);
expect(Math.abs(d)).toBeLessThan(0.001);
// Visual bottom center of the 594×420 display: 28 pt margin, centered.
expect(x).toBeGreaterThan(384);
expect(x).toBeLessThan(396);
expect(y).toBeGreaterThan(280);
expect(y).toBeLessThan(292);
const plainContent = getPageContentText(output, 1);
expect(getPageText(output, 1)).toContain('Bl. 2');
expect(plainContent).toMatch(/1 0 0 1 [-\d.]+ [-\d.]+ Tm\n<426C2E2032> Tj/);
expect(pageErrors).toEqual([]);
});
test('pagination explains how to handle a password-protected PDF', async ({ page }) => {
const pageErrors = trackPageErrors(page);
await page.goto('/tools/pagination');
await dropFiles(page, pdfDropzone(page), [
{
name: 'geschuetzt.pdf',
mimeType: 'application/pdf',
buffer: await createEncryptedPdf(1, 'Geschützt', 'test-passwort')
}
]);
await expect(page.getByRole('alert')).toContainText('passwortgeschützt');
await expect(page.getByRole('alert')).toContainText('Passwort entfernen');
expect(pageErrors).toEqual([]);
});
test('merge hands the merged PDF over to pagination', async ({ page }) => {
const pageErrors = trackPageErrors(page);
await page.goto('/tools/merge');
await dropFiles(page, pdfDropzone(page), [
{ name: 'eins.pdf', mimeType: 'application/pdf', buffer: await createPdf(1, 'Eins') },
{ name: 'zwei.pdf', mimeType: 'application/pdf', buffer: await createPdf(2, 'Zwei') }
]);
await page.getByRole('button', { name: 'Zusammenfügen & paginieren' }).click();
await expect(page).toHaveURL(/\/tools\/pagination$/);
await expect(page.getByText('Seite 1 von 3')).toBeVisible({ timeout: 15_000 });
const bytes = await downloadPaginated(page, /^zusammengefuegt_paginiert\.pdf$/);
const output = await PDFDocument.load(bytes);
expect(output.getPageCount()).toBe(3);
expect(getPageText(output, 0)).toContain('Bl. 1');
expect(getPageText(output, 1)).toContain('Bl. 2');
expect(getPageText(output, 2)).toContain('Bl. 3');
expect(pageErrors).toEqual([]);
});