@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.
Files changed (144) hide show
  1. package/CHANGELOG.md +20 -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 +64 -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 +4 -7
  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.test.js +163 -145
  33. package/dist/e2e-tests/tests/react-router.test.js.map +1 -1
  34. package/dist/e2e-tests/tests/remix.test.js +162 -132
  35. package/dist/e2e-tests/tests/remix.test.js.map +1 -1
  36. package/dist/e2e-tests/tests/sveltekit-hooks.test.js +48 -36
  37. package/dist/e2e-tests/tests/sveltekit-hooks.test.js.map +1 -1
  38. package/dist/e2e-tests/tests/sveltekit-tracing.test.js +3 -6
  39. package/dist/e2e-tests/tests/sveltekit-tracing.test.js.map +1 -1
  40. package/dist/e2e-tests/utils/index.d.ts +15 -43
  41. package/dist/e2e-tests/utils/index.js +95 -185
  42. package/dist/e2e-tests/utils/index.js.map +1 -1
  43. package/dist/get-e2e-test-matrix.mjs +11 -0
  44. package/dist/lib/Constants.d.ts +1 -0
  45. package/dist/lib/Constants.js +5 -0
  46. package/dist/lib/Constants.js.map +1 -1
  47. package/dist/src/android/android-wizard.js +2 -4
  48. package/dist/src/android/android-wizard.js.map +1 -1
  49. package/dist/src/angular/angular-wizard.js +4 -6
  50. package/dist/src/angular/angular-wizard.js.map +1 -1
  51. package/dist/src/angular/sdk-setup.js +1 -1
  52. package/dist/src/angular/sdk-setup.js.map +1 -1
  53. package/dist/src/apple/apple-wizard.js +2 -4
  54. package/dist/src/apple/apple-wizard.js.map +1 -1
  55. package/dist/src/cloudflare/cloudflare-wizard.d.ts +3 -0
  56. package/dist/src/cloudflare/cloudflare-wizard.js +99 -0
  57. package/dist/src/cloudflare/cloudflare-wizard.js.map +1 -0
  58. package/dist/src/cloudflare/sdk-setup.d.ts +7 -0
  59. package/dist/src/cloudflare/sdk-setup.js +47 -0
  60. package/dist/src/cloudflare/sdk-setup.js.map +1 -0
  61. package/dist/src/cloudflare/templates.d.ts +4 -0
  62. package/dist/src/cloudflare/templates.js +44 -0
  63. package/dist/src/cloudflare/templates.js.map +1 -0
  64. package/dist/src/cloudflare/wrangler/create-wrangler-config.d.ts +4 -0
  65. package/dist/src/cloudflare/wrangler/create-wrangler-config.js +27 -0
  66. package/dist/src/cloudflare/wrangler/create-wrangler-config.js.map +1 -0
  67. package/dist/src/cloudflare/wrangler/ensure-wrangler-config.d.ts +4 -0
  68. package/dist/src/cloudflare/wrangler/ensure-wrangler-config.js +25 -0
  69. package/dist/src/cloudflare/wrangler/ensure-wrangler-config.js.map +1 -0
  70. package/dist/src/cloudflare/wrangler/find-wrangler-config.d.ts +4 -0
  71. package/dist/src/cloudflare/wrangler/find-wrangler-config.js +23 -0
  72. package/dist/src/cloudflare/wrangler/find-wrangler-config.js.map +1 -0
  73. package/dist/src/cloudflare/wrangler/get-entry-point-from-wrangler-config.d.ts +6 -0
  74. package/dist/src/cloudflare/wrangler/get-entry-point-from-wrangler-config.js +52 -0
  75. package/dist/src/cloudflare/wrangler/get-entry-point-from-wrangler-config.js.map +1 -0
  76. package/dist/src/cloudflare/wrangler/update-wrangler-config.d.ts +17 -0
  77. package/dist/src/cloudflare/wrangler/update-wrangler-config.js +173 -0
  78. package/dist/src/cloudflare/wrangler/update-wrangler-config.js.map +1 -0
  79. package/dist/src/cloudflare/wrap-worker.d.ts +32 -0
  80. package/dist/src/cloudflare/wrap-worker.js +109 -0
  81. package/dist/src/cloudflare/wrap-worker.js.map +1 -0
  82. package/dist/src/flutter/flutter-wizard.js +3 -6
  83. package/dist/src/flutter/flutter-wizard.js.map +1 -1
  84. package/dist/src/nextjs/nextjs-wizard.js +0 -2
  85. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  86. package/dist/src/nuxt/nuxt-wizard.js +3 -5
  87. package/dist/src/nuxt/nuxt-wizard.js.map +1 -1
  88. package/dist/src/react-native/react-native-wizard.js +2 -4
  89. package/dist/src/react-native/react-native-wizard.js.map +1 -1
  90. package/dist/src/react-router/react-router-wizard.js +3 -5
  91. package/dist/src/react-router/react-router-wizard.js.map +1 -1
  92. package/dist/src/react-router/sdk-setup.d.ts +1 -1
  93. package/dist/src/react-router/sdk-setup.js +3 -4
  94. package/dist/src/react-router/sdk-setup.js.map +1 -1
  95. package/dist/src/remix/remix-wizard.js +2 -4
  96. package/dist/src/remix/remix-wizard.js.map +1 -1
  97. package/dist/src/run.d.ts +1 -1
  98. package/dist/src/run.js +5 -0
  99. package/dist/src/run.js.map +1 -1
  100. package/dist/src/sveltekit/sveltekit-wizard.js +2 -4
  101. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  102. package/dist/src/utils/abort-if-sportlight-not-supported.d.ts +5 -0
  103. package/dist/src/utils/abort-if-sportlight-not-supported.js +40 -0
  104. package/dist/src/utils/abort-if-sportlight-not-supported.js.map +1 -0
  105. package/dist/src/utils/ast-utils.d.ts +1 -1
  106. package/dist/src/utils/ast-utils.js.map +1 -1
  107. package/dist/src/utils/clack/index.d.ts +2 -2
  108. package/dist/src/utils/clack/index.js.map +1 -1
  109. package/dist/src/utils/clack/mcp-config.js +117 -59
  110. package/dist/src/utils/clack/mcp-config.js.map +1 -1
  111. package/dist/src/version.d.ts +1 -1
  112. package/dist/src/version.js +1 -1
  113. package/dist/src/version.js.map +1 -1
  114. package/dist/test/angular/angular-wizard.test.js +2 -4
  115. package/dist/test/angular/angular-wizard.test.js.map +1 -1
  116. package/dist/test/cloudflare/create-wrangler-config.test.d.ts +1 -0
  117. package/dist/test/cloudflare/create-wrangler-config.test.js +48 -0
  118. package/dist/test/cloudflare/create-wrangler-config.test.js.map +1 -0
  119. package/dist/test/cloudflare/ensure-wrangler-config.test.d.ts +1 -0
  120. package/dist/test/cloudflare/ensure-wrangler-config.test.js +61 -0
  121. package/dist/test/cloudflare/ensure-wrangler-config.test.js.map +1 -0
  122. package/dist/test/cloudflare/find-wrangler-config.test.d.ts +1 -0
  123. package/dist/test/cloudflare/find-wrangler-config.test.js +77 -0
  124. package/dist/test/cloudflare/find-wrangler-config.test.js.map +1 -0
  125. package/dist/test/cloudflare/get-entry-point-from-wrangler-config.test.d.ts +1 -0
  126. package/dist/test/cloudflare/get-entry-point-from-wrangler-config.test.js +81 -0
  127. package/dist/test/cloudflare/get-entry-point-from-wrangler-config.test.js.map +1 -0
  128. package/dist/test/cloudflare/sdk-setup.test.d.ts +1 -0
  129. package/dist/test/cloudflare/sdk-setup.test.js +152 -0
  130. package/dist/test/cloudflare/sdk-setup.test.js.map +1 -0
  131. package/dist/test/cloudflare/templates.test.d.ts +1 -0
  132. package/dist/test/cloudflare/templates.test.js +68 -0
  133. package/dist/test/cloudflare/templates.test.js.map +1 -0
  134. package/dist/test/cloudflare/update-wrangler-config.test.d.ts +1 -0
  135. package/dist/test/cloudflare/update-wrangler-config.test.js +216 -0
  136. package/dist/test/cloudflare/update-wrangler-config.test.js.map +1 -0
  137. package/dist/test/cloudflare/wrap-worker.test.d.ts +1 -0
  138. package/dist/test/cloudflare/wrap-worker.test.js +143 -0
  139. package/dist/test/cloudflare/wrap-worker.test.js.map +1 -0
  140. package/dist/test/react-router/sdk-setup.test.js +2 -2
  141. package/dist/test/react-router/sdk-setup.test.js.map +1 -1
  142. package/dist/test/utils/clack/mcp-config.test.js +176 -51
  143. package/dist/test/utils/clack/mcp-config.test.js.map +1 -1
  144. 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
