54 lines
5.7 KiB
JavaScript
54 lines
5.7 KiB
JavaScript
// Explicitly supplied private case directory; never check real photos/raw into Git.
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';import os from 'node:os';import path from 'node:path';
|
|
import {spawn,execSync} from 'node:child_process';import{once}from'node:events';
|
|
import{fileURLToPath,pathToFileURL}from'node:url';import{createRequire}from'node:module';
|
|
const require=createRequire(import.meta.url),SS=require('../js/skin-score'),UI=require('../js/report-ui');
|
|
const input=process.env.H5_WRINKLE_CASE_DIR;
|
|
assert(input,'set H5_WRINKLE_CASE_DIR to a private, explicitly downloaded case directory');
|
|
const raw=JSON.parse(fs.readFileSync(path.join(input,'raw.json')));
|
|
const profile={real_age:26,daily_skin_type:'干性肌肤'};
|
|
const projected=SS.buildViewModel(raw.result,{left:raw.left_side_result,right:raw.right_side_result},{realAge:26,skinType:profile.daily_skin_type});
|
|
assert.equal(raw.result.score_info.wrinkle_score,65);
|
|
assert.equal(projected.cats.wrinkles.P,65);assert.equal(projected.cats.wrinkles.S,8.95);
|
|
assert.equal(projected.dims.firm.S,8.95);assert.equal(projected.total,94);
|
|
assert.equal(projected.dims.vita.S,9.63);assert.equal(projected.dims.stab.S,9.91);
|
|
assert.equal(projected.cats.acne.vars.acne_counts.acne,1);
|
|
assert.equal(projected.cats.acne.vars.acne_counts.closed_comedones,1);
|
|
assert.equal(projected.cats.acne.vars.acne_scoring.deduction,3);
|
|
const root=fs.mkdtempSync(path.join(os.tmpdir(),'juzi-wrinkle-replay-'));
|
|
const user=require('./wechat-fixture.cjs')(path.join(root,'accounts'))[0];
|
|
const{chromium}=await import(pathToFileURL(execSync('npm root -g',{encoding:'utf8'}).trim()+'/playwright/index.mjs').href);
|
|
const child=spawn(process.execPath,['-e',"const fs=require('fs');const raw=JSON.parse(fs.readFileSync(process.env.H5_WRINKLE_CASE_DIR+'/raw.json'));global.fetch=async()=>({ok:true,status:200,json:async()=>raw});require('./server.js')"],{
|
|
cwd:fileURLToPath(new URL('../',import.meta.url)),env:{...process.env,PORT:'18118',HOST:'127.0.0.1',JUZI_DATA_ROOT:root,SKIN_API_KEY:'fixture',SKIN_API_SECRET:'fixture',PUBLIC_BASE_URL:'',WECHAT_APP_ID:'',WECHAT_APP_SECRET:''},stdio:['ignore','pipe','pipe']});
|
|
let browser;
|
|
try{
|
|
await new Promise((resolve,reject)=>{const timer=setTimeout(()=>reject(Error('startup timeout')),10000);child.stdout.on('data',b=>{if(b.toString().includes('已启动')){clearTimeout(timer);resolve()}});child.once('exit',c=>{clearTimeout(timer);reject(Error('exited '+c))})});
|
|
const base='http://127.0.0.1:18118/';browser=await chromium.launch();const context=await browser.newContext({viewport:{width:375,height:726}});
|
|
await context.addCookies([{name:'juzi_session',value:user.token,url:base}]);
|
|
const images=Object.fromEntries(['front','left','right'].map(k=>[k+'_image','data:image/jpeg;base64,'+fs.readFileSync(path.join(input,k+'.jpg')).toString('base64')]));
|
|
const response=await context.request.post(base+'api/skin-analyze',{data:{...images,profile}});assert.equal(response.status(),200);const result=await response.json();
|
|
assert.equal(result.ok,true);assert.equal(result.vm.total,94);assert.equal(result.vm.dims.firm.S,8.95);
|
|
const admin=await(await context.request.get(base+'api/admin/records/'+result.record_id)).json();
|
|
assert.equal(admin.record.total_score,94);assert.equal(admin.record.cats.wrinkles.P,65);assert.equal(admin.record.dims.firm.S,8.95);assert.equal(admin.record.wrinkle_score_rule,'api-overall-v1');
|
|
assert.equal(admin.record.acne_scoring.types.find(t=>t.key==='acne').count,1);
|
|
const errors=[];const page=await context.newPage();page.on('pageerror',e=>errors.push(e.message));
|
|
await page.goto(base);await page.click('[data-action="history"]');await page.click('[data-report-id]');await page.waitForSelector('#s-report.active');
|
|
assert.equal(await page.locator('#stat-score b').textContent(),'94');assert.equal(await page.locator('#dim-firm').textContent(),'9.0');
|
|
await page.waitForTimeout(500);await page.screenshot({path:path.join(input,'report-overall-wrinkles.png')});
|
|
await page.locator('#viz-tone').scrollIntoViewIfNeeded();
|
|
await page.waitForFunction(()=>{const i=document.querySelector('#viz-tone');return !i.hidden&&i.naturalWidth&&i.dataset.overlayAlpha==='0.3'});
|
|
assert(!(await page.locator('#tx-tone').textContent()).includes('占肤色项20%'));
|
|
assert.equal(await page.locator('#tone-detections').count(),0,'detection status overlay removed');
|
|
await page.locator('#viz-tone').locator('xpath=ancestor::div[contains(@class,"card-detail")][1]').screenshot({path:path.join(input,'tone-color-30.png')});
|
|
const desktop=await context.newPage({viewport:{width:1600,height:1000}});await desktop.setViewportSize({width:1600,height:1000});desktop.on('pageerror',e=>errors.push(e.message));
|
|
await desktop.goto(base+'admin.html');const row=desktop.locator('#tbl-records tbody tr').first();assert.equal(await row.locator('td').nth(6).textContent(),'94');
|
|
await row.click();await desktop.waitForFunction(()=>document.querySelector('#d-body').textContent.includes('综合得分'));
|
|
assert((await desktop.locator('#d-body').textContent()).includes('痘痘三视图计分明细'));
|
|
await desktop.screenshot({path:path.join(input,'admin-overall-wrinkles.png')});assert.deepEqual(errors,[]);
|
|
const auditSection=desktop.locator('.d-sec').filter({hasText:'痘痘三视图计分明细'});
|
|
await auditSection.scrollIntoViewIfNeeded();
|
|
await auditSection.screenshot({path:path.join(input,'admin-acne-three-view.png')});
|
|
console.log(JSON.stringify({case:'R20261014-171215-CYLF',interfaceWrinkles:65,wrinkles:8.95,firm:8.95,display:UI.dimensionScore(8.95),vita:9.63,stab:9.91,papules:1,closedComedones:1,countDeduction:3,total:94,tests:'PASS local replay only; no paid call/production writes'}));
|
|
}finally{if(browser)await browser.close();child.kill('SIGTERM');if(child.exitCode===null)await once(child,'exit');fs.rmSync(root,{recursive:true,force:true})}
|