Files
lucky_shop/tests/_v.js
ZF sun 55a9ed5946 refactor: 重构底部导航高亮逻辑,新增跨平台兼容与工具脚本
1. 抽离底部导航归属逻辑为纯函数resolveEntryTab,优化分包页面高亮规则,对齐iOS HIG规范
2. 新增页面Tab通配符映射,简化PAGE_TAB_MAP维护
3. 修复鸿蒙平台webview返回按钮适配问题
4. 新增pages.json校验与导航标题自动注入脚本
5. 优化启动页动画性能,提升合成渲染效率
6. 修复diy-picture组件v-for缺少key的警告
7. 清理鸿蒙平台多余的分包构建配置
8. 更新统计数据与store注释说明
2026-07-16 11:47:29 +08:00

173 lines
10 KiB
JavaScript

'use strict';
// 验证新测试文件全过
const fs = require('fs'), path = require('path'), vm = require('vm'), assert = require('assert');
const TABBAR_SRC = fs.readFileSync(path.join(__dirname, '..', 'common', 'js', 'util-tabbar.js'), 'utf8');
const moduleObj = { exports: {} };
const wrapped = TABBAR_SRC
.replace(/^import\s+.*$/gm, '// x')
.replace(/^export\s+function\s+/gm, 'function ')
.replace(/^export\s+const\s+/gm, 'const ')
.replace(/^export\s+let\s+/gm, 'let ')
.replace(/^export\s+var\s+/gm, 'var ')
+ '\nmodule.exports = { resolvePageUrl, isPageInRouteList, resolveLinkUrl, makeSystemTabBarList, adaptSubpackageUrl, isTabBarRootPage, isCurrentPageTabBarRoot, checkTabBarActive, resolveEntryTab, lookupPageTabMap };\n';
const sb = { console, globalThis: {}, module: moduleObj, exports: moduleObj.exports };
sb.globalThis = sb; vm.createContext(sb); vm.runInContext(wrapped, sb);
const { resolveEntryTab, checkTabBarActive, lookupPageTabMap } = sb.module.exports;
const HOME='/pages/index/index', CATEGORY='/pages_goods/category', CART='/pages_goods/cart',
MERCHANT='/pages_promotion/merch/merchcategory', MEMBER='/pages_tool/member/index',
DIY='/pages_tool/index/diy', MEMBER_INFO='/pages_tool/member/info',
MEMBER_COUPON='/pages_tool/member/coupon', MEMBER_BALANCE='/pages_tool/member/balance',
GOODS_LIST='/pages_goods/list', GOODS_DETAIL='/pages_goods/detail',
SEARCH='/pages_tool/goods/search', BRAND='/pages_tool/goods/brand',
HELP_LIST='/pages_tool/help/list', NOTICE_LIST='/pages_tool/notice/list',
FILES_LIST='/pages_tool/files/list', FENXIAO='/pages_promotion/fenxiao/index',
LOGIN='/pages_tool/login/login', PAY='/pages_tool/pay/index', WEBVIEW='/pages_tool/webview/webview';
const tabBarList = { list: [
{ id:1, text:'首页', link:{wap_url:HOME} }, { id:2, text:'分类', link:{wap_url:CATEGORY} },
{ id:3, text:'购物车', link:{wap_url:CART} }, { id:4, text:'商户', link:{wap_url:MERCHANT} },
{ id:5, text:'我的', link:{wap_url:MEMBER} },
]};
const PAGE_TAB_MAP = {
[HOME]:HOME, '/pages/empty/index':HOME, [HELP_LIST]:HOME, [NOTICE_LIST]:HOME,
[CATEGORY]:CATEGORY, [GOODS_LIST]:CATEGORY, [GOODS_DETAIL]:CATEGORY,
[MERCHANT]:MERCHANT, '/pages_promotion/merch/detail':MERCHANT,
[CART]:CART, [MEMBER]:MEMBER, '/pages_order/list':MEMBER,
'/pages_tool/recharge/list':MEMBER, '/pages_tool/recharge/order_list':MEMBER,
[FILES_LIST]:null,
};
const PAGE_TAB_WILDCARDS = [
{ pattern: '/pages_tool/member/*', tab: MEMBER },
{ pattern: '/pages_tool/goods/*', tab: CATEGORY },
];
const NO_TAB_PAGES = new Set([LOGIN, PAY, WEBVIEW, '/pages_tool/ai-chat/index', '/pages_tool/vr/index', '/pages_tool/cases/index']);
const findH = (cur, hist, tbl = tabBarList) => {
const e = resolveEntryTab({currentPageRoute:cur, tabBarList:tbl, historyRoutes:hist, noTabPages:NO_TAB_PAGES});
const pkg = cur.split('?')[0].split('/')[1] || '';
const pet = e ? {[pkg]:e} : {};
for (const t of tbl.list) {
if (checkTabBarActive(t.link.wap_url, cur, tbl, pet, {}, {noTabPages:NO_TAB_PAGES, pageTabMap:PAGE_TAB_MAP, pageTabWildcards:PAGE_TAB_WILDCARDS}))
return t.link.wap_url;
}
return null;
};
let p=0, f=0;
const T = (l, fn) => { try { fn(); p++; } catch(e) { f++; console.log(' FAIL', l, '|', e.message.slice(0,80)); } };
const A = (a, e, l) => assert.equal(a, e, `[${l}] 期望 ${e||'不点亮'}, 实际 ${a||'不点亮'}`);
// 场景 1
T('1.1 home→diy', () => A(findH(DIY, [HOME]), HOME, '1.1'));
T('1.2 category→diy', () => A(findH(DIY, [CATEGORY]), CATEGORY, '1.2'));
T('1.3 merchant→diy', () => A(findH(DIY, [MERCHANT]), MERCHANT, '1.3'));
T('1.4 home→list→diy', () => A(findH(DIY, [HOME, GOODS_LIST]), HOME, '1.4'));
T('1.5 home→login→diy', () => A(findH(DIY, [HOME, LOGIN]), HOME, '1.5'));
// 场景 2
T('2.1 list→detail', () => A(findH(GOODS_DETAIL, [GOODS_LIST]), CATEGORY, '2.1'));
T('2.2 coupon→info', () => A(findH(MEMBER_INFO, [MEMBER_COUPON]), MEMBER, '2.2'));
T('2.3 home→member→coupon→info', () => A(findH(MEMBER_INFO, [HOME, MEMBER, MEMBER_COUPON]), MEMBER, '2.3'));
// 场景 3
T('3.1 home→category tab', () => A(findH(CATEGORY, [HOME]), CATEGORY, '3.1'));
T('3.2 diy→member tab', () => A(findH(MEMBER, [DIY]), MEMBER, '3.2'));
T('3.3 diy→home tab', () => A(findH(HOME, [DIY]), HOME, '3.3'));
T('3.4 链式', () => {
const tabs = [HOME, CATEGORY, MEMBER, CART, HOME];
for (let i = 0; i < tabs.length; i++) {
assert.equal(findH(tabs[i], tabs.slice(0, i)), tabs[i], `链式${i+1}`);
}
});
// 场景 4
T('4.1 home→search', () => A(findH(SEARCH, [HOME]), CATEGORY, '4.1'));
T('4.2 home→brand', () => A(findH(BRAND, [HOME]), CATEGORY, '4.2'));
T('4.3 home→member/info', () => A(findH(MEMBER_INFO, [HOME]), MEMBER, '4.3'));
T('4.4 home→coupon', () => A(findH(MEMBER_COUPON, [HOME]), MEMBER, '4.4'));
T('4.5 home→balance', () => A(findH(MEMBER_BALANCE, [HOME]), MEMBER, '4.5'));
T('4.6 category→goods/detail', () => A(findH(GOODS_DETAIL, [CATEGORY]), CATEGORY, '4.6'));
// 场景 5
T('5.1 files/list null', () => A(findH(FILES_LIST, [HOME]), null, '5.1'));
T('5.2 diy 走 history-walk', () => A(findH(DIY, [HOME]), HOME, '5.2'));
T('5.3 diy 不在 PAGE_TAB_MAP', () => assert.equal(PAGE_TAB_MAP[DIY], undefined));
// 场景 6
T('6.1 深链 diy', () => A(findH(DIY, []), null, '6.1'));
T('6.2 深链 diy?id=123', () => A(findH(DIY + '?id=123', []), null, '6.2'));
T('6.3 深链 goods/detail', () => A(findH(GOODS_DETAIL, []), CATEGORY, '6.3'));
T('6.4 深链 member', () => A(findH(MEMBER, []), MEMBER, '6.4'));
T('6.5 深链 fenxiao', () => A(findH(FENXIAO, []), null, '6.5'));
// 场景 7
T('7.1 home→login', () => A(findH(LOGIN, [HOME]), null, '7.1'));
T('7.2 home→list→pay', () => A(findH(PAY, [HOME, GOODS_LIST]), null, '7.2'));
T('7.3 home→login→diy', () => A(findH(DIY, [HOME, LOGIN]), HOME, '7.3'));
T('7.4 home→cart→pay→list', () => A(findH(GOODS_LIST, [HOME, CART, PAY]), HOME, '7.4'));
// 场景 8
T('8.1 from_tab=home', () => assert.equal(checkTabBarActive(HOME, DIY, tabBarList, {}, {from_tab:HOME}, {noTabPages:NO_TAB_PAGES, pageTabMap:{}, pageTabWildcards:[]}), true));
T('8.2 PAGE_TAB_MAP 优先 from_tab', () => assert.equal(checkTabBarActive(MEMBER, DIY, tabBarList, {}, {from_tab:MEMBER}, {noTabPages:NO_TAB_PAGES, pageTabMap:{[DIY]:HOME}, pageTabWildcards:[]}), false));
T('8.3 非法 from_tab', () => assert.equal(checkTabBarActive(HOME, DIY, tabBarList, {}, {from_tab:'/foo/bar'}, {noTabPages:NO_TAB_PAGES, pageTabMap:{}, pageTabWildcards:[]}), false));
// 场景 9
T('9.1 diy 带 query', () => A(findH('/pages_tool/index/diy?id=123&from=share', [HOME]), HOME, '9.1'));
T('9.2 tab 根带 query', () => assert.equal(resolveEntryTab({currentPageRoute:'/pages/index/index?from=share', tabBarList, historyRoutes:[]}), HOME));
T('9.3 老路径', () => assert.equal(resolveEntryTab({currentPageRoute:DIY, tabBarList, historyRoutes:['/pages/member/index']}), MEMBER));
// 场景 10
T('10.1 互斥 1 亮', () => {
const r = tabBarList.list.map(t => checkTabBarActive(t.link.wap_url, DIY, tabBarList, {pages_tool:HOME}, {}, {noTabPages:NO_TAB_PAGES, pageTabMap:PAGE_TAB_MAP, pageTabWildcards:PAGE_TAB_WILDCARDS}));
assert.equal(r.filter(Boolean).length, 1);
});
T('10.2 幂等', () => {
const o = {currentPageRoute:DIY, tabBarList, historyRoutes:[HOME, GOODS_LIST]};
assert.equal(resolveEntryTab(o), resolveEntryTab(o));
assert.equal(resolveEntryTab(o), HOME);
});
// 场景 11
T('11.1 tabBarList 空', () => A(findH(DIY, [HOME], {list:[]}), null, '11.1'));
T('11.2 脏数据', () => {
const dirty = { list: [{id:1,link:null},{id:2,link:{}},{id:3,link:{wap_url:HOME}},{id:4,link:{wap_url:MEMBER}}] };
assert.equal(resolveEntryTab({currentPageRoute:DIY, tabBarList:dirty, historyRoutes:[HOME]}), HOME);
});
T('11.3 history 脏', () => assert.equal(resolveEntryTab({currentPageRoute:DIY, tabBarList, historyRoutes:[null,'',undefined,HOME]}), HOME));
T('11.4 history 非数组', () => assert.equal(resolveEntryTab({currentPageRoute:DIY, tabBarList, historyRoutes:'not-array'}), MEMBER));
T('11.5 深链 unmapped', () => A(findH('/pages_goods/some-unmapped-page', []), null, '11.5'));
// 场景 12
T('12.1 home→diy→member→回→home→diy', () => A(findH(DIY, [HOME]), HOME, '12.1'));
T('12.2 home→diy→点 cart tab', () => A(findH(CART, [HOME]), CART, '12.2'));
T('12.3 登出后 diy', () => A(findH(DIY, [HOME]), HOME, '12.3'));
// 单元 13
T('13.1 tab 根优先', () => assert.equal(resolveEntryTab({currentPageRoute:CATEGORY, tabBarList, historyRoutes:[HOME, MEMBER]}), CATEGORY));
T('13.2 精确', () => assert.equal(lookupPageTabMap(MEMBER, PAGE_TAB_MAP, PAGE_TAB_WILDCARDS), MEMBER));
T('13.3 通配符', () => {
assert.equal(lookupPageTabMap('/pages_tool/member/balance_detail', PAGE_TAB_MAP, PAGE_TAB_WILDCARDS), MEMBER);
assert.equal(lookupPageTabMap('/pages_tool/goods/brand', PAGE_TAB_MAP, PAGE_TAB_WILDCARDS), CATEGORY);
});
T('13.4 null 优先', () => {
const cm = {[FILES_LIST]:null};
const cw = [{pattern:'/pages_tool/files/*', tab: HOME}];
assert.equal(lookupPageTabMap(FILES_LIST, cm, cw), null);
});
T('13.5 无匹配', () => assert.equal(lookupPageTabMap(DIY, PAGE_TAB_MAP, PAGE_TAB_WILDCARDS), undefined));
T('13.6 前缀相近', () => assert.equal(lookupPageTabMap('/pages_tool/members', PAGE_TAB_MAP, PAGE_TAB_WILDCARDS), undefined));
// 兜底 14
T('14.1 非法 link', () => assert.equal(checkTabBarActive('/pages/x', DIY, tabBarList, {pages_tool:HOME}, {from_tab:HOME}, {noTabPages:NO_TAB_PAGES, pageTabMap:PAGE_TAB_MAP, pageTabWildcards:PAGE_TAB_WILDCARDS}), false));
T('14.2 NO_TAB_PAGES 都不亮', () => {
for (const u of [HOME, CATEGORY, CART, MERCHANT, MEMBER]) {
assert.equal(checkTabBarActive(u, LOGIN, tabBarList, {pages:HOME, pages_tool:MEMBER}, {}, {noTabPages:NO_TAB_PAGES, pageTabMap:PAGE_TAB_MAP, pageTabWildcards:PAGE_TAB_WILDCARDS}), false);
}
});
// 场景对照 15
T('15. 8 场景', () => {
const SC = [
['深链扫 diy', DIY, [], null],
['首页 → diy 链接', DIY, [HOME], HOME],
['首页 → 会员中心', MEMBER, [HOME], MEMBER],
['分类 → diy 链接', DIY, [CATEGORY], CATEGORY],
['分类 → 商品列表 → diy', DIY, [CATEGORY, GOODS_LIST], CATEGORY],
['我的 → 优惠券 → diy', DIY, [MEMBER, MEMBER_COUPON], MEMBER],
['首页 → 登录页 → diy', DIY, [HOME, LOGIN], HOME],
['深链扫 member/info', MEMBER_INFO, [], MEMBER],
];
for (const [l, c, h, e] of SC) {
assert.equal(findH(c, h), e, l);
}
});
console.log('通过:', p, '/ 失败:', f);
process.exit(f === 0 ? 0 : 1);