@sentry/wizard
Version:
Sentry wizard helping you to configure your project
178 lines • 10.9 kB
JavaScript
;
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