import { test, expect } from '@playwright/test'; import path from 'path'; import { fileURLToPath } from 'url'; import fs from 'fs'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const PDF_FIXTURE = path.resolve(__dirname, 'fixtures/minimal.pdf'); /** * Bottom panel E2E tests — issue #62. * Verifies the new document detail layout: full-viewport viewer + floating bottom panel. */ let pdfDocHref: string; let noFileDocHref: string; test.describe('Document bottom panel', () => { test.beforeAll(async ({ request }) => { const baseURL = process.env.E2E_BASE_URL ?? 'http://localhost:3000'; // Create a document with a PDF and a date for metadata tests. const createRes = await request.post('/api/documents', { multipart: { title: 'E2E Bottom Panel Test', documentDate: '1945-05-08' } }); if (!createRes.ok()) throw new Error(`Create document failed: ${createRes.status()}`); const doc = await createRes.json(); const uploadRes = await request.put(`/api/documents/${doc.id}`, { multipart: { title: doc.title, documentDate: '1945-05-08', transcription: 'Dies ist eine vollständige Transkription des Dokuments für den E2E-Test.', file: { name: 'minimal.pdf', mimeType: 'application/pdf', buffer: fs.readFileSync(PDF_FIXTURE) } } }); if (!uploadRes.ok()) throw new Error(`Upload PDF failed: ${uploadRes.status()}`); pdfDocHref = `${baseURL}/documents/${doc.id}`; // Create a document WITHOUT a file — panel should open to Metadaten by default. const noFileRes = await request.post('/api/documents', { multipart: { title: 'E2E Bottom Panel No-File Test' } }); if (!noFileRes.ok()) throw new Error(`Create no-file document failed: ${noFileRes.status()}`); noFileDocHref = `${baseURL}/documents/${noFileRes.json().then ? (await noFileRes.json()).id : ''}`; const noFileDoc = await noFileRes.json(); noFileDocHref = `${baseURL}/documents/${noFileDoc.id}`; }); test('bottom panel tab bar is visible and panel content is closed by default on a PDF document', async ({ page }) => { test.setTimeout(30_000); // Clear localStorage to ensure no previous panel state. await page.goto(pdfDocHref); await page.evaluate(() => localStorage.clear()); await page.reload(); await page.waitForSelector('[data-hydrated]'); // Tab bar must always be visible. await expect(page.getByRole('button', { name: 'Metadaten' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Transkription' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Diskussion' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Verlauf' })).toBeVisible(); // Panel content must NOT be visible when closed. await expect(page.locator('[data-testid="bottom-panel-content"]')).not.toBeVisible(); await page.screenshot({ path: 'test-results/e2e/bottom-panel-closed-default.png' }); }); test('clicking Metadaten tab opens the panel and shows metadata content', async ({ page }) => { test.setTimeout(30_000); await page.goto(pdfDocHref); await page.evaluate(() => localStorage.clear()); await page.reload(); await page.waitForSelector('[data-hydrated]'); await page.getByRole('button', { name: 'Metadaten' }).click(); // Panel content becomes visible. await expect(page.locator('[data-testid="bottom-panel-content"]')).toBeVisible(); // Metadata section heading should be present. await expect(page.getByText('Details', { exact: false })).toBeVisible(); await page.screenshot({ path: 'test-results/e2e/bottom-panel-metadata.png' }); }); test('clicking Transkription tab shows transcription text', async ({ page }) => { test.setTimeout(30_000); await page.goto(pdfDocHref); await page.evaluate(() => localStorage.clear()); await page.reload(); await page.waitForSelector('[data-hydrated]'); await page.getByRole('button', { name: 'Transkription' }).click(); await expect(page.locator('[data-testid="bottom-panel-content"]')).toBeVisible(); await expect( page.getByText('Dies ist eine vollständige Transkription', { exact: false }) ).toBeVisible(); await page.screenshot({ path: 'test-results/e2e/bottom-panel-transcription.png' }); }); test('clicking Diskussion tab shows the comment input', async ({ page }) => { test.setTimeout(30_000); await page.goto(pdfDocHref); await page.evaluate(() => localStorage.clear()); await page.reload(); await page.waitForSelector('[data-hydrated]'); await page.getByRole('button', { name: 'Diskussion' }).click(); await expect(page.locator('[data-testid="bottom-panel-content"]')).toBeVisible(); await expect(page.getByPlaceholder('Kommentar schreiben…')).toBeVisible(); await page.screenshot({ path: 'test-results/e2e/bottom-panel-discussion.png' }); }); test('clicking × close button collapses the panel content', async ({ page }) => { test.setTimeout(30_000); await page.goto(pdfDocHref); await page.evaluate(() => localStorage.clear()); await page.reload(); await page.waitForSelector('[data-hydrated]'); // Open the panel first. await page.getByRole('button', { name: 'Metadaten' }).click(); await expect(page.locator('[data-testid="bottom-panel-content"]')).toBeVisible(); // Close it. await page.locator('[data-testid="panel-close-btn"]').click(); await expect(page.locator('[data-testid="bottom-panel-content"]')).not.toBeVisible(); // Tab bar still visible after closing. await expect(page.getByRole('button', { name: 'Metadaten' })).toBeVisible(); await page.screenshot({ path: 'test-results/e2e/bottom-panel-closed-after-x.png' }); }); test('panel open state persists after page reload', async ({ page }) => { test.setTimeout(30_000); await page.goto(pdfDocHref); await page.evaluate(() => localStorage.clear()); await page.reload(); await page.waitForSelector('[data-hydrated]'); // Open the panel to Diskussion. await page.getByRole('button', { name: 'Diskussion' }).click(); await expect(page.locator('[data-testid="bottom-panel-content"]')).toBeVisible(); // Reload — panel should re-open on the same tab. await page.reload(); await page.waitForSelector('[data-hydrated]'); await expect(page.locator('[data-testid="bottom-panel-content"]')).toBeVisible(); await expect(page.getByPlaceholder('Kommentar schreiben…')).toBeVisible(); await page.screenshot({ path: 'test-results/e2e/bottom-panel-persisted.png' }); }); test('document without a file opens panel to Metadaten by default', async ({ page }) => { test.setTimeout(30_000); await page.goto(noFileDocHref); await page.evaluate(() => localStorage.clear()); await page.reload(); await page.waitForSelector('[data-hydrated]'); // Panel should be open to Metadaten by default when there is no file. await expect(page.locator('[data-testid="bottom-panel-content"]')).toBeVisible(); await expect(page.getByText('Details', { exact: false })).toBeVisible(); await page.screenshot({ path: 'test-results/e2e/bottom-panel-no-file-default.png' }); }); });