Compare commits
7
Commits
6d08db54a5
...
94138ba7f4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94138ba7f4 | ||
|
|
33b54fe454 | ||
|
|
901e42a41b | ||
|
|
af8934fe7f | ||
|
|
048c5a9ff4 | ||
|
|
1d4028f486 | ||
|
|
944493a39b |
+4
-2
@@ -36,12 +36,14 @@ void main() {
|
||||
FlutterError.onError = (details) {
|
||||
FlutterError.presentError(details);
|
||||
print('CK_ERROR: ${details.exceptionAsString()}');
|
||||
print('CK_CTX: ${details.context}');
|
||||
print('CK_STACK: ${details.stack}');
|
||||
};
|
||||
// Enable configureApp will cause route problem.
|
||||
// configureApp();
|
||||
setPathUrlStrategy();
|
||||
runApp(MyApp());
|
||||
Future.delayed(Duration(seconds: 3), () => Util.devAutologin()); // DEV e2e: 延迟 3s 等 Flutter/Hive ready
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@@ -170,12 +172,12 @@ class MyApp extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (pathElements[1] == 'renew-minioffice') {
|
||||
if (pathElements[1] == 'renew-minioffice' && pathElements.length >= 3) {
|
||||
return MaterialPageRoute(builder: (BuildContext context) =>
|
||||
RenewMiniOffice(int.parse(pathElements[2]))
|
||||
);
|
||||
}
|
||||
if (pathElements[1] == 'buy-service') {
|
||||
if (pathElements[1] == 'buy-service' && pathElements.length >= 4) {
|
||||
return MaterialPageRoute(builder: (BuildContext context) =>
|
||||
BuyService(int.parse(pathElements[2]), pathElements[3])
|
||||
);
|
||||
|
||||
@@ -72,12 +72,16 @@ class BuyServiceState extends State<BuyService> {
|
||||
'store_id': '${widget.sid}',
|
||||
}
|
||||
).then((value) {
|
||||
data = value;
|
||||
if (!mounted) return;
|
||||
if (value == null || value is! Map || value['service_selections'] == null) {
|
||||
Utils.showMessageDialog(context, 'Service info unavailable');
|
||||
return;
|
||||
}
|
||||
data = Map<String, dynamic>.from(value);
|
||||
data!['domain'] = widget.domain;
|
||||
print('data: $data');
|
||||
setState(() {});
|
||||
}).onError((error, stackTrace) {
|
||||
Utils.showMessageDialog(context, error);
|
||||
if (mounted) Utils.showMessageDialog(context, error);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -199,6 +199,9 @@ class Util {
|
||||
return box;
|
||||
}
|
||||
|
||||
/// DEV ONLY stub (原生不自动登录)
|
||||
static void devAutologin() {}
|
||||
|
||||
static Widget showImage(String imageUrl, {double? width, double? height,
|
||||
BoxFit? fit, Widget Function(BuildContext, String, dynamic)? errorWidget}) {
|
||||
if (imageUrl != null && imageUrl.isNotEmpty && imageUrl.startsWith('https:')) {
|
||||
|
||||
@@ -41,6 +41,28 @@ class Util {
|
||||
return box;
|
||||
}
|
||||
|
||||
/// DEV ONLY (e2e tests): 读 localStorage['_test_token'] = 'username|password',
|
||||
/// 调登录 API 写 Hive + dispatch currentUser。测试后移除。
|
||||
static Future<void> devAutologin() async {
|
||||
try {
|
||||
final t = window.localStorage['_test_token'];
|
||||
if (t == null || t.isEmpty) return;
|
||||
final parts = t.split('|');
|
||||
if (parts.length < 2) return;
|
||||
print('CK_AUTOLOGIN_CALL');
|
||||
await HttpUtil.httpPost('v1/oauth-wisetronic/access_token', (response) async {
|
||||
final box = await getBox();
|
||||
await box.put(Constants.KEY_ACCESS_TOKEN, response.data['access_token']);
|
||||
await box.put(Constants.KEY_USER_ID, response.data['user_id']);
|
||||
store.dispatch(UpdateCurrentUser(User.fromJson(response.data['user'])));
|
||||
print('CK_AUTOLOGIN_OK');
|
||||
},
|
||||
queryParameters: {'client_id': Utils.getPlatformName(), 'grant_type': 'password'},
|
||||
body: {'username': parts[0], 'password': parts[1], 'fcm_token': ''},
|
||||
isFormData: true, businessId: Constants.BUSINESS_ID);
|
||||
} catch (e) { print('CK_AUTOLOGIN_EX: ${e.toString().substring(0, 150)}'); }
|
||||
}
|
||||
|
||||
static Widget showImage(String imageUrl, {double? width, double? height,
|
||||
BoxFit? fit, Widget Function(BuildContext, String?, dynamic)? errorWidget}) {
|
||||
return Image.network(imageUrl,
|
||||
|
||||
@@ -383,13 +383,14 @@ class CreateOnlineStore1State extends State<CreateOnlineStore1> {
|
||||
'service': Constants.WEB_MINISTORE_SERVICE
|
||||
}
|
||||
).then((value) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
stores = value['stores'];
|
||||
service = value['service'];
|
||||
group = Group.fromJson(value['group']);
|
||||
print('stores: ${value}');
|
||||
});
|
||||
}).onError((error, stackTrace) {
|
||||
if (!mounted) return;
|
||||
Utils.showMessageDialog(context, error, onOk: () {
|
||||
Routes.router.navigateTo(context, '/');
|
||||
});
|
||||
@@ -411,7 +412,7 @@ class CreateOnlineStore1State extends State<CreateOnlineStore1> {
|
||||
print('value: $value');
|
||||
if (value['status'] == 'OK') {
|
||||
domainResult = (value['domain_check_result'] as List);
|
||||
setState(() {});
|
||||
if (mounted) setState(() {});
|
||||
} else if (value['status'] == 'ERROR') {
|
||||
String errors = '';
|
||||
for (int i = 0; i < (value['errors'] as List).length; i++) {
|
||||
|
||||
@@ -55,6 +55,7 @@ class DesktopDownloadAppsState extends State<DesktopDownloadApps> {
|
||||
}
|
||||
col.children.add(wrap);
|
||||
Widget view = SingleChildScrollView(
|
||||
primary: true,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
*.png
|
||||
@@ -0,0 +1,13 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext()).newPage();
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load' });
|
||||
await page.waitForTimeout(3000);
|
||||
const data = await page.evaluate(async () => {
|
||||
const r = await fetch('https://api.minipos.us/v1/get-buy-service-info/pos/1?store_id=0');
|
||||
return JSON.stringify(await r.json());
|
||||
}).catch(e => 'ERR: ' + e.message);
|
||||
console.log('buy-service API:', data.slice(0, 600));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,22 @@
|
||||
// 拦截 buy-service 的 API 请求/响应,看实际数据结构
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
page.on('response', async (resp) => {
|
||||
const url = resp.url();
|
||||
if (url.includes('get-buy-service-info')) {
|
||||
console.log('=== buy-service API 响应 ===');
|
||||
console.log('状态:', resp.status());
|
||||
try {
|
||||
const body = await resp.text();
|
||||
console.log('body:', body.slice(0, 700));
|
||||
} catch(e) { console.log('读 body 失败'); }
|
||||
}
|
||||
});
|
||||
page.on('pageerror', e => console.log('PAGEERR:', e.message.slice(0,150)));
|
||||
page.on('console', m => { if (m.text().startsWith('data:') || m.text().includes('RangeError')) console.log('CONSOLE:', m.text().slice(0,200)); });
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/pos', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(8000);
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,12 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/pos', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(6000);
|
||||
const dataLog = logs.filter(l => l.startsWith('data: {') || l.startsWith('data:{'));
|
||||
console.log('应用 data:', dataLog[0] ? dataLog[0].slice(0, 500) : '(未捕获)');
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,21 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
page.on('pageerror', e => logs.push('PAGEERR: ' + e.message));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/pos', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(7000);
|
||||
// 找 data 打印 + RangeError
|
||||
const dataLog = logs.find(l => l.startsWith('data:'));
|
||||
console.log('=== data ===', dataLog ? dataLog.slice(0,400) : '(无)');
|
||||
// 看 service_selections / options 部分
|
||||
if (dataLog) {
|
||||
const m = dataLog.match(/service_selections:[^,}]+/);
|
||||
if (m) console.log('service_selections:', m[0].slice(0,200));
|
||||
const o = dataLog.match(/options:[^]]*\]/);
|
||||
if (o) console.log('options:', o[0].slice(0,200));
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,33 @@
|
||||
// 诊断:对失败路由打印完整错误 + CK_STACK
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
const ROUTES = ['/contact-stores', '/download', '/buy-service/1/pos'];
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
for (const route of ROUTES) {
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
const page = await ctx.newPage();
|
||||
const allLogs = [];
|
||||
page.on('pageerror', e => allLogs.push(`PAGEERROR: ${e.message}`));
|
||||
page.on('console', m => allLogs.push(m.text()));
|
||||
await page.goto(BASE + route, { waitUntil: 'load', timeout: 30000 }).catch(e => allLogs.push('GOTO: ' + e.message.slice(0,80)));
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
console.log(`\n########## ${route} ##########`);
|
||||
// 只打印 CK_ERROR + CK_STACK + PAGEERROR
|
||||
for (let i = 0; i < allLogs.length; i++) {
|
||||
const l = allLogs[i];
|
||||
if (l.startsWith('CK_ERROR:') || l.startsWith('PAGEERROR')) {
|
||||
console.log(l.slice(0, 300));
|
||||
// 找紧跟的 CK_STACK
|
||||
if (allLogs[i+1] && allLogs[i+1].startsWith('CK_STACK:')) {
|
||||
const proj = allLogs[i+1].match(/flutter_wisetronic\/[^\s]+:\d+/g);
|
||||
console.log(' → 项目位置:', proj ? [...new Set(proj)].slice(0,4) : '(无项目帧)');
|
||||
}
|
||||
}
|
||||
}
|
||||
await ctx.close();
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,32 @@
|
||||
// 诊断 v2:打印完整 CK_STACK(定位 framework 抛的错误的源头)
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
const ROUTES = ['/buy-service/1/pos', '/download'];
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
for (const route of ROUTES) {
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
const page = await ctx.newPage();
|
||||
const allLogs = [];
|
||||
page.on('pageerror', e => allLogs.push(`PAGEERROR: ${e.stack || e.message}`));
|
||||
page.on('console', m => allLogs.push(m.text()));
|
||||
await page.goto(BASE + route, { waitUntil: 'load', timeout: 30000 }).catch(()=>{});
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
console.log(`\n########## ${route} ##########`);
|
||||
for (let i = 0; i < allLogs.length; i++) {
|
||||
const l = allLogs[i];
|
||||
if (l.startsWith('CK_ERROR:') || l.startsWith('PAGEERROR')) {
|
||||
console.log('--- ' + l.slice(0, 150));
|
||||
if (allLogs[i+1] && allLogs[i+1].startsWith('CK_STACK:')) {
|
||||
// 打印堆栈里所有 flutter_wisetronic 帧
|
||||
const proj = allLogs[i+1].match(/flutter_wisetronic\/[^\s]+/g) || [];
|
||||
console.log(' 项目帧: ' + (proj.length ? [...new Set(proj)].join('\n ') : '(无)'));
|
||||
}
|
||||
}
|
||||
}
|
||||
await ctx.close();
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,14 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
page.on('pageerror', e => logs.push('PAGEERR: ' + e.message.slice(0,150)));
|
||||
page.on('response', async r => { if (r.url().includes('get-buy-service')) console.log('API:', r.status()); });
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/pos', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(9000);
|
||||
console.log('--- RangeError 相关 console ---');
|
||||
logs.filter(l => l.includes('RangeError') || l.includes('CK_ERROR') || l.startsWith('PAGEERR')).slice(0,5).forEach(l => console.log(l.slice(0,180)));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,16 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(8000);
|
||||
for (let i=0;i<logs.length;i++){
|
||||
if(logs[i].startsWith('CK_ERROR')){console.log(logs[i].slice(0,120));
|
||||
const ctx=logs.slice(i+1,i+4).find(l=>l.startsWith('CK_CTX'));
|
||||
if(ctx) console.log(' CTX:', ctx.slice(0,150));
|
||||
}
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,13 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(7000);
|
||||
const ctx = logs.filter(l => l.startsWith('CK_CTX'));
|
||||
if (ctx.length) console.log(ctx[0].slice(0, 600));
|
||||
else console.log('无 CK_CTX');
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,14 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(7000);
|
||||
const range = logs.filter(l => l.includes('RangeError'));
|
||||
const err = logs.filter(l => l.includes('CK_BUY_ERR'));
|
||||
console.log('RangeError 数:', range.length, err.length ? '| CK_BUY_ERR:'+err[0].slice(0,100) : '');
|
||||
if (range.length) console.log(range[0].slice(0,120));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,21 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 375, height: 812 } });
|
||||
await ctx.addInitScript(([u,p]) => { localStorage['_test_token'] = u+'|'+p; }, ['6477108200','8818']);
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load' });
|
||||
await page.waitForSelector('canvas', { timeout: 90000 });
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(8000);
|
||||
await page.goto('http://127.0.0.1:8099/me', { waitUntil: 'load' });
|
||||
await page.waitForTimeout(6000);
|
||||
const stacks = logs.filter(l => l.startsWith('CK_STACK:'));
|
||||
for (const s of stacks) {
|
||||
const proj = s.match(/flutter_wisetronic\/[^\s]+:\d+/g);
|
||||
if (proj) console.log('RenderFlex 项目位置:', [...new Set(proj)].slice(0,3));
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,15 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(7000);
|
||||
console.log('=== path elements 打印(看是否有短路径)===');
|
||||
logs.filter(l => l.includes('path elements')).forEach(l => console.log(' ' + l.slice(0,80)));
|
||||
console.log('=== RangeError 之前的最后几条 console ===');
|
||||
const idx = logs.findIndex(l => l.includes('RangeError'));
|
||||
if (idx >= 0) logs.slice(Math.max(0,idx-4), idx).forEach(l => console.log(' ' + l.slice(0,90)));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,14 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
for (const route of ['/download', '/contact-stores', '/me', '/orders', '/buy-service/1/ministore']) {
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
let range = 0;
|
||||
page.on('console', m => { if (m.text().includes('RangeError')) range++; });
|
||||
await page.goto('http://127.0.0.1:8099'+route, { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(6000);
|
||||
console.log(`${range>0?'✗ '+range+' RangeError':'✓ OK'} ${route}`);
|
||||
await page.close();
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,13 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(7000);
|
||||
const seq = logs.filter(l => l.startsWith('CK_BS_') || l.includes('RangeError'));
|
||||
console.log('时序:');
|
||||
seq.slice(0,10).forEach(l => console.log(' ' + l.slice(0,90)));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,19 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(7000);
|
||||
const stack = logs.filter(l => l.startsWith('CK_STACK'));
|
||||
if (stack.length) {
|
||||
// 找 .dart 源文件帧(非 main.dart.js)
|
||||
const dartFrames = stack[0].split('\n').filter(l => l.includes('.dart') && !l.includes('main.dart.js'));
|
||||
console.log('源文件帧:', dartFrames.length ? dartFrames.slice(0,5).join('\n ') : '(无,全是 main.dart.js)');
|
||||
// 也看前 3 个 main.dart.js 帧
|
||||
const jsFrames = stack[0].split('\n').filter(l => l.includes('main.dart.js')).slice(0,4);
|
||||
console.log('main.dart.js 帧:', jsFrames.join('\n '));
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,11 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/buy-service/1/ministore', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(8000);
|
||||
logs.filter(l => l.includes('CK_BUY_THEN') || l.includes('RangeError') || l.includes('CK_ERROR')).slice(0,5).forEach(l => console.log(l.slice(0,160)));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,47 @@
|
||||
// dump IndexedDB 结构 + Hive entry 格式
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext()).newPage();
|
||||
await page.goto(BASE + '/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForTimeout(7000);
|
||||
|
||||
const result = await page.evaluate(async () => {
|
||||
const out = {};
|
||||
const dbs = await indexedDB.databases();
|
||||
out.databases = dbs.map(d => d.name);
|
||||
for (const db of dbs) {
|
||||
out[db.name] = [];
|
||||
await new Promise((resolve) => {
|
||||
const req = indexedDB.open(db.name);
|
||||
req.onsuccess = () => {
|
||||
const d = req.result;
|
||||
const stores = [...d.objectStoreNames];
|
||||
out[db.name + '_stores'] = stores;
|
||||
if (stores.length === 0) { d.close(); resolve(); return; }
|
||||
const tx = d.transaction(stores[0], 'readonly');
|
||||
const store = tx.objectStore(stores[0]);
|
||||
const allReq = store.getAll();
|
||||
const keysReq = store.getAllKeys();
|
||||
allReq.onsuccess = () => {
|
||||
out[db.name] = allReq.result.map((v, i) => {
|
||||
let hex = '';
|
||||
if (v instanceof ArrayBuffer) {
|
||||
const u = new Uint8Array(v);
|
||||
hex = Array.from(u.slice(0, 40)).map(b => b.toString(16).padStart(2,'0')).join(' ');
|
||||
return { key: keysReq.result[i], valueBytes: v.byteLength, hexFirst40: hex };
|
||||
}
|
||||
return { key: keysReq.result[i], value: typeof v === 'string' ? v.slice(0,80) : v };
|
||||
});
|
||||
};
|
||||
tx.oncomplete = () => { d.close(); resolve(); };
|
||||
};
|
||||
req.onerror = () => resolve();
|
||||
});
|
||||
}
|
||||
return out;
|
||||
});
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
await browser.close();
|
||||
})().catch(e => { console.error('失败:', e.message); process.exit(1); });
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 813 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
@@ -0,0 +1,46 @@
|
||||
"use strict";
|
||||
|
||||
|
||||
|
||||
// A map containing the URLs for the bootstrap scripts in debug.
|
||||
let _scriptUrls = {
|
||||
"mapper": "stack_trace_mapper.js",
|
||||
"requireJs": "require.js"
|
||||
};
|
||||
|
||||
// Create a TrustedTypes policy so we can attach Scripts...
|
||||
let _ttPolicy;
|
||||
if (window.trustedTypes) {
|
||||
_ttPolicy = trustedTypes.createPolicy("flutter-tools-bootstrap", {
|
||||
createScriptURL: (url) => {
|
||||
let scriptUrl = _scriptUrls[url];
|
||||
if (!scriptUrl) {
|
||||
console.error("Unknown Flutter Web bootstrap resource!", url);
|
||||
}
|
||||
return scriptUrl;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Creates a TrustedScriptURL for a given `scriptName`.
|
||||
// See `_scriptUrls` and `_ttPolicy` above.
|
||||
function getTTScriptUrl(scriptName) {
|
||||
let defaultUrl = _scriptUrls[scriptName];
|
||||
return _ttPolicy ? _ttPolicy.createScriptURL(scriptName) : defaultUrl;
|
||||
}
|
||||
|
||||
// Attach source mapping.
|
||||
var mapperEl = document.createElement("script");
|
||||
mapperEl.defer = true;
|
||||
mapperEl.async = false;
|
||||
mapperEl.src = getTTScriptUrl("mapper");
|
||||
document.head.appendChild(mapperEl);
|
||||
|
||||
// Attach require JS.
|
||||
var requireEl = document.createElement("script");
|
||||
requireEl.defer = true;
|
||||
requireEl.async = false;
|
||||
requireEl.src = getTTScriptUrl("requireJs");
|
||||
// This attribute tells require JS what to load as main (defined below).
|
||||
requireEl.setAttribute("data-main", "main_module.bootstrap");
|
||||
document.head.appendChild(requireEl);
|
||||
Generated
+70
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "e2e",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "e2e",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"playwright": "^1.62.1",
|
||||
"source-map": "^0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.62.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz",
|
||||
"integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.62.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.62.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz",
|
||||
"integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0.tgz",
|
||||
"integrity": "sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "e2e",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"playwright": "^1.62.1",
|
||||
"source-map": "^0.8.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
const fs = require('fs');
|
||||
const { SourceMapConsumer } = require('source-map');
|
||||
(async () => {
|
||||
const mapJson = await (await fetch('http://127.0.0.1:8099/main.dart.js.map')).text().catch(()=>null);
|
||||
if (!mapJson) { console.log('无 sourcemap'); return; }
|
||||
const smc = await new SourceMapConsumer(mapJson);
|
||||
// 解析 299:254 (line:col)
|
||||
const pos = smc.originalPositionFor({ line: 299, column: 254 });
|
||||
console.log('299:254 →', pos.source, pos.line + ':' + pos.column, '|', pos.name);
|
||||
// 也试 299 的前后几行
|
||||
for (const c of [200, 100, 50]) {
|
||||
const p = smc.originalPositionFor({ line: 299, column: c });
|
||||
if (p.source) console.log(`299:${c} →`, p.source.split('/').slice(-2).join('/') + ':' + p.line);
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,68 @@
|
||||
// 探测脚本:检查 Flutter renderer + 登录页 DOM + Hive 存储后端
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext();
|
||||
const page = await ctx.newPage();
|
||||
|
||||
// 收集 console
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(`[${m.type()}] ${m.text()}`));
|
||||
page.on('pageerror', e => logs.push(`[PAGEERROR] ${e.message}`));
|
||||
|
||||
console.log('=== 1. 访问首页,检查 renderer ===');
|
||||
await page.goto(BASE + '/', { waitUntil: 'networkidle', timeout: 60000 });
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// 检查 renderer:canvaskit 有 <canvas> + flt-glass-pane;html 有 flutter-view 含 div
|
||||
const canvasCount = await page.locator('canvas').count();
|
||||
const fltGlass = await page.locator('flt-glass-pane').count();
|
||||
const htmlView = await page.locator('flutter-view, .flutter-view').count();
|
||||
console.log(`canvas 元素数: ${canvasCount}`);
|
||||
console.log(`flt-glass-pane (canvaskit 标志): ${fltGlass}`);
|
||||
console.log(`flutter-view: ${htmlView}`);
|
||||
|
||||
// body HTML 片段(判断 renderer)
|
||||
const bodySnippet = await page.evaluate(() => document.body.innerHTML.slice(0, 400));
|
||||
console.log(`body 片段: ${bodySnippet.slice(0, 200)}`);
|
||||
|
||||
console.log('\n=== 2. 访问登录页,检查输入框 DOM ===');
|
||||
await page.goto(BASE + '/login', { waitUntil: 'networkidle', timeout: 60000 });
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Flutter web 输入框:canvaskit 会在聚焦时创建 <input>;检查有无 input
|
||||
const inputCount = await page.locator('input').count();
|
||||
console.log(`input 元素数: ${inputCount}`);
|
||||
const inputs = await page.locator('input').evaluateAll(els =>
|
||||
els.map(e => ({ type: e.type, placeholder: e.placeholder, ariaLabel: e.getAttribute('aria-label'), cls: e.className.slice(0, 40) }))
|
||||
);
|
||||
console.log('input 详情:', JSON.stringify(inputs).slice(0, 300));
|
||||
|
||||
// 检查 semantics(辅助功能树)
|
||||
const semanticsRoles = await page.locator('[role="textbox"], [role="button"]').count();
|
||||
console.log(`semantics (role=textbox/button): ${semanticsRoles}`);
|
||||
|
||||
console.log('\n=== 3. 检查存储(localStorage + IndexedDB)===');
|
||||
const ls = await page.evaluate(() => {
|
||||
return JSON.stringify(Object.keys(localStorage).reduce((o, k) => { o[k] = localStorage.getItem(k).slice(0, 100); return o; }, {}));
|
||||
});
|
||||
console.log(`localStorage: ${ls}`);
|
||||
|
||||
const idb = await page.evaluate(async () => {
|
||||
const dbs = indexedDB.databases ? await indexedDB.databases() : [];
|
||||
const result = [];
|
||||
for (const db of dbs) {
|
||||
result.push({ name: db.name, version: db.version });
|
||||
}
|
||||
return JSON.stringify(result);
|
||||
});
|
||||
console.log(`IndexedDB databases: ${idb}`);
|
||||
|
||||
console.log('\n=== 4. console 日志(前 10 条)===');
|
||||
console.log(logs.slice(0, 10).join('\n'));
|
||||
|
||||
await browser.close();
|
||||
})().catch(e => { console.error('探测失败:', e.message); process.exit(1); });
|
||||
@@ -0,0 +1,51 @@
|
||||
// 探测 v2:正确加载 + 截图 + 检测 canvaskit 隐藏 input
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('pageerror', e => logs.push(`[PAGEERROR] ${e.message.slice(0, 120)}`));
|
||||
|
||||
console.log('=== 访问 /login (load 模式) ===');
|
||||
await page.goto(BASE + '/login', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForTimeout(6000); // 等 Flutter 渲染
|
||||
|
||||
await page.screenshot({ path: 'login.png', fullPage: true });
|
||||
console.log('截图: login.png');
|
||||
|
||||
// 检查所有 input(含 display:none)
|
||||
const inputs = await page.evaluate(() => {
|
||||
return [...document.querySelectorAll('input')].map(e => ({
|
||||
type: e.type,
|
||||
hidden: e.hidden || e.style.display === 'none' || e.offsetParent === null,
|
||||
cls: e.className.slice(0, 50),
|
||||
pos: e.getBoundingClientRect ? (() => { const r = e.getBoundingClientRect(); return {x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height)}; })() : null,
|
||||
}));
|
||||
});
|
||||
console.log(`所有 input (${inputs.length}):`, JSON.stringify(inputs));
|
||||
|
||||
// 检查 flt-glass-pane 内的 semantics
|
||||
const semTextboxes = await page.locator('[role="textbox"]').count();
|
||||
console.log(`role=textbox: ${semTextboxes}`);
|
||||
|
||||
console.log('\n=== 尝试点击用户名输入框区域,看是否触发 input ===');
|
||||
// canvaskit 点击中间偏上区域(假设用户名框)
|
||||
await page.mouse.click(640, 400);
|
||||
await page.waitForTimeout(1500);
|
||||
const inputsAfterClick = await page.evaluate(() => {
|
||||
return [...document.querySelectorAll('input')].map(e => ({
|
||||
hidden: e.hidden || e.style.display === 'none',
|
||||
pos: (() => { const r = e.getBoundingClientRect(); return {x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width)}; })(),
|
||||
}));
|
||||
});
|
||||
console.log('点击后 input:', JSON.stringify(inputsAfterClick));
|
||||
console.log('是否有 activeElement 是 input:', await page.evaluate(() => document.activeElement && document.activeElement.tagName));
|
||||
|
||||
console.log('\n=== pageerror 日志 ===');
|
||||
console.log(logs.slice(0, 5).join('\n'));
|
||||
|
||||
await browser.close();
|
||||
})().catch(e => { console.error('失败:', e.message); process.exit(1); });
|
||||
@@ -0,0 +1,40 @@
|
||||
// probe3:开启 Flutter web semantics(Enable accessibility),看能否得到 DOM 输入框
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({ viewport: { width: 1280, height: 900 } })).newPage();
|
||||
await page.goto(BASE + '/login', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
// 方法1:点击 flt-semantics-placeholder("Enable accessibility")
|
||||
const ph = await page.locator('flt-semantics-placeholder').count();
|
||||
console.log('placeholder 数:', ph);
|
||||
if (ph > 0) {
|
||||
await page.locator('flt-semantics-placeholder').click({ force: true }).catch(e => console.log('点击 placeholder 失败:', e.message.slice(0,60)));
|
||||
await page.waitForTimeout(3000);
|
||||
}
|
||||
|
||||
// 方法2:尝试 JS 开启(多种已知方式)
|
||||
await page.evaluate(() => {
|
||||
// 部分版本支持
|
||||
if (window._flutter) { try { window._flutter.semanticsEnabled = true; } catch(e){} }
|
||||
});
|
||||
|
||||
const textbox = await page.locator('[role="textbox"]').count();
|
||||
const btn = await page.locator('[role="button"]').count();
|
||||
const input = await page.locator('input').count();
|
||||
console.log(`开启后: role=textbox=${textbox}, role=button=${btn}, input=${input}`);
|
||||
|
||||
// 列出 semantics 元素的 label(找用户名/密码/登录按钮)
|
||||
const labels = await page.evaluate(() => {
|
||||
return [...document.querySelectorAll('[role="textbox"], [role="button"], [aria-label]')]
|
||||
.map(e => ({ role: e.getAttribute('role'), label: e.getAttribute('aria-label'), name: e.tagName }))
|
||||
.filter(x => x.label && x.label.length < 40)
|
||||
.slice(0, 25);
|
||||
});
|
||||
console.log('semantics labels:', JSON.stringify(labels));
|
||||
|
||||
await page.screenshot({ path: 'login_semantics.png' });
|
||||
await browser.close();
|
||||
})().catch(e => { console.error('失败:', e.message); process.exit(1); });
|
||||
@@ -0,0 +1,27 @@
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({ viewport: { width: 1280, height: 900 } })).newPage();
|
||||
await page.goto(BASE + '/login', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
// JS 直接触发 placeholder click(绕过 viewport 限制)
|
||||
await page.evaluate(() => {
|
||||
const el = document.querySelector('flt-semantics-placeholder');
|
||||
if (el) el.click();
|
||||
});
|
||||
await page.waitForTimeout(4000);
|
||||
|
||||
const textbox = await page.locator('[role="textbox"]').count();
|
||||
const input = await page.locator('input').count();
|
||||
console.log(`JS click 后: role=textbox=${textbox}, input=${input}`);
|
||||
|
||||
const labels = await page.evaluate(() =>
|
||||
[...document.querySelectorAll('[role="textbox"],[role="button"]')]
|
||||
.map(e => ({ role: e.getAttribute('role'), label: e.getAttribute('aria-label') }))
|
||||
.filter(x => x.label && x.label.length < 50).slice(0, 30)
|
||||
);
|
||||
console.log('labels:', JSON.stringify(labels));
|
||||
await browser.close();
|
||||
})().catch(e => { console.error('失败:', e.message); process.exit(1); });
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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(', ')}`);
|
||||
}
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,94 @@
|
||||
// 登录态路由测试:注入 _test_token,autologin 后遍历需登录路由
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
const USER = '6477108200';
|
||||
const PASS = '8818';
|
||||
|
||||
// 需登录路由(动态参数用合理默认)
|
||||
const AUTH_ROUTES = [
|
||||
'/me',
|
||||
'/orders',
|
||||
'/user-profile',
|
||||
'/my-cards',
|
||||
'/my-addresses/310',
|
||||
'/coupons/310',
|
||||
'/my-support/310',
|
||||
'/new-ticket/310',
|
||||
'/change-password',
|
||||
'/checkout/310',
|
||||
// 带动态 id(无效 id 也应优雅处理不崩)
|
||||
'/orderdetail/1',
|
||||
'/paynow/1',
|
||||
'/view-ticket/1',
|
||||
'/new-comment/1',
|
||||
'/ocr-scan',
|
||||
'/renew-license',
|
||||
'/renew-minioffice/1',
|
||||
];
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
await ctx.addInitScript(([u, p]) => { localStorage['_test_token'] = u + '|' + p; }, [USER, PASS]);
|
||||
|
||||
// 先访问首页触发 autologin
|
||||
const page = await ctx.newPage();
|
||||
let autologinOk = false;
|
||||
page.on('console', m => { if (m.text().includes('CK_AUTOLOGIN_OK')) autologinOk = true; });
|
||||
console.log('触发 autologin...');
|
||||
await page.goto(BASE + '/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForSelector('canvas', { timeout: 90000 });
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(8000);
|
||||
console.log('autologin:', autologinOk ? '✓ 成功' : '✗ 未确认\n');
|
||||
|
||||
const results = [];
|
||||
for (const route of AUTH_ROUTES) {
|
||||
const errors = [];
|
||||
const stacks = [];
|
||||
const pageErrs = [];
|
||||
page.removeAllListeners('console');
|
||||
page.removeAllListeners('pageerror');
|
||||
page.on('console', m => {
|
||||
const t = m.text();
|
||||
if (t.startsWith('CK_ERROR:')) errors.push(t.slice(0, 120));
|
||||
if (t.startsWith('CK_STACK:')) {
|
||||
const proj = t.match(/flutter_wisetronic\/[^\s]+:\d+/g);
|
||||
if (proj) stacks.push([...new Set(proj)].slice(0, 2));
|
||||
}
|
||||
});
|
||||
page.on('pageerror', e => pageErrs.push(e.message.slice(0, 120)));
|
||||
|
||||
try {
|
||||
await page.goto(BASE + route, { waitUntil: 'load', timeout: 30000 });
|
||||
await page.waitForTimeout(6000);
|
||||
} catch (e) {
|
||||
errors.push('GOTO: ' + e.message.slice(0, 80));
|
||||
}
|
||||
|
||||
const allErr = [...errors, ...pageErrs];
|
||||
const status = allErr.length === 0 ? '✓ OK' : `✗ ${allErr.length}`;
|
||||
console.log(`${status.padEnd(10)} ${route}`);
|
||||
if (allErr.length) {
|
||||
console.log(` ${allErr[0].slice(0, 120)}`);
|
||||
if (stacks.length) console.log(` 位置: ${JSON.stringify(stacks[0])}`);
|
||||
}
|
||||
results.push({ route, errors: allErr, stacks });
|
||||
}
|
||||
|
||||
const failed = results.filter(r => r.errors.length > 0);
|
||||
console.log(`\n========== 汇总 ==========`);
|
||||
console.log(`${results.length} 个登录态路由,${failed.length} 个有错误`);
|
||||
if (failed.length) {
|
||||
const locMap = {};
|
||||
for (const f of failed) {
|
||||
const loc = f.stacks[0] ? f.stacks[0][0] : f.errors[0].slice(0, 40);
|
||||
locMap[loc] = locMap[loc] || [];
|
||||
locMap[loc].push(f.route);
|
||||
}
|
||||
for (const [loc, routes] of Object.entries(locMap)) {
|
||||
console.log(` ${loc}\n → ${routes.join(', ')}`);
|
||||
}
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,71 @@
|
||||
// Mobile 视口测试(width 375,触发 ScreenTypeLayout mobile 分支)
|
||||
const { chromium } = require('playwright');
|
||||
const BASE = 'http://127.0.0.1:8099';
|
||||
|
||||
const ROUTES = [
|
||||
'/',
|
||||
'/shop',
|
||||
'/shop/310',
|
||||
'/me',
|
||||
'/orders',
|
||||
'/checkout/310',
|
||||
'/blog/310',
|
||||
'/view-blog/52',
|
||||
'/download',
|
||||
'/about-us',
|
||||
'/contact-us',
|
||||
'/user-profile',
|
||||
'/my-addresses/310',
|
||||
'/new-user',
|
||||
];
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 375, height: 812 } }); // iPhone 尺寸
|
||||
await ctx.addInitScript(([u, p]) => { localStorage['_test_token'] = u + '|' + p; }, ['6477108200', '8818']);
|
||||
|
||||
const page = await ctx.newPage();
|
||||
// 先 autologin
|
||||
page.on('console', () => {});
|
||||
await page.goto(BASE + '/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForSelector('canvas', { timeout: 90000 });
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(8000);
|
||||
console.log('Mobile 视口 (375x812) 测试:\n');
|
||||
|
||||
const results = [];
|
||||
for (const route of ROUTES) {
|
||||
const errors = [];
|
||||
const stacks = [];
|
||||
const pageErrs = [];
|
||||
page.removeAllListeners('console');
|
||||
page.removeAllListeners('pageerror');
|
||||
page.on('console', m => {
|
||||
const t = m.text();
|
||||
if (t.startsWith('CK_ERROR:')) errors.push(t.slice(0, 100));
|
||||
if (t.startsWith('CK_STACK:')) {
|
||||
const proj = t.match(/flutter_wisetronic\/[^\s]+:\d+/g);
|
||||
if (proj) stacks.push([...new Set(proj)].slice(0, 2));
|
||||
}
|
||||
});
|
||||
page.on('pageerror', e => pageErrs.push(e.message.slice(0, 100)));
|
||||
try {
|
||||
await page.goto(BASE + route, { waitUntil: 'load', timeout: 30000 });
|
||||
await page.waitForTimeout(5000);
|
||||
} catch (e) { errors.push('GOTO: ' + e.message.slice(0, 70)); }
|
||||
|
||||
const allErr = [...errors, ...pageErrs];
|
||||
const status = allErr.length === 0 ? '✓ OK' : `✗ ${allErr.length}`;
|
||||
console.log(`${status.padEnd(10)} ${route}`);
|
||||
if (allErr.length) {
|
||||
console.log(` ${allErr[0].slice(0, 100)}`);
|
||||
if (stacks.length) console.log(` 位置: ${JSON.stringify(stacks[0])}`);
|
||||
}
|
||||
results.push({ route, errors: allErr, stacks });
|
||||
}
|
||||
|
||||
const failed = results.filter(r => r.errors.length > 0);
|
||||
console.log(`\n========== 汇总 ==========`);
|
||||
console.log(`${results.length} 路由 (mobile),${failed.length} 个有错误`);
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,22 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
// 注入 _test_token(addInitScript 在每个页面加载前执行)
|
||||
await ctx.addInitScript(() => { localStorage['_test_token'] = '6477108200|8818'; });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForTimeout(8000); // 等 autologin
|
||||
const ok = logs.filter(l => l.includes('CK_AUTOLOGIN_OK'));
|
||||
const fail = logs.filter(l => l.includes('CK_AUTOLOGIN_FAIL'));
|
||||
console.log('autologin:', ok.length ? '✓ OK' : (fail.length ? '✗ FAIL '+fail[0].slice(0,100) : '? 未触发'));
|
||||
// 访问 /me(登录态)
|
||||
await page.goto('http://127.0.0.1:8099/me', { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(6000);
|
||||
const errs = logs.filter(l => l.startsWith('CK_ERROR') || l.includes('PAGEERROR'));
|
||||
console.log('/me 错误数:', errs.length);
|
||||
if (errs.length) console.log(' ', errs[0].slice(0,150));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,16 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
await ctx.addInitScript(() => { localStorage['_test_token'] = '6477108200|8818'; });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForTimeout(10000);
|
||||
// 确认 localStorage 注入 + autologin 日志
|
||||
const ls = await page.evaluate(() => localStorage['_test_token']);
|
||||
console.log('localStorage 注入:', ls ? '✓' : '✗');
|
||||
console.log('autologin 日志:', logs.filter(l=>l.includes('CK_AUTOLOGIN')).join(' | ') || '(无)');
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,20 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
await ctx.addInitScript(() => { localStorage['_test_token'] = '6477108200|8818'; });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
// 第一次访问触发编译
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForSelector('canvas', { timeout: 60000 });
|
||||
await page.waitForTimeout(3000);
|
||||
console.log('编译完成 canvas:', await page.locator('canvas').count());
|
||||
// reload 让 main() 重跑(devAutologin)
|
||||
logs.length = 0;
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(10000);
|
||||
console.log('reload 后 autologin 日志:', logs.filter(l=>l.includes('CK_AUTOLOGIN')).join(' | ') || '(无)');
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,17 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
await ctx.addInitScript(() => { localStorage['_test_token'] = '6477108200|8818'; });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
page.on('response', r => { if (r.url().includes('access_token')) console.log('登录API:', r.status()); });
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForSelector('canvas', { timeout: 60000 });
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(18000);
|
||||
console.log('所有 CK_AUTOLOGIN 日志:');
|
||||
logs.filter(l=>l.includes('AUTOLOGIN')).forEach(l => console.log(' ', l.slice(0,120)));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,16 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
await ctx.addInitScript(() => { localStorage['_test_token'] = '6477108200|8818'; });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
page.on('response', r => { if (r.url().includes('access_token')) console.log(' 登录API:', r.status()); });
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForSelector('canvas', { timeout: 60000 });
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(25000);
|
||||
console.log('autologin 日志:', logs.filter(l=>l.includes('CK_AUTOLOGIN')).join(' | ') || '(无)');
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,18 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
await ctx.addInitScript(() => { localStorage['_test_token'] = '6477108200|8818'; });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', m => logs.push(m.text()));
|
||||
page.on('request', r => { if (r.url().includes('access_token')) console.log(' →请求发出:', r.url().slice(-40)); });
|
||||
page.on('response', r => { if (r.url().includes('access_token')) console.log(' ←响应:', r.status()); });
|
||||
page.on('requestfailed', r => { if (r.url().includes('access_token') || r.url().includes('api.minipos')) console.log(' ✗请求失败:', r.url().slice(-50), r.failure()?.errorText); });
|
||||
await page.goto('http://127.0.0.1:8099/', { waitUntil: 'load', timeout: 60000 });
|
||||
await page.waitForSelector('canvas', { timeout: 90000 });
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForTimeout(15000); // 等 3s 延迟 + autologin
|
||||
console.log('autologin 日志:', logs.filter(l=>l.includes('CK_AUTOLOGIN')).join(' | ') || '(无)');
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,16 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await (await browser.newContext({viewport:{width:1280,height:900}})).newPage();
|
||||
const errs = [];
|
||||
page.on('console', m => { if (m.text().includes('RangeError')||m.text().startsWith('CK_ERROR')) errs.push(m.text().slice(0,120)); });
|
||||
page.on('response', r => { if (r.url().includes('get-buy-service')) console.log(' API:', r.url().split('get-buy-service')[1], '→', r.status()); });
|
||||
// 试几个 service 参数
|
||||
for (const p of ['/buy-service/1/ministore', '/buy-service/1/igoshow', '/buy-service/310/ministore']) {
|
||||
errs.length = 0;
|
||||
await page.goto('http://127.0.0.1:8099'+p, { waitUntil: 'load' }).catch(()=>{});
|
||||
await page.waitForTimeout(6000);
|
||||
console.log(p, errs.length ? '✗ '+errs[0] : '✓ OK');
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
Reference in New Issue
Block a user