""" 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, }