UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

178 lines 10.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); const vitest_1 = require("vitest"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const root_1 = require("../../../src/react-router/codemods/root"); vitest_1.vi.mock('@clack/prompts', () => { const mock = { log: { warn: vitest_1.vi.fn(), info: vitest_1.vi.fn(), success: vitest_1.vi.fn(), }, }; return { default: mock, ...mock, }; }); vitest_1.vi.mock('../../../src/utils/debug', () => ({ debug: vitest_1.vi.fn(), })); (0, vitest_1.describe)('instrumentRoot', () => { const fixturesDir = path.join(__dirname, 'fixtures', 'root'); let tmpDir; let appDir; (0, vitest_1.beforeEach)(() => { vitest_1.vi.clearAllMocks(); // Create unique tmp directory for each test tmpDir = path.join(fixturesDir, 'tmp', `test-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`); appDir = path.join(tmpDir, 'app'); // Ensure tmp and app directories exist fs.mkdirSync(appDir, { recursive: true }); // Mock process.cwd() to return the tmp directory vitest_1.vi.spyOn(process, 'cwd').mockReturnValue(tmpDir); }); (0, vitest_1.afterEach)(() => { // Clean up tmp directory if (fs.existsSync(tmpDir)) { fs.rmSync(tmpDir, { recursive: true }); } vitest_1.vi.restoreAllMocks(); }); (0, vitest_1.it)('should add ErrorBoundary when no ErrorBoundary exists and no Sentry content', async () => { // Copy fixture to tmp directory for testing const srcFile = path.join(fixturesDir, 'no-error-boundary.tsx'); // Create app directory and copy file fs.copyFileSync(srcFile, path.join(appDir, 'root.tsx')); // Mock process.cwd() to return tmpDir await (0, root_1.instrumentRoot)('root.tsx'); // Check that the file was modified correctly const modifiedContent = fs.readFileSync(path.join(appDir, 'root.tsx'), 'utf8'); (0, vitest_1.expect)(modifiedContent).toContain('import * as Sentry from "@sentry/react-router";'); (0, vitest_1.expect)(modifiedContent).toContain("import { Outlet, isRouteErrorResponse } from 'react-router';"); (0, vitest_1.expect)(modifiedContent).toContain('export function ErrorBoundary({ error })'); (0, vitest_1.expect)(modifiedContent).toContain('Sentry.captureException(error);'); (0, vitest_1.expect)(modifiedContent).toContain('if (isRouteErrorResponse(error))'); }); (0, vitest_1.it)('should add Sentry.captureException to existing function declaration ErrorBoundary', async () => { const srcFile = path.join(fixturesDir, 'with-function-error-boundary.tsx'); fs.copyFileSync(srcFile, path.join(appDir, 'root.tsx')); await (0, root_1.instrumentRoot)('root.tsx'); const modifiedContent = fs.readFileSync(path.join(appDir, 'root.tsx'), 'utf8'); (0, vitest_1.expect)(modifiedContent).toContain('import * as Sentry from "@sentry/react-router";'); (0, vitest_1.expect)(modifiedContent).toContain('Sentry.captureException(error);'); }); (0, vitest_1.it)('should add Sentry.captureException to existing variable declaration ErrorBoundary', async () => { const srcFile = path.join(fixturesDir, 'with-variable-error-boundary.tsx'); fs.copyFileSync(srcFile, path.join(appDir, 'root.tsx')); await (0, root_1.instrumentRoot)('root.tsx'); const modifiedContent = fs.readFileSync(path.join(appDir, 'root.tsx'), 'utf8'); (0, vitest_1.expect)(modifiedContent).toContain('import * as Sentry from "@sentry/react-router";'); // Now properly handles variable declaration ErrorBoundary (0, vitest_1.expect)(modifiedContent).toContain('Sentry.captureException(error);'); }); (0, vitest_1.it)('should not modify file when ErrorBoundary already has Sentry.captureException', async () => { const srcFile = path.join(fixturesDir, 'with-sentry-error-boundary.tsx'); fs.copyFileSync(srcFile, path.join(appDir, 'root.tsx')); await (0, root_1.instrumentRoot)('root.tsx'); const modifiedContent = fs.readFileSync(path.join(appDir, 'root.tsx'), 'utf8'); // Should not add duplicate Sentry.captureException const captureExceptionOccurrences = (modifiedContent.match(/Sentry\.captureException/g) || []).length; (0, vitest_1.expect)(captureExceptionOccurrences).toBe(1); }); (0, vitest_1.it)('should not add Sentry import when Sentry content already exists', async () => { const srcFile = path.join(fixturesDir, 'with-existing-sentry.tsx'); fs.copyFileSync(srcFile, path.join(appDir, 'root.tsx')); await (0, root_1.instrumentRoot)('root.tsx'); const modifiedContent = fs.readFileSync(path.join(appDir, 'root.tsx'), 'utf8'); // Should not duplicate Sentry imports const sentryImportOccurrences = (modifiedContent.match(/import.*@sentry\/react-router/g) || []).length; (0, vitest_1.expect)(sentryImportOccurrences).toBe(1); }); (0, vitest_1.it)('should add isRouteErrorResponse import when not present and ErrorBoundary is added', async () => { const srcFile = path.join(fixturesDir, 'no-isrouteerrorresponse.tsx'); fs.copyFileSync(srcFile, path.join(appDir, 'root.tsx')); await (0, root_1.instrumentRoot)('root.tsx'); const modifiedContent = fs.readFileSync(path.join(appDir, 'root.tsx'), 'utf8'); (0, vitest_1.expect)(modifiedContent).toContain("import { Outlet, isRouteErrorResponse } from 'react-router';"); (0, vitest_1.expect)(modifiedContent).toContain('export function ErrorBoundary({ error })'); }); (0, vitest_1.it)('should not add duplicate isRouteErrorResponse import when already present', async () => { const srcFile = path.join(fixturesDir, 'with-isrouteerrorresponse.tsx'); fs.copyFileSync(srcFile, path.join(appDir, 'root.tsx')); await (0, root_1.instrumentRoot)('root.tsx'); const modifiedContent = fs.readFileSync(path.join(appDir, 'root.tsx'), 'utf8'); // Should not duplicate isRouteErrorResponse imports const isRouteErrorResponseOccurrences = (modifiedContent.match(/isRouteErrorResponse/g) || []).length; (0, vitest_1.expect)(isRouteErrorResponseOccurrences).toBe(2); // One import, one usage in template }); (0, vitest_1.it)('should handle ErrorBoundary with alternative function declaration syntax', async () => { const srcFile = path.join(fixturesDir, 'function-expression-error-boundary.tsx'); fs.copyFileSync(srcFile, path.join(appDir, 'root.tsx')); await (0, root_1.instrumentRoot)('root.tsx'); const modifiedContent = fs.readFileSync(path.join(appDir, 'root.tsx'), 'utf8'); (0, vitest_1.expect)(modifiedContent).toContain('import * as Sentry from "@sentry/react-router";'); (0, vitest_1.expect)(modifiedContent).toContain('Sentry.captureException(error);'); }); (0, vitest_1.it)('should handle function declaration with separate export', async () => { const srcFile = path.join(fixturesDir, 'function-declaration-separate-export.tsx'); fs.copyFileSync(srcFile, path.join(appDir, 'root.tsx')); await (0, root_1.instrumentRoot)('root.tsx'); const modifiedContent = fs.readFileSync(path.join(appDir, 'root.tsx'), 'utf8'); (0, vitest_1.expect)(modifiedContent).toContain('import * as Sentry from "@sentry/react-router";'); (0, vitest_1.expect)(modifiedContent).toContain('Sentry.captureException(error);'); // Should preserve function declaration syntax (0, vitest_1.expect)(modifiedContent).toMatch(/function ErrorBoundary\(/); (0, vitest_1.expect)(modifiedContent).toContain('export { ErrorBoundary }'); }); (0, vitest_1.it)('should handle ErrorBoundary with captureException imported directly', async () => { const srcFile = path.join(fixturesDir, 'with-direct-capture-exception.tsx'); fs.copyFileSync(srcFile, path.join(appDir, 'root.tsx')); await (0, root_1.instrumentRoot)('root.tsx'); const modifiedContent = fs.readFileSync(path.join(appDir, 'root.tsx'), 'utf8'); // Should not add duplicate captureException calls const captureExceptionOccurrences = (modifiedContent.match(/captureException/g) || []).length; (0, vitest_1.expect)(captureExceptionOccurrences).toBe(2); // One import, one usage }); (0, vitest_1.it)('should not modify an already properly configured file', async () => { const srcFile = path.join(fixturesDir, 'fully-configured.tsx'); fs.copyFileSync(srcFile, path.join(appDir, 'root.tsx')); await (0, root_1.instrumentRoot)('root.tsx'); const modifiedContent = fs.readFileSync(path.join(appDir, 'root.tsx'), 'utf8'); // Should not add duplicate imports or modify existing Sentry configuration const sentryImportOccurrences = (modifiedContent.match(/import.*@sentry\/react-router/g) || []).length; (0, vitest_1.expect)(sentryImportOccurrences).toBe(1); const captureExceptionOccurrences = (modifiedContent.match(/Sentry\.captureException/g) || []).length; (0, vitest_1.expect)(captureExceptionOccurrences).toBe(1); const errorBoundaryOccurrences = (modifiedContent.match(/export function ErrorBoundary/g) || []).length; (0, vitest_1.expect)(errorBoundaryOccurrences).toBe(1); (0, vitest_1.expect)(modifiedContent).toContain("import * as Sentry from '@sentry/react-router';"); (0, vitest_1.expect)(modifiedContent).toContain("import { Outlet, isRouteErrorResponse } from 'react-router';"); }); }); //# sourceMappingURL=root.test.js.map