import re """Functional smoke checks and screenshots against a running Sidereal server.""" import json import os from pathlib import Path from playwright.sync_api import sync_playwright, expect base = os.environ.get('SIDEREAL_TEST_URL', 'http://127.0.0.1:5173') out = Path('test-results') out.mkdir(exist_ok=True) errors = [] with sync_playwright() as p: installs = list(Path(os.environ.get('PLAYWRIGHT_BROWSERS_PATH', '/opt/playwright')).glob('chromium-*/chrome-linux*/chrome')) browser = p.chromium.launch(headless=True, executable_path=str(installs[0]) if installs else None, args=['--no-sandbox']) context = browser.new_context(viewport={'width': 1600, 'height': 1000}, device_scale_factor=1, geolocation={'latitude': 47.3769, 'longitude': 8.5417}, permissions=['geolocation']) page = context.new_page() page.on('pageerror', lambda e: errors.append(str(e))) page.goto(base, wait_until='networkidle') expect(page.locator('canvas').first).to_be_visible() page.get_by_role('button', name='Pause time', exact=True).click() page.get_by_label(re.compile('Observing date and time in')).fill('2026-09-05T22:00') def search(name): page.get_by_label('Search sky objects').fill(name) page.get_by_label('Search sky objects').press('Enter') search('Vega') expect(page.locator('.object-heading h2')).to_have_text('Vega') expect(page.locator('.object-stats')).to_contain_text('25 ly') page.get_by_role('button', name='CENTRE IN SKY VIEW', exact=True).click() box = page.locator('.sky-canvas').bounding_box() cx,cy = box['x']+box['width']/2, box['y']+box['height']/2 page.mouse.move(cx,cy) expect(page.get_by_role('tooltip')).to_contain_text('Vega') # An off-centre object must stay under the pointer through scroll zoom. page.mouse.down(); page.mouse.move(cx+65,cy-42,steps=10); page.mouse.up() page.wait_for_timeout(150) page.mouse.move(cx+66,cy-42) expect(page.get_by_role('tooltip')).to_contain_text('Vega') page.mouse.wheel(0,-320) page.wait_for_timeout(350) page.mouse.move(cx+65,cy-42) expect(page.get_by_role('tooltip')).to_contain_text('Vega') page.mouse.wheel(0,320) page.wait_for_timeout(350) page.mouse.move(cx+66,cy-42) expect(page.get_by_role('tooltip')).to_contain_text('Vega') page.mouse.move(10,10) page.screenshot(path=str(out/'desktop.png'), full_page=True) search('M31') expect(page.locator('.object-heading')).to_contain_text('GALAXY') expect(page.locator('.right-rail')).to_contain_text('BEYOND OUR GALAXY') search('Saturn') expect(page.locator('.right-rail')).to_contain_text('THE SOLAR SYSTEM') expect(page.get_by_role('button',name='ALL PLANETS',exact=True)).to_have_class('active') expect(page.locator('.solar-distance')).to_contain_text('AU') page.screenshot(path=str(out/'planet.png'), full_page=True) search('Mars') expect(page.get_by_role('button',name='INNER PLANETS',exact=True)).to_have_class('active') expect(page.locator('[data-planet-position]')).to_have_count(4) page.screenshot(path=str(out/'inner-planets.png'), full_page=True) page.get_by_role('button',name='ALL PLANETS',exact=True).click() expect(page.locator('[data-planet-position]')).to_have_count(8) search('ISS') expect(page.locator('.right-rail')).to_contain_text('EARTH / GROUND TRACK') expect(page.locator('.object-stats')).to_contain_text('Space station') expect(page.locator('.object-stats')).to_contain_text('1998-11-20') page.screenshot(path=str(out/'satellite.png'), full_page=True) for target,label in [('HST','Space telescope'),('sat733','Rocket body'),('SPACEMOBILE-001','Communications satellite')]: search(target) expect(page.locator('.object-stats')).to_contain_text(label) page.screenshot(path=str(out/'communications-satellite.png'), full_page=True) page.get_by_role('button',name='ALL SKY',exact=True).click() expect(page.locator('.map-readout')).to_contain_text('180°') page.get_by_role('button',name='Zoom in',exact=True).click() expect(page.locator('.map-readout')).to_contain_text('90°') page.get_by_role('button',name='OFF',exact=True).count() # highlights start on page.get_by_role('button',name='ON',exact=True).click() expect(page.get_by_role('button',name='OFF',exact=True)).to_have_attribute('aria-pressed','false') page.get_by_role('button',name='OFF',exact=True).click() page.get_by_label('Highlight limit').fill('100') expect(page.locator('.range-heading')).to_contain_text('100') page.get_by_label('Highlight limit').fill('30') stars = page.locator('.layer-row').filter(has_text='Stars').get_by_role('checkbox') stars.uncheck(); expect(stars).not_to_be_checked(); stars.check() search('Vega') page.get_by_role('button',name='CENTRE IN SKY VIEW',exact=True).click() page.get_by_role('button',name='CONNECT SIMULATOR',exact=False).click() expect(page.locator('.sim-label')).to_have_text('SIMULATING TRACKING') page.get_by_label('Field of view preset').select_option('wide') box = page.locator('.sky-canvas').bounding_box() cx,cy = box['x']+box['width']/2,box['y']+box['height']/2 page.mouse.move(cx,cy); page.mouse.down(); page.mouse.move(cx+75,cy+20,steps=10); page.mouse.up() expect(page.locator('.sim-label')).to_have_text('SIMULATING SLEW') before=page.locator('.scope-strip strong').inner_text() page.wait_for_timeout(1200) assert page.locator('.scope-strip strong').inner_text()!=before, 'Simulated mount coordinates did not move' page.screenshot(path=str(out/'slewing.png'),full_page=True) page.get_by_role('button',name='STOP',exact=False).click() expect(page.locator('.sim-label')).to_have_text('SIMULATOR STOPPED') page.get_by_label('Field of view preset').select_option('camera') expect(page.get_by_label('Field width in degrees')).to_have_value('0.321') page.get_by_role('button',name='ZOOM TO FIELD',exact=False).click() expect(page.locator('.map-readout')).to_contain_text('FIELD 1.60°') page.screenshot(path=str(out/'field.png'),full_page=True) page.locator('.location-top').click() page.get_by_label('Choose a location').select_option(label='Sydney') page.get_by_role('button',name='UPDATE SKY',exact=False).click() expect(page.locator('.location-top')).to_contain_text('SYDNEY') page.locator('.location-top').click() page.get_by_label('Latitude',exact=True).focus() page.keyboard.press('Escape') expect(page.get_by_role('dialog')).to_have_count(0) page.get_by_role('button',name='LOCATE ME',exact=False).click() expect(page.locator('.location-top')).to_contain_text('CURRENT LOCATION') expect(page.locator('.location-top')).to_contain_text('47.377') page.get_by_role('button',name='ABOUT / DATA & CONTROLS',exact=False).click() expect(page.get_by_role('dialog')).to_be_visible() page.get_by_role('button',name='Close about',exact=True).click() assert page.evaluate('document.documentElement.scrollWidth')==1600 page.set_viewport_size({'width':390,'height':844}) page.get_by_role('button',name='Reset sky view',exact=True).click() page.screenshot(path=str(out/'mobile-sky.png'),full_page=True) assert page.evaluate('document.documentElement.scrollWidth')==390, 'Mobile page overflows' page.get_by_role('button',name='CONTROLS',exact=True).click() expect(page.locator('.left-rail')).to_be_visible() page.screenshot(path=str(out/'mobile-controls.png'),full_page=True) page.get_by_role('button',name='OBJECT',exact=True).click() expect(page.locator('.right-rail')).to_be_visible() page.screenshot(path=str(out/'mobile-object.png'),full_page=True) page.get_by_role('button',name='SKY',exact=True).click() page.get_by_label(re.compile('Observing date and time in')).fill('2025-01-01T22:00') page.get_by_role('button',name='CONTROLS',exact=True).click() expect(page.locator('.left-rail')).to_contain_text('Satellite predictions unavailable') browser.close() print(json.dumps({'browser_errors':errors,'checks':'location, time, search, hover, all contexts, layers, zoom, draggable field, slew, stop, camera, mobile','screenshots':len(list(out.glob('*.png')))})) assert not errors