@sentry/wizard 6.10.0 → 6.12.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.
Files changed (203) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/ci-ensure-runtime-loaded.sh +82 -0
  3. package/dist/e2e-tests/tests/angular-17.test.js +72 -82
  4. package/dist/e2e-tests/tests/angular-17.test.js.map +1 -1
  5. package/dist/e2e-tests/tests/angular-19.test.js +71 -80
  6. package/dist/e2e-tests/tests/angular-19.test.js.map +1 -1
  7. package/dist/e2e-tests/tests/cloudflare-worker.test.d.ts +1 -0
  8. package/dist/e2e-tests/tests/cloudflare-worker.test.js +69 -0
  9. package/dist/e2e-tests/tests/cloudflare-worker.test.js.map +1 -0
  10. package/dist/e2e-tests/tests/cloudflare-wrangler-sourcemaps.test.js +2 -5
  11. package/dist/e2e-tests/tests/cloudflare-wrangler-sourcemaps.test.js.map +1 -1
  12. package/dist/e2e-tests/tests/expo.test.js +36 -61
  13. package/dist/e2e-tests/tests/expo.test.js.map +1 -1
  14. package/dist/e2e-tests/tests/flutter.test.js +63 -70
  15. package/dist/e2e-tests/tests/flutter.test.js.map +1 -1
  16. package/dist/e2e-tests/tests/help-message.test.js +2 -2
  17. package/dist/e2e-tests/tests/help-message.test.js.map +1 -1
  18. package/dist/e2e-tests/tests/nextjs-14.test.js +48 -76
  19. package/dist/e2e-tests/tests/nextjs-14.test.js.map +1 -1
  20. package/dist/e2e-tests/tests/nextjs-15.test.js +89 -99
  21. package/dist/e2e-tests/tests/nextjs-15.test.js.map +1 -1
  22. package/dist/e2e-tests/tests/nextjs-16.test.js +48 -45
  23. package/dist/e2e-tests/tests/nextjs-16.test.js.map +1 -1
  24. package/dist/e2e-tests/tests/nuxt-3.test.js +45 -58
  25. package/dist/e2e-tests/tests/nuxt-3.test.js.map +1 -1
  26. package/dist/e2e-tests/tests/nuxt-4.test.js +59 -73
  27. package/dist/e2e-tests/tests/nuxt-4.test.js.map +1 -1
  28. package/dist/e2e-tests/tests/pnpm-workspace.test.js +6 -8
  29. package/dist/e2e-tests/tests/pnpm-workspace.test.js.map +1 -1
  30. package/dist/e2e-tests/tests/react-native.test.js +44 -80
  31. package/dist/e2e-tests/tests/react-native.test.js.map +1 -1
  32. package/dist/e2e-tests/tests/react-router-instrumentation-api.test.d.ts +1 -0
  33. package/dist/e2e-tests/tests/react-router-instrumentation-api.test.js +96 -0
  34. package/dist/e2e-tests/tests/react-router-instrumentation-api.test.js.map +1 -0
  35. package/dist/e2e-tests/tests/react-router.test.js +165 -145
  36. package/dist/e2e-tests/tests/react-router.test.js.map +1 -1
  37. package/dist/e2e-tests/tests/remix.test.js +162 -132
  38. package/dist/e2e-tests/tests/remix.test.js.map +1 -1
  39. package/dist/e2e-tests/tests/sveltekit-hooks.test.js +48 -36
  40. package/dist/e2e-tests/tests/sveltekit-hooks.test.js.map +1 -1
  41. package/dist/e2e-tests/tests/sveltekit-tracing.test.js +5 -7
  42. package/dist/e2e-tests/tests/sveltekit-tracing.test.js.map +1 -1
  43. package/dist/e2e-tests/utils/index.d.ts +15 -43
  44. package/dist/e2e-tests/utils/index.js +95 -185
  45. package/dist/e2e-tests/utils/index.js.map +1 -1
  46. package/dist/get-e2e-test-matrix.mjs +11 -0
  47. package/dist/lib/Constants.d.ts +1 -0
  48. package/dist/lib/Constants.js +5 -0
  49. package/dist/lib/Constants.js.map +1 -1
  50. package/dist/src/android/android-wizard.js +2 -4
  51. package/dist/src/android/android-wizard.js.map +1 -1
  52. package/dist/src/angular/angular-wizard.js +4 -6
  53. package/dist/src/angular/angular-wizard.js.map +1 -1
  54. package/dist/src/angular/sdk-setup.js +1 -1
  55. package/dist/src/angular/sdk-setup.js.map +1 -1
  56. package/dist/src/apple/apple-wizard.js +2 -4
  57. package/dist/src/apple/apple-wizard.js.map +1 -1
  58. package/dist/src/apple/code-tools.js +17 -3
  59. package/dist/src/apple/code-tools.js.map +1 -1
  60. package/dist/src/apple/configure-package-manager.js +18 -5
  61. package/dist/src/apple/configure-package-manager.js.map +1 -1
  62. package/dist/src/cloudflare/cloudflare-wizard.d.ts +3 -0
  63. package/dist/src/cloudflare/cloudflare-wizard.js +104 -0
  64. package/dist/src/cloudflare/cloudflare-wizard.js.map +1 -0
  65. package/dist/src/cloudflare/sdk-setup.d.ts +8 -0
  66. package/dist/src/cloudflare/sdk-setup.js +47 -0
  67. package/dist/src/cloudflare/sdk-setup.js.map +1 -0
  68. package/dist/src/cloudflare/templates.d.ts +5 -0
  69. package/dist/src/cloudflare/templates.js +50 -0
  70. package/dist/src/cloudflare/templates.js.map +1 -0
  71. package/dist/src/cloudflare/wrangler/create-wrangler-config.d.ts +4 -0
  72. package/dist/src/cloudflare/wrangler/create-wrangler-config.js +27 -0
  73. package/dist/src/cloudflare/wrangler/create-wrangler-config.js.map +1 -0
  74. package/dist/src/cloudflare/wrangler/ensure-wrangler-config.d.ts +4 -0
  75. package/dist/src/cloudflare/wrangler/ensure-wrangler-config.js +25 -0
  76. package/dist/src/cloudflare/wrangler/ensure-wrangler-config.js.map +1 -0
  77. package/dist/src/cloudflare/wrangler/find-wrangler-config.d.ts +4 -0
  78. package/dist/src/cloudflare/wrangler/find-wrangler-config.js +23 -0
  79. package/dist/src/cloudflare/wrangler/find-wrangler-config.js.map +1 -0
  80. package/dist/src/cloudflare/wrangler/get-entry-point-from-wrangler-config.d.ts +6 -0
  81. package/dist/src/cloudflare/wrangler/get-entry-point-from-wrangler-config.js +52 -0
  82. package/dist/src/cloudflare/wrangler/get-entry-point-from-wrangler-config.js.map +1 -0
  83. package/dist/src/cloudflare/wrangler/update-wrangler-config.d.ts +17 -0
  84. package/dist/src/cloudflare/wrangler/update-wrangler-config.js +173 -0
  85. package/dist/src/cloudflare/wrangler/update-wrangler-config.js.map +1 -0
  86. package/dist/src/cloudflare/wrap-worker.d.ts +33 -0
  87. package/dist/src/cloudflare/wrap-worker.js +116 -0
  88. package/dist/src/cloudflare/wrap-worker.js.map +1 -0
  89. package/dist/src/flutter/flutter-wizard.js +3 -6
  90. package/dist/src/flutter/flutter-wizard.js.map +1 -1
  91. package/dist/src/nextjs/nextjs-wizard.js +0 -2
  92. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  93. package/dist/src/nuxt/nuxt-wizard.js +3 -5
  94. package/dist/src/nuxt/nuxt-wizard.js.map +1 -1
  95. package/dist/src/react-native/expo.d.ts +6 -0
  96. package/dist/src/react-native/expo.js +27 -1
  97. package/dist/src/react-native/expo.js.map +1 -1
  98. package/dist/src/react-native/git.d.ts +5 -0
  99. package/dist/src/react-native/git.js +32 -1
  100. package/dist/src/react-native/git.js.map +1 -1
  101. package/dist/src/react-native/javascript.js +3 -1
  102. package/dist/src/react-native/javascript.js.map +1 -1
  103. package/dist/src/react-native/react-native-wizard.js +14 -10
  104. package/dist/src/react-native/react-native-wizard.js.map +1 -1
  105. package/dist/src/react-router/codemods/client.entry.d.ts +1 -1
  106. package/dist/src/react-router/codemods/client.entry.js +69 -12
  107. package/dist/src/react-router/codemods/client.entry.js.map +1 -1
  108. package/dist/src/react-router/codemods/react-router-config.js +1 -1
  109. package/dist/src/react-router/codemods/react-router-config.js.map +1 -1
  110. package/dist/src/react-router/codemods/root.js +1 -2
  111. package/dist/src/react-router/codemods/root.js.map +1 -1
  112. package/dist/src/react-router/codemods/server-entry.d.ts +1 -1
  113. package/dist/src/react-router/codemods/server-entry.js +39 -4
  114. package/dist/src/react-router/codemods/server-entry.js.map +1 -1
  115. package/dist/src/react-router/codemods/vite.js +46 -1
  116. package/dist/src/react-router/codemods/vite.js.map +1 -1
  117. package/dist/src/react-router/react-router-wizard.js +55 -10
  118. package/dist/src/react-router/react-router-wizard.js.map +1 -1
  119. package/dist/src/react-router/sdk-setup.d.ts +5 -3
  120. package/dist/src/react-router/sdk-setup.js +35 -9
  121. package/dist/src/react-router/sdk-setup.js.map +1 -1
  122. package/dist/src/react-router/templates.d.ts +2 -2
  123. package/dist/src/react-router/templates.js +72 -2
  124. package/dist/src/react-router/templates.js.map +1 -1
  125. package/dist/src/remix/remix-wizard.js +2 -4
  126. package/dist/src/remix/remix-wizard.js.map +1 -1
  127. package/dist/src/run.d.ts +1 -1
  128. package/dist/src/run.js +5 -0
  129. package/dist/src/run.js.map +1 -1
  130. package/dist/src/sourcemaps/tools/vite.js +1 -1
  131. package/dist/src/sourcemaps/tools/vite.js.map +1 -1
  132. package/dist/src/sveltekit/sdk-setup/vite.js +1 -1
  133. package/dist/src/sveltekit/sdk-setup/vite.js.map +1 -1
  134. package/dist/src/sveltekit/sveltekit-wizard.js +2 -4
  135. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  136. package/dist/src/utils/abort-if-sportlight-not-supported.d.ts +5 -0
  137. package/dist/src/utils/abort-if-sportlight-not-supported.js +40 -0
  138. package/dist/src/utils/abort-if-sportlight-not-supported.js.map +1 -0
  139. package/dist/src/utils/ast-utils.d.ts +11 -1
  140. package/dist/src/utils/ast-utils.js +19 -1
  141. package/dist/src/utils/ast-utils.js.map +1 -1
  142. package/dist/src/utils/clack/index.d.ts +2 -2
  143. package/dist/src/utils/clack/index.js.map +1 -1
  144. package/dist/src/utils/clack/mcp-config.js +117 -59
  145. package/dist/src/utils/clack/mcp-config.js.map +1 -1
  146. package/dist/src/version.d.ts +1 -1
  147. package/dist/src/version.js +1 -1
  148. package/dist/src/version.js.map +1 -1
  149. package/dist/test/angular/angular-wizard.test.js +2 -4
  150. package/dist/test/angular/angular-wizard.test.js.map +1 -1
  151. package/dist/test/apple/code-tools.test.js +78 -0
  152. package/dist/test/apple/code-tools.test.js.map +1 -1
  153. package/dist/test/apple/configure-package-manager.test.d.ts +1 -0
  154. package/dist/test/apple/configure-package-manager.test.js +161 -0
  155. package/dist/test/apple/configure-package-manager.test.js.map +1 -0
  156. package/dist/test/cloudflare/create-wrangler-config.test.d.ts +1 -0
  157. package/dist/test/cloudflare/create-wrangler-config.test.js +48 -0
  158. package/dist/test/cloudflare/create-wrangler-config.test.js.map +1 -0
  159. package/dist/test/cloudflare/ensure-wrangler-config.test.d.ts +1 -0
  160. package/dist/test/cloudflare/ensure-wrangler-config.test.js +61 -0
  161. package/dist/test/cloudflare/ensure-wrangler-config.test.js.map +1 -0
  162. package/dist/test/cloudflare/find-wrangler-config.test.d.ts +1 -0
  163. package/dist/test/cloudflare/find-wrangler-config.test.js +77 -0
  164. package/dist/test/cloudflare/find-wrangler-config.test.js.map +1 -0
  165. package/dist/test/cloudflare/get-entry-point-from-wrangler-config.test.d.ts +1 -0
  166. package/dist/test/cloudflare/get-entry-point-from-wrangler-config.test.js +81 -0
  167. package/dist/test/cloudflare/get-entry-point-from-wrangler-config.test.js.map +1 -0
  168. package/dist/test/cloudflare/sdk-setup.test.d.ts +1 -0
  169. package/dist/test/cloudflare/sdk-setup.test.js +170 -0
  170. package/dist/test/cloudflare/sdk-setup.test.js.map +1 -0
  171. package/dist/test/cloudflare/templates.test.d.ts +1 -0
  172. package/dist/test/cloudflare/templates.test.js +122 -0
  173. package/dist/test/cloudflare/templates.test.js.map +1 -0
  174. package/dist/test/cloudflare/update-wrangler-config.test.d.ts +1 -0
  175. package/dist/test/cloudflare/update-wrangler-config.test.js +216 -0
  176. package/dist/test/cloudflare/update-wrangler-config.test.js.map +1 -0
  177. package/dist/test/cloudflare/wrap-worker.test.d.ts +1 -0
  178. package/dist/test/cloudflare/wrap-worker.test.js +206 -0
  179. package/dist/test/cloudflare/wrap-worker.test.js.map +1 -0
  180. package/dist/test/react-native/expo.test.js +140 -0
  181. package/dist/test/react-native/expo.test.js.map +1 -1
  182. package/dist/test/react-native/git.test.d.ts +1 -0
  183. package/dist/test/react-native/git.test.js +160 -0
  184. package/dist/test/react-native/git.test.js.map +1 -0
  185. package/dist/test/react-router/codemods/client-entry.test.js +29 -0
  186. package/dist/test/react-router/codemods/client-entry.test.js.map +1 -1
  187. package/dist/test/react-router/codemods/root.test.js +4 -0
  188. package/dist/test/react-router/codemods/root.test.js.map +1 -1
  189. package/dist/test/react-router/codemods/server-entry.test.js +70 -0
  190. package/dist/test/react-router/codemods/server-entry.test.js.map +1 -1
  191. package/dist/test/react-router/codemods/vite.test.js +89 -0
  192. package/dist/test/react-router/codemods/vite.test.js.map +1 -1
  193. package/dist/test/react-router/sdk-setup.test.js +64 -8
  194. package/dist/test/react-router/sdk-setup.test.js.map +1 -1
  195. package/dist/test/react-router/templates.test.js +50 -0
  196. package/dist/test/react-router/templates.test.js.map +1 -1
  197. package/dist/test/sourcemaps/tools/vite.test.js +12 -8
  198. package/dist/test/sourcemaps/tools/vite.test.js.map +1 -1
  199. package/dist/test/utils/ast-utils.test.js +22 -0
  200. package/dist/test/utils/ast-utils.test.js.map +1 -1
  201. package/dist/test/utils/clack/mcp-config.test.js +176 -51
  202. package/dist/test/utils/clack/mcp-config.test.js.map +1 -1
  203. package/package.json +6 -5
