preload-app.spec.ts 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. import { afterEach, expect, it, vi } from 'vitest'
  2. import { DESKTOP_IPC, type DshDesktopStartupApi } from '../src/ipc.ts'
  3. const electron = vi.hoisted(() => ({
  4. contextBridge: { exposeInMainWorld: vi.fn() },
  5. ipcRenderer: { invoke: vi.fn(), on: vi.fn(), off: vi.fn(), send: vi.fn() },
  6. }))
  7. vi.mock('electron', () => electron)
  8. vi.mock('../src/preload-platform.ts', () => ({ markDocumentPlatform: vi.fn() }))
  9. vi.mock('../src/preload-theme.ts', () => ({ syncNativeTheme: vi.fn() }))
  10. afterEach(() => { vi.unstubAllGlobals(); vi.clearAllMocks(); vi.resetModules() })
  11. it.each(['dsh-app://app/index.html', 'https://shell/startup.html'])('exposes only the carrier marker to %s', async (url) => {
  12. vi.stubGlobal('location', new URL(url))
  13. await import('../src/preload-app.ts')
  14. expect(electron.contextBridge.exposeInMainWorld).toHaveBeenCalledWith('dshDesktop', { protocolVersion: 1 })
  15. })
  16. it('provides startup controls and a removable state subscription to shell documents', async () => {
  17. vi.stubGlobal('location', new URL('dsh-app://shell/startup.html'))
  18. await import('../src/preload-app.ts')
  19. const api = electron.contextBridge.exposeInMainWorld.mock.calls[0]?.[1] as DshDesktopStartupApi
  20. await api.locale()
  21. await api.backend.status()
  22. await api.disablePlugins()
  23. await api.resetConfiguration()
  24. await api.restart()
  25. expect(electron.ipcRenderer.invoke.mock.calls).toEqual([
  26. [DESKTOP_IPC.localeGet], [DESKTOP_IPC.backendStatus],
  27. [DESKTOP_IPC.pluginsDisableAll], [DESKTOP_IPC.configurationReset], [DESKTOP_IPC.applicationRestart],
  28. ])
  29. const listener = vi.fn()
  30. const dispose = api.backend.subscribe(listener)
  31. const handler = electron.ipcRenderer.on.mock.calls[0]?.[1] as (event: unknown, state: unknown) => void
  32. handler({}, { phase: 'error', message: 'startup failed' })
  33. expect(listener).toHaveBeenCalledWith({ phase: 'error', message: 'startup failed' })
  34. dispose()
  35. expect(electron.ipcRenderer.off).toHaveBeenCalledWith(DESKTOP_IPC.backendState, handler)
  36. expect(api).not.toHaveProperty('plugins')
  37. })
  38. it('exposes asynchronous boot only to the local application document', async () => {
  39. vi.stubGlobal('location', new URL('dsh-app://app/'))
  40. await import('../src/preload-app.ts')
  41. const api = electron.contextBridge.exposeInMainWorld.mock.calls.find(([name]) => name === 'dshDesktopBoot')?.[1] as { ready(): Promise<unknown> }
  42. await api.ready()
  43. expect(electron.ipcRenderer.invoke).toHaveBeenCalledWith(DESKTOP_IPC.boot)
  44. vi.resetModules()
  45. electron.contextBridge.exposeInMainWorld.mockClear()
  46. vi.stubGlobal('location', new URL('https://other.example/'))
  47. await import('../src/preload-app.ts')
  48. expect(electron.contextBridge.exposeInMainWorld.mock.calls.some(([name]) => name === 'dshDesktopBoot')).toBe(false)
  49. })