from __future__ import annotations import asyncio import threading import unittest from contextlib import contextmanager from playwright.async_api import async_playwright from werkzeug.serving import make_server from topology_api.app import create_app @contextmanager def topology_server(): app = create_app() app.config.update(TESTING=True) server = make_server("127.0.0.1", 0, app, threaded=True) thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start() try: port = server.socket.getsockname()[1] yield f"http://127.0.0.1:{port}" finally: server.shutdown() server.server_close() thread.join(timeout=2) class GuanghanTopologyE2ETests(unittest.TestCase): def test_historical_graph_and_ghc05_rendering(self) -> None: async def scenario(base_url: str) -> None: async with async_playwright() as pw: browser = await pw.chromium.launch(headless=True) page = await browser.new_page( viewport={"width": 1440, "height": 1000} ) console_errors: list[str] = [] page.on( "console", lambda msg: ( console_errors.append(msg.text) if msg.type == "error" else None ), ) async def open_mission(code: str) -> None: await page.goto( f"{base_url}/guanghan_topology_demo.html" f"?mission={code}", wait_until="networkidle", ) await page.wait_for_function( """code => ( document.querySelector('#page-subtitle') ?.textContent.startsWith(code + ' ยท') )""", arg=code, ) await open_mission("BL-01") self.assertEqual( await page.locator(".topology-node").count(), 1 ) self.assertEqual( await page.locator(".connection").count(), 0 ) self.assertEqual(await page.locator(".radiator").count(), 0) await open_mission("GHC-01") self.assertEqual( await page.locator(".topology-node").count(), 2 ) self.assertEqual( await page.locator(".connection").count(), 0 ) await open_mission("GHC-05") self.assertEqual( await page.locator(".topology-node").count(), 14 ) self.assertEqual( await page.locator(".connection.pressurized").count(), 2 ) self.assertEqual(await page.locator(".radiator").count(), 4) strokes = await page.locator( ".connection.pressurized" ).evaluate_all( "(els) => els.map((el) => getComputedStyle(el).stroke)" ) self.assertEqual( strokes, ["rgb(81, 232, 156)", "rgb(81, 232, 156)"], ) overflows = await page.locator( ".topology-node" ).evaluate_all( """nodes => nodes.flatMap(node => { const rect = node.querySelector('rect').getBBox(); return [...node.querySelectorAll('text')] .map(text => ({text: text.textContent, box: text.getBBox()})) .filter(item => item.box.x < rect.x || item.box.x + item.box.width > rect.x + rect.width ); })""" ) self.assertEqual(overflows, []) self.assertEqual(console_errors, []) await browser.close() with topology_server() as base_url: asyncio.run(scenario(base_url)) if __name__ == "__main__": unittest.main()