← back to CelebritySignatures
5x/ink-gate.mjs
56 lines
// Supplementary E2E for the modal-gated SIGNATURE INK control, via CDP.
// Opens the /wear product modal, asserts the ink chips populate and are visible,
// clicks a preset and Auto, and collects any console/page errors. Exit 0 = pass.
import { spawn } from 'child_process';
const PORT = process.argv[2]; const DEBUG = 9337;
const CHROME = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
const proc = spawn(CHROME, ['--headless=new','--disable-gpu',`--remote-debugging-port=${DEBUG}`,`--user-data-dir=/tmp/inkgate-${process.pid}`], {stdio:'ignore'});
await new Promise(r=>setTimeout(r,2000));
const errors = [];
try {
const list = await (await fetch(`http://localhost:${DEBUG}/json/list`)).json();
const tgt = list.find(t=>t.type==='page');
const ws = new WebSocket(tgt.webSocketDebuggerUrl);
let id=0; const pending=new Map();
const send=(m,p={})=>new Promise(r=>{const i=++id;pending.set(i,r);ws.send(JSON.stringify({id:i,method:m,params:p}));});
await new Promise(r=>ws.onopen=r);
ws.onmessage=e=>{const m=JSON.parse(e.data);
if(m.method==='Runtime.consoleAPICalled'&&m.params.type==='error')errors.push('console:'+m.params.args.map(a=>a.value||a.description||'').join(' '));
if(m.method==='Runtime.exceptionThrown')errors.push('pageerror:'+(m.params.exceptionDetails?.exception?.description||m.params.exceptionDetails?.text||''));
if(m.id&&pending.has(m.id))pending.get(m.id)(m.result),pending.delete(m.id);};
await send('Page.enable'); await send('Runtime.enable');
await send('Page.navigate',{url:`http://localhost:${PORT}/wear`});
await new Promise(r=>setTimeout(r,4500));
const script=`(()=>{const first=document.querySelector('.card');if(first)first.click();
return new Promise(res=>setTimeout(()=>{
const ink=document.getElementById('inkChips');
const swatches=[...ink.querySelectorAll('[data-ink]')];
const burg=ink.querySelector('[data-ink="#6e1f2e"]'); if(burg)burg.click();
setTimeout(()=>{
const auto=ink.querySelector('[data-ink="auto"]');
const afterBurg=window.__wearInk; // not exposed; read selected class instead
const burgSel=ink.querySelector('[data-ink="#6e1f2e"]')?.classList.contains('sel');
if(auto)auto.click();
setTimeout(()=>{
const autoSel=ink.querySelector('[data-ink="auto"]')?.classList.contains('sel');
res(JSON.stringify({modalOpen:document.getElementById('scrim').classList.contains('on'),chips:swatches.length,inkVisible:ink.offsetParent!==null,burgSelectable:!!burg,burgSel,autoSel}));
},400);
},500);
},1200));})()`;
const r=await send('Runtime.evaluate',{expression:script,awaitPromise:true,returnByValue:true});
const st=JSON.parse(r.result.value);
ws.close();
const fails=[];
if(!st.modalOpen)fails.push('modal did not open on card click');
if(st.chips<9)fails.push(`only ${st.chips} ink chips (expected >=9)`);
if(!st.inkVisible)fails.push('ink chips not visible');
if(!st.burgSelectable)fails.push('burgundy preset missing');
if(!st.burgSel)fails.push('burgundy preset did not select');
if(!st.autoSel)fails.push('Auto did not re-select');
if(errors.length)fails.push('JS errors: '+errors.join(' | '));
console.log('INK-GATE state:',JSON.stringify(st));
if(fails.length){console.log('INK-GATE FAIL:',fails.join('; '));process.exitCode=1;}
else console.log('INK-GATE PASS');
} catch(e){ console.log('INK-GATE ERROR:',e.message); process.exitCode=2; }
finally { proc.kill(); }