Files
juzi/h5/test/wrinkle-three-views.mjs
T

54 lines
5.3 KiB
JavaScript

// Deterministic API payloads; no network writes or upstream detection.
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 b=await chromium.launch();
try{
const p=await b.newPage({viewport:{width:390,height:1100},isMobile:true,hasTouch:true});const errors=[];p.on('pageerror',e=>errors.push(e.message));
await p.route('**/api/**',r=>{assert.equal(r.request().method(),'GET');return r.fulfill({json:{ok:true,user:{kind:'browser'}}});});
const source=fs.readFileSync(new URL('../js/app.js',import.meta.url),'utf8');
await p.route('**/js/app.js*',r=>r.fulfill({contentType:'text/javascript',body:source.replace(/\}\)\(\);\s*$/,`
window.__wrinkleTest=function(x){var fx=MOCK_FIXTURE,raw=JSON.parse(JSON.stringify(fx.result));
if(x.noFrontMap)delete raw.face_maps.texture_enhanced_lines;
var vm=SkinScore.buildViewModel(raw,fx.side,{});
renderReport(vm,fx.photos[0],null,{result:raw,side:fx.side,wrinkle_views:x.views,face_rectangle:fx.face_rectangle},[fx.photos[0],x.left,x.right]);
showScreen('s-report');setDetailTab(document.querySelector('.card-detail'),'1',true);
};})();`)}));
await p.goto((process.env.H5_BASE_URL||'http://127.0.0.1:8080/')+'?mock=1&screen=report');
const svg=body=>'data:image/svg+xml,'+encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="320" height="240">'+body+'</svg>');
const left=svg('<rect width="320" height="240" fill="#e6bba2"/>'),right=svg('<rect width="320" height="240" fill="#dcb196"/>');
const map=color=>svg('<path d="M0 80 L320 80 M0 140 L320 140" stroke="'+color+'" stroke-width="8"/>');
const entry=color=>({status:'ready',result:{face_maps:{texture_enhanced_lines:map(color)}}});
for(const width of[320,375,414,480]){
await p.setViewportSize({width,height:1100});
await p.evaluate(x=>window.__wrinkleTest(x),{left,right,views:{left:entry('red'),right:entry('blue')}});
await p.waitForFunction(()=>['wrinkle-left','wrinkle-right'].every(id=>{const i=document.getElementById(id);return i.naturalWidth>0&&!i.closest('figure').hidden&&i.dataset.hasAnnotation==='true';}));
assert(await p.locator('#wrinkle-layer-note').evaluate(e=>e.hidden));
assert(await p.locator('#ditem-img2').evaluate(e=>e.dataset.hasAnnotation==='true'));
const layout=await p.evaluate(()=>{
const styles=e=>{const s=getComputedStyle(e);return{width:s.width,height:s.height,marginBottom:s.marginBottom,transform:s.transform,objectFit:s.objectFit,objectPosition:s.objectPosition};};
return{contour:styles(document.querySelector('#ditem-img1').parentElement),wrinkle:styles(document.querySelector('#ditem-img2').parentElement),frontA:styles(document.querySelector('#ditem-img1')),frontB:styles(document.querySelector('#ditem-img2')),sideA:styles(document.querySelector('#contour-left')),sideB:styles(document.querySelector('#wrinkle-left')),offsetA:document.querySelector('#ditem-img1').dataset.contentOffset,offsetB:document.querySelector('#ditem-img2').dataset.contentOffset};
});
assert.deepEqual(layout.contour,layout.wrinkle,'same front frame and spacing');
assert.deepEqual(layout.frontA,layout.frontB,'same front image size');
assert.deepEqual(layout.sideA,layout.sideB,'same side image scaling and positioning');
assert.equal(layout.offsetA,layout.offsetB,'same face crop offset; overlay moves with photo');
const leftSrc=await p.locator('#wrinkle-left').getAttribute('src'),rightSrc=await p.locator('#wrinkle-right').getAttribute('src');
assert.notEqual(leftSrc,left);assert.notEqual(rightSrc,right);assert.notEqual(leftSrc,rightSrc);
await p.getByRole('button',{name:'查看左脸皱纹检测大图'}).click();assert.notEqual(await p.locator('#zoom-img').getAttribute('src'),leftSrc);await p.locator('#p-zoom').click();
await p.getByRole('button',{name:'查看右脸皱纹检测大图'}).click();assert.notEqual(await p.locator('#zoom-img').getAttribute('src'),rightSrc);await p.locator('#p-zoom').click();
}
await p.evaluate(x=>window.__wrinkleTest(x),{left,right,views:{}});
await p.waitForFunction(()=>!document.querySelector('#wrinkle-left').closest('figure').hidden&&!document.querySelector('#wrinkle-right').closest('figure').hidden);
assert.equal(await p.locator('#wrinkle-left').getAttribute('src'),left,'no front-face map projected onto unannotated side');
assert(await p.locator('#wrinkle-layer-note').isVisible());
const marks={left_cheek_wrinkle_outline:[{x:10,y:10},{x:200,y:10},{x:200,y:80}]};
await p.evaluate(x=>window.__wrinkleTest(x),{left,right:null,noFrontMap:true,views:{left:{status:'ready',result:{wrinkle_mark:marks}}}});
await p.waitForFunction(()=>document.querySelector('#wrinkle-left').dataset.hasAnnotation==='true'&&document.querySelector('#ditem-img2').dataset.hasAnnotation==='true');
assert(await p.locator('#wrinkle-right').evaluate(e=>e.closest('figure').hidden&&!e.hasAttribute('src')));
await p.evaluate(x=>window.__wrinkleTest(x),{left:null,right:null,views:{}});
assert(await p.locator('#wrinkle-sides').evaluate(e=>e.hidden));assert(await p.locator('#wrinkle-layer-note').evaluate(e=>e.hidden));
assert.deepEqual(errors,[]);
console.log('PASS wrinkle three views: 4 widths, own overlays, annotated zoom, front overlay preserved, missing layer/photo, real-coordinate fallback, no false projection and stale clearing');
}finally{await b.close();}