This repository has been archived on 2026-04-03. You can view files and clone it, but cannot push or open issues or pull requests.
agent-runtime/tests/test_ui.py
Nico 44f6116855 feat(tests): ui suite — toolbar groups, scroll preservation, DOM keep-alive
6 Playwright tests against nyx-test (localhost:30802, auth disabled):
- toolbar group counts per route (nyx=4, tests=2, home=1)
- toolbar survives full nav roundtrip without losing groups
- scroll position preserved across navigation (keep-alive working)
- all visited views stay in DOM (not removed on nav)

Run: NYX_URL=http://localhost:30802 python tests/run_tests.py ui

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 23:24:51 +02:00

141 lines
4.7 KiB
Python

"""
UI tests — toolbar, navigation, scroll preservation.
Runs against a nyx instance with VITE_AUTH_DISABLED=true (auth skipped).
Local dev (after restarting Vite with .env.local VITE_AUTH_DISABLED=true):
NYX_URL=http://localhost:5173 python tests/run_tests.py ui
K3s test build (no restart needed — already built with auth disabled):
NYX_URL=http://localhost:30802 python tests/run_tests.py ui
"""
import os
from playwright.sync_api import sync_playwright, Page, expect
NYX_URL = os.environ.get('NYX_URL', 'http://localhost:30802')
_pw = None
_browser = None
def _ensure_browser():
global _pw, _browser
if _browser is None:
_pw = sync_playwright().start()
_browser = _pw.chromium.launch(headless=True)
return _browser
def _page(path: str = '/nyx') -> tuple:
browser = _ensure_browser()
ctx = browser.new_context(viewport={'width': 1280, 'height': 800})
page = ctx.new_page()
page.goto(f'{NYX_URL}{path}')
page.wait_for_selector('.app-toolbar', timeout=15000)
page.wait_for_timeout(500) # let Vue commit reactive toolbar updates
return page, ctx
def _click_nav(page: Page, text: str):
page.locator('.sidebar-link', has_text=text).click()
page.wait_for_timeout(800)
# ── Tests ────────────────────────────────────────────────────────────────────
def test_toolbar_nyx_has_all_groups():
"""nyx shows 4 toolbar groups: connection, quad-view, themes, panels."""
page, ctx = _page('/nyx')
try:
expect(page.locator('.toolbar-group')).to_have_count(4, timeout=5000)
finally:
ctx.close()
def test_toolbar_tests_has_two_groups():
"""tests view shows 2 toolbar groups: connection + themes."""
page, ctx = _page('/tests')
try:
expect(page.locator('.toolbar-group')).to_have_count(2, timeout=5000)
finally:
ctx.close()
def test_toolbar_home_has_one_group():
"""home page shows 1 toolbar group: themes only."""
page, ctx = _page('/')
try:
expect(page.locator('.toolbar-group')).to_have_count(1, timeout=5000)
finally:
ctx.close()
def test_toolbar_survives_roundtrip():
"""Navigate nyx→tests→home→nyx — toolbar groups correct at each stop."""
page, ctx = _page('/nyx')
try:
expect(page.locator('.toolbar-group')).to_have_count(4, timeout=5000)
_click_nav(page, 'Tests')
expect(page.locator('.toolbar-group')).to_have_count(2, timeout=3000)
_click_nav(page, 'Home')
expect(page.locator('.toolbar-group')).to_have_count(1, timeout=3000)
_click_nav(page, 'nyx')
expect(page.locator('.toolbar-group')).to_have_count(4, timeout=3000)
_click_nav(page, 'Tests')
expect(page.locator('.toolbar-group')).to_have_count(2, timeout=3000)
finally:
ctx.close()
def test_scroll_preserved_across_navigation():
"""Scroll down in tests view, navigate away and back — position preserved."""
page, ctx = _page('/tests')
try:
page.wait_for_selector('.tests-view', timeout=5000)
# Scroll the tests container
page.evaluate('() => { const el = document.querySelector(".tests-view"); if (el) el.scrollTop = 200; }')
page.wait_for_timeout(200)
before = page.evaluate('() => document.querySelector(".tests-view")?.scrollTop ?? 0')
_click_nav(page, 'Home')
_click_nav(page, 'Tests')
after = page.evaluate('() => document.querySelector(".tests-view")?.scrollTop ?? 0')
assert after == before, f'scroll not preserved: was {before}, now {after}'
finally:
ctx.close()
def test_all_views_stay_in_dom():
"""After visiting nyx and tests, both stay in DOM (hidden not removed)."""
page, ctx = _page('/nyx')
try:
expect(page.locator('.toolbar-group')).to_have_count(4, timeout=5000)
_click_nav(page, 'Tests')
# AgentsView should still be in DOM (just hidden)
assert page.locator('.agents-view').count() > 0, 'AgentsView removed from DOM'
_click_nav(page, 'nyx')
# TestsView should still be in DOM
assert page.locator('.tests-view').count() > 0, 'TestsView removed from DOM'
finally:
ctx.close()
# Test registry
TESTS = {
'ui_toolbar_nyx_all_groups': test_toolbar_nyx_has_all_groups,
'ui_toolbar_tests_two_groups': test_toolbar_tests_has_two_groups,
'ui_toolbar_home_one_group': test_toolbar_home_has_one_group,
'ui_toolbar_roundtrip': test_toolbar_survives_roundtrip,
'ui_scroll_preserved': test_scroll_preserved_across_navigation,
'ui_views_stay_in_dom': test_all_views_stay_in_dom,
}