fix(announcements): report failed read confirmations accurately

This commit is contained in:
chihao.ou
2026-09-30 02:45:09 +08:00
parent a60a29549f
commit cee76030be
3 changed files with 49 additions and 2 deletions
@@ -373,14 +373,17 @@ function closeDetail() {
async function markAsRead(id: number) {
try {
await announcementStore.markAsRead(id)
const marked = await announcementStore.markAsRead(id)
if (!marked) appStore.showError(t('common.unknownError'))
return marked
} catch (err: any) {
appStore.showError(err?.message || t('common.unknownError'))
return false
}
}
async function markAsReadAndClose(id: number) {
await markAsRead(id)
if (!await markAsRead(id)) return
appStore.showSuccess(t('announcements.markedAsRead'))
closeDetail()
}
@@ -0,0 +1,42 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import AnnouncementBell from '../AnnouncementBell.vue'
import { useAnnouncementStore } from '@/stores/announcements'
const { markRead, showError, showSuccess } = vi.hoisted(() => ({ markRead: vi.fn(), showError: vi.fn(), showSuccess: vi.fn() }))
vi.mock('@/api', () => ({ announcementsAPI: { markRead } }))
vi.mock('@/stores/app', () => ({ useAppStore: () => ({ showError, showSuccess }) }))
vi.mock('vue-i18n', () => ({ useI18n: () => ({ t: (key: string) => key }) }))
vi.mock('@/utils/format', () => ({ formatRelativeTime: () => 'now', formatRelativeWithDateTime: () => 'now' }))
enableAutoUnmount(afterEach)
beforeEach(() => {
setActivePinia(createPinia())
vi.clearAllMocks()
vi.spyOn(console, 'error').mockImplementation(() => {})
useAnnouncementStore().announcements = [{ id: 1, title: 'Notice', content: 'Details', notify_mode: 'silent', created_at: '2026-09-30', updated_at: '2026-09-30' }]
})
afterEach(() => { vi.restoreAllMocks(); document.body.style.overflow = '' })
describe('announcement read confirmation', () => {
it('keeps a failed read confirmation open and only reports success after retry', async () => {
markRead.mockRejectedValue(new Error('offline'))
const wrapper = mount(AnnouncementBell, { global: { stubs: { Teleport: true, Transition: true, Icon: true } } })
await wrapper.get('button').trigger('click')
await wrapper.get('.group.relative').trigger('click')
await flushPromises()
const confirm = () => wrapper.findAll('button').find(button => button.text() === 'announcements.markRead')!
await confirm().trigger('click')
await flushPromises()
expect(markRead).toHaveBeenCalledTimes(2)
expect(showSuccess).not.toHaveBeenCalled()
expect(showError).toHaveBeenCalled()
expect(wrapper.find('.markdown-body').exists()).toBe(true)
markRead.mockResolvedValue(undefined)
await confirm().trigger('click')
await flushPromises()
expect(showSuccess).toHaveBeenCalledWith('announcements.markedAsRead')
expect(wrapper.find('.markdown-body').exists()).toBe(false)
expect(useAnnouncementStore().unreadCount).toBe(0)
})
})
+2
View File
@@ -96,8 +96,10 @@ export const useAnnouncementStore = defineStore('announcements', () => {
if (ann) {
ann.read_at = new Date().toISOString()
}
return true
} catch (err: any) {
console.error('Failed to mark announcement as read:', err)
return false
}
}