- download: desktop_download_apps SingleChildScrollView 加 primary:true (Scrollbar 缺 ScrollPosition) - contact-stores: checkDomainAvailability async 后 setState 加 mounted 保护 (dispose 后 setState) - buy-service: _loadData 加 data 结构校验 + mounted 保护 (API 400 无效参数时 data 为错误结构 -> RangeError)
89 lines
2.7 KiB
JavaScript
89 lines
2.7 KiB
JavaScript
// e2e 测试:遍历公开路由,捕获 Flutter 错误(print→console 的 CK_ERROR/CK_STACK)
|
||
const { chromium } = require('playwright');
|
||
const BASE = 'http://127.0.0.1:8099';
|
||
|
||
// 公开路由(不需登录)。动态参数用合理默认值
|
||
const ROUTES = [
|
||
'/',
|
||
'/shop',
|
||
'/shop/310',
|
||
'/about-us',
|
||
'/contact-us',
|
||
'/contact-stores',
|
||
'/download',
|
||
'/blog/310',
|
||
'/view-blog/52',
|
||
'/igoshow-learn-more',
|
||
'/minipos-learn-more',
|
||
'/buy-service/1/pos',
|
||
'/forgot-password',
|
||
'/new-user',
|
||
'/privacy-policy',
|
||
'/return-policy',
|
||
'/service-policy',
|
||
'/end-user-license-agreement',
|
||
];
|
||
|
||
const results = [];
|
||
|
||
async function testRoute(browser, route) {
|
||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||
const page = await ctx.newPage();
|
||
const errors = [];
|
||
const ckStacks = [];
|
||
|
||
page.on('pageerror', e => errors.push(`PAGEERROR: ${e.message.slice(0, 200)}`));
|
||
page.on('console', m => {
|
||
const t = m.text();
|
||
if (t.startsWith('CK_ERROR:')) errors.push(t);
|
||
if (t.startsWith('CK_STACK:')) {
|
||
// 提取项目文件:行号
|
||
const proj = t.match(/flutter_wisetronic\/[^\s]+:\d+/g);
|
||
if (proj) ckStacks.push([...new Set(proj)].slice(0, 3));
|
||
}
|
||
});
|
||
|
||
try {
|
||
await page.goto(BASE + route, { waitUntil: 'load', timeout: 30000 });
|
||
await page.waitForTimeout(5000); // 等 Flutter 渲染 + API
|
||
} catch (e) {
|
||
errors.push(`GOTO_ERR: ${e.message.slice(0, 100)}`);
|
||
}
|
||
|
||
const status = errors.length === 0 ? '✓ OK' : `✗ ${errors.length} 错误`;
|
||
console.log(`${status.padEnd(12)} ${route}`);
|
||
if (errors.length > 0) {
|
||
console.log(' 错误: ' + errors.slice(0, 3).join(' | ').slice(0, 250));
|
||
if (ckStacks.length > 0) console.log(' 位置: ' + JSON.stringify(ckStacks[0]));
|
||
}
|
||
results.push({ route, errors, ckStacks });
|
||
await ctx.close();
|
||
}
|
||
|
||
(async () => {
|
||
console.log(`Flutter e2e 测试:${ROUTES.length} 个公开路由\n`);
|
||
const browser = await chromium.launch({ headless: true });
|
||
for (const r of ROUTES) {
|
||
await testRoute(browser, r);
|
||
}
|
||
await browser.close();
|
||
|
||
// 汇总
|
||
const failed = results.filter(r => r.errors.length > 0);
|
||
console.log(`\n========== 汇总 ==========`);
|
||
console.log(`总计 ${results.length} 路由,${failed.length} 个有错误`);
|
||
if (failed.length > 0) {
|
||
console.log('\n失败路由 + 崩溃位置:');
|
||
const locMap = {};
|
||
for (const f of failed) {
|
||
const loc = f.ckStacks[0] ? f.ckStacks[0][0] : '(无堆栈)';
|
||
locMap[loc] = locMap[loc] || [];
|
||
locMap[loc].push(f.route);
|
||
}
|
||
for (const [loc, routes] of Object.entries(locMap)) {
|
||
console.log(` ${loc}`);
|
||
console.log(` 影响: ${routes.join(', ')}`);
|
||
}
|
||
}
|
||
})();
|