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