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: '
' }, }, }, }) } 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') }) })