71 lines
4.6 KiB
JavaScript
71 lines
4.6 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { createServer } from 'node:http';
|
|
import { existsSync } from 'node:fs';
|
|
import { chromium } from 'playwright';
|
|
import { compile } from 'sass';
|
|
import { loadConfig } from '../server/config.mjs';
|
|
import { createBrowserChecks } from '../server/browser-checks.mjs';
|
|
|
|
const available = existsSync('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome') || existsSync(chromium.executablePath());
|
|
|
|
test('real browser journey blocks writes, applies reduced motion and handles night-time empty data', { skip: !available }, async () => {
|
|
let writes = 0, observedMotion = null;
|
|
const css = compile('src/styles.scss').css;
|
|
const fixture = createServer((req, res) => {
|
|
const url = new URL(req.url, 'http://localhost');
|
|
if (req.method !== 'GET') { writes++; res.end('write'); return; }
|
|
if (url.pathname === '/api/board') { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ departures: [] })); return; }
|
|
if (url.pathname === '/motion-report') { observedMotion = url.searchParams.get('motion'); res.end('ok'); return; }
|
|
res.setHeader('Content-Type', 'text/html');
|
|
res.end(`<!doctype html><html><head><style>${css}</style></head><body><div class="board"><header class="board-header"><h1>Test stop</h1></header></div><section class="servicesCard">Test</section><script>
|
|
(async()=>{ await fetch('/api/device/pair',{method:'POST'}).catch(()=>{}); await fetch('/api/board?stopId=1').then(r=>r.json()); await fetch('/motion-report?motion='+getComputedStyle(document.querySelector('.servicesCard')).animationName); const main=document.createElement('main');main.className='scene-container';main.textContent='No departures tonight';document.querySelector('.board').append(main); })();
|
|
</script></body></html>`);
|
|
});
|
|
await new Promise((resolve, reject) => { fixture.once('error', reject); fixture.listen(0, '127.0.0.1', resolve); });
|
|
const config = loadConfig({ STATUS_SCENARIO_TIMEOUT_MS: '2000' });
|
|
const browser = createBrowserChecks(config);
|
|
try {
|
|
const result = await browser.run({ type: 'board', baseUrl: `http://127.0.0.1:${fixture.address().port}`, cookie: null }, { cityId: 1, stopId: 1 });
|
|
assert.equal(result.ok, true); assert.equal(writes, 0); assert.equal(observedMotion, 'none');
|
|
} finally { await browser.close(); fixture.closeIdleConnections(); await new Promise(resolve => fixture.close(resolve)); }
|
|
});
|
|
|
|
test('paired board browser uses real cookies while writes and requests to another origin are blocked', { skip: !available }, async () => {
|
|
const cookie = 'ridebus_board_device=test-session-value';
|
|
let authenticatedReads = 0, unauthenticatedReads = 0, writes = 0, otherOriginRequests = 0;
|
|
const otherOrigin = createServer((_req, res) => { otherOriginRequests++; res.end('unexpected'); });
|
|
await new Promise(resolve => otherOrigin.listen(0, '127.0.0.1', resolve));
|
|
const fixture = createServer((req, res) => {
|
|
if (req.method !== 'GET') { writes++; res.end('write'); return; }
|
|
if (req.url.startsWith('/api/')) {
|
|
if (req.headers.cookie !== cookie) { unauthenticatedReads++; res.writeHead(401); res.end('{}'); return; }
|
|
authenticatedReads++; res.setHeader('Content-Type', 'application/json'); res.end('{}'); return;
|
|
}
|
|
res.setHeader('Content-Type', 'text/html');
|
|
res.end(`<!doctype html><html><body><div class="device-setup">Connecting</div><script>
|
|
(async()=>{
|
|
await fetch('/api/device/heartbeat',{method:'POST'}).catch(()=>{});
|
|
await fetch('http://127.0.0.1:${otherOrigin.address().port}/api/private').catch(()=>{});
|
|
if(!(await fetch('/api/device/status')).ok)return;
|
|
if(!(await fetch('/api/board?stopId=28')).ok)return;
|
|
document.body.innerHTML='<div class="board"><header class="board-header"><h1>Assigned stop</h1></header><main class="scene-container">Live departures</main></div>';
|
|
})();
|
|
</script></body></html>`);
|
|
});
|
|
await new Promise(resolve => fixture.listen(0, '127.0.0.1', resolve));
|
|
const browser = createBrowserChecks(loadConfig({ STATUS_SCENARIO_TIMEOUT_MS: '3000' }));
|
|
try {
|
|
const result = await browser.run({ type: 'board', baseUrl: `http://127.0.0.1:${fixture.address().port}`, cookie }, { cityId: 1, stopId: 28 });
|
|
assert.equal(result.ok, true);
|
|
assert.equal(authenticatedReads, 2);
|
|
assert.equal(unauthenticatedReads, 0);
|
|
assert.equal(writes, 0);
|
|
assert.equal(otherOriginRequests, 0);
|
|
} finally {
|
|
await browser.close();
|
|
fixture.closeAllConnections(); otherOrigin.closeAllConnections();
|
|
await Promise.all([new Promise(resolve => fixture.close(resolve)), new Promise(resolve => otherOrigin.close(resolve))]);
|
|
}
|
|
});
|