import { test, expect } from '@playwright/test'; import path from 'path'; import { fileURLToPath } from 'url'; import fs from 'fs'; import { login } from './helpers/auth'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const PDF_FIXTURE = path.resolve(__dirname, 'fixtures/minimal.pdf'); /** * E2E for issue #697 — read-only users can read an existing transcription. * * Setup runs as admin (default storage state): a PDF document with one * transcription block, so hasTranscription is true. The assertions run in a * fresh context logged in as the seeded READ_ALL-only "reader": they can open * the read view but see no edit tab and no per-block edit controls, and the * panel cannot be switched to edit. */ let docId: string; let docHref: string; test.describe.configure({ mode: 'serial' }); test.describe('Read-only user reads an existing transcription', () => { test.beforeAll(async ({ request }) => { const baseURL = process.env.E2E_BASE_URL ?? 'http://localhost:3000'; const uniqueSuffix = Date.now(); const docRes = await request.post('/api/documents', { multipart: { title: `E2E Read-only Transcription ${uniqueSuffix}`, documentDate: '1945-05-08' } }); if (!docRes.ok()) throw new Error(`Create document failed: ${docRes.status()}`); docId = (await docRes.json()).id; docHref = `${baseURL}/documents/${docId}`; await request.put(`/api/documents/${docId}`, { multipart: { title: `E2E Read-only Transcription ${uniqueSuffix}`, documentDate: '1945-05-08', file: { name: 'minimal.pdf', mimeType: 'application/pdf', buffer: fs.readFileSync(PDF_FIXTURE) } } }); const annRes = await request.post(`/api/documents/${docId}/annotations`, { data: { pageNumber: 1, x: 0.1, y: 0.1, width: 0.5, height: 0.1, color: '#00C7B1' } }); if (!annRes.ok()) throw new Error(`Create annotation failed: ${annRes.status()}`); const blockRes = await request.post(`/api/documents/${docId}/transcription-blocks`, { data: { pageNumber: 1, x: 0.1, y: 0.1, width: 0.5, height: 0.1, text: 'Liebe Mutter, viele Grüße vom Mai 1945', label: null } }); if (!blockRes.ok()) throw new Error(`Create block failed: ${blockRes.status()}`); }); test.afterAll(async ({ request }) => { if (docId) await request.delete(`/api/documents/${docId}`); }); test('reader opens the read view with no edit tab or edit controls', async ({ browser }) => { const context = await browser.newContext({ storageState: { cookies: [], origins: [] } }); const page = await context.newPage(); try { await login(page, 'reader', 'reader123'); await page.goto(docHref); // Reader entry control is the read label, not "Transkribieren". const readButton = page.getByRole('button', { name: /Transkription lesen/i }); await expect(readButton).toBeVisible({ timeout: 5000 }); await readButton.click(); // Read view shows the transcription text. await expect(page.getByText(/Mai 1945/)).toBeVisible({ timeout: 5000 }); // Header is a plain "Transkription" label, not a Lesen/Bearbeiten toggle. await expect(page.getByRole('heading', { name: /^Transkription$/i })).toBeVisible(); await expect(page.getByTestId('mode-edit')).toHaveCount(0); await expect(page.getByTestId('mode-read')).toHaveCount(0); } finally { await context.close(); } }); });