const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'C:/Users/13070/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright'); const base = process.env.DEMO_BASE_URL || 'http://localhost:6688'; const uuid = /[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}/i; const code = /^[A-Z]{2}\d{11}$/; const output = path.resolve(__dirname, '../.mimosa/public-id-page-evidence'); fs.mkdirSync(output, { recursive: true }); (async () => { assert(process.env.DEMO_OPERATOR_PASSWORD, '请通过环境变量提供演示账号密码,不写入测试文件'); const browser = await chromium.launch({ channel: 'msedge', headless: true }); try { const context = await browser.newContext({ viewport: { width: 1366, height: 768 } }); const page = await context.newPage(); const errors = []; page.on('pageerror', error => errors.push(error.message)); await page.goto(`${base}/admin/login`); await page.getByPlaceholder('请输入用户名').fill(process.env.DEMO_OPERATOR_USERNAME || 'demo_admin'); await page.locator('input[type=password]').fill(process.env.DEMO_OPERATOR_PASSWORD); await page.getByRole('button', { name: '登录管理后台' }).click(); await page.waitForURL('**/admin/dashboard'); const endpoints = [ '/operator/users?page=1&pageSize=20', '/operator/operators', '/operator/activities', '/operator/reviews/pending?page=1&pageSize=20', '/operator/risk/reports/pending', '/operator/matchmaker/management/services', '/operator/matchmaker/recommendations', '/service-appointments/operator/list', '/referrals/operator/history', '/activities/operator/activity-interest-summary', ]; let checked = 0; const results = []; for (const endpoint of endpoints) { const response = await context.request.get(`${base}/api/v1${endpoint}`); assert(response.ok(), `${endpoint}: HTTP ${response.status()}`); const body = await response.json(); assert(body.success, `${endpoint}: ${body.message}`); function inspect(value) { if (!value || typeof value !== 'object') return; for (const [key, child] of Object.entries(value)) { if (/DisplayId$|DisplayNumber$|^displayId$|^displayNumber$/.test(key) && child != null) { assert(code.test(child), `${endpoint}: ${key} 格式错误`); checked++; } if (typeof child === 'object') inspect(child); } } inspect(body.data); results.push({ endpoint, status: response.status() }); } assert(checked > 0, '接口没有可验收编号'); const pages = [ ['users', '用户管理'], ['operators', '系统设置'], ['activities', '活动管理'], ['review', '资料审核'], ['risk/reports', '举报风控'], ['service-appointment', '服务预约'], ['referral', '公益红娘'], ['activities/interests', '匹配管理'], ['matchmaker', '红娘服务'], ]; for (const [route, label] of pages) { await page.getByText(label, { exact: true }).first().click(); await page.waitForTimeout(300); await page.waitForLoadState('networkidle'); assert(!page.url().includes('/login'), `${route}: 被退回登录`); const text = await page.locator('body').innerText(); assert(!uuid.test(text), `${route}: 可见文本仍有 UUID`); await page.screenshot({ path: path.join(output, `${route.replaceAll('/', '-')}-1366.png`), fullPage: true }); if (route === 'matchmaker') { await page.getByRole('tab', { name: '联系授权审批' }).click(); await page.waitForLoadState('networkidle'); assert(!uuid.test(await page.locator('body').innerText()), '联系授权仍有 UUID'); await page.screenshot({ path: path.join(output, 'contact-authorization-1366.png'), fullPage: true }); await page.setViewportSize({ width: 1920, height: 1080 }); await page.screenshot({ path: path.join(output, 'contact-authorization-1920.png'), fullPage: true }); } } assert.equal(errors.length, 0, `页面运行错误: ${errors.join('; ')}`); const anonymous = await browser.newContext(); const denied = await anonymous.request.get(`${base}/api/v1/operator/matchmaker/management/services`); assert([401, 403].includes(denied.status()), '未登录不应读取管理服务'); fs.writeFileSync(path.join(output, 'results.json'), JSON.stringify({ results, checkedDisplayFields: checked, pages: 10, anonymousStatus: denied.status(), errors }, null, 2)); console.log(`PASS: ${results.length} 类真实接口、${checked} 个展示字段、10 个页面/区域、匿名访问拒绝;截图已保存。`); } finally { await browser.close(); } })().catch(error => { console.error(error.message); process.exitCode = 1; });