forked from genewildish/Mainline
feat(websocket): add WebSocket display backend for browser client
This commit is contained in:
133
tests/e2e/test_web_client.py
Normal file
133
tests/e2e/test_web_client.py
Normal file
@@ -0,0 +1,133 @@
|
||||
"""
|
||||
End-to-end tests for web client with headless browser.
|
||||
"""
|
||||
|
||||
import os
|
||||
import socketserver
|
||||
import threading
|
||||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
CLIENT_DIR = Path(__file__).parent.parent.parent / "client"
|
||||
|
||||
|
||||
class ThreadedHTTPServer(socketserver.ThreadingMixIn, HTTPServer):
|
||||
"""Threaded HTTP server for handling concurrent requests."""
|
||||
|
||||
daemon_threads = True
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def http_server():
|
||||
"""Start a local HTTP server for the client."""
|
||||
os.chdir(CLIENT_DIR)
|
||||
|
||||
handler = SimpleHTTPRequestHandler
|
||||
server = ThreadedHTTPServer(("127.0.0.1", 0), handler)
|
||||
port = server.server_address[1]
|
||||
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
|
||||
yield f"http://127.0.0.1:{port}"
|
||||
|
||||
server.shutdown()
|
||||
|
||||
|
||||
class TestWebClient:
|
||||
"""Tests for the web client using Playwright."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_browser(self):
|
||||
"""Set up browser for tests."""
|
||||
pytest.importorskip("playwright")
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
self.playwright = sync_playwright().start()
|
||||
self.browser = self.playwright.chromium.launch(headless=True)
|
||||
self.context = self.browser.new_context()
|
||||
self.page = self.context.new_page()
|
||||
|
||||
yield
|
||||
|
||||
self.page.close()
|
||||
self.context.close()
|
||||
self.browser.close()
|
||||
self.playwright.stop()
|
||||
|
||||
def test_client_loads(self, http_server):
|
||||
"""Web client loads without errors."""
|
||||
response = self.page.goto(http_server)
|
||||
assert response.status == 200, f"Page load failed with status {response.status}"
|
||||
|
||||
self.page.wait_for_load_state("domcontentloaded")
|
||||
|
||||
content = self.page.content()
|
||||
assert "<canvas" in content, "Canvas element not found in page"
|
||||
|
||||
canvas = self.page.locator("#terminal")
|
||||
assert canvas.count() > 0, "Canvas not found"
|
||||
|
||||
def test_status_shows_connecting(self, http_server):
|
||||
"""Status shows connecting initially."""
|
||||
self.page.goto(http_server)
|
||||
self.page.wait_for_load_state("domcontentloaded")
|
||||
|
||||
status = self.page.locator("#status")
|
||||
assert status.count() > 0, "Status element not found"
|
||||
|
||||
def test_canvas_has_dimensions(self, http_server):
|
||||
"""Canvas has correct dimensions after load."""
|
||||
self.page.goto(http_server)
|
||||
self.page.wait_for_load_state("domcontentloaded")
|
||||
|
||||
canvas = self.page.locator("#terminal")
|
||||
assert canvas.count() > 0, "Canvas not found"
|
||||
|
||||
def test_no_console_errors_on_load(self, http_server):
|
||||
"""No JavaScript errors on page load (websocket errors are expected without server)."""
|
||||
js_errors = []
|
||||
|
||||
def handle_console(msg):
|
||||
if msg.type == "error":
|
||||
text = msg.text
|
||||
if "WebSocket" not in text:
|
||||
js_errors.append(text)
|
||||
|
||||
self.page.on("console", handle_console)
|
||||
self.page.goto(http_server)
|
||||
self.page.wait_for_load_state("domcontentloaded")
|
||||
|
||||
assert len(js_errors) == 0, f"JavaScript errors: {js_errors}"
|
||||
|
||||
|
||||
class TestWebClientProtocol:
|
||||
"""Tests for WebSocket protocol handling in client."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_browser(self):
|
||||
"""Set up browser for tests."""
|
||||
pytest.importorskip("playwright")
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
self.playwright = sync_playwright().start()
|
||||
self.browser = self.playwright.chromium.launch(headless=True)
|
||||
self.context = self.browser.new_context()
|
||||
self.page = self.context.new_page()
|
||||
|
||||
yield
|
||||
|
||||
self.page.close()
|
||||
self.context.close()
|
||||
self.browser.close()
|
||||
self.playwright.stop()
|
||||
|
||||
def test_websocket_reconnection(self, http_server):
|
||||
"""Client attempts reconnection on disconnect."""
|
||||
self.page.goto(http_server)
|
||||
self.page.wait_for_load_state("domcontentloaded")
|
||||
|
||||
status = self.page.locator("#status")
|
||||
assert status.count() > 0, "Status element not found"
|
||||
Reference in New Issue
Block a user