feat: rename app to Qiyun and refine Tiangong rituals and UI
This commit is contained in:
1 parent
7871114c50
commit
df84a4c130
32 files changed
+482
-130
No files matched your search
@@ -45,7 +45,7 @@ test('首页和我的未登录入口使用同一登录组件;天宫包装也
|
||||
assert.equal(page('src/pages/home/index.tsx', { guest: true }).type, 'shared-login-gate')
|
||||
assert.equal(page('src/pages/my/index.tsx', { guest: true }).type, 'shared-login-gate')
|
||||
assert.match(read('src/components/TiangongLoginGate.tsx'), /return <LoginGate/)
|
||||
assert.doesNotMatch(read('src/pages/login/index.tsx'), /brand-mark|一触即吉|那吉/)
|
||||
assert.doesNotMatch(read('src/pages/login/index.tsx'), /brand-mark|一触即吉|祈运/)
|
||||
})
|
||||
|
||||
test('首页头像点击使用主Tab导航前往我的页面', () => {
|
||||
|
||||
+18
-14
@@ -53,7 +53,7 @@ test('本期无功德、余额、捐款接口或入口', () => {
|
||||
test('祈愿/回向校验空值,不持久化姓名或祝愿;重置还原样例', () => {
|
||||
const mock = createTiangongMock()
|
||||
assert.throws(() => mock.wish('wealth', ' '), /愿望/)
|
||||
assert.throws(() => mock.dedicate('wealth', '甲', ''), /填写/)
|
||||
assert.throws(() => mock.dedicate('wealth', '', ''), /填写/)
|
||||
assert.deepEqual(mock.wish('wealth', '测试愿望'), { mock: true })
|
||||
assert.deepEqual(mock.dedicate('wealth', '测试姓名', '测试祝愿'), { mock: true })
|
||||
assert.ok(!JSON.stringify(mock.snapshot()).includes('测试'))
|
||||
@@ -95,23 +95,18 @@ test('最后一位解绑锁定,任意一位绑定重新解锁;解绑他位
|
||||
mock.unbind('fortune')
|
||||
assert.equal(hasBoundDeity(mock.snapshot()), true)
|
||||
})
|
||||
test('每位初始2次,重复恭请/取消不扣减或重置,耗尽后送神也不能绕过', () => {
|
||||
test('送神保留绑定和解绑额度,重复请神不重置累计上香次数', () => {
|
||||
const mock = createTiangongMock()
|
||||
mock.incense('wealth'); mock.sendOff('wealth')
|
||||
assert.equal(mock.snapshot().deities.wealth.remainingUnbinds, 2)
|
||||
// Viewing a confirmation/cancelling is read-only.
|
||||
mock.snapshot(); mock.snapshot()
|
||||
assert.equal(mock.snapshot().deities.wealth.remainingUnbinds, 2)
|
||||
assert.equal(mock.snapshot().deities.wealth.unlocked, false)
|
||||
assert.equal(hasBoundDeity(mock.snapshot()), true)
|
||||
assert.throws(() => mock.incense('wealth'), /未恭请/)
|
||||
mock.invite('wealth')
|
||||
assert.equal(mock.snapshot().deities.wealth.incenseCount, 60)
|
||||
mock.unbind('wealth')
|
||||
assert.equal(mock.snapshot().deities.wealth.remainingUnbinds, 1)
|
||||
assert.throws(() => mock.unbind('wealth'), /未恭请/)
|
||||
mock.invite('wealth'); mock.invite('wealth')
|
||||
assert.equal(mock.snapshot().deities.wealth.remainingUnbinds, 1)
|
||||
mock.sendOff('wealth')
|
||||
assert.equal(mock.snapshot().deities.wealth.remainingUnbinds, 0)
|
||||
mock.invite('wealth')
|
||||
for (const action of ['unbind', 'sendOff']) assert.throws(() => mock[action]('wealth'), /次数已用尽/)
|
||||
assert.equal(mock.snapshot().deities.wealth.unlocked, true)
|
||||
assert.equal(mock.snapshot().deities.fortune.remainingUnbinds, 2)
|
||||
assert.equal(hasBoundDeity(mock.snapshot()), false)
|
||||
})
|
||||
test('绑定、解绑、重置同步通知缓存Tab;退订后不再通知', () => {
|
||||
const mock = createTiangongMock()
|
||||
@@ -134,3 +129,12 @@ test('绑定门禁和卡片采用神仙用语,真实NFC与解绑契约不被
|
||||
assert.match(ui, /disabled=\{state.remainingUnbinds === 0\}/)
|
||||
assert.doesNotMatch(ui, /getNFCAdapter|tiangongRemoteContract\./)
|
||||
})
|
||||
|
||||
test('已送神仍可解绑设备,解绑清除再次请神资格并正常扣减额度', () => {
|
||||
const mock = createTiangongMock()
|
||||
mock.sendOff('wealth'); assert.equal(hasBoundDeity(mock.snapshot()), true)
|
||||
mock.unbind('wealth')
|
||||
assert.equal(hasBoundDeity(mock.snapshot()), false)
|
||||
assert.equal(mock.snapshot().deities.wealth.canInvite, false)
|
||||
assert.equal(mock.snapshot().deities.wealth.remainingUnbinds, 1)
|
||||
})
|
||||
@@ -0,0 +1,55 @@
|
||||
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(respond = async () => ({ id: 1 })) {
|
||||
const calls = [], exports = {}
|
||||
const source = fs.readFileSync(path.join(__dirname, '../src/services/tiangongApi.ts'), 'utf8')
|
||||
vm.runInNewContext(ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS } }).outputText, {
|
||||
exports, require: () => ({ request: (...args) => { calls.push(JSON.parse(JSON.stringify(args))); return respond(...args) } })
|
||||
})
|
||||
return { api: exports.tiangongApi, calls }
|
||||
}
|
||||
test('上香、祈愿、回向按新编码提交,各类型不混传明细或接收账号', async () => {
|
||||
const { api, calls } = harness()
|
||||
await api.offer(726, 1)
|
||||
await api.offer(726, 2, '愿顺遂')
|
||||
await api.dedicate(726, '家人', '平安')
|
||||
await api.dedicate(726, '众生', '')
|
||||
assert.deepEqual(calls, [
|
||||
['/api/user/virtual-items/offerings', { method: 'POST', data: { userVirtualItemId: 726, offeringType: 1, incenseCount: 3 } }],
|
||||
['/api/user/virtual-items/offerings', { method: 'POST', data: { userVirtualItemId: 726, offeringType: 2, prayerContent: '愿顺遂' } }],
|
||||
['/api/user/virtual-items/dedications', { method: 'POST', data: { userVirtualItemId: 726, dedicationTarget: '家人', dedicationContent: '平安' } }],
|
||||
['/api/user/virtual-items/dedications', { method: 'POST', data: { userVirtualItemId: 726, dedicationTarget: '众生' } }]
|
||||
])
|
||||
})
|
||||
test('请神送神调用独立生命周期接口', async () => {
|
||||
const { api, calls } = harness()
|
||||
await api.lifecycle(726, 1); await api.lifecycle(726, 2)
|
||||
assert.deepEqual(calls.map(call => call[1].data), [{ userVirtualItemId: 726, actionType: 1 }, { userVirtualItemId: 726, actionType: 2 }])
|
||||
assert.ok(calls.every(call => call[0] === '/api/user/virtual-items/lifecycle'))
|
||||
})
|
||||
test('列表通过持有ID和字典ID共同关联状态,不把未绑定或其他资产当作已送神', async () => {
|
||||
const { api } = harness(async url => url.startsWith('/api/tiangong') ? [
|
||||
{ id: 81, holdingId: 726, active: false, incenseCount: 7 }, { id: 82, holdingId: 727, active: false }, { id: 83, active: false }
|
||||
] : [{ id: 726, virtualItemId: 81, status: 0 }, { id: 727, virtualItemId: 999, status: 0 }])
|
||||
const rows = await api.load()
|
||||
assert.equal(rows[0].holdingStatus, 0); assert.equal(rows[0].incenseCount, 7)
|
||||
assert.equal(rows[1].holdingStatus, undefined); assert.equal(rows[2].holdingStatus, undefined)
|
||||
})
|
||||
test('查询保留后端按当前用户和神仙返回的最新祈愿及回向', async () => {
|
||||
const { api } = harness(async url => url.startsWith('/api/tiangong') ? [
|
||||
{ id: 81, holdingId: 726, active: true,
|
||||
latestPrayer: { offeringId: 10, prayerContent: '愿顺遂' },
|
||||
latestDedication: { offeringId: 11, dedicationTarget: '众生', dedicationContent: null } },
|
||||
{ id: 82, latestPrayer: null, latestDedication: null }
|
||||
] : [{ id: 726, virtualItemId: 81, status: 1 }])
|
||||
const rows = await api.load()
|
||||
assert.equal(rows[0].latestPrayer.prayerContent, '愿顺遂')
|
||||
assert.equal(rows[0].latestDedication.dedicationTarget, '众生')
|
||||
assert.equal(rows[0].latestDedication.dedicationContent, null)
|
||||
assert.equal(rows[1].latestPrayer, null)
|
||||
assert.equal(rows[1].latestDedication, null)
|
||||
})
|
||||
@@ -5,15 +5,16 @@ const path = require('node:path')
|
||||
const vm = require('node:vm')
|
||||
const ts = require('typescript')
|
||||
const root = path.resolve(__dirname, '..')
|
||||
function page(file, { bound = false, demo = false, modal = '', detail = 'https://example.invalid/detail.png', description = '后端返回的介绍\n保留换行' } = {}) {
|
||||
function page(file, { bound = false, demo = false, modal = '', detail = 'https://example.invalid/detail.png', description = '后端返回的介绍\n保留换行', latestPrayer = null, latestDedication = null } = {}) {
|
||||
const deity = { id: 'wealth', name: '财神', ritualName: '武财神', icon: 'local-list.png', statue: 'local-statue.png', introduction: '本地原型介绍', missingStatue: true }
|
||||
const state = { available: true, unlocked: bound, iconUrl: 'https://example.invalid/list.png', iconDetailUrl: detail, description, days: 4, incenseCount: 12 }
|
||||
const state = { available: true, unlocked: bound, iconUrl: 'https://example.invalid/list.png', iconDetailUrl: detail, description, days: 4, incenseCount: 12, latestPrayer, latestDedication }
|
||||
const states = []; let index = 0
|
||||
const exports = {}
|
||||
vm.runInNewContext(ts.transpileModule(fs.readFileSync(path.join(root, file), '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.endsWith('.jpg')) return { default: id }
|
||||
if (id === 'react') return { useRef: value => ({ current: value }), useState: initial => {
|
||||
const current = index++
|
||||
if (!(current in states)) states[current] = current === 1 && file.includes('tiangong-detail') ? modal : initial
|
||||
@@ -39,6 +40,42 @@ function nodes(tree) {
|
||||
}
|
||||
const text = tree => nodes(tree).filter(node => node.type === 'Text').map(node => node.props.children).flat().join(' ')
|
||||
const image = tree => nodes(tree).filter(node => node.type === 'Image')
|
||||
const statueImages = tree => image(tree).filter(node => /tg-statue|tg-panel-statue/.test(node.props.className))
|
||||
test('打开祈愿回填最新内容,编辑中不被查询快照覆盖,再次打开恢复已保存内容', () => {
|
||||
const render = page('src/pages/tiangong-detail/index.tsx', { bound: true, latestPrayer: { prayerContent: '已保存的愿望' } })
|
||||
const openWish = () => nodes(render()).find(node => node.props['aria-label'] === '祈愿').props.onClick()
|
||||
const input = () => nodes(render()).find(node => node.type === 'Textarea')
|
||||
openWish()
|
||||
assert.equal(input().props.value, '已保存的愿望')
|
||||
input().props.onInput({ detail: { value: '正在编辑的新愿望' } })
|
||||
assert.equal(input().props.value, '正在编辑的新愿望')
|
||||
nodes(render()).find(node => node.type === 'Button' && node.props.children === '取消').props.onClick()
|
||||
openWish()
|
||||
assert.equal(input().props.value, '已保存的愿望')
|
||||
})
|
||||
test('打开回向回填对象及可选祝愿,没有可用记录时均为空', () => {
|
||||
for (const latestDedication of [null, { dedicationTarget: '家人', dedicationContent: '平安顺遂' }, { dedicationTarget: '一切众生', dedicationContent: null }]) {
|
||||
const render = page('src/pages/tiangong-detail/index.tsx', { bound: true, latestDedication })
|
||||
nodes(render()).find(node => node.props['aria-label'] === '回向').props.onClick()
|
||||
assert.equal(nodes(render()).find(node => node.type === 'Input').props.value, latestDedication?.dedicationTarget || '')
|
||||
assert.equal(nodes(render()).find(node => node.type === 'Textarea').props.value, latestDedication?.dedicationContent || '')
|
||||
}
|
||||
const render = page('src/pages/tiangong-detail/index.tsx', { bound: true })
|
||||
nodes(render()).find(node => node.props['aria-label'] === '祈愿').props.onClick()
|
||||
assert.equal(nodes(render()).find(node => node.type === 'Textarea').props.value, '')
|
||||
})
|
||||
test('请神和敬神均用原生图片加载本地山水,背景位于神像下方', () => {
|
||||
for (const bound of [true, false]) {
|
||||
const tree = page('src/pages/tiangong-detail/index.tsx', { bound })()
|
||||
const scene = nodes(tree).find(node => node.props.className === 'tg-statue-space')
|
||||
assert.equal(scene.props.style?.backgroundImage, undefined)
|
||||
const background = scene.props.children[0]
|
||||
assert.equal(background.type, 'Image')
|
||||
assert.equal(background.props.className, 'tg-scene-background')
|
||||
assert.match(background.props.src, /proto-clouds\.jpg$/)
|
||||
assert.equal(background.props.mode, bound ? 'aspectFill' : 'scaleToFill')
|
||||
}
|
||||
})
|
||||
test('详情及弹窗均使用详情图;仅请神页展示description,敬神页不展示介绍', () => {
|
||||
for (const bound of [true, false]) {
|
||||
const tree = page('src/pages/tiangong-detail/index.tsx', { bound, modal: 'wish' })()
|
||||
@@ -66,12 +103,13 @@ test('敬神页在正式和Demo模式均不显示介绍,缺少介绍也不显
|
||||
test('真实字段为空或加载失败时提示,不以本地神像、列表缩略图或原型介绍顶替', () => {
|
||||
const empty = page('src/pages/tiangong-detail/index.tsx', { detail: '', description: '' })()
|
||||
assert.match(text(empty), /详情图片暂未配置/); assert.match(text(empty), /暂无介绍/)
|
||||
assert.equal(image(empty).length, 0)
|
||||
assert.equal(statueImages(empty).length, 0)
|
||||
const render = page('src/pages/tiangong-detail/index.tsx')
|
||||
image(render())[0].props.onError()
|
||||
statueImages(render())[0].props.onError()
|
||||
const failed = render()
|
||||
assert.match(text(failed), /详情图片加载失败/)
|
||||
assert.equal(image(failed).length, 0)
|
||||
assert.equal(statueImages(failed).length, 0)
|
||||
assert.ok(image(failed).some(node => node.props.className === 'tg-scene-background'))
|
||||
})
|
||||
test('列表保留iconUrl与20%透明度规则,详情字段不误用到列表;显式Demo保留原型', () => {
|
||||
for (const bound of [true, false]) {
|
||||
@@ -82,6 +120,6 @@ test('列表保留iconUrl与20%透明度规则,详情字段不误用到列表
|
||||
}
|
||||
assert.match(fs.readFileSync(path.join(root, 'src/pages/fortune/index.scss'), 'utf8'), /\.deity-card\.locked \.deity-image \{ opacity: \.2; \}/)
|
||||
const demo = page('src/pages/tiangong-detail/index.tsx', { demo: true })()
|
||||
assert.equal(image(demo)[0].props.src, 'local-statue.png')
|
||||
assert.equal(statueImages(demo)[0].props.src, 'local-statue.png')
|
||||
assert.match(text(demo), /本地原型介绍/)
|
||||
})
|
||||
@@ -15,11 +15,11 @@ const { createTiangongStore } = load('src/utils/tiangongStore.ts', { './tiangong
|
||||
const defer = () => { let resolve, reject; const promise = new Promise((ok, fail) => { resolve = ok; reject = fail }); return { promise, resolve, reject } }
|
||||
function harness() {
|
||||
let key = 'account-a', time = 1000, rows = [{ id: 81, name: '财神', holdingId: 726, active: true, incenseCount: 7, days: 3 }]
|
||||
let loader = async () => rows, offer = async () => ({ id: 99 })
|
||||
let loader = async () => rows, offer = async () => ({ id: 99 }), lifecycle = async () => ({ id: 101 }), dedicate = async () => ({ id: 100 })
|
||||
const calls = []
|
||||
const store = createTiangongStore({ key: () => key, now: () => time, resolve: name => name === '财神' ? 'wealth' : undefined,
|
||||
load: () => loader(), offer: (...args) => { calls.push(args); return offer(...args) }, dedicate: async (...args) => { calls.push(args); return { id: 100 } } })
|
||||
return { store, calls, key: value => { key = value }, time: value => { time = value }, rows: value => { rows = value }, load: value => { loader = value }, offer: value => { offer = value } }
|
||||
load: () => loader(), offer: (...args) => { calls.push(args); return offer(...args) }, lifecycle: (...args) => { calls.push(args); return lifecycle(...args) }, dedicate: (...args) => { calls.push(args); return dedicate(...args) } })
|
||||
return { store, calls, key: value => { key = value }, time: value => { time = value }, rows: value => { rows = value }, load: value => { loader = value }, offer: value => { offer = value }, lifecycle: value => { lifecycle = value }, dedicate: value => { dedicate = value } }
|
||||
}
|
||||
test('真实状态不预置财神,按后端持有ID提交,后端确认后才计数', async () => {
|
||||
const h = harness()
|
||||
@@ -29,7 +29,7 @@ test('真实状态不预置财神,按后端持有ID提交,后端确认后才
|
||||
const request = h.store.incense('wealth')
|
||||
assert.equal(h.store.snapshot().deities.wealth.incenseCount, 7)
|
||||
assert.equal(h.store.snapshot().deities.wealth.lit, false)
|
||||
assert.equal(h.calls[0][0], 726); assert.equal(h.calls[0][1], 2)
|
||||
assert.equal(h.calls[0][0], 726); assert.equal(h.calls[0][1], 1)
|
||||
response.resolve({ id: 10 }); await request
|
||||
assert.equal(h.store.snapshot().deities.wealth.incenseCount, 8)
|
||||
assert.equal(h.store.snapshot().deities.wealth.incenseUntil, 16000)
|
||||
@@ -101,8 +101,128 @@ test('祈愿和任意姓名回向去空白提交,不携带功德或注册收
|
||||
const h = harness(); await h.store.refresh()
|
||||
await h.store.wish('wealth', ' 愿顺遂 ')
|
||||
await h.store.dedicate('wealth', ' 家人 ', ' 平安 ')
|
||||
assert.deepEqual(h.calls, [[726, 3, '愿顺遂'], [726, '家人', '平安']])
|
||||
assert.deepEqual(h.calls, [[726, 2, '愿顺遂'], [726, '家人', '平安']])
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestPrayer.prayerContent, '愿顺遂')
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestDedication.dedicationTarget, '家人')
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestDedication.dedicationContent, '平安')
|
||||
await assert.rejects(h.store.wish('wealth', ' '), /愿望/)
|
||||
await assert.rejects(h.store.dedicate('wealth', '', '祝愿'), /回向对象/)
|
||||
assert.equal(h.calls.length, 2)
|
||||
})
|
||||
|
||||
test('最新祈愿回向随查询加载,失效或账号切换清空,不影响其他神仙', async () => {
|
||||
const h = harness()
|
||||
const row = { id: 81, name: '财神', holdingId: 726, active: true, incenseCount: 0, days: 1,
|
||||
latestPrayer: { offeringId: 10, prayerContent: '已保存愿望' },
|
||||
latestDedication: { offeringId: 11, dedicationTarget: '众生', dedicationContent: null } }
|
||||
h.rows([row]); await h.store.refresh()
|
||||
const first = h.store.snapshot().deities
|
||||
assert.equal(first.wealth.latestPrayer.prayerContent, '已保存愿望')
|
||||
assert.equal(first.wealth.latestDedication.dedicationTarget, '众生')
|
||||
assert.equal(first.wealth.latestDedication.dedicationContent, null)
|
||||
assert.equal(first.wenchang.latestPrayer, undefined)
|
||||
first.wealth.latestPrayer.prayerContent = '修改快照不污染缓存'
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestPrayer.prayerContent, '已保存愿望')
|
||||
h.rows([{ ...row, latestPrayer: null, latestDedication: null }]); await h.store.refresh(true)
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestPrayer, null)
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestDedication, null)
|
||||
h.rows([row]); await h.store.refresh(true)
|
||||
h.key('account-b')
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestPrayer, undefined)
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestDedication, undefined)
|
||||
})
|
||||
|
||||
test('祈愿确认后才更新回显,失败和账号切换不写入旧账号内容', async () => {
|
||||
const h = harness(); await h.store.refresh()
|
||||
const response = defer(); h.offer(() => response.promise)
|
||||
const request = h.store.wish('wealth', '待确认的愿望')
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestPrayer, null)
|
||||
response.resolve({ id: 105 }); await request
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestPrayer.prayerContent, '待确认的愿望')
|
||||
h.offer(async () => { throw new Error('提交失败') })
|
||||
await assert.rejects(h.store.wish('wealth', '失败愿望'), /提交失败/)
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestPrayer.prayerContent, '待确认的愿望')
|
||||
const pending = defer(); h.offer(() => pending.promise)
|
||||
const sending = h.store.wish('wealth', '旧账号愿望')
|
||||
h.key('account-b'); h.store.snapshot()
|
||||
pending.resolve({ id: 106 }); await assert.rejects(sending, /登录状态/)
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestPrayer, undefined)
|
||||
})
|
||||
|
||||
test('回向成功后可回显空祝愿,失败不覆盖已保存对象', async () => {
|
||||
const h = harness(); await h.store.refresh()
|
||||
await h.store.dedicate('wealth', '家人', '平安')
|
||||
h.dedicate(async () => { throw new Error('提交失败') })
|
||||
await assert.rejects(h.store.dedicate('wealth', '失败对象', ''), /提交失败/)
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestDedication.dedicationTarget, '家人')
|
||||
const response = defer(); h.dedicate(() => response.promise)
|
||||
const sending = h.store.dedicate('wealth', '众生', '')
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestDedication.dedicationContent, '平安')
|
||||
response.resolve({ id: 101 }); await sending
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestDedication.dedicationTarget, '众生')
|
||||
assert.equal(h.store.snapshot().deities.wealth.latestDedication.dedicationContent, '')
|
||||
})
|
||||
|
||||
test('送神和重新请神使用独立流水,成功后才改变状态且保留次数', async () => {
|
||||
const h = harness(); await h.store.refresh()
|
||||
const response = defer(); h.lifecycle(() => response.promise)
|
||||
const sending = h.store.sendOff('wealth')
|
||||
assert.equal(h.store.snapshot().deities.wealth.unlocked, true)
|
||||
await assert.rejects(h.store.sendOff('wealth'), /提交/)
|
||||
await assert.rejects(h.store.incense('wealth'), /提交/)
|
||||
response.resolve({ id: 102 }); await sending
|
||||
const stopped = h.store.snapshot().deities.wealth
|
||||
assert.equal(stopped.unlocked, false); assert.equal(stopped.canInvite, true)
|
||||
assert.equal(stopped.incenseCount, 7); assert.equal(stopped.incenseUntil, 0)
|
||||
assert.equal(mock.hasBoundDeity(h.store.snapshot()), true)
|
||||
await assert.rejects(h.store.wish('wealth', '愿顺遂'), /绑定/)
|
||||
h.lifecycle(async () => ({ id: 103 }))
|
||||
await h.store.invite('wealth')
|
||||
assert.equal(h.store.snapshot().deities.wealth.unlocked, true)
|
||||
assert.equal(h.store.snapshot().deities.wealth.incenseCount, 7)
|
||||
assert.deepEqual(h.calls, [[726, 2], [726, 1]])
|
||||
})
|
||||
test('刷新后只允许已送神持有关系重新请神,失败及账号切换不伪造成功', async () => {
|
||||
const h = harness()
|
||||
h.rows([{ id: 81, name: '财神', holdingId: 726, holdingStatus: 0, active: false, incenseCount: 7 }])
|
||||
await h.store.refresh(); assert.equal(h.store.snapshot().deities.wealth.canInvite, true)
|
||||
h.lifecycle(async () => { throw new Error('设备已解绑') })
|
||||
await assert.rejects(h.store.invite('wealth'), /设备已解绑/)
|
||||
assert.equal(h.store.snapshot().deities.wealth.unlocked, false)
|
||||
const response = defer(); h.lifecycle(() => response.promise)
|
||||
const sending = h.store.invite('wealth'); h.key('account-b'); h.store.snapshot()
|
||||
response.resolve({ id: 12 }); await assert.rejects(sending, /登录状态/)
|
||||
assert.equal(h.store.snapshot().deities.wealth.unlocked, false)
|
||||
for (const status of [undefined, 1, 3]) {
|
||||
h.rows([{ id: 81, name: '财神', holdingId: 726, holdingStatus: status, active: false }])
|
||||
await h.store.refresh(true)
|
||||
await assert.rejects(h.store.invite('wealth'), /当前状态/)
|
||||
}
|
||||
})
|
||||
test('回向对象允许255字,祝愿可空;上香在途不能送神', async () => {
|
||||
const h = harness(); await h.store.refresh()
|
||||
await h.store.dedicate('wealth', '甲'.repeat(255), '')
|
||||
await assert.rejects(h.store.dedicate('wealth', '甲'.repeat(256), ''), /回向对象/)
|
||||
await assert.rejects(h.store.dedicate('wealth', '甲', '愿'.repeat(501)), /500/)
|
||||
const response = defer(); h.offer(() => response.promise)
|
||||
const sending = h.store.incense('wealth')
|
||||
await assert.rejects(h.store.sendOff('wealth'), /提交/)
|
||||
response.resolve({ id: 23 }); await sending
|
||||
assert.equal(h.store.snapshot().deities.wealth.incenseCount, 8)
|
||||
})
|
||||
|
||||
test('送神失败保留供奉状态,无效流水不显示成功,晚到列表不撤销已送神', async () => {
|
||||
const h = harness(); await h.store.refresh()
|
||||
h.lifecycle(async () => { throw new Error('网络失败') })
|
||||
await assert.rejects(h.store.sendOff('wealth'), /网络失败/)
|
||||
assert.equal(h.store.snapshot().deities.wealth.unlocked, true)
|
||||
h.lifecycle(async () => ({ id: 0 }))
|
||||
await assert.rejects(h.store.sendOff('wealth'), /有效记录/)
|
||||
assert.equal(h.store.snapshot().deities.wealth.unlocked, true)
|
||||
const read = defer(); h.load(() => read.promise)
|
||||
const refreshing = h.store.refresh(true)
|
||||
h.lifecycle(async () => ({ id: 15 })); await h.store.sendOff('wealth')
|
||||
read.resolve([{ id: 81, name: '财神', holdingId: 726, active: true, incenseCount: 7 }]); await refreshing
|
||||
assert.equal(h.store.snapshot().deities.wealth.unlocked, false)
|
||||
assert.equal(h.store.snapshot().deities.wealth.canInvite, true)
|
||||
})
|
||||
Reference in new issue
Block a user