"""Offline Chromium check for real four-theme mobile templates without localhost/file navigation. Uses page.set_content to work in CI sandboxes that block navigation to localhost/file URLs. Tests actual rendered HTML, CSS, and JS; external image loading is not asserted. """ from pathlib import Path import json,re,sys from playwright.sync_api import sync_playwright, expect ROOT=Path(__file__).resolve().parents[1] SAMPLE=ROOT/'examples'/'mobile-showcase' def inline(theme,page_name="index.html"): path=SAMPLE/theme html=(path/page_name).read_text('utf-8') css='\n'.join((path/'assets'/name).read_text('utf-8') for name in ['theme.css','blocks.css','factory.css','pro-base.css','pro.css','commercial.css','custom.css']) html=re.sub(r']*rel="stylesheet"[^>]*>', '',html) html=re.sub(r'', '',html) html=html.replace('','') js=(path/'assets/pro.js').read_text('utf-8') html=html.replace('','') return html def run(): results=[] with sync_playwright() as pw: browser=pw.chromium.launch(executable_path='/usr/bin/chromium',headless=True,args=['--no-sandbox','--disable-dev-shm-usage']) try: for theme in ['automotive','industrial','corporate','minimal']: page=browser.new_page(viewport={'width':390,'height':844},device_scale_factor=1) errors=[] page.on('pageerror',lambda e:errors.append(str(e))) page.set_content(inline(theme),wait_until='domcontentloaded') assert page.locator('.sf-mobile-tabs').is_visible(),theme+' tabs absent' assert page.locator('[data-mobile-open-menu]').count()==1 assert not page.locator('#pro-navigation').is_visible(),theme+' drawer initially visible' page.locator('[data-pro-menu]').click() assert page.locator('#pro-navigation').is_visible(),theme+' drawer not open' expect(page.locator('.sf-mobile-drawer-backdrop')).to_be_visible(timeout=3000) assert page.locator('[data-pro-menu]').get_attribute('aria-expanded')=='true' page.keyboard.press('Escape') assert not page.locator('#pro-navigation').is_visible(),theme+' Esc fails' page.locator('[data-mobile-open-menu]').click() assert page.locator('#pro-navigation').is_visible(),theme+' tabs menu failed' page.locator('.sf-mobile-drawer-backdrop').click(position={'x':4,'y':100}) assert not page.locator('#pro-navigation').is_visible(),theme+' backdrop close fails' assert page.locator('.sf-mobile-tabs').evaluate('(el)=>getComputedStyle(el).display')=='grid' overflow=page.evaluate('document.documentElement.scrollWidth - window.innerWidth') for filename in ['index.html','listing.html','product.html','article.html']: for width in (375,390,430): sub=browser.new_page(viewport={'width':width,'height':844}) sub.set_content(inline(theme,filename),wait_until='domcontentloaded') assert sub.locator('.sf-mobile-tabs').is_visible(),f'{theme}/{filename}: tabs missing' excess=sub.evaluate('document.documentElement.scrollWidth - window.innerWidth') assert excess<=1,f'{theme}/{filename} width={width} overflow={excess}' sub.close() page.set_viewport_size({'width':1440,'height':900}) assert not page.locator('.sf-mobile-tabs').is_visible(),theme+' desktop tabs leakage' assert page.locator('#pro-navigation').is_visible(),theme+' desktop nav hidden' assert not errors,theme+' script errors: '+str(errors) results.append({'theme':theme,'drawer':'PASS','tabs':'PASS','responsive':'PASS','mobile_horizontal_overflow_px':overflow,'page_types_tested':4,'javascript_errors':len(errors)}) if theme=='automotive': page.set_viewport_size({'width':390,'height':844}) page.wait_for_timeout(350);page.screenshot(path=str(ROOT/'reports'/'mobile-v8213-automotive.png'),full_page=True) page.close() finally:browser.close() out=ROOT/'reports'/'mobile-v8213-browser.json';out.parent.mkdir(exist_ok=True,parents=True) out.write_text(json.dumps(results,ensure_ascii=False,indent=2),encoding='utf-8') print(json.dumps(results,ensure_ascii=False,indent=2)) return 0 if __name__=='__main__':sys.exit(run())