diff --git a/src/renderer/__helpers__/vitest.dom.setup.ts b/src/renderer/__helpers__/vitest.dom.setup.ts new file mode 100644 index 000000000..32377700f --- /dev/null +++ b/src/renderer/__helpers__/vitest.dom.setup.ts @@ -0,0 +1,55 @@ +/** + * Minimal DOM-globals setup for renderer .ts test files that run in the node project + * but opt into a DOM environment via `// @vitest-environment happy-dom`. + * + * This file is added to the node project's `setupFiles` so that it runs for + * every test in that project. The `typeof window !== 'undefined'` guard makes + * it a no-op for tests that stay in the real node environment, while tests + * that declare `// @vitest-environment happy-dom` at the top of their file + * get the full `window.gitify` context bridge mock they need. + */ + +if (typeof window !== 'undefined') { + window.gitify = { + app: { + version: vi.fn().mockResolvedValue('v0.0.1'), + hide: vi.fn(), + quit: vi.fn(), + show: vi.fn(), + }, + twemojiDirectory: vi.fn().mockResolvedValue('/mock/images/assets'), + openExternalLink: vi.fn(), + decryptValue: vi.fn().mockResolvedValue('decrypted'), + encryptValue: vi.fn().mockResolvedValue('encrypted'), + platform: { + isLinux: vi.fn().mockReturnValue(false), + isMacOS: vi.fn().mockReturnValue(true), + isWindows: vi.fn().mockReturnValue(false), + }, + zoom: { + getLevel: vi.fn(), + setLevel: vi.fn(), + }, + tray: { + updateColor: vi.fn(), + updateTitle: vi.fn(), + useAlternateIdleIcon: vi.fn(), + useUnreadActiveIcon: vi.fn(), + }, + notificationSoundPath: vi.fn(), + onAuthCallback: vi.fn(), + onResetApp: vi.fn(), + onSystemThemeUpdate: vi.fn(), + setAutoLaunch: vi.fn(), + applyKeyboardShortcut: vi.fn().mockResolvedValue({ success: true }), + raiseNativeNotification: vi.fn(), + }; + + Object.defineProperty(navigator, 'clipboard', { + value: { + writeText: vi.fn().mockResolvedValue(undefined), + readText: vi.fn().mockResolvedValue(''), + }, + configurable: true, + }); +} diff --git a/src/renderer/hooks/useNotifications.test.ts b/src/renderer/hooks/useNotifications.test.ts index d71df2328..15542d351 100644 --- a/src/renderer/hooks/useNotifications.test.ts +++ b/src/renderer/hooks/useNotifications.test.ts @@ -1,3 +1,4 @@ +// @vitest-environment happy-dom import { act, renderHook, waitFor } from '@testing-library/react'; import { diff --git a/src/renderer/stores/useFiltersStore.test.ts b/src/renderer/stores/useFiltersStore.test.ts index 3d76f680c..ad7d3b36c 100644 --- a/src/renderer/stores/useFiltersStore.test.ts +++ b/src/renderer/stores/useFiltersStore.test.ts @@ -1,3 +1,4 @@ +// @vitest-environment happy-dom import { act, renderHook } from '@testing-library/react'; import type { SearchToken } from '../types'; diff --git a/src/renderer/utils/api/octokit.test.ts b/src/renderer/utils/api/octokit.test.ts index 2a394abb8..d375512c4 100644 --- a/src/renderer/utils/api/octokit.test.ts +++ b/src/renderer/utils/api/octokit.test.ts @@ -1,3 +1,4 @@ +// @vitest-environment happy-dom import { mockGitHubAppAccount, mockGitHubCloudAccount, diff --git a/src/renderer/utils/auth/flows.test.ts b/src/renderer/utils/auth/flows.test.ts index 73dacce24..ed9284544 100644 --- a/src/renderer/utils/auth/flows.test.ts +++ b/src/renderer/utils/auth/flows.test.ts @@ -1,3 +1,4 @@ +// @vitest-environment happy-dom // Use a hoist-safe mock factory for '@octokit/oauth-methods' vi.mock('@octokit/oauth-methods', async () => { const actual = await vi.importActual( diff --git a/src/renderer/utils/auth/utils.test.ts b/src/renderer/utils/auth/utils.test.ts index 2cd041170..87d68ba76 100644 --- a/src/renderer/utils/auth/utils.test.ts +++ b/src/renderer/utils/auth/utils.test.ts @@ -1,3 +1,4 @@ +// @vitest-environment happy-dom import { mockGitHubCloudAccount } from '../../__mocks__/account-mocks'; import { mockAuth } from '../../__mocks__/state-mocks'; import { mockRawUser } from '../api/__mocks__/response-mocks'; diff --git a/src/renderer/utils/notifications/filters/filter.test.ts b/src/renderer/utils/notifications/filters/filter.test.ts index 5b0b2c48f..dbdb23eca 100644 --- a/src/renderer/utils/notifications/filters/filter.test.ts +++ b/src/renderer/utils/notifications/filters/filter.test.ts @@ -1,3 +1,4 @@ +// @vitest-environment happy-dom import { mockPartialGitifyNotification } from '../../../__mocks__/notifications-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; @@ -8,6 +9,10 @@ import type { GitifyOwner, Link, SearchToken } from '../../../types'; import { filterBaseNotifications, filterDetailedNotifications } from './filter'; describe('renderer/utils/notifications/filters/filter.ts', () => { + beforeEach(() => { + useFiltersStore.getState().reset(); + }); + describe('filterNotifications', () => { const mockNotifications = [ mockPartialGitifyNotification( diff --git a/src/renderer/utils/system/audio.test.ts b/src/renderer/utils/system/audio.test.ts index d903105a8..359fa1b01 100644 --- a/src/renderer/utils/system/audio.test.ts +++ b/src/renderer/utils/system/audio.test.ts @@ -1,3 +1,4 @@ +// @vitest-environment happy-dom import type { Percentage } from '../../types'; import { raiseSoundNotification } from './audio'; diff --git a/src/renderer/utils/system/comms.test.ts b/src/renderer/utils/system/comms.test.ts index f3869d4e8..513f3737e 100644 --- a/src/renderer/utils/system/comms.test.ts +++ b/src/renderer/utils/system/comms.test.ts @@ -1,3 +1,4 @@ +// @vitest-environment happy-dom import { mockSettings } from '../../__mocks__/state-mocks'; import { type Link, OpenPreference } from '../../types'; diff --git a/src/renderer/utils/system/keyboardShortcut.test.ts b/src/renderer/utils/system/keyboardShortcut.test.ts index 2ea357e4e..7e8943e77 100644 --- a/src/renderer/utils/system/keyboardShortcut.test.ts +++ b/src/renderer/utils/system/keyboardShortcut.test.ts @@ -1,3 +1,4 @@ +// @vitest-environment happy-dom import { formatAcceleratorForDisplay, keyboardEventToAccelerator, diff --git a/src/renderer/utils/system/native.test.ts b/src/renderer/utils/system/native.test.ts index 5d5158af7..457675dad 100644 --- a/src/renderer/utils/system/native.test.ts +++ b/src/renderer/utils/system/native.test.ts @@ -1,3 +1,4 @@ +// @vitest-environment happy-dom import { waitFor } from '@testing-library/react'; import { diff --git a/src/renderer/utils/system/tray.test.ts b/src/renderer/utils/system/tray.test.ts index 4d76bba35..886266e21 100644 --- a/src/renderer/utils/system/tray.test.ts +++ b/src/renderer/utils/system/tray.test.ts @@ -1,3 +1,4 @@ +// @vitest-environment happy-dom import { mockSettings } from '../../__mocks__/state-mocks'; import type { SettingsState } from '../../types'; diff --git a/src/renderer/utils/ui/zoom.test.ts b/src/renderer/utils/ui/zoom.test.ts index e602dc6c5..1749f8c05 100644 --- a/src/renderer/utils/ui/zoom.test.ts +++ b/src/renderer/utils/ui/zoom.test.ts @@ -1,3 +1,4 @@ +// @vitest-environment happy-dom import type { Percentage } from '../../types'; import { diff --git a/vitest.config.ts b/vitest.config.ts index d099e8482..cebf7808d 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -37,21 +37,25 @@ export default defineConfig({ css: true, include: [ 'src/preload/**/*.test.{ts,tsx}', - 'src/renderer/**/*.test.{ts,tsx}', + 'src/renderer/**/*.test.tsx', ], setupFiles: ['./src/renderer/__helpers__/vitest.setup.ts'], }, }, { - // TODO - Opportunity in future to move some of the renderer util tests to node environment extends: true, test: { - name: 'node [main, shared]', + name: 'node [main, shared, renderer utils]', environment: 'node', include: [ 'src/shared/**/*.test.{ts,tsx}', 'src/main/**/*.test.{ts,tsx}', + 'src/renderer/**/*.test.ts', ], + // Conditionally sets up window.gitify for .ts files that opt into + // happy-dom via the `// @vitest-environment happy-dom` file docblock. + // The guard in the setup file makes it a no-op for pure node tests. + setupFiles: ['./src/renderer/__helpers__/vitest.dom.setup.ts'], }, }, ],