import { expect, test, type Page } from '@playwright/test'; import { PDFDocument, StandardFonts, rgb } from 'pdf-lib'; import { createEncryptedPdf, createPdf, dropFiles, expectPdfDownload, pdfDropzone, trackPageErrors } from './helpers'; const createPdfWithMetadata = async () => { const document = await PDFDocument.create(); const font = await document.embedFont(StandardFonts.Helvetica); for (let pageNumber = 1; pageNumber <= 2; pageNumber += 1) { const page = document.addPage([420, 594]); page.drawText(`Vertraulich – Seite ${pageNumber}`, { x: 40, y: 520, size: 20, font, color: rgb(0.1, 0.2, 0.4) }); } document.setTitle('Schriftsatz 12. Kammer'); document.setAuthor('Dr. Echte Anwältin'); document.setSubject('Klageerwiderung'); document.setKeywords(['vertraulich, Mandat 4711']); document.setCreator('Anwaltssoftware Pro'); document.setProducer('PDF-Fabrik 3.2'); document.setCreationDate(new Date('2024-05-06T09:12:00Z')); document.setModificationDate(new Date('2024-05-07T16:40:00Z')); return Buffer.from(await document.save()); }; const metadataValue = (page: Page, label: string) => page.locator(`[data-metadata-value="${label}"]`); test('metadata tool shows document properties of a dropped PDF', async ({ page }) => { const pageErrors = trackPageErrors(page); await page.goto('/tools/metadata'); await dropFiles(page, pdfDropzone(page), [ { name: 'schriftsatz.pdf', mimeType: 'application/pdf', buffer: await createPdfWithMetadata() } ]); await expect(metadataValue(page, 'Titel')).toHaveText('Schriftsatz 12. Kammer'); await expect(metadataValue(page, 'Autor')).toHaveText('Dr. Echte Anwältin'); await expect(metadataValue(page, 'Betreff')).toHaveText('Klageerwiderung'); await expect(metadataValue(page, 'Schlüsselwörter')).toHaveText('vertraulich, Mandat 4711'); await expect(metadataValue(page, 'Erstellt mit')).toHaveText('Anwaltssoftware Pro'); await expect(metadataValue(page, 'PDF-Erzeuger')).toHaveText('PDF-Fabrik 3.2'); await expect(metadataValue(page, 'Erstellt am')).toContainText('2024'); await expect(metadataValue(page, 'Geändert am')).toContainText('2024'); await expect(metadataValue(page, 'Seiten')).toHaveText('2'); await expect(metadataValue(page, 'Verschlüsselung')).toHaveText('Nein'); expect(pageErrors).toEqual([]); }); test('metadata tool shows a dash for empty fields', async ({ page }) => { const pageErrors = trackPageErrors(page); await page.goto('/tools/metadata'); await dropFiles(page, pdfDropzone(page), [ { name: 'nackt.pdf', mimeType: 'application/pdf', buffer: await createPdf(1, 'Nackt') } ]); await expect(metadataValue(page, 'Titel')).toHaveText('–'); await expect(metadataValue(page, 'Autor')).toHaveText('–'); await expect(metadataValue(page, 'Schlüsselwörter')).toHaveText('–'); expect(pageErrors).toEqual([]); }); test('metadata tool downloads a cleaned PDF with empty document properties', async ({ page }) => { const pageErrors = trackPageErrors(page); await page.goto('/tools/metadata'); await dropFiles(page, pdfDropzone(page), [ { name: 'schriftsatz.pdf', mimeType: 'application/pdf', buffer: await createPdfWithMetadata() } ]); const downloadPromise = page.waitForEvent('download'); await page.getByRole('button', { name: 'Bereinigt herunterladen' }).click(); const bytes = await expectPdfDownload(await downloadPromise, /^schriftsatz_bereinigt\.pdf$/); const document = await PDFDocument.load(bytes, { updateMetadata: false }); expect((document.getTitle() ?? '').trim()).toBe(''); expect((document.getAuthor() ?? '').trim()).toBe(''); expect((document.getSubject() ?? '').trim()).toBe(''); expect((document.getKeywords() ?? '').trim()).toBe(''); expect(document.getProducer()).toBe('beA-Edit'); expect(document.getPageCount()).toBe(2); expect(pageErrors).toEqual([]); }); test('metadata tool rejects encrypted PDFs with a decrypt hint', async ({ page }) => { const pageErrors = trackPageErrors(page); await page.goto('/tools/metadata'); await dropFiles(page, pdfDropzone(page), [ { name: 'geheim.pdf', mimeType: 'application/pdf', buffer: await createEncryptedPdf(1, 'Geheim', 'test-passwort') } ]); await expect(page.getByText('Dieses PDF ist verschlüsselt')).toBeVisible(); await expect(page.getByRole('link', { name: 'Passwort entfernen' })).toHaveAttribute( 'href', '/tools/decrypt' ); await expect(page.getByRole('button', { name: 'Bereinigt herunterladen' })).toBeDisabled(); expect(pageErrors).toEqual([]); }); test('metadata tool warns when embedded files are suspected', async ({ page }) => { const pageErrors = trackPageErrors(page); const pdf = await createPdf(1, 'Anlage'); const withMarker = Buffer.concat([pdf, Buffer.from('\n% /EmbeddedFile\n')]); await page.goto('/tools/metadata'); await dropFiles(page, pdfDropzone(page), [ { name: 'mit-anlage.pdf', mimeType: 'application/pdf', buffer: withMarker } ]); await expect( page.getByText( 'Die Datei enthält möglicherweise eingebettete Anlagen. Diese werden beim Bereinigen nicht entfernt.' ) ).toBeVisible(); await expect(page.getByRole('button', { name: 'Bereinigt herunterladen' })).toBeEnabled(); expect(pageErrors).toEqual([]); });