49 lines
3.1 KiB
JavaScript
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)); }
|