workspaceInspector.test.ts 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
  1. import {afterEach,expect,it,vi} from 'vitest'
  2. import {flushPromises,mount} from '@vue/test-utils'
  3. import Panel from '../../src/client/components/workspace/WorkspaceInspectorPanel.vue'
  4. import {apiRequest} from '../../src/client/api/client'
  5. import type {WorkspaceInspectorEntry} from '../../src/shared/workspacePanels'
  6. vi.mock('../../src/client/api/client',()=>({apiRequest:vi.fn()}))
  7. const entry=(id:string,at:number,method='message.delta',direction:WorkspaceInspectorEntry['direction']='event',agentId='agent'):WorkspaceInspectorEntry=>({id,at,method,direction,agentId,runId:'run',data:{text:id}})
  8. afterEach(()=>{vi.restoreAllMocks();vi.clearAllMocks()})
  9. function setup(entries:WorkspaceInspectorEntry[]){
  10. vi.spyOn(document,'hidden','get').mockReturnValue(false)
  11. vi.mocked(apiRequest).mockResolvedValue({entries})
  12. return mount(Panel,{props:{conversationId:'conversation'},global:{stubs:{AppIcon:true}}})
  13. }
  14. it('sorts by timestamp before grouping, shows newest groups first and keeps delta text in order',async()=>{
  15. const wrapper=setup([entry('done',400,'message.complete'),entry('B',300),entry('A',200),entry('other',100,'message.delta','event','other')])
  16. try{
  17. await flushPromises()
  18. const rows=wrapper.findAll('details')
  19. expect(rows.map(row=>row.find('.summary').text())).toEqual(['done','AB','other'])
  20. expect(rows[1]!.find('time').text()).toMatch(/\.300$/)
  21. expect(rows[1]!.find('.tag').text()).toContain('×2')
  22. expect(JSON.parse(rows[1]!.find('pre').text()).map((e:WorkspaceInspectorEntry)=>e.id)).toEqual(['A','B'])
  23. }finally{wrapper.unmount()}
  24. })
  25. it('sorts Raw responses and requests newest first, including equal-time arrival order',async()=>{
  26. const wrapper=setup([entry('response',300,'session.usage','response'),entry('request',100,'prompt.submit','request'),entry('same-1',200,'first','request'),entry('same-2',200,'second','response')])
  27. try{
  28. await flushPromises();await wrapper.get('.lenses button:last-child').trigger('click');await flushPromises()
  29. expect(wrapper.findAll('pre').map(row=>JSON.parse(row.text()).id)).toEqual(['response','same-2','same-1','request'])
  30. }finally{wrapper.unmount()}
  31. })
  32. it('opens at the top, follows new records at capacity, and resets a changed lens after reading older records',async()=>{
  33. const wrapper=setup([entry('old',100,'message.complete'),entry('new',200,'message.complete')])
  34. try{
  35. await flushPromises()
  36. const list=wrapper.get('.inspector-list').element as HTMLElement
  37. expect(list.scrollTop).toBe(0)
  38. list.scrollTop=150;await wrapper.get('.inspector-list').trigger('scroll')
  39. vi.mocked(apiRequest).mockResolvedValue({entries:[entry('new',200,'message.complete'),entry('latest',300,'message.complete')]})
  40. await wrapper.get('.reload').trigger('click');await flushPromises()
  41. expect(list.scrollTop).toBe(150)
  42. await wrapper.get('.lenses button:last-child').trigger('click');await flushPromises();expect(list.scrollTop).toBe(0)
  43. await wrapper.get('.lenses button:first-child').trigger('click');await flushPromises()
  44. list.scrollTop=20;await wrapper.get('.inspector-list').trigger('scroll')
  45. vi.mocked(apiRequest).mockResolvedValue({entries:[entry('latest',300,'message.complete'),entry('live',400,'message.complete')]})
  46. await wrapper.get('.reload').trigger('click');await flushPromises();expect(list.scrollTop).toBe(0)
  47. expect(wrapper.find('.summary').text()).toBe('live')
  48. }finally{wrapper.unmount()}
  49. })