86 lines
5.1 KiB
Python
86 lines
5.1 KiB
Python
|
|
"""Offline Chromium smoke for the REAL V8.27 workspace code using a mock origin.
|
||
|
|
No real website, network credentials, or production data is touched.
|
||
|
|
"""
|
||
|
|
import json
|
||
|
|
from pathlib import Path
|
||
|
|
from playwright.sync_api import sync_playwright
|
||
|
|
|
||
|
|
ROOT=Path(__file__).resolve().parents[1]
|
||
|
|
URL='about:blank'
|
||
|
|
|
||
|
|
|
||
|
|
def run():
|
||
|
|
store={'sites':[], 'created':0}
|
||
|
|
errors=[]
|
||
|
|
with sync_playwright() as p:
|
||
|
|
browser=p.chromium.launch(headless=True, executable_path='/usr/bin/chromium', args=['--no-sandbox'])
|
||
|
|
page=browser.new_page(viewport={'width':1360,'height':900}, device_scale_factor=1)
|
||
|
|
page.on('pageerror',lambda e:errors.append(str(e)))
|
||
|
|
# Execute the actual workspace HTML/CSS/JS without navigating to localhost
|
||
|
|
# (which this runtime's browser administrator policy blocks).
|
||
|
|
markup=(ROOT/'app/templates/workspace.html').read_text()
|
||
|
|
markup=markup.replace('<link rel="stylesheet" href="/ui/workspace.css">','')
|
||
|
|
markup=markup.replace('<script defer src="/ui/workspace-guide.js"></script>','')
|
||
|
|
markup=markup.replace('<script defer src="/ui/workspace.js"></script>','')
|
||
|
|
page.goto('about:blank')
|
||
|
|
page.set_content(markup)
|
||
|
|
page.add_style_tag(content=(ROOT/'app/static/workspace.css').read_text())
|
||
|
|
mock_js=r"""
|
||
|
|
window.mockCreateCount=0; window.mockSites=[];
|
||
|
|
window.history.pushState=()=>{};window.history.replaceState=()=>{};
|
||
|
|
window.fetch=async function(uri,opts={}){
|
||
|
|
const path=String(uri).split('?')[0], method=opts.method||'GET'; let result={};
|
||
|
|
if(path==='/api/auth/me')result={csrf:'test'};
|
||
|
|
else if(path==='/api/health')result={version:'8.27.0-dev.1'};
|
||
|
|
else if(path==='/api/sites'&&method==='GET')result=window.mockSites;
|
||
|
|
else if(path==='/api/sites'&&method==='POST'){
|
||
|
|
window.mockCreateCount++;const data=JSON.parse(opts.body);
|
||
|
|
result={id:'demo-id',name:data.name,theme:data.theme,mode:data.mode,domain:data.domain,tagline:data.tagline,updated_at:'2026-01-01T00:00:00Z',entries_count:1,build_count:0};
|
||
|
|
window.mockSites=[result];
|
||
|
|
}else if(path==='/api/sites/demo-id')result=window.mockSites[0];
|
||
|
|
else if(path==='/api/sites/demo-id/entries')result=[{id:'home',source:'starter',status:'published',route:'/',type:'page',title:'首页'}];
|
||
|
|
else if(path==='/api/sites/demo-id/builds')result=[];
|
||
|
|
else if(path==='/api/enterprise/sites/demo-id/jobs')result=[];
|
||
|
|
else if(path==='/api/production/sites/demo-id/delivery-status')result={jobs:[],history:[],configured:{}};
|
||
|
|
else return {status:404,ok:false,json:async()=>({detail:'Mock route missing: '+path})};
|
||
|
|
return {status:method==='POST'?201:200,ok:true,json:async()=>result};
|
||
|
|
};
|
||
|
|
"""
|
||
|
|
page.evaluate(mock_js)
|
||
|
|
page.add_script_tag(content=(ROOT/'app/static/workspace-guide.js').read_text())
|
||
|
|
page.add_script_tag(content=(ROOT/'app/static/workspace.js').read_text())
|
||
|
|
page.evaluate("document.dispatchEvent(new Event('DOMContentLoaded'))")
|
||
|
|
page.wait_for_selector('[data-action="new-site"]')
|
||
|
|
page.locator('[data-action="new-site"]').first.click()
|
||
|
|
assert page.get_by_text('第 1 步,共 3 步').count()>0
|
||
|
|
page.locator('#createNext').click()
|
||
|
|
assert '请填写网站名称' in page.locator('#createError').inner_text()
|
||
|
|
page.locator('#siteForm [name="name"]').fill('演示企业网站')
|
||
|
|
page.locator('#createNext').click()
|
||
|
|
assert page.locator('[data-create-step="1"]').is_visible()
|
||
|
|
page.locator('#createNext').click()
|
||
|
|
assert page.locator('[data-create-step="2"]').is_visible()
|
||
|
|
assert page.evaluate('window.mockCreateCount')==0
|
||
|
|
page.locator('#createSubmit').click()
|
||
|
|
page.wait_for_selector('.sf-journey-list',timeout=10000)
|
||
|
|
assert page.evaluate('window.mockCreateCount')==1
|
||
|
|
assert '准备网站内容' in page.locator('.sf-journey-list').inner_text()
|
||
|
|
assert '1/4' in page.locator('.sf-journey-intro').inner_text()
|
||
|
|
out=ROOT/'reports/v827';out.mkdir(parents=True,exist_ok=True)
|
||
|
|
page.screenshot(path=str(out/'v827-guided-workspace.png'),full_page=True)
|
||
|
|
page.set_viewport_size({'width':390,'height':844})
|
||
|
|
page.evaluate('window.scrollTo(0,0)')
|
||
|
|
page.wait_for_timeout(350)
|
||
|
|
page.screenshot(path=str(out/'v827-guided-mobile.png'),full_page=True)
|
||
|
|
assert page.evaluate('document.documentElement.scrollWidth <= innerWidth+1'), 'mobile horizontal overflow'
|
||
|
|
assert page.evaluate("document.querySelector('.nav-side').getBoundingClientRect().right <= 2"), 'mobile drawer should start closed'
|
||
|
|
page.locator('#newSiteTop').click()
|
||
|
|
page.locator('#siteForm [name="name"]').fill('另一家公司')
|
||
|
|
page.locator('#createNext').click()
|
||
|
|
page.screenshot(path=str(out/'v827-create-wizard-mobile.png'),full_page=True)
|
||
|
|
assert errors==[],errors
|
||
|
|
browser.close()
|
||
|
|
print('PASS: real guided workspace JS, 3-step validation, actual create request, starter homepage excluded, 0 JS exceptions')
|
||
|
|
|
||
|
|
if __name__=='__main__':run()
|