99 行
6.1 KiB
JavaScript
99 行
6.1 KiB
JavaScript
const fs = require('fs');
|
||
const path = require('path');
|
||
const { execSync } = require('child_process');
|
||
|
||
let passed = 0;
|
||
let failed = 0;
|
||
|
||
function assert(condition, testName, detail = '') {
|
||
if (condition) {
|
||
console.log(` [PASS] ${testName}`);
|
||
passed++;
|
||
} else {
|
||
console.error(` [FAIL] ${testName} - ${detail}`);
|
||
failed++;
|
||
}
|
||
}
|
||
|
||
console.log('========================================================');
|
||
console.log(' 商品详情 pages/goods/index 展示缺陷全链路验证规范');
|
||
console.log('========================================================\n');
|
||
|
||
const rootDir = path.resolve(__dirname, '..');
|
||
|
||
// 1. 本地前端代码检查
|
||
console.log('一、本地前端代码检查:');
|
||
const indexVuePath = path.join(rootDir, 'uniapp/pages/goods/index.vue');
|
||
const indexVueContent = fs.readFileSync(indexVuePath, 'utf8');
|
||
|
||
assert(indexVueContent.includes('goodsInfo: null,'), 'index.vue 中 goodsInfo 初始值明确声明为 null (完美呼应缺省判断)');
|
||
assert(indexVueContent.includes('state.skeletonLoading = false;') && indexVueContent.includes('finally {'), 'index.vue getGoodsDetail 包含 try...finally 确保骨架屏无论成功失败均解除死锁');
|
||
assert(indexVueContent.includes('sheep.$helper.toast('), 'index.vue 参数缺失时采用合法 sheep.$helper.toast 代替未定义 toast');
|
||
assert(indexVueContent.includes('sheep.$router.back('), 'index.vue 参数缺失时采用合法 sheep.$router.back 代替未定义 $router');
|
||
assert(indexVueContent.includes('options?.id || options?.goods_id'), 'index.vue 支持 id 与 goods_id 双参数兼容解析');
|
||
|
||
const cardVuePath = path.join(rootDir, 'uniapp/pages/goods/components/detail/detail-content-card.vue');
|
||
const cardVueContent = fs.readFileSync(cardVuePath, 'utf8');
|
||
|
||
assert(cardVueContent.includes(':domain="sheep.$url.cdn()"'), 'detail-content-card.vue mp-html 明确绑定 :domain 提供微信小程序 CDN 图片前缀补全');
|
||
assert(cardVueContent.includes('暂无商品图文详情'), 'detail-content-card.vue 包含内容为空时的优雅兜底提示');
|
||
|
||
// 2. 本地后端代码检查
|
||
console.log('\n二、本地后端控制器检查:');
|
||
const goodsPhpPath = path.join(rootDir, 'addons/shopro/controller/goods/Goods.php');
|
||
const goodsPhpContent = fs.readFileSync(goodsPhpPath, 'utf8');
|
||
|
||
assert(goodsPhpContent.includes('cdnurl(\'\', true)'), 'Goods.php detail 接口包含获取 CDN 完整域名逻辑');
|
||
assert(goodsPhpContent.includes('preg_replace') && goodsPhpContent.includes('uploads'), 'Goods.php 自动将富文本内容中 /uploads/ 补齐为完整 CDN 绝对路径');
|
||
|
||
// 3. 构建产物检查
|
||
console.log('\n三、小程序编译构建产物检查:');
|
||
const buildCardJsPath = path.join(rootDir, 'uniapp/unpackage/dist/build/mp-weixin/pages/goods/components/detail/detail-content-card.js');
|
||
const buildCardJsContent = fs.readFileSync(buildCardJsPath, 'utf8');
|
||
assert(buildCardJsContent.includes('domain:t.sheep.$url.cdn()'), 'build mp-weixin detail-content-card.js 已生效 domain 属性');
|
||
|
||
const buildIndexJsPath = path.join(rootDir, 'uniapp/unpackage/dist/build/mp-weixin/pages/goods/index.js');
|
||
const buildIndexJsContent = fs.readFileSync(buildIndexJsPath, 'utf8');
|
||
assert(buildIndexJsContent.includes('goodsInfo:null') && buildIndexJsContent.includes('finally{n.skeletonLoading=!1}'), 'build mp-weixin index.js 已生效 goodsInfo:null 与骨架屏 finally 解锁');
|
||
|
||
// 4. 线上 API 模拟测试
|
||
console.log('\n四、线上 API 模拟测试(正常场景、业务失败场景、参数错误场景):');
|
||
try {
|
||
const remoteScript = `#!/bin/bash
|
||
curl -s -k --resolve henglian.gynhc.com:443:127.0.0.1 -H 'platform: WechatMiniProgram' 'https://henglian.gynhc.com/addons/shopro/goods.goods/detail?id=35' > /tmp/api_test_goods_35.json
|
||
curl -s -k --resolve henglian.gynhc.com:443:127.0.0.1 -H 'platform: WechatMiniProgram' 'https://henglian.gynhc.com/addons/shopro/goods.goods/detail?id=99999' > /tmp/api_test_goods_notfound.json
|
||
curl -s -k --resolve henglian.gynhc.com:443:127.0.0.1 -H 'platform: WechatMiniProgram' 'https://henglian.gynhc.com/addons/shopro/goods.goods/detail' > /tmp/api_test_goods_empty.json
|
||
`;
|
||
fs.writeFileSync('/tmp/test_remote_goods.sh', remoteScript);
|
||
execSync("sshpass -p Bjsslxz1 scp -o StrictHostKeyChecking=no /tmp/test_remote_goods.sh root@118.184.172.94:/tmp/test_remote_goods.sh");
|
||
execSync("sshpass -p Bjsslxz1 ssh -o StrictHostKeyChecking=no root@118.184.172.94 'bash /tmp/test_remote_goods.sh'");
|
||
|
||
const fetchCmd = "sshpass -p Bjsslxz1 ssh -o StrictHostKeyChecking=no root@118.184.172.94 \"cat /tmp/api_test_goods_35.json && echo '===SEP===' && cat /tmp/api_test_goods_notfound.json && echo '===SEP===' && cat /tmp/api_test_goods_empty.json\"";
|
||
const fetched = execSync(fetchCmd, { timeout: 20000 }).toString();
|
||
const parts = fetched.split('===SEP===').map(p => p.trim());
|
||
|
||
const okRes = JSON.parse(parts[0]);
|
||
const notFoundRes = JSON.parse(parts[1]);
|
||
const emptyRes = JSON.parse(parts[2]);
|
||
|
||
assert(okRes.code === 1, '正常场景: 商品 35 详情接口请求成功 (code === 1)');
|
||
assert(okRes.data && okRes.data.id === 35, '正常场景: 返回有效商品数据 ID 35');
|
||
assert(okRes.data.title === '贵州飞天盛世-窖藏', '正常场景: 商品标题正确回显');
|
||
assert(okRes.data.content && okRes.data.content.includes('https://henglian.gynhc.com/uploads/'), '正常场景: 富文本图片已自动补全完整 CDN 绝对路径');
|
||
|
||
assert(notFoundRes.code === 0, '业务失败场景: 不存在的商品返回 code === 0');
|
||
assert(notFoundRes.msg === 'No Results were found' || notFoundRes.msg.includes('未找到') || notFoundRes.msg.includes('不存在'), '业务失败场景: 规范返回未找到商品消息');
|
||
|
||
assert(emptyRes.code === 0, '参数错误场景: 缺少商品 ID 时返回 code === 0');
|
||
} catch (err) {
|
||
console.error('线上测试发生异常:', err.message);
|
||
}
|
||
|
||
console.log('\n========================================================');
|
||
console.log(` 全部测试完成:通过 ${passed} 项,失败 ${failed} 项`);
|
||
console.log('========================================================\n');
|
||
|
||
if (failed > 0) {
|
||
process.exit(1);
|
||
}
|