Files
docs/悦/货代SAAS平台后台/tests/verify-fba-product-detail.cjs
T
2026-08-12 19:11:56 +08:00

242 lines
7.4 KiB
JavaScript

#!/usr/bin/env node
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const prototypePath = path.resolve(__dirname, '..', '货代SaaS平台原型.html');
const failures = [];
let html = '';
try {
html = fs.readFileSync(prototypePath, 'utf8');
} catch (error) {
failures.push(`无法读取原型文件:${error.message}`);
}
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function requireMatch(pattern, label, source = html) {
if (!pattern.test(source)) failures.push(`missing ${label}`);
}
function findClosingDelimiter(source, openingIndex, opening, closing) {
let depth = 0;
let quote = '';
let lineComment = false;
let blockComment = false;
for (let index = openingIndex; index < source.length; index += 1) {
const character = source[index];
const next = source[index + 1];
if (lineComment) {
if (character === '\n' || character === '\r') lineComment = false;
continue;
}
if (blockComment) {
if (character === '*' && next === '/') {
blockComment = false;
index += 1;
}
continue;
}
if (quote) {
if (character === '\\') {
index += 1;
} else if (character === quote) {
quote = '';
}
continue;
}
if (character === '/' && next === '/') {
lineComment = true;
index += 1;
continue;
}
if (character === '/' && next === '*') {
blockComment = true;
index += 1;
continue;
}
if (character === "'" || character === '"' || character === '`') {
quote = character;
continue;
}
if (character === opening) depth += 1;
if (character === closing) {
depth -= 1;
if (depth === 0) return index;
}
}
return -1;
}
function getPageSource(pageId) {
const openingTag = new RegExp(
`<div\\b(?=[^>]*\\bclass=["'][^"']*\\bpage\\b)(?=[^>]*\\bid=["']page-${escapeRegExp(pageId)}["'])[^>]*>`,
'i',
).exec(html);
if (!openingTag) return '';
const remainder = html.slice(openingTag.index + openingTag[0].length);
const nextPage = /<div\b(?=[^>]*\bclass=["'][^"']*\bpage\b)(?=[^>]*\bid=["']page-[^"']+["'])[^>]*>/i.exec(remainder);
const end = nextPage
? openingTag.index + openingTag[0].length + nextPage.index
: html.length;
return html.slice(openingTag.index, end);
}
function getFunctionSource(functionName) {
const declaration = new RegExp(
`function\\s+${escapeRegExp(functionName)}\\s*\\([^)]*\\)\\s*\\{`,
).exec(html);
if (!declaration) return '';
const openingBrace = html.indexOf('{', declaration.index);
const closingBrace = findClosingDelimiter(html, openingBrace, '{', '}');
if (closingBrace < 0) return '';
return html.slice(declaration.index, closingBrace + 1);
}
function getArrayValue(variableName) {
const declaration = new RegExp(
`(?:const|let|var)\\s+${escapeRegExp(variableName)}\\s*=\\s*\\[`,
).exec(html);
if (!declaration) return null;
const openingBracket = html.indexOf('[', declaration.index);
const closingBracket = findClosingDelimiter(html, openingBracket, '[', ']');
if (closingBracket < 0) return null;
try {
return vm.runInNewContext(html.slice(openingBracket, closingBracket + 1), Object.create(null));
} catch (error) {
failures.push(`fbaProductData 无法解析:${error.message}`);
return null;
}
}
const pageSource = getPageSource('fba-product-detail');
if (!pageSource) failures.push('missing 页面节点 page-fba-product-detail');
requireMatch(/产品详情/, '产品详情页面标题', pageSource);
for (const id of ['fbd-product-sku', 'fbd-product-category', 'fbd-product-content']) {
requireMatch(new RegExp(`\\bid=["']${escapeRegExp(id)}["']`), `产品详情页面节点 ${id}`, pageSource);
}
requireMatch(/onclick=["']backToFbaProducts\(\)["']/, '产品详情返回入口 backToFbaProducts()', pageSource);
const openSource = getFunctionSource('openFbaProductDetail');
if (!openSource) failures.push('missing openFbaProductDetail 打开函数');
requireMatch(
/\.find\s*\(\s*\(?\s*item\s*\)?\s*=>\s*item\.sku\s*===\s*sku\s*\)/,
'openFbaProductDetail 按 item.sku===sku 查找产品',
openSource,
);
requireMatch(
/showToast\s*\(\s*['"]未找到产品记录['"]\s*,\s*['"]error['"]\s*\)/,
"openFbaProductDetail 未找到时 showToast('未找到产品记录','error')",
openSource,
);
requireMatch(
/showFbaReadOnlyDetail\s*\(\s*['"]fba-product-detail['"]\s*,/,
"openFbaProductDetail 调用 showFbaReadOnlyDetail('fba-product-detail', ...)",
openSource,
);
for (const label of [
'基础信息',
'商品SKU',
'中文品名',
'英文品名',
'产品分类',
'包装信息',
'长(cm)',
'宽(cm)',
'高(cm)',
'重量(kg)',
'包装体积(m³)',
'其他信息',
'创建时间',
'更新时间',
'备注',
]) {
requireMatch(new RegExp(escapeRegExp(label)), `openFbaProductDetail 详情文案“${label}”`, openSource);
}
const rendererSource = getFunctionSource('renderFbaProduct');
requireMatch(
/openFbaProductDetail\(\$\{lmoEsc\(JSON\.stringify\(d\.sku\)\)\}\)/,
'renderFbaProduct 使用 JSON.stringify 和 lmoEsc 安全传入 SKU',
rendererSource,
);
const backSource = getFunctionSource('backToFbaProducts');
if (!backSource) failures.push('missing backToFbaProducts 返回函数');
requireMatch(
/showExistingFbaList\s*\(\s*['"]fba-product['"]\s*\)/,
"backToFbaProducts 调用 showExistingFbaList('fba-product')",
backSource,
);
if (/\bfbprReset\s*\(/.test(backSource)) {
failures.push('backToFbaProducts 不应调用 fbprReset');
}
if (/\brenderFbaProduct\s*\(/.test(backSource)) {
failures.push('backToFbaProducts 不应调用 renderFbaProduct');
}
const productData = getArrayValue('fbaProductData');
if (!Array.isArray(productData) || productData.length === 0) {
failures.push('missing 非空 fbaProductData 数据');
} else {
for (const field of ['createdAt', 'updatedAt', 'remark']) {
if (!productData.every((item) => Object.prototype.hasOwnProperty.call(item, field))) {
failures.push(`fbaProductData 每条记录缺少 ${field} 字段`);
}
}
}
const volumeSource = getFunctionSource('formatFbaProductVolume');
if (!volumeSource) {
failures.push('missing formatFbaProductVolume 函数');
} else {
let formatVolume;
try {
formatVolume = vm.runInNewContext(`(${volumeSource})`, Object.create(null));
} catch (error) {
failures.push(`formatFbaProductVolume 无法在 node:vm 中执行:${error.message}`);
}
if (typeof formatVolume === 'function') {
try {
if (formatVolume({ len: 30, wid: 20, height: 15 }) !== '0.0090') {
failures.push("formatFbaProductVolume({len:30,wid:20,height:15}) 应返回 '0.0090'");
}
const emptyDimensionCases = [
{ wid: 20, height: 15 },
{ len: 30, height: 15 },
{ len: 30, wid: 20 },
{ len: 0, wid: 20, height: 15 },
{ len: 30, wid: 0, height: 15 },
{ len: 30, wid: 20, height: 0 },
];
if (emptyDimensionCases.some((product) => formatVolume(product) !== '—')) {
failures.push("formatFbaProductVolume 缺失或为零的尺寸应返回 '—'");
}
} catch (error) {
failures.push(`formatFbaProductVolume 调用失败:${error.message}`);
}
}
}
if (failures.length) {
console.error('FBA_PRODUCT_DETAIL_CHECK_FAIL');
failures.forEach((failure) => console.error(`- ${failure}`));
process.exitCode = 1;
} else {
console.log('FBA_PRODUCT_DETAIL_CHECK_PASS');
}