feat(miniapp): 实现订阅管理页面与运势页订阅入口
- 新增订阅管理页面:订阅开关、推送时间设置、模板管理 - 运势主页添加订阅入口区域 - 更新页面路由配置 - 补充运势页相关测试
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import { triggerOnShow } from '../../../__mocks__/dcloudio/uni-app'
|
||||
import SubscriptionPage from '../index.vue'
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: 'zh-CN',
|
||||
messages: {},
|
||||
})
|
||||
|
||||
// Mock wx 全局对象
|
||||
const mockWx = {
|
||||
cloud: {
|
||||
callFunction: vi.fn(),
|
||||
},
|
||||
}
|
||||
|
||||
function mountPage() {
|
||||
return mount(SubscriptionPage, {
|
||||
global: {
|
||||
plugins: [i18n],
|
||||
stubs: {
|
||||
picker: { template: '<div><slot /></div>' },
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
describe('PushSubscriptionPage', () => {
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('wx', mockWx)
|
||||
vi.clearAllMocks()
|
||||
// 默认设置订阅状态
|
||||
const store = (globalThis as any).__uniStorage__ ?? {}
|
||||
store['eis_push_subscription'] = JSON.stringify({
|
||||
pushTime: '07:00',
|
||||
pushEnabled: true,
|
||||
subscribedAt: '2026-08-13T00:00:00.000Z',
|
||||
})
|
||||
;(globalThis as any).__uniStorage__ = store
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('应显示订阅状态为"已开启"', async () => {
|
||||
const wrapper = mountPage()
|
||||
triggerOnShow()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(wrapper.find('.status-value').exists()).toBe(true)
|
||||
expect(wrapper.find('.status-value').text()).toContain('已开启')
|
||||
})
|
||||
|
||||
it('应显示当前推送时间', async () => {
|
||||
const wrapper = mountPage()
|
||||
triggerOnShow()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(wrapper.find('.time-value').exists()).toBe(true)
|
||||
expect(wrapper.find('.time-value').text()).toBe('07:00')
|
||||
})
|
||||
|
||||
it('点击"取消订阅"应弹出确认对话框', async () => {
|
||||
const wrapper = mountPage()
|
||||
triggerOnShow()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
wrapper.find('.unsubscribe-btn').trigger('tap')
|
||||
expect(uni.showModal).toHaveBeenCalled()
|
||||
const callArgs = (uni.showModal as any).mock.calls[0][0]
|
||||
expect(callArgs.title).toContain('确认取消')
|
||||
expect(callArgs.content).toContain('取消后将不再收到每日运势推送')
|
||||
})
|
||||
|
||||
it('确认取消订阅后应调用云函数并清除缓存', async () => {
|
||||
// 模拟 wx.cloud.callFunction 返回成功
|
||||
mockWx.cloud.callFunction.mockResolvedValue({
|
||||
result: { success: true },
|
||||
})
|
||||
|
||||
const wrapper = mountPage()
|
||||
triggerOnShow()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
// 触发取消订阅
|
||||
wrapper.find('.unsubscribe-btn').trigger('tap')
|
||||
|
||||
// 模拟确认对话框
|
||||
const modalCall = (uni.showModal as any).mock.calls[0][0]
|
||||
modalCall.success({ confirm: true })
|
||||
|
||||
// 等待异步操作完成(包括 setTimeout 1500ms)
|
||||
await new Promise((r) => setTimeout(r, 2000))
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
// 验证云函数被调用
|
||||
expect(mockWx.cloud.callFunction).toHaveBeenCalledWith({ name: 'unsubscribe' })
|
||||
// 验证本地缓存被清除
|
||||
expect(uni.removeStorageSync).toHaveBeenCalledWith('eis_push_subscription')
|
||||
// 验证提示
|
||||
expect(uni.showToast).toHaveBeenCalledWith({ title: '已取消订阅', icon: 'success' })
|
||||
// 验证返回上一页
|
||||
expect(uni.navigateBack).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('取消订阅失败时应提示"取消失败"', async () => {
|
||||
// 模拟云函数返回失败
|
||||
mockWx.cloud.callFunction.mockResolvedValue({
|
||||
result: { success: false, error: '未找到订阅记录' },
|
||||
})
|
||||
|
||||
const wrapper = mountPage()
|
||||
triggerOnShow()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
wrapper.find('.unsubscribe-btn').trigger('tap')
|
||||
const modalCall = (uni.showModal as any).mock.calls[0][0]
|
||||
modalCall.success({ confirm: true })
|
||||
|
||||
await new Promise((r) => setTimeout(r, 50))
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(uni.showToast).toHaveBeenCalledWith({ title: '取消失败,请重试', icon: 'none' })
|
||||
})
|
||||
|
||||
it('保存推送时间应调用云函数并更新本地缓存', async () => {
|
||||
mockWx.cloud.callFunction.mockResolvedValue({
|
||||
result: { success: true },
|
||||
})
|
||||
|
||||
const wrapper = mountPage()
|
||||
triggerOnShow()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
// 修改时间
|
||||
await wrapper.vm.onTimeChange({ detail: { value: '08:00' } })
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
// 保存
|
||||
wrapper.find('.save-btn').trigger('tap')
|
||||
await new Promise((r) => setTimeout(r, 50))
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
// 验证云函数被调用
|
||||
expect(mockWx.cloud.callFunction).toHaveBeenCalledWith({
|
||||
name: 'updatePushTime',
|
||||
data: { pushTime: '08:00' },
|
||||
})
|
||||
// 验证提示
|
||||
expect(uni.showToast).toHaveBeenCalledWith({ title: '保存成功', icon: 'success' })
|
||||
})
|
||||
|
||||
it('未订阅时,应使用默认时间 07:00', async () => {
|
||||
// 清空存储
|
||||
;(globalThis as any).__uniStorage__ = {}
|
||||
|
||||
const wrapper = mountPage()
|
||||
triggerOnShow()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(wrapper.vm.selectedTime).toBe('07:00')
|
||||
})
|
||||
|
||||
it('时间选择应四舍五入到 30 分钟步长', async () => {
|
||||
const wrapper = mountPage()
|
||||
triggerOnShow()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
// 07:08 → 07:00
|
||||
wrapper.vm.onTimeChange({ detail: { value: '07:08' } })
|
||||
expect(wrapper.vm.selectedTime).toBe('07:00')
|
||||
|
||||
// 07:15 → 07:30
|
||||
wrapper.vm.onTimeChange({ detail: { value: '07:15' } })
|
||||
expect(wrapper.vm.selectedTime).toBe('07:30')
|
||||
|
||||
// 07:45 → 08:00
|
||||
wrapper.vm.onTimeChange({ detail: { value: '07:45' } })
|
||||
expect(wrapper.vm.selectedTime).toBe('08:00')
|
||||
|
||||
// 22:45 → 22:00(上限 22:00)
|
||||
wrapper.vm.onTimeChange({ detail: { value: '22:45' } })
|
||||
expect(wrapper.vm.selectedTime).toBe('22:00')
|
||||
|
||||
// 05:30 → 06:30(下限 06:00,分钟 30 保留)
|
||||
wrapper.vm.onTimeChange({ detail: { value: '05:30' } })
|
||||
expect(wrapper.vm.selectedTime).toBe('06:30')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user