fix(notification): handle empty channel configuration

This commit is contained in:
jxxghp
2026-09-02 11:38:13 +08:00
parent 0024880577
commit 92464b754a
2 changed files with 26 additions and 4 deletions
@@ -83,7 +83,7 @@ type NotificationConfigInput = Partial<NotificationConf> & {
} }
interface NotificationConfigResponse { interface NotificationConfigResponse {
value?: NotificationConfigInput[] value?: NotificationConfigInput[] | null
} }
type NotificationLoadState = 'idle' | 'loading' | 'ready' | 'error' type NotificationLoadState = 'idle' | 'loading' | 'ready' | 'error'
@@ -184,8 +184,9 @@ function normalizeNotification(notification: NotificationConfigInput, index: num
} }
} }
function normalizeNotificationList(value: NotificationConfigInput[] = []) { /** 将后端尚未创建的空配置与已有渠道统一为可编辑列表。 */
return value.map((notification, index) => normalizeNotification(notification, index)) function normalizeNotificationList(value?: NotificationConfigInput[] | null) {
return (value ?? []).map((notification, index) => normalizeNotification(notification, index))
} }
function notificationNameKey(name: string) { function notificationNameKey(name: string) {
@@ -91,7 +91,7 @@ const templateFixture = {
subscribeComplete: '{}', subscribeComplete: '{}',
} }
function mockLoadedSettings(channels = notificationsFixture) { function mockLoadedSettings(channels: typeof notificationsFixture | null = notificationsFixture) {
mocks.apiGet.mockImplementation((endpoint: string) => { mocks.apiGet.mockImplementation((endpoint: string) => {
if (endpoint === 'system/setting/Notifications') { if (endpoint === 'system/setting/Notifications') {
return { success: true, data: { value: structuredClone(channels) } } return { success: true, data: { value: structuredClone(channels) } }
@@ -178,6 +178,27 @@ describe('AccountSettingNotification', () => {
) )
}) })
it('treats a null notification configuration as empty and allows the first channel to be saved', async () => {
mockLoadedSettings(null)
const user = userEvent.setup()
await renderNotificationSettings()
await waitFor(() => expect(mocks.apiGet).toHaveBeenCalledWith('system/setting/Notifications'))
expect(screen.queryByText('加载通知渠道失败,请刷新后重试')).not.toBeInTheDocument()
const channelCard = getCard('通知渠道')
await user.click(channelCard.getAllByRole('button').at(-1)!)
await user.click(await screen.findByText('Telegram', { selector: '.v-list-item-title' }))
expect(screen.getByText('通知1 / telegram')).toBeInTheDocument()
await user.click(channelCard.getByRole('button', { name: '保存' }))
await waitFor(() =>
expect(mocks.apiPost).toHaveBeenCalledWith('notification/config', [
expect.objectContaining({ id: expect.any(String), name: '通知1', type: 'telegram' }),
]),
)
})
it('creates a unique automatic channel name, removes channels, and saves the current order once', async () => { it('creates a unique automatic channel name, removes channels, and saves the current order once', async () => {
const user = userEvent.setup() const user = userEvent.setup()
await renderNotificationSettings() await renderNotificationSettings()