56 lines
6.3 KiB
JavaScript
56 lines
6.3 KiB
JavaScript
// Real HTTP + report UI. All provider calls are delayed in-memory fixtures.
|
|
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 {createRequire} from 'node:module';import {fileURLToPath,pathToFileURL} from 'node:url';
|
|
const require=createRequire(import.meta.url),h5=fileURLToPath(new URL('../',import.meta.url));
|
|
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'juzi-wrinkle-async-'));
|
|
const owner=require('./wechat-fixture.cjs')(path.join(dir,'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'),vm=require('vm'),path=require('path'),w={};vm.runInNewContext(fs.readFileSync('assets/mock/report-fixture.js','utf8'),{window:w});const f=w.MOCK_FIXTURE;
|
|
global.fetch=async(url,o)=>{const side=o.body.get('return_maps')==='texture_enhanced_lines';
|
|
fs.appendFileSync(path.join(process.env.JUZI_DATA_ROOT,'provider-calls.jsonl'),JSON.stringify({side})+'\\n');
|
|
if(side)await new Promise(r=>setTimeout(r,1000));
|
|
return{ok:true,status:200,json:async()=>({result:f.result,face_rectangle:f.face_rectangle,left_side_result:f.side.left,right_side_result:f.side.right})};};require('./server.js');
|
|
`],{cwd:h5,env:{...process.env,HOST:'127.0.0.1',PORT:'18142',JUZI_DATA_ROOT:dir,NODE_ENV:'test',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 t=setTimeout(()=>reject(Error('startup timeout')),10000);child.stdout.on('data',b=>{if(b.toString().includes('已启动')){clearTimeout(t);resolve();}});child.once('exit',()=>{clearTimeout(t);reject(Error('server exit'));});});
|
|
const base='http://127.0.0.1:18142/';browser=await chromium.launch({headless:true});const page=await browser.newPage();const errors=[];page.on('pageerror',e=>errors.push(e.message));
|
|
await page.context().addCookies([{name:'juzi_session',value:owner.token,url:base}]);
|
|
const source=fs.readFileSync(path.join(h5,'js/app.js'),'utf8');
|
|
await page.route('**/js/app.js*',r=>r.fulfill({contentType:'text/javascript',body:source.replace(/\}\)\(\);\s*$/,`
|
|
window.__asyncScreen=showScreen;
|
|
window.__startAsyncReport=function(){beginCaptureJourney();['front','right','left'].forEach(trackCapture);photos=Array(3).fill(window.MOCK_FIXTURE.photos[0]);camSeq++;showScreen('s-cam');submitPhotos(camSeq);};})();`)}));
|
|
await page.addInitScript(()=>{const f=window.fetch;window.__annotationStarts=[];window.fetch=function(url,options){if(String(url).includes('api/wrinkle-views'))window.__annotationStarts.push({report:!!document.querySelector('#s-report.active'),scan:!!document.querySelector('#p-analyzing.show')});return f.call(this,url,options);};});
|
|
await page.goto(base);await page.waitForFunction(()=>AccountClient.user());await page.addScriptTag({url:base+'assets/mock/report-fixture.js'});
|
|
const primary=page.waitForResponse(r=>r.url().endsWith('/api/skin-analyze'));await page.evaluate(()=>window.__startAsyncReport());
|
|
const result=await(await primary).json();assert.equal(result.ok,true);assert.equal(result.wrinkle_views.left.status,'pending');
|
|
assert.equal(fs.readFileSync(path.join(dir,'provider-calls.jsonl'),'utf8').trim().split('\n').length,1,'main response cannot wait for side paid calls');
|
|
await page.waitForSelector('#s-report.active');const conclusion=await page.locator('#ditem-para2').textContent();
|
|
await page.waitForFunction(()=>document.querySelector('#wrinkle-layer-note').textContent.includes('加载中'));
|
|
await page.waitForFunction(()=>window.__annotationStarts.length===1);
|
|
await page.evaluate(()=>window.__asyncScreen('s-splash'));
|
|
const api=page.context().request;
|
|
const cached=await(await api.post(base+'api/wrinkle-views',{data:{token:result.wrinkle_job.token}})).json();assert.equal(cached.ok,true);
|
|
await page.waitForTimeout(100);
|
|
assert.equal(await page.locator('#s-splash.active').count(),1,'late annotation response cannot navigate');
|
|
assert.equal(await page.locator('#wrinkle-left').getAttribute('data-has-annotation'),'false','hidden report is not patched by stale response');
|
|
await page.evaluate(()=>window.__asyncScreen('s-report'));
|
|
await page.waitForFunction(()=>['left','right'].every(v=>document.querySelector('#wrinkle-'+v).dataset.hasAnnotation==='true'));
|
|
assert.equal(await page.locator('#ditem-para2').textContent(),conclusion);
|
|
assert.deepEqual(await page.evaluate(()=>window.__annotationStarts),[{report:true,scan:false}]);
|
|
const stored=await(await api.get(base+'api/reports/'+result.record_id)).json();assert.equal(stored.report.payload.wrinkle_job.status,'complete');assert.deepEqual(stored.report.vm,result.vm);assert.deepEqual(stored.report.presentation,result.presentation);
|
|
const other=await browser.newContext();await other.request.post(base+'api/auth/browser',{data:{consent:true}});assert.equal((await other.request.post(base+'api/wrinkle-views',{data:{token:result.wrinkle_job.token}})).status(),404);await other.close();
|
|
assert.equal(fs.readFileSync(path.join(dir,'provider-calls.jsonl'),'utf8').trim().split('\n').length,3,'main + exactly two side calls, cache/access denial never repeat');
|
|
assert.equal((await api.get(base+'data/wrinkle-jobs/'+result.wrinkle_job.token+'.json')).status(),403);
|
|
await page.route('**/api/wrinkle-views',r=>r.fulfill({status:503,json:{ok:false}}));
|
|
await page.evaluate(()=>window.__startAsyncReport());await page.waitForSelector('#s-report.active');
|
|
await page.waitForFunction(()=>document.querySelector('#wrinkle-layer-note').textContent.includes('部分侧脸未获取'));
|
|
assert.equal(await page.locator('#s-report.active').count(),1,'optional annotation failure keeps report accessible');
|
|
assert.equal(await page.locator('#ditem-para2').textContent(),conclusion);
|
|
assert.equal(fs.readFileSync(path.join(dir,'provider-calls.jsonl'),'utf8').trim().split('\n').length,4,'failed optional endpoint adds no provider retry');
|
|
assert.deepEqual(errors,[]);console.log('PASS: fast main report, side requests only after active report, pending/ready UI, unchanged conclusions/scores, owner isolation, snapshot cache, exactly 3 fixture calls');
|
|
} finally {if(browser)await browser.close();child.kill();}
|