@@ -28,136 +28,162 @@ 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
- async function runWizardOnReactRouterProject(projectDir, integration) {
32
- const wizardInstance = (0, utils_1.startWizardInstance)(integration, projectDir);
33
- const packageManagerPrompted = await wizardInstance.waitForOutput('Please select your package manager.');
34
- const tracingOptionPrompted = packageManagerPrompted &&
35
- (await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.DOWN, utils_1.KEYS.ENTER], 'to track the performance of your application?', { timeout: 240000 }));
36
- const replayOptionPrompted = tracingOptionPrompted &&
37
- (await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.ENTER], 'to get a video-like reproduction of errors during a user session?'));
38
- const logOptionPrompted = replayOptionPrompted &&
39
- (await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.ENTER], 'to send your application logs to Sentry?'));
40
- const profilingOptionPrompted = logOptionPrompted &&
41
- (await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.ENTER], 'to track application performance in detail?'));
42
- const examplePagePrompted = profilingOptionPrompted &&
43
- (await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.ENTER], 'Do you want to create an example page'));
44
- const mcpPrompted = examplePagePrompted &&
45
- (await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.ENTER], 'Optionally add a project-scoped MCP server configuration for the Sentry MCP?', { optional: true }));
46
- mcpPrompted &&
47
- (await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.DOWN, utils_1.KEYS.ENTER], 'Successfully installed the Sentry React Router SDK!'));
48
- wizardInstance.kill();
49
- }
50
- function checkReactRouterProject(projectDir, integration) {
51
- (0, vitest_1.test)('package.json is updated correctly', () => {
52
- (0, utils_1.checkPackageJson)(projectDir, integration);
53
- });
54
- (0, vitest_1.test)('.env.sentry-build-plugin is created and contains the auth token', () => {
55
- (0, utils_1.checkEnvBuildPlugin)(projectDir);
56
- });
57
- (0, vitest_1.test)('example page exists', () => {
58
- (0, utils_1.checkFileExists)(`${projectDir}/app/routes/sentry-example-page.tsx`);
59
- });
60
- (0, vitest_1.test)('example API route exists', () => {
61
- (0, utils_1.checkFileExists)(`${projectDir}/app/routes/api.sentry-example-api.ts`);
62
- });
63
- (0, vitest_1.test)('example page is added to routes configuration', () => {
64
- (0, utils_1.checkFileContents)(`${projectDir}/app/routes.ts`, [
65
- 'route("/sentry-example-page", "routes/sentry-example-page.tsx")',
66
- 'route("/api/sentry-example-api", "routes/api.sentry-example-api.ts")',
67
- ]);
68
- });
69
- (0, vitest_1.test)('instrument.server file exists', () => {
70
- (0, utils_1.checkFileExists)(`${projectDir}/instrument.server.mjs`);
71
- });
72
- (0, vitest_1.test)('entry.client file contains Sentry initialization', () => {
73
- (0, utils_1.checkFileContents)(`${projectDir}/app/entry.client.tsx`, [
74
- 'import * as Sentry from',
75
- '@sentry/react-router',
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
+ .whenAsked('Do you want to use the Instrumentation API')
59
+ .respondWith(clifty_1.KEYS.DOWN, clifty_1.KEYS.ENTER) // No
60
+ .expectOutput('Installing @sentry/profiling-node')
61
+ .expectOutput('Installed @sentry/profiling-node', {
62
+ timeout: 240000,
63
+ })
64
+ .whenAsked('Do you want to create an example page')
65
+ .respondWith(clifty_1.KEYS.ENTER);
66
+ if (modifiedFiles) {
67
+ wizardInteraction
68
+ .whenAsked('Would you like to try running npx react-router reveal')
69
+ .respondWith(clifty_1.KEYS.ENTER)
70
+ .whenAsked('Did you apply the snippet above?')
71
+ .respondWith(clifty_1.KEYS.ENTER);
72
+ }
73
+ return wizardInteraction
74
+ .whenAsked('Optionally add a project-scoped MCP server configuration for the Sentry MCP?')
75
+ .respondWith(clifty_1.KEYS.DOWN, clifty_1.KEYS.ENTER)
76
+ .expectOutput('Successfully installed the Sentry React Router SDK!')
77
+ .run((0, utils_1.getWizardCommand)(Constants_1.Integration.reactRouter));
139
78
  }
140
79
  (0, vitest_1.describe)('React Router', () => {
141
80
  (0, vitest_1.describe)('with empty project', () => {
142
- const integration = Constants_1.Integration.reactRouter;
143
- const projectDir = path.resolve(__dirname, '../test-applications/react-router-test-app');
81
+ let wizardExitCode;
82
+ const { projectDir, cleanup } = (0, utils_1.createIsolatedTestEnv)('react-router-test-app');
144
83
  (0, vitest_1.beforeAll)(async () => {
145
- await runWizardOnReactRouterProject(projectDir, integration);
84
+ wizardExitCode = await runWizardOnReactRouterProject(projectDir);
146
85
  });
147
86
  (0, vitest_1.afterAll)(() => {
148
- (0, utils_1.revertLocalChanges)(projectDir);
149
- (0, utils_1.cleanupGit)(projectDir);
87
+ cleanup();
150
88
  });
151
- checkReactRouterProject(projectDir, integration);
89
+ (0, vitest_1.test)('exits with exit code 0', () => {
90
+ (0, vitest_1.expect)(wizardExitCode).toBe(0);
91
+ });
92
+ (0, vitest_1.test)('package.json is updated correctly', () => {
93
+ (0, utils_1.checkPackageJson)(projectDir, '@sentry/react-router');
94
+ });
95
+ (0, vitest_1.test)('.env.sentry-build-plugin is created and contains the auth token', () => {
96
+ (0, utils_1.checkEnvBuildPlugin)(projectDir);
97
+ });
98
+ (0, vitest_1.test)('example page exists', () => {
99
+ (0, utils_1.checkFileExists)(`${projectDir}/app/routes/sentry-example-page.tsx`);
100
+ });
101
+ (0, vitest_1.test)('example API route exists', () => {
102
+ (0, utils_1.checkFileExists)(`${projectDir}/app/routes/api.sentry-example-api.ts`);
103
+ });
104
+ (0, vitest_1.test)('example page is added to routes configuration', () => {
105
+ (0, utils_1.checkFileContents)(`${projectDir}/app/routes.ts`, [
106
+ 'route("/sentry-example-page", "routes/sentry-example-page.tsx")',
107
+ 'route("/api/sentry-example-api", "routes/api.sentry-example-api.ts")',
108
+ ]);
109
+ });
110
+ (0, vitest_1.test)('instrument.server file exists', () => {
111
+ (0, utils_1.checkFileExists)(`${projectDir}/instrument.server.mjs`);
112
+ });
113
+ (0, vitest_1.test)('entry.client file contains Sentry initialization', () => {
114
+ (0, utils_1.checkFileContents)(`${projectDir}/app/entry.client.tsx`, [
115
+ 'import * as Sentry from',
116
+ '@sentry/react-router',
117
+ `Sentry.init({
118
+ dsn: "${utils_1.TEST_ARGS.PROJECT_DSN}",`,
119
+ 'integrations: [Sentry.reactRouterTracingIntegration(), Sentry.replayIntegration()]',
120
+ 'enableLogs: true,',
121
+ 'tracesSampleRate: 1.0,',
122
+ ]);
123
+ });
124
+ (0, vitest_1.test)('package.json scripts are updated correctly', () => {
125
+ (0, utils_1.checkFileContents)(`${projectDir}/package.json`, [
126
+ `"start": "NODE_ENV=production NODE_OPTIONS='--import ./instrument.server.mjs' react-router-serve ./build/server/index.js"`,
127
+ `"dev": "NODE_OPTIONS='--import ./instrument.server.mjs' react-router dev"`,
128
+ ]);
129
+ });
130
+ (0, vitest_1.test)('entry.server file contains Sentry instrumentation', () => {
131
+ (0, utils_1.checkFileContents)(`${projectDir}/app/entry.server.tsx`, [
132
+ 'import * as Sentry from',
133
+ '@sentry/react-router',
134
+ 'export const handleError = Sentry.createSentryHandleError(',
135
+ 'export default Sentry.wrapSentryHandleRequest(handleRequest);',
136
+ ]);
137
+ });
138
+ (0, vitest_1.test)('instrument.server file contains Sentry initialization', () => {
139
+ (0, utils_1.checkFileContents)(`${projectDir}/instrument.server.mjs`, [
140
+ "import * as Sentry from '@sentry/react-router';",
141
+ `Sentry.init({
142
+ dsn: "${utils_1.TEST_ARGS.PROJECT_DSN}",`,
143
+ 'enableLogs: true,',
144
+ ]);
145
+ });
146
+ (0, vitest_1.test)('root file contains Sentry ErrorBoundary', () => {
147
+ (0, utils_1.checkFileContents)(`${projectDir}/app/root.tsx`, [
148
+ 'import * as Sentry from',
149
+ '@sentry/react-router',
150
+ 'export function ErrorBoundary',
151
+ 'Sentry.captureException(error)',
152
+ ]);
153
+ });
154
+ (0, vitest_1.test)('vite.config file contains sentryReactRouter plugin', () => {
155
+ (0, utils_1.checkFileContents)(`${projectDir}/vite.config.ts`, [
156
+ 'import { sentryReactRouter } from',
157
+ '@sentry/react-router',
158
+ 'sentryReactRouter(',
159
+ 'authToken: process.env.SENTRY_AUTH_TOKEN',
160
+ ]);
161
+ });
162
+ (0, vitest_1.test)('react-router.config file contains buildEnd hook with sentryOnBuildEnd', () => {
163
+ (0, utils_1.checkFileContents)(`${projectDir}/react-router.config.ts`, [
164
+ 'import { sentryOnBuildEnd } from',
165
+ '@sentry/react-router',
166
+ 'ssr: true,',
167
+ 'buildEnd: async',
168
+ 'await sentryOnBuildEnd({',
169
+ ]);
170
+ });
171
+ (0, vitest_1.test)('builds successfully', async () => {
172
+ await (0, utils_1.checkIfBuilds)(projectDir);
173
+ }, 60000); // 1 minute timeout
174
+ (0, vitest_1.test)('runs on dev mode correctly', async () => {
175
+ await (0, utils_1.checkIfRunsOnDevMode)(projectDir, 'to expose');
176
+ }, 30000); // 30 second timeout
177
+ (0, vitest_1.test)('runs on prod mode correctly', async () => {
178
+ await (0, utils_1.checkIfRunsOnProdMode)(projectDir, 'react-router-serve');
179
+ }, 30000); // 30 second timeout
152
180
  });
153
181
  (0, vitest_1.describe)('edge cases', () => {
154
- const baseProjectDir = path.resolve(__dirname, '../test-applications/react-router-test-app');
155
182
  (0, vitest_1.describe)('existing Sentry setup', () => {
156
- const integration = Constants_1.Integration.reactRouter;
157
- const projectDir = path.resolve(__dirname, '../test-applications/react-router-test-app-existing');
183
+ let wizardExitCode;
184
+ const { projectDir, cleanup } = (0, utils_1.createIsolatedTestEnv)('react-router-test-app');
158
185
  (0, vitest_1.beforeAll)(async () => {
159
- // Copy project and add existing Sentry setup
160
- fs.cpSync(baseProjectDir, projectDir, { recursive: true });
186
+ // Add existing Sentry setup to the isolated test app
161
187
  const clientEntryPath = path.join(projectDir, 'app', 'entry.client.tsx');
162
188
  const existingContent = `import * as Sentry from "@sentry/react-router";
163
189
  import { startTransition, StrictMode } from "react";
@@ -178,28 +204,27 @@ startTransition(() => {
178
204
  );
179
205
  });`;
180
206
  fs.writeFileSync(clientEntryPath, existingContent);
181
- await runWizardOnReactRouterProject(projectDir, integration);
207
+ wizardExitCode = await runWizardOnReactRouterProject(projectDir, {
208
+ modifiedFiles: true,
209
+ });
182
210
  });
183
211
  (0, vitest_1.afterAll)(() => {
184
- (0, utils_1.revertLocalChanges)(projectDir);
185
- (0, utils_1.cleanupGit)(projectDir);
186
- try {
187
- fs.rmSync(projectDir, { recursive: true, force: true });
188
- }
189
- catch (e) {
190
- // Ignore cleanup errors
191
- }
212
+ cleanup();
213
+ });
214
+ (0, vitest_1.test)('exits with exit code 0', () => {
215
+ (0, vitest_1.expect)(wizardExitCode).toBe(0);
192
216
  });
193
217
  (0, vitest_1.test)('wizard handles existing Sentry without duplication', () => {
194
218
  const clientContent = fs.readFileSync(`${projectDir}/app/entry.client.tsx`, 'utf8');
195
219
  const sentryImportCount = (clientContent.match(/import \* as Sentry from "@sentry\/react-router"/g) || []).length;
196
- const sentryInitCount = (clientContent.match(/Sentry\.init\(/g) || []).length;
220
+ const sentryInitCount = (clientContent.match(/Sentry\.init\(/g) || [])
221
+ .length;
197
222
  (0, vitest_1.expect)(sentryImportCount).toBe(1);
198
223
  (0, vitest_1.expect)(sentryInitCount).toBe(1);
199
224
  });
200
225
  // Only test the essential checks for this edge case
201
226
  (0, vitest_1.test)('package.json is updated correctly', () => {
202
- (0, utils_1.checkPackageJson)(projectDir, integration);
227
+ (0, utils_1.checkPackageJson)(projectDir, '@sentry/react-router');
203
228
  });
204
229
  (0, vitest_1.test)('essential files exist or wizard completes gracefully', () => {
205
230
  // Check if key directories exist
@@ -211,42 +236,37 @@ startTransition(() => {
211
236
  (0, vitest_1.expect)(fs.existsSync(packageJsonPath)).toBe(true);
212
237
  const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
213
238
  const packageJson = JSON.parse(packageJsonContent);
214
- const hasSentryPackage = (packageJson.dependencies?.['@sentry/react-router']) ||
215
- (packageJson.devDependencies?.['@sentry/react-router']);
239
+ const hasSentryPackage = packageJson.dependencies?.['@sentry/react-router'] ||
240
+ packageJson.devDependencies?.['@sentry/react-router'];
216
241
  // The wizard should have at least installed the Sentry package
217
242
  (0, vitest_1.expect)(hasSentryPackage).toBeTruthy();
218
243
  });
219
244
  });
220
245
  (0, vitest_1.describe)('missing entry files', () => {
221
- const integration = Constants_1.Integration.reactRouter;
222
- const projectDir = path.resolve(__dirname, '../test-applications/react-router-test-app-missing-entries');
246
+ let wizardExitCode;
247
+ const { projectDir, cleanup } = (0, utils_1.createIsolatedTestEnv)('react-router-test-app');
223
248
  (0, vitest_1.beforeAll)(async () => {
224
249
  // Copy project and remove entry files
225
- fs.cpSync(baseProjectDir, projectDir, { recursive: true });
226
250
  const entryClientPath = path.join(projectDir, 'app', 'entry.client.tsx');
227
251
  const entryServerPath = path.join(projectDir, 'app', 'entry.server.tsx');
228
252
  if (fs.existsSync(entryClientPath))
229
253
  fs.unlinkSync(entryClientPath);
230
254
  if (fs.existsSync(entryServerPath))
231
255
  fs.unlinkSync(entryServerPath);
232
- await runWizardOnReactRouterProject(projectDir, integration);
256
+ wizardExitCode = await runWizardOnReactRouterProject(projectDir);
233
257
  });
234
258
  (0, vitest_1.afterAll)(() => {
235
- (0, utils_1.revertLocalChanges)(projectDir);
236
- (0, utils_1.cleanupGit)(projectDir);
237
- try {
238
- fs.rmSync(projectDir, { recursive: true, force: true });
239
- }
240
- catch (e) {
241
- // Ignore cleanup errors
242
- }
259
+ cleanup();
260
+ });
261
+ (0, vitest_1.test)('exits with exit code 0', () => {
262
+ (0, vitest_1.expect)(wizardExitCode).toBe(0);
243
263
  });
244
264
  (0, vitest_1.test)('wizard creates missing entry files', () => {
245
265
  (0, utils_1.checkFileExists)(`${projectDir}/app/entry.client.tsx`);
246
266
  (0, utils_1.checkFileExists)(`${projectDir}/app/entry.server.tsx`);
247
267
  });
248
268
  (0, vitest_1.test)('basic configuration still works', () => {
249
- (0, utils_1.checkPackageJson)(projectDir, integration);
269
+ (0, utils_1.checkPackageJson)(projectDir, '@sentry/react-router');
250
270
  (0, utils_1.checkFileExists)(`${projectDir}/instrument.server.mjs`);
251
271
  });
252
272
  });
@@ -1 +1 @@
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,oCAakB;AAClB,mCAAqE;AAErE,KAAK,UAAU,6BAA6B,CAC1C,UAAkB,EAClB,WAAwB;IAExB,MAAM,cAAc,GAAG,IAAA,2BAAmB,EAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IAEpE,MAAM,sBAAsB,GAAG,MAAM,cAAc,CAAC,aAAa,CAC/D,qCAAqC,CACtC,CAAC;IAEF,MAAM,qBAAqB,GACzB,sBAAsB;QACtB,CAAC,MAAM,cAAc,CAAC,yBAAyB,CAC7C,CAAC,YAAI,CAAC,IAAI,EAAE,YAAI,CAAC,KAAK,CAAC,EACvB,+CAA+C,EAC/C,EAAE,OAAO,EAAE,MAAO,EAAE,CACrB,CAAC,CAAC;IAEL,MAAM,oBAAoB,GACxB,qBAAqB;QACrB,CAAC,MAAM,cAAc,CAAC,yBAAyB,CAC7C,CAAC,YAAI,CAAC,KAAK,CAAC,EACZ,mEAAmE,CACpE,CAAC,CAAC;IAEL,MAAM,iBAAiB,GACrB,oBAAoB;QACpB,CAAC,MAAM,cAAc,CAAC,yBAAyB,CAC7C,CAAC,YAAI,CAAC,KAAK,CAAC,EACZ,0CAA0C,CAC3C,CAAC,CAAC;IAEL,MAAM,uBAAuB,GAC3B,iBAAiB;QACjB,CAAC,MAAM,cAAc,CAAC,yBAAyB,CAC7C,CAAC,YAAI,CAAC,KAAK,CAAC,EACZ,6CAA6C,CAC9C,CAAC,CAAC;IAEL,MAAM,mBAAmB,GACvB,uBAAuB;QACvB,CAAC,MAAM,cAAc,CAAC,yBAAyB,CAC7C,CAAC,YAAI,CAAC,KAAK,CAAC,EACZ,uCAAuC,CACxC,CAAC,CAAC;IAEL,MAAM,WAAW,GACf,mBAAmB;QACnB,CAAC,MAAM,cAAc,CAAC,yBAAyB,CAC7C,CAAC,YAAI,CAAC,KAAK,CAAC,EACZ,8EAA8E,EAC9E,EAAE,QAAQ,EAAE,IAAI,EAAE,CACnB,CAAC,CAAC;IAEL,WAAW;QACT,CAAC,MAAM,cAAc,CAAC,yBAAyB,CAC7C,CAAC,YAAI,CAAC,IAAI,EAAE,YAAI,CAAC,KAAK,CAAC,EACvB,qDAAqD,CACtD,CAAC,CAAC;IAEL,cAAc,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,uBAAuB,CAAC,UAAkB,EAAE,WAAwB;IAC3E,IAAA,aAAI,EAAC,mCAAmC,EAAE,GAAG,EAAE;QAC7C,IAAA,wBAAgB,EAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,iEAAiE,EAAE,GAAG,EAAE;QAC3E,IAAA,2BAAmB,EAAC,UAAU,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,qBAAqB,EAAE,GAAG,EAAE;QAC/B,IAAA,uBAAe,EAAC,GAAG,UAAU,qCAAqC,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,0BAA0B,EAAE,GAAG,EAAE;QACpC,IAAA,uBAAe,EAAC,GAAG,UAAU,uCAAuC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,+CAA+C,EAAE,GAAG,EAAE;QACzD,IAAA,yBAAiB,EAAC,GAAG,UAAU,gBAAgB,EAAE;YAC/C,iEAAiE;YACjE,sEAAsE;SACvE,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,+BAA+B,EAAE,GAAG,EAAE;QACzC,IAAA,uBAAe,EAAC,GAAG,UAAU,wBAAwB,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,kDAAkD,EAAE,GAAG,EAAE;QAC5D,IAAA,yBAAiB,EAAC,GAAG,UAAU,uBAAuB,EAAE;YACtD,yBAAyB;YACzB,sBAAsB;YACtB;UACI,iBAAS,CAAC,WAAW,IAAI;YAC7B,oFAAoF;YACpF,mBAAmB;YACnB,wBAAwB;SACzB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,4CAA4C,EAAE,GAAG,EAAE;QACtD,IAAA,yBAAiB,EAAC,GAAG,UAAU,eAAe,EAAE;YAC9C,uGAAuG;YACvG,2EAA2E;SAC5E,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,mDAAmD,EAAE,GAAG,EAAE;QAC7D,IAAA,yBAAiB,EAAC,GAAG,UAAU,uBAAuB,EAAE;YACtD,yBAAyB;YACzB,sBAAsB;YACtB,4DAA4D;YAC5D,+DAA+D;SAChE,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,uDAAuD,EAAE,GAAG,EAAE;QACjE,IAAA,yBAAiB,EAAC,GAAG,UAAU,wBAAwB,EAAE;YACvD,mDAAmD;YACnD;UACI,iBAAS,CAAC,WAAW,IAAI;YAC7B,mBAAmB;SACpB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,yCAAyC,EAAE,GAAG,EAAE;QACnD,IAAA,yBAAiB,EAAC,GAAG,UAAU,eAAe,EAAE;YAC9C,yBAAyB;YACzB,sBAAsB;YACtB,+BAA+B;YAC/B,gCAAgC;SACjC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,oDAAoD,EAAE,GAAG,EAAE;QAC9D,IAAA,yBAAiB,EAAC,GAAG,UAAU,iBAAiB,EAAE;YAChD,mCAAmC;YACnC,sBAAsB;YACtB,oBAAoB;YACpB,0CAA0C;SAC3C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,uEAAuE,EAAE,GAAG,EAAE;QACjF,IAAA,yBAAiB,EAAC,GAAG,UAAU,yBAAyB,EAAE;YACxD,kCAAkC;YAClC,sBAAsB;YACtB,YAAY;YACZ,iBAAiB;YACjB,0BAA0B;SAC3B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,qBAAqB,EAAE,KAAK,IAAI,EAAE;QACrC,MAAM,IAAA,qBAAa,EAAC,UAAU,CAAC,CAAC;IAClC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,mBAAmB;IAE9B,IAAA,aAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,IAAA,4BAAoB,EAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IACtD,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,oBAAoB;IAE/B,IAAA,aAAI,EAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC7C,MAAM,IAAA,6BAAqB,EAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC;IAChE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,oBAAoB;AACjC,CAAC;AAED,IAAA,iBAAQ,EAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,IAAA,iBAAQ,EAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,MAAM,WAAW,GAAG,uBAAW,CAAC,WAAW,CAAC;QAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAC7B,SAAS,EACT,4CAA4C,CAC7C,CAAC;QAEF,IAAA,kBAAS,EAAC,KAAK,IAAI,EAAE;YACnB,MAAM,6BAA6B,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;QAEH,IAAA,iBAAQ,EAAC,GAAG,EAAE;YACZ,IAAA,0BAAkB,EAAC,UAAU,CAAC,CAAC;YAC/B,IAAA,kBAAU,EAAC,UAAU,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;QAEH,uBAAuB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CACjC,SAAS,EACT,4CAA4C,CAC7C,CAAC;QAEF,IAAA,iBAAQ,EAAC,uBAAuB,EAAE,GAAG,EAAE;YACrC,MAAM,WAAW,GAAG,uBAAW,CAAC,WAAW,CAAC;YAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAC7B,SAAS,EACT,qDAAqD,CACtD,CAAC;YAEF,IAAA,kBAAS,EAAC,KAAK,IAAI,EAAE;gBACnB,6CAA6C;gBAC7C,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAE3D,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC;gBACzE,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;IAiB5B,CAAC;gBACG,EAAE,CAAC,aAAa,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;gBAEnD,MAAM,6BAA6B,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;YAC/D,CAAC,CAAC,CAAC;YAEH,IAAA,iBAAQ,EAAC,GAAG,EAAE;gBACZ,IAAA,0BAAkB,EAAC,UAAU,CAAC,CAAC;gBAC/B,IAAA,kBAAU,EAAC,UAAU,CAAC,CAAC;gBACvB,IAAI;oBACF,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;iBACzD;gBAAC,OAAO,CAAC,EAAE;oBACV,wBAAwB;iBACzB;YACH,CAAC,CAAC,CAAC;YAEH,IAAA,aAAI,EAAC,oDAAoD,EAAE,GAAG,EAAE;gBAC9D,MAAM,aAAa,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,UAAU,uBAAuB,EAAE,MAAM,CAAC,CAAC;gBACpF,MAAM,iBAAiB,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,mDAAmD,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;gBAClH,MAAM,eAAe,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;gBAE9E,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,WAAW,CAAC,CAAC;YAC5C,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,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC,sBAAsB,CAAC,CAAC;oBACpD,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC,sBAAsB,CAAC,CAAC,CAAC;gBAE1D,+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,MAAM,WAAW,GAAG,uBAAW,CAAC,WAAW,CAAC;YAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAC7B,SAAS,EACT,4DAA4D,CAC7D,CAAC;YAEF,IAAA,kBAAS,EAAC,KAAK,IAAI,EAAE;gBACnB,sCAAsC;gBACtC,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAE3D,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC;gBACzE,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC;gBAEzE,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,MAAM,6BAA6B,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;YAC/D,CAAC,CAAC,CAAC;YAEH,IAAA,iBAAQ,EAAC,GAAG,EAAE;gBACZ,IAAA,0BAAkB,EAAC,UAAU,CAAC,CAAC;gBAC/B,IAAA,kBAAU,EAAC,UAAU,CAAC,CAAC;gBACvB,IAAI;oBACF,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;iBACzD;gBAAC,OAAO,CAAC,EAAE;oBACV,wBAAwB;iBACzB;YACH,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,WAAW,CAAC,CAAC;gBAC1C,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 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":"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","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 .whenAsked('Do you want to use the Instrumentation API')\n .respondWith(KEYS.DOWN, KEYS.ENTER) // No\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_ENV=production 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"]}