80 lines
4.5 KiB
JavaScript
80 lines
4.5 KiB
JavaScript
// Public-site error recovery and real model loading. Analysis requests are intercepted;
|
|
// no paid upstream call or report is created. A separate empty browser identity is used.
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import { execSync } from 'node:child_process';
|
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
const { chromium } = await import(pathToFileURL(execSync('npm root -g', { encoding: 'utf8' }).trim() + '/playwright/index.mjs').href);
|
|
const base = process.env.H5_BASE_URL || 'http://127.0.0.1:8080/';
|
|
const samplePath = fileURLToPath(new URL('../../reference/facepp-skinanalyze/examples/images/C3_180.jpg', import.meta.url));
|
|
const browser = await chromium.launch({ headless: true });
|
|
const page = await browser.newPage({ viewport: { width: 375, height: 812 } });
|
|
page.setDefaultTimeout(60000);
|
|
const errors = [];
|
|
page.on('pageerror', e => errors.push(e.message));
|
|
await page.addInitScript(() => {
|
|
navigator.mediaDevices.getUserMedia = () => Promise.reject(new DOMException('test denial', 'NotAllowedError'));
|
|
const original = window.setTimeout;
|
|
window.setTimeout = function (fn, delay, ...args) {
|
|
// Accelerate only the actual analyze deadline for the timeout branch.
|
|
return original(fn, window.__fastAnalyzeTimeout && delay === 150000 ? 150 : delay, ...args);
|
|
};
|
|
});
|
|
try {
|
|
await page.goto(base, { waitUntil: 'domcontentloaded' });
|
|
// Real models from production paths, actual landmark inference on the sample image.
|
|
const detected = await page.evaluate(async data => {
|
|
await Promise.all([
|
|
faceapi.nets.tinyFaceDetector.loadFromUri('vendor/faceapi/models'),
|
|
faceapi.nets.faceLandmark68Net.loadFromUri('vendor/faceapi/models')
|
|
]);
|
|
const img = await faceapi.fetchImage(data);
|
|
const d = await faceapi.detectSingleFace(img, new faceapi.TinyFaceDetectorOptions({ inputSize: 416, scoreThreshold: 0.3 })).withFaceLandmarks();
|
|
return d ? { score: d.detection.score, landmarks: d.landmarks.positions.length } : null;
|
|
}, 'data:image/jpeg;base64,' + fs.readFileSync(samplePath).toString('base64'));
|
|
assert(detected && detected.landmarks === 68 && detected.score > 0.3);
|
|
console.log('PASS: real production models load and detect sample face with 68 landmarks');
|
|
// Keep subsequent error-flow uploads independent of angle validation.
|
|
await page.evaluate(() => { window.faceapi = undefined; });
|
|
await page.waitForFunction(() => !document.querySelector('#login-message').textContent.includes('检查'));
|
|
await page.check('#login-consent');
|
|
await page.click('[data-action="login-browser"]');
|
|
await page.waitForSelector('#s-splash.active');
|
|
await page.click('[data-action="open-privacy"]');
|
|
await page.click('#privacy-check');
|
|
await page.click('[data-action="agree-continue"]');
|
|
for (const selector of ['#pick-birth', '#pick-gender', '#pick-region']) {
|
|
await page.click(selector);
|
|
await page.click('.ms-mobile-select.ms-show .ms-ensure');
|
|
await page.waitForTimeout(400);
|
|
}
|
|
const cases = [
|
|
['business', '未检测到人脸', route => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ ok: false, message: '未检测到人脸' }) })],
|
|
['network', '网络异常', route => route.abort('failed')],
|
|
['timeout', '检测服务开小差', async route => { await new Promise(resolve => setTimeout(resolve, 500)); await route.abort().catch(() => {}); }]
|
|
];
|
|
for (const [name, message, handler] of cases) {
|
|
await page.route('**/api/skin-analyze', handler);
|
|
await page.evaluate(fast => { window.__fastAnalyzeTimeout = fast; }, name === 'timeout');
|
|
await page.click('[data-action="submit-profile"]');
|
|
await page.click('[data-action="start-camera"]');
|
|
for (let i = 1; i <= 3; i++) {
|
|
await page.waitForFunction(n => document.querySelector('#cam-step').textContent.includes(n + '/3'), i);
|
|
await page.waitForTimeout(1600);
|
|
await page.setInputFiles('#cam-file', samplePath);
|
|
}
|
|
await page.waitForSelector('#s-profile.active');
|
|
assert((await page.locator('#toast').textContent()).includes(message), name);
|
|
assert.equal(await page.locator('#p-analyzing.show').count(), 0);
|
|
assert(await page.locator('#f-birth').inputValue());
|
|
await page.unroute('**/api/skin-analyze', handler);
|
|
console.log('PASS: ' + name + ' error → dismiss loading → retain profile → allow retry');
|
|
}
|
|
const reports = await (await page.context().request.get(base + 'api/reports')).json();
|
|
assert.equal(reports.total, 0);
|
|
assert.deepEqual(errors, []);
|
|
console.log('PASS: no spurious reports and no JS errors');
|
|
} finally {
|
|
await browser.close();
|
|
}
|