feat(h5): allow guest access without login and retain history only for WeChat

This commit is contained in:
xsl
2026-10-13 15:23:23 +08:00
parent 98935dd168
commit 1fdbb575b0
29 changed files with 2561 additions and 1866 deletions
+20 -32
View File
@@ -35,22 +35,23 @@ try {
await page.goto(base, { waitUntil: 'domcontentloaded' });
await page.waitForFunction(() => !document.querySelector('#login-message').textContent.includes('检查'));
await page.check('#login-consent');
await page.click('[data-action="login-browser"]');
await page.click('[data-action="guest-experience"]');
await page.waitForSelector('#s-splash.active');
await page.click('[data-action="login-confirm"]');
const api = context.request;
assert.equal((await api.get(base + 'api/reports')).status(), 200);
const cookie = (await context.cookies()).find(c => c.httpOnly);
assert(cookie && cookie.secure && cookie.sameSite === 'Lax' && cookie.path === new URL(base).pathname);
assert.equal((await api.post(base + 'api/skin-analyze', { data: {} })).status(), 400);
assert.equal((await api.post(base + 'api/skin-analyze', { data: {}, headers: { Origin: 'https://invalid.example' } })).status(), 403);
console.log('PASS: public HTTPS, secure scoped cookie, missing-image validation, cross-origin denial');
assert.equal((await api.get(base + 'api/reports')).status(), 401);
await page.click('[data-action="open-privacy"]');
await page.click('[data-action="agree-continue"]');
assert.equal(await page.locator('#s-profile.active').count(), 0);
await page.click('#privacy-check');
await page.click('[data-action="agree-continue"]');
await page.waitForSelector('#s-profile.active');
assert.equal((await api.get(base + 'api/reports')).status(), 200);
const cookie = (await context.cookies()).find(c => c.httpOnly);
assert(cookie && cookie.secure && cookie.sameSite === 'Lax' && cookie.path === new URL(base).pathname);
assert.equal((await api.post(base + 'api/skin-analyze', { data: {} })).status(), 400);
assert.equal((await api.post(base + 'api/skin-analyze', { data: {}, headers: { Origin: 'https://invalid.example' } })).status(), 403);
console.log('PASS: consent-gated temporary HTTPS session, secure scoped cookie, missing-image validation, cross-origin denial');
await page.click('[data-action="submit-profile"]');
assert.equal(await page.locator('#s-profile.active').count(), 1);
for (const selector of ['#pick-birth', '#pick-gender', '#pick-region']) {
@@ -79,17 +80,12 @@ try {
assert.equal(response.status(), 200);
const result = await response.json();
assert.equal(result.ok, true, JSON.stringify({ code: result.code, message: result.message }));
assert.equal(result.report_saved, true);
assert.equal(result.report_saved, false, 'guest reports are deliberately not saved');
assert.equal(analyzeCalls, 1);
assert((await page.evaluate(() => window.__analyzeTimeouts)).includes(150000));
await page.waitForSelector('#s-report.active');
await page.waitForFunction(() => [...document.querySelectorAll('.ditem-img')].every(i => !i.hidden && i.naturalWidth > 0));
const snap = (await (await api.get(base + 'api/reports/' + result.record_id)).json()).report;
assert.equal(snap.photos.filter(Boolean).length, 3);
assert.deepEqual(snap.presentation, result.presentation);
assert.deepEqual(snap.payload.result.face_maps, result.result.face_maps);
const images = await page.locator('.ditem-img').evaluateAll(nodes => nodes.map(i => i.src));
const copy = await page.locator('.ditem-para').allTextContents();
assert.equal((await api.get(base + 'api/reports/' + result.record_id)).status(), 404);
for (const card of await page.locator('.card-detail').all()) {
for (const tab of await card.locator('.dtab').all()) {
await tab.click();
@@ -102,31 +98,23 @@ try {
await page.waitForSelector('#p-zoom.show', { state: 'hidden' });
assert.equal(await page.locator('#s-report.active').count(), 1);
await page.screenshot({ path: path.join(artifacts, 'real-report.png'), fullPage: true });
console.log('PASS: three sample views → REAL Face++ → scoring → seven images/tabs → zoom/back → backend snapshot');
console.log('PASS: three sample views → REAL Face++ → scoring → seven images/tabs → zoom/back; guest report is not saved');
// Cookie loss is restored using this browser's stored random identity, not a new account.
// Cookie loss cannot restore a guest identity or any guest history.
await context.clearCookies();
await page.reload({ waitUntil: 'domcontentloaded' });
await page.waitForSelector('#s-login.active');
await page.waitForFunction(() => !document.querySelector('#login-message').textContent.includes('检查'));
await page.click('[data-action="guest-experience"]');
await page.waitForSelector('#s-splash.active');
await page.click('[data-action="history"]');
await page.click('[data-report-id="' + result.record_id + '"]');
await page.waitForSelector('#s-report.active');
await page.waitForFunction(() => [...document.querySelectorAll('.ditem-img')].every(i => !i.hidden && i.naturalWidth > 0));
assert.deepEqual(await page.locator('.ditem-img').evaluateAll(nodes => nodes.map(i => i.src)), images);
assert.deepEqual(await page.locator('.ditem-para').allTextContents(), copy);
await page.goBack();
await page.waitForSelector('#s-history.active');
// Read failure + explicit retry must recover without losing the report.
await page.route('**/api/reports', route => route.fulfill({ status: 503, contentType: 'application/json', body: JSON.stringify({ ok: false, message: '测试临时不可用' }) }));
await page.click('[data-action="history-retry"]');
await page.waitForFunction(() => document.querySelector('#history-list').textContent.includes('测试临时不可用'));
await page.unroute('**/api/reports');
await page.click('[data-action="history-retry"]');
await page.waitForSelector('[data-report-id="' + result.record_id + '"]');
assert.equal(await page.locator('[data-report-id]').count(), 0);
assert((await page.locator('#identity-note').textContent()).includes('不保存'));
await page.screenshot({ path: path.join(artifacts, 'real-history.png') });
await page.goBack();
await page.waitForSelector('#s-splash.active');
console.log('PASS: cookie restoration, report reload with identical seven images/copy, history error/retry, browser back');
console.log('PASS: guest has no credential restoration or saved history; refresh/back navigation works');
const other = await browser.newContext();
assert.equal((await other.request.get(base + 'api/reports/' + result.record_id)).status(), 401);
@@ -139,7 +127,7 @@ try {
assert.deepEqual(errors, []);
fs.writeFileSync(path.join(artifacts, 'summary.json'), JSON.stringify({ record_id: result.record_id, analyzeCalls, errors, tested_at: new Date().toISOString() }, null, 2));
console.log('PASS: cross-account isolation, admin/source/data protection, zero JS errors');
console.log('Test report (dedicated identity): ' + result.record_id + '; artifacts: ' + artifacts);
console.log('Temporary guest verification report (not saved): ' + result.record_id + '; artifacts: ' + artifacts);
} finally {
await browser.close();
}