| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768 |
- import assert from 'node:assert/strict';
- import { readFile, mkdir, writeFile } from 'node:fs/promises';
- import { chromium } from 'playwright';
- import { checkCustomer } from './customer-functional.mjs';
- import { checkPlatform } from './platform-functional.mjs';
- import { checkLayouts } from './layout-functional.mjs';
- import { checkSessions } from './session-functional.mjs';
- import { checkComponents } from './components-functional.mjs';
- const baseUrl = process.env.FRONTEND_URL || 'http://127.0.0.1:18762';
- const accounts = JSON.parse(await readFile(new URL('../../.work/frontend-accounts.json', import.meta.url), 'utf8'));
- const browser = await chromium.launch({ headless: true, executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE });
- const results = [];
- const errors = [];
- let requests = 0;
- try {
- for (const [name, check] of [['customer', checkCustomer], ['platform', checkPlatform]]) {
- const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } });
- const page = await context.newPage();
- page.setDefaultTimeout(15000);
- page.on('pageerror', error => errors.push(`${name}: ${error.message}`));
- page.on('request', request => { if (/\/(openapi|admin)\//.test(request.url())) requests++; });
- const checks = await check(page, baseUrl, accounts);
- results.push({ area: name, checks });
- const palette = await page.locator(name === 'customer' ? '.mobile .content' : '.admin-sidebar').evaluate(element => getComputedStyle(element).backgroundColor);
- assert.equal(palette, name === 'customer' ? 'rgb(246, 248, 252)' : 'rgb(7, 17, 38)', `${name} 已确认背景色`);
- // 只检查公开 DOM 的布局,不读取组件内部状态,也不使用截图。
- for (const width of [1440, 390]) {
- await page.setViewportSize({ width, height: 1000 });
- const geometry = await page.locator('body').evaluate(element => ({ width: document.documentElement.clientWidth, scroll: element.scrollWidth }));
- assert.ok(geometry.scroll <= geometry.width + 1, `${name} ${width}px 页面横向溢出`);
- }
- await context.close();
- }
- const layoutContext = await browser.newContext();
- const layoutPage = await layoutContext.newPage();
- layoutPage.on('pageerror', error => errors.push(`layout: ${error.message}`));
- results.push({ area: 'layout', checks: await checkLayouts(layoutPage, baseUrl, accounts) });
- await layoutContext.close();
- const sessionContext = await browser.newContext();
- const sessionPage = await sessionContext.newPage();
- sessionPage.on('pageerror', error => errors.push(`session: ${error.message}`));
- results.push({ area: 'session', checks: await checkSessions(sessionPage, baseUrl, accounts) });
- await sessionContext.close();
- const componentContext = await browser.newContext();
- const componentPage = await componentContext.newPage();
- componentPage.on('pageerror', error => errors.push(`components: ${error.message}`));
- results.push({ area: 'components', checks: await checkComponents(componentPage, baseUrl) });
- await componentContext.close();
- assert.deepEqual(errors, []);
- await mkdir(new URL('../../.work/', import.meta.url), { recursive: true });
- await writeFile(new URL('../../.work/frontend-functional.json', import.meta.url), JSON.stringify({ results, errors, requests, screenshots: 0, generatedModelRequests: 0 }, null, 2));
- console.log(JSON.stringify({ areas: results.map(item => ({ area: item.area, passed: item.checks.length })), errors, requests, screenshots: 0 }, null, 2));
- } finally {
- await browser.close();
- }
- // 商家流程使用自己的真实登录与独立浏览器会话,与前面的消费者流程依次执行。
- await import('./merchant-functional.mjs');
- await import('./merchant-business.mjs');
- await import('./entry-isolation.mjs');
- await import('./platform-session.mjs');
- await import('./login-rate-limit.mjs');
- const merchant = JSON.parse(await readFile(new URL('../../.work/frontend-verification/merchant.json', import.meta.url), 'utf8'));
- const business = JSON.parse(await readFile(new URL('../../.work/frontend-verification/merchant-business.json', import.meta.url), 'utf8'));
- const extra = {};
- for (const name of ['entry-isolation', 'platform-session', 'login-rate-limit']) extra[name] = JSON.parse(await readFile(new URL(`../../.work/frontend-verification/${name}.json`, import.meta.url), 'utf8'));
- await writeFile(new URL('../../.work/frontend-verification/full.json', import.meta.url), JSON.stringify({ date: new Date().toISOString(), results, merchant, business, extra, browserErrors: errors, screenshots: 0, generatedModelRequests: 0 }, null, 2));
- console.log('全部前端黑盒与功能检查通过。');
|