mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
fix(profile): show normalized API error messages
This commit is contained in:
@@ -44,6 +44,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { userAPI } from '@/api'
|
||||
import { extractApiErrorMessage } from '@/utils/apiError'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
initialUsername: string
|
||||
@@ -76,8 +77,8 @@ const handleUpdateProfile = async () => {
|
||||
})
|
||||
authStore.user = updatedUser
|
||||
appStore.showSuccess(t('profile.updateSuccess'))
|
||||
} catch (error: any) {
|
||||
appStore.showError(error.response?.data?.detail || t('profile.updateFailed'))
|
||||
} catch (error: unknown) {
|
||||
appStore.showError(extractApiErrorMessage(error, t('profile.updateFailed')))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { userAPI } from '@/api'
|
||||
import { extractApiErrorMessage } from '@/utils/apiError'
|
||||
|
||||
const { t } = useI18n()
|
||||
const appStore = useAppStore()
|
||||
@@ -107,8 +108,8 @@ const handleChangePassword = async () => {
|
||||
await userAPI.changePassword(form.value.old_password, form.value.new_password)
|
||||
form.value = { old_password: '', new_password: '', confirm_password: '' }
|
||||
appStore.showSuccess(t('profile.passwordChangeSuccess'))
|
||||
} catch (error: any) {
|
||||
appStore.showError(error.response?.data?.detail || t('profile.passwordChangeFailed'))
|
||||
} catch (error: unknown) {
|
||||
appStore.showError(extractApiErrorMessage(error, t('profile.passwordChangeFailed')))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import ProfileEditForm from '@/components/user/profile/ProfileEditForm.vue'
|
||||
|
||||
const { updateProfileMock, showErrorMock, authState } = vi.hoisted(() => ({
|
||||
updateProfileMock: vi.fn(),
|
||||
showErrorMock: vi.fn(),
|
||||
authState: { user: { username: 'alice' } },
|
||||
}))
|
||||
|
||||
vi.mock('@/api', () => ({
|
||||
userAPI: { updateProfile: updateProfileMock },
|
||||
}))
|
||||
vi.mock('@/stores/auth', () => ({
|
||||
useAuthStore: () => authState,
|
||||
}))
|
||||
vi.mock('@/stores/app', () => ({
|
||||
useAppStore: () => ({ showError: showErrorMock }),
|
||||
}))
|
||||
vi.mock('vue-i18n', () => ({
|
||||
useI18n: () => ({ t: (key: string) => key }),
|
||||
}))
|
||||
|
||||
describe('ProfileEditForm', () => {
|
||||
it.each([
|
||||
[{ status: 400, code: 'VALIDATION_ERROR', message: 'username is too long' }, 'username is too long'],
|
||||
[{ response: { data: { detail: 'backend failure' } } }, 'backend failure'],
|
||||
[{}, 'profile.updateFailed'],
|
||||
])('shows API failure %j without changing the saved profile', async (error, expectedMessage) => {
|
||||
updateProfileMock.mockRejectedValue(error)
|
||||
const wrapper = mount(ProfileEditForm, { props: { initialUsername: 'alice' } })
|
||||
|
||||
await wrapper.get('#username').setValue('new-name')
|
||||
await wrapper.get('form').trigger('submit.prevent')
|
||||
|
||||
expect(updateProfileMock).toHaveBeenCalledWith({ username: 'new-name' })
|
||||
expect(showErrorMock).toHaveBeenLastCalledWith(expectedMessage)
|
||||
expect(authState.user.username).toBe('alice')
|
||||
expect(wrapper.get('button[type="submit"]').attributes('disabled')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
@@ -60,10 +60,12 @@ describe('ProfilePasswordForm', () => {
|
||||
expect(wrapper.find('.input-error-text').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('shows API failures as toast messages', async () => {
|
||||
changePasswordMock.mockRejectedValue({
|
||||
response: { data: { detail: 'backend failure' } }
|
||||
})
|
||||
it.each([
|
||||
[{ status: 400, code: 'PASSWORD_INCORRECT', message: 'current password is incorrect' }, 'current password is incorrect'],
|
||||
[{ response: { data: { detail: 'backend failure' } } }, 'backend failure'],
|
||||
[{}, 'Failed to change password'],
|
||||
])('shows API failure %j as a toast', async (error, expectedMessage) => {
|
||||
changePasswordMock.mockRejectedValue(error)
|
||||
|
||||
const wrapper = mount(ProfilePasswordForm)
|
||||
|
||||
@@ -73,7 +75,7 @@ describe('ProfilePasswordForm', () => {
|
||||
await wrapper.get('form').trigger('submit.prevent')
|
||||
|
||||
expect(changePasswordMock).toHaveBeenCalledWith('old-password', 'new-password')
|
||||
expect(showErrorMock).toHaveBeenCalledWith('backend failure')
|
||||
expect(showErrorMock).toHaveBeenLastCalledWith(expectedMessage)
|
||||
expect(wrapper.find('.input-error-text').exists()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user