@sentry/wizard
Version:
Sentry wizard helping you to configure your project
913 lines (752 loc) • 26.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const javascript_1 = require("../../src/react-native/javascript");
// @ts-expect-error - magicast is ESM and TS complains about that. It works though
const magicast_1 = require("magicast");
const vitest_1 = require("vitest");
(0, vitest_1.describe)('react-native javascript', () => {
(0, vitest_1.describe)('addSentryInitWithSdkImport', () => {
(0, vitest_1.it)('adds sdk import and sentry init under last import in the file', () => {
const input = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
const expectedOutput = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'dsn',
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Enable Logs
enableLogs: false,
// uncomment the line below to enable Spotlight (https://spotlightjs.com)
// spotlight: __DEV__,
});
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
(0, vitest_1.expect)((0, javascript_1.addSentryInitWithSdkImport)(input, { dsn: 'dsn' })).toBe(expectedOutput);
});
(0, vitest_1.it)('adds sdk import and sentry init under last import in the file and enables session replay', () => {
const input = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
const expectedOutput = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'dsn',
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Enable Logs
enableLogs: false,
// Configure Session Replay
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1,
integrations: [Sentry.mobileReplayIntegration()],
// uncomment the line below to enable Spotlight (https://spotlightjs.com)
// spotlight: __DEV__,
});
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
(0, vitest_1.expect)((0, javascript_1.addSentryInitWithSdkImport)(input, {
dsn: 'dsn',
enableSessionReplay: true,
})).toBe(expectedOutput);
});
(0, vitest_1.it)('adds sdk import and sentry init under last import in the file and enables feedback widget', () => {
const input = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
const expectedOutput = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'dsn',
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Enable Logs
enableLogs: false,
integrations: [Sentry.feedbackIntegration()],
// uncomment the line below to enable Spotlight (https://spotlightjs.com)
// spotlight: __DEV__,
});
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
(0, vitest_1.expect)((0, javascript_1.addSentryInitWithSdkImport)(input, {
dsn: 'dsn',
enableFeedbackWidget: true,
})).toBe(expectedOutput);
});
(0, vitest_1.it)('adds sdk import and sentry init under last import in the file and enables logs', () => {
const input = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
const expectedOutput = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'dsn',
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Enable Logs
enableLogs: true,
// uncomment the line below to enable Spotlight (https://spotlightjs.com)
// spotlight: __DEV__,
});
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
(0, vitest_1.expect)((0, javascript_1.addSentryInitWithSdkImport)(input, {
dsn: 'dsn',
enableLogs: true,
})).toBe(expectedOutput);
});
(0, vitest_1.it)('adds sdk import and sentry init with logs disabled', () => {
const input = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
const expectedOutput = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'dsn',
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Enable Logs
enableLogs: false,
// uncomment the line below to enable Spotlight (https://spotlightjs.com)
// spotlight: __DEV__,
});
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
(0, vitest_1.expect)((0, javascript_1.addSentryInitWithSdkImport)(input, {
dsn: 'dsn',
enableLogs: false,
})).toBe(expectedOutput);
});
(0, vitest_1.it)('adds sdk import and sentry init under last import in the file and enables session replay and feedback widget', () => {
const input = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
const expectedOutput = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'dsn',
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Enable Logs
enableLogs: false,
// Configure Session Replay
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1,
integrations: [Sentry.mobileReplayIntegration(), Sentry.feedbackIntegration()],
// uncomment the line below to enable Spotlight (https://spotlightjs.com)
// spotlight: __DEV__,
});
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
(0, vitest_1.expect)((0, javascript_1.addSentryInitWithSdkImport)(input, {
dsn: 'dsn',
enableSessionReplay: true,
enableFeedbackWidget: true,
})).toBe(expectedOutput);
});
(0, vitest_1.it)('adds sdk import and sentry init with all features enabled', () => {
const input = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
const expectedOutput = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'dsn',
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Enable Logs
enableLogs: true,
// Configure Session Replay
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1,
integrations: [Sentry.mobileReplayIntegration(), Sentry.feedbackIntegration()],
// uncomment the line below to enable Spotlight (https://spotlightjs.com)
// spotlight: __DEV__,
});
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
(0, vitest_1.expect)((0, javascript_1.addSentryInitWithSdkImport)(input, {
dsn: 'dsn',
enableSessionReplay: true,
enableFeedbackWidget: true,
enableLogs: true,
})).toBe(expectedOutput);
});
(0, vitest_1.it)('adds sdk import and sentry init with logs enabled and other features disabled', () => {
const input = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
const expectedOutput = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'dsn',
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Enable Logs
enableLogs: true,
// uncomment the line below to enable Spotlight (https://spotlightjs.com)
// spotlight: __DEV__,
});
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
(0, vitest_1.expect)((0, javascript_1.addSentryInitWithSdkImport)(input, {
dsn: 'dsn',
enableSessionReplay: false,
enableFeedbackWidget: false,
enableLogs: true,
})).toBe(expectedOutput);
});
(0, vitest_1.it)('does not add sdk import and sentry init in the file without imports', () => {
const input = `export const test = 'test';`;
(0, vitest_1.expect)((0, javascript_1.addSentryInitWithSdkImport)(input, { dsn: 'dsn' })).toBe(input);
});
(0, vitest_1.it)('does not add sdk import and sentry init in the empty file', () => {
const input = '';
(0, vitest_1.expect)((0, javascript_1.addSentryInitWithSdkImport)(input, { dsn: 'dsn' })).toBe(input);
});
});
(0, vitest_1.describe)('doesJsCodeIncludeSdkSentryImport', () => {
(0, vitest_1.it)('returns true if code has sdk import', () => {
const input = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
import * as Sentry from '@sentry/react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
(0, vitest_1.expect)((0, javascript_1.doesJsCodeIncludeSdkSentryImport)(input, {
sdkPackageName: '@sentry/react-native',
})).toBe(true);
});
(0, vitest_1.it)('returns true if code has sdk require', () => {
const input = `import * as React from 'react';
const test = 'test';
import { View } from 'react-native';
const Sentry = require('@sentry/react-native');
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`;
(0, vitest_1.expect)((0, javascript_1.doesJsCodeIncludeSdkSentryImport)(input, {
sdkPackageName: '@sentry/react-native',
})).toBe(true);
});
(0, vitest_1.it)('returns false if code does not have sdk import', () => {
const input = `export const test = 'test';`;
(0, vitest_1.expect)((0, javascript_1.doesJsCodeIncludeSdkSentryImport)(input, {
sdkPackageName: '@sentry/react-native',
})).toBe(false);
});
(0, vitest_1.it)('returns false for empty file', () => {
const input = '';
(0, vitest_1.expect)((0, javascript_1.doesJsCodeIncludeSdkSentryImport)(input, {
sdkPackageName: '@sentry/react-native',
})).toBe(false);
});
});
(0, vitest_1.describe)('addSentryWrap', () => {
(0, vitest_1.it)('wraps the root app component', () => {
const mod = (0, magicast_1.parseModule)(`import * as React from 'react';
import * as Sentry from '@sentry/react-native';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default App;`);
const expectedOutput = `import * as React from 'react';
import * as Sentry from '@sentry/react-native';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default Sentry.wrap(App);`;
const result = (0, javascript_1.checkAndWrapRootComponent)(mod);
(0, vitest_1.expect)(result).toBe(javascript_1.SentryWrapResult.Success);
(0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(expectedOutput);
});
(0, vitest_1.it)('wraps a wrapped root app component', () => {
const mod = (0, magicast_1.parseModule)(`import * as React from 'react';
import * as Sentry from '@sentry/react-native';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default AnotheWrapper.wrap(App);`);
const expectedOutput = `import * as React from 'react';
import * as Sentry from '@sentry/react-native';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default Sentry.wrap(AnotheWrapper.wrap(App));`;
const result = (0, javascript_1.checkAndWrapRootComponent)(mod);
(0, vitest_1.expect)(result).toBe(javascript_1.SentryWrapResult.Success);
(0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(expectedOutput);
});
(0, vitest_1.it)('wraps a root app named function', () => {
const mod = (0, magicast_1.parseModule)(`import * as Sentry from '@sentry/react-native';
export default function RootLayout() {
return (
<View>
Test App
</View>
);
}`);
const expectedOutput = `import * as Sentry from '@sentry/react-native';
export default Sentry.wrap(function RootLayout() {
return (
<View>
Test App
</View>
);
});`;
const result = (0, javascript_1.checkAndWrapRootComponent)(mod);
(0, vitest_1.expect)(result).toBe(javascript_1.SentryWrapResult.Success);
(0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(expectedOutput);
});
(0, vitest_1.it)('wraps a wrapped root app named function', () => {
const mod = (0, magicast_1.parseModule)(`import * as Sentry from '@sentry/react-native';
export default Another.wrapper(function RootLayout() {
return (
<View>
Test App
</View>
);
});`);
const expectedOutput = `import * as Sentry from '@sentry/react-native';
export default Sentry.wrap(Another.wrapper(function RootLayout() {
return (
<View>
Test App
</View>
);
}));`;
const result = (0, javascript_1.checkAndWrapRootComponent)(mod);
(0, vitest_1.expect)(result).toBe(javascript_1.SentryWrapResult.Success);
(0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(expectedOutput);
});
(0, vitest_1.it)('wraps a root app anonymous function', () => {
const mod = (0, magicast_1.parseModule)(`import * as Sentry from '@sentry/react-native';
export default () => {
return (
<View>
Test App
</View>
);
}`);
const expectedOutput = `import * as Sentry from '@sentry/react-native';
export default Sentry.wrap(() => {
return (
<View>
Test App
</View>
);
});`;
const result = (0, javascript_1.checkAndWrapRootComponent)(mod);
(0, vitest_1.expect)(result).toBe(javascript_1.SentryWrapResult.Success);
(0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(expectedOutput);
});
(0, vitest_1.it)('wraps a wrapped root app anonymous function', () => {
const mod = (0, magicast_1.parseModule)(`import * as Sentry from '@sentry/react-native';
export default Another.wrap(() => {
return (
<View>
Test App
</View>
);
});`);
const expectedOutput = `import * as Sentry from '@sentry/react-native';
export default Sentry.wrap(Another.wrap(() => {
return (
<View>
Test App
</View>
);
}));`;
const result = (0, javascript_1.checkAndWrapRootComponent)(mod);
(0, vitest_1.expect)(result).toBe(javascript_1.SentryWrapResult.Success);
(0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(expectedOutput);
});
(0, vitest_1.it)('wraps a complex root function', () => {
// This is the default export for a new Expo 52 project
const mod = (0, magicast_1.parseModule)(`import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import { StatusBar } from 'expo-status-bar';
import { useEffect } from 'react';
import 'react-native-reanimated';
import { useColorScheme } from '@/hooks/useColorScheme';
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'https://sentry.io/123',
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// uncomment the line below to enable Spotlight (https://spotlightjs.com)
// spotlight: __DEV__,
});
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
const colorScheme = useColorScheme();
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
});
useEffect(() => {
if (loaded) {
SplashScreen.hideAsync();
}
}, [loaded]);
if (!loaded) {
return null;
}
return (
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
<StatusBar style="auto" />
</ThemeProvider>
);
}
`);
const expectedOutput = `import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import { StatusBar } from 'expo-status-bar';
import { useEffect } from 'react';
import 'react-native-reanimated';
import { useColorScheme } from '@/hooks/useColorScheme';
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'https://sentry.io/123',
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// uncomment the line below to enable Spotlight (https://spotlightjs.com)
// spotlight: __DEV__,
});
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
export default Sentry.wrap(function RootLayout() {
const colorScheme = useColorScheme();
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
});
useEffect(() => {
if (loaded) {
SplashScreen.hideAsync();
}
}, [loaded]);
if (!loaded) {
return null;
}
return (
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
<StatusBar style="auto" />
</ThemeProvider>
);
});`;
const result = (0, javascript_1.checkAndWrapRootComponent)(mod);
(0, vitest_1.expect)(result).toBe(javascript_1.SentryWrapResult.Success);
(0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(expectedOutput);
});
(0, vitest_1.it)('wraps a root app anonymous complex function', () => {
const mod = (0, magicast_1.parseModule)(`import * as Sentry from '@sentry/react-native';
export default () => {
const colorScheme = useColorScheme();
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
});
useEffect(() => {
if (loaded) {
SplashScreen.hideAsync();
}
}, [loaded]);
if (!loaded) {
return null;
}
return (
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
<StatusBar style="auto" />
</ThemeProvider>
);
}`);
const expectedOutput = `import * as Sentry from '@sentry/react-native';
export default Sentry.wrap(() => {
const colorScheme = useColorScheme();
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
});
useEffect(() => {
if (loaded) {
SplashScreen.hideAsync();
}
}, [loaded]);
if (!loaded) {
return null;
}
return (
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
<StatusBar style="auto" />
</ThemeProvider>
);
});`;
const result = (0, javascript_1.checkAndWrapRootComponent)(mod);
(0, vitest_1.expect)(result).toBe(javascript_1.SentryWrapResult.Success);
(0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(expectedOutput);
});
(0, vitest_1.it)('wraps a default class export', () => {
const mod = (0, magicast_1.parseModule)(`import * as Sentry from '@sentry/react-native';
export default class RootLayout extends React.Component {
render() {
return (
<View>
Test App
</View>
);
}
}`);
const expectedOutput = `import * as Sentry from '@sentry/react-native';
export default Sentry.wrap(class RootLayout extends React.Component {
render() {
return (
<View>
Test App
</View>
);
}
});`;
const result = (0, javascript_1.checkAndWrapRootComponent)(mod);
(0, vitest_1.expect)(result).toBe(javascript_1.SentryWrapResult.Success);
(0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(expectedOutput);
});
(0, vitest_1.it)('does not wrap a root app component if not found', () => {
const input = `import * as React from 'react';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export { App };`;
const mod = (0, magicast_1.parseModule)(input);
const result = (0, javascript_1.checkAndWrapRootComponent)(mod);
(0, vitest_1.expect)(result).toBe(javascript_1.SentryWrapResult.NotFound);
(0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(input);
});
(0, vitest_1.it)('does not wrap a root app component if already wrapped', () => {
const input = `import * as React from 'react';
import * as Sentry from '@sentry/react-native';
import { View } from 'react-native';
const App = () => {
return (
<View>
Test App
</View>
);
};
export default Sentry.wrap(App);`;
const mod = (0, magicast_1.parseModule)(input);
const result = (0, javascript_1.checkAndWrapRootComponent)(mod);
(0, vitest_1.expect)(result).toBe(javascript_1.SentryWrapResult.AlreadyWrapped);
(0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(input);
});
(0, vitest_1.it)('does not wrap the root app component in an empty file', () => {
const mod = (0, magicast_1.parseModule)(``);
const result = (0, javascript_1.checkAndWrapRootComponent)(mod);
(0, vitest_1.expect)(result).toBe(javascript_1.SentryWrapResult.NotFound);
(0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(``);
});
});
(0, vitest_1.it)('does detect Sentry.wrap if exists', () => {
const mod = (0, magicast_1.parseModule)(`export default Sentry.wrap(App);`);
const result = (0, javascript_1.doesContainSentryWrap)(mod.$ast);
(0, vitest_1.expect)(result).toBeTruthy();
});
(0, vitest_1.it)('does not detect Sentry.wrap if not present', () => {
const mod = (0, magicast_1.parseModule)(`export default App;`);
const result = (0, javascript_1.doesContainSentryWrap)(mod.$ast);
(0, vitest_1.expect)(result).toBeFalsy();
});
});
//# sourceMappingURL=javascript.test.js.map