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>
This commit is contained in:
parent
5964ca55e1
commit
44f6116855
@ -134,6 +134,13 @@ def get_node_tests() -> dict:
|
|||||||
return TESTS
|
return TESTS
|
||||||
|
|
||||||
|
|
||||||
|
def get_ui_tests() -> dict:
|
||||||
|
"""Load UI tests — toolbar, navigation, scroll (Playwright, no backend needed)."""
|
||||||
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
|
from test_ui import TESTS
|
||||||
|
return TESTS
|
||||||
|
|
||||||
|
|
||||||
SUITES = {
|
SUITES = {
|
||||||
'engine': get_engine_tests,
|
'engine': get_engine_tests,
|
||||||
'api': get_api_tests,
|
'api': get_api_tests,
|
||||||
@ -141,6 +148,7 @@ SUITES = {
|
|||||||
'matrix': get_matrix_tests,
|
'matrix': get_matrix_tests,
|
||||||
'testcases': get_testcase_tests,
|
'testcases': get_testcase_tests,
|
||||||
'roundtrip': get_roundtrip_tests,
|
'roundtrip': get_roundtrip_tests,
|
||||||
|
'ui': get_ui_tests,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
140
tests/test_ui.py
Normal file
140
tests/test_ui.py
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
"""
|
||||||
|
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,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user