import asyncio,json,os from datetime import datetime,timezone from urllib.parse import parse_qs,urlparse from playwright.async_api import async_playwright async def main(): async with async_playwright() as p: browser=await p.chromium.launch(headless=True,executable_path=os.environ.get('CHROMIUM_EXECUTABLE','/opt/pw-browsers/chromium-1234/chrome-linux64/chrome'),args=['--no-sandbox']) page=await browser.new_page(viewport={'width':1440,'height':1000}) errors=[];calls=[] page.on('pageerror',lambda e:errors.append(str(e))) await page.clock.set_fixed_time(datetime(2026,9,6,20,tzinfo=timezone.utc)) await page.add_init_script("localStorage.setItem('sidereal-site',JSON.stringify({name:'Bern',lat:46.948,lon:7.4474,elevation:540}))") async def route(r): calls.append(r.request.url);n=len(calls) if n==1: await r.fulfill(status=503,json={'detail':'Gaia archive temporarily unavailable.'});return params={k:float(v[0]) for k,v in parse_qs(urlparse(r.request.url).query).items()} sid='111' if n==2 else '222' star={'id':'gaia'+sid,'gaiaId':sid,'hip':'','name':'Gaia DR3 '+sid,'named':False,'source':'Gaia DR3','band':'G','epoch':2016,'ra':params['ra']/15,'dec':params['dec'],'mag':13.2,'dist':None,'spec':'','con':'','lum':None,'ci':None,'pmra':None,'pmdec':None,'x':0,'y':0,'z':0} if n==2:await asyncio.sleep(2) try:await r.fulfill(json={**params,'stars':[star],'truncated':False}) except Exception:pass # Aborted stale browser requests must not update the current field. await page.route('**/api/stars/deep?*',route) await page.goto(os.environ.get('SIDEREAL_TEST_URL','http://127.0.0.1:7866'),wait_until='networkidle') slider=page.get_by_label('Faintest displayed star magnitude') await slider.fill('14') await page.get_by_role('button',name='ZOOM FOR FAINT STARS',exact=False).click() await page.get_by_role('button',name='RETRY GAIA',exact=False).wait_for() await page.get_by_role('button',name='RETRY GAIA',exact=False).click() while len(calls)<2:await asyncio.sleep(.1) await page.get_by_label('Observing date and time in Europe/Zurich').fill('2026-09-06T10:00') await page.wait_for_function("document.querySelector('.deep-star-status')?.textContent.includes('Gaia field loaded')") await asyncio.sleep(2.2) await page.get_by_label('Search sky objects').fill('Gaia DR3 222') assert await page.locator('.search-results button').count()==1 await page.get_by_label('Search sky objects').fill('Gaia DR3 111') assert await page.get_by_text('No match in this catalogue.',exact=True).is_visible() await page.get_by_label('Search sky objects').fill('') before=len(calls) await page.locator('.layer-row').first.get_by_role('checkbox').uncheck() assert await slider.is_disabled() await page.locator('.layer-row').first.get_by_role('checkbox').check() await page.wait_for_function("document.querySelector('.deep-star-status')?.textContent.includes('Gaia field loaded')") assert len(calls)==before,'Stars toggle should reuse cached field' await slider.fill('7.5') assert await page.locator('.deep-star-status').count()==0 assert not errors,errors print('PASSED: explicit Gaia error, retry, stale-response cancellation after time change, layer toggle cache reuse, bright-only fallback, no browser errors.') await browser.close() asyncio.run(main())