composeDesktopConnection.test.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import {afterEach,beforeEach,expect,it,vi} from 'vitest'
  2. import {flushPromises,mount} from '@vue/test-utils'
  3. import Panel from '../../src/client/components/workspace/LocalVmChatPanel.vue'
  4. import {apiRequest} from '../../src/client/api/client'
  5. vi.mock('../../src/client/api/client',()=>({apiRequest:vi.fn()}))
  6. const agent={id:'bot',name:'小民',nodeId:'local',profile:'default',execution:'profile',computer:'auto'} as any
  7. const desktop={id:'cursor',name:'Cursor 开发桌面',online:true,ready:true,available:true}
  8. let state:any,problem:string
  9. const mountPanel=()=>mount(Panel,{props:{agents:[agent],isAdmin:true,active:true},global:{stubs:{AppIcon:true,GrokAuthPanel:true}}})
  10. beforeEach(()=>{
  11. vi.spyOn(document,'hidden','get').mockReturnValue(false)
  12. problem=''
  13. state={fixedCapacity:true,enabled:false,desktops:[{...desktop}]}
  14. vi.mocked(apiRequest).mockImplementation(async(path,options:any)=>{
  15. if(path.endsWith('/host-tools'))return {vm:true,cloud:false,serverComputer:false,scriptMachine:false} as any
  16. if(path.endsWith('/desktop-environment'))return {hosts:[]} as any
  17. if(path.endsWith('/cloud-computer'))return {configured:false} as any
  18. if(path.includes('/computer/frame?backend=vm'))return {data:'fixture',width:100,height:80} as any
  19. if(options?.method==='PUT'){
  20. if(problem)throw new Error(problem)
  21. state={...state,desktopId:options.body.desktopId,enabled:true,container:'running',ready:true,image:true,mode:'shared'}
  22. return {agent:{...agent,computerEnvironmentId:state.desktopId}} as any
  23. }
  24. return state
  25. })
  26. })
  27. afterEach(()=>{vi.restoreAllMocks();vi.clearAllMocks()})
  28. it('connects an unassigned bot to an existing Compose desktop and opens the VM viewer',async()=>{
  29. const wrapper=mountPanel()
  30. try{
  31. await flushPromises()
  32. expect(wrapper.get('[aria-label="共享桌面"]').element).toHaveProperty('value','cursor')
  33. expect(vi.mocked(apiRequest).mock.calls.some(([,o])=>o?.method==='PUT')).toBe(false)
  34. await wrapper.get('[data-testid="connect-compose-desktop"]').trigger('click');await flushPromises()
  35. expect(apiRequest).toHaveBeenCalledWith('/api/app/agents/bot/local-vm',{method:'PUT',body:{enabled:true,desktopId:'cursor'}})
  36. expect(wrapper.emitted('changed')).toHaveLength(1)
  37. expect(wrapper.get('.preview img').attributes('src')).toContain('base64,fixture')
  38. expect(wrapper.findAll('button').some(b=>b.text()==='设置本地虚拟机')).toBe(false)
  39. await wrapper.findAll('button').find(b=>b.text()==='打开桌面')!.trigger('click')
  40. expect(wrapper.emitted('desktop')?.at(-1)).toEqual([agent,'vm',undefined])
  41. }finally{wrapper.unmount()}
  42. })
  43. it.each([
  44. {online:false}, {ready:false}, {available:false},
  45. ])('does not connect an unavailable desktop: %j',async unavailable=>{
  46. state.desktops=[{...desktop,...unavailable}]
  47. const wrapper=mountPanel()
  48. try{
  49. await flushPromises()
  50. expect(wrapper.get('[aria-label="共享桌面"] option[value="cursor"]').attributes('disabled')).toBeDefined()
  51. expect(wrapper.get('[data-testid="connect-compose-desktop"]').attributes('disabled')).toBeDefined()
  52. }finally{wrapper.unmount()}
  53. })
  54. it('requires an explicit choice with several desktops and preserves it across refreshes',async()=>{
  55. state.desktops.push({...desktop,id:'standard',name:'标准桌面'})
  56. const wrapper=mountPanel()
  57. try{
  58. await flushPromises()
  59. expect(wrapper.get('[data-testid="connect-compose-desktop"]').attributes('disabled')).toBeDefined()
  60. await wrapper.get('[aria-label="共享桌面"]').setValue('standard')
  61. await wrapper.setProps({active:false});await wrapper.setProps({active:true});await flushPromises()
  62. expect(wrapper.get('[aria-label="共享桌面"]').element).toHaveProperty('value','standard')
  63. await wrapper.get('[data-testid="connect-compose-desktop"]').trigger('click');await flushPromises()
  64. expect(apiRequest).toHaveBeenCalledWith('/api/app/agents/bot/local-vm',{method:'PUT',body:{enabled:true,desktopId:'standard'}})
  65. }finally{wrapper.unmount()}
  66. })
  67. it('keeps a rejected binding visible and does not claim the desktop is connected',async()=>{
  68. problem='请连接启用 Compose 桌面模式的执行节点'
  69. const wrapper=mountPanel()
  70. try{
  71. await flushPromises();await wrapper.get('[data-testid="connect-compose-desktop"]').trigger('click');await flushPromises()
  72. expect(wrapper.get('[role="alert"]').text()).toBe(problem)
  73. expect(wrapper.emitted('changed')).toBeUndefined()
  74. expect(wrapper.findAll('button').some(b=>b.text()==='打开桌面')).toBe(false)
  75. }finally{wrapper.unmount()}
  76. })
  77. it('keeps the existing assignment and blocks changes while the desktop is in use',async()=>{
  78. state={...state,enabled:true,desktopId:'cursor',container:'running',ready:true,image:true,inUse:true}
  79. const wrapper=mountPanel()
  80. try{
  81. await flushPromises()
  82. expect(wrapper.get('[aria-label="共享桌面"]').element).toHaveProperty('value','cursor')
  83. expect(wrapper.get('[aria-label="共享桌面"]').attributes('disabled')).toBeDefined()
  84. expect(wrapper.get('[data-testid="connect-compose-desktop"]').text()).toBe('已连接')
  85. expect(wrapper.get('[data-testid="connect-compose-desktop"]').attributes('disabled')).toBeDefined()
  86. }finally{wrapper.unmount()}
  87. })
  88. it('does not reuse another bot selection or bind a desktop that became unavailable',async()=>{
  89. state.desktops.push({...desktop,id:'standard',name:'标准桌面'})
  90. const wrapper=mountPanel()
  91. try{
  92. await flushPromises();await wrapper.get('[aria-label="共享桌面"]').setValue('standard')
  93. await wrapper.setProps({agents:[{...agent,id:'other'}]});await flushPromises()
  94. expect(wrapper.get('[aria-label="共享桌面"]').element).toHaveProperty('value','')
  95. await wrapper.get('[aria-label="共享桌面"]').setValue('standard')
  96. state={...state,desktops:state.desktops.map((d:any)=>({...d,online:false}))}
  97. await wrapper.setProps({active:false});await wrapper.setProps({active:true});await flushPromises()
  98. expect(wrapper.get('[data-testid="connect-compose-desktop"]').attributes('disabled')).toBeDefined()
  99. expect(vi.mocked(apiRequest).mock.calls.some(([,o])=>o?.method==='PUT')).toBe(false)
  100. }finally{wrapper.unmount()}
  101. })