#!/usr/bin/env python3 """Execute the actual offline room page in Chromium with bounded HTTP mocks.""" import html import json import os from pathlib import Path import re import subprocess import tempfile import unittest from test_webconfig_ui_runtime import BROWSER, run_browser ROOT = Path(__file__).resolve().parents[1] HEADER = ROOT / "src/helpers/esp32/RoomWebHtml.h" BOUNDARY = ROOT / "test/fixtures/room_web_service/boundary.json" COMMON = r''' const boundary=__BOUNDARY__,calls=[]; localStorage.setItem('mc-room-token','01'.repeat(32));localStorage.setItem('mc-room-sequence','0'); window.attack=()=>document.body.dataset.testAttacked='yes'; window.addEventListener('error',e=>document.body.dataset.testJsError=e.message); window.addEventListener('unhandledrejection',e=>document.body.dataset.testJsError=String(e.reason)); const response=(value,status=200)=>Promise.resolve({ok:status>=200&&status<300,status:status,json:()=>Promise.resolve(value)}); const until=async predicate=>{for(let i=0;i<500;i++){if(predicate())return;await new Promise(r=>setTimeout(r,10))}throw Error('test condition timed out')}; const submit=id=>document.getElementById(id).dispatchEvent(new Event('submit',{bubbles:true,cancelable:true})); const record=(url,options)=>{let p=options.body?JSON.parse(options.body):null;calls.push({url:url,body:options.body||'',seq:options.headers['X-Room-Seq'],token:options.headers['X-Room-Token'],op:p?p.op:'result'});return p}; const mark=(key,value)=>document.body.setAttribute('data-test-'+key,String(value)); ''' ADMIN_COMMON = r''' const radioKey='ab'.repeat(32),otherKey='ab'.repeat(6)+'cd'.repeat(26); const radioUser=(key=radioKey,extra={})=>Object.assign({key:key,role:1,retained:true,active:true,heard_ago:12,pending_count:4, delivery:'post',failures:0,outpath:'A1',altpath:'B1',observed:'A2',observed_pending:false,sync_since:100, pending_post:101,topic_pending:0,ack_wait_ms:700,banned:false},extra); const adminPage=(users,next=null)=>({users:users,next:next,total:3,active:2,backlog:6,pending:1,failed:1,boot:71}); const baseRoom=p=>p.op==='status'?response(Object.assign({},boundary.status,{role:3})):p.op==='posts'?response({post:null}):p.op==='board.index'?response({revision:0,articles:[],next:255}):null; const joinedRoom=async()=>{submit('join');await until(()=>!$('room').hidden&&$('notices').textContent==='No notices published.')}; ''' class RoomWebUiTest(unittest.TestCase): def run_page(self, scenario, virtual_time=7000): if not BROWSER: if os.environ.get("MESHCORE_REQUIRE_ROOM_BROWSER") == "1": self.fail("Chromium is required for room browser validation") self.skipTest("Chromium-family browser is unavailable") source = HEADER.read_text().split('R"roomhtml(', 1)[1].split(')roomhtml";', 1)[0] transform = getattr(self, "page_transform", None) if transform: source = transform(source) fixture = json.loads(BOUNDARY.read_text()) prelude = "" self.assertIn("