From 6a5b5c968f43ee99897669fcfad9e0c53f130b43 Mon Sep 17 00:00:00 2001 From: Alexander Brown Date: Mon, 26 Jan 2026 14:08:54 -0800 Subject: [PATCH 1/2] refactor: replace vi.doMock with vi.mock + vi.hoisted (#8319) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace `vi.doMock` with `vi.mock` + `vi.hoisted` pattern for cleaner test mocking. - Add ESLint rule to prevent future `doMock` usage - Refactor `versionUtil.test.ts` and `firebaseAuthStore.test.ts` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8319-refactor-replace-vi-doMock-with-vi-mock-vi-hoisted-2f46d73d365081ae9fccf2fa7471a75b) by [Unito](https://www.unito.io) Co-authored-by: Amp --- eslint.config.ts | 14 +++ src/stores/firebaseAuthStore.test.ts | 36 +++++--- .../manager/utils/versionUtil.test.ts | 92 ++++++------------- 3 files changed, 62 insertions(+), 80 deletions(-) diff --git a/eslint.config.ts b/eslint.config.ts index aa59d118c..4974637fa 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -240,6 +240,20 @@ export default defineConfig([ ] } }, + { + files: ['**/*.test.ts'], + rules: { + 'no-restricted-properties': [ + 'error', + { + object: 'vi', + property: 'doMock', + message: + 'Use vi.mock() with vi.hoisted() instead of vi.doMock(). See docs/testing/vitest-patterns.md' + } + ] + } + }, { files: ['scripts/**/*.js'], languageOptions: { diff --git a/src/stores/firebaseAuthStore.test.ts b/src/stores/firebaseAuthStore.test.ts index bbad43264..54beac934 100644 --- a/src/stores/firebaseAuthStore.test.ts +++ b/src/stores/firebaseAuthStore.test.ts @@ -82,6 +82,21 @@ vi.mock('@/stores/toastStore', () => ({ // Mock useDialogService vi.mock('@/services/dialogService') +const mockDistributionTypes = vi.hoisted(() => ({ + isCloud: false, + isDesktop: false +})) + +vi.mock('@/platform/distribution/types', () => mockDistributionTypes) + +const mockApiKeyStore = vi.hoisted(() => ({ + getAuthHeader: vi.fn().mockReturnValue(null) +})) + +vi.mock('@/stores/apiKeyAuthStore', () => ({ + useApiKeyAuthStore: () => mockApiKeyStore +})) + describe('useFirebaseAuthStore', () => { let store: ReturnType let authStateCallback: (user: any) => void @@ -147,12 +162,9 @@ describe('useFirebaseAuthStore', () => { }) describe('token refresh events', () => { - beforeEach(async () => { - vi.resetModules() - vi.doMock('@/platform/distribution/types', () => ({ - isCloud: true, - isDesktop: true - })) + beforeEach(() => { + mockDistributionTypes.isCloud = true + mockDistributionTypes.isDesktop = true vi.mocked(firebaseAuth.onIdTokenChanged).mockImplementation( (_auth, callback) => { @@ -164,8 +176,7 @@ describe('useFirebaseAuthStore', () => { vi.mocked(vuefire.useFirebaseAuth).mockReturnValue(mockAuth as any) setActivePinia(createPinia()) - const storeModule = await import('@/stores/firebaseAuthStore') - store = storeModule.useFirebaseAuthStore() + store = useFirebaseAuthStore() }) it("should not increment tokenRefreshTrigger on the user's first ID token event", () => { @@ -442,13 +453,8 @@ describe('useFirebaseAuthStore', () => { // This test reproduces the issue where getAuthHeader fails due to network errors // when Firebase Auth tries to refresh tokens offline - // Mock useApiKeyAuthStore to return null (no API key fallback) - const mockApiKeyStore = { - getAuthHeader: vi.fn().mockReturnValue(null) - } - vi.doMock('@/stores/apiKeyAuthStore', () => ({ - useApiKeyAuthStore: () => mockApiKeyStore - })) + // Configure mockApiKeyStore to return null (no API key fallback) + mockApiKeyStore.getAuthHeader.mockReturnValue(null) // Setup user with network error on token refresh mockUser.getIdToken.mockReset() diff --git a/src/workbench/extensions/manager/utils/versionUtil.test.ts b/src/workbench/extensions/manager/utils/versionUtil.test.ts index d1127368a..a9eacfc9d 100644 --- a/src/workbench/extensions/manager/utils/versionUtil.test.ts +++ b/src/workbench/extensions/manager/utils/versionUtil.test.ts @@ -1,15 +1,16 @@ -import { describe, expect, it, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { checkVersionCompatibility, getFrontendVersion } from '@/workbench/extensions/manager/utils/versionUtil' -// Mock config module +const mockConfig = vi.hoisted((): { app_version: string | undefined } => ({ + app_version: '1.24.0-1' +})) + vi.mock('@/config', () => ({ - default: { - app_version: '1.24.0-1' - } + default: mockConfig })) describe('versionUtil', () => { @@ -266,79 +267,40 @@ describe('versionUtil', () => { }) describe('getFrontendVersion', () => { + let originalEnv: string | undefined + + beforeEach(() => { + originalEnv = import.meta.env.VITE_APP_VERSION + mockConfig.app_version = '1.24.0-1' + }) + + afterEach(() => { + if (originalEnv !== undefined) { + import.meta.env.VITE_APP_VERSION = originalEnv + } else { + delete import.meta.env.VITE_APP_VERSION + } + }) + it('should return app_version from config when available', () => { const version = getFrontendVersion() expect(version).toBe('1.24.0-1') }) - it('should fallback to VITE_APP_VERSION when app_version is not available', async () => { - // Save original environment - const originalEnv = import.meta.env.VITE_APP_VERSION - - // Mock config without app_version - vi.doMock('@/config', () => ({ - default: {} - })) - - // Set VITE_APP_VERSION + it('should fallback to VITE_APP_VERSION when app_version is not available', () => { + mockConfig.app_version = undefined import.meta.env.VITE_APP_VERSION = '2.0.0' - // Clear module cache to force re-import - vi.resetModules() - - // Import fresh module - const versionUtil = - await import('@/workbench/extensions/manager/utils/versionUtil') - - const version = versionUtil.getFrontendVersion() + const version = getFrontendVersion() expect(version).toBe('2.0.0') - - // Restore original env - import.meta.env.VITE_APP_VERSION = originalEnv - - // Reset mocks for next test - vi.resetModules() - vi.doMock('@/config', () => ({ - default: { - app_version: '1.24.0-1' - } - })) }) - it('should return undefined when no version is available', async () => { - // Save original environment - const originalEnv = import.meta.env.VITE_APP_VERSION - - // Mock config without app_version - vi.doMock('@/config', () => ({ - default: {} - })) - - // Clear VITE_APP_VERSION + it('should return undefined when no version is available', () => { + mockConfig.app_version = undefined delete import.meta.env.VITE_APP_VERSION - // Clear module cache to force re-import - vi.resetModules() - - // Import fresh module - const versionUtil = - await import('@/workbench/extensions/manager/utils/versionUtil') - - const version = versionUtil.getFrontendVersion() + const version = getFrontendVersion() expect(version).toBeUndefined() - - // Restore original env - if (originalEnv !== undefined) { - import.meta.env.VITE_APP_VERSION = originalEnv - } - - // Reset mocks for next test - vi.resetModules() - vi.doMock('@/config', () => ({ - default: { - app_version: '1.24.0-1' - } - })) }) }) }) From 5eda23b7aab636eb6ecd32804f10d96ac093dc7b Mon Sep 17 00:00:00 2001 From: Simula_r <18093452+simula-r@users.noreply.github.com> Date: Mon, 26 Jan 2026 16:33:00 -0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20workspace=20icon=20flash=20and=20cre?= =?UTF-8?q?dits=20showing=200=20while=20workspace=20is=20in=E2=80=A6=20(#8?= =?UTF-8?q?323)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Fix: flash of wrong workspace icon (replaced with loader) - Fix: personal workspace showing 0 credits ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8323-fix-workspace-icon-flash-and-credits-showing-0-while-workspace-is-in-2f46d73d36508159b52fec3fa0c17e35) by [Unito](https://www.unito.io) --- .../topbar/CurrentUserButton.test.ts | 19 ++++++++++- src/components/topbar/CurrentUserButton.vue | 34 ++++++++++++++----- .../topbar/CurrentUserPopoverWorkspace.vue | 16 ++++++--- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/components/topbar/CurrentUserButton.test.ts b/src/components/topbar/CurrentUserButton.test.ts index db5349b49..c8e568291 100644 --- a/src/components/topbar/CurrentUserButton.test.ts +++ b/src/components/topbar/CurrentUserButton.test.ts @@ -25,7 +25,24 @@ vi.mock('firebase/auth', () => ({ })) // Mock pinia -vi.mock('pinia') +vi.mock('pinia', () => ({ + storeToRefs: vi.fn((store) => store) +})) + +// Mock the useFeatureFlags composable +vi.mock('@/composables/useFeatureFlags', () => ({ + useFeatureFlags: vi.fn(() => ({ + flags: { teamWorkspacesEnabled: false } + })) +})) + +// Mock the useTeamWorkspaceStore +vi.mock('@/platform/workspace/stores/teamWorkspaceStore', () => ({ + useTeamWorkspaceStore: vi.fn(() => ({ + workspaceName: { value: '' }, + initState: { value: 'idle' } + })) +})) // Mock the useCurrentUser composable vi.mock('@/composables/auth/useCurrentUser', () => ({ diff --git a/src/components/topbar/CurrentUserButton.vue b/src/components/topbar/CurrentUserButton.vue index fde213498..85d587e91 100644 --- a/src/components/topbar/CurrentUserButton.vue +++ b/src/components/topbar/CurrentUserButton.vue @@ -12,12 +12,18 @@ :class=" cn( 'flex items-center gap-1 rounded-full hover:bg-interface-button-hover-surface justify-center', - compact && 'size-full aspect-square' + compact && 'size-full ' ) " > + @@ -40,13 +46,16 @@ } }" > - + - + @@ -54,6 +63,7 @@