63 lines
5.0 KiB
JavaScript
63 lines
5.0 KiB
JavaScript
// Run: PLAYWRIGHT_MODULE=/path/to/playwright node tests/compare-browser.cjs
|
|
// Isolated, read-only API harness. Never writes to ComfyUI or library state.
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const http = require('node:http');
|
|
const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright');
|
|
const root = path.resolve(__dirname, '..');
|
|
const scratch = process.env.COMPARE_QA_DIR || path.join(process.env.LOCALAPPDATA || root, 'hermes/cache/scratch/image-browser-compare');
|
|
fs.mkdirSync(scratch, { recursive: true });
|
|
const images = ['A.png', 'B.png', 'Portrait.png'].map(name => ({name, path: name, folder_id: 'default', is_favorite: false}));
|
|
const fixture = name => {
|
|
const [w,h] = name === 'Portrait.png' ? [480,800] : name === 'B.png' ? [1280,960] : [640,480];
|
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 640 480"><rect width="640" height="480" fill="#283b52"/><path d="M0 0L640 480M0 480L640 0" stroke="#eeeeee" stroke-width="8"/><rect x="240" y="180" width="160" height="120" fill="${name === 'B.png' ? '#d95757' : '#45ac90'}"/></svg>`;
|
|
};
|
|
const server = http.createServer((req,res) => {
|
|
const url = new URL(req.url, 'http://localhost');
|
|
if(req.method !== 'GET') { res.writeHead(405); res.end('Read only'); return; }
|
|
if(url.pathname === '/gemini-image-browser/ui') { res.setHeader('Content-Type','text/html'); res.end(fs.readFileSync(path.join(root,'ui.html'))); }
|
|
else if(url.pathname === '/gemini-image-browser/js') { res.setHeader('Content-Type','text/javascript'); res.end(fs.readFileSync(path.join(root,'browser.js'))); }
|
|
else if(url.pathname === '/gemini-image-browser/list') { res.setHeader('Content-Type','application/json'); res.end(JSON.stringify({images, has_more:false,total:images.length, folders:[{id:'default',name:'Fixtures',path:'read-only fixtures'}]})); }
|
|
else if(url.pathname === '/gemini-image-browser/metadata') { res.setHeader('Content-Type','application/json');res.end('{}'); }
|
|
else if(url.pathname === '/gemini-image-browser/thumbnail') { res.setHeader('Content-Type','image/svg+xml');res.end(fixture(url.searchParams.get('filename'))); }
|
|
else { res.writeHead(404);res.end(); }
|
|
});
|
|
(async () => {
|
|
await new Promise(resolve => server.listen(0,'127.0.0.1',resolve));
|
|
const browser = await chromium.launch({headless:true, ...(process.env.COMPARE_BROWSER ? {executablePath:process.env.COMPARE_BROWSER} : {})});
|
|
try {
|
|
const page = await browser.newPage({viewport:{width:1280,height:900}});
|
|
const errors = [];page.on('pageerror', error => errors.push(error.message));
|
|
await page.goto(`http://127.0.0.1:${server.address().port}/gemini-image-browser/ui`);
|
|
await page.locator('.image-card img').first().waitFor();
|
|
await page.locator('#compareModeBtn').click();
|
|
await page.locator('.image-card img').nth(0).click();
|
|
await page.locator('.image-card img').nth(1).click();
|
|
await page.locator('#compareBtn').click();
|
|
assert.equal(await page.locator('[data-compare-view]').count(),4,'compare exposes Wipe, Side by side, Difference and Overlay controls');
|
|
console.log('PASS comparison exposes four visible modes');
|
|
await page.locator('[data-compare-view="side-by-side"]').click();
|
|
const stage = page.locator('#compareImageContainer');
|
|
await page.evaluate(() => document.getElementById('compareImageContainer').addEventListener('wheel', event => { window.observedWheel = {x:event.clientX,y:event.clientY}; }));
|
|
const box = await stage.boundingBox();
|
|
await page.mouse.move(box.x + box.width * .17, box.y + box.height * .35);
|
|
await page.mouse.wheel(0,-120);
|
|
await page.waitForFunction(() => document.getElementById('compareZoomLevel').textContent === '110%');
|
|
const anchored = await page.evaluate(() => {
|
|
const stage = document.getElementById('compareImageContainer');
|
|
const rect = stage.getBoundingClientRect();
|
|
const width = stage.clientWidth / 2;
|
|
const x = (window.observedWheel.x - rect.left) * stage.offsetWidth / rect.width - stage.clientLeft;
|
|
const y = (window.observedWheel.y - rect.top) * stage.offsetHeight / rect.height - stage.clientTop;
|
|
const matrix = new DOMMatrix(document.querySelector('.compare-img-front').style.transform);
|
|
return {actualX:matrix.e,actualY:matrix.f,expectedX:-(x-width/2)*.1,expectedY:-(y-stage.clientHeight/2)*.1};
|
|
});
|
|
assert.ok(Math.abs(anchored.actualX-anchored.expectedX)<.001 && Math.abs(anchored.actualY-anchored.expectedY)<.001, `wheel keeps corresponding image point under pointer: ${JSON.stringify(anchored)}`);
|
|
console.log('PASS pointer-anchored side-by-side zoom');
|
|
const additional = await require('./compare-acceptance.cjs')(page,browser,scratch);
|
|
assert.deepEqual(errors,[],'no page errors');
|
|
console.log(`RESULT ${additional+2} acceptance checks passed; 0 page errors; screenshots: ${scratch}`);
|
|
} finally {await browser.close();server.close();}
|
|
})().catch(error => {console.error(error);server.close();process.exitCode=1;});
|