File size: 7,826 Bytes
03677d0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
"""Browser regression for the paper Atlas refresh; uses real full-corpus lookup.

Run against the built server with a pinned Atlas release. Set CHROMIUM_PATH and
PREVIEW_URL for local installations. No live inference jobs are submitted.
"""
import json
import os
from pathlib import Path
from playwright.sync_api import sync_playwright, expect

ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT/'artifacts/atlas-refresh-20261110'
BASE = os.environ.get('PREVIEW_URL','http://127.0.0.1:7860')
CASES = json.loads((ROOT/'public/data/atlas-cases.json').read_text())
POINTS = json.loads((ROOT/'public/data/atlas-map.json').read_text())
checks=[]
def passed(message):
    checks.append(message)
    print(message,flush=True)

with sync_playwright() as p:
    browser=p.chromium.launch(executable_path=os.environ.get('CHROMIUM_PATH'),args=['--no-sandbox','--disable-dev-shm-usage','--use-gl=angle','--use-angle=swiftshader','--enable-unsafe-swiftshader'])
    context=browser.new_context(viewport={'width':1440,'height':1050},device_scale_factor=1)
    page=context.new_page();errors=[];requests=[]
    page.on('pageerror',lambda e:errors.append(str(e)))
    page.on('request',lambda r:requests.append(r.url))
    page.goto(BASE+'/atlas',wait_until='domcontentloaded')
    expect(page.locator('.atlas-marker')).to_have_count(8)
    assert page.locator('.atlas-map-error').count()==0
    expect(page.locator('.atlas-case')).to_have_count(8)
    boxes=page.locator('.atlas-marker').evaluate_all('(els)=>els.map(e=>{let r=e.getBoundingClientRect();return {x:r.x,y:r.y,w:r.width,h:r.height}})')
    for i,a in enumerate(boxes):
        for b in boxes[i+1:]:
            assert a['x']+a['w']<=b['x'] or b['x']+b['w']<=a['x'] or a['y']+a['h']<=b['y'] or b['y']+b['h']<=a['y'], 'Overlapping case markers'
    page.wait_for_timeout(750)
    page.screenshot(path=str(OUT/'final-atlas.png'),full_page=True)
    passed('Eight separated paper landmarks render on the real WebGL map')
    for i,c in enumerate(CASES):
        page.locator('.atlas-case').nth(i).click()
        expect(page.locator('.atlas-detail-body h2')).to_have_text(c['uniref_id'])
        page.locator('.atlas-structure .molecule[data-render-state="ready"]').wait_for(timeout=30000)
        expect(page.locator('.atlas-marker.selected')).to_have_count(1)
    passed('All eight case cards, structures and selected map locations work')
    page.get_by_role('button',name='Next showcase',exact=True).click()
    expect(page.locator('.atlas-detail-body h2')).to_have_text(CASES[0]['uniref_id'])
    page.reload(wait_until='domcontentloaded')
    expect(page.locator('.atlas-detail-body h2')).to_have_text(CASES[0]['uniref_id'])
    page.get_by_role('button',name='Next showcase',exact=True).click();page.go_back()
    expect(page.locator('.atlas-detail-body h2')).to_have_text(CASES[0]['uniref_id'])
    passed('Case next/previous navigation, deep links, refresh and browser history preserve selection')
    page.get_by_role('button',name='pLDDT confidence',exact=True).click()
    expect(page.get_by_role('button',name='pLDDT confidence',exact=True)).to_have_attribute('aria-pressed','true')
    page.get_by_role('button',name='N → C terminus',exact=True).click()
    page.get_by_role('button',name='Swiss-Prot overlap',exact=True).click()
    expect(page.get_by_role('button',name='Swiss-Prot overlap',exact=True)).to_have_attribute('aria-pressed','true')
    page.get_by_role('button',name='Sequence communities',exact=True).click()
    page.get_by_role('button',name='Reset map',exact=True).click()
    expect(page.locator('.atlas-marker')).to_have_count(8)
    page.get_by_role('button',name='Zoom in',exact=True).click();page.get_by_role('button',name='Zoom out',exact=True).click()
    canvas=page.locator('.atlas-map canvas');canvas.focus();page.keyboard.press('ArrowLeft');page.keyboard.press('0')
    page.wait_for_timeout(600);page.locator('.atlas-workspace').screenshot(path=str(OUT/'final-atlas-selected.png'))
    passed('Coverage layer, structure coloring, zoom/reset and keyboard pan work')
    outside='UniRef50_A0A418SD25';assert outside not in POINTS['ids']
    field=page.get_by_role('textbox',name='Find an Atlas record by ID')
    field.fill(outside[9:].lower());page.get_by_role('button',name='Find protein',exact=False).click()
    expect(page.locator('.atlas-detail-body h2')).to_have_text(outside,timeout=60000)
    expect(page.locator('.atlas-location')).to_contain_text('outside the current map sample')
    passed('Real full-corpus lookup resolves a protein absent from the 12,000-point sample')
    field.fill('../bad');page.get_by_role('button',name='Find protein',exact=False).click()
    expect(page.locator('.atlas-error')).to_contain_text('Enter a record ID')
    page.locator('.atlas-case').nth(0).click()
    with page.expect_download() as download:
        page.get_by_role('button',name='↓ FASTA',exact=True).click()
    assert download.value.suggested_filename.endswith('.fasta')
    passed('Invalid IDs show a clear error; case FASTA downloads remain available')
    page.goto(BASE,wait_until='domcontentloaded');page.locator('.hero').wait_for();page.locator('.hero .molecule[data-render-state="ready"]').wait_for(timeout=30000)
    page.evaluate('document.fonts.ready');page.screenshot(path=str(OUT/'final-home.png'))
    assert not page.locator('a[href*="/paper/"], img[src*="/paper/"], #paper').count()
    assert not any('/paper/' in url for url in requests)
    passed('Homepage contains no manuscript gallery, image requests or original-figure links')
    page.locator('#atlas').scroll_into_view_if_needed();expect(page.locator('#atlas .atlas-marker')).to_have_count(8)
    page.wait_for_timeout(650);page.locator('#atlas').screenshot(path=str(OUT/'final-home-atlas.png'))
    expect(page.locator('#atlas .atlas-map-toolbar')).to_contain_text('4,000')
    for i,c in enumerate(CASES):
        page.locator('#atlas .atlas-case').nth(i).click();expect(page.locator('.atlas-preview-story')).to_contain_text(c['uniref_id'][9:]);expect(page.locator('#atlas .atlas-marker')).to_have_count(8)
    passed('Homepage uses the 4,000-point asset and all eight preview stories respond')
    assert not page.evaluate('document.documentElement.scrollWidth>innerWidth')
    for width in [390,768]:
        page.set_viewport_size({'width':width,'height':844})
        page.goto(BASE,wait_until='domcontentloaded');page.locator('.hero').wait_for();page.wait_for_timeout(500)
        page.screenshot(path=str(OUT/f'final-home-{width}.png'))
        page.locator('#atlas').scroll_into_view_if_needed();expect(page.locator('#atlas .atlas-marker')).to_have_count(8)
        page.wait_for_timeout(600);page.locator('#atlas').screenshot(path=str(OUT/f'final-preview-{width}.png'))
        assert not page.evaluate('document.documentElement.scrollWidth>innerWidth')
        page.goto(BASE+'/atlas',wait_until='domcontentloaded');expect(page.locator('.atlas-marker')).to_have_count(8)
        page.wait_for_timeout(500);page.screenshot(path=str(OUT/f'final-atlas-{width}.png'),full_page=True)
        page.locator('.atlas-case').nth(7).click();expect(page.locator('.atlas-detail-body h2')).to_have_text(CASES[7]['uniref_id'])
        assert not page.evaluate('document.documentElement.scrollWidth>innerWidth')
    passed('390 px phone and 768 px tablet have no horizontal overflow; case selection works')
    page.emulate_media(reduced_motion='reduce');page.reload(wait_until='domcontentloaded')
    page.locator('.atlas-marker.selected').wait_for();assert page.locator('.atlas-map-error').count()==0
    passed('Reduced-motion mode preserves map and case interactions')
    assert not errors,errors
    (OUT/'browser-validation.json').write_text(json.dumps({'status':'passed','checks':checks,'page_errors':errors},indent=2)+'\n')
    browser.close()