Spaces:
Running
Running
Download check_presentation.py from HuggingFaceBio/carbon-a-release: direct link, hf CLI and curl.
- Browser
- Download file 13.3 kB
-
https://huggingface.co/spaces/HuggingFaceBio/carbon-a-release/resolve/main/check_presentation.py
- Command line
-
hf download hf://spaces/HuggingFaceBio/carbon-a-release/check_presentation.py
-
curl -L -o check_presentation.py https://huggingface.co/spaces/HuggingFaceBio/carbon-a-release/resolve/main/check_presentation.py
13.3 kB
| """Browser verification for the presentation layout and counter-free sweep.""" | |
| from pathlib import Path | |
| import sys,json,math | |
| sys.path.insert(0,'/home/node/local/carbon-a-render/python') | |
| from playwright.sync_api import sync_playwright | |
| R=Path(__file__).parent | |
| BROWSER='/opt/pw-browsers/chromium_headless_shell-1234/chrome-headless-shell-linux64/chrome-headless-shell' | |
| ARGS=['--no-sandbox','--enable-webgl','--use-gl=angle','--use-angle=swiftshader','--enable-unsafe-swiftshader','--disable-renderer-backgrounding','--disable-background-timer-throttling','--disable-backgrounding-occluded-windows'] | |
| def check(url=None,headers=None): | |
| with sync_playwright() as p: | |
| browser=p.chromium.launch(executable_path=BROWSER,args=ARGS) | |
| page=browser.new_page(viewport={'width':1500,'height':1050},extra_http_headers=headers or {}) | |
| errors=[];page.on('pageerror',lambda e:errors.append(str(e))) | |
| page.goto(url or (R/'index.html').as_uri(),wait_until='domcontentloaded') | |
| page.wait_for_function('window.POC && !document.querySelector("#loading")',timeout=60000) | |
| assert page.evaluate('POC.assemblies')==33588 | |
| assert page.locator('#animate-sweep,.sweep-meter,#seen-species,#seen-assemblies,#seen-loci').count()==0 | |
| assert page.locator('.resource-links a').all_text_contents()==['Model↗','Database↗','Explorer↗','Paper↗','Blog↗'] | |
| assert page.locator('h1').inner_text()=='Carbon-A: Discovering Genes Across the Tree of Life' | |
| assert page.locator('.wordmark em').evaluate('e=>getComputedStyle(e).color')=='rgb(41, 233, 154)' | |
| assert page.locator('#view-dialog').is_hidden() | |
| resources=page.locator('.resources').bounding_box();tree=page.locator('.stage-wrap').bounding_box() | |
| assert resources['x']+resources['width']<=tree['x'] | |
| assert tree['height']>880 and abs(tree['y']+tree['height']-1050)<2 | |
| assert page.evaluate('Math.abs(stage.getBoundingClientRect().x+screen([0,0])[0]-innerWidth/2)')<1 | |
| assert page.locator('#links-title').inner_text()=='Links:' | |
| buttons=[a.bounding_box() for a in page.locator('.resource-links a').all()] | |
| assert max(b['width'] for b in buttons)-min(b['width'] for b in buttons)<1 | |
| assert all(a['y']+a['height']<b['y'] for a,b in zip(buttons,buttons[1:])) | |
| assert page.locator('.strand-legend').bounding_box()['y']>buttons[-1]['y']+buttons[-1]['height'] | |
| page.screenshot(path=str(R/'presentation-desktop.png')) | |
| # Hover previews a strand and its details; clicking pins that preview. | |
| # The next click anywhere clears without selecting a replacement. | |
| def point(i,radius=62): | |
| return page.evaluate("""([i,radius])=>{const q=screen(polar(radius,leaves[i].angle)),b=stage.getBoundingClientRect();return [b.x+q[0],b.y+q[1]]}""",[i,radius]) | |
| page.mouse.move(*point(12345));page.wait_for_function('hoveredAssembly?.i===12345') | |
| assert page.evaluate('selected.i===12345 && pinnedAssembly===null') | |
| assert page.locator('#selection').is_visible() | |
| assert page.locator('#sel-state').text_content()=='Preview · click strand to keep' | |
| assert page.locator('#sel-id').inner_text()==page.evaluate('leaves[12345].accession') | |
| page.mouse.move(*point(25000));page.wait_for_function('hoveredAssembly?.i===25000') | |
| page.mouse.move(0,0);page.wait_for_function('selected===null') | |
| assert page.locator('#selection').is_hidden() | |
| page.mouse.move(*point(12345,730));page.wait_for_function('hoveredAssembly!==null') | |
| assert page.evaluate('selected===hoveredAssembly && pinnedAssembly===null') | |
| # Highlights use the sampled WebGL strand, including on hover. | |
| assert page.evaluate('''()=>{const moves=[],lines=[],move=fx.moveTo,line=fx.lineTo;fx.moveTo=(...p)=>{moves.push(p);move.apply(fx,p)};fx.lineTo=(...p)=>{lines.push(p);line.apply(fx,p)};try{drawInteraction()}finally{fx.moveTo=move;fx.lineTo=line}const a=selected,expected=[];for(let k=a.geometryStart+4;k<a.geometryEnd;k+=8)expected.push(screen([hitData[k],hitData[k+1]]));return JSON.stringify(lines.slice(1))===JSON.stringify(expected)&&JSON.stringify(moves[1])===JSON.stringify(screen([hitData[a.geometryStart],hitData[a.geometryStart+1]]))}''') | |
| page.mouse.move(*point(12345));page.wait_for_function('hoveredAssembly?.i===12345') | |
| page.mouse.click(*point(12345));assert page.evaluate('pinnedAssembly.i')==12345 | |
| assert page.locator('#selection').is_visible() and page.evaluate('zoom')==1 | |
| page.mouse.move(*point(25000));page.wait_for_timeout(80) | |
| assert page.evaluate('pinnedAssembly.i===12345 && selected===pinnedAssembly') | |
| page.mouse.click(*point(25000));assert page.evaluate('selected===null') | |
| assert page.locator('#selection').is_hidden() | |
| page.wait_for_timeout(100);assert page.evaluate('selected===null') | |
| page.mouse.click(*point(25000));assert page.evaluate('pinnedAssembly.i')==25000 | |
| page.locator('h1').click();assert page.evaluate('selected===null') | |
| # Plain strands, icons and binned bars all use selection, without zoom or | |
| # changing bar mode. Clicking info-box text also deselects. | |
| targets=[point(12345,730)] | |
| targets+=page.evaluate("""()=>{const r=stage.getBoundingClientRect(),ic=iconHits[0],bin=locusBins.find(b=>b.known&&b.value>10000),q=screen(polar(R+BAR_GAP+4,leaves[Math.floor(bin.mid)].angle));return [[r.x+ic.x,r.y+ic.y],[r.x+q[0],r.y+q[1]]]}""") | |
| for xy in targets: | |
| page.mouse.click(*xy);assert page.evaluate('selected!==null') | |
| assert page.evaluate('zoom')==1 and page.evaluate('locusMode')=='5' | |
| page.locator('#sel-name').click();assert page.evaluate('selected===null') | |
| page.mouse.dblclick(*point(12345,730));assert page.evaluate('zoom')==1 | |
| assert page.evaluate('selected===null') | |
| # Wheel zoom and pan have an always-accessible reset on the graphic. | |
| page.mouse.move(*point(10000));page.mouse.wheel(0,-450) | |
| page.wait_for_function('zoom>1 && !document.querySelector("#home").hidden') | |
| page.locator('#home').click();page.wait_for_function('zoom===1 && pan.x===0 && pan.y===0') | |
| page.locator('#home').wait_for(state='hidden') | |
| box=page.locator('#stage').bounding_box() | |
| page.mouse.move(box['x']+20,box['y']+30);page.mouse.down() | |
| page.mouse.move(box['x']+75,box['y']+60,steps=4);page.mouse.up() | |
| page.wait_for_function('!document.querySelector("#home").hidden') | |
| assert page.evaluate('selected===null') | |
| page.locator('#home').click();assert page.evaluate('pan.x===0 && pan.y===0') | |
| # Search is accessible through the dialog; selecting a result closes it. | |
| page.locator('#view-options').click();page.locator('#search').fill('GCA_000001405.29') | |
| page.locator('#results button').first.click() | |
| assert page.locator('#view-dialog').is_hidden() | |
| assert page.locator('#sel-id').inner_text()=='GCA_000001405.29' | |
| assert page.evaluate('zoom')==1 | |
| assert page.locator('#sel-files').get_attribute('href').endswith('/GCA_000001405.29') | |
| page.locator('#sel-download').click();compile(page.locator('#data-code').inner_text(),'download_example','exec') | |
| assert page.evaluate('selected===null') # the info action runs before clearing | |
| page.locator('#data-close').click() | |
| page.locator('#view-options').click();page.locator('#scope').select_option('1');page.locator('#locus-mode').select_option('20') | |
| assert page.evaluate('mode')==1 and page.evaluate('locusMode')=='20' | |
| page.locator('#scope').select_option('0');page.locator('#locus-mode').select_option('5');page.locator('#reset').click();page.locator('#view-close').click() | |
| # Space inside a search input must never start animation. | |
| page.locator('#view-options').click();page.locator('#search').focus();page.keyboard.press('Space') | |
| assert not page.evaluate('sweep.running') | |
| page.locator('#view-close').click();page.locator('#theme-toggle').click();assert page.evaluate('theme')=='light' | |
| page.locator('#theme-toggle').click() | |
| # Test capture from a fresh load, matching the recording entry point. | |
| # Headless Chromium may stop compositor frames after repeated modal closures. | |
| page.reload(wait_until='domcontentloaded') | |
| page.wait_for_function('window.POC && !document.querySelector("#loading")',timeout=60000) | |
| # Keyboard playback, smooth full turn, exact highlighted geometry and no UI counters. | |
| page.evaluate('''()=>{document.activeElement.blur();window.framesSeen=[];window.treeRedraws=0;const oldDraw=gl.drawArrays.bind(gl);gl.drawArrays=(...a)=>{treeRedraws++;oldDraw(...a)};const oldUpdate=updateSweep;updateSweep=t=>{oldUpdate(t);framesSeen.push({t:performance.now(),p:sweep.progress,angle:sweep.angle,selected:selected?.i??null,info:document.querySelector('#sel-id').textContent,expected:selected?.accession})}}''') | |
| page.keyboard.press('Space');page.wait_for_function('sweep.running && sweep.progress>.1') | |
| assert page.locator('#selection').is_visible() | |
| try: page.wait_for_function('sweep.status==="complete"',timeout=12000) | |
| except Exception: | |
| print('sweep failure',page.evaluate('({sweep,hidden:document.hidden,frames:window.framesSeen?.length,focus:document.activeElement.tagName,now:performance.now(),redraws:treeRedraws,pending,zoom})'),errors,flush=True);page.screenshot(path=str(R/'sweep-failure.png'));print('after screenshot',page.evaluate('sweep'),flush=True);raise | |
| result=page.evaluate('({frames:framesSeen,redraws:treeRedraws,angle:sweep.angle,selected:selected?.i,pinned:pinnedAssembly?.i,zoom})') | |
| frames=result['frames'];duration=frames[-1]['t']-frames[1]['t'] | |
| assert 3900<=duration<4700,duration | |
| assert len(frames)>35,len(frames) | |
| assert result['redraws']<6,result['redraws'] | |
| assert abs(result['angle']-1.5*math.pi)<1e-9 | |
| assert result['selected']==33587 and result['pinned']==33587 and result['zoom']==1 | |
| assert all(f['info']==f['expected'] for f in frames), 'Info must match the highlighted strand every frame' | |
| assert page.locator('#selection').is_visible() | |
| page.screenshot(path=str(R/'sweep-info.png')) | |
| assert all(a['angle']<=b['angle'] for a,b in zip(frames,frames[1:])) | |
| # Space toggles playback off and freezes the current strand and info. | |
| # Repeats from holding the key do not toggle, scroll or activate a button. | |
| page.keyboard.down('Space');page.wait_for_function('sweep.running && sweep.progress>.1') | |
| page.keyboard.down('Space');assert page.evaluate('sweep.running') | |
| page.keyboard.up('Space');page.keyboard.press('Space') | |
| stopped=page.evaluate('({running:sweep.running,status:sweep.status,progress:sweep.progress,angle:sweep.angle,id:selected?.i,pin:pinnedAssembly?.i})') | |
| assert not stopped['running'] and stopped['status']=='stopped' | |
| assert .1<stopped['progress']<1 and stopped['id']==stopped['pin'] | |
| assert page.locator('#selection').is_visible() | |
| page.wait_for_timeout(250) | |
| assert page.evaluate('({running:sweep.running,status:sweep.status,progress:sweep.progress,angle:sweep.angle,id:selected?.i,pin:pinnedAssembly?.i})')==stopped | |
| # Playback can be stopped while a button holds focus, without triggering it. | |
| page.keyboard.press('Space');page.wait_for_function('sweep.running && sweep.progress>.1') | |
| page.locator('#sel-download').focus();page.keyboard.press('Space') | |
| assert not page.evaluate('sweep.running') and page.evaluate('pinnedAssembly===selected && selected!==null') | |
| assert page.locator('#data-dialog').is_hidden() | |
| assert page.locator('#selection').is_visible() | |
| page.evaluate('document.activeElement.blur()') | |
| page.keyboard.press('Space');page.wait_for_function('sweep.progress>.1 && sweep.running');page.keyboard.press('Escape') | |
| assert not page.evaluate('sweep.running') and page.evaluate('selected===null') | |
| # A phone layout has no horizontal overflow and retains all resource buttons. | |
| page.evaluate('reset()');page.mouse.move(0,0);page.set_viewport_size({'width':390,'height':844}) | |
| page.wait_for_timeout(150) | |
| assert page.evaluate('document.documentElement.scrollWidth')==390 | |
| resources=page.locator('.resources').bounding_box();tree=page.locator('.stage-wrap').bounding_box() | |
| assert tree['width']==390 and tree['height']>=660 | |
| assert page.evaluate('Math.abs(stage.getBoundingClientRect().x+screen([0,0])[0]-innerWidth/2)')<1 | |
| assert resources['y']>=tree['y']+tree['height'] | |
| assert all(x.is_visible() for x in page.locator('.resource-links a').all()) | |
| page.screenshot(path=str(R/'presentation-mobile.png'),full_page=True) | |
| page.mouse.click(*point(11000));assert page.locator('#selection').is_visible() | |
| page.locator('h1').click();assert page.evaluate('selected===null') | |
| # Capture mode must retain the info box too. | |
| page.evaluate("document.documentElement.dataset.recording='true';POC.startSweep()") | |
| page.wait_for_function('sweep.progress>.1 && sweep.running') | |
| assert page.locator('#selection').is_visible() | |
| page.locator('h1').click() | |
| assert page.evaluate('selected===null && !sweep.running') | |
| assert page.locator('#selection').is_hidden() | |
| assert not errors,errors | |
| browser.close() | |
| return {'sweep_duration_ms':round(duration),'sweep_frames':len(frames),'background_redraws':result['redraws'],'layout':'desktop and mobile passed','hover_preview_and_exact_geometry':'passed','click_selection_and_anywhere_deselection':'passed','space_toggle_and_focused_button_stop':'passed','clicks_do_not_zoom':'passed','visible_home_reset':'passed','sweep_info_matches_strand':'passed','search_and_downloads':'passed','resource_links':5,'counter_and_button':'removed','errors':errors} | |
| if __name__=='__main__': | |
| result=check();(R/'presentation-verification.json').write_text(json.dumps(result,indent=2));print(json.dumps(result)) | |