import { execSync } from 'node:child_process'; import { pathToFileURL } from 'node:url'; const { chromium } = await import(pathToFileURL(execSync('npm root -g', { encoding: 'utf8' }).trim() + '/playwright/index.mjs').href); import fs from 'node:fs'; import assert from 'node:assert/strict'; const root = process.env.H5_ARTIFACT_DIR ? pathToFileURL(process.env.H5_ARTIFACT_DIR.replace(/\/$/, '') + '/') : new URL('../../design/ui-20261009/verification/', import.meta.url); fs.mkdirSync(root, { recursive: true }); const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] }); const page = await browser.newPage({ viewport: { width: 375, height: 726 } }); const errors = []; page.on('pageerror', e => errors.push(e.message)); const base = process.env.H5_BASE_URL || 'http://127.0.0.1:8080/'; await page.goto(base); await page.waitForFunction(() => !document.querySelector('#login-message').textContent.includes('检查')); await page.evaluate(() => document.fonts.ready); await page.waitForTimeout(400); await page.screenshot({ path: new URL('01-login.png', root).pathname }); await page.click('[data-action="login-wechat"]'); assert(await page.locator('#s-login').evaluate(e => e.classList.contains('active'))); await page.check('#login-consent'); await page.click('[data-action="login-wechat"]'); await page.waitForFunction(() => document.querySelector('#login-message').textContent.includes('免登录')); await page.click('[data-action="guest-experience"]'); await page.waitForSelector('#s-splash.active'); assert.equal(await page.locator('#p-login-success.show').count(), 0); await page.waitForTimeout(400); await page.waitForFunction(() => !document.querySelector('#toast').classList.contains('show')); await page.waitForTimeout(400); await page.screenshot({ path: new URL('02-home.png', root).pathname }); await page.click('[data-action="history"]'); await page.waitForFunction(() => document.querySelector('#history-count').textContent.includes('不保存')); await page.waitForTimeout(400); await page.screenshot({ path: new URL('03-history-empty.png', root).pathname }); await page.goBack(); await page.waitForSelector('#s-splash.active'); await page.reload(); 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="open-privacy"]'); await page.waitForTimeout(400); await page.screenshot({ path: new URL('04-privacy.png', root).pathname }); await page.click('#privacy-check'); await page.click('[data-action="agree-continue"]'); await page.waitForTimeout(400); assert.equal(await page.locator('#s-profile').evaluate(e => e.getBoundingClientRect().top), 0); await page.screenshot({ path: new URL('05-profile.png', root).pathname }); // Static mask layout audit only; real capture flow is covered by e2e-browser.mjs. await page.evaluate(() => document.querySelectorAll('.screen').forEach(s => s.classList.toggle('active', s.id === 's-cam'))); await page.waitForTimeout(400); for (const [step, name, label] of [['0', 'front', '1/3
请正脸出镜'], ['2', 'right', '2/3
请右脸出镜'], ['1', 'left', '3/3
请左脸出镜']]) { await page.evaluate(({ step, label }) => { document.querySelector('#cam-mask').dataset.step = step; document.querySelector('#cam-step').innerHTML = label; }, { step, label }); await page.screenshot({ path: new URL('08-camera-' + name + '-layout.png', root).pathname }); } await page.goto(base + '?mock=1&screen=report'); await page.waitForFunction(() => document.querySelector('#stat-score b').textContent !== '--'); await page.waitForTimeout(1200); assert.equal(await page.locator('.card-detail').count(), 3); assert.equal(await page.locator('.dtab').count(), 7); assert.equal(await page.locator('.card-product img#product-image').count(), 1); await page.screenshot({ path: new URL('06-report-top.png', root).pathname }); await page.locator('#s-report').evaluate(e => { e.style.position = 'relative'; e.style.height = 'auto'; e.style.overflow = 'visible'; document.body.style.overflow = 'visible'; }); await page.screenshot({ path: new URL('07-report-full.png', root).pathname, fullPage: true }); for (const card of await page.locator('.card-detail').all()) { for (const tab of await card.locator('.dtab').all()) { await tab.click(); assert.equal(await card.locator('.dpanel.active').count(), 1); } } for (const width of [320, 390, 480]) { await page.setViewportSize({ width, height: 844 }); assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), 'horizontal overflow ' + width); } assert.deepEqual(errors, []); console.log('PASS: login/consent/fallback/refresh/history/profile/report/7 tabs/3 viewport widths, no JS errors'); await browser.close();