Files
ridebus-status/test/browser-checks.test.mjs
2026-10-13 02:24:39 +03:00

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))]);
}
});