exec(open('ui_test.py').read().split("with sync_playwright() as pw:")[0])
import subprocess
with sync_playwright() as pw:
    b = pw.chromium.launch(); ctx = b.new_context(viewport={'width': 1440, 'height': 900}); p, errs = login(ctx)
    p.evaluate("PH.put('/settings/links', {links:[{label:'Carrier portal', url:'https://example.com/portal', tip:'Open the carrier portal', scope:'all'},{label:'Training library', url:'https://example.com/train', tip:'Recorded trainings', scope:'all'}]})")
    p.goto(BASE + '#/team'); p.wait_for_timeout(1500); p.evaluate("PH.reloadWorkspace && PH.reloadWorkspace()"); p.wait_for_timeout(1200)
    a = p.query_selector('.sb-links a')
    if a:
        a.scroll_into_view_if_needed(); p.wait_for_timeout(300); a.hover(); p.wait_for_timeout(400)
        t = p.eval_on_selector('#tip', "e => { const r = e.getBoundingClientRect(), s = document.getElementById('sidebar').getBoundingClientRect(); return {cls: e.className, left: r.left, sideRight: s.right, bg: getComputedStyle(e).backgroundColor} }")
        ok('side' in t['cls'] and t['left'] >= t['sideRight'], 'quick-link tooltip sits beside the menu, not over it', t)
        ok(t['bg'] == 'rgb(255, 255, 255)', 'tooltip is light against the dark menu', t['bg'])
        bg = a.evaluate("e => getComputedStyle(e).backgroundColor"); ok(bg != 'rgba(0, 0, 0, 0)', 'hover highlight shows', bg)
        p.screenshot(path='/tmp/tip.png', clip={'x': 0, 'y': 500, 'width': 520, 'height': 300})
    else: ok(False, 'quick links rendered')
    p.hover('.ah, .hero, h1'); p.wait_for_timeout(200)
    print(errs[:3])
print(f"{sum(R)} passed, {len(R)-sum(R)} failed")
