Files

49 lines
3.1 KiB
JavaScript

// Cheap layout red/green only: real packaged static files, synthetic HTTP data.
// Not media/auth/integration acceptance; run inside network-none browser container.
import { chromium, expect } from '@playwright/test';
import assert from 'node:assert/strict';
import { createServer } from 'node:http';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
const stream = { id: 'layoutfixture', title: 'Synthetic local test pattern', status: 'ended',
rtmpUrl: 'rtmp://127.0.0.1:1935/live', hlsUrl: 'http://127.0.0.1:8888/live/layoutfixture/index.m3u8' };
const data = {
'/api/auth/me': { pubkey: 'ab'.repeat(32), displayName: null, isAdmin: true },
'/api/streams/layoutfixture': stream,
'/api/streams/layoutfixture/recordings': [{ file: '2026-10-22_16-00-00-000000.mp4', path: 'live/layoutfixture/recording.mp4' }],
'/api/podcasts': [{ id: 'fixture-podcast', title: 'Synthetic recording qualification', feed_url: 'http://127.0.0.1:8095/feeds/fixture-podcast/feed.xml' }],
};
const server = createServer(async (req, res) => {
const path = new URL(req.url, 'http://127.0.0.1:8095').pathname;
if (path.startsWith('/api/')) {
res.writeHead(path in data ? 200 : 404, { 'content-type': 'application/json' });
res.end(JSON.stringify(data[path] ?? { error: 'No synthetic route' })); return;
}
if (path.includes('..')) { res.writeHead(400).end(); return; }
const file = path.startsWith('/assets/') ? path : '/index.html';
try {
const bytes = await readFile(join('/fixture-public', file));
res.writeHead(200, { 'content-type': file.endsWith('.js') ? 'application/javascript' : file.endsWith('.css') ? 'text/css' : 'text/html' });
res.end(bytes);
} catch { res.writeHead(404).end(); }
});
await new Promise(resolve => server.listen(8095, '127.0.0.1', resolve));
const browser = await chromium.launch({ executablePath: process.env.CHROMIUM_EXECUTABLE,
headless: true, args: ['--no-sandbox', '--disable-dev-shm-usage'] });
try {
const page = await browser.newPage();
await page.route('**/*', route => new URL(route.request().url()).origin === 'http://127.0.0.1:8095' ? route.continue() : route.abort());
for (const width of [390, 1440]) {
await page.setViewportSize({ width, height: 900 });
await page.goto('http://127.0.0.1:8095/streams/layoutfixture');
await expect(page.getByRole('heading', { name: 'Recordings', exact: true })).toBeVisible();
const geometry = await page.evaluate(() => ({ width: innerWidth, scrollWidth: document.documentElement.scrollWidth,
offenders: [...document.querySelectorAll('body *')].map(e => ({ tag: e.tagName, class: e.className,
text: (e.textContent ?? '').trim().slice(0, 80), right: e.getBoundingClientRect().right }))
.filter(e => e.right > innerWidth+1) }));
console.log(JSON.stringify(geometry));
assert.equal(geometry.scrollWidth <= width+1, true, `horizontal overflow at ${width}px`);
}
console.log('PASS packaged static dashboard layout at390/1440 with synthetic API; not media/auth acceptance');
} finally { await browser.close(); await new Promise(resolve => server.close(resolve)); }