feat: update agreements and refine miniapp layouts and integrations

This commit is contained in:
zhouzhibo committed 2026-10-21 20:33:50 +08:00
1 parent 38fc4187ea
commit 7871114c50
26 files changed
+2018 -120

No files matched your search

+77
View File
@@ -0,0 +1,77 @@
const test = require('node:test')
const assert = require('node:assert/strict')
const fs = require('node:fs')
const path = require('node:path')
const vm = require('node:vm')
const ts = require('typescript')
function harness({ demo = false, bound = true, mode = 'view', iconUrl = 'https://example.invalid/backend-icon.png' } = {}) {
const states = [], calls = []; let index = 0, closed = 0
const exports = {}
const deity = { id: 'wealth', name: '财神', icon: 'local-prototype.png' }
vm.runInNewContext(ts.transpileModule(fs.readFileSync(path.join(__dirname, '../src/components/DeityBinding.tsx'), 'utf8'), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2019, jsx: ts.JsxEmit.ReactJSX }
}).outputText, { exports, require: id => {
if (id === 'react/jsx-runtime') return require(id)
if (id === 'react') return { useRef: value => ({ current: value }), useState: initial => {
const current = index++; if (!(current in states)) states[current] = initial
return [states[current], value => { states[current] = value }]
} }
if (id === '@tarojs/components') return { View: 'View', Image: 'Image', Text: 'Text', Button: 'Button' }
if (id === '@tarojs/taro') return { default: { showToast: () => {} } }
if (id.endsWith('/tiangong')) return { getDeity: () => deity, isTiangongDemo: () => demo,
tiangongMock: { invite: () => calls.push('invite'), unbind: () => calls.push('unbind') } }
if (id.endsWith('/request')) return { resolveAssetUrl: value => value || '' }
if (id.endsWith('/useTiangongSnapshot')) return { useTiangongSnapshot: () => ({ deities: {
wealth: { unlocked: bound, days: 23, holdingId: 726, iconUrl, remainingUnbinds: 2 }
} }) }
return {}
} })
return { calls, closed: () => closed, render: () => { index = 0; return exports.DeityCardDialog({ deityId: 'wealth', mode, onClose: () => { closed++ } }) } }
}
function nodes(tree) {
if (!tree || typeof tree !== 'object') return []
const children = tree.props?.children
return [tree, ...(Array.isArray(children) ? children : [children]).flatMap(nodes)]
}
function text(tree) {
const walk = node => typeof node === 'string' || typeof node === 'number' ? String(node) : node && typeof node === 'object' ? (Array.isArray(node.props?.children) ? node.props.children : [node.props?.children]).map(walk).join('') : ''
return walk(tree)
}
const find = (tree, className) => nodes(tree).find(node => node.props.className === className)
test('正式与Demo共用金色挂饰卡片、固定图片框与关闭入口', () => {
for (const demo of [false, true]) {
const h = harness({ demo }), tree = h.render()
for (const name of ['binding-dialog-stack', 'binding-gold-card', 'binding-card-hook', 'binding-art-frame', 'binding-guarded']) assert.ok(find(tree, name))
assert.equal(find(tree, 'binding-card-art').props.mode, 'aspectFit')
assert.equal(find(tree, 'binding-card-art').props.src, demo ? 'local-prototype.png' : 'https://example.invalid/backend-icon.png')
assert.equal(find(tree, 'binding-days').props.children, 23)
find(tree, 'binding-card-close').props.onClick(); assert.equal(h.closed(), 1)
}
})
test('正式卡片只显示真实持有记录,无演示ID、模拟绑定解绑或示例额度', () => {
for (const mode of ['view', 'bind']) {
const h = harness({ mode }), tree = h.render()
assert.match(text(tree), /持有记录:726/)
assert.doesNotMatch(text(tree), /DEMO|Mock|模拟|解绑|确认绑定|唯一 ID/)
assert.equal(find(tree, 'binding-more'), undefined)
assert.equal(find(tree, 'binding-primary binding-card-confirm'), undefined)
assert.deepEqual(h.calls, [])
}
})
test('正式未持有不伪造守护;缺图和加载失败保留金色布局而不替换成本地神像', () => {
const missing = harness({ bound: false, iconUrl: '' }).render()
assert.ok(find(missing, 'binding-gold-card')); assert.equal(find(missing, 'binding-guarded'), undefined)
assert.match(text(missing), /图片暂未配置|请通过 NFC 绑定神仙/)
assert.equal(find(missing, 'binding-card-art'), undefined)
const h = harness(); find(h.render(), 'binding-card-art').props.onError()
assert.match(text(h.render()), /图片加载失败/)
assert.equal(find(h.render(), 'binding-card-art'), undefined)
})
test('Demo保留原有模拟解绑分支,正式模式不会复用它', () => {
const h = harness({ demo: true })
find(h.render(), 'binding-more').props.onClick()
find(h.render(), 'binding-unbind-menu').props.onClick()
const confirm = nodes(h.render()).find(node => node.props.className?.startsWith('binding-primary ') && text(node) === '确定解绑')
assert.ok(confirm); confirm.props.onClick()
assert.deepEqual(h.calls, ['unbind']); assert.equal(h.closed(), 1)
})