File size: 3,511 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
"""Targeted scroll-scene layout, rewind, and visibility-state regression."""
from playwright.sync_api import sync_playwright,expect
from pathlib import Path
import os,json
out=Path('artifacts/scroll-story-20261110')
with sync_playwright() as p:
 b=p.chromium.launch(executable_path=os.environ.get('CHROMIUM_PATH'),args=['--no-sandbox','--use-gl=angle','--use-angle=swiftshader','--enable-unsafe-swiftshader'])
 page=b.new_page(viewport={'width':1280,'height':720})
 errors=[]
 page.on('pageerror',lambda e:errors.append(str(e)))
 page.goto(os.environ.get('PREVIEW_URL','http://127.0.0.1:7860'),wait_until='networkidle')
 def at(p):
  page.locator('.story-scroll').evaluate('(e,p)=>{const s=e.querySelector(".story-stage");window.scrollTo({top:scrollY+e.getBoundingClientRect().top-82+(e.offsetHeight-s.offsetHeight)*p,behavior:"instant"})}',p)
  page.wait_for_timeout(1100)
 at(.23)
 expect(page.locator('.story-stage')).to_have_attribute('data-beat','1')
 # Moving packets stay clear of the projection title/equation band.
 at(.385);frame=page.locator('.dual-scene-svg').inner_html()
 at(.96);at(.385)
 assert page.locator('.dual-scene-svg').inner_html()==frame
 at(1)
 page.screenshot(path=str(out/'desktop-1280x720-joint.png'))
 assert page.locator('.story-scrubber').bounding_box()['y'] < 720
 svg=page.locator('.dual-scene-svg').bounding_box()
 chapters=page.locator('.story-chapters').bounding_box()
 assert svg['y']+svg['height'] <= chapters['y'], (svg, chapters)
 assert page.locator('.ds-objective').bounding_box()['y'] < chapters['y']
 page.locator('.story-chapters button').first.click()
 expect(page.locator('.story-stage')).to_have_attribute('data-beat','0')
 at(1)
 assert page.evaluate('document.documentElement.scrollWidth<=innerWidth')
 page.set_viewport_size({'width':1920,'height':1080});at(.475)
 page.screenshot(path=str(out/'desktop-1920-residues.png'))
 assert page.evaluate('document.documentElement.scrollWidth<=innerWidth')
 page.set_viewport_size({'width':1440,'height':620})
 expect(page.locator('#dual-learning')).to_have_attribute('data-story-mode','reading')
 page.set_viewport_size({'width':1440,'height':1000})
 expect(page.locator('#dual-learning')).to_have_attribute('data-story-mode','scroll')
 # Runtime reduction yields all steps, and is reversible.
 page.emulate_media(reduced_motion='reduce')
 expect(page.locator('.reading-step')).to_have_count(6)
 page.emulate_media(reduced_motion='no-preference')
 at(.23)
 # Dispatch the browser visibility event with its hidden state to exercise cleanup.
 page.evaluate('Object.defineProperty(document,"hidden",{configurable:true,get:()=>true});document.dispatchEvent(new Event("visibilitychange"))')
 page.wait_for_timeout(100)
 frozen=page.locator('.story-stage').get_attribute('data-progress')
 page.mouse.wheel(0,250);page.wait_for_timeout(400)
 assert page.locator('.story-stage').get_attribute('data-progress')==frozen
 page.evaluate('delete document.hidden;document.dispatchEvent(new Event("visibilitychange"))')
 page.wait_for_timeout(1200)
 assert page.locator('.story-stage').get_attribute('data-progress')!=frozen
 assert not errors,errors
 b.close()
(out/'final-layout-report.json').write_text(json.dumps({'checks':['1280x720 complete sticky stage','1920x1080 display','short viewport reading fallback','rewind after final projection-layout change','runtime reduced-motion switching','simulated hidden-tab visibility event freeze and resume'],'errors':errors},indent=2))
print('Final layout and timeline checks passed')