Files
admin-web/src/views/LoginView.test.ts
T

151 lines
5.6 KiB
TypeScript
Raw Normal View History

2026-07-30 17:59:43 +08:00
import { createPinia, setActivePinia } from 'pinia'
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { flushPromises, mount } from '@vue/test-utils'
import { createMemoryHistory, createRouter } from 'vue-router'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { useAccessPreviewStore } from '@/stores/accessPreview'
import { useAuthStore } from '@/stores/auth'
import LoginView from './LoginView.vue'
const loginSource = readFileSync(resolve(process.cwd(), 'src/views/LoginView.vue'), 'utf8')
function createMemoryStorage(): Storage {
const values = new Map<string, string>()
return {
get length() { return values.size },
clear: () => values.clear(),
getItem: (key) => values.get(key) ?? null,
key: (index) => [...values.keys()][index] ?? null,
removeItem: (key) => values.delete(key),
setItem: (key, value) => values.set(key, value),
}
}
describe('LoginView access preview lifecycle', () => {
beforeEach(() => {
vi.stubGlobal('localStorage', createMemoryStorage())
})
it('only exposes the integration account hint through the development environment branch', () => {
expect(loginSource).toContain('const isDevelopment = import.meta.env.DEV')
expect(loginSource).toContain('v-if="developmentHint" class="demo-tip"')
expect(loginSource).toContain("account: isDevelopment ? 'platform_admin' : ''")
expect(loginSource).toContain("password: isDevelopment ? 'ChangeMe123!' : ''")
})
it('initializes preview access after login and before redirecting', async () => {
const pinia = createPinia()
setActivePinia(pinia)
const auth = useAuthStore()
const access = useAccessPreviewStore()
const router = createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/login', component: LoginView },
{ path: '/', component: { template: '<p>home</p>' } },
],
})
await router.push('/login')
await router.isReady()
const calls: string[] = []
vi.spyOn(auth, 'login').mockImplementation(async () => { calls.push('login') })
vi.spyOn(access, 'initializeAfterLogin').mockImplementation(() => { calls.push('access') })
vi.spyOn(router, 'replace').mockImplementation(async () => { calls.push('replace') })
const wrapper = mount(LoginView, {
global: {
plugins: [pinia, router],
stubs: {
'el-form': { template: '<form><slot /></form>' },
'el-form-item': { template: '<label><slot /></label>' },
'el-input': { template: '<input />' },
'el-button': { emits: ['click'], template: '<button type="button" @click="$emit(\'click\')"><slot /></button>' },
},
},
})
await wrapper.get('.login-submit').trigger('click')
await flushPromises()
expect(calls).toEqual(['login', 'access', 'replace'])
})
it.each([
['/customers?page=2', '/customers?page=2'],
['/customers/reviews', '/customers/reviews'],
['//example.com', '/customers'],
['https://example.com', '/customers'],
[['/customers', '/orders/first-leg'], '/customers'],
[null, '/customers'],
])('sanitizes the post-login redirect %j to %s', async (redirect, expected) => {
const pinia = createPinia()
setActivePinia(pinia)
const auth = useAuthStore()
const router = createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/login', component: LoginView },
{ path: '/', component: { template: '<p>home</p>' } },
],
})
await router.push({ path: '/login', query: { redirect } })
await router.isReady()
vi.spyOn(auth, 'login').mockResolvedValue()
const replace = vi.spyOn(router, 'replace').mockResolvedValue()
const wrapper = mount(LoginView, {
global: {
plugins: [pinia, router],
stubs: {
'el-form': { template: '<form><slot /></form>' },
'el-form-item': { template: '<label><slot /></label>' },
'el-input': { template: '<input />' },
'el-button': { emits: ['click'], template: '<button type="button" @click="$emit(\'click\')"><slot /></button>' },
},
},
})
await wrapper.get('.login-submit').trigger('click')
await flushPromises()
expect(replace).toHaveBeenCalledWith(expected)
})
it('preserves the form and reports a recoverable error when login fails', async () => {
const pinia = createPinia()
setActivePinia(pinia)
const auth = useAuthStore()
const access = useAccessPreviewStore()
const router = createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/login', component: LoginView },
{ path: '/customers', component: { template: '<p>customers</p>' } },
],
})
await router.push('/login')
await router.isReady()
vi.spyOn(auth, 'login').mockRejectedValue(new Error('network unavailable'))
const initialize = vi.spyOn(access, 'initializeAfterLogin')
const replace = vi.spyOn(router, 'replace')
const wrapper = mount(LoginView, {
global: {
plugins: [pinia, router],
stubs: {
'el-form': { template: '<form><slot /></form>' },
'el-form-item': { template: '<label><slot /></label>' },
'el-input': { template: '<input />' },
'el-button': { emits: ['click'], template: '<button type="button" @click="$emit(\'click\')"><slot /></button>' },
},
},
})
await wrapper.get('.login-submit').trigger('click')
await flushPromises()
expect(wrapper.get('[role="alert"]').text()).toBe('network unavailable')
expect(initialize).not.toHaveBeenCalled()
expect(replace).not.toHaveBeenCalled()
})
})