81 lines
7.6 KiB
JavaScript
81 lines
7.6 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import {resolve} from 'node:path';
|
|
import {mkdir} from 'node:fs/promises';
|
|
import {contextFor,appearance} from './ui-fixture.mjs';
|
|
export async function transportChecks(page,engine,layout,theme) {
|
|
const shots=resolve('plans/transport-shots');await mkdir(shots,{recursive:true});
|
|
const result=await page.evaluate(async()=>{
|
|
const ids=['playBtn','prevBtn','nextBtn','back10Btn','fwd10Btn','stgBack','stgPlay','stgFwd','miniPlayBtn','servicePrevBtn','servicePlayBtn','serviceNextBtn','rvPrev','rvToggle','rvNext'];
|
|
const legacy=/[▶⏸⏮⏭⏪⏩⏯◀❚]/u;
|
|
const bad=[...document.querySelectorAll('button,a,.home-pl-play,.brand-mark,.ph-logo,.empty-icon')].filter(n=>legacy.test(n.textContent)).map(n=>n.outerHTML);
|
|
const inspect=el=>{
|
|
const svg=el.querySelector('.transport-icon'),style=getComputedStyle(svg),parent=getComputedStyle(el),box=el.getBoundingClientRect();
|
|
return {id:el.id,kind:svg?.dataset.transportIcon,href:[...svg.querySelectorAll('use')].find(n=>getComputedStyle(n).display!=='none')?.getAttribute('href'),fill:style.fill,stroke:style.stroke,color:style.color,parentColor:parent.color,legacy:!!el.querySelector('.fill,.legacy-nav-icon'),label:el.getAttribute('aria-label'),width:box.width,height:box.height,visible:box.width>0&&box.height>0};
|
|
};
|
|
const icons=ids.map(id=>inspect(document.getElementById(id)));
|
|
const previous=document.querySelector('#prevBtn svg');
|
|
const previousBox=[...previous.querySelectorAll('use')].find(n=>getComputedStyle(n).display!=='none').getBBox();
|
|
const previousX=previousBox.x;
|
|
const shapes=[...document.querySelectorAll('#playBtn .transport-shape')].filter(n=>getComputedStyle(n).display!=='none').map(n=>n.classList[1]);
|
|
const old=Object.getOwnPropertyDescriptor(Player.master,'paused');let paused=true;
|
|
Object.defineProperty(Player.master,'paused',{configurable:true,get:()=>paused});
|
|
const states=[];
|
|
for(const state of [true,false,true]){paused=state;updatePlayBtn();states.push(['playBtn','stgPlay','miniPlayBtn','servicePlayBtn'].map(id=>inspect(document.getElementById(id))));}
|
|
// Exercise the real fullscreen HUD and Car mode's own refresh callbacks.
|
|
const stage=document.querySelector('.player-stage'); const request=stage.requestFullscreen;stage.requestFullscreen=undefined;
|
|
await playerFullscreen.enter();paused=false;await new Promise(r=>setTimeout(r,300));
|
|
const hudPlaying=inspect(document.querySelector('.fullscreen-controls button'));
|
|
paused=true;await new Promise(r=>setTimeout(r,300));const hudPaused=inspect(document.querySelector('.fullscreen-controls button'));playerFullscreen.leave();stage.requestFullscreen=request;
|
|
CarMode.configure({input:els.searchInput,search(){},toast(){},state:()=>({paused,title:'Transport fixture'}),action(){}});CarMode.open();
|
|
const car=[...document.querySelectorAll('.car-controls button')].map(inspect);
|
|
paused=false;await new Promise(r=>setTimeout(r,550));const carPlaying=inspect(document.querySelectorAll('.car-controls button')[1]);CarMode.close();
|
|
if(old)Object.defineProperty(Player.master,'paused',old);else delete Player.master.paused;updatePlayBtn();
|
|
// A preview strip uses the very same symbols to make all actions visible together.
|
|
const strip=document.createElement('section');strip.id='transportPreview';strip.style.cssText='position:fixed;left:8px;right:8px;top:120px;z-index:30000;padding:20px;background:var(--bg-1);color:var(--text);border:1px solid var(--line);border-radius:20px;display:flex;flex-wrap:wrap;gap:12px';
|
|
for(const kind of ['previous','back10','play','pause','forward10','next']){const b=document.createElement('button');b.className=kind==='play'||kind==='pause'?'ctrl play':'ctrl';b.title=kind;if(kind==='play'||kind==='pause'){const s=getComputedStyle(document.getElementById('playBtn'));for(const prop of ['width','height','borderRadius','background','color','border','boxShadow','backdropFilter'])b.style[prop]=s[prop];}b.innerHTML=TransportIcons.svg(kind);strip.append(b);}document.body.append(strip);
|
|
return {bad,icons,previousX,shapes,states,hudPlaying,hudPaused,car,carPlaying};
|
|
});
|
|
const label=`${engine}/${layout}/${theme}`;
|
|
assert.deepEqual(result.bad,[],label+': legacy transport glyph');
|
|
assert.deepEqual(result.shapes,[`transport-shape-${layout}`],label+': wrong drawing');
|
|
if(result.icons.find(n=>n.id==='prevBtn').visible)assert.equal(result.previousX,layout==='f7-swipe'?4:5,label+': rendered geometry must switch');
|
|
for(const icon of [...result.icons,...result.car,result.hudPlaying,result.hudPaused,result.carPlaying]){
|
|
assert(icon.kind&&!icon.legacy&&icon.href===`#transport-${icon.kind}${layout==='classic'?'':'-'+layout}`,label+': nonshared icon');
|
|
assert(icon.label,label+': missing accessible name');
|
|
assert.equal(icon.stroke,icon.color,label+': nonmonochrome stroke');
|
|
if(layout==='f7-swipe'&&['play','pause','previous','next','forward'].includes(icon.kind))assert.equal(icon.fill,icon.color,label+': F7 glyph must be filled '+JSON.stringify(icon));assert.equal(icon.color,icon.parentColor,label+': detached control colour');
|
|
if(icon.visible)assert(icon.width>=44&&icon.height>=44,`${label}/${icon.id||icon.kind}: target ${icon.width}x${icon.height}`);
|
|
}
|
|
for(const [i,icons] of result.states.entries())for(const icon of icons){assert.equal(icon.kind,i===1?'pause':'play');assert.equal(icon.label,i===1?'Pause':'Play');}
|
|
assert.equal(result.hudPlaying.kind,'pause');assert.equal(result.hudPaused.kind,'play');assert.equal(result.carPlaying.kind,'pause');
|
|
await page.locator('#transportPreview').screenshot({path:resolve(shots,`${engine}-${layout}-${theme}-symbols.png`)});
|
|
await page.evaluate(()=>document.getElementById('transportPreview').remove());
|
|
return result;
|
|
}
|
|
|
|
export async function remoteTransportFixture(browser,origin,engine) {
|
|
const context=await contextFor(browser,origin);
|
|
await context.route('**/api/remote/pair',route=>route.fulfill({json:{ok:true,roomId:'fixture',remoteId:'phone',token:'fixture',hostName:'Transport fixture',name:'Phone'}}));
|
|
await context.routeWebSocket('**/ws/remote**',ws=>{setTimeout(()=>ws.send(JSON.stringify({type:'hello',hostName:'Transport fixture',state:{paused:true,cur:0,dur:240,volume:1,v:{id:'iconcheck00',title:'Transport fixture',channel:'Fixture'}}})),100);});
|
|
const page=await context.newPage();const errors=[];page.on('pageerror',error=>errors.push(error.message));
|
|
await page.goto(origin+'/?pair=123456');await page.waitForSelector('#rvControl:not(.hidden)');
|
|
await page.waitForFunction(()=>document.getElementById('rvTitle').textContent==='Transport fixture');
|
|
return {
|
|
async check(layout,theme){
|
|
await appearance(page,layout,theme);
|
|
const states=[];
|
|
for(let i=0;i<3;i++){
|
|
states.push(await page.evaluate(()=>['rvPrev','rvToggle','rvNext'].map(id=>{const b=document.getElementById(id),svg=b.querySelector('.transport-icon'),r=b.getBoundingClientRect();return {kind:svg.dataset.transportIcon,href:[...svg.querySelectorAll('use')].find(n=>getComputedStyle(n).display!=='none').getAttribute('href'),label:b.getAttribute('aria-label'),width:r.width,height:r.height};})));
|
|
if(i<2)await page.locator('#rvToggle').click();
|
|
}
|
|
for(const [i,icons] of states.entries()){
|
|
assert.equal(icons[1].kind,i===1?'pause':'play');assert.equal(icons[1].label,i===1?'Pause':'Play');
|
|
for(const icon of icons){assert.equal(icon.href,'#transport-'+icon.kind+(layout==='classic'?'':'-'+layout));assert(icon.width>=44&&icon.height>=44);}
|
|
}
|
|
await page.locator('.rv-transport').screenshot({path:resolve('plans/transport-shots',`${engine}-${layout}-${theme}-remote.png`)});
|
|
assert.deepEqual(errors,[],engine+': remote script errors');
|
|
return states;
|
|
},close:()=>context.close()
|
|
};
|
|
}
|