- 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
+ .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
- const integration = Constants_1.Integration.reactRouter;
143
- const projectDir = path.resolve(__dirname, '../test-applications/react-router-test-app');
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, integration);
82
+ wizardExitCode = await runWizardOnReactRouterProject(projectDir);
146
83
  });
147
84
  (0, vitest_1.afterAll)(() => {
148
- (0, utils_1.revertLocalChanges)(projectDir);
149
- (0, utils_1.cleanupGit)(projectDir);
85
+ cleanup();
150
86
  });
151
- checkReactRouterProject(projectDir, integration);
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
- const integration = Constants_1.Integration.reactRouter;
157
- const projectDir = path.resolve(__dirname, '../test-applications/react-router-test-app-existing');
181
+ let wizardExitCode;
182
+ const { projectDir, cleanup } = (0, utils_1.createIsolatedTestEnv)('react-router-test-app');
158
183
  (0, vitest_1.beforeAll)(async () => {
159
- // Copy project and add existing Sentry setup
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, integration);
205
+ wizardExitCode = await runWizardOnReactRouterProject(projectDir, {
206
+ modifiedFiles: true,
207
+ });
182
208
  });
183
209
  (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
- }
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) || []).length;
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, integration);
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 = (packageJson.dependencies?.['@sentry/react-router']) ||
215
- (packageJson.devDependencies?.['@sentry/react-router']);
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
- const integration = Constants_1.Integration.reactRouter;
222
- const projectDir = path.resolve(__dirname, '../test-applications/react-router-test-app-missing-entries');
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, integration);
254
+ wizardExitCode = await runWizardOnReactRouterProject(projectDir);
233
255
  });
234
256
  (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
- }
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, integration);
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":"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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 .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"]}