179 行
7.2 KiB
JavaScript
179 行
7.2 KiB
JavaScript
/**
|
||
* tests/partner/verify_withdraw_methods_visibility_spec.js
|
||
*
|
||
* 验证提现方式选择弹窗(account-type-select.vue)与提现主页(withdraw.vue)
|
||
* “如果后台设置关闭的 这里隐藏掉 只有开启才显示” 业务规范自动化测试:
|
||
*
|
||
* 1. 组件源码结构静态检查(filteredTypeList 仅渲染后台开启的提现渠道)
|
||
* 2. 彻底废除旧的 disabled 灰色禁用行显示,改为未开启方式完全不渲染
|
||
* 3. 提现主页面关闭渠道自愈与单渠道智能预选逻辑验证
|
||
* 4. 各种后台配置场景模拟(单银行卡、微信+银行卡、全开、全关等)
|
||
* 5. API 模拟测试(正常场景、业务拒绝场景、参数边界场景)
|
||
*/
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const assert = require('assert');
|
||
|
||
console.log('========================================================');
|
||
console.log(' 验证:提现方式选择弹窗后台关闭项完全隐藏专项测试');
|
||
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++;
|
||
}
|
||
}
|
||
|
||
const rootDir = path.resolve(__dirname, '../../');
|
||
|
||
// 1. 验证 uniapp/pages/pay/components/account-type-select.vue
|
||
runTest('account-type-select.vue 源码实现 filteredTypeList 动态过滤与彻底隐藏未开启项', () => {
|
||
const file = path.join(rootDir, 'uniapp/pages/pay/components/account-type-select.vue');
|
||
assert.ok(fs.existsSync(file), 'account-type-select.vue 文件存在');
|
||
const content = fs.readFileSync(file, 'utf-8');
|
||
|
||
// 验证 template 中通过 filteredTypeList 遍历,而非全量 typeList
|
||
assert.ok(content.includes('v-for="(item, index) in filteredTypeList"'), '必须遍历 filteredTypeList');
|
||
assert.ok(!content.includes(':disabled="!methods.includes(item.value)"'), '严禁以 disabled 形式展示未开启的灰色提现项');
|
||
|
||
// 验证 computed 定义与过滤逻辑
|
||
assert.ok(content.includes('const filteredTypeList = computed('), '必须包含 filteredTypeList 计算属性');
|
||
assert.ok(content.includes('props.methods.includes(item.value)'), '必须根据 props.methods 严格过滤项');
|
||
|
||
// 验证空状态提示
|
||
assert.ok(content.includes('暂无可用的提现方式'), '包含全关闭时的友好兜底提示');
|
||
|
||
// 验证响应式同步
|
||
assert.ok(content.includes('function syncCurrentValue('), '包含有效提现方式同步校验函数');
|
||
});
|
||
|
||
// 2. 验证 uniapp/pages/pay/withdraw.vue 协同自愈逻辑
|
||
runTest('withdraw.vue 规则加载后自动清理后台已关闭的提现方式并支持单方式预选', () => {
|
||
const file = path.join(rootDir, 'uniapp/pages/pay/withdraw.vue');
|
||
assert.ok(fs.existsSync(file), 'withdraw.vue 文件存在');
|
||
const content = fs.readFileSync(file, 'utf-8');
|
||
|
||
// 验证对已失效提现方式的清理逻辑
|
||
assert.ok(
|
||
content.includes('!data.methods.includes(state.accountInfo.type)'),
|
||
'当后台关闭某提现方式时,清空当前失效选中'
|
||
);
|
||
// 验证单提现方式自动预选
|
||
assert.ok(
|
||
content.includes('data.methods.length === 1'),
|
||
'当后台仅开启1种提现方式时,自动预选该方式'
|
||
);
|
||
});
|
||
|
||
// 3. 核心算法模拟测试:矩阵过滤验证
|
||
const allTypes = [
|
||
{ icon: 'wechat.png', title: '微信零钱', value: 'wechat' },
|
||
{ icon: 'alipay.png', title: '支付宝账户', value: 'alipay' },
|
||
{ icon: 'bank.png', title: '银行卡转账', value: 'bank' },
|
||
];
|
||
|
||
function filterTypeList(methods) {
|
||
if (!Array.isArray(methods) || methods.length === 0) return [];
|
||
return allTypes.filter((item) => methods.includes(item.value));
|
||
}
|
||
|
||
runTest('场景1(如用户截图):后台仅开启银行卡,微信与支付宝必须完全隐藏', () => {
|
||
const result = filterTypeList(['bank']);
|
||
assert.strictEqual(result.length, 1);
|
||
assert.strictEqual(result[0].value, 'bank');
|
||
assert.strictEqual(result[0].title, '银行卡转账');
|
||
const hasWechat = result.some((r) => r.value === 'wechat');
|
||
const hasAlipay = result.some((r) => r.value === 'alipay');
|
||
assert.strictEqual(hasWechat, false, '微信零钱必须完全隐藏');
|
||
assert.strictEqual(hasAlipay, false, '支付宝账户必须完全隐藏');
|
||
});
|
||
|
||
runTest('场景2:后台开启微信和银行卡,支付宝关闭,仅显示微信和银行卡', () => {
|
||
const result = filterTypeList(['wechat', 'bank']);
|
||
assert.strictEqual(result.length, 2);
|
||
assert.deepStrictEqual(result.map(r => r.value), ['wechat', 'bank']);
|
||
const hasAlipay = result.some((r) => r.value === 'alipay');
|
||
assert.strictEqual(hasAlipay, false, '支付宝账户必须完全隐藏');
|
||
});
|
||
|
||
runTest('场景3:后台开启全部三种提现方式,三者均正常显示', () => {
|
||
const result = filterTypeList(['wechat', 'alipay', 'bank']);
|
||
assert.strictEqual(result.length, 3);
|
||
assert.deepStrictEqual(result.map(r => r.value), ['wechat', 'alipay', 'bank']);
|
||
});
|
||
|
||
runTest('场景4:后台关闭所有提现方式,列表为空且触发兜底', () => {
|
||
const result1 = filterTypeList([]);
|
||
assert.strictEqual(result1.length, 0);
|
||
|
||
const result2 = filterTypeList(null);
|
||
assert.strictEqual(result2.length, 0);
|
||
});
|
||
|
||
// 4. API 模拟测试
|
||
runTest('API 模拟测试:pay/withdraw/rules 返回仅包含 bank 的提现规则', () => {
|
||
const mockRulesResponse = {
|
||
code: 1,
|
||
msg: '提现规则',
|
||
data: {
|
||
status: 1,
|
||
is_withdraw_open: true,
|
||
min_amount: '1.00',
|
||
max_amount: '50000.00',
|
||
charge_rate_format: 0.5,
|
||
charge_rate: 0.005,
|
||
methods: ['bank'],
|
||
auto_arrival: 0,
|
||
}
|
||
};
|
||
|
||
assert.strictEqual(mockRulesResponse.code, 1);
|
||
assert.deepStrictEqual(mockRulesResponse.data.methods, ['bank']);
|
||
const visible = filterTypeList(mockRulesResponse.data.methods);
|
||
assert.strictEqual(visible.length, 1);
|
||
assert.strictEqual(visible[0].value, 'bank');
|
||
});
|
||
|
||
runTest('API 模拟测试:提现申请发起时若渠道已被关闭,服务端拦截', () => {
|
||
const mockConfig = { status: 1, methods: ['bank'] };
|
||
function simulateApply(type) {
|
||
if (!mockConfig.methods.includes(type)) {
|
||
return { code: 3001, message: '暂不支持该提现方式' };
|
||
}
|
||
return { code: 0, message: '操作成功', data: { withdraw_sn: 'W123456789' } };
|
||
}
|
||
|
||
const resWechat = simulateApply('wechat');
|
||
assert.strictEqual(resWechat.code, 3001);
|
||
assert.strictEqual(resWechat.message, '暂不支持该提现方式');
|
||
|
||
const resBank = simulateApply('bank');
|
||
assert.strictEqual(resBank.code, 0);
|
||
assert.strictEqual(resBank.data.withdraw_sn, 'W123456789');
|
||
});
|
||
|
||
runTest('API 模拟测试:参数异常(非法提现方式字符串)前端与接口防穿透拦截', () => {
|
||
const allowed = ['bank'];
|
||
const testInput = 'malicious_eval_type';
|
||
const isAllowed = allowed.includes(testInput);
|
||
assert.strictEqual(isAllowed, false);
|
||
});
|
||
|
||
console.log('\n========================================================');
|
||
console.log(`测试完成: 成功 ${passCount} 项, 失败 ${failCount} 项`);
|
||
console.log('========================================================');
|
||
|
||
if (failCount > 0) {
|
||
process.exit(1);
|
||
}
|