| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 |
- // fpc 端到端测试:计价引擎单元断言 + (可选)真实图纸识别
- import { buildQuote, kitchenBillable, tierUnitPrice, applyOps, withinAuthority } from '../src/quote.js';
- import { normalizeCabinetName } from '../src/vision.js';
- import { renderSVG, buildPlan } from '../src/redraw.js';
- import { renderReport } from '../src/report.js';
- import assert from 'node:assert';
- let pass = 0; let fail = 0;
- function t(name, fn) {
- try { fn(); pass++; console.log(' ✓', name); }
- catch (e) { fail++; console.error(' ✗', name, '→', e.message); }
- }
- console.log('== 计价引擎 ==');
- t('厨房U型计费米数: 长3.5宽2.4 → 扣2转角+冰箱 = 7.2m', () => {
- const kb = kitchenBillable([3.5, 2.4, 3.5, 2.4]);
- assert.equal(kb.kitchenType, 'U');
- assert.equal(kb.billable, 7.2);
- });
- t('厨房L型计费米数: 长3.6宽1.8 → 扣1转角+冰箱 = 3.8m', () => {
- const kb = kitchenBillable([3.6, 1.8, 0, 0]);
- assert.equal(kb.kitchenType, 'L');
- assert.equal(kb.billable, 3.8);
- });
- t('PET档15%浮动单价 = 区间上限5000', () => {
- assert.equal(tierUnitPrice({ name: 'PET', min: 4500, max: 5000 }, 15), 5000);
- });
- t('PET档10%浮动单价 = 区间下限4500', () => {
- assert.equal(tierUnitPrice({ name: 'PET', min: 4500, max: 5000 }, 10), 4500);
- });
- t('快速报价: 3.6×2.6衣柜@5000 = 46800', () => {
- const q = buildQuote({ cabinets: [{ type: '主卧衣柜', width: 3.6, height: 2.6 }], tierName: 'PET', floatPct: 15 });
- assert.equal(q.rooms[0].total, 46800);
- });
- t('价格型客户默认85折+自动活动', () => {
- const q = buildQuote({ cabinets: [{ type: '主卧衣柜', width: 3.6, height: 2.6 }], tierKey: 'price', floatPct: 15 });
- assert.equal(q.meta.discount, 0.85);
- assert.ok(q.activityLines.length >= 1, '活动应自动启用');
- });
- t('品质型客户 +15% 品质系数', () => {
- const q = buildQuote({ cabinets: [{ type: '主卧衣柜', width: 3.6, height: 2.6 }], tierKey: 'quality', floatPct: 15 });
- assert.equal(q.meta.premiumRate, 1.15);
- });
- t('增删项 ops: add+remove 生效', () => {
- const out = applyOps([{ type: '主卧衣柜', width: 3, height: 2.6 }, { type: '书房书柜', width: 2, height: 2.6 }],
- [{ op: 'remove', target: '书房书柜' }, { op: 'add', type: '阳台柜', width: 1.8 }]);
- assert.deepEqual(out.map((c) => c.type), ['主卧衣柜', '阳台柜']);
- });
- t('谈价区间 = 到手价 ± 浮动率', () => {
- const q = buildQuote({ cabinets: [{ type: '玄关鞋柜', width: 1.2, height: 2.4 }], floatPct: 15 });
- const f = q.totals.finalPrice;
- assert.equal(q.totals.range.low, Math.round(f * 0.85));
- assert.ok(withinAuthority(q, f));
- assert.ok(!withinAuthority(q, f * 1.2), '超出区间的价格应在授权外');
- });
- console.log('== 名称归一 ==');
- t('厨房吊柜→厨房橱柜(强制延米计价)', () => {
- assert.equal(normalizeCabinetName('厨房吊柜'), '厨房橱柜');
- assert.equal(normalizeCabinetName('开放式厨房柜'), '厨房橱柜');
- });
- t('玄关柜/入户柜→玄关鞋柜', () => {
- assert.equal(normalizeCabinetName('玄关柜'), '玄关鞋柜');
- assert.equal(normalizeCabinetName('入户柜'), '玄关鞋柜');
- });
- console.log('== 报告渲染 ==');
- t('renderReport 产出含引擎与交互的HTML', () => {
- const q = buildQuote({ cabinets: [{ type: '主卧衣柜', width: 3.2, height: 2.6 }], tierKey: 'standard' });
- const plan = buildPlan({ analysis: { rooms: [{ name: '主卧', pos: '左中', approxArea: 14 }] }, cabinets: q.rooms.map((r) => ({ type: r.type, width: 3.2, height: 2.6, pos: '左中' })) });
- const svg = renderSVG(plan);
- const html = renderReport({ quote: q, planSVG: svg, clientName: '测试', salesNote: 'x' });
- assert.ok(html.includes('id="finalPrice"'));
- assert.ok(html.includes('function buildQuote'), '计价引擎应内嵌');
- assert.ok(svg.startsWith('<svg'), 'SVG应可生成');
- });
- console.log(`\n结果: ${pass} 通过, ${fail} 失败`);
- process.exit(fail ? 1 : 0);
|