Files
lucky_shop/tests/diy-card-bio-social.test.js
ZF sun 5e6eec166a feat(card): 名片区块流与基础信息补齐(2.22/2.23) + 分享带名片ID
- 组件支持六个「可独立成块」模块任意排序(区块流): 资料行/快捷操作/社交/二维码/
  标签来源的位置可调, 头部恒最上、发名片恒最下; 二维码位置档位归并为 flow。
- 补齐「名片基础信息」遗漏字段: 简介区块、社交区块、名片附加信息(过期遮罩/
  地址细分/来源), 并与 wap 端展示口径对齐。
- 小程序分享给游客时携带名片 ID: 分享 path 追加 card_id, 访客打开「我的名片」
  即看到分享者那张卡。新增 common/js/card-share.js 统一处理「取卡ID / 拼参数」
  (dataset 通道 + storage 通道兜底, 朋友圈场景靠 storage)。
2026-10-28 16:37:12 +08:00

107 lines
5.4 KiB
JavaScript

/**
* 名片 DIY 组件 ·「简介 / 社交媒体 / 标签 / 来源」契约测试(2.22, 2026-10-28)
*
* 背景: 名片 DIY 组件相对「名片基础信息」有字段遗漏(简介 bio 后端已下发但组件
* 不渲染、社交媒体 socials[] 整族缺失、标签/来源标注也没有)。
* 本次补齐, 且「简介」像资料区一样可独立成块 + 逐向微调。
*
* 覆盖:
* 1. 简介区块: 开关 + 独立成块判定 + 逐向间距(prefix=bio, 复用 blockCustomSpacing)
* 2. 社交/标签/来源: 字段读取与渲染
* 3. 地址细分: address 行用完整地址覆盖 + 邮编独立成行
* 4. 通用块样式工厂: blockIndependent / blockFinalStyle 的语义
* 5. 反例: 不得出现「简介写死 mock」「社交恒为空」这类假绿
*
* 跑法: node --test tests/diy-card-bio-social.test.js
*/
'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const DIY_DIR = path.resolve(__dirname, '../components-diy');
const read = f => fs.readFileSync(path.join(DIY_DIR, f), 'utf8');
// 先剥注释再断言 —— 注释里写「绝不能写 X」会被自判违规
const strip = s => s
.replace(/<!--[\s\S]*?-->/g, '')
.replace(/\/\*[\s\S]*?\*\//g, '')
.replace(/(^|[^:])\/\/.*$/gm, '$1');
const miniSrc = strip(read('diy-card-mini.vue'));
test('简介区块: 开关 + 独立成块 + 逐向间距(bio 前缀)', () => {
// 开关
assert.match(miniSrc, /showBioBlock\(\)\s*\{[\s\S]*?flagOn\(this\.c\.showBio, false\)/,
'缺少「显示简介」开关(flagOn(this.c.showBio))');
// 数据源: 名片简介
assert.match(miniSrc, /bioText\(\)\s*\{[\s\S]*?this\.card && this\.card\.bio/,
'简介文本未取 card.bio');
// 独立成块判定 + 逐向间距
assert.match(miniSrc, /bioFinalStyle\(\)\s*\{[\s\S]*?blockFinalStyle\(this\.c, 'bio'\)/,
'简介未走通用独立成块样式(blockFinalStyle(this.c, "bio"))');
assert.match(miniSrc, /function blockFinalStyle\(c, prefix\)/,
'缺少通用 blockFinalStyle 工厂');
assert.match(miniSrc, /function blockCustomSpacing\(c, prefix\)/,
'缺少通用逐向间距工厂(应复用而非新写一套)');
// 逐向字段走**通用 prefix 拼接**(C 端不写字面量, 与资料区/二维码同一套)
assert.match(miniSrc, /side\('MarginTop'\)|c\[prefix \+ 'MarginTop'\]/,
'通用逐向工厂未按 prefix 拼字段名(bio 应复用同一套)');
// 标题与行数
assert.match(miniSrc, /bioTitleShow\(\)/, '缺少简介标题开关');
assert.match(miniSrc, /bioMaxLines\(\)/, '缺少简介行数控制');
});
test('简介渲染: 模板消费 bioText, 不是写死文案', () => {
assert.match(miniSrc, /class="mini-bio"[\s\S]*?\{\{ bioText \}\}/,
'简介块未渲染 bioText');
// 反例: 不得出现写死的简介正文
assert.doesNotMatch(miniSrc, /这是一段示例简介|示例简介文案/,
'简介正文疑似写死');
});
test('社交媒体: 读取 card.socials 并渲染, 可独立成块', () => {
assert.match(miniSrc, /socialList\(\)\s*\{[\s\S]*?this\.card && Array\.isArray\(this\.card\.socials\)/,
'社交列表未取 card.socials');
assert.match(miniSrc, /showSocialBlock\(\)/, '缺少社交显示开关');
assert.match(miniSrc, /socialFinalStyle\(\)\s*\{[\s\S]*?blockFinalStyle\(this\.c, 'social'\)/,
'社交块未走通用独立成块样式');
assert.match(miniSrc, /class="mini-social"[\s\S]*?v-for="\(s, i\) in socialList"/,
'社交块未渲染列表');
// 反例: 不得把社交写死成空
assert.doesNotMatch(miniSrc, /socialList\(\)\s*\{\s*return \[\];/, '社交列表被写死为空');
});
test('名片标签 / 来源标注 / 过期提示', () => {
assert.match(miniSrc, /cardTags\(\)\s*\{[\s\S]*?this\.card && Array\.isArray\(this\.card\.tags\)/,
'标签未取 card.tags');
assert.match(miniSrc, /showTagsRow\(\)/, '缺少标签显示开关');
assert.match(miniSrc, /sourceName\(\)\s*\{[\s\S]*?this\.card && this\.card\.source_name/,
'来源标注未取 card.source_name');
assert.match(miniSrc, /showSourceRow\(\)/, '缺少来源显示开关');
assert.match(miniSrc, /cardExpired\(\)\s*\{[\s\S]*?this\.card && this\.card\.expired/,
'过期标记未取 card.expired');
assert.match(miniSrc, /class="mini-expired"/, '缺少过期遮罩元素');
});
test('地址细分: address 行用完整地址覆盖 + 邮编独立成行', () => {
assert.match(miniSrc, /decorateRows\(rows\)/, '缺少资料行装饰');
assert.match(miniSrc, /this\.card && this\.card\.address_full/, '未取完整地址');
assert.match(miniSrc, /this\.card && this\.card\.postal_code/, '未取邮编');
assert.match(miniSrc, /field === 'address'/, '未按 address 字段覆盖');
assert.match(miniSrc, /label: '邮编'/, '邮编未独立成行');
// rowsInfo 必须走 decorateRows(否则覆盖不生效)
assert.match(miniSrc, /rowsInfo\(\)\s*\{[\s\S]*?visibleRows\(this\.decorateRows\(this\.card\.rows\)/,
'rowsInfo 未走 decorateRows');
});
test('多语言: 请求带 lang 参数(后端按 _en 优先下发)', () => {
for (const f of ['diy-card-show.vue', 'diy-card-self.vue', 'diy-card-list.vue']) {
const src = read(f);
assert.match(src, /lang: uni\.getStorageSync\('lang'\) \|\| 'zh-cn'/,
f + ' 请求未带 lang 参数');
}
});