74 lines
5.7 KiB
JavaScript
74 lines
5.7 KiB
JavaScript
const {chromium}=require(process.env.PLAYWRIGHT_MODULE||'playwright');
|
|
const fs=require('node:fs'),path=require('node:path'),assert=require('node:assert/strict');
|
|
const root=path.resolve(__dirname,'..'),consoleRoot=path.resolve(root,'../pigway-device-console');
|
|
const manifest=JSON.parse(fs.readFileSync(path.join(root,'plugin/manifest.json')));
|
|
(async()=>{
|
|
const browser=await chromium.launch({headless:true});
|
|
try{
|
|
for(const lang of ['zh','en'])for(const theme of ['light','dark'])for(const width of [1440,820,390]){
|
|
let host={id:'frame-service',name:'Frame Flow',adapter_id:manifest.id,category:'host',adapter_enabled:true,capabilities:['status'],url:'https://frames.example.test',connection:{url:'https://frames.example.test'}};
|
|
let writes=[],failSave=false;
|
|
const page=await browser.newPage({viewport:{width,height:1000}}),errors=[];
|
|
page.on('pageerror',error=>errors.push(error.message));
|
|
await page.addInitScript(({lang,theme})=>{localStorage.setItem('pigwayLanguage',lang);localStorage.setItem('pigwayTheme',theme)},{lang,theme});
|
|
await page.route('http://console.test/**',route=>{
|
|
const p=new URL(route.request().url()).pathname;
|
|
if(p.startsWith('/api/')){
|
|
if(route.request().method()==='PUT'){
|
|
assert.equal(p,'/api/v1/hosts');const body=route.request().postDataJSON();writes.push(body);
|
|
assert.equal(body.operation,'save');assert.equal(body.adapter_id,manifest.id);assert.equal(body.connection.token,'');
|
|
if(failSave)return route.fulfill({status:500,json:{error:'Save failed'}});
|
|
host={...host,url:body.connection.url,connection:{...body.connection}};
|
|
return route.fulfill({json:{hosts:[host]}});
|
|
}
|
|
const values={
|
|
'/api/v1/auth/status':{authenticated:true,setup_required:false},
|
|
'/api/v1/hosts':{hosts:[host]},
|
|
'/api/v1/adapters':{adapters:[{...manifest,enabled:true,state:'ready'}]},
|
|
'/api/v1/fleet/status':{hosts:[{host,online:true,snapshot:{identity:{},resources:[{id:'frame',name:'Living room frame',type:'device'},{id:'job:slideshow',name:'Slideshow',type:'job'}],metrics:[{resource_id:'job:slideshow',key:'job_enabled',value:1},{resource_id:'job:slideshow',key:'last_run_success',value:1}],alerts:[],alerts_complete:true}}]},
|
|
};
|
|
return route.fulfill({json:values[p]||{}});
|
|
}
|
|
const file=p.startsWith('/adapter-assets/')?path.join(root,'plugin/ui/index.js'):path.join(consoleRoot,'web',p==='/'?'index.html':p.replace(/^\/assets\//,''));
|
|
if(!fs.existsSync(file))return route.fulfill({status:404});
|
|
return route.fulfill({contentType:file.endsWith('.js')?'text/javascript':file.endsWith('.css')?'text/css':'text/html',body:fs.readFileSync(file)});
|
|
});
|
|
const link=()=>page.locator('#hostCards .host-title a');
|
|
const config=async()=>{
|
|
await page.locator('[data-tab="config"]').click();
|
|
await page.locator('[data-config-section="frame-flow"]').click();
|
|
await page.locator('#saveFrameFlowConnection').waitFor();
|
|
};
|
|
await page.goto('http://console.test');await link().waitFor();
|
|
assert.equal(await link().getAttribute('href'),'https://frames.example.test/');
|
|
assert.equal(await link().getAttribute('target'),'_blank');
|
|
assert.equal(await link().getAttribute('rel'),'noopener noreferrer');
|
|
assert.equal(await link().getAttribute('aria-label'),lang==='zh'?'打开 Frame Flow 管理':'Open Frame Flow management');
|
|
assert.equal(await page.locator('#hostCards .device-details').textContent(),'Living room frame');
|
|
await config();
|
|
assert.equal(await page.locator('[data-frame-flow-connection="url"]').inputValue(),'https://frames.example.test');
|
|
assert.equal(await page.locator('[data-frame-flow-connection="token"]').inputValue(),'');
|
|
assert(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth+1));
|
|
if(lang==='zh'&&theme==='light')await page.locator('#config').screenshot({path:'/tmp/frame-flow-config-'+width+'.png'});
|
|
if(lang==='zh'&&theme==='light'&&width===1440){
|
|
const management=page.locator('[data-frame-flow-connection="management_url"]');
|
|
await management.fill('javascript:alert(1)');await page.locator('#saveFrameFlowConnection').click();
|
|
await page.locator('#frameFlowConnectionMessage.error').waitFor();assert.equal(writes.length,0);
|
|
await management.fill('https://manage.example.test');await page.locator('#saveFrameFlowConnection').click();
|
|
await page.locator('#frameFlowConnectionMessage.success').waitFor();assert.equal(writes.length,1);
|
|
await page.reload();await link().waitFor();assert.equal(await link().getAttribute('href'),'https://manage.example.test/');
|
|
await config();failSave=true;
|
|
await page.locator('[data-frame-flow-connection="management_url"]').fill('https://changed.example.test');await page.locator('#saveFrameFlowConnection').click();
|
|
await page.getByText('Save failed',{exact:true}).waitFor();
|
|
assert.equal(await page.locator('[data-frame-flow-connection="management_url"]').inputValue(),'https://changed.example.test');
|
|
failSave=false;await page.locator('[data-frame-flow-connection="management_url"]').fill('');await page.locator('#saveFrameFlowConnection').click();
|
|
await page.locator('#frameFlowConnectionMessage.success').waitFor();await page.reload();await link().waitFor();
|
|
assert.equal(await link().getAttribute('href'),'https://frames.example.test/');
|
|
host.connection.management_url='javascript:alert(1)';await page.reload();await page.locator('#hostCards h2').waitFor();assert.equal(await link().count(),0);
|
|
}
|
|
assert.deepEqual(errors,[]);await page.close();
|
|
}
|
|
console.log('PASS Frame Flow management link/configuration: default/custom/reset URL, save failure, unsafe URLs, API-only edits; 12 language/theme/viewport combinations');
|
|
}finally{await browser.close();}
|
|
})().catch(error=>{console.error(error);process.exit(1)});
|