Files
juzi/h5/test/browser-failures.mjs

82 lines
4.7 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' });
await page.addScriptTag({ url: base + 'vendor/faceapi/face-api.js' });
// 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.route('**/vendor/faceapi/face-api.js', route => route.abort());
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="guest-experience"]');
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();
}