Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion frontend/src/store/modules/account/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import teamApi from '../../../api/team.js'
import userApi from '../../../api/user.js'
import { getTeamProperty } from '../../../composables/TeamProperties.js'
import router from '../../../routes.js'

import product from '../../../services/product.js'

import { useContextStore } from '@/stores/context.js'
import { useProductAssistantStore } from '@/stores/product-assistant.js'
import { useProductExpertInsightsAgentStore } from '@/stores/product-expert-insights-agent.js'
import { useUxDialogStore } from '@/stores/ux-dialog.js'
import { useUxDrawersStore } from '@/stores/ux-drawers.js'
import { useUxNavigationStore } from '@/stores/ux-navigation.js'
Expand Down Expand Up @@ -528,7 +530,7 @@ const actions = {
// Task 6: useProductTablesStore().$reset()
// Task 7: useProductBrokersStore().$reset()
useProductAssistantStore().$reset()
// Task 9: useProductExpertFfAgentStore().$reset()
useProductExpertInsightsAgentStore().$reset()
// Task 10: useProductExpertOperatorAgentStore().$reset()
// Task 11: useProductExpertStore().$reset()
}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/stores/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { useUxNavigationStore } from './ux-navigation.js'
export { useUxStore } from './ux.js'
export { useUxDialogStore } from './ux-dialog.js'
export { useUxToursStore } from './ux-tours.js'
export { useProductExpertInsightsAgentStore } from './product-expert-insights-agent.js'
30 changes: 30 additions & 0 deletions frontend/src/stores/product-expert-insights-agent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { defineStore } from 'pinia'
import { markRaw } from 'vue'

export const useProductExpertInsightsAgentStore = defineStore('product-expert-insights-agent', {
state: () => ({
context: null,
sessionId: null,
messages: [],

// Session timing
abortController: null,
sessionStartTime: null,
sessionWarningShown: false,
sessionExpiredShown: false,
sessionCheckTimer: null
}),
actions: {
reset () {
if (this.sessionCheckTimer) clearInterval(this.sessionCheckTimer)
this.$reset()
},
setSessionCheckTimer (timer) {
this.sessionCheckTimer = markRaw(timer)
}
},
persist: {
pick: ['context'],
storage: localStorage
}
})
157 changes: 157 additions & 0 deletions test/unit/frontend/stores/product-expert-insights-agent.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { createPinia, setActivePinia } from 'pinia'
import { beforeEach, describe, expect, it, vi } from 'vitest'

import { useProductExpertInsightsAgentStore } from '@/stores/product-expert-insights-agent.js'

describe('useProductExpertInsightsAgentStore', () => {
beforeEach(() => {
setActivePinia(createPinia())
})

describe('initial state', () => {
it('has null context', () => {
const store = useProductExpertInsightsAgentStore()
expect(store.context).toBeNull()
})

it('has null sessionId', () => {
const store = useProductExpertInsightsAgentStore()
expect(store.sessionId).toBeNull()
})

it('has empty messages array', () => {
const store = useProductExpertInsightsAgentStore()
expect(store.messages).toEqual([])
})

it('has null sessionStartTime', () => {
const store = useProductExpertInsightsAgentStore()
expect(store.sessionStartTime).toBeNull()
})

it('has sessionWarningShown false', () => {
const store = useProductExpertInsightsAgentStore()
expect(store.sessionWarningShown).toBe(false)
})

it('has sessionExpiredShown false', () => {
const store = useProductExpertInsightsAgentStore()
expect(store.sessionExpiredShown).toBe(false)
})

it('has null abortController', () => {
const store = useProductExpertInsightsAgentStore()
expect(store.abortController).toBeNull()
})

it('has null sessionCheckTimer', () => {
const store = useProductExpertInsightsAgentStore()
expect(store.sessionCheckTimer).toBeNull()
})
})

describe('setSessionCheckTimer', () => {
it('stores the timer reference', () => {
const store = useProductExpertInsightsAgentStore()
const fakeTimer = setInterval(() => {}, 10000)
store.setSessionCheckTimer(fakeTimer)
expect(store.sessionCheckTimer).toBe(fakeTimer)
clearInterval(fakeTimer)
})

it('replaces an existing timer', () => {
const store = useProductExpertInsightsAgentStore()
const timer1 = setInterval(() => {}, 10000)
const timer2 = setInterval(() => {}, 10000)
store.setSessionCheckTimer(timer1)
store.setSessionCheckTimer(timer2)
expect(store.sessionCheckTimer).toBe(timer2)
clearInterval(timer1)
clearInterval(timer2)
})
})

describe('reset', () => {
it('clears all state back to initial values', () => {
const store = useProductExpertInsightsAgentStore()
store.context = { instanceId: 'abc' }
store.sessionId = 'session-123'
store.messages = [{ role: 'user', content: 'hello' }]
store.abortController = new AbortController()
store.sessionStartTime = Date.now()
store.sessionWarningShown = true
store.sessionExpiredShown = true

store.reset()

expect(store.context).toBeNull()
expect(store.sessionId).toBeNull()
expect(store.messages).toEqual([])
expect(store.abortController).toBeNull()
expect(store.sessionStartTime).toBeNull()
expect(store.sessionWarningShown).toBe(false)
expect(store.sessionExpiredShown).toBe(false)
expect(store.sessionCheckTimer).toBeNull()
})

it('calls clearInterval when a timer is active', () => {
const store = useProductExpertInsightsAgentStore()
const clearIntervalSpy = vi.spyOn(globalThis, 'clearInterval')
const fakeTimer = setInterval(() => {}, 10000)
store.setSessionCheckTimer(fakeTimer)

store.reset()

expect(clearIntervalSpy).toHaveBeenCalledWith(fakeTimer)
clearIntervalSpy.mockRestore()
})

it('does not throw when no timer is set', () => {
const store = useProductExpertInsightsAgentStore()
expect(() => store.reset()).not.toThrow()
})

it('clears sessionCheckTimer after reset', () => {
const store = useProductExpertInsightsAgentStore()
const fakeTimer = setInterval(() => {}, 10000)
store.setSessionCheckTimer(fakeTimer)

store.reset()

expect(store.sessionCheckTimer).toBeNull()
})
})

describe('state mutations', () => {
it('allows direct state assignment for context', () => {
const store = useProductExpertInsightsAgentStore()
store.context = { instanceId: 'test-id' }
expect(store.context).toEqual({ instanceId: 'test-id' })
})

it('allows direct state assignment for sessionId', () => {
const store = useProductExpertInsightsAgentStore()
store.sessionId = 'abc-123'
expect(store.sessionId).toBe('abc-123')
})

it('allows pushing to messages', () => {
const store = useProductExpertInsightsAgentStore()
store.messages.push({ role: 'user', content: 'hello' })
store.messages.push({ role: 'assistant', content: 'hi' })
expect(store.messages).toHaveLength(2)
})

it('allows setting sessionWarningShown', () => {
const store = useProductExpertInsightsAgentStore()
store.sessionWarningShown = true
expect(store.sessionWarningShown).toBe(true)
})

it('allows setting sessionExpiredShown', () => {
const store = useProductExpertInsightsAgentStore()
store.sessionExpiredShown = true
expect(store.sessionExpiredShown).toBe(true)
})
})
})
2 changes: 1 addition & 1 deletion test/unit/frontend/stores/ux-navigation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'

import { useUxNavigationStore } from '@/stores/ux-navigation.js'

// Prevent _account-bridge from importing the real Vuex store
// Prevent _account_bridge from importing the real Vuex store
vi.mock('@/stores/_account_bridge.js', () => ({
useAccountBridge: vi.fn(() => ({
team: null,
Expand Down
Loading