File size: 15,645 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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
"""Browser regression for presentation, motion preferences and existing research controls.

Run against a built exhibition preview; this check never submits inference jobs.
PREVIEW_URL and CHROMIUM_PATH select an existing server/browser.
"""
import json
import os
from pathlib import Path
from playwright.sync_api import sync_playwright, expect

OUT = Path('artifacts/refinements-20261110')
OUT.mkdir(parents=True, exist_ok=True)
BASE = os.environ.get('PREVIEW_URL', 'http://127.0.0.1:7860')
errors = []
requests_failed = []
checks = []

def scroll(page, selector):
    page.locator(selector).evaluate("e=>window.scrollTo({top:e.getBoundingClientRect().top+scrollY-100,behavior:'instant'})")
    page.wait_for_timeout(350)

def no_overflow(page, label):
    assert page.evaluate('document.documentElement.scrollWidth <= innerWidth'), label + ' overflow'
    checks.append(label)
    print('PASS:', label, flush=True)

with sync_playwright() as p:
    browser = p.chromium.launch(executable_path=os.environ.get('CHROMIUM_PATH'), args=[
        '--no-sandbox', '--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'])
    context = browser.new_context(viewport={'width': 1440, 'height': 1000})
    page = context.new_page()
    page.on('pageerror', lambda error: errors.append(str(error)))
    page.on('response', lambda response: requests_failed.append(f'{response.status} {response.url}') if response.status >= 400 else None)
    inference_requests = []
    page.on('request', lambda request: inference_requests.append(request.url) if request.method == 'POST' and '/api/' in request.url else None)
    page.goto(BASE, wait_until='networkidle')
    page.evaluate('document.fonts.ready')
    expect(page.locator('[data-render-state="ready"]')).to_have_count(2, timeout=30000)
    expect(page.locator('.hero-scene')).to_have_attribute('data-running', 'true')
    expect(page.locator('.site-header .ouroboros-mark')).to_be_visible()
    expect(page.locator('.hero-brand-lockup .ouroboros-mark')).to_be_visible()
    page.wait_for_timeout(1100)
    page.screenshot(path=str(OUT / 'desktop-hero.png'))
    for label, direction in [('Design →', 'design'), ('← Describe', 'describe'), ('Learn together', 'joint')]:
        page.get_by_role('group', name='Hero learning direction', exact=True).get_by_role('button', name=label, exact=True).click()
        expect(page.locator('.hero-scene')).to_have_attribute('data-direction', direction)
    # Path animation actually advances, then freezes when paused.
    path = page.locator('.design-path .path-stream')
    before = path.evaluate('e=>getComputedStyle(e).strokeDashoffset')
    page.wait_for_timeout(200)
    assert path.evaluate('e=>getComputedStyle(e).strokeDashoffset') != before
    page.get_by_role('button', name='Pause page animations', exact=True).click()
    expect(page.locator('html')).to_have_attribute('data-motion', 'paused')
    page.wait_for_timeout(150)
    frozen = path.evaluate('e=>getComputedStyle(e).strokeDashoffset')
    page.wait_for_timeout(200)
    after_pause = path.evaluate('e=>getComputedStyle(e).strokeDashoffset')
    assert after_pause == frozen, (frozen, after_pause, path.evaluate('e=>getComputedStyle(e).animationPlayState'))
    expect(page.locator('.hero-scene')).to_have_attribute('data-running', 'false')
    page.reload(wait_until='networkidle')
    expect(page.get_by_role('button', name='Resume page animations', exact=True)).to_be_visible()
    scroll(page, '#evidence')
    assert page.locator('.bar-track>span').first.evaluate('e=>getComputedStyle(e).transform') == 'none'
    checks.append('Animation pause, persisted preference and visible charts while paused')
    page.get_by_role('button', name='Resume page animations', exact=True).click()
    scroll(page, '#dual-learning')
    expect(page.locator('.hero-scene')).to_have_attribute('data-running', 'false')
    expect(page.locator('nav a[href="#dual-learning"]')).to_have_attribute('aria-current', 'location')
    print('PASS: global motion and hero controls', flush=True)
    dual = page.locator('#dual-learning')
    # Every illustration is a function of scroll position, including on rewind.
    expected = json.loads(Path('public/data/core-example.json').read_text())
    stage = page.locator('.story-stage')
    def at(value):
        page.locator('.story-scroll').evaluate("""(e,p)=>{const stage=e.querySelector('.story-stage');
          const offset=parseFloat(getComputedStyle(stage).top);
          window.scrollTo({top:e.getBoundingClientRect().top+scrollY-offset+(e.offsetHeight-stage.offsetHeight)*p,behavior:'instant'});
        }""", value)
        page.wait_for_function('(p)=>Math.abs(Number(document.querySelector(".story-stage").dataset.progress)-p)<.0002', arg=value)
        page.wait_for_timeout(220)
    for name, value, beat in [('start',0,0),('encoding',.09,0),('projection',.23,1),('residues',.475,2),('reverse',.665,3),('words',.815,4),('joint',1,5)]:
        at(value)
        expect(stage).to_have_attribute('data-beat', str(beat))
        no_overflow(page, 'story ' + name)
        page.screenshot(path=str(OUT / f'desktop-{name}.png'))
    at(.385)
    halfway = page.locator('.dual-scene-svg').inner_html()
    arrived = page.locator('[data-output="protein"] [data-arrived="true"]').count()
    assert 0 < arrived < 72, arrived
    at(.475)
    assert page.locator('[data-output="protein"] [data-arrived="true"]').count() == 72
    assert page.locator('[data-output="protein"] [data-letter]').evaluate_all('els=>els.map(e=>e.dataset.letter).join("")') == expected['generatedProtein'][:72]
    at(.385)
    assert page.locator('.dual-scene-svg').inner_html() == halfway, 'Rewinding must reproduce the same complete SVG state'
    page.wait_for_timeout(400)
    assert page.locator('.dual-scene-svg').inner_html() == halfway, 'An idle scene must not animate'
    assert page.locator('[data-kind="hidden-state"] text').count() == 0
    assert page.locator('.ds-count').all_text_contents() == ['×24'] * 4
    stage.get_by_role('button', name='Pause scene', exact=True).click()
    frozen = stage.get_attribute('data-progress')
    page.mouse.wheel(0, 350)
    page.wait_for_timeout(300)
    assert stage.get_attribute('data-progress') == frozen
    # Explicit navigation is useful even while paused, and uses the same timeline.
    stage.get_by_role('button', name='06 Learn together', exact=False).click()
    expect(stage).to_have_attribute('data-beat', '5')
    stage.get_by_role('button', name='Resume scene', exact=True).click()
    at(.7)
    page.get_by_role('button', name='Pause page animations', exact=True).click()
    frozen = stage.get_attribute('data-progress')
    page.mouse.wheel(0, 200)
    page.wait_for_timeout(300)
    assert stage.get_attribute('data-progress') == frozen
    page.get_by_role('button', name='Resume page animations', exact=True).click()
    page.get_by_role('slider', name='Explanation progress').focus()
    page.keyboard.press('End')
    expect(stage).to_have_attribute('data-beat', '5')
    page.keyboard.press('Home')
    expect(stage).to_have_attribute('data-beat', '0')
    scroll(page, '#dataset')
    page.wait_for_timeout(300)
    frozen = stage.get_attribute('data-progress')
    page.mouse.wheel(0, 180)
    page.wait_for_timeout(400)
    assert stage.get_attribute('data-progress') == frozen, 'Offscreen scene must stop'
    at(.815)
    stage.get_by_role('button', name='Read all steps', exact=False).click()
    expect(dual).to_have_attribute('data-story-mode', 'reading')
    assert dual.locator('.reading-step').count() == 6
    dual.get_by_role('button', name='Return to scroll experience', exact=False).click()
    expect(dual).to_have_attribute('data-story-mode', 'scroll')
    page.locator('.story-archive summary').click()
    assert page.locator('[data-archive="generated-protein"]').inner_text() == expected['generatedProtein']
    assert page.locator('[data-archive="reference-protein"]').inner_text() == expected['protein']
    assert page.locator('[data-archive="generated-text"]').inner_text() == expected['generatedText']
    page.locator('.story-archive summary').click()
    checks.append('Six scroll beats, exact rewind, idle/offscreen freeze, local/global pause, keyboard/chapters, independent archived inputs and full outputs')
    scroll(page, '#dataset')
    assert page.locator('.key-rail button').count() == 29
    page.locator('.key-rail button').last.click()
    expect(page.locator('.key-rail button').last).to_have_attribute('aria-pressed', 'true')
    scroll(page, '#atlas')
    expect(page.locator('#atlas .atlas-case')).to_have_count(8)
    page.locator('#atlas .atlas-case').first.click()
    expect(page.locator('#atlas .atlas-preview-story')).to_contain_text('SHOWCASE 01')
    page.screenshot(path=str(OUT / 'desktop-atlas.png'))
    scroll(page, '#petase')
    petase = page.locator('#petase')
    expect(page.locator('.fold-story')).to_have_attribute('data-fold-state', 'playing', timeout=15000)
    slider = page.get_by_role('slider', name='Sequence to predicted structure')
    slider.fill('100')
    expect(slider).to_have_value('100')
    assert page.locator('.fold-canvas-wrap canvas').evaluate('e=>e.width>0&&e.height>0')
    expect(page.locator('.fold-story')).to_have_attribute('data-fold-state', 'paused')
    page.get_by_role('button', name='Replay structure transformation').click()
    expect(page.locator('.fold-story')).to_have_attribute('data-fold-state', 'playing')
    slider.fill('100')
    page.screenshot(path=str(OUT / 'desktop-fold.png'))
    checks.append('DE2 backbone loads, plays, scrubs, pauses and replays')
    assert petase.locator('.case-tabs button').count() == 10
    petase.locator('.case-tabs button').nth(1).click()
    expect(petase.locator('[data-render-state="ready"]')).to_have_count(1, timeout=30000)
    petase.locator('.case-tabs button').first.click()
    expect(petase.locator('[data-render-state="ready"]')).to_have_count(1, timeout=30000)
    petase.get_by_role('button', name='Color by confidence', exact=True).click()
    expect(petase.locator('.confidence-legend')).to_be_visible()
    petase.locator('.residue-list button').nth(42).click()
    page.keyboard.press('ArrowRight')
    expect(petase.locator('.residue-list button').nth(43)).to_be_focused()
    petase.get_by_role('button', name='Try this function in the lab', exact=False).click()
    expect(page.locator('#model-input')).not_to_have_value('')
    expect(page.locator('#playground')).to_contain_text('Original LoRA generation prompt')
    assert page.locator('#playground .generation-result').count() == 0
    scroll(page, '#evidence')
    evidence = page.locator('#evidence')
    evidence.get_by_role('button', name='Keyword recovery', exact=True).click()
    expect(evidence.locator('.chart-selection')).to_contain_text('n = 476')
    evidence.get_by_role('button', name='Protein → Text', exact=True).click()
    evidence.get_by_role('button', name='ROUGE-L', exact=True).click()
    assert evidence.locator('#benchmark-set').count() == 0
    expect(evidence.locator('.benchmark-dataset')).to_contain_text('Swiss-Prot time-cutoff')
    assert '100 pairs' not in evidence.inner_text()
    assert 'Hard' not in evidence.inner_text()
    assert evidence.locator('.bar-row').count() > 1
    page.wait_for_timeout(1100)
    page.screenshot(path=str(OUT / 'desktop-evidence.png'))
    checks.append('29 annotations, 8 Atlas cases, PETase structure/residues, prompt reuse, benchmark filters')
    no_overflow(page, '1440px desktop')
    # Ensure paused preferences on the homepage do not strand the standalone Atlas.
    page.get_by_role('button', name='Pause page animations', exact=True).click()
    page.goto(BASE + '/atlas?id=UniRef50_A0A9J7N2Q8', wait_until='networkidle')
    page.get_by_role('button', name='Resume page animations', exact=True).click()
    expect(page.locator('.atlas-structure [data-render-state="ready"]')).to_have_count(1, timeout=30000)
    expect(page.locator('.atlas-description')).to_be_visible()
    no_overflow(page, 'Standalone Atlas')
    for width in [1280, 1024, 820, 768, 390, 320]:
        page.set_viewport_size({'width': width, 'height': 844})
        page.goto(BASE, wait_until='networkidle')
        page.wait_for_timeout(900)
        no_overflow(page, f'{width}px homepage')
        if width in [390, 320]:
            page.screenshot(path=str(OUT / f'mobile-{width}-hero.png'))
            scroll(page, '.hero-scene')
            page.screenshot(path=str(OUT / f'mobile-{width}-scene.png'))
        for section in ['dual-learning', 'dataset', 'atlas', 'petase', 'evidence', 'playground']:
            scroll(page, '#' + section)
            no_overflow(page, f'{width}px {section}')
            if width == 390 and section in ['dual-learning', 'atlas', 'petase']:
                page.screenshot(path=str(OUT / f'mobile-{section}.png'))
        if width <= 1100:
            expect(page.locator('#dual-learning')).to_have_attribute('data-story-mode', 'reading')
            assert page.locator('.reading-step').count() == 6
            for step in range(1, 7):
                scroll(page, f'#dual-step-{step}')
                no_overflow(page, f'{width}px reading step {step}')
                if width == 390:
                    page.screenshot(path=str(OUT / f'mobile-step-{step}.png'))
        if width <= 780:
            page.get_by_role('button', name='Toggle navigation', exact=True).click()
            expect(page.locator('#main-navigation')).to_be_visible()
            page.keyboard.press('Escape')
            expect(page.locator('#main-navigation')).not_to_be_visible()
            expect(page.get_by_role('button', name='Toggle navigation')).to_be_focused()
            page.get_by_role('button', name='Toggle navigation', exact=True).click()
            page.locator('nav a[href="#dataset"]').click()
            expect(page.locator('#main-navigation')).not_to_be_visible()
    # System preference is respected immediately and when changed at runtime.
    page.emulate_media(reduced_motion='reduce')
    page.goto(BASE, wait_until='networkidle')
    expect(page.locator('html')).to_have_attribute('data-motion', 'paused')
    expect(page.get_by_role('button', name='Animations off: reduced motion preference')).to_be_disabled()
    expect(page.locator('.hero-scene')).to_have_attribute('data-running', 'false')
    assert page.locator('.path-stream').first.evaluate('e=>getComputedStyle(e).animationName') == 'none'
    scroll(page, '#dual-learning')
    expect(page.locator('#dual-learning')).to_have_attribute('data-story-mode', 'reading')
    assert page.locator('.reading-step').count() == 6
    assert page.locator('.story-stage').count() == 0
    page.set_viewport_size({'width':1440,'height':1000})
    expect(page.locator('#dual-learning')).to_have_attribute('data-story-mode', 'reading')
    scroll(page, '#evidence')
    assert page.locator('.bar-track>span').first.evaluate('e=>getComputedStyle(e).transform') == 'none'
    page.emulate_media(reduced_motion='no-preference')
    expect(page.get_by_role('button', name='Pause page animations')).to_be_enabled()
    checks.append('Reduced motion at load and runtime; all outputs and charts remain readable')
    assert not errors, errors
    assert not requests_failed, requests_failed
    assert not inference_requests, inference_requests
    browser.close()

report = {'checks': checks, 'page_errors': errors, 'http_errors': requests_failed,
          'inference_jobs_submitted': len(inference_requests)}
(OUT / 'browser-report.json').write_text(json.dumps(report, indent=2) + '\n')
print(json.dumps(report, indent=2))