forked from genewildish/Mainline
134 lines
4.0 KiB
Python
134 lines
4.0 KiB
Python
"""
|
|
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"
|