@sentry/wizard 6.10.0 → 6.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/ci-ensure-runtime-loaded.sh +82 -0
- package/dist/e2e-tests/tests/angular-17.test.js +72 -82
- package/dist/e2e-tests/tests/angular-17.test.js.map +1 -1
- package/dist/e2e-tests/tests/angular-19.test.js +71 -80
- package/dist/e2e-tests/tests/angular-19.test.js.map +1 -1
- package/dist/e2e-tests/tests/cloudflare-worker.test.d.ts +1 -0
- package/dist/e2e-tests/tests/cloudflare-worker.test.js +64 -0
- package/dist/e2e-tests/tests/cloudflare-worker.test.js.map +1 -0
- package/dist/e2e-tests/tests/cloudflare-wrangler-sourcemaps.test.js +2 -5
- package/dist/e2e-tests/tests/cloudflare-wrangler-sourcemaps.test.js.map +1 -1
- package/dist/e2e-tests/tests/expo.test.js +36 -61
- package/dist/e2e-tests/tests/expo.test.js.map +1 -1
- package/dist/e2e-tests/tests/flutter.test.js +63 -70
- package/dist/e2e-tests/tests/flutter.test.js.map +1 -1
- package/dist/e2e-tests/tests/help-message.test.js +2 -2
- package/dist/e2e-tests/tests/help-message.test.js.map +1 -1
- package/dist/e2e-tests/tests/nextjs-14.test.js +48 -76
- package/dist/e2e-tests/tests/nextjs-14.test.js.map +1 -1
- package/dist/e2e-tests/tests/nextjs-15.test.js +89 -99
- package/dist/e2e-tests/tests/nextjs-15.test.js.map +1 -1
- package/dist/e2e-tests/tests/nextjs-16.test.js +48 -45
- package/dist/e2e-tests/tests/nextjs-16.test.js.map +1 -1
- package/dist/e2e-tests/tests/nuxt-3.test.js +45 -58
- package/dist/e2e-tests/tests/nuxt-3.test.js.map +1 -1
- package/dist/e2e-tests/tests/nuxt-4.test.js +59 -73
- package/dist/e2e-tests/tests/nuxt-4.test.js.map +1 -1
- package/dist/e2e-tests/tests/pnpm-workspace.test.js +4 -7
- package/dist/e2e-tests/tests/pnpm-workspace.test.js.map +1 -1
- package/dist/e2e-tests/tests/react-native.test.js +44 -80
- package/dist/e2e-tests/tests/react-native.test.js.map +1 -1
- package/dist/e2e-tests/tests/react-router.test.js +163 -145
- package/dist/e2e-tests/tests/react-router.test.js.map +1 -1
- package/dist/e2e-tests/tests/remix.test.js +162 -132
- package/dist/e2e-tests/tests/remix.test.js.map +1 -1
- package/dist/e2e-tests/tests/sveltekit-hooks.test.js +48 -36
- package/dist/e2e-tests/tests/sveltekit-hooks.test.js.map +1 -1
- package/dist/e2e-tests/tests/sveltekit-tracing.test.js +3 -6
- package/dist/e2e-tests/tests/sveltekit-tracing.test.js.map +1 -1
- package/dist/e2e-tests/utils/index.d.ts +15 -43
- package/dist/e2e-tests/utils/index.js +95 -185
- package/dist/e2e-tests/utils/index.js.map +1 -1
- package/dist/get-e2e-test-matrix.mjs +11 -0
- package/dist/lib/Constants.d.ts +1 -0
- package/dist/lib/Constants.js +5 -0
- package/dist/lib/Constants.js.map +1 -1
- package/dist/src/android/android-wizard.js +2 -4
- package/dist/src/android/android-wizard.js.map +1 -1
- package/dist/src/angular/angular-wizard.js +4 -6
- package/dist/src/angular/angular-wizard.js.map +1 -1
- package/dist/src/angular/sdk-setup.js +1 -1
- package/dist/src/angular/sdk-setup.js.map +1 -1
- package/dist/src/apple/apple-wizard.js +2 -4
- package/dist/src/apple/apple-wizard.js.map +1 -1
- package/dist/src/cloudflare/cloudflare-wizard.d.ts +3 -0
- package/dist/src/cloudflare/cloudflare-wizard.js +99 -0
- package/dist/src/cloudflare/cloudflare-wizard.js.map +1 -0
- package/dist/src/cloudflare/sdk-setup.d.ts +7 -0
- package/dist/src/cloudflare/sdk-setup.js +47 -0
- package/dist/src/cloudflare/sdk-setup.js.map +1 -0
- package/dist/src/cloudflare/templates.d.ts +4 -0
- package/dist/src/cloudflare/templates.js +44 -0
- package/dist/src/cloudflare/templates.js.map +1 -0
- package/dist/src/cloudflare/wrangler/create-wrangler-config.d.ts +4 -0
- package/dist/src/cloudflare/wrangler/create-wrangler-config.js +27 -0
- package/dist/src/cloudflare/wrangler/create-wrangler-config.js.map +1 -0
- package/dist/src/cloudflare/wrangler/ensure-wrangler-config.d.ts +4 -0
- package/dist/src/cloudflare/wrangler/ensure-wrangler-config.js +25 -0
- package/dist/src/cloudflare/wrangler/ensure-wrangler-config.js.map +1 -0
- package/dist/src/cloudflare/wrangler/find-wrangler-config.d.ts +4 -0
- package/dist/src/cloudflare/wrangler/find-wrangler-config.js +23 -0
- package/dist/src/cloudflare/wrangler/find-wrangler-config.js.map +1 -0
- package/dist/src/cloudflare/wrangler/get-entry-point-from-wrangler-config.d.ts +6 -0
- package/dist/src/cloudflare/wrangler/get-entry-point-from-wrangler-config.js +52 -0
- package/dist/src/cloudflare/wrangler/get-entry-point-from-wrangler-config.js.map +1 -0
- package/dist/src/cloudflare/wrangler/update-wrangler-config.d.ts +17 -0
- package/dist/src/cloudflare/wrangler/update-wrangler-config.js +173 -0
- package/dist/src/cloudflare/wrangler/update-wrangler-config.js.map +1 -0
- package/dist/src/cloudflare/wrap-worker.d.ts +32 -0
- package/dist/src/cloudflare/wrap-worker.js +109 -0
- package/dist/src/cloudflare/wrap-worker.js.map +1 -0
- package/dist/src/flutter/flutter-wizard.js +3 -6
- package/dist/src/flutter/flutter-wizard.js.map +1 -1
- package/dist/src/nextjs/nextjs-wizard.js +0 -2
- package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
- package/dist/src/nuxt/nuxt-wizard.js +3 -5
- package/dist/src/nuxt/nuxt-wizard.js.map +1 -1
- package/dist/src/react-native/react-native-wizard.js +2 -4
- package/dist/src/react-native/react-native-wizard.js.map +1 -1
- package/dist/src/react-router/react-router-wizard.js +3 -5
- package/dist/src/react-router/react-router-wizard.js.map +1 -1
- package/dist/src/react-router/sdk-setup.d.ts +1 -1
- package/dist/src/react-router/sdk-setup.js +3 -4
- package/dist/src/react-router/sdk-setup.js.map +1 -1
- package/dist/src/remix/remix-wizard.js +2 -4
- package/dist/src/remix/remix-wizard.js.map +1 -1
- package/dist/src/run.d.ts +1 -1
- package/dist/src/run.js +5 -0
- package/dist/src/run.js.map +1 -1
- package/dist/src/sveltekit/sveltekit-wizard.js +2 -4
- package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
- package/dist/src/utils/abort-if-sportlight-not-supported.d.ts +5 -0
- package/dist/src/utils/abort-if-sportlight-not-supported.js +40 -0
- package/dist/src/utils/abort-if-sportlight-not-supported.js.map +1 -0
- package/dist/src/utils/ast-utils.d.ts +1 -1
- package/dist/src/utils/ast-utils.js.map +1 -1
- package/dist/src/utils/clack/index.d.ts +2 -2
- package/dist/src/utils/clack/index.js.map +1 -1
- package/dist/src/utils/clack/mcp-config.js +117 -59
- package/dist/src/utils/clack/mcp-config.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/test/angular/angular-wizard.test.js +2 -4
- package/dist/test/angular/angular-wizard.test.js.map +1 -1
- package/dist/test/cloudflare/create-wrangler-config.test.d.ts +1 -0
- package/dist/test/cloudflare/create-wrangler-config.test.js +48 -0
- package/dist/test/cloudflare/create-wrangler-config.test.js.map +1 -0
- package/dist/test/cloudflare/ensure-wrangler-config.test.d.ts +1 -0
- package/dist/test/cloudflare/ensure-wrangler-config.test.js +61 -0
- package/dist/test/cloudflare/ensure-wrangler-config.test.js.map +1 -0
- package/dist/test/cloudflare/find-wrangler-config.test.d.ts +1 -0
- package/dist/test/cloudflare/find-wrangler-config.test.js +77 -0
- package/dist/test/cloudflare/find-wrangler-config.test.js.map +1 -0
- package/dist/test/cloudflare/get-entry-point-from-wrangler-config.test.d.ts +1 -0
- package/dist/test/cloudflare/get-entry-point-from-wrangler-config.test.js +81 -0
- package/dist/test/cloudflare/get-entry-point-from-wrangler-config.test.js.map +1 -0
- package/dist/test/cloudflare/sdk-setup.test.d.ts +1 -0
- package/dist/test/cloudflare/sdk-setup.test.js +152 -0
- package/dist/test/cloudflare/sdk-setup.test.js.map +1 -0
- package/dist/test/cloudflare/templates.test.d.ts +1 -0
- package/dist/test/cloudflare/templates.test.js +68 -0
- package/dist/test/cloudflare/templates.test.js.map +1 -0
- package/dist/test/cloudflare/update-wrangler-config.test.d.ts +1 -0
- package/dist/test/cloudflare/update-wrangler-config.test.js +216 -0
- package/dist/test/cloudflare/update-wrangler-config.test.js.map +1 -0
- package/dist/test/cloudflare/wrap-worker.test.d.ts +1 -0
- package/dist/test/cloudflare/wrap-worker.test.js +143 -0
- package/dist/test/cloudflare/wrap-worker.test.js.map +1 -0
- package/dist/test/react-router/sdk-setup.test.js +2 -2
- package/dist/test/react-router/sdk-setup.test.js.map +1 -1
- package/dist/test/utils/clack/mcp-config.test.js +176 -51
- package/dist/test/utils/clack/mcp-config.test.js.map +1 -1
- package/package.json +5 -4
|
@@ -28,136 +28,160 @@ const fs = __importStar(require("node:fs"));
|
|
|
28
28
|
const Constants_1 = require("../../lib/Constants");
|
|
29
29
|
const utils_1 = require("../utils");
|
|
30
30
|
const vitest_1 = require("vitest");
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
'
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
`Sentry.init({
|
|
77
|
-
dsn: "${utils_1.TEST_ARGS.PROJECT_DSN}",`,
|
|
78
|
-
'integrations: [Sentry.reactRouterTracingIntegration(), Sentry.replayIntegration()]',
|
|
79
|
-
'enableLogs: true,',
|
|
80
|
-
'tracesSampleRate: 1.0,',
|
|
81
|
-
]);
|
|
82
|
-
});
|
|
83
|
-
(0, vitest_1.test)('package.json scripts are updated correctly', () => {
|
|
84
|
-
(0, utils_1.checkFileContents)(`${projectDir}/package.json`, [
|
|
85
|
-
`"start": "NODE_OPTIONS='--import ./instrument.server.mjs' react-router-serve ./build/server/index.js"`,
|
|
86
|
-
`"dev": "NODE_OPTIONS='--import ./instrument.server.mjs' react-router dev"`,
|
|
87
|
-
]);
|
|
88
|
-
});
|
|
89
|
-
(0, vitest_1.test)('entry.server file contains Sentry instrumentation', () => {
|
|
90
|
-
(0, utils_1.checkFileContents)(`${projectDir}/app/entry.server.tsx`, [
|
|
91
|
-
'import * as Sentry from',
|
|
92
|
-
'@sentry/react-router',
|
|
93
|
-
'export const handleError = Sentry.createSentryHandleError(',
|
|
94
|
-
'export default Sentry.wrapSentryHandleRequest(handleRequest);'
|
|
95
|
-
]);
|
|
96
|
-
});
|
|
97
|
-
(0, vitest_1.test)('instrument.server file contains Sentry initialization', () => {
|
|
98
|
-
(0, utils_1.checkFileContents)(`${projectDir}/instrument.server.mjs`, [
|
|
99
|
-
'import * as Sentry from \'@sentry/react-router\';',
|
|
100
|
-
`Sentry.init({
|
|
101
|
-
dsn: "${utils_1.TEST_ARGS.PROJECT_DSN}",`,
|
|
102
|
-
'enableLogs: true,',
|
|
103
|
-
]);
|
|
104
|
-
});
|
|
105
|
-
(0, vitest_1.test)('root file contains Sentry ErrorBoundary', () => {
|
|
106
|
-
(0, utils_1.checkFileContents)(`${projectDir}/app/root.tsx`, [
|
|
107
|
-
'import * as Sentry from',
|
|
108
|
-
'@sentry/react-router',
|
|
109
|
-
'export function ErrorBoundary',
|
|
110
|
-
'Sentry.captureException(error)',
|
|
111
|
-
]);
|
|
112
|
-
});
|
|
113
|
-
(0, vitest_1.test)('vite.config file contains sentryReactRouter plugin', () => {
|
|
114
|
-
(0, utils_1.checkFileContents)(`${projectDir}/vite.config.ts`, [
|
|
115
|
-
'import { sentryReactRouter } from',
|
|
116
|
-
'@sentry/react-router',
|
|
117
|
-
'sentryReactRouter(',
|
|
118
|
-
'authToken: process.env.SENTRY_AUTH_TOKEN',
|
|
119
|
-
]);
|
|
120
|
-
});
|
|
121
|
-
(0, vitest_1.test)('react-router.config file contains buildEnd hook with sentryOnBuildEnd', () => {
|
|
122
|
-
(0, utils_1.checkFileContents)(`${projectDir}/react-router.config.ts`, [
|
|
123
|
-
'import { sentryOnBuildEnd } from',
|
|
124
|
-
'@sentry/react-router',
|
|
125
|
-
'ssr: true,',
|
|
126
|
-
'buildEnd: async',
|
|
127
|
-
'await sentryOnBuildEnd({',
|
|
128
|
-
]);
|
|
129
|
-
});
|
|
130
|
-
(0, vitest_1.test)('builds successfully', async () => {
|
|
131
|
-
await (0, utils_1.checkIfBuilds)(projectDir);
|
|
132
|
-
}, 60000); // 1 minute timeout
|
|
133
|
-
(0, vitest_1.test)('runs on dev mode correctly', async () => {
|
|
134
|
-
await (0, utils_1.checkIfRunsOnDevMode)(projectDir, 'to expose');
|
|
135
|
-
}, 30000); // 30 second timeout
|
|
136
|
-
(0, vitest_1.test)('runs on prod mode correctly', async () => {
|
|
137
|
-
await (0, utils_1.checkIfRunsOnProdMode)(projectDir, 'react-router-serve');
|
|
138
|
-
}, 30000); // 30 second timeout
|
|
31
|
+
//@ts-expect-error - clifty is ESM only
|
|
32
|
+
const clifty_1 = require("clifty");
|
|
33
|
+
async function runWizardOnReactRouterProject(projectDir, opts) {
|
|
34
|
+
const { modifiedFiles = false } = opts || {};
|
|
35
|
+
const wizardInteraction = (0, clifty_1.withEnv)({
|
|
36
|
+
cwd: projectDir,
|
|
37
|
+
}).defineInteraction();
|
|
38
|
+
if (modifiedFiles) {
|
|
39
|
+
wizardInteraction
|
|
40
|
+
.whenAsked('Do you want to continue anyway?')
|
|
41
|
+
.respondWith(clifty_1.KEYS.ENTER);
|
|
42
|
+
}
|
|
43
|
+
wizardInteraction
|
|
44
|
+
.whenAsked('Please select your package manager.')
|
|
45
|
+
.respondWith(clifty_1.KEYS.DOWN, clifty_1.KEYS.ENTER)
|
|
46
|
+
.expectOutput('Installing @sentry/react-router')
|
|
47
|
+
.expectOutput('Installed @sentry/react-router', {
|
|
48
|
+
timeout: 240000,
|
|
49
|
+
})
|
|
50
|
+
.whenAsked('Do you want to enable Tracing')
|
|
51
|
+
.respondWith(clifty_1.KEYS.ENTER)
|
|
52
|
+
.whenAsked('Do you want to enable Session Replay')
|
|
53
|
+
.respondWith(clifty_1.KEYS.ENTER)
|
|
54
|
+
.whenAsked('Do you want to enable Logs')
|
|
55
|
+
.respondWith(clifty_1.KEYS.ENTER)
|
|
56
|
+
.whenAsked('Do you want to enable Profiling')
|
|
57
|
+
.respondWith(clifty_1.KEYS.ENTER)
|
|
58
|
+
.expectOutput('Installing @sentry/profiling-node')
|
|
59
|
+
.expectOutput('Installed @sentry/profiling-node', {
|
|
60
|
+
timeout: 240000,
|
|
61
|
+
})
|
|
62
|
+
.whenAsked('Do you want to create an example page')
|
|
63
|
+
.respondWith(clifty_1.KEYS.ENTER);
|
|
64
|
+
if (modifiedFiles) {
|
|
65
|
+
wizardInteraction
|
|
66
|
+
.whenAsked('Would you like to try running npx react-router reveal')
|
|
67
|
+
.respondWith(clifty_1.KEYS.ENTER)
|
|
68
|
+
.whenAsked('Did you apply the snippet above?')
|
|
69
|
+
.respondWith(clifty_1.KEYS.ENTER);
|
|
70
|
+
}
|
|
71
|
+
return wizardInteraction
|
|
72
|
+
.whenAsked('Optionally add a project-scoped MCP server configuration for the Sentry MCP?')
|
|
73
|
+
.respondWith(clifty_1.KEYS.DOWN, clifty_1.KEYS.ENTER)
|
|
74
|
+
.expectOutput('Successfully installed the Sentry React Router SDK!')
|
|
75
|
+
.run((0, utils_1.getWizardCommand)(Constants_1.Integration.reactRouter));
|
|
139
76
|
}
|
|
140
77
|
(0, vitest_1.describe)('React Router', () => {
|
|
141
78
|
(0, vitest_1.describe)('with empty project', () => {
|
|
142
|
-
|
|
143
|
-
const projectDir =
|
|
79
|
+
let wizardExitCode;
|
|
80
|
+
const { projectDir, cleanup } = (0, utils_1.createIsolatedTestEnv)('react-router-test-app');
|
|
144
81
|
(0, vitest_1.beforeAll)(async () => {
|
|
145
|
-
await runWizardOnReactRouterProject(projectDir
|
|
82
|
+
wizardExitCode = await runWizardOnReactRouterProject(projectDir);
|
|
146
83
|
});
|
|
147
84
|
(0, vitest_1.afterAll)(() => {
|
|
148
|
-
(
|
|
149
|
-
(0, utils_1.cleanupGit)(projectDir);
|
|
85
|
+
cleanup();
|
|
150
86
|
});
|
|
151
|
-
|
|
87
|
+
(0, vitest_1.test)('exits with exit code 0', () => {
|
|
88
|
+
(0, vitest_1.expect)(wizardExitCode).toBe(0);
|
|
89
|
+
});
|
|
90
|
+
(0, vitest_1.test)('package.json is updated correctly', () => {
|
|
91
|
+
(0, utils_1.checkPackageJson)(projectDir, '@sentry/react-router');
|
|
92
|
+
});
|
|
93
|
+
(0, vitest_1.test)('.env.sentry-build-plugin is created and contains the auth token', () => {
|
|
94
|
+
(0, utils_1.checkEnvBuildPlugin)(projectDir);
|
|
95
|
+
});
|
|
96
|
+
(0, vitest_1.test)('example page exists', () => {
|
|
97
|
+
(0, utils_1.checkFileExists)(`${projectDir}/app/routes/sentry-example-page.tsx`);
|
|
98
|
+
});
|
|
99
|
+
(0, vitest_1.test)('example API route exists', () => {
|
|
100
|
+
(0, utils_1.checkFileExists)(`${projectDir}/app/routes/api.sentry-example-api.ts`);
|
|
101
|
+
});
|
|
102
|
+
(0, vitest_1.test)('example page is added to routes configuration', () => {
|
|
103
|
+
(0, utils_1.checkFileContents)(`${projectDir}/app/routes.ts`, [
|
|
104
|
+
'route("/sentry-example-page", "routes/sentry-example-page.tsx")',
|
|
105
|
+
'route("/api/sentry-example-api", "routes/api.sentry-example-api.ts")',
|
|
106
|
+
]);
|
|
107
|
+
});
|
|
108
|
+
(0, vitest_1.test)('instrument.server file exists', () => {
|
|
109
|
+
(0, utils_1.checkFileExists)(`${projectDir}/instrument.server.mjs`);
|
|
110
|
+
});
|
|
111
|
+
(0, vitest_1.test)('entry.client file contains Sentry initialization', () => {
|
|
112
|
+
(0, utils_1.checkFileContents)(`${projectDir}/app/entry.client.tsx`, [
|
|
113
|
+
'import * as Sentry from',
|
|
114
|
+
'@sentry/react-router',
|
|
115
|
+
`Sentry.init({
|
|
116
|
+
dsn: "${utils_1.TEST_ARGS.PROJECT_DSN}",`,
|
|
117
|
+
'integrations: [Sentry.reactRouterTracingIntegration(), Sentry.replayIntegration()]',
|
|
118
|
+
'enableLogs: true,',
|
|
119
|
+
'tracesSampleRate: 1.0,',
|
|
120
|
+
]);
|
|
121
|
+
});
|
|
122
|
+
(0, vitest_1.test)('package.json scripts are updated correctly', () => {
|
|
123
|
+
(0, utils_1.checkFileContents)(`${projectDir}/package.json`, [
|
|
124
|
+
`"start": "NODE_OPTIONS='--import ./instrument.server.mjs' react-router-serve ./build/server/index.js"`,
|
|
125
|
+
`"dev": "NODE_OPTIONS='--import ./instrument.server.mjs' react-router dev"`,
|
|
126
|
+
]);
|
|
127
|
+
});
|
|
128
|
+
(0, vitest_1.test)('entry.server file contains Sentry instrumentation', () => {
|
|
129
|
+
(0, utils_1.checkFileContents)(`${projectDir}/app/entry.server.tsx`, [
|
|
130
|
+
'import * as Sentry from',
|
|
131
|
+
'@sentry/react-router',
|
|
132
|
+
'export const handleError = Sentry.createSentryHandleError(',
|
|
133
|
+
'export default Sentry.wrapSentryHandleRequest(handleRequest);',
|
|
134
|
+
]);
|
|
135
|
+
});
|
|
136
|
+
(0, vitest_1.test)('instrument.server file contains Sentry initialization', () => {
|
|
137
|
+
(0, utils_1.checkFileContents)(`${projectDir}/instrument.server.mjs`, [
|
|
138
|
+
"import * as Sentry from '@sentry/react-router';",
|
|
139
|
+
`Sentry.init({
|
|
140
|
+
dsn: "${utils_1.TEST_ARGS.PROJECT_DSN}",`,
|
|
141
|
+
'enableLogs: true,',
|
|
142
|
+
]);
|
|
143
|
+
});
|
|
144
|
+
(0, vitest_1.test)('root file contains Sentry ErrorBoundary', () => {
|
|
145
|
+
(0, utils_1.checkFileContents)(`${projectDir}/app/root.tsx`, [
|
|
146
|
+
'import * as Sentry from',
|
|
147
|
+
'@sentry/react-router',
|
|
148
|
+
'export function ErrorBoundary',
|
|
149
|
+
'Sentry.captureException(error)',
|
|
150
|
+
]);
|
|
151
|
+
});
|
|
152
|
+
(0, vitest_1.test)('vite.config file contains sentryReactRouter plugin', () => {
|
|
153
|
+
(0, utils_1.checkFileContents)(`${projectDir}/vite.config.ts`, [
|
|
154
|
+
'import { sentryReactRouter } from',
|
|
155
|
+
'@sentry/react-router',
|
|
156
|
+
'sentryReactRouter(',
|
|
157
|
+
'authToken: process.env.SENTRY_AUTH_TOKEN',
|
|
158
|
+
]);
|
|
159
|
+
});
|
|
160
|
+
(0, vitest_1.test)('react-router.config file contains buildEnd hook with sentryOnBuildEnd', () => {
|
|
161
|
+
(0, utils_1.checkFileContents)(`${projectDir}/react-router.config.ts`, [
|
|
162
|
+
'import { sentryOnBuildEnd } from',
|
|
163
|
+
'@sentry/react-router',
|
|
164
|
+
'ssr: true,',
|
|
165
|
+
'buildEnd: async',
|
|
166
|
+
'await sentryOnBuildEnd({',
|
|
167
|
+
]);
|
|
168
|
+
});
|
|
169
|
+
(0, vitest_1.test)('builds successfully', async () => {
|
|
170
|
+
await (0, utils_1.checkIfBuilds)(projectDir);
|
|
171
|
+
}, 60000); // 1 minute timeout
|
|
172
|
+
(0, vitest_1.test)('runs on dev mode correctly', async () => {
|
|
173
|
+
await (0, utils_1.checkIfRunsOnDevMode)(projectDir, 'to expose');
|
|
174
|
+
}, 30000); // 30 second timeout
|
|
175
|
+
(0, vitest_1.test)('runs on prod mode correctly', async () => {
|
|
176
|
+
await (0, utils_1.checkIfRunsOnProdMode)(projectDir, 'react-router-serve');
|
|
177
|
+
}, 30000); // 30 second timeout
|
|
152
178
|
});
|
|
153
179
|
(0, vitest_1.describe)('edge cases', () => {
|
|
154
|
-
const baseProjectDir = path.resolve(__dirname, '../test-applications/react-router-test-app');
|
|
155
180
|
(0, vitest_1.describe)('existing Sentry setup', () => {
|
|
156
|
-
|
|
157
|
-
const projectDir =
|
|
181
|
+
let wizardExitCode;
|
|
182
|
+
const { projectDir, cleanup } = (0, utils_1.createIsolatedTestEnv)('react-router-test-app');
|
|
158
183
|
(0, vitest_1.beforeAll)(async () => {
|
|
159
|
-
//
|
|
160
|
-
fs.cpSync(baseProjectDir, projectDir, { recursive: true });
|
|
184
|
+
// Add existing Sentry setup to the isolated test app
|
|
161
185
|
const clientEntryPath = path.join(projectDir, 'app', 'entry.client.tsx');
|
|
162
186
|
const existingContent = `import * as Sentry from "@sentry/react-router";
|
|
163
187
|
import { startTransition, StrictMode } from "react";
|
|
@@ -178,28 +202,27 @@ startTransition(() => {
|
|
|
178
202
|
);
|
|
179
203
|
});`;
|
|
180
204
|
fs.writeFileSync(clientEntryPath, existingContent);
|
|
181
|
-
await runWizardOnReactRouterProject(projectDir,
|
|
205
|
+
wizardExitCode = await runWizardOnReactRouterProject(projectDir, {
|
|
206
|
+
modifiedFiles: true,
|
|
207
|
+
});
|
|
182
208
|
});
|
|
183
209
|
(0, vitest_1.afterAll)(() => {
|
|
184
|
-
(
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
catch (e) {
|
|
190
|
-
// Ignore cleanup errors
|
|
191
|
-
}
|
|
210
|
+
cleanup();
|
|
211
|
+
});
|
|
212
|
+
(0, vitest_1.test)('exits with exit code 0', () => {
|
|
213
|
+
(0, vitest_1.expect)(wizardExitCode).toBe(0);
|
|
192
214
|
});
|
|
193
215
|
(0, vitest_1.test)('wizard handles existing Sentry without duplication', () => {
|
|
194
216
|
const clientContent = fs.readFileSync(`${projectDir}/app/entry.client.tsx`, 'utf8');
|
|
195
217
|
const sentryImportCount = (clientContent.match(/import \* as Sentry from "@sentry\/react-router"/g) || []).length;
|
|
196
|
-
const sentryInitCount = (clientContent.match(/Sentry\.init\(/g) || [])
|
|
218
|
+
const sentryInitCount = (clientContent.match(/Sentry\.init\(/g) || [])
|
|
219
|
+
.length;
|
|
197
220
|
(0, vitest_1.expect)(sentryImportCount).toBe(1);
|
|
198
221
|
(0, vitest_1.expect)(sentryInitCount).toBe(1);
|
|
199
222
|
});
|
|
200
223
|
// Only test the essential checks for this edge case
|
|
201
224
|
(0, vitest_1.test)('package.json is updated correctly', () => {
|
|
202
|
-
(0, utils_1.checkPackageJson)(projectDir,
|
|
225
|
+
(0, utils_1.checkPackageJson)(projectDir, '@sentry/react-router');
|
|
203
226
|
});
|
|
204
227
|
(0, vitest_1.test)('essential files exist or wizard completes gracefully', () => {
|
|
205
228
|
// Check if key directories exist
|
|
@@ -211,42 +234,37 @@ startTransition(() => {
|
|
|
211
234
|
(0, vitest_1.expect)(fs.existsSync(packageJsonPath)).toBe(true);
|
|
212
235
|
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
|
|
213
236
|
const packageJson = JSON.parse(packageJsonContent);
|
|
214
|
-
const hasSentryPackage =
|
|
215
|
-
|
|
237
|
+
const hasSentryPackage = packageJson.dependencies?.['@sentry/react-router'] ||
|
|
238
|
+
packageJson.devDependencies?.['@sentry/react-router'];
|
|
216
239
|
// The wizard should have at least installed the Sentry package
|
|
217
240
|
(0, vitest_1.expect)(hasSentryPackage).toBeTruthy();
|
|
218
241
|
});
|
|
219
242
|
});
|
|
220
243
|
(0, vitest_1.describe)('missing entry files', () => {
|
|
221
|
-
|
|
222
|
-
const projectDir =
|
|
244
|
+
let wizardExitCode;
|
|
245
|
+
const { projectDir, cleanup } = (0, utils_1.createIsolatedTestEnv)('react-router-test-app');
|
|
223
246
|
(0, vitest_1.beforeAll)(async () => {
|
|
224
247
|
// Copy project and remove entry files
|
|
225
|
-
fs.cpSync(baseProjectDir, projectDir, { recursive: true });
|
|
226
248
|
const entryClientPath = path.join(projectDir, 'app', 'entry.client.tsx');
|
|
227
249
|
const entryServerPath = path.join(projectDir, 'app', 'entry.server.tsx');
|
|
228
250
|
if (fs.existsSync(entryClientPath))
|
|
229
251
|
fs.unlinkSync(entryClientPath);
|
|
230
252
|
if (fs.existsSync(entryServerPath))
|
|
231
253
|
fs.unlinkSync(entryServerPath);
|
|
232
|
-
await runWizardOnReactRouterProject(projectDir
|
|
254
|
+
wizardExitCode = await runWizardOnReactRouterProject(projectDir);
|
|
233
255
|
});
|
|
234
256
|
(0, vitest_1.afterAll)(() => {
|
|
235
|
-
(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
catch (e) {
|
|
241
|
-
// Ignore cleanup errors
|
|
242
|
-
}
|
|
257
|
+
cleanup();
|
|
258
|
+
});
|
|
259
|
+
(0, vitest_1.test)('exits with exit code 0', () => {
|
|
260
|
+
(0, vitest_1.expect)(wizardExitCode).toBe(0);
|
|
243
261
|
});
|
|
244
262
|
(0, vitest_1.test)('wizard creates missing entry files', () => {
|
|
245
263
|
(0, utils_1.checkFileExists)(`${projectDir}/app/entry.client.tsx`);
|
|
246
264
|
(0, utils_1.checkFileExists)(`${projectDir}/app/entry.server.tsx`);
|
|
247
265
|
});
|
|
248
266
|
(0, vitest_1.test)('basic configuration still works', () => {
|
|
249
|
-
(0, utils_1.checkPackageJson)(projectDir,
|
|
267
|
+
(0, utils_1.checkPackageJson)(projectDir, '@sentry/react-router');
|
|
250
268
|
(0, utils_1.checkFileExists)(`${projectDir}/instrument.server.mjs`);
|
|
251
269
|
});
|
|
252
270
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-router.test.js","sourceRoot":"","sources":["../../../e2e-tests/tests/react-router.test.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'node:path';\nimport * as fs from 'node:fs';\nimport { Integration } from '../../lib/Constants';\nimport {\n KEYS,\n TEST_ARGS,\n checkEnvBuildPlugin,\n checkFileContents,\n checkFileExists,\n checkIfBuilds,\n checkIfRunsOnDevMode,\n checkIfRunsOnProdMode,\n checkPackageJson,\n cleanupGit,\n revertLocalChanges,\n startWizardInstance,\n} from '../utils';\nimport { afterAll, beforeAll, describe, test, expect } from 'vitest';\n\nasync function runWizardOnReactRouterProject(\n projectDir: string,\n integration: Integration,\n) {\n const wizardInstance = startWizardInstance(integration, projectDir);\n\n const packageManagerPrompted = await wizardInstance.waitForOutput(\n 'Please select your package manager.',\n );\n\n const tracingOptionPrompted =\n packageManagerPrompted &&\n (await wizardInstance.sendStdinAndWaitForOutput(\n [KEYS.DOWN, KEYS.ENTER],\n 'to track the performance of your application?',\n { timeout: 240_000 }\n ));\n\n const replayOptionPrompted =\n tracingOptionPrompted &&\n (await wizardInstance.sendStdinAndWaitForOutput(\n [KEYS.ENTER],\n 'to get a video-like reproduction of errors during a user session?'\n ));\n\n const logOptionPrompted =\n replayOptionPrompted &&\n (await wizardInstance.sendStdinAndWaitForOutput(\n [KEYS.ENTER],\n 'to send your application logs to Sentry?'\n ));\n\n const profilingOptionPrompted =\n logOptionPrompted &&\n (await wizardInstance.sendStdinAndWaitForOutput(\n [KEYS.ENTER],\n 'to track application performance in detail?'\n ));\n\n const examplePagePrompted =\n profilingOptionPrompted &&\n (await wizardInstance.sendStdinAndWaitForOutput(\n [KEYS.ENTER],\n 'Do you want to create an example page'\n ));\n\n const mcpPrompted =\n examplePagePrompted &&\n (await wizardInstance.sendStdinAndWaitForOutput(\n [KEYS.ENTER],\n 'Optionally add a project-scoped MCP server configuration for the Sentry MCP?',\n { optional: true }\n ));\n\n mcpPrompted &&\n (await wizardInstance.sendStdinAndWaitForOutput(\n [KEYS.DOWN, KEYS.ENTER],\n 'Successfully installed the Sentry React Router SDK!'\n ));\n\n wizardInstance.kill();\n}\n\nfunction checkReactRouterProject(projectDir: string, integration: Integration) {\n test('package.json is updated correctly', () => {\n checkPackageJson(projectDir, integration);\n });\n\n test('.env.sentry-build-plugin is created and contains the auth token', () => {\n checkEnvBuildPlugin(projectDir);\n });\n\n test('example page exists', () => {\n checkFileExists(`${projectDir}/app/routes/sentry-example-page.tsx`);\n });\n\n test('example API route exists', () => {\n checkFileExists(`${projectDir}/app/routes/api.sentry-example-api.ts`);\n });\n\n test('example page is added to routes configuration', () => {\n checkFileContents(`${projectDir}/app/routes.ts`, [\n 'route(\"/sentry-example-page\", \"routes/sentry-example-page.tsx\")',\n 'route(\"/api/sentry-example-api\", \"routes/api.sentry-example-api.ts\")',\n ]);\n });\n\n test('instrument.server file exists', () => {\n checkFileExists(`${projectDir}/instrument.server.mjs`);\n });\n\n test('entry.client file contains Sentry initialization', () => {\n checkFileContents(`${projectDir}/app/entry.client.tsx`, [\n 'import * as Sentry from',\n '@sentry/react-router',\n `Sentry.init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",`,\n 'integrations: [Sentry.reactRouterTracingIntegration(), Sentry.replayIntegration()]',\n 'enableLogs: true,',\n 'tracesSampleRate: 1.0,',\n ]);\n });\n\n test('package.json scripts are updated correctly', () => {\n checkFileContents(`${projectDir}/package.json`, [\n `\"start\": \"NODE_OPTIONS='--import ./instrument.server.mjs' react-router-serve ./build/server/index.js\"`,\n `\"dev\": \"NODE_OPTIONS='--import ./instrument.server.mjs' react-router dev\"`,\n ]);\n });\n\n test('entry.server file contains Sentry instrumentation', () => {\n checkFileContents(`${projectDir}/app/entry.server.tsx`, [\n 'import * as Sentry from',\n '@sentry/react-router',\n 'export const handleError = Sentry.createSentryHandleError(',\n 'export default Sentry.wrapSentryHandleRequest(handleRequest);'\n ]);\n });\n\n test('instrument.server file contains Sentry initialization', () => {\n checkFileContents(`${projectDir}/instrument.server.mjs`, [\n 'import * as Sentry from \\'@sentry/react-router\\';',\n `Sentry.init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",`,\n 'enableLogs: true,',\n ]);\n });\n\n test('root file contains Sentry ErrorBoundary', () => {\n checkFileContents(`${projectDir}/app/root.tsx`, [\n 'import * as Sentry from',\n '@sentry/react-router',\n 'export function ErrorBoundary',\n 'Sentry.captureException(error)',\n ]);\n });\n\n test('vite.config file contains sentryReactRouter plugin', () => {\n checkFileContents(`${projectDir}/vite.config.ts`, [\n 'import { sentryReactRouter } from',\n '@sentry/react-router',\n 'sentryReactRouter(',\n 'authToken: process.env.SENTRY_AUTH_TOKEN',\n ]);\n });\n\n test('react-router.config file contains buildEnd hook with sentryOnBuildEnd', () => {\n checkFileContents(`${projectDir}/react-router.config.ts`, [\n 'import { sentryOnBuildEnd } from',\n '@sentry/react-router',\n 'ssr: true,',\n 'buildEnd: async',\n 'await sentryOnBuildEnd({',\n ]);\n });\n\n test('builds successfully', async () => {\n await checkIfBuilds(projectDir);\n }, 60000); // 1 minute timeout\n\n test('runs on dev mode correctly', async () => {\n await checkIfRunsOnDevMode(projectDir, 'to expose');\n }, 30000); // 30 second timeout\n\n test('runs on prod mode correctly', async () => {\n await checkIfRunsOnProdMode(projectDir, 'react-router-serve');\n }, 30000); // 30 second timeout\n}\n\ndescribe('React Router', () => {\n describe('with empty project', () => {\n const integration = Integration.reactRouter;\n const projectDir = path.resolve(\n __dirname,\n '../test-applications/react-router-test-app',\n );\n\n beforeAll(async () => {\n await runWizardOnReactRouterProject(projectDir, integration);\n });\n\n afterAll(() => {\n revertLocalChanges(projectDir);\n cleanupGit(projectDir);\n });\n\n checkReactRouterProject(projectDir, integration);\n });\n\n describe('edge cases', () => {\n const baseProjectDir = path.resolve(\n __dirname,\n '../test-applications/react-router-test-app',\n );\n\n describe('existing Sentry setup', () => {\n const integration = Integration.reactRouter;\n const projectDir = path.resolve(\n __dirname,\n '../test-applications/react-router-test-app-existing',\n );\n\n beforeAll(async () => {\n // Copy project and add existing Sentry setup\n fs.cpSync(baseProjectDir, projectDir, { recursive: true });\n\n const clientEntryPath = path.join(projectDir, 'app', 'entry.client.tsx');\n const existingContent = `import * as Sentry from \"@sentry/react-router\";\nimport { startTransition, StrictMode } from \"react\";\nimport { hydrateRoot } from \"react-dom/client\";\nimport { HydratedRouter } from \"react-router/dom\";\n\nSentry.init({\n dsn: \"https://existing@dsn.ingest.sentry.io/1337\",\n tracesSampleRate: 1.0,\n});\n\nstartTransition(() => {\n hydrateRoot(\n document,\n <StrictMode>\n <HydratedRouter />\n </StrictMode>\n );\n});`;\n fs.writeFileSync(clientEntryPath, existingContent);\n\n await runWizardOnReactRouterProject(projectDir, integration);\n });\n\n afterAll(() => {\n revertLocalChanges(projectDir);\n cleanupGit(projectDir);\n try {\n fs.rmSync(projectDir, { recursive: true, force: true });\n } catch (e) {\n // Ignore cleanup errors\n }\n });\n\n test('wizard handles existing Sentry without duplication', () => {\n const clientContent = fs.readFileSync(`${projectDir}/app/entry.client.tsx`, 'utf8');\n const sentryImportCount = (clientContent.match(/import \\* as Sentry from \"@sentry\\/react-router\"/g) || []).length;\n const sentryInitCount = (clientContent.match(/Sentry\\.init\\(/g) || []).length;\n\n expect(sentryImportCount).toBe(1);\n expect(sentryInitCount).toBe(1);\n });\n\n // Only test the essential checks for this edge case\n test('package.json is updated correctly', () => {\n checkPackageJson(projectDir, integration);\n });\n\n test('essential files exist or wizard completes gracefully', () => {\n // Check if key directories exist\n expect(fs.existsSync(`${projectDir}/app`)).toBe(true);\n\n // When there's existing Sentry setup, the wizard may skip some file creation\n // to avoid conflicts. This is acceptable behavior.\n // Let's check if the wizard at least completed by verifying package.json was updated\n const packageJsonPath = `${projectDir}/package.json`;\n expect(fs.existsSync(packageJsonPath)).toBe(true);\n\n const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');\n const packageJson = JSON.parse(packageJsonContent) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n\n const hasSentryPackage =\n (packageJson.dependencies?.['@sentry/react-router']) ||\n (packageJson.devDependencies?.['@sentry/react-router']);\n\n // The wizard should have at least installed the Sentry package\n expect(hasSentryPackage).toBeTruthy();\n });\n });\n\n describe('missing entry files', () => {\n const integration = Integration.reactRouter;\n const projectDir = path.resolve(\n __dirname,\n '../test-applications/react-router-test-app-missing-entries',\n );\n\n beforeAll(async () => {\n // Copy project and remove entry files\n fs.cpSync(baseProjectDir, projectDir, { recursive: true });\n\n const entryClientPath = path.join(projectDir, 'app', 'entry.client.tsx');\n const entryServerPath = path.join(projectDir, 'app', 'entry.server.tsx');\n\n if (fs.existsSync(entryClientPath)) fs.unlinkSync(entryClientPath);\n if (fs.existsSync(entryServerPath)) fs.unlinkSync(entryServerPath);\n\n await runWizardOnReactRouterProject(projectDir, integration);\n });\n\n afterAll(() => {\n revertLocalChanges(projectDir);\n cleanupGit(projectDir);\n try {\n fs.rmSync(projectDir, { recursive: true, force: true });\n } catch (e) {\n // Ignore cleanup errors\n }\n });\n\n test('wizard creates missing entry files', () => {\n checkFileExists(`${projectDir}/app/entry.client.tsx`);\n checkFileExists(`${projectDir}/app/entry.server.tsx`);\n });\n\n test('basic configuration still works', () => {\n checkPackageJson(projectDir, integration);\n checkFileExists(`${projectDir}/instrument.server.mjs`);\n });\n });\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"react-router.test.js","sourceRoot":"","sources":["../../../e2e-tests/tests/react-router.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAkC;AAClC,4CAA8B;AAC9B,mDAAkD;AAClD,oCAWkB;AAClB,mCAAqE;AAErE,uCAAuC;AACvC,mCAAuC;AAEvC,KAAK,UAAU,6BAA6B,CAC1C,UAAkB,EAClB,IAEC;IAED,MAAM,EAAE,aAAa,GAAG,KAAK,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC;IAE7C,MAAM,iBAAiB,GAAG,IAAA,gBAAO,EAAC;QAChC,GAAG,EAAE,UAAU;KAChB,CAAC,CAAC,iBAAiB,EAAE,CAAC;IAEvB,IAAI,aAAa,EAAE;QACjB,iBAAiB;aACd,SAAS,CAAC,iCAAiC,CAAC;aAC5C,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC,CAAC;KAC5B;IAED,iBAAiB;SACd,SAAS,CAAC,qCAAqC,CAAC;SAChD,WAAW,CAAC,aAAI,CAAC,IAAI,EAAE,aAAI,CAAC,KAAK,CAAC;SAClC,YAAY,CAAC,iCAAiC,CAAC;SAC/C,YAAY,CAAC,gCAAgC,EAAE;QAC9C,OAAO,EAAE,MAAO;KACjB,CAAC;SAED,SAAS,CAAC,+BAA+B,CAAC;SAC1C,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC;SACvB,SAAS,CAAC,sCAAsC,CAAC;SACjD,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC;SACvB,SAAS,CAAC,4BAA4B,CAAC;SACvC,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC;SACvB,SAAS,CAAC,iCAAiC,CAAC;SAC5C,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC;SACvB,YAAY,CAAC,mCAAmC,CAAC;SACjD,YAAY,CAAC,kCAAkC,EAAE;QAChD,OAAO,EAAE,MAAO;KACjB,CAAC;SACD,SAAS,CAAC,uCAAuC,CAAC;SAClD,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC,CAAC;IAE3B,IAAI,aAAa,EAAE;QACjB,iBAAiB;aACd,SAAS,CAAC,uDAAuD,CAAC;aAClE,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC;aACvB,SAAS,CAAC,kCAAkC,CAAC;aAC7C,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC,CAAC;KAC5B;IAED,OAAO,iBAAiB;SACrB,SAAS,CACR,8EAA8E,CAC/E;SACA,WAAW,CAAC,aAAI,CAAC,IAAI,EAAE,aAAI,CAAC,KAAK,CAAC;SAClC,YAAY,CAAC,qDAAqD,CAAC;SACnE,GAAG,CAAC,IAAA,wBAAgB,EAAC,uBAAW,CAAC,WAAW,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,IAAA,iBAAQ,EAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,IAAA,iBAAQ,EAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,IAAI,cAAsB,CAAC;QAC3B,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,IAAA,6BAAqB,EACnD,uBAAuB,CACxB,CAAC;QAEF,IAAA,kBAAS,EAAC,KAAK,IAAI,EAAE;YACnB,cAAc,GAAG,MAAM,6BAA6B,CAAC,UAAU,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QAEH,IAAA,iBAAQ,EAAC,GAAG,EAAE;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,wBAAwB,EAAE,GAAG,EAAE;YAClC,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,mCAAmC,EAAE,GAAG,EAAE;YAC7C,IAAA,wBAAgB,EAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,iEAAiE,EAAE,GAAG,EAAE;YAC3E,IAAA,2BAAmB,EAAC,UAAU,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,qBAAqB,EAAE,GAAG,EAAE;YAC/B,IAAA,uBAAe,EAAC,GAAG,UAAU,qCAAqC,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,0BAA0B,EAAE,GAAG,EAAE;YACpC,IAAA,uBAAe,EAAC,GAAG,UAAU,uCAAuC,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,+CAA+C,EAAE,GAAG,EAAE;YACzD,IAAA,yBAAiB,EAAC,GAAG,UAAU,gBAAgB,EAAE;gBAC/C,iEAAiE;gBACjE,sEAAsE;aACvE,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,+BAA+B,EAAE,GAAG,EAAE;YACzC,IAAA,uBAAe,EAAC,GAAG,UAAU,wBAAwB,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,kDAAkD,EAAE,GAAG,EAAE;YAC5D,IAAA,yBAAiB,EAAC,GAAG,UAAU,uBAAuB,EAAE;gBACtD,yBAAyB;gBACzB,sBAAsB;gBACtB;UACE,iBAAS,CAAC,WAAW,IAAI;gBAC3B,oFAAoF;gBACpF,mBAAmB;gBACnB,wBAAwB;aACzB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,4CAA4C,EAAE,GAAG,EAAE;YACtD,IAAA,yBAAiB,EAAC,GAAG,UAAU,eAAe,EAAE;gBAC9C,uGAAuG;gBACvG,2EAA2E;aAC5E,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,mDAAmD,EAAE,GAAG,EAAE;YAC7D,IAAA,yBAAiB,EAAC,GAAG,UAAU,uBAAuB,EAAE;gBACtD,yBAAyB;gBACzB,sBAAsB;gBACtB,4DAA4D;gBAC5D,+DAA+D;aAChE,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,uDAAuD,EAAE,GAAG,EAAE;YACjE,IAAA,yBAAiB,EAAC,GAAG,UAAU,wBAAwB,EAAE;gBACvD,iDAAiD;gBACjD;UACE,iBAAS,CAAC,WAAW,IAAI;gBAC3B,mBAAmB;aACpB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,yCAAyC,EAAE,GAAG,EAAE;YACnD,IAAA,yBAAiB,EAAC,GAAG,UAAU,eAAe,EAAE;gBAC9C,yBAAyB;gBACzB,sBAAsB;gBACtB,+BAA+B;gBAC/B,gCAAgC;aACjC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,oDAAoD,EAAE,GAAG,EAAE;YAC9D,IAAA,yBAAiB,EAAC,GAAG,UAAU,iBAAiB,EAAE;gBAChD,mCAAmC;gBACnC,sBAAsB;gBACtB,oBAAoB;gBACpB,0CAA0C;aAC3C,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,uEAAuE,EAAE,GAAG,EAAE;YACjF,IAAA,yBAAiB,EAAC,GAAG,UAAU,yBAAyB,EAAE;gBACxD,kCAAkC;gBAClC,sBAAsB;gBACtB,YAAY;gBACZ,iBAAiB;gBACjB,0BAA0B;aAC3B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,qBAAqB,EAAE,KAAK,IAAI,EAAE;YACrC,MAAM,IAAA,qBAAa,EAAC,UAAU,CAAC,CAAC;QAClC,CAAC,EAAE,KAAM,CAAC,CAAC,CAAC,mBAAmB;QAE/B,IAAA,aAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC5C,MAAM,IAAA,4BAAoB,EAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QACtD,CAAC,EAAE,KAAM,CAAC,CAAC,CAAC,oBAAoB;QAEhC,IAAA,aAAI,EAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC7C,MAAM,IAAA,6BAAqB,EAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC;QAChE,CAAC,EAAE,KAAM,CAAC,CAAC,CAAC,oBAAoB;IAClC,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,IAAA,iBAAQ,EAAC,uBAAuB,EAAE,GAAG,EAAE;YACrC,IAAI,cAAsB,CAAC;YAE3B,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,IAAA,6BAAqB,EACnD,uBAAuB,CACxB,CAAC;YAEF,IAAA,kBAAS,EAAC,KAAK,IAAI,EAAE;gBACnB,qDAAqD;gBACrD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAC/B,UAAU,EACV,KAAK,EACL,kBAAkB,CACnB,CAAC;gBACF,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;IAiB5B,CAAC;gBACG,EAAE,CAAC,aAAa,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;gBAEnD,cAAc,GAAG,MAAM,6BAA6B,CAAC,UAAU,EAAE;oBAC/D,aAAa,EAAE,IAAI;iBACpB,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,IAAA,iBAAQ,EAAC,GAAG,EAAE;gBACZ,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;YAEH,IAAA,aAAI,EAAC,wBAAwB,EAAE,GAAG,EAAE;gBAClC,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjC,CAAC,CAAC,CAAC;YAEH,IAAA,aAAI,EAAC,oDAAoD,EAAE,GAAG,EAAE;gBAC9D,MAAM,aAAa,GAAG,EAAE,CAAC,YAAY,CACnC,GAAG,UAAU,uBAAuB,EACpC,MAAM,CACP,CAAC;gBACF,MAAM,iBAAiB,GAAG,CACxB,aAAa,CAAC,KAAK,CACjB,mDAAmD,CACpD,IAAI,EAAE,CACR,CAAC,MAAM,CAAC;gBACT,MAAM,eAAe,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC;qBACnE,MAAM,CAAC;gBAEV,IAAA,eAAM,EAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClC,IAAA,eAAM,EAAC,eAAe,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClC,CAAC,CAAC,CAAC;YAEH,oDAAoD;YACpD,IAAA,aAAI,EAAC,mCAAmC,EAAE,GAAG,EAAE;gBAC7C,IAAA,wBAAgB,EAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;YACvD,CAAC,CAAC,CAAC;YAEH,IAAA,aAAI,EAAC,sDAAsD,EAAE,GAAG,EAAE;gBAChE,iCAAiC;gBACjC,IAAA,eAAM,EAAC,EAAE,CAAC,UAAU,CAAC,GAAG,UAAU,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAEtD,6EAA6E;gBAC7E,mDAAmD;gBACnD,qFAAqF;gBACrF,MAAM,eAAe,GAAG,GAAG,UAAU,eAAe,CAAC;gBACrD,IAAA,eAAM,EAAC,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAElD,MAAM,kBAAkB,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;gBACpE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAGhD,CAAC;gBAEF,MAAM,gBAAgB,GACpB,WAAW,CAAC,YAAY,EAAE,CAAC,sBAAsB,CAAC;oBAClD,WAAW,CAAC,eAAe,EAAE,CAAC,sBAAsB,CAAC,CAAC;gBAExD,+DAA+D;gBAC/D,IAAA,eAAM,EAAC,gBAAgB,CAAC,CAAC,UAAU,EAAE,CAAC;YACxC,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,iBAAQ,EAAC,qBAAqB,EAAE,GAAG,EAAE;YACnC,IAAI,cAAsB,CAAC;YAE3B,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,IAAA,6BAAqB,EACnD,uBAAuB,CACxB,CAAC;YAEF,IAAA,kBAAS,EAAC,KAAK,IAAI,EAAE;gBACnB,sCAAsC;gBAEtC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAC/B,UAAU,EACV,KAAK,EACL,kBAAkB,CACnB,CAAC;gBACF,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAC/B,UAAU,EACV,KAAK,EACL,kBAAkB,CACnB,CAAC;gBAEF,IAAI,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC;oBAAE,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;gBACnE,IAAI,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC;oBAAE,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;gBAEnE,cAAc,GAAG,MAAM,6BAA6B,CAAC,UAAU,CAAC,CAAC;YACnE,CAAC,CAAC,CAAC;YAEH,IAAA,iBAAQ,EAAC,GAAG,EAAE;gBACZ,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;YAEH,IAAA,aAAI,EAAC,wBAAwB,EAAE,GAAG,EAAE;gBAClC,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjC,CAAC,CAAC,CAAC;YAEH,IAAA,aAAI,EAAC,oCAAoC,EAAE,GAAG,EAAE;gBAC9C,IAAA,uBAAe,EAAC,GAAG,UAAU,uBAAuB,CAAC,CAAC;gBACtD,IAAA,uBAAe,EAAC,GAAG,UAAU,uBAAuB,CAAC,CAAC;YACxD,CAAC,CAAC,CAAC;YAEH,IAAA,aAAI,EAAC,iCAAiC,EAAE,GAAG,EAAE;gBAC3C,IAAA,wBAAgB,EAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;gBACrD,IAAA,uBAAe,EAAC,GAAG,UAAU,wBAAwB,CAAC,CAAC;YACzD,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import * as path from 'node:path';\nimport * as fs from 'node:fs';\nimport { Integration } from '../../lib/Constants';\nimport {\n TEST_ARGS,\n checkEnvBuildPlugin,\n checkFileContents,\n checkFileExists,\n checkIfBuilds,\n checkIfRunsOnDevMode,\n checkIfRunsOnProdMode,\n checkPackageJson,\n createIsolatedTestEnv,\n getWizardCommand,\n} from '../utils';\nimport { afterAll, beforeAll, describe, test, expect } from 'vitest';\n\n//@ts-expect-error - clifty is ESM only\nimport { KEYS, withEnv } from 'clifty';\n\nasync function runWizardOnReactRouterProject(\n projectDir: string,\n opts?: {\n modifiedFiles?: boolean;\n },\n): Promise<number> {\n const { modifiedFiles = false } = opts || {};\n\n const wizardInteraction = withEnv({\n cwd: projectDir,\n }).defineInteraction();\n\n if (modifiedFiles) {\n wizardInteraction\n .whenAsked('Do you want to continue anyway?')\n .respondWith(KEYS.ENTER);\n }\n\n wizardInteraction\n .whenAsked('Please select your package manager.')\n .respondWith(KEYS.DOWN, KEYS.ENTER)\n .expectOutput('Installing @sentry/react-router')\n .expectOutput('Installed @sentry/react-router', {\n timeout: 240_000,\n })\n\n .whenAsked('Do you want to enable Tracing')\n .respondWith(KEYS.ENTER)\n .whenAsked('Do you want to enable Session Replay')\n .respondWith(KEYS.ENTER)\n .whenAsked('Do you want to enable Logs')\n .respondWith(KEYS.ENTER)\n .whenAsked('Do you want to enable Profiling')\n .respondWith(KEYS.ENTER)\n .expectOutput('Installing @sentry/profiling-node')\n .expectOutput('Installed @sentry/profiling-node', {\n timeout: 240_000,\n })\n .whenAsked('Do you want to create an example page')\n .respondWith(KEYS.ENTER);\n\n if (modifiedFiles) {\n wizardInteraction\n .whenAsked('Would you like to try running npx react-router reveal')\n .respondWith(KEYS.ENTER)\n .whenAsked('Did you apply the snippet above?')\n .respondWith(KEYS.ENTER);\n }\n\n return wizardInteraction\n .whenAsked(\n 'Optionally add a project-scoped MCP server configuration for the Sentry MCP?',\n )\n .respondWith(KEYS.DOWN, KEYS.ENTER)\n .expectOutput('Successfully installed the Sentry React Router SDK!')\n .run(getWizardCommand(Integration.reactRouter));\n}\n\ndescribe('React Router', () => {\n describe('with empty project', () => {\n let wizardExitCode: number;\n const { projectDir, cleanup } = createIsolatedTestEnv(\n 'react-router-test-app',\n );\n\n beforeAll(async () => {\n wizardExitCode = await runWizardOnReactRouterProject(projectDir);\n });\n\n afterAll(() => {\n cleanup();\n });\n\n test('exits with exit code 0', () => {\n expect(wizardExitCode).toBe(0);\n });\n\n test('package.json is updated correctly', () => {\n checkPackageJson(projectDir, '@sentry/react-router');\n });\n\n test('.env.sentry-build-plugin is created and contains the auth token', () => {\n checkEnvBuildPlugin(projectDir);\n });\n\n test('example page exists', () => {\n checkFileExists(`${projectDir}/app/routes/sentry-example-page.tsx`);\n });\n\n test('example API route exists', () => {\n checkFileExists(`${projectDir}/app/routes/api.sentry-example-api.ts`);\n });\n\n test('example page is added to routes configuration', () => {\n checkFileContents(`${projectDir}/app/routes.ts`, [\n 'route(\"/sentry-example-page\", \"routes/sentry-example-page.tsx\")',\n 'route(\"/api/sentry-example-api\", \"routes/api.sentry-example-api.ts\")',\n ]);\n });\n\n test('instrument.server file exists', () => {\n checkFileExists(`${projectDir}/instrument.server.mjs`);\n });\n\n test('entry.client file contains Sentry initialization', () => {\n checkFileContents(`${projectDir}/app/entry.client.tsx`, [\n 'import * as Sentry from',\n '@sentry/react-router',\n `Sentry.init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",`,\n 'integrations: [Sentry.reactRouterTracingIntegration(), Sentry.replayIntegration()]',\n 'enableLogs: true,',\n 'tracesSampleRate: 1.0,',\n ]);\n });\n\n test('package.json scripts are updated correctly', () => {\n checkFileContents(`${projectDir}/package.json`, [\n `\"start\": \"NODE_OPTIONS='--import ./instrument.server.mjs' react-router-serve ./build/server/index.js\"`,\n `\"dev\": \"NODE_OPTIONS='--import ./instrument.server.mjs' react-router dev\"`,\n ]);\n });\n\n test('entry.server file contains Sentry instrumentation', () => {\n checkFileContents(`${projectDir}/app/entry.server.tsx`, [\n 'import * as Sentry from',\n '@sentry/react-router',\n 'export const handleError = Sentry.createSentryHandleError(',\n 'export default Sentry.wrapSentryHandleRequest(handleRequest);',\n ]);\n });\n\n test('instrument.server file contains Sentry initialization', () => {\n checkFileContents(`${projectDir}/instrument.server.mjs`, [\n \"import * as Sentry from '@sentry/react-router';\",\n `Sentry.init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",`,\n 'enableLogs: true,',\n ]);\n });\n\n test('root file contains Sentry ErrorBoundary', () => {\n checkFileContents(`${projectDir}/app/root.tsx`, [\n 'import * as Sentry from',\n '@sentry/react-router',\n 'export function ErrorBoundary',\n 'Sentry.captureException(error)',\n ]);\n });\n\n test('vite.config file contains sentryReactRouter plugin', () => {\n checkFileContents(`${projectDir}/vite.config.ts`, [\n 'import { sentryReactRouter } from',\n '@sentry/react-router',\n 'sentryReactRouter(',\n 'authToken: process.env.SENTRY_AUTH_TOKEN',\n ]);\n });\n\n test('react-router.config file contains buildEnd hook with sentryOnBuildEnd', () => {\n checkFileContents(`${projectDir}/react-router.config.ts`, [\n 'import { sentryOnBuildEnd } from',\n '@sentry/react-router',\n 'ssr: true,',\n 'buildEnd: async',\n 'await sentryOnBuildEnd({',\n ]);\n });\n\n test('builds successfully', async () => {\n await checkIfBuilds(projectDir);\n }, 60_000); // 1 minute timeout\n\n test('runs on dev mode correctly', async () => {\n await checkIfRunsOnDevMode(projectDir, 'to expose');\n }, 30_000); // 30 second timeout\n\n test('runs on prod mode correctly', async () => {\n await checkIfRunsOnProdMode(projectDir, 'react-router-serve');\n }, 30_000); // 30 second timeout\n });\n\n describe('edge cases', () => {\n describe('existing Sentry setup', () => {\n let wizardExitCode: number;\n\n const { projectDir, cleanup } = createIsolatedTestEnv(\n 'react-router-test-app',\n );\n\n beforeAll(async () => {\n // Add existing Sentry setup to the isolated test app\n const clientEntryPath = path.join(\n projectDir,\n 'app',\n 'entry.client.tsx',\n );\n const existingContent = `import * as Sentry from \"@sentry/react-router\";\nimport { startTransition, StrictMode } from \"react\";\nimport { hydrateRoot } from \"react-dom/client\";\nimport { HydratedRouter } from \"react-router/dom\";\n\nSentry.init({\n dsn: \"https://existing@dsn.ingest.sentry.io/1337\",\n tracesSampleRate: 1.0,\n});\n\nstartTransition(() => {\n hydrateRoot(\n document,\n <StrictMode>\n <HydratedRouter />\n </StrictMode>\n );\n});`;\n fs.writeFileSync(clientEntryPath, existingContent);\n\n wizardExitCode = await runWizardOnReactRouterProject(projectDir, {\n modifiedFiles: true,\n });\n });\n\n afterAll(() => {\n cleanup();\n });\n\n test('exits with exit code 0', () => {\n expect(wizardExitCode).toBe(0);\n });\n\n test('wizard handles existing Sentry without duplication', () => {\n const clientContent = fs.readFileSync(\n `${projectDir}/app/entry.client.tsx`,\n 'utf8',\n );\n const sentryImportCount = (\n clientContent.match(\n /import \\* as Sentry from \"@sentry\\/react-router\"/g,\n ) || []\n ).length;\n const sentryInitCount = (clientContent.match(/Sentry\\.init\\(/g) || [])\n .length;\n\n expect(sentryImportCount).toBe(1);\n expect(sentryInitCount).toBe(1);\n });\n\n // Only test the essential checks for this edge case\n test('package.json is updated correctly', () => {\n checkPackageJson(projectDir, '@sentry/react-router');\n });\n\n test('essential files exist or wizard completes gracefully', () => {\n // Check if key directories exist\n expect(fs.existsSync(`${projectDir}/app`)).toBe(true);\n\n // When there's existing Sentry setup, the wizard may skip some file creation\n // to avoid conflicts. This is acceptable behavior.\n // Let's check if the wizard at least completed by verifying package.json was updated\n const packageJsonPath = `${projectDir}/package.json`;\n expect(fs.existsSync(packageJsonPath)).toBe(true);\n\n const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');\n const packageJson = JSON.parse(packageJsonContent) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n\n const hasSentryPackage =\n packageJson.dependencies?.['@sentry/react-router'] ||\n packageJson.devDependencies?.['@sentry/react-router'];\n\n // The wizard should have at least installed the Sentry package\n expect(hasSentryPackage).toBeTruthy();\n });\n });\n\n describe('missing entry files', () => {\n let wizardExitCode: number;\n\n const { projectDir, cleanup } = createIsolatedTestEnv(\n 'react-router-test-app',\n );\n\n beforeAll(async () => {\n // Copy project and remove entry files\n\n const entryClientPath = path.join(\n projectDir,\n 'app',\n 'entry.client.tsx',\n );\n const entryServerPath = path.join(\n projectDir,\n 'app',\n 'entry.server.tsx',\n );\n\n if (fs.existsSync(entryClientPath)) fs.unlinkSync(entryClientPath);\n if (fs.existsSync(entryServerPath)) fs.unlinkSync(entryServerPath);\n\n wizardExitCode = await runWizardOnReactRouterProject(projectDir);\n });\n\n afterAll(() => {\n cleanup();\n });\n\n test('exits with exit code 0', () => {\n expect(wizardExitCode).toBe(0);\n });\n\n test('wizard creates missing entry files', () => {\n checkFileExists(`${projectDir}/app/entry.client.tsx`);\n checkFileExists(`${projectDir}/app/entry.server.tsx`);\n });\n\n test('basic configuration still works', () => {\n checkPackageJson(projectDir, '@sentry/react-router');\n checkFileExists(`${projectDir}/instrument.server.mjs`);\n });\n });\n });\n});\n"]}
|