import asyncio,json,math,os,re from pathlib import Path OUTPUT=Path(os.environ.get("SIDEREAL_TEST_OUTPUT","/tmp/sidereal-deep-check")) OUTPUT.mkdir(parents=True,exist_ok=True) from datetime import datetime,timezone 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=[];requests=[];responses=[];response_ready=asyncio.Event() page.on('pageerror',lambda e:errors.append(str(e))) page.on('request',lambda r:requests.append(r.url) if '/api/stars/deep?' in r.url else None) async def response(r): if '/api/stars/deep?' in r.url and r.status==200: responses.append(await r.json());response_ready.set() page.on('response',response) 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})); window.faintDots=[]; const clear=CanvasRenderingContext2D.prototype.clearRect,arc=CanvasRenderingContext2D.prototype.arc; CanvasRenderingContext2D.prototype.clearRect=function(...args){if(!this.canvas.classList.contains('map-overlay'))window.faintDots=[];return clear.apply(this,args)}; CanvasRenderingContext2D.prototype.arc=function(x,y,r,...rest){if(r>=0.85&&r<=1.2&&!this.canvas.classList.contains('map-overlay'))window.faintDots.push({x,y});return arc.call(this,x,y,r,...rest)}; """) base=os.environ.get('SIDEREAL_TEST_URL','http://127.0.0.1:7866') if 'hf.space' in base: async def auth(route):await route.continue_(headers={**route.request.headers,'Authorization':'Bearer '+os.environ['HF_TOKEN']}) await page.route('https://lvwerra-sidereal.hf.space/**',auth) await page.goto(base,wait_until='networkidle') assert not requests slider=page.get_by_label('Faintest displayed star magnitude') assert await slider.get_attribute('max')=='14' await page.get_by_label('Search sky objects').fill('Vega') await page.locator('.search-results button').first.click() await slider.fill('14') assert '10°' in await page.locator('.deep-star-status').inner_text() await page.wait_for_timeout(500) assert not requests await page.get_by_role('button',name='ZOOM FOR FAINT STARS',exact=False).click() await page.wait_for_function("document.querySelector('.deep-star-status')?.textContent.includes('Dense field') || document.querySelector('.deep-star-status')?.textContent.includes('Gaia field loaded')",timeout=45000) initial=await page.locator('.sky-canvas').bounding_box() await page.mouse.move(initial['x']+initial['width']/2,initial['y']+initial['height']/2) await page.mouse.wheel(0,math.log(.2)/.0015) await page.wait_for_function("document.querySelector('.deep-star-status')?.textContent.includes('Gaia field loaded')",timeout=45000) await asyncio.wait_for(response_ready.wait(),5) assert len(responses)==2 and len(responses[-1]['stars'])>500,(requests,len(responses)) assert max(s['mag'] for s in responses[-1]['stars'])>13.9 assert not responses[-1]['truncated'] await page.wait_for_timeout(150) box=await page.locator('.sky-canvas').bounding_box() dots=await page.evaluate('window.faintDots') selected=False for dot in dots[::max(1,len(dots)//30)]: if not(500 for width in [390,320]: await page.set_viewport_size({'width':width,'height':844}) await page.get_by_role('button',name='CONTROLS',exact=True).click() assert await slider.is_visible() assert not await page.evaluate('document.documentElement.scrollWidth>innerWidth') await page.screenshot(path=str(OUTPUT/f'deep-mobile-{width}.png')) assert not errors,errors print(f'PASSED {base}: {len(responses[-1]["stars"])} Gaia entries through {max(s["mag"] for s in responses[-1]["stars"]):.2f}; zoom gate, hover, click, G-band details, cone reuse, selection/search, full-sky mode, mobile. Browser errors: {errors}') await browser.close() asyncio.run(main())