89 lines
6.5 KiB
JavaScript
89 lines
6.5 KiB
JavaScript
// Browser-only fixtures, no writes/paid detection. Full photo opens in the
|
|
// existing zoom overlay while thumbnail cropping is CSS-only.
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import {execSync} from 'node:child_process';
|
|
import {pathToFileURL} from 'node:url';
|
|
const {chromium}=await import(pathToFileURL(execSync('npm root -g',{encoding:'utf8'}).trim()+'/playwright/index.mjs').href);
|
|
const browser=await chromium.launch();
|
|
try {
|
|
const page=await browser.newPage({viewport:{width:375,height:1000},isMobile:true,hasTouch:true});
|
|
const errors=[];page.on('pageerror',e=>errors.push(e.message));
|
|
const source=fs.readFileSync(new URL('../js/app.js',import.meta.url),'utf8');
|
|
await page.route('**/api/**',r=>r.fulfill({json:{ok:true,user:{kind:'browser'}}}));
|
|
await page.route('**/js/app.js*',r=>r.fulfill({contentType:'text/javascript',body:source.replace(/\}\)\(\);\s*$/,`
|
|
window.__sideTest=function(ph){
|
|
var fx=window.MOCK_FIXTURE,vm=SkinScore.buildViewModel(fx.result,fx.side,{});
|
|
renderReport(vm,fx.photos[0],null,{result:fx.result},ph);showScreen('s-report');
|
|
};})();`)}));
|
|
await page.goto((process.env.H5_BASE_URL||'http://127.0.0.1:8080/')+'?mock=1&screen=report');
|
|
const photo=color=>'data:image/svg+xml,'+encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="320" height="240"><rect width="320" height="240" fill="'+color+'"/></svg>');
|
|
const left=photo('red'),right=photo('blue');
|
|
for(const width of [320,375,414,480]) {
|
|
await page.setViewportSize({width,height:1000});
|
|
await page.evaluate(ph=>window.__sideTest(ph),[null,left,right]);
|
|
await page.waitForFunction(()=>[...document.querySelectorAll('#contour-sides figure')].every(f=>!f.hidden&&f.querySelector('img').naturalWidth===320));
|
|
const figures=page.locator('#contour-sides figure');
|
|
assert.equal(await figures.count(),2);
|
|
assert.equal((await page.locator('#contour-sides').textContent()).trim(),'','no visible left/right captions');
|
|
assert.equal(await page.locator('#contour-left').getAttribute('src'),left);
|
|
assert.equal(await page.locator('#contour-right').getAttribute('src'),right);
|
|
await page.waitForFunction(()=>document.querySelector('#ditem-img1').naturalWidth>0);
|
|
const crop=await page.locator('#ditem-img1').evaluate(img=>{
|
|
const i=img.getBoundingClientRect(),f=img.parentElement.getBoundingClientRect();
|
|
const other=document.querySelector('#ditem-img2').parentElement.getBoundingClientRect();
|
|
return{ratio:f.height/i.height,top:i.top-f.top,width:i.width-f.width,other:other.height/i.height,overflow:getComputedStyle(img.parentElement).overflow};
|
|
});
|
|
assert(Math.abs(crop.ratio-.9)<.002);assert(Math.abs(crop.top)<1);assert(Math.abs(crop.width)<1);
|
|
assert(Math.abs(crop.other-.9)<.002,'wrinkle front frame matches contour crop');assert.equal(crop.overflow,'hidden');
|
|
const offset=await page.locator('#ditem-img1').getAttribute('data-content-offset');
|
|
assert(offset!=null&&Number(offset)>=0&&Number(offset)<=.090001);
|
|
const fullFront=await page.locator('#ditem-img1').getAttribute('src');
|
|
await page.locator('#ditem-img1').click();
|
|
assert.equal(await page.locator('#zoom-img').getAttribute('src'),fullFront,'front zoom still uses uncropped source');
|
|
await page.locator('#p-zoom').click();
|
|
const geo=await page.locator('.contour-side-photo').first().evaluate(e=>{const r=e.getBoundingClientRect();return{w:r.width,h:r.height,fit:getComputedStyle(e.querySelector('img')).objectFit};});
|
|
assert(Math.abs(geo.h/geo.w-92.25/104.35)<.002);assert.equal(geo.fit,'cover');
|
|
for(const img of await page.locator('.contour-side-photo img').all()) {
|
|
const fill=await img.evaluate(e=>{
|
|
const m=new DOMMatrix(getComputedStyle(e).transform),r=e.getBoundingClientRect(),b=e.parentElement.getBoundingClientRect();
|
|
return{sx:m.a,sy:m.d,shift:m.f/b.height,top:r.top-b.top,bottom:r.bottom-b.bottom};
|
|
});
|
|
assert(Math.abs(fill.sx-2.8)<.001);assert(Math.abs(fill.sy-2.8)<.001);assert(Math.abs(fill.shift-(-.25))<.001);
|
|
assert(fill.top<=.01&&fill.bottom>0,'zoomed fill covers frame without blank top edge');
|
|
}
|
|
const spacing=await page.locator('#contour-sides').evaluate(e=>{
|
|
const frame=document.querySelector('.contour-front-frame'),f=frame.getBoundingClientRect(),s=e.getBoundingClientRect();
|
|
return{u:f.width/215,horizontal:parseFloat(getComputedStyle(e).columnGap),vertical:s.top-f.bottom};
|
|
});
|
|
assert(Math.abs(spacing.horizontal/spacing.u-6.3)<.1);
|
|
assert(Math.abs(spacing.vertical/spacing.u-7.56)<.1);
|
|
await page.getByRole('button',{name:'查看左脸完整大图'}).click();
|
|
assert(await page.locator('#p-zoom').evaluate(e=>e.classList.contains('show')));
|
|
assert.equal(await page.locator('#zoom-img').getAttribute('src'),left);
|
|
assert.equal(await page.locator('#zoom-img').evaluate(e=>e.naturalWidth/e.naturalHeight),4/3);
|
|
await page.locator('#p-zoom').click();
|
|
await page.getByRole('button',{name:'查看右脸完整大图'}).click();
|
|
assert.equal(await page.locator('#zoom-img').getAttribute('src'),right);
|
|
await page.keyboard.press('Escape');
|
|
assert(!await page.locator('#p-zoom').evaluate(e=>e.classList.contains('show')));
|
|
await page.locator('.card-detail').first().locator('[data-tab="1"]').click();
|
|
await page.waitForTimeout(400);
|
|
assert(await page.locator('#contour-sides').evaluate(e=>{
|
|
const r=e.getBoundingClientRect(),v=e.closest('.detail-viewport').getBoundingClientRect();
|
|
return e.closest('.dpanel').getAttribute('aria-hidden')==='true'&&r.right<=v.left+1;
|
|
}),'contour thumbnails remain clipped outside the wrinkle tab');
|
|
}
|
|
await page.evaluate(ph=>window.__sideTest(ph),[null,left,null]);
|
|
await page.waitForFunction(()=>!document.querySelector('#contour-left').closest('figure').hidden);
|
|
assert(await page.locator('#contour-right').evaluate(e=>e.closest('figure').hidden&&!e.hasAttribute('src')));
|
|
await page.evaluate(()=>window.__sideTest([null,null,null]));
|
|
assert(await page.locator('#contour-sides').evaluate(e=>e.hidden));
|
|
assert.equal(await page.locator('#contour-sides img[src]').count(),0,'new report clears previous side photos');
|
|
await page.evaluate(()=>window.__sideTest([null,'data:image/jpeg;base64,broken',null]));
|
|
await page.waitForFunction(()=>!document.querySelector('#contour-left').hasAttribute('src'));
|
|
assert(await page.locator('#contour-sides').evaluate(e=>e.hidden));
|
|
assert.deepEqual(errors,[]);
|
|
console.log('PASS contour side photos: 4 widths, preserved thumbnail heights and 30% tighter gaps, left/right sources, full-aspect zoom, close/Escape, tab isolation, one/missing/broken photos and stale-photo cleanup');
|
|
} finally {await browser.close();}
|