fix frontend phase0 bugs
This commit is contained in:
@@ -85,6 +85,19 @@ describe('Login Page', () => {
|
||||
expect(html).toContain('登录')
|
||||
})
|
||||
|
||||
// 辅助:填写表单并提交,绕过 Ant Design 表单验证
|
||||
async function submitLogin(
|
||||
fields: { username: string; password: string; remember?: boolean },
|
||||
) {
|
||||
const vm = wrapper.getCurrentComponent().setupState
|
||||
vm.formState.username = fields.username
|
||||
vm.formState.password = fields.password
|
||||
if (fields.remember !== undefined) vm.formState.remember = fields.remember
|
||||
vm.formRef = { validate: vi.fn().mockResolvedValue(true) }
|
||||
await vm.handleSubmit()
|
||||
await flushPromises()
|
||||
}
|
||||
|
||||
it('calls /api/v1/login, sets token/user and redirects to /', async () => {
|
||||
const { api } = await import('@/utils/request')
|
||||
const mockResponse = {
|
||||
@@ -96,17 +109,7 @@ describe('Login Page', () => {
|
||||
|
||||
await mountPage()
|
||||
const store = useUserStore()
|
||||
|
||||
// 直接调用 handleSubmit 来绕过表单验证
|
||||
// 通过组件 vm 访问 setup 暴露的方法
|
||||
const vm = wrapper.getCurrentComponent().setupState
|
||||
vm.formState.username = 'admin'
|
||||
vm.formState.password = 'password'
|
||||
vm.formRef = { value: { validate: vi.fn().mockResolvedValue(true) } }
|
||||
// mock formRef.validate
|
||||
vm.formRef = { validate: vi.fn().mockResolvedValue(true) }
|
||||
await vm.handleSubmit()
|
||||
await flushPromises()
|
||||
await submitLogin({ username: 'admin', password: 'password' })
|
||||
|
||||
expect(api.post).toHaveBeenCalledWith('/api/v1/login', {
|
||||
username: 'admin',
|
||||
@@ -128,13 +131,7 @@ describe('Login Page', () => {
|
||||
})
|
||||
|
||||
await mountPage()
|
||||
const vm = wrapper.getCurrentComponent().setupState
|
||||
vm.formState.username = 'user'
|
||||
vm.formState.password = 'pass'
|
||||
vm.formState.remember = false
|
||||
vm.formRef = { validate: vi.fn().mockResolvedValue(true) }
|
||||
await vm.handleSubmit()
|
||||
await flushPromises()
|
||||
await submitLogin({ username: 'user', password: 'pass', remember: false })
|
||||
|
||||
expect(localStorage.getItem('access_token')).toBeNull()
|
||||
expect(localStorage.getItem('user')).toBeNull()
|
||||
@@ -150,12 +147,7 @@ describe('Login Page', () => {
|
||||
mockRoute.query = { redirect: '/dashboard' }
|
||||
|
||||
await mountPage()
|
||||
const vm = wrapper.getCurrentComponent().setupState
|
||||
vm.formState.username = 'user'
|
||||
vm.formState.password = 'pass'
|
||||
vm.formRef = { validate: vi.fn().mockResolvedValue(true) }
|
||||
await vm.handleSubmit()
|
||||
await flushPromises()
|
||||
await submitLogin({ username: 'user', password: 'pass' })
|
||||
|
||||
vi.advanceTimersByTime(500)
|
||||
expect(mockPush).toHaveBeenCalledWith('/dashboard')
|
||||
@@ -171,12 +163,7 @@ describe('Login Page', () => {
|
||||
mockRoute.query = { redirect: '//evil.com' }
|
||||
|
||||
await mountPage()
|
||||
const vm = wrapper.getCurrentComponent().setupState
|
||||
vm.formState.username = 'user'
|
||||
vm.formState.password = 'pass'
|
||||
vm.formRef = { validate: vi.fn().mockResolvedValue(true) }
|
||||
await vm.handleSubmit()
|
||||
await flushPromises()
|
||||
await submitLogin({ username: 'user', password: 'pass' })
|
||||
|
||||
vi.advanceTimersByTime(500)
|
||||
expect(mockPush).toHaveBeenCalledWith('/')
|
||||
@@ -192,12 +179,7 @@ describe('Login Page', () => {
|
||||
mockRoute.query = { redirect: 'http://evil.com' }
|
||||
|
||||
await mountPage()
|
||||
const vm = wrapper.getCurrentComponent().setupState
|
||||
vm.formState.username = 'user'
|
||||
vm.formState.password = 'pass'
|
||||
vm.formRef = { validate: vi.fn().mockResolvedValue(true) }
|
||||
await vm.handleSubmit()
|
||||
await flushPromises()
|
||||
await submitLogin({ username: 'user', password: 'pass' })
|
||||
|
||||
vi.advanceTimersByTime(500)
|
||||
expect(mockPush).toHaveBeenCalledWith('/')
|
||||
@@ -209,12 +191,7 @@ describe('Login Page', () => {
|
||||
const errorSpy = vi.spyOn(message, 'error').mockImplementation(() => ({}) as any)
|
||||
|
||||
await mountPage()
|
||||
const vm = wrapper.getCurrentComponent().setupState
|
||||
vm.formState.username = 'user'
|
||||
vm.formState.password = 'wrong'
|
||||
vm.formRef = { validate: vi.fn().mockResolvedValue(true) }
|
||||
await vm.handleSubmit()
|
||||
await flushPromises()
|
||||
await submitLogin({ username: 'user', password: 'wrong' })
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith('用户名或密码错误')
|
||||
errorSpy.mockRestore()
|
||||
@@ -226,12 +203,7 @@ describe('Login Page', () => {
|
||||
const errorSpy = vi.spyOn(message, 'error').mockImplementation(() => ({}) as any)
|
||||
|
||||
await mountPage()
|
||||
const vm = wrapper.getCurrentComponent().setupState
|
||||
vm.formState.username = 'user'
|
||||
vm.formState.password = 'pass'
|
||||
vm.formRef = { validate: vi.fn().mockResolvedValue(true) }
|
||||
await vm.handleSubmit()
|
||||
await flushPromises()
|
||||
await submitLogin({ username: 'user', password: 'pass' })
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith('登录失败,请检查网络连接')
|
||||
errorSpy.mockRestore()
|
||||
|
||||
@@ -96,8 +96,9 @@ describe('useUserStore', () => {
|
||||
})
|
||||
|
||||
describe('setUser', () => {
|
||||
it('sets user info in store and localStorage', () => {
|
||||
it('persists to localStorage when remember=true', () => {
|
||||
const store = useUserStore()
|
||||
store.setToken('h.p.s', 'r.p.s', true)
|
||||
const userInfo = { id: 1, username: 'test', email: 't@t.com', role: 'user', status: 1 }
|
||||
|
||||
store.setUser(userInfo)
|
||||
@@ -108,7 +109,17 @@ describe('useUserStore', () => {
|
||||
expect(JSON.parse(localStorage.getItem('user')!)).toEqual(userInfo)
|
||||
})
|
||||
|
||||
it('does not write to localStorage when remember=false', () => {
|
||||
it('does not persist to localStorage by default (remember=false)', () => {
|
||||
const store = useUserStore()
|
||||
const userInfo = { id: 1, username: 'test', email: 't@t.com', role: 'user', status: 1 }
|
||||
|
||||
store.setUser(userInfo)
|
||||
|
||||
expect(store.user).toEqual(userInfo)
|
||||
expect(localStorage.getItem('user')).toBeNull()
|
||||
})
|
||||
|
||||
it('does not write to localStorage after setToken(remember=false)', () => {
|
||||
const store = useUserStore()
|
||||
store.setToken('h.p.s', 'r.p.s', false)
|
||||
|
||||
@@ -136,6 +147,19 @@ describe('useUserStore', () => {
|
||||
expect(localStorage.getItem('refresh_token')).toBeNull()
|
||||
expect(localStorage.getItem('user')).toBeNull()
|
||||
})
|
||||
|
||||
it('resets _remember so subsequent setUser does not persist', () => {
|
||||
const store = useUserStore()
|
||||
store.setToken('h.p.s', 'r.p.s', true)
|
||||
store.setUser({ id: 1, username: 'a', email: 'a@a.com', role: 'admin', status: 1 })
|
||||
expect(localStorage.getItem('user')).not.toBeNull()
|
||||
|
||||
store.logout()
|
||||
store.setUser({ id: 2, username: 'b', email: 'b@b.com', role: 'user', status: 1 })
|
||||
|
||||
expect(store.user?.username).toBe('b')
|
||||
expect(localStorage.getItem('user')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('fetchCurrentUser', () => {
|
||||
|
||||
@@ -12,7 +12,8 @@ export const useUserStore = defineStore('user', () => {
|
||||
const token = ref<string | null>(localStorage.getItem('access_token'))
|
||||
const refreshToken = ref<string | null>(localStorage.getItem('refresh_token'))
|
||||
const user = ref<UserInfo | null>(null)
|
||||
const _remember = ref(true)
|
||||
// 默认不持久化;从 localStorage 推断:有 token 说明上次选了"记住我"
|
||||
const _remember = ref(!!localStorage.getItem('access_token'))
|
||||
|
||||
// 基本 JWT 格式校验(三段式),防止垃圾值绕过路由守卫
|
||||
const isLoggedIn = computed(() => {
|
||||
@@ -30,7 +31,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
localStorage.setItem('access_token', accessToken)
|
||||
localStorage.setItem('refresh_token', newRefreshToken)
|
||||
} else {
|
||||
// 不记住:清除持久化,token 仅存于内存,关闭标签页即失效
|
||||
// 不记住:清除全部持久化数据,token 仅存于内存,关闭标签页即失效
|
||||
localStorage.removeItem('access_token')
|
||||
localStorage.removeItem('refresh_token')
|
||||
localStorage.removeItem('user')
|
||||
@@ -54,6 +55,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
token.value = null
|
||||
refreshToken.value = null
|
||||
user.value = null
|
||||
_remember.value = false
|
||||
localStorage.removeItem('access_token')
|
||||
localStorage.removeItem('refresh_token')
|
||||
localStorage.removeItem('user')
|
||||
|
||||
Reference in New Issue
Block a user