desktop-hosts.spec.ts 5.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import { test, expect } from '@playwright/test'
  2. test('registers a desktop host, downloads its private config and disables it', async ({ page }, testInfo) => {
  3. await page.goto('/conversations')
  4. await page.getByRole('textbox', { name: '账号', exact: true }).fill('fixture')
  5. await page.getByRole('textbox', { name: '密码', exact: true }).fill('fixture-pass')
  6. await page.getByRole('button', { name: '登录', exact: true }).click()
  7. await page.locator('.desktop-sidebar .sidebar-account-switcher__main').click()
  8. await page.getByRole('menuitem', { name: '我的设置', exact: true }).click()
  9. const dialog = page.getByRole('dialog', { name: '我的设置' })
  10. await dialog.getByRole('button', { name: 'Hermes 连接', exact: true }).click()
  11. await dialog.locator('details.desktop-host-settings summary').click()
  12. const panel = dialog.getByRole('region', { name: '桌面主机设置' })
  13. await expect(panel).toContainText('尚未注册桌面主机')
  14. await panel.getByRole('textbox', { name: '主机名称', exact: true }).fill('书房 iMac')
  15. await page.screenshot({ path: testInfo.outputPath('desktop-host-registration.png') })
  16. await panel.getByRole('button', { name: '注册主机', exact: true }).click()
  17. await expect(panel).toContainText('待连接')
  18. const downloaded = page.waitForEvent('download')
  19. await panel.getByRole('button', { name: '下载主机配置', exact: true }).click()
  20. const download = await downloaded
  21. expect(download.suggestedFilename()).toBe('desktop-host.json')
  22. const stream = await download.createReadStream(); const chunks: Buffer[] = []
  23. for await (const chunk of stream!) chunks.push(Buffer.from(chunk))
  24. const configuration = JSON.parse(Buffer.concat(chunks).toString())
  25. expect(configuration).toMatchObject({ protocol: 1, serverURL: expect.stringMatching(/^https?:\/\//) })
  26. expect(configuration.hostId).toMatch(/^[0-9a-f-]{36}$/)
  27. expect(configuration.token.length).toBeGreaterThan(32)
  28. const result = await (await page.request.get('/api/app/admin/desktop-hosts')).json()
  29. expect(JSON.stringify(result)).not.toContain(configuration.token)
  30. expect(JSON.stringify(result)).not.toContain('tokenHash')
  31. await page.setViewportSize({ width: 390, height: 844 })
  32. await panel.getByRole('button', { name: '停用 书房 iMac' }).scrollIntoViewIfNeeded()
  33. await page.screenshot({ path: testInfo.outputPath('desktop-host-registered-mobile.png') })
  34. await panel.getByRole('button', { name: '停用 书房 iMac' }).click()
  35. await expect(panel).toContainText('已停用')
  36. expect((await (await page.request.get('/api/app/admin/desktop-hosts')).json()).hosts.find((h: any) => h.id === configuration.hostId).enabled).toBe(false)
  37. })
  38. test('binds an agent to a connected desktop host from its chat panel', async ({ page }, testInfo) => {
  39. await page.goto('/conversations')
  40. await page.getByRole('textbox', { name: '账号', exact: true }).fill('fixture')
  41. await page.getByRole('textbox', { name: '密码', exact: true }).fill('fixture-pass')
  42. await page.getByRole('button', { name: '登录', exact: true }).click()
  43. await page.locator('.desktop-sidebar .sidebar-account-switcher__main').click()
  44. await page.getByRole('menuitem', { name: '我的设置', exact: true }).click()
  45. const dialog = page.getByRole('dialog', { name: '我的设置' })
  46. await dialog.getByRole('button', { name: 'Hermes 连接', exact: true }).click()
  47. await dialog.locator('details.desktop-host-settings summary').click()
  48. const panel = dialog.getByRole('region', { name: '桌面主机设置' })
  49. await panel.getByRole('textbox', { name: '主机名称', exact: true }).fill('客厅 Mac mini')
  50. await panel.getByRole('button', { name: '注册主机', exact: true }).click()
  51. await expect(panel).toContainText('待连接')
  52. const downloaded = page.waitForEvent('download')
  53. await panel.getByRole('button', { name: '下载主机配置', exact: true }).click()
  54. const stream = await (await downloaded).createReadStream(); const chunks: Buffer[] = []
  55. for await (const chunk of stream!) chunks.push(Buffer.from(chunk))
  56. const configuration = JSON.parse(Buffer.concat(chunks).toString())
  57. const hostInfo = { id: crypto.randomUUID(), name: '客厅 Mac mini', platform: 'darwin', screen: true, accessibility: true, approved: [] }
  58. const exchange = () => page.request.post(`/api/desktop-host/v1/${configuration.hostId}/exchange`, {
  59. headers: { Authorization: `Bearer ${configuration.token}`, 'x-desktop-host-protocol': '1' },
  60. data: { host: hostInfo, results: [] },
  61. })
  62. expect((await (await exchange()).json()).commands).toEqual([])
  63. const seed = await (await page.request.post('/__test/computer?off=1', { data: {} })).json()
  64. await page.goto(`/conversations/${seed.conversationId}`)
  65. await page.getByRole('button', { name: '电脑与定时任务', exact: true }).click()
  66. const dock = page.getByRole('complementary', { name: '机器人电脑面板' })
  67. const desktopBox = dock.locator('.env-check', { hasText: '本机控制' }).getByRole('checkbox')
  68. await desktopBox.check()
  69. await expect(desktopBox).toBeChecked()
  70. await expect(dock.getByRole('combobox', { name: '操作的电脑', exact: true })).toHaveCount(0)
  71. const hostCard = dock.locator('.host-permission', { hasText: '客厅 Mac mini' })
  72. await expect(hostCard).toBeVisible()
  73. await expect(hostCard).toContainText('在线')
  74. await hostCard.scrollIntoViewIfNeeded()
  75. await page.screenshot({ path: testInfo.outputPath('desktop-host-binding.png') })
  76. const agents = (await (await page.request.get('/api/app/agents')).json()).agents
  77. const updated = agents.find((a: any) => a.id === seed.agentId)
  78. expect(updated).toMatchObject({ envs: { vm: true, desktop: true } })
  79. expect(updated.desktopHost).toBeUndefined()
  80. })