/** * tests/partner/verify_madow_switch_spec.js * * 验证“我的dmepa”页面马币市值指示胶囊后台开关: * 1. 后端配置读取与默认值向后兼容逻辑验证 * 2. 移动端前台显隐条件判定逻辑(开启、关闭、未配置)验证 * 3. 接口数据契约与统一返回结构校验 * 4. 关键文件代码变更完整性静态检查 */ const fs = require('fs'); const path = require('path'); const assert = require('assert'); console.log('===================================================='); console.log(' 开始验证:“我的dmepa”马币市值指示胶囊后台显隐开关'); console.log('====================================================\n'); let passCount = 0; let failCount = 0; function runTest(title, fn) { try { fn(); console.log(`[PASS] ${title}`); passCount++; } catch (err) { console.error(`[FAIL] ${title}`); console.error(` 错误原因: ${err.message}`); failCount++; } } // ---------------------------------------------------- // 1. 测试后端 showMadowRate 配置降级与逻辑映射 // ---------------------------------------------------- function mockShowMadowRate(configMap) { const get = (key, defaultVal) => (configMap[key] !== undefined ? configMap[key] : defaultVal); const show = get('partner_show_madow_rate', 1); if (show === null || show === '') { return 1; } return parseInt(show, 10) ? 1 : 0; } runTest('配置未设置或为 null/空字符时,向后兼容默认返回 1 (显示)', () => { assert.strictEqual(mockShowMadowRate({}), 1); assert.strictEqual(mockShowMadowRate({ partner_show_madow_rate: null }), 1); assert.strictEqual(mockShowMadowRate({ partner_show_madow_rate: '' }), 1); }); runTest('配置显式设置为 0 时,返回 0 (隐藏)', () => { assert.strictEqual(mockShowMadowRate({ partner_show_madow_rate: 0 }), 0); assert.strictEqual(mockShowMadowRate({ partner_show_madow_rate: '0' }), 0); }); runTest('配置显式设置为 1 时,返回 1 (显示)', () => { assert.strictEqual(mockShowMadowRate({ partner_show_madow_rate: 1 }), 1); assert.strictEqual(mockShowMadowRate({ partner_show_madow_rate: '1' }), 1); }); // ---------------------------------------------------- // 2. 测试移动端 madow.vue 显隐条件判定 // ---------------------------------------------------- function evalRateCapsuleVisible(showMadowRate) { return showMadowRate !== false && showMadowRate !== 0 && showMadowRate !== '0'; } runTest('移动端当 show_madow_rate 为 1 / "1" / true 时判定展示', () => { assert.strictEqual(evalRateCapsuleVisible(1), true); assert.strictEqual(evalRateCapsuleVisible('1'), true); assert.strictEqual(evalRateCapsuleVisible(true), true); }); runTest('移动端当 show_madow_rate 为 0 / "0" / false 时判定隐藏', () => { assert.strictEqual(evalRateCapsuleVisible(0), false); assert.strictEqual(evalRateCapsuleVisible('0'), false); assert.strictEqual(evalRateCapsuleVisible(false), false); }); runTest('移动端当 show_madow_rate 为 undefined / null 时保底默认展示', () => { assert.strictEqual(evalRateCapsuleVisible(undefined), true); assert.strictEqual(evalRateCapsuleVisible(null), true); }); // ---------------------------------------------------- // 3. 接口数据返回结构校验 // ---------------------------------------------------- runTest('madow 接口返回 payload 需包含 show_madow_rate 字段', () => { const mockApiResponse = { code: 1, msg: 'ok', data: { madow: '100.0000', madow_rate: '0.000007865', madow_value: '0.00', show_madow_rate: 1, list: [] } }; assert.strictEqual(mockApiResponse.code, 1); assert.strictEqual(typeof mockApiResponse.data.show_madow_rate, 'number'); assert.strictEqual(mockApiResponse.data.show_madow_rate, 1); assert.strictEqual(evalRateCapsuleVisible(mockApiResponse.data.show_madow_rate), true); }); // ---------------------------------------------------- // 4. 源码静态变更完整性检查 // ---------------------------------------------------- const rootDir = path.resolve(__dirname, '../../'); runTest('检查 application/admin/controller/shopro/Config.php 包含 partner_show_madow_rate', () => { const file = path.join(rootDir, 'application/admin/controller/shopro/Config.php'); const content = fs.readFileSync(file, 'utf-8'); assert.ok(content.includes('partner_show_madow_rate'), 'Config.php 必须包含 partner_show_madow_rate 字段'); assert.ok(content.includes("ShoproConfig::where('code', 'shop.commission.partner_show_madow_rate')"), 'Config.php 必须包含自愈检查逻辑'); }); runTest('检查 application/admin/view/shopro/config/index.html 包含开关组件', () => { const file = path.join(rootDir, 'application/admin/view/shopro/config/index.html'); const content = fs.readFileSync(file, 'utf-8'); assert.ok(content.includes('form.model.partner_show_madow_rate'), 'index.html 必须绑定 partner_show_madow_rate'); assert.ok(content.includes('显示马币市值'), 'index.html 必须包含表单中文标签'); }); runTest('检查 public/assets/js/backend/shopro/config.js 包含初始化', () => { const file = path.join(rootDir, 'public/assets/js/backend/shopro/config.js'); const content = fs.readFileSync(file, 'utf-8'); assert.ok(content.includes('form.model.partner_show_madow_rate'), 'config.js 必须包含 partner_show_madow_rate 初始化'); }); runTest('检查 addons/shopro/service/partner/PartnerConfig.php 包含 showMadowRate 方法', () => { const file = path.join(rootDir, 'addons/shopro/service/partner/PartnerConfig.php'); const content = fs.readFileSync(file, 'utf-8'); assert.ok(content.includes('public static function showMadowRate()'), 'PartnerConfig 必须包含 showMadowRate 方法'); }); runTest('检查 addons/shopro/controller/partner/Index.php 包含 show_madow_rate 输出', () => { const file = path.join(rootDir, 'addons/shopro/controller/partner/Index.php'); const content = fs.readFileSync(file, 'utf-8'); assert.ok(content.includes("'show_madow_rate' => PartnerConfig::showMadowRate()"), 'Index.php 必须在 madow 中返回 show_madow_rate'); }); runTest('检查 uniapp/pages/commission/madow.vue 包含条件渲染与状态绑定', () => { const file = path.join(rootDir, 'uniapp/pages/commission/madow.vue'); const content = fs.readFileSync(file, 'utf-8'); assert.ok(content.includes('state.info.show_madow_rate'), 'madow.vue 必须使用 state.info.show_madow_rate'); assert.ok(content.includes('v-if="state.info.show_madow_rate'), 'madow.vue 的 rate-capsule 必须绑定 v-if'); }); runTest('检查 addons/shopro/upgrade/partner_v2.sql 包含 SQL 种子', () => { const file = path.join(rootDir, 'addons/shopro/upgrade/partner_v2.sql'); const content = fs.readFileSync(file, 'utf-8'); assert.ok(content.includes('shop.commission.partner_show_madow_rate'), 'partner_v2.sql 必须包含配置初始行'); }); console.log('\n===================================================='); console.log(`测试完成: 成功 ${passCount} 项, 失败 ${failCount} 项`); console.log('===================================================='); if (failCount > 0) { process.exit(1); }