From a1d2fbd96bb6713c3172198d71e94274af409aee Mon Sep 17 00:00:00 2001 From: naoNao89 <90588855+naoNao89@users.noreply.github.com> Date: Thu, 31 Jul 2025 05:29:42 +0700 Subject: [Editor] Prevent excessive project creation from invalid routes Race conditions in useEffect hook caused navigation to invalid project IDs to create multiple 'Untitled Project' entries. Users reported accumulating 885+ projects due to this bug. This change adds global invalid project ID tracking across component remounts and prevents race conditions through: - Global invalidProjectIds Set in project store with tracking functions - isInitializingRef to prevent duplicate initialization attempts - Proper useEffect cleanup with cancellation tokens - Specific error handling for 'not found' vs recoverable errors - Removed problematic activeProject?.id dependency Testing: - Add comprehensive test suite (15 tests) with Vitest + React Testing Library - Include integration tests demonstrating bug and validating fix - Update CI pipeline to run automated tests Fixes #182 --- apps/web/src/__tests__/setup.ts | 65 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 apps/web/src/__tests__/setup.ts (limited to 'apps/web/src/__tests__/setup.ts') diff --git a/apps/web/src/__tests__/setup.ts b/apps/web/src/__tests__/setup.ts new file mode 100644 index 00000000..82176a41 --- /dev/null +++ b/apps/web/src/__tests__/setup.ts @@ -0,0 +1,65 @@ +import '@testing-library/jest-dom' + +// Mock Next.js router +const mockRouter = { + push: vi.fn(), + replace: vi.fn(), + back: vi.fn(), + forward: vi.fn(), + refresh: vi.fn(), + prefetch: vi.fn(), +} + +vi.mock('next/navigation', () => ({ + useRouter: () => mockRouter, + usePathname: () => '/test', + useSearchParams: () => new URLSearchParams(), +})) + +// Mock IndexedDB for storage tests +const mockIndexedDB = { + open: vi.fn(), + deleteDatabase: vi.fn(), +} + +Object.defineProperty(window, 'indexedDB', { + value: mockIndexedDB, + writable: true, +}) + +// Mock OPFS (Origin Private File System) +Object.defineProperty(navigator, 'storage', { + value: { + getDirectory: vi.fn(), + }, + writable: true, +}) + +// Global test utilities +global.mockRouter = mockRouter + +// Suppress console logs during tests unless explicitly needed +const originalConsoleLog = console.log +const originalConsoleError = console.error +const originalConsoleWarn = console.warn + +beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks() + + // Suppress console output unless VITEST_VERBOSE is set + if (!process.env.VITEST_VERBOSE) { + console.log = vi.fn() + console.error = vi.fn() + console.warn = vi.fn() + } +}) + +afterEach(() => { + // Restore console if it was mocked + if (!process.env.VITEST_VERBOSE) { + console.log = originalConsoleLog + console.error = originalConsoleError + console.warn = originalConsoleWarn + } +}) -- cgit v1.3.1