import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { setActivePinia, createPinia } from 'pinia'
const loadSession = vi.fn()
vi.mock('@/stores/chat', () => ({
useChatStore: () => ({ currentId: null, loading: false, loadSession }),
}))
vi.mock('@/stores/sessions', () => ({
useSessionsStore: () => ({ sessions: [], nameById: {}, fetchSessions: vi.fn() }),
}))
vi.mock('@/stores/profiles', () => ({
useProfilesStore: () => ({ selectedProfileId: null, fetchProfiles: vi.fn() }),
}))
vi.mock('@/stores/auth', () => ({
useAuthStore: () => ({
authConfigured: false,
isAuthenticated: true,
loading: false,
fetchStatus: vi.fn(),
fetchMe: vi.fn(),
}),
}))
vi.mock('gnexus-ui-kit/vue', () => ({
GnToastProvider: { name: 'GnToastProvider', template: '<div><slot /></div>' },
}))
// Factories are hoisted above the module they mock, so each stub is written
// out in full instead of going through a helper.
vi.mock('@/components/sidebar/AppSidebar.vue', () => ({
default: { name: 'AppSidebar', template: '<div data-test="sidebar" />' },
}))
vi.mock('@/components/ui/ConfirmDialog.vue', () => ({
default: { name: 'ConfirmDialog', template: '<div data-test="confirm" />' },
}))
vi.mock('@/components/ui/ImageLightbox.vue', () => ({
default: { name: 'ImageLightbox', template: '<div data-test="lightbox" />' },
}))
vi.mock('@/components/ui/SelectionToolbar.vue', () => ({
default: { name: 'SelectionToolbar', template: '<div data-test="selection" />' },
}))
vi.mock('@/components/ui/OfflineBanner.vue', () => ({
default: { name: 'OfflineBanner', template: '<div data-test="offline" />' },
}))
vi.mock('@/components/ui/LoginScreen.vue', () => ({
default: { name: 'LoginScreen', template: '<div data-test="login" />' },
}))
vi.mock('@/components/ui/WelcomeScreen.vue', () => ({
default: { name: 'WelcomeScreen', template: '<div data-test="welcome" />' },
}))
vi.mock('@/components/chat/ChatArea.vue', () => ({
default: { name: 'ChatArea', template: '<div data-test="chat" />' },
}))
vi.mock('@/components/artifacts/ArtifactsPanel.vue', () => ({
default: { name: 'ArtifactsPanel', template: '<div data-test="artifacts" />' },
}))
vi.mock('@/components/settings/SettingsView.vue', () => ({
default: { name: 'SettingsView', template: '<div data-test="settings" />' },
}))
import App from '@/App.vue'
function setHash(hash) {
history.replaceState(null, '', hash ?? location.pathname)
}
async function mountAt(hash) {
setHash(hash)
const wrapper = mount(App)
await flushPromises()
return wrapper
}
// `#settings/app` names a settings section, not a session. If the hash parser
// ever loses that distinction the app quietly tries to open a session whose id
// is "settings/app" — an empty chat instead of the settings screen.
describe('routing on the hash', () => {
beforeEach(() => {
setActivePinia(createPinia())
loadSession.mockReset()
})
afterEach(setHash)
it('routes a sectioned settings hash to Settings', async () => {
const wrapper = await mountAt('#settings/app')
expect(wrapper.find('[data-test="settings"]').exists()).toBe(true)
expect(wrapper.find('[data-test="chat"]').exists()).toBe(false)
expect(loadSession).not.toHaveBeenCalled()
})
it('routes a bare settings hash to Settings', async () => {
const wrapper = await mountAt('#settings')
expect(wrapper.find('[data-test="settings"]').exists()).toBe(true)
expect(loadSession).not.toHaveBeenCalled()
})
it('still opens a session for a session id', async () => {
const wrapper = await mountAt('#3f9c2b1e-4d6a-4a1f-9c2e-8b7d0e5a1c33')
expect(loadSession).toHaveBeenCalledWith('3f9c2b1e-4d6a-4a1f-9c2e-8b7d0e5a1c33')
expect(wrapper.find('[data-test="settings"]').exists()).toBe(false)
})
it('leaves settings when the hash becomes a session id', async () => {
const wrapper = await mountAt('#settings/mcp')
expect(wrapper.find('[data-test="settings"]').exists()).toBe(true)
setHash('#3f9c2b1e-4d6a-4a1f-9c2e-8b7d0e5a1c33')
window.dispatchEvent(new Event('hashchange'))
await flushPromises()
expect(loadSession).toHaveBeenCalledWith('3f9c2b1e-4d6a-4a1f-9c2e-8b7d0e5a1c33')
})
})