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 '@/core/access/accessPreview' import { useAuthStore } from '@/core/auth/auth' import LoginView from './LoginView.vue' const loginSource = readFileSync(resolve(process.cwd(), 'src/modules/auth/pages/LoginView.vue'), 'utf8') const formStub = { template: '
', setup(_: unknown, { expose }: { expose: (exposed: { validate: () => Promise }) => void }) { expose({ validate: async () => true }) }, } function createMemoryStorage(): Storage { const values = new Map() 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 authentication lifecycle', () => { beforeEach(() => { vi.stubGlobal('localStorage', createMemoryStorage()) }) it('always renders a blank login form without demo credentials or hints', () => { expect(loginSource).toContain("account: ''") expect(loginSource).toContain("password: ''") expect(loginSource).not.toContain('import.meta.env.DEV') expect(loginSource).not.toContain('platform_admin') expect(loginSource).not.toContain('ChangeMe123!') expect(loginSource).not.toContain('demo-tip') }) 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: '

home

' } }, ], }) 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': formStub, 'el-form-item': { template: '' }, 'el-input': { props: ['modelValue'], emits: ['update:modelValue'], template: '' }, 'el-button': { emits: ['click'], template: '' }, }, }, }) const inputs = wrapper.findAll('input') await inputs[0]!.setValue('test_admin') await inputs[1]!.setValue('password') await wrapper.get('form').trigger('submit') await flushPromises() expect(calls).toEqual(['login', 'access', 'replace']) }) it.each([ ['/customers?page=2', '/customers?page=2'], ['/customers/reviews', '/customers/reviews'], ['//example.com', '/403'], ['https://example.com', '/403'], [['/customers', '/orders/first-leg'], '/403'], [null, '/403'], ])('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: '

home

' } }, ], }) 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': formStub, 'el-form-item': { template: '' }, 'el-input': { props: ['modelValue'], emits: ['update:modelValue'], template: '' }, 'el-button': { emits: ['click'], template: '' }, }, }, }) const inputs = wrapper.findAll('input') await inputs[0]!.setValue('test_admin') await inputs[1]!.setValue('password') await wrapper.get('form').trigger('submit') 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: '

customers

' } }, ], }) 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': formStub, 'el-form-item': { template: '' }, 'el-input': { props: ['modelValue'], emits: ['update:modelValue'], template: '' }, 'el-button': { emits: ['click'], template: '' }, }, }, }) const inputs = wrapper.findAll('input') await inputs[0]!.setValue('test_admin') await inputs[1]!.setValue('password') await wrapper.get('form').trigger('submit') await flushPromises() expect(wrapper.get('[role="alert"]').text()).toBe('network unavailable') expect(initialize).not.toHaveBeenCalled() expect(replace).not.toHaveBeenCalled() }) })