Files
ObsiGate/tests/e2e/pdf-viewer.spec.js
T
bruno 2c460022f8
CI / lint (push) Successful in 1m37s
CI / security (push) Successful in 1m3s
CI / test (push) Successful in 3m33s
CI / build (push) Successful in 59s
CI / e2e (push) Successful in 10m57s
test(pdf): helper d'ouverture robuste au vault replie (BUG-060)
2026-09-17 19:42:43 -04:00

92 lines
3.6 KiB
JavaScript

/**
* E2E tests for ObsiGate PDF inline viewer (BUG-060).
*
* Regression : la CSP posée par BUG-034 (`object-src 'none'`) bloque l'élément
* `<embed>` qui servait le PDF. Le viewer doit rendre le stream dans une
* `<iframe>` (autorisée par `frame-src 'self'`).
*
* Fixtures : `test_vault/sample-pdf.pdf` (3 pages, texte simple).
*
* Run (local):
* BASE_URL=http://localhost:2029 npx playwright test tests/e2e/pdf-viewer.spec.js
* BASE_URL=http://localhost:2029 npx playwright test tests/e2e/pdf-viewer.spec.js --headed
*/
import { test, expect } from '@playwright/test';
const BASE = process.env.BASE_URL || 'http://localhost:2029';
const CREDS = {
username: process.env.OBSIGATE_USER || 'admin',
password: process.env.OBSIGATE_PASS || 'test123',
};
async function login(page) {
await page.goto(BASE);
const loginForm = page.locator('#login-screen');
await expect(loginForm).toBeVisible({ timeout: 5000 }).catch(() => {});
if (await loginForm.isVisible()) {
await page.fill('#login-username', CREDS.username);
await page.fill('#login-password', CREDS.password);
await page.click('#login-btn');
}
await page.waitForFunction(() => window.__OBSIGATE_BOOTED === true, { timeout: 20000 });
}
async function openFile(page, vault, filePath) {
const treeItem = page.locator(`.tree-item[data-vault="${vault}"][data-path="${filePath}"]`);
// Le vault peut être replié (auth activée) : l'étendre avant de chercher le fichier.
if (!(await treeItem.count())) {
await page.locator(`.tree-item.vault-item[data-vault="${vault}"]`).first().click();
await treeItem.waitFor({ state: 'attached', timeout: 8000 });
}
await treeItem.dblclick({ timeout: 5000 });
await page.waitForTimeout(500);
}
test.describe('PDF viewer — affichage inline (BUG-060)', () => {
test('ouvre un PDF dans une iframe (pas d\'<embed>) et le stream charge sans violation CSP', async ({ page }) => {
const cspViolations = [];
page.on('console', (msg) => {
const text = msg.text();
if (text.includes('Content Security Policy') && (text.includes('object-src') || text.includes('Refused'))) {
cspViolations.push(text);
}
});
await login(page);
// Le stream est demandé au moment où le viewer monte l'iframe : enregistrer
// l'écoute AVANT d'ouvrir le fichier (sinon la réponse est déjà passée).
const streamResponsePromise = page.waitForResponse(
(r) => r.url().includes('/pdf/stream') && (r.status() === 200 || r.status() === 206),
{ timeout: 15000 },
);
await openFile(page, 'TestVault', 'sample-pdf.pdf');
const iframe = page.locator('#content-area .pdf-iframe');
await expect(iframe).toBeVisible({ timeout: 15000 });
await expect(iframe).toHaveAttribute('src', /\/api\/file\/TestVault\/pdf\/stream\?path=/);
// La balise doit être une iframe (le <embed>/<object> serait bloqué par CSP)
const tagName = await iframe.evaluate((el) => el.tagName);
expect(tagName).toBe('IFRAME');
expect(await page.locator('#content-area embed, #content-area object').count()).toBe(0);
// La barre d'outils indique le nombre de pages du PDF
await expect(page.locator('.pdf-info')).toContainText('3 pages');
// Le stream est bien servi en application/pdf (200 ou 206 Range)
const streamResp = await streamResponsePromise;
expect(streamResp.headers()['content-type'] || '').toContain('application/pdf');
// Le cadre embarque réellement le document PDF (navigateur natif)
const pdfFrame = await iframe.contentFrame();
expect(pdfFrame).not.toBeNull();
// Aucune violation CSP liée à object-src pendant l'ouverture
expect(cspViolations).toEqual([]);
});
});