6 lines
6.7 KiB
JavaScript
6 lines
6.7 KiB
JavaScript
/* All-page layout and daily-poetry checks; Mock only, no real device writes. */
|
|
const assert=require('assert'),{chromium}=require('playwright');
|
|
(async()=>{const browser=await chromium.launch({headless:true,executablePath:'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'});const page=await browser.newPage();let unintended=0;page.on('request',r=>{if(r.method()==='POST'&&/\/(jobs|playlists|display)$/.test(r.url()))unintended++;});const base=process.env.FRAME_FLOW_URL||'http://127.0.0.1:6005';await page.goto(base);await page.locator('#username').fill(process.env.FRAME_FLOW_TEST_USERNAME);await page.locator('#password').fill(process.env.FRAME_FLOW_TEST_PASSWORD);await page.locator('#login-form button').click();await page.locator('.hero').waitFor();assert(await page.evaluate(()=>state.settings.mock));let checks=0;for(const lang of ['zh','en'])for(const writable of [false,true]){const clean=await page.evaluate(({lang,writable})=>{const previous={lang:state.lang,settings:state.settings};state.lang=lang;state.settings={...state.settings,mock:false,write_enabled:writable};shell();const clean=notice()===''&&!document.querySelector('#connection')&&!document.querySelector('.sidebar-bottom .live-dot');state.lang=previous.lang;state.settings=previous.settings;shell();return clean;},{lang,writable});assert(clean,'Live-device status must not appear in either language');checks++;}for(const width of [1440,390])for(const theme of ['light','dark']){await page.setViewportSize({width,height:1000});await page.evaluate(theme=>{localStorage.setItem('ff-theme',theme);document.documentElement.dataset.theme=theme;},theme);for(const name of ['dashboard','gallery','photo-sources','upload','templates','automation','playlists','history','settings']){await page.locator(`[data-page="${name}"]`).click();await page.waitForTimeout(350);if(name==='settings')await page.locator('#image-host-form').waitFor();const result=await page.evaluate(()=>{const children=[...document.querySelector('#main').children].filter(x=>x.getBoundingClientRect().height>0);return {overflow:document.documentElement.scrollWidth>innerWidth,buttonGaps:[...document.querySelectorAll('#main button')].flatMap(b=>{const next=b.nextElementSibling;if(next?.tagName!=='BUTTON')return [];const a=b.getBoundingClientRect(),c=next.getBoundingClientRect();if(!a.width||!c.width)return [];return [Math.abs(a.top-c.top)<4?c.left-a.right:c.top-a.bottom];}),gaps:children.slice(1).map((x,i)=>x.getBoundingClientRect().top-children[i].getBoundingClientRect().bottom)};});const controls=await page.evaluate(()=>{const nodes=[...document.querySelectorAll('#main input:not([type="checkbox"]):not([type="radio"]),#main select,#main textarea,#main button')].filter(x=>{const r=x.getBoundingClientRect();return r.width&&r.height&&getComputedStyle(x).visibility!=='hidden';});const issues=[];for(let i=0;i<nodes.length;i++)for(let j=i+1;j<nodes.length;j++){const a=nodes[i].getBoundingClientRect(),b=nodes[j].getBoundingClientRect();const ox=Math.min(a.right,b.right)-Math.max(a.left,b.left),oy=Math.min(a.bottom,b.bottom)-Math.max(a.top,b.top);const dy=Math.max(a.top,b.top)-Math.min(a.bottom,b.bottom),dx=Math.max(a.left,b.left)-Math.min(a.right,b.right);if((ox>Math.min(a.width,b.width)/2&&dy>=0&&dy<8)||(oy>Math.min(a.height,b.height)/2&&dx>=0&&dx<8))issues.push({a:nodes[i].id,b:nodes[j].id,gap:ox>0?dy:dx});}return issues;});assert(controls.length===0,`${name} ${width} ${theme} controls ${JSON.stringify(controls)}`);checks++;
|
|
assert(result.buttonGaps.every(x=>x>=8),`${name} ${width} button gaps ${result.buttonGaps}`);assert(!result.overflow,`${name} ${width} overflow`);assert(result.gaps.every(x=>x>=16),`${name} ${width} gaps ${result.gaps}`);checks++;if(name==='templates'||name==='settings')await page.screenshot({path:`docs/screenshots/${name}-${width}-${theme}.png`,fullPage:true});}}
|
|
await page.locator('[data-page="templates"]').click();await page.locator('[data-template="daily-poetry"]').click();await page.locator('#poem-choice').waitFor();assert(await page.locator('#poem-choice option').count()===21);checks++;await page.locator('#poem-choice').selectOption('jing-ye-si');await page.locator('#render-template').click();await page.locator('#template-image').waitFor();await page.waitForFunction(()=>document.querySelector('#template-image')?.naturalWidth===1200);checks++;await page.locator('#daily-poetry-job').click();assert(!await page.locator('#poetry-job-enabled').isChecked());checks++;const gap=await page.evaluate(()=>{const a=document.querySelector('#daily-poetry-job').getBoundingClientRect(),b=document.querySelector('#template-fields').getBoundingClientRect();return b.top-a.bottom;});assert(gap>=20,'Poetry action touches fields');checks++;await page.locator('#modal-cancel').click();await page.screenshot({path:'docs/screenshots/daily-poetry-controls-mobile-dark.png',fullPage:true});for(const width of [1440,390])for(const theme of ['light','dark']){await page.setViewportSize({width,height:1000});await page.evaluate(theme=>document.documentElement.dataset.theme=theme,theme);await page.locator('[data-page="templates"]').click();for(const id of ['calendar','daily-dashboard','daily-poetry','photo-text','quote','system-alert','weather']){await page.locator(`[data-template="${id}"]`).click();await page.waitForTimeout(250);await page.locator('#download-template').waitFor();if(id==='photo-text'){const photoGap=await page.evaluate(()=>document.querySelector('#template-photo-upload').getBoundingClientRect().top-document.querySelector('#field-photo_id').getBoundingClientRect().bottom);assert(photoGap>=12,`${width} ${theme} photo controls touch`);checks++;}const gap=await page.evaluate(()=>{const a=document.querySelector('#display-template').getBoundingClientRect(),b=document.querySelector('#download-template').getBoundingClientRect();return Math.abs(a.top-b.top)<4?b.left-a.right:b.top-a.bottom;});assert(gap>=8,`${id} ${width} preview buttons touch`);checks++;}for(const [name,button] of [['automation','new-job'],['playlists','new-playlist'],['templates','daily-poetry-job']]){await page.locator(`[data-page="${name}"]`).click();if(name==='templates'){await page.locator('[data-template="daily-poetry"]').click();await page.locator('#poem-choice').waitFor();}await page.locator('#'+button).click();const gap=await page.evaluate(()=>{const [a,b]=[...document.querySelectorAll('#modal .modal-actions button')].map(x=>x.getBoundingClientRect());return Math.abs(a.top-b.top)<4?b.left-a.right:b.top-a.bottom;});assert(gap>=8,`${name} ${width} modal buttons touch`);checks++;await page.locator('#modal-cancel').click();}}assert(unintended===0,`Cancel submitted ${unintended} requests`);checks++;await browser.close();console.log(`Layout/poetry browser: ${checks} passed (32 page/profile combinations)`);})().catch(e=>{console.error(e.message);process.exit(1)});
|