// @vitest-environment jsdom import { beforeEach, describe, expect, it, vi } from 'vitest' import { defineComponent, nextTick } from 'vue' import { flushPromises, mount } from '@vue/test-utils' import ArcoVue from '@arco-design/web-vue' const mockPush = vi.hoisted(() => vi.fn()) const mockFetchCurrentUser = vi.hoisted(() => vi.fn()) const mockGetApiKey = vi.hoisted(() => vi.fn()) const routeState = vi.hoisted(() => ({ fullPath: '/hermes/chat', name: 'hermes.chat' as any })) vi.mock('vue-router', () => ({ useRoute: () => routeState, useRouter: () => ({ push: mockPush }), })) vi.mock('vue-i18n', () => ({ useI18n: () => ({ t: (key: string) => key, }), })) vi.mock('@/api/auth', () => ({ fetchCurrentUser: mockFetchCurrentUser, })) vi.mock('@/api/client', () => ({ getApiKey: mockGetApiKey, })) // a-modal teleports to body in jsdom, so render it inline through a stub keyed // by Arco's internal component name. const AModalStub = defineComponent({ name: 'Modal', props: { visible: Boolean, title: String }, emits: ['update:visible'], template: '

{{ title }}

', }) const arcoGlobal = { plugins: [ArcoVue], stubs: { Modal: AModalStub } } function buttonByText(wrapper: any, text: string) { const button = wrapper.findAll('button').find((node: any) => node.text().trim() === text) if (!button) throw new Error(`no button labelled "${text}"`) return button } import DefaultCredentialPrompt from '@/components/auth/DefaultCredentialPrompt.vue' describe('DefaultCredentialPrompt', () => { beforeEach(() => { sessionStorage.clear() vi.clearAllMocks() routeState.fullPath = '/hermes/chat' routeState.name = 'hermes.chat' mockGetApiKey.mockReturnValue('jwt-token') }) it('prompts after login when the current user still has default credentials', async () => { mockFetchCurrentUser.mockResolvedValue({ id: 1, username: 'admin', role: 'super_admin', status: 'active', created_at: 1, updated_at: 1, last_login_at: 1, requiresCredentialChange: true, }) const wrapper = mount(DefaultCredentialPrompt, { global: arcoGlobal }) await flushPromises() await nextTick() expect(mockFetchCurrentUser).toHaveBeenCalledOnce() expect(wrapper.text()).toContain('login.defaultCredentialMessage') await buttonByText(wrapper, 'login.defaultCredentialAction').trigger('click') expect(mockPush).toHaveBeenCalledWith({ name: 'hermes.settings', query: { tab: 'account' } }) }) it('does not prompt on the login route', async () => { routeState.fullPath = '/' routeState.name = 'login' mount(DefaultCredentialPrompt, { global: arcoGlobal }) await Promise.resolve() expect(mockFetchCurrentUser).not.toHaveBeenCalled() }) })