@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
@@ -1,32 +1,10 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
- const path = __importStar(require("node:path"));
27
3
  const Constants_1 = require("../../lib/Constants");
28
4
  const utils_1 = require("../utils");
29
5
  const vitest_1 = require("vitest");
6
+ //@ts-expect-error - clifty is ESM only
7
+ const clifty_1 = require("clifty");
30
8
  const SERVER_TEMPLATE = `import { createRequestHandler } from '@remix-run/express';
31
9
  import { installGlobals } from '@remix-run/node';
32
10
  import compression from 'compression';
@@ -70,70 +48,63 @@ app.all(
70
48
  app.listen(0, () => console.log('Express server listening'));
71
49
  `;
72
50
  async function runWizardOnRemixProject(projectDir, integration, fileModificationFn) {
73
- const wizardInstance = (0, utils_1.startWizardInstance)(integration, projectDir);
74
- let packageManagerPrompted = false;
51
+ const wizardInteraction = (0, clifty_1.withEnv)({
52
+ cwd: projectDir,
53
+ }).defineInteraction();
75
54
  if (fileModificationFn) {
76
55
  fileModificationFn(projectDir, integration);
77
- await wizardInstance.waitForOutput('Do you want to continue anyway?');
78
- packageManagerPrompted = await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.ENTER], 'Please select your package manager.');
79
- }
80
- else {
81
- packageManagerPrompted = await wizardInstance.waitForOutput('Please select your package manager.');
56
+ wizardInteraction
57
+ .whenAsked('Do you want to continue anyway?')
58
+ .respondWith(clifty_1.KEYS.ENTER);
82
59
  }
83
- const tracingOptionPrompted = packageManagerPrompted &&
84
- (await wizardInstance.sendStdinAndWaitForOutput(
85
- // Selecting `yarn` as the package manager
86
- [utils_1.KEYS.DOWN, utils_1.KEYS.ENTER],
87
- // "Do you want to enable Tracing", sometimes doesn't work as `Tracing` can be printed in bold.
88
- 'to track the performance of your application?', {
89
- timeout: 240000,
90
- }));
91
- const replayOptionPrompted = tracingOptionPrompted &&
92
- (await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.ENTER],
93
- // "Do you want to enable Sentry Session Replay", sometimes doesn't work as `Sentry Session Replay` can be printed in bold.
94
- 'to get a video-like reproduction of errors during a user session?'));
95
- const logOptionPrompted = replayOptionPrompted &&
96
- (await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.ENTER],
97
- // "Do you want to enable Logs", sometimes doesn't work as `Logs` can be printed in bold.
98
- 'to send your application logs to Sentry?'));
99
- const examplePagePrompted = logOptionPrompted &&
100
- (await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.ENTER], 'Do you want to create an example page', {
101
- optional: true,
102
- }));
103
- // After the example page prompt, we send ENTER to accept it
104
- // Then handle the MCP prompt that comes after
105
- const mcpPrompted = examplePagePrompted &&
106
- (await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.ENTER], // This ENTER is for accepting the example page
107
- 'Optionally add a project-scoped MCP server configuration for the Sentry MCP?', {
108
- optional: true,
109
- }));
110
- // Decline MCP config (default is Yes, so press DOWN then ENTER to select No)
111
- if (mcpPrompted) {
112
- await wizardInstance.sendStdinAndWaitForOutput([utils_1.KEYS.DOWN, utils_1.KEYS.ENTER], 'Sentry has been successfully configured for your Remix project');
113
- }
114
- else {
115
- // If MCP wasn't prompted, wait for success message directly
116
- await wizardInstance.waitForOutput('Sentry has been successfully configured for your Remix project');
117
- }
118
- wizardInstance.kill();
60
+ return wizardInteraction
61
+ .whenAsked('Please select your package manager.')
62
+ .respondWith(clifty_1.KEYS.DOWN, clifty_1.KEYS.ENTER)
63
+ .whenAsked('to track the performance of your application?', {
64
+ timeout: 240000, // package installation can take a while in CI
65
+ })
66
+ .respondWith(clifty_1.KEYS.ENTER)
67
+ .whenAsked('to get a video-like reproduction of errors during a user session?')
68
+ .respondWith(clifty_1.KEYS.ENTER)
69
+ .whenAsked('to send your application logs to Sentry?')
70
+ .respondWith(clifty_1.KEYS.ENTER)
71
+ .whenAsked('Do you want to create an example page')
72
+ .respondWith(clifty_1.KEYS.ENTER)
73
+ .whenAsked('Optionally add a project-scoped MCP server configuration for the Sentry MCP?')
74
+ .respondWith(clifty_1.KEYS.DOWN, clifty_1.KEYS.ENTER)
75
+ .expectOutput('Sentry has been successfully configured for your Remix project')
76
+ .run((0, utils_1.getWizardCommand)(integration));
119
77
  }
120
- function checkRemixProject(projectDir, integration, options) {
121
- (0, vitest_1.test)('package.json is updated correctly', () => {
122
- (0, utils_1.checkPackageJson)(projectDir, integration);
123
- });
124
- (0, vitest_1.test)('.env-sentry-build-plugin is created and contains the auth token', () => {
125
- (0, utils_1.checkEnvBuildPlugin)(projectDir);
126
- });
127
- (0, vitest_1.test)('example page exists', () => {
128
- (0, utils_1.checkFileExists)(`${projectDir}/app/routes/sentry-example-page.tsx`);
129
- });
130
- (0, vitest_1.test)('instrumentation.server file exists', () => {
131
- (0, utils_1.checkFileExists)(`${projectDir}/instrumentation.server.mjs`);
132
- });
133
- (0, vitest_1.test)('entry.client file contains Sentry initialization', () => {
134
- (0, utils_1.checkFileContents)(`${projectDir}/app/entry.client.tsx`, [
135
- 'import { init, replayIntegration, browserTracingIntegration } from "@sentry/remix";',
136
- `init({
78
+ (0, vitest_1.describe)('Remix', () => {
79
+ (0, vitest_1.describe)('with empty project', () => {
80
+ const integration = Constants_1.Integration.remix;
81
+ let wizardExitCode;
82
+ const { projectDir, cleanup } = (0, utils_1.createIsolatedTestEnv)('remix-test-app');
83
+ (0, vitest_1.beforeAll)(async () => {
84
+ wizardExitCode = await runWizardOnRemixProject(projectDir, integration);
85
+ });
86
+ (0, vitest_1.afterAll)(() => {
87
+ cleanup();
88
+ });
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/remix');
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)('instrumentation.server file exists', () => {
102
+ (0, utils_1.checkFileExists)(`${projectDir}/instrumentation.server.mjs`);
103
+ });
104
+ (0, vitest_1.test)('entry.client file contains Sentry initialization', () => {
105
+ (0, utils_1.checkFileContents)(`${projectDir}/app/entry.client.tsx`, [
106
+ 'import { init, replayIntegration, browserTracingIntegration } from "@sentry/remix";',
107
+ `init({
137
108
  dsn: "${utils_1.TEST_ARGS.PROJECT_DSN}",
138
109
  tracesSampleRate: 1,
139
110
  enableLogs: true,
@@ -151,65 +122,53 @@ function checkRemixProject(projectDir, integration, options) {
151
122
  replaysOnErrorSampleRate: 1,
152
123
  sendDefaultPii: true
153
124
  })`,
154
- ]);
155
- });
156
- (0, vitest_1.test)('entry.server file contains Sentry code', () => {
157
- (0, utils_1.checkFileContents)(`${projectDir}/app/entry.server.tsx`, [
158
- 'import * as Sentry from "@sentry/remix";',
159
- `export const handleError = Sentry.wrapHandleErrorWithSentry((error, { request }) => {
125
+ ]);
126
+ });
127
+ (0, vitest_1.test)('entry.server file contains Sentry code', () => {
128
+ (0, utils_1.checkFileContents)(`${projectDir}/app/entry.server.tsx`, [
129
+ 'import * as Sentry from "@sentry/remix";',
130
+ `export const handleError = Sentry.wrapHandleErrorWithSentry((error, { request }) => {
160
131
  // Custom handleError implementation
161
132
  });`,
162
- ]);
163
- });
164
- (0, vitest_1.test)('instrumentation.server file contains Sentry initialization', () => {
165
- (0, utils_1.checkFileContents)(`${projectDir}/instrumentation.server.mjs`, [
166
- 'import * as Sentry from "@sentry/remix";',
167
- `Sentry.init({
133
+ ]);
134
+ });
135
+ (0, vitest_1.test)('instrumentation.server file contains Sentry initialization', () => {
136
+ (0, utils_1.checkFileContents)(`${projectDir}/instrumentation.server.mjs`, [
137
+ 'import * as Sentry from "@sentry/remix";',
138
+ `Sentry.init({
168
139
  dsn: "${utils_1.TEST_ARGS.PROJECT_DSN}",
169
140
  tracesSampleRate: 1,
170
141
  enableLogs: true
171
142
  })`,
172
- ]);
173
- });
174
- (0, vitest_1.test)('root file contains Sentry ErrorBoundary and withSentry wrapper', () => {
175
- (0, utils_1.checkFileContents)(`${projectDir}/app/root.tsx`, [
176
- 'import { captureRemixErrorBoundaryError, withSentry } from "@sentry/remix";',
177
- `export const ErrorBoundary = () => {
143
+ ]);
144
+ });
145
+ (0, vitest_1.test)('root file contains Sentry ErrorBoundary and withSentry wrapper', () => {
146
+ (0, utils_1.checkFileContents)(`${projectDir}/app/root.tsx`, [
147
+ 'import { captureRemixErrorBoundaryError, withSentry } from "@sentry/remix";',
148
+ `export const ErrorBoundary = () => {
178
149
  const error = useRouteError();
179
150
  captureRemixErrorBoundaryError(error);
180
151
  return <div>Something went wrong</div>;
181
152
  };`,
182
- `export default withSentry(App);`,
183
- ]);
184
- });
185
- (0, vitest_1.test)('builds successfully', async () => {
186
- await (0, utils_1.checkIfBuilds)(projectDir);
187
- });
188
- (0, vitest_1.test)('runs on dev mode correctly', async () => {
189
- await (0, utils_1.checkIfRunsOnDevMode)(projectDir, options?.devModeExpectedOutput || 'to expose');
190
- });
191
- (0, vitest_1.test)('runs on prod mode correctly', async () => {
192
- await (0, utils_1.checkIfRunsOnProdMode)(projectDir, options?.prodModeExpectedOutput || '[remix-serve]');
193
- });
194
- }
195
- (0, vitest_1.describe)('Remix', () => {
196
- (0, vitest_1.describe)('with empty project', () => {
197
- const integration = Constants_1.Integration.remix;
198
- const projectDir = path.resolve(__dirname, '../test-applications/remix-test-app');
199
- (0, vitest_1.beforeAll)(async () => {
200
- await runWizardOnRemixProject(projectDir, integration);
153
+ `export default withSentry(App);`,
154
+ ]);
201
155
  });
202
- (0, vitest_1.afterAll)(() => {
203
- (0, utils_1.revertLocalChanges)(projectDir);
204
- (0, utils_1.cleanupGit)(projectDir);
156
+ (0, vitest_1.test)('builds successfully', async () => {
157
+ await (0, utils_1.checkIfBuilds)(projectDir);
158
+ });
159
+ (0, vitest_1.test)('runs on dev mode correctly', async () => {
160
+ await (0, utils_1.checkIfRunsOnDevMode)(projectDir, 'to expose');
161
+ });
162
+ (0, vitest_1.test)('runs on prod mode correctly', async () => {
163
+ await (0, utils_1.checkIfRunsOnProdMode)(projectDir, '[remix-serve]');
205
164
  });
206
- checkRemixProject(projectDir, integration);
207
165
  });
208
166
  (0, vitest_1.describe)('with existing custom Express server', () => {
209
167
  const integration = Constants_1.Integration.remix;
210
- const projectDir = path.resolve(__dirname, '../test-applications/remix-test-app');
168
+ let wizardExitCode;
169
+ const { projectDir, cleanup } = (0, utils_1.createIsolatedTestEnv)('remix-test-app');
211
170
  (0, vitest_1.beforeAll)(async () => {
212
- await runWizardOnRemixProject(projectDir, integration, (projectDir) => {
171
+ wizardExitCode = await runWizardOnRemixProject(projectDir, integration, (projectDir) => {
213
172
  (0, utils_1.createFile)(`${projectDir}/server.mjs`, SERVER_TEMPLATE);
214
173
  (0, utils_1.modifyFile)(`${projectDir}/package.json`, {
215
174
  '"start": "remix-serve ./build/server/index.js"': '"start": "node ./server.mjs"',
@@ -218,12 +177,83 @@ function checkRemixProject(projectDir, integration, options) {
218
177
  });
219
178
  });
220
179
  (0, vitest_1.afterAll)(() => {
221
- (0, utils_1.revertLocalChanges)(projectDir);
222
- (0, utils_1.cleanupGit)(projectDir);
180
+ cleanup();
181
+ });
182
+ (0, vitest_1.test)('exits with exit code 0', () => {
183
+ (0, vitest_1.expect)(wizardExitCode).toBe(0);
184
+ });
185
+ (0, vitest_1.test)('package.json is updated correctly', () => {
186
+ (0, utils_1.checkPackageJson)(projectDir, '@sentry/remix');
187
+ });
188
+ (0, vitest_1.test)('.env-sentry-build-plugin is created and contains the auth token', () => {
189
+ (0, utils_1.checkEnvBuildPlugin)(projectDir);
190
+ });
191
+ (0, vitest_1.test)('example page exists', () => {
192
+ (0, utils_1.checkFileExists)(`${projectDir}/app/routes/sentry-example-page.tsx`);
193
+ });
194
+ (0, vitest_1.test)('instrumentation.server file exists', () => {
195
+ (0, utils_1.checkFileExists)(`${projectDir}/instrumentation.server.mjs`);
196
+ });
197
+ (0, vitest_1.test)('entry.client file contains Sentry initialization', () => {
198
+ (0, utils_1.checkFileContents)(`${projectDir}/app/entry.client.tsx`, [
199
+ 'import { init, replayIntegration, browserTracingIntegration } from "@sentry/remix";',
200
+ `init({
201
+ dsn: "${utils_1.TEST_ARGS.PROJECT_DSN}",
202
+ tracesSampleRate: 1,
203
+ enableLogs: true,
204
+
205
+ integrations: [browserTracingIntegration({
206
+ useEffect,
207
+ useLocation,
208
+ useMatches
209
+ }), replayIntegration({
210
+ maskAllText: true,
211
+ blockAllMedia: true
212
+ })],
213
+
214
+ replaysSessionSampleRate: 0.1,
215
+ replaysOnErrorSampleRate: 1,
216
+ sendDefaultPii: true
217
+ })`,
218
+ ]);
219
+ });
220
+ (0, vitest_1.test)('entry.server file contains Sentry code', () => {
221
+ (0, utils_1.checkFileContents)(`${projectDir}/app/entry.server.tsx`, [
222
+ 'import * as Sentry from "@sentry/remix";',
223
+ `export const handleError = Sentry.wrapHandleErrorWithSentry((error, { request }) => {
224
+ // Custom handleError implementation
225
+ });`,
226
+ ]);
227
+ });
228
+ (0, vitest_1.test)('instrumentation.server file contains Sentry initialization', () => {
229
+ (0, utils_1.checkFileContents)(`${projectDir}/instrumentation.server.mjs`, [
230
+ 'import * as Sentry from "@sentry/remix";',
231
+ `Sentry.init({
232
+ dsn: "${utils_1.TEST_ARGS.PROJECT_DSN}",
233
+ tracesSampleRate: 1,
234
+ enableLogs: true
235
+ })`,
236
+ ]);
237
+ });
238
+ (0, vitest_1.test)('root file contains Sentry ErrorBoundary and withSentry wrapper', () => {
239
+ (0, utils_1.checkFileContents)(`${projectDir}/app/root.tsx`, [
240
+ 'import { captureRemixErrorBoundaryError, withSentry } from "@sentry/remix";',
241
+ `export const ErrorBoundary = () => {
242
+ const error = useRouteError();
243
+ captureRemixErrorBoundaryError(error);
244
+ return <div>Something went wrong</div>;
245
+ };`,
246
+ `export default withSentry(App);`,
247
+ ]);
248
+ });
249
+ (0, vitest_1.test)('builds successfully', async () => {
250
+ await (0, utils_1.checkIfBuilds)(projectDir);
251
+ });
252
+ (0, vitest_1.test)('runs on dev mode correctly', async () => {
253
+ await (0, utils_1.checkIfRunsOnDevMode)(projectDir, 'Express server listening');
223
254
  });
224
- checkRemixProject(projectDir, integration, {
225
- devModeExpectedOutput: 'Express server listening',
226
- prodModeExpectedOutput: 'Express server listening',
255
+ (0, vitest_1.test)('runs on prod mode correctly', async () => {
256
+ await (0, utils_1.checkIfRunsOnProdMode)(projectDir, 'Express server listening');
227
257
  });
228
258
  (0, vitest_1.test)('server.mjs contains instrumentation file import', () => {
229
259
  (0, utils_1.checkFileContents)(`${projectDir}/server.mjs`, [
@@ -1 +1 @@
1
- {"version":3,"file":"remix.test.js","sourceRoot":"","sources":["../../../e2e-tests/tests/remix.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAkC;AAClC,mDAAkD;AAClD,oCAekB;AAClB,mCAA6D;AAE7D,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyCvB,CAAC;AAEF,KAAK,UAAU,uBAAuB,CACpC,UAAkB,EAClB,WAAwB,EACxB,kBAGY;IAEZ,MAAM,cAAc,GAAG,IAAA,2BAAmB,EAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACpE,IAAI,sBAAsB,GAAG,KAAK,CAAC;IAEnC,IAAI,kBAAkB,EAAE;QACtB,kBAAkB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAE5C,MAAM,cAAc,CAAC,aAAa,CAAC,iCAAiC,CAAC,CAAC;QAEtE,sBAAsB,GAAG,MAAM,cAAc,CAAC,yBAAyB,CACrE,CAAC,YAAI,CAAC,KAAK,CAAC,EACZ,qCAAqC,CACtC,CAAC;KACH;SAAM;QACL,sBAAsB,GAAG,MAAM,cAAc,CAAC,aAAa,CACzD,qCAAqC,CACtC,CAAC;KACH;IAED,MAAM,qBAAqB,GACzB,sBAAsB;QACtB,CAAC,MAAM,cAAc,CAAC,yBAAyB;QAC7C,0CAA0C;QAC1C,CAAC,YAAI,CAAC,IAAI,EAAE,YAAI,CAAC,KAAK,CAAC;QACvB,+FAA+F;QAC/F,+CAA+C,EAC/C;YACE,OAAO,EAAE,MAAO;SACjB,CACF,CAAC,CAAC;IAEL,MAAM,oBAAoB,GACxB,qBAAqB;QACrB,CAAC,MAAM,cAAc,CAAC,yBAAyB,CAC7C,CAAC,YAAI,CAAC,KAAK,CAAC;QACZ,2HAA2H;QAC3H,mEAAmE,CACpE,CAAC,CAAC;IAEL,MAAM,iBAAiB,GACrB,oBAAoB;QACpB,CAAC,MAAM,cAAc,CAAC,yBAAyB,CAC7C,CAAC,YAAI,CAAC,KAAK,CAAC;QACZ,yFAAyF;QACzF,0CAA0C,CAC3C,CAAC,CAAC;IAEL,MAAM,mBAAmB,GACvB,iBAAiB;QACjB,CAAC,MAAM,cAAc,CAAC,yBAAyB,CAC7C,CAAC,YAAI,CAAC,KAAK,CAAC,EACZ,uCAAuC,EACvC;YACE,QAAQ,EAAE,IAAI;SACf,CACF,CAAC,CAAC;IAEL,4DAA4D;IAC5D,8CAA8C;IAC9C,MAAM,WAAW,GACf,mBAAmB;QACnB,CAAC,MAAM,cAAc,CAAC,yBAAyB,CAC7C,CAAC,YAAI,CAAC,KAAK,CAAC,EAAG,+CAA+C;QAC9D,8EAA8E,EAC9E;YACE,QAAQ,EAAE,IAAI;SACf,CACF,CAAC,CAAC;IAEL,6EAA6E;IAC7E,IAAI,WAAW,EAAE;QACf,MAAM,cAAc,CAAC,yBAAyB,CAC5C,CAAC,YAAI,CAAC,IAAI,EAAE,YAAI,CAAC,KAAK,CAAC,EACvB,gEAAgE,CACjE,CAAC;KACH;SAAM;QACL,4DAA4D;QAC5D,MAAM,cAAc,CAAC,aAAa,CAChC,gEAAgE,CACjE,CAAC;KACH;IAED,cAAc,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,iBAAiB,CACxB,UAAkB,EAClB,WAAwB,EACxB,OAGC;IAED,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,oCAAoC,EAAE,GAAG,EAAE;QAC9C,IAAA,uBAAe,EAAC,GAAG,UAAU,6BAA6B,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,kDAAkD,EAAE,GAAG,EAAE;QAC5D,IAAA,yBAAiB,EAAC,GAAG,UAAU,uBAAuB,EAAE;YACtD,qFAAqF;YACrF;YACM,iBAAS,CAAC,WAAW;;;;;;;;;;;;;;;;GAgB9B;SACE,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,wCAAwC,EAAE,GAAG,EAAE;QAClD,IAAA,yBAAiB,EAAC,GAAG,UAAU,uBAAuB,EAAE;YACtD,0CAA0C;YAC1C;;IAEF;SACC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,4DAA4D,EAAE,GAAG,EAAE;QACtE,IAAA,yBAAiB,EAAC,GAAG,UAAU,6BAA6B,EAAE;YAC5D,0CAA0C;YAC1C;YACM,iBAAS,CAAC,WAAW;;;GAG9B;SACE,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,gEAAgE,EAAE,GAAG,EAAE;QAC1E,IAAA,yBAAiB,EAAC,GAAG,UAAU,eAAe,EAAE;YAC9C,6EAA6E;YAC7E;;;;GAIH;YACG,iCAAiC;SAClC,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,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,IAAA,4BAAoB,EACxB,UAAU,EACV,OAAO,EAAE,qBAAqB,IAAI,WAAW,CAC9C,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC7C,MAAM,IAAA,6BAAqB,EACzB,UAAU,EACV,OAAO,EAAE,sBAAsB,IAAI,eAAe,CACnD,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,IAAA,iBAAQ,EAAC,OAAO,EAAE,GAAG,EAAE;IACrB,IAAA,iBAAQ,EAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,MAAM,WAAW,GAAG,uBAAW,CAAC,KAAK,CAAC;QACtC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAC7B,SAAS,EACT,qCAAqC,CACtC,CAAC;QAEF,IAAA,kBAAS,EAAC,KAAK,IAAI,EAAE;YACnB,MAAM,uBAAuB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QACzD,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,iBAAiB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,qCAAqC,EAAE,GAAG,EAAE;QACnD,MAAM,WAAW,GAAG,uBAAW,CAAC,KAAK,CAAC;QACtC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAC7B,SAAS,EACT,qCAAqC,CACtC,CAAC;QAEF,IAAA,kBAAS,EAAC,KAAK,IAAI,EAAE;YACnB,MAAM,uBAAuB,CAAC,UAAU,EAAE,WAAW,EAAE,CAAC,UAAU,EAAE,EAAE;gBACpE,IAAA,kBAAU,EAAC,GAAG,UAAU,aAAa,EAAE,eAAe,CAAC,CAAC;gBAExD,IAAA,kBAAU,EAAC,GAAG,UAAU,eAAe,EAAE;oBACvC,gDAAgD,EAC9C,8BAA8B;oBAChC,yBAAyB,EAAE,4BAA4B;iBACxD,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,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,iBAAiB,CAAC,UAAU,EAAE,WAAW,EAAE;YACzC,qBAAqB,EAAE,0BAA0B;YACjD,sBAAsB,EAAE,0BAA0B;SACnD,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,iDAAiD,EAAE,GAAG,EAAE;YAC3D,IAAA,yBAAiB,EAAC,GAAG,UAAU,aAAa,EAAE;gBAC5C,wCAAwC;aACzC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import * as path from 'node:path';\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 createFile,\n modifyFile,\n revertLocalChanges,\n startWizardInstance,\n} from '../utils';\nimport { afterAll, beforeAll, describe, test } from 'vitest';\n\nconst SERVER_TEMPLATE = `import { createRequestHandler } from '@remix-run/express';\nimport { installGlobals } from '@remix-run/node';\nimport compression from 'compression';\nimport express from 'express';\nimport morgan from 'morgan';\n\ninstallGlobals();\n\nconst viteDevServer =\n process.env.NODE_ENV === 'production'\n ? undefined\n : await import('vite').then(vite =>\n vite.createServer({\n server: { middlewareMode: true },\n }),\n );\n\nconst app = express();\n\napp.use(compression());\napp.disable('x-powered-by');\n\nif (viteDevServer) {\n app.use(viteDevServer.middlewares);\n} else {\n app.use('/assets', express.static('build/client/assets', { immutable: true, maxAge: '1y' }));\n}\n\napp.use(express.static('build/client', { maxAge: '1h' }));\napp.use(morgan('tiny'));\n\napp.all(\n '*',\n createRequestHandler({\n build: viteDevServer\n ? () => viteDevServer.ssrLoadModule('virtual:remix/server-build')\n : await import('./build/server/index.js'),\n }),\n);\n\napp.listen(0, () => console.log('Express server listening'));\n`;\n\nasync function runWizardOnRemixProject(\n projectDir: string,\n integration: Integration,\n fileModificationFn?: (\n projectDir: string,\n integration: Integration,\n ) => unknown,\n) {\n const wizardInstance = startWizardInstance(integration, projectDir);\n let packageManagerPrompted = false;\n\n if (fileModificationFn) {\n fileModificationFn(projectDir, integration);\n\n await wizardInstance.waitForOutput('Do you want to continue anyway?');\n\n packageManagerPrompted = await wizardInstance.sendStdinAndWaitForOutput(\n [KEYS.ENTER],\n 'Please select your package manager.',\n );\n } else {\n packageManagerPrompted = await wizardInstance.waitForOutput(\n 'Please select your package manager.',\n );\n }\n\n const tracingOptionPrompted =\n packageManagerPrompted &&\n (await wizardInstance.sendStdinAndWaitForOutput(\n // Selecting `yarn` as the package manager\n [KEYS.DOWN, KEYS.ENTER],\n // \"Do you want to enable Tracing\", sometimes doesn't work as `Tracing` can be printed in bold.\n 'to track the performance of your application?',\n {\n timeout: 240_000,\n },\n ));\n\n const replayOptionPrompted =\n tracingOptionPrompted &&\n (await wizardInstance.sendStdinAndWaitForOutput(\n [KEYS.ENTER],\n // \"Do you want to enable Sentry Session Replay\", sometimes doesn't work as `Sentry Session Replay` can be printed in bold.\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 // \"Do you want to enable Logs\", sometimes doesn't work as `Logs` can be printed in bold.\n 'to send your application logs to Sentry?',\n ));\n\n const examplePagePrompted =\n logOptionPrompted &&\n (await wizardInstance.sendStdinAndWaitForOutput(\n [KEYS.ENTER],\n 'Do you want to create an example page',\n {\n optional: true,\n },\n ));\n\n // After the example page prompt, we send ENTER to accept it\n // Then handle the MCP prompt that comes after\n const mcpPrompted =\n examplePagePrompted &&\n (await wizardInstance.sendStdinAndWaitForOutput(\n [KEYS.ENTER], // This ENTER is for accepting the example page\n 'Optionally add a project-scoped MCP server configuration for the Sentry MCP?',\n {\n optional: true,\n },\n ));\n\n // Decline MCP config (default is Yes, so press DOWN then ENTER to select No)\n if (mcpPrompted) {\n await wizardInstance.sendStdinAndWaitForOutput(\n [KEYS.DOWN, KEYS.ENTER],\n 'Sentry has been successfully configured for your Remix project',\n );\n } else {\n // If MCP wasn't prompted, wait for success message directly\n await wizardInstance.waitForOutput(\n 'Sentry has been successfully configured for your Remix project',\n );\n }\n\n wizardInstance.kill();\n}\n\nfunction checkRemixProject(\n projectDir: string,\n integration: Integration,\n options?: {\n devModeExpectedOutput?: string;\n prodModeExpectedOutput?: string;\n },\n) {\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('instrumentation.server file exists', () => {\n checkFileExists(`${projectDir}/instrumentation.server.mjs`);\n });\n\n test('entry.client file contains Sentry initialization', () => {\n checkFileContents(`${projectDir}/app/entry.client.tsx`, [\n 'import { init, replayIntegration, browserTracingIntegration } from \"@sentry/remix\";',\n `init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",\n tracesSampleRate: 1,\n enableLogs: true,\n\n integrations: [browserTracingIntegration({\n useEffect,\n useLocation,\n useMatches\n }), replayIntegration({\n maskAllText: true,\n blockAllMedia: true\n })],\n\n replaysSessionSampleRate: 0.1,\n replaysOnErrorSampleRate: 1,\n sendDefaultPii: true\n})`,\n ]);\n });\n\n test('entry.server file contains Sentry code', () => {\n checkFileContents(`${projectDir}/app/entry.server.tsx`, [\n 'import * as Sentry from \"@sentry/remix\";',\n `export const handleError = Sentry.wrapHandleErrorWithSentry((error, { request }) => {\n // Custom handleError implementation\n});`,\n ]);\n });\n\n test('instrumentation.server file contains Sentry initialization', () => {\n checkFileContents(`${projectDir}/instrumentation.server.mjs`, [\n 'import * as Sentry from \"@sentry/remix\";',\n `Sentry.init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",\n tracesSampleRate: 1,\n enableLogs: true\n})`,\n ]);\n });\n\n test('root file contains Sentry ErrorBoundary and withSentry wrapper', () => {\n checkFileContents(`${projectDir}/app/root.tsx`, [\n 'import { captureRemixErrorBoundaryError, withSentry } from \"@sentry/remix\";',\n `export const ErrorBoundary = () => {\n const error = useRouteError();\n captureRemixErrorBoundaryError(error);\n return <div>Something went wrong</div>;\n};`,\n `export default withSentry(App);`,\n ]);\n });\n\n test('builds successfully', async () => {\n await checkIfBuilds(projectDir);\n });\n\n test('runs on dev mode correctly', async () => {\n await checkIfRunsOnDevMode(\n projectDir,\n options?.devModeExpectedOutput || 'to expose',\n );\n });\n\n test('runs on prod mode correctly', async () => {\n await checkIfRunsOnProdMode(\n projectDir,\n options?.prodModeExpectedOutput || '[remix-serve]',\n );\n });\n}\n\ndescribe('Remix', () => {\n describe('with empty project', () => {\n const integration = Integration.remix;\n const projectDir = path.resolve(\n __dirname,\n '../test-applications/remix-test-app',\n );\n\n beforeAll(async () => {\n await runWizardOnRemixProject(projectDir, integration);\n });\n\n afterAll(() => {\n revertLocalChanges(projectDir);\n cleanupGit(projectDir);\n });\n\n checkRemixProject(projectDir, integration);\n });\n\n describe('with existing custom Express server', () => {\n const integration = Integration.remix;\n const projectDir = path.resolve(\n __dirname,\n '../test-applications/remix-test-app',\n );\n\n beforeAll(async () => {\n await runWizardOnRemixProject(projectDir, integration, (projectDir) => {\n createFile(`${projectDir}/server.mjs`, SERVER_TEMPLATE);\n\n modifyFile(`${projectDir}/package.json`, {\n '\"start\": \"remix-serve ./build/server/index.js\"':\n '\"start\": \"node ./server.mjs\"',\n '\"dev\": \"remix vite:dev\"': '\"dev\": \"node ./server.mjs\"',\n });\n });\n });\n\n afterAll(() => {\n revertLocalChanges(projectDir);\n cleanupGit(projectDir);\n });\n\n checkRemixProject(projectDir, integration, {\n devModeExpectedOutput: 'Express server listening',\n prodModeExpectedOutput: 'Express server listening',\n });\n\n test('server.mjs contains instrumentation file import', () => {\n checkFileContents(`${projectDir}/server.mjs`, [\n \"import './instrumentation.server.mjs';\",\n ]);\n });\n });\n});\n"]}
1
+ {"version":3,"file":"remix.test.js","sourceRoot":"","sources":["../../../e2e-tests/tests/remix.test.ts"],"names":[],"mappings":";;AAAA,mDAAkD;AAClD,oCAakB;AAClB,mCAAqE;AAErE,uCAAuC;AACvC,mCAAuC;AAEvC,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyCvB,CAAC;AAEF,KAAK,UAAU,uBAAuB,CACpC,UAAkB,EAClB,WAAwB,EACxB,kBAGY;IAEZ,MAAM,iBAAiB,GAAG,IAAA,gBAAO,EAAC;QAChC,GAAG,EAAE,UAAU;KAChB,CAAC,CAAC,iBAAiB,EAAE,CAAC;IAEvB,IAAI,kBAAkB,EAAE;QACtB,kBAAkB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAE5C,iBAAiB;aACd,SAAS,CAAC,iCAAiC,CAAC;aAC5C,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC,CAAC;KAC5B;IAED,OAAO,iBAAiB;SACrB,SAAS,CAAC,qCAAqC,CAAC;SAChD,WAAW,CAAC,aAAI,CAAC,IAAI,EAAE,aAAI,CAAC,KAAK,CAAC;SAClC,SAAS,CAAC,+CAA+C,EAAE;QAC1D,OAAO,EAAE,MAAO,EAAE,8CAA8C;KACjE,CAAC;SACD,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC;SACvB,SAAS,CACR,mEAAmE,CACpE;SACA,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC;SACvB,SAAS,CAAC,0CAA0C,CAAC;SACrD,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC;SACvB,SAAS,CAAC,uCAAuC,CAAC;SAClD,WAAW,CAAC,aAAI,CAAC,KAAK,CAAC;SACvB,SAAS,CACR,8EAA8E,CAC/E;SACA,WAAW,CAAC,aAAI,CAAC,IAAI,EAAE,aAAI,CAAC,KAAK,CAAC;SAClC,YAAY,CACX,gEAAgE,CACjE;SACA,GAAG,CAAC,IAAA,wBAAgB,EAAC,WAAW,CAAC,CAAC,CAAC;AACxC,CAAC;AAED,IAAA,iBAAQ,EAAC,OAAO,EAAE,GAAG,EAAE;IACrB,IAAA,iBAAQ,EAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,MAAM,WAAW,GAAG,uBAAW,CAAC,KAAK,CAAC;QACtC,IAAI,cAAsB,CAAC;QAE3B,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,IAAA,6BAAqB,EAAC,gBAAgB,CAAC,CAAC;QAExE,IAAA,kBAAS,EAAC,KAAK,IAAI,EAAE;YACnB,cAAc,GAAG,MAAM,uBAAuB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAC1E,CAAC,CAAC,CAAC;QAEH,IAAA,iBAAQ,EAAC,GAAG,EAAE;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,wBAAwB,EAAE,GAAG,EAAE;YAClC,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,mCAAmC,EAAE,GAAG,EAAE;YAC7C,IAAA,wBAAgB,EAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,iEAAiE,EAAE,GAAG,EAAE;YAC3E,IAAA,2BAAmB,EAAC,UAAU,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,qBAAqB,EAAE,GAAG,EAAE;YAC/B,IAAA,uBAAe,EAAC,GAAG,UAAU,qCAAqC,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,oCAAoC,EAAE,GAAG,EAAE;YAC9C,IAAA,uBAAe,EAAC,GAAG,UAAU,6BAA6B,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,kDAAkD,EAAE,GAAG,EAAE;YAC5D,IAAA,yBAAiB,EAAC,GAAG,UAAU,uBAAuB,EAAE;gBACtD,qFAAqF;gBACrF;YACI,iBAAS,CAAC,WAAW;;;;;;;;;;;;;;;;GAgB9B;aACI,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,wCAAwC,EAAE,GAAG,EAAE;YAClD,IAAA,yBAAiB,EAAC,GAAG,UAAU,uBAAuB,EAAE;gBACtD,0CAA0C;gBAC1C;;IAEJ;aACG,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,4DAA4D,EAAE,GAAG,EAAE;YACtE,IAAA,yBAAiB,EAAC,GAAG,UAAU,6BAA6B,EAAE;gBAC5D,0CAA0C;gBAC1C;YACI,iBAAS,CAAC,WAAW;;;GAG9B;aACI,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,gEAAgE,EAAE,GAAG,EAAE;YAC1E,IAAA,yBAAiB,EAAC,GAAG,UAAU,eAAe,EAAE;gBAC9C,6EAA6E;gBAC7E;;;;GAIL;gBACK,iCAAiC;aAClC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,qBAAqB,EAAE,KAAK,IAAI,EAAE;YACrC,MAAM,IAAA,qBAAa,EAAC,UAAU,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC5C,MAAM,IAAA,4BAAoB,EAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC7C,MAAM,IAAA,6BAAqB,EAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,qCAAqC,EAAE,GAAG,EAAE;QACnD,MAAM,WAAW,GAAG,uBAAW,CAAC,KAAK,CAAC;QACtC,IAAI,cAAsB,CAAC;QAE3B,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,IAAA,6BAAqB,EAAC,gBAAgB,CAAC,CAAC;QAExE,IAAA,kBAAS,EAAC,KAAK,IAAI,EAAE;YACnB,cAAc,GAAG,MAAM,uBAAuB,CAC5C,UAAU,EACV,WAAW,EACX,CAAC,UAAU,EAAE,EAAE;gBACb,IAAA,kBAAU,EAAC,GAAG,UAAU,aAAa,EAAE,eAAe,CAAC,CAAC;gBAExD,IAAA,kBAAU,EAAC,GAAG,UAAU,eAAe,EAAE;oBACvC,gDAAgD,EAC9C,8BAA8B;oBAChC,yBAAyB,EAAE,4BAA4B;iBACxD,CAAC,CAAC;YACL,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAA,iBAAQ,EAAC,GAAG,EAAE;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,wBAAwB,EAAE,GAAG,EAAE;YAClC,IAAA,eAAM,EAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,mCAAmC,EAAE,GAAG,EAAE;YAC7C,IAAA,wBAAgB,EAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,iEAAiE,EAAE,GAAG,EAAE;YAC3E,IAAA,2BAAmB,EAAC,UAAU,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,qBAAqB,EAAE,GAAG,EAAE;YAC/B,IAAA,uBAAe,EAAC,GAAG,UAAU,qCAAqC,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,oCAAoC,EAAE,GAAG,EAAE;YAC9C,IAAA,uBAAe,EAAC,GAAG,UAAU,6BAA6B,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,kDAAkD,EAAE,GAAG,EAAE;YAC5D,IAAA,yBAAiB,EAAC,GAAG,UAAU,uBAAuB,EAAE;gBACtD,qFAAqF;gBACrF;YACI,iBAAS,CAAC,WAAW;;;;;;;;;;;;;;;;GAgB9B;aACI,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,wCAAwC,EAAE,GAAG,EAAE;YAClD,IAAA,yBAAiB,EAAC,GAAG,UAAU,uBAAuB,EAAE;gBACtD,0CAA0C;gBAC1C;;IAEJ;aACG,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,4DAA4D,EAAE,GAAG,EAAE;YACtE,IAAA,yBAAiB,EAAC,GAAG,UAAU,6BAA6B,EAAE;gBAC5D,0CAA0C;gBAC1C;YACI,iBAAS,CAAC,WAAW;;;GAG9B;aACI,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,gEAAgE,EAAE,GAAG,EAAE;YAC1E,IAAA,yBAAiB,EAAC,GAAG,UAAU,eAAe,EAAE;gBAC9C,6EAA6E;gBAC7E;;;;GAIL;gBACK,iCAAiC;aAClC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,qBAAqB,EAAE,KAAK,IAAI,EAAE;YACrC,MAAM,IAAA,qBAAa,EAAC,UAAU,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC5C,MAAM,IAAA,4BAAoB,EAAC,UAAU,EAAE,0BAA0B,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC7C,MAAM,IAAA,6BAAqB,EAAC,UAAU,EAAE,0BAA0B,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;QAEH,IAAA,aAAI,EAAC,iDAAiD,EAAE,GAAG,EAAE;YAC3D,IAAA,yBAAiB,EAAC,GAAG,UAAU,aAAa,EAAE;gBAC5C,wCAAwC;aACzC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { Integration } from '../../lib/Constants';\nimport {\n TEST_ARGS,\n checkEnvBuildPlugin,\n checkFileContents,\n checkFileExists,\n checkIfBuilds,\n checkIfRunsOnDevMode,\n checkIfRunsOnProdMode,\n checkPackageJson,\n createFile,\n createIsolatedTestEnv,\n getWizardCommand,\n modifyFile,\n} from '../utils';\nimport { afterAll, beforeAll, describe, expect, test } from 'vitest';\n\n//@ts-expect-error - clifty is ESM only\nimport { KEYS, withEnv } from 'clifty';\n\nconst SERVER_TEMPLATE = `import { createRequestHandler } from '@remix-run/express';\nimport { installGlobals } from '@remix-run/node';\nimport compression from 'compression';\nimport express from 'express';\nimport morgan from 'morgan';\n\ninstallGlobals();\n\nconst viteDevServer =\n process.env.NODE_ENV === 'production'\n ? undefined\n : await import('vite').then(vite =>\n vite.createServer({\n server: { middlewareMode: true },\n }),\n );\n\nconst app = express();\n\napp.use(compression());\napp.disable('x-powered-by');\n\nif (viteDevServer) {\n app.use(viteDevServer.middlewares);\n} else {\n app.use('/assets', express.static('build/client/assets', { immutable: true, maxAge: '1y' }));\n}\n\napp.use(express.static('build/client', { maxAge: '1h' }));\napp.use(morgan('tiny'));\n\napp.all(\n '*',\n createRequestHandler({\n build: viteDevServer\n ? () => viteDevServer.ssrLoadModule('virtual:remix/server-build')\n : await import('./build/server/index.js'),\n }),\n);\n\napp.listen(0, () => console.log('Express server listening'));\n`;\n\nasync function runWizardOnRemixProject(\n projectDir: string,\n integration: Integration,\n fileModificationFn?: (\n projectDir: string,\n integration: Integration,\n ) => unknown,\n): Promise<number> {\n const wizardInteraction = withEnv({\n cwd: projectDir,\n }).defineInteraction();\n\n if (fileModificationFn) {\n fileModificationFn(projectDir, integration);\n\n wizardInteraction\n .whenAsked('Do you want to continue anyway?')\n .respondWith(KEYS.ENTER);\n }\n\n return wizardInteraction\n .whenAsked('Please select your package manager.')\n .respondWith(KEYS.DOWN, KEYS.ENTER)\n .whenAsked('to track the performance of your application?', {\n timeout: 240_000, // package installation can take a while in CI\n })\n .respondWith(KEYS.ENTER)\n .whenAsked(\n 'to get a video-like reproduction of errors during a user session?',\n )\n .respondWith(KEYS.ENTER)\n .whenAsked('to send your application logs to Sentry?')\n .respondWith(KEYS.ENTER)\n .whenAsked('Do you want to create an example page')\n .respondWith(KEYS.ENTER)\n .whenAsked(\n 'Optionally add a project-scoped MCP server configuration for the Sentry MCP?',\n )\n .respondWith(KEYS.DOWN, KEYS.ENTER)\n .expectOutput(\n 'Sentry has been successfully configured for your Remix project',\n )\n .run(getWizardCommand(integration));\n}\n\ndescribe('Remix', () => {\n describe('with empty project', () => {\n const integration = Integration.remix;\n let wizardExitCode: number;\n\n const { projectDir, cleanup } = createIsolatedTestEnv('remix-test-app');\n\n beforeAll(async () => {\n wizardExitCode = await runWizardOnRemixProject(projectDir, integration);\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/remix');\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('instrumentation.server file exists', () => {\n checkFileExists(`${projectDir}/instrumentation.server.mjs`);\n });\n\n test('entry.client file contains Sentry initialization', () => {\n checkFileContents(`${projectDir}/app/entry.client.tsx`, [\n 'import { init, replayIntegration, browserTracingIntegration } from \"@sentry/remix\";',\n `init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",\n tracesSampleRate: 1,\n enableLogs: true,\n\n integrations: [browserTracingIntegration({\n useEffect,\n useLocation,\n useMatches\n }), replayIntegration({\n maskAllText: true,\n blockAllMedia: true\n })],\n\n replaysSessionSampleRate: 0.1,\n replaysOnErrorSampleRate: 1,\n sendDefaultPii: true\n})`,\n ]);\n });\n\n test('entry.server file contains Sentry code', () => {\n checkFileContents(`${projectDir}/app/entry.server.tsx`, [\n 'import * as Sentry from \"@sentry/remix\";',\n `export const handleError = Sentry.wrapHandleErrorWithSentry((error, { request }) => {\n // Custom handleError implementation\n});`,\n ]);\n });\n\n test('instrumentation.server file contains Sentry initialization', () => {\n checkFileContents(`${projectDir}/instrumentation.server.mjs`, [\n 'import * as Sentry from \"@sentry/remix\";',\n `Sentry.init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",\n tracesSampleRate: 1,\n enableLogs: true\n})`,\n ]);\n });\n\n test('root file contains Sentry ErrorBoundary and withSentry wrapper', () => {\n checkFileContents(`${projectDir}/app/root.tsx`, [\n 'import { captureRemixErrorBoundaryError, withSentry } from \"@sentry/remix\";',\n `export const ErrorBoundary = () => {\n const error = useRouteError();\n captureRemixErrorBoundaryError(error);\n return <div>Something went wrong</div>;\n};`,\n `export default withSentry(App);`,\n ]);\n });\n\n test('builds successfully', async () => {\n await checkIfBuilds(projectDir);\n });\n\n test('runs on dev mode correctly', async () => {\n await checkIfRunsOnDevMode(projectDir, 'to expose');\n });\n\n test('runs on prod mode correctly', async () => {\n await checkIfRunsOnProdMode(projectDir, '[remix-serve]');\n });\n });\n\n describe('with existing custom Express server', () => {\n const integration = Integration.remix;\n let wizardExitCode: number;\n\n const { projectDir, cleanup } = createIsolatedTestEnv('remix-test-app');\n\n beforeAll(async () => {\n wizardExitCode = await runWizardOnRemixProject(\n projectDir,\n integration,\n (projectDir) => {\n createFile(`${projectDir}/server.mjs`, SERVER_TEMPLATE);\n\n modifyFile(`${projectDir}/package.json`, {\n '\"start\": \"remix-serve ./build/server/index.js\"':\n '\"start\": \"node ./server.mjs\"',\n '\"dev\": \"remix vite:dev\"': '\"dev\": \"node ./server.mjs\"',\n });\n },\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('package.json is updated correctly', () => {\n checkPackageJson(projectDir, '@sentry/remix');\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('instrumentation.server file exists', () => {\n checkFileExists(`${projectDir}/instrumentation.server.mjs`);\n });\n\n test('entry.client file contains Sentry initialization', () => {\n checkFileContents(`${projectDir}/app/entry.client.tsx`, [\n 'import { init, replayIntegration, browserTracingIntegration } from \"@sentry/remix\";',\n `init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",\n tracesSampleRate: 1,\n enableLogs: true,\n\n integrations: [browserTracingIntegration({\n useEffect,\n useLocation,\n useMatches\n }), replayIntegration({\n maskAllText: true,\n blockAllMedia: true\n })],\n\n replaysSessionSampleRate: 0.1,\n replaysOnErrorSampleRate: 1,\n sendDefaultPii: true\n})`,\n ]);\n });\n\n test('entry.server file contains Sentry code', () => {\n checkFileContents(`${projectDir}/app/entry.server.tsx`, [\n 'import * as Sentry from \"@sentry/remix\";',\n `export const handleError = Sentry.wrapHandleErrorWithSentry((error, { request }) => {\n // Custom handleError implementation\n});`,\n ]);\n });\n\n test('instrumentation.server file contains Sentry initialization', () => {\n checkFileContents(`${projectDir}/instrumentation.server.mjs`, [\n 'import * as Sentry from \"@sentry/remix\";',\n `Sentry.init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",\n tracesSampleRate: 1,\n enableLogs: true\n})`,\n ]);\n });\n\n test('root file contains Sentry ErrorBoundary and withSentry wrapper', () => {\n checkFileContents(`${projectDir}/app/root.tsx`, [\n 'import { captureRemixErrorBoundaryError, withSentry } from \"@sentry/remix\";',\n `export const ErrorBoundary = () => {\n const error = useRouteError();\n captureRemixErrorBoundaryError(error);\n return <div>Something went wrong</div>;\n};`,\n `export default withSentry(App);`,\n ]);\n });\n\n test('builds successfully', async () => {\n await checkIfBuilds(projectDir);\n });\n\n test('runs on dev mode correctly', async () => {\n await checkIfRunsOnDevMode(projectDir, 'Express server listening');\n });\n\n test('runs on prod mode correctly', async () => {\n await checkIfRunsOnProdMode(projectDir, 'Express server listening');\n });\n\n test('server.mjs contains instrumentation file import', () => {\n checkFileContents(`${projectDir}/server.mjs`, [\n \"import './instrumentation.server.mjs';\",\n ]);\n });\n });\n});\n"]}
@@ -55,17 +55,35 @@ export async function handleError({ error, event }) {
55
55
  vitest_1.describe.sequential('Sveltekit', () => {
56
56
  (0, vitest_1.describe)('without existing hooks', () => {
57
57
  const integration = Constants_1.Integration.sveltekit;
58
- const projectDir = path.resolve(__dirname, '../test-applications/sveltekit-hooks-test-app');
58
+ const { projectDir, cleanup } = (0, utils_1.createIsolatedTestEnv)('sveltekit-hooks-test-app');
59
59
  (0, vitest_1.beforeAll)(async () => {
60
- (0, utils_1.initGit)(projectDir);
61
- (0, utils_1.revertLocalChanges)(projectDir);
62
60
  await runWizardOnSvelteKitProject(projectDir, integration);
63
61
  });
64
62
  (0, vitest_1.afterAll)(() => {
65
- (0, utils_1.revertLocalChanges)(projectDir);
66
- (0, utils_1.cleanupGit)(projectDir);
63
+ cleanup();
64
+ });
65
+ (0, vitest_1.test)('has the correct package.json', () => {
66
+ (0, utils_1.checkPackageJson)(projectDir, '@sentry/sveltekit');
67
+ });
68
+ (0, vitest_1.test)('has the correct .env.sentry-build-plugin', () => {
69
+ (0, utils_1.checkEnvBuildPlugin)(projectDir);
70
+ });
71
+ (0, vitest_1.test)('vite.config contains sentry plugin', () => {
72
+ (0, utils_1.checkFileContents)(path.resolve(projectDir, 'vite.config.ts'), `plugins: [sentrySvelteKit({`);
73
+ });
74
+ (0, vitest_1.test)('hook files created', () => {
75
+ (0, utils_1.checkFileExists)(path.resolve(projectDir, 'src/hooks.server.ts'));
76
+ (0, utils_1.checkFileExists)(path.resolve(projectDir, 'src/hooks.client.ts'));
77
+ });
78
+ (0, vitest_1.test)('builds successfully', async () => {
79
+ await (0, utils_1.checkIfBuilds)(projectDir);
80
+ });
81
+ (0, vitest_1.test)('runs on dev mode correctly', async () => {
82
+ await (0, utils_1.checkIfRunsOnDevMode)(projectDir, 'ready in');
83
+ });
84
+ (0, vitest_1.test)('runs on prod mode correctly', async () => {
85
+ await (0, utils_1.checkIfRunsOnProdMode)(projectDir, 'to expose', 'preview');
67
86
  });
68
- checkSvelteKitProject(projectDir, integration);
69
87
  (0, vitest_1.test)('hooks.client.ts contains sentry', () => {
70
88
  (0, utils_1.checkFileContents)(path.resolve(projectDir, 'src/hooks.client.ts'), [
71
89
  `import * as Sentry from '@sentry/sveltekit';`,
@@ -127,20 +145,38 @@ vitest_1.describe.sequential('Sveltekit', () => {
127
145
  });
128
146
  (0, vitest_1.describe)('with existing hooks', () => {
129
147
  const integration = Constants_1.Integration.sveltekit;
130
- const projectDir = path.resolve(__dirname, '../test-applications/sveltekit-hooks-test-app');
148
+ const { projectDir, cleanup } = (0, utils_1.createIsolatedTestEnv)('sveltekit-hooks-test-app');
131
149
  (0, vitest_1.beforeAll)(async () => {
132
- (0, utils_1.initGit)(projectDir);
133
- (0, utils_1.revertLocalChanges)(projectDir);
134
150
  await runWizardOnSvelteKitProject(projectDir, integration, (projectDir) => {
135
151
  (0, utils_1.createFile)(path.resolve(projectDir, 'src/hooks.server.ts'), SERVER_HOOK_TEMPLATE);
136
152
  (0, utils_1.createFile)(path.resolve(projectDir, 'src/hooks.client.ts'), CLIENT_HOOK_TEMPLATE);
137
153
  });
138
154
  });
139
155
  (0, vitest_1.afterAll)(() => {
140
- (0, utils_1.revertLocalChanges)(projectDir);
141
- (0, utils_1.cleanupGit)(projectDir);
156
+ cleanup();
157
+ });
158
+ (0, vitest_1.test)('has the correct package.json', () => {
159
+ (0, utils_1.checkPackageJson)(projectDir, '@sentry/sveltekit');
160
+ });
161
+ (0, vitest_1.test)('has the correct .env.sentry-build-plugin', () => {
162
+ (0, utils_1.checkEnvBuildPlugin)(projectDir);
163
+ });
164
+ (0, vitest_1.test)('vite.config contains sentry plugin', () => {
165
+ (0, utils_1.checkFileContents)(path.resolve(projectDir, 'vite.config.ts'), `plugins: [sentrySvelteKit({`);
166
+ });
167
+ (0, vitest_1.test)('hook files created', () => {
168
+ (0, utils_1.checkFileExists)(path.resolve(projectDir, 'src/hooks.server.ts'));
169
+ (0, utils_1.checkFileExists)(path.resolve(projectDir, 'src/hooks.client.ts'));
170
+ });
171
+ (0, vitest_1.test)('builds successfully', async () => {
172
+ await (0, utils_1.checkIfBuilds)(projectDir);
173
+ });
174
+ (0, vitest_1.test)('runs on dev mode correctly', async () => {
175
+ await (0, utils_1.checkIfRunsOnDevMode)(projectDir, 'ready in');
176
+ });
177
+ (0, vitest_1.test)('runs on prod mode correctly', async () => {
178
+ await (0, utils_1.checkIfRunsOnProdMode)(projectDir, 'to expose', 'preview');
142
179
  });
143
- checkSvelteKitProject(projectDir, integration);
144
180
  // These are removed from the common tests as the content is different
145
181
  // when the hooks are merged instead of created from the template
146
182
  (0, vitest_1.test)('hooks.client.ts contains sentry instrumentation', () => {
@@ -202,28 +238,4 @@ async function runWizardOnSvelteKitProject(projectDir, integration, fileModifica
202
238
  .expectOutput('Successfully installed the Sentry SvelteKit SDK!');
203
239
  await wizardInteraction.run((0, utils_1.getWizardCommand)(Constants_1.Integration.sveltekit));
204
240
  }
205
- function checkSvelteKitProject(projectDir, integration, options) {
206
- (0, vitest_1.test)('should have the correct package.json', () => {
207
- (0, utils_1.checkPackageJson)(projectDir, integration);
208
- });
209
- (0, vitest_1.test)('should have the correct .env.sentry-build-plugin', () => {
210
- (0, utils_1.checkEnvBuildPlugin)(projectDir);
211
- });
212
- (0, vitest_1.test)('vite.config contains sentry plugin', () => {
213
- (0, utils_1.checkFileContents)(path.resolve(projectDir, 'vite.config.ts'), `plugins: [sentrySvelteKit({`);
214
- });
215
- (0, vitest_1.test)('hook files created', () => {
216
- (0, utils_1.checkFileExists)(path.resolve(projectDir, 'src/hooks.server.ts'));
217
- (0, utils_1.checkFileExists)(path.resolve(projectDir, 'src/hooks.client.ts'));
218
- });
219
- (0, vitest_1.test)('builds successfully', async () => {
220
- await (0, utils_1.checkIfBuilds)(projectDir);
221
- });
222
- (0, vitest_1.test)('runs on dev mode correctly', async () => {
223
- await (0, utils_1.checkIfRunsOnDevMode)(projectDir, options?.devModeExpectedOutput || 'ready in');
224
- });
225
- (0, vitest_1.test)('runs on prod mode correctly', async () => {
226
- await (0, utils_1.checkIfRunsOnProdMode)(projectDir, options?.prodModeExpectedOutput || 'to expose', 'preview');
227
- });
228
- }
229
241
  //# sourceMappingURL=sveltekit-hooks.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sveltekit-hooks.test.js","sourceRoot":"","sources":["../../../e2e-tests/tests/sveltekit-hooks.test.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'node:path';\nimport { Integration } from '../../lib/Constants';\nimport {\n TEST_ARGS,\n checkEnvBuildPlugin,\n checkFileContents,\n checkFileExists,\n checkIfBuilds,\n checkIfRunsOnDevMode,\n checkIfRunsOnProdMode,\n checkPackageJson,\n cleanupGit,\n createFile,\n getWizardCommand,\n initGit,\n revertLocalChanges,\n} from '../utils';\nimport { afterAll, beforeAll, describe, test } from 'vitest';\n//@ts-expect-error - clifty is ESM only\nimport { KEYS, withEnv } from 'clifty';\n\nconst SERVER_HOOK_TEMPLATE = `import type { Handle } from '@sveltejs/kit';\n\nexport const handle: Handle = async ({ event, resolve }) => {\n\tif (event.url.pathname.startsWith('/custom')) {\n\t\treturn new Response('custom response');\n\t}\n\n\tconst response = await resolve(event);\n\treturn response;\n};\n`;\nconst CLIENT_HOOK_TEMPLATE = `\nexport async function handleError({ error, event }) {\n // you can capture the \\`error\\` and \\`event\\` from the client\n // but it only runs if the unexpected error comes from \\`+ page.ts\\`\n console.log(error)\n\n return {\n // don't show sensitive data to the user\n message: 'Yikes! 💩',\n }\n}\n`;\n\ndescribe.sequential('Sveltekit', () => {\n describe('without existing hooks', () => {\n const integration = Integration.sveltekit;\n const projectDir = path.resolve(\n __dirname,\n '../test-applications/sveltekit-hooks-test-app',\n );\n\n beforeAll(async () => {\n initGit(projectDir);\n revertLocalChanges(projectDir);\n\n await runWizardOnSvelteKitProject(projectDir, integration);\n });\n\n afterAll(() => {\n revertLocalChanges(projectDir);\n cleanupGit(projectDir);\n });\n\n checkSvelteKitProject(projectDir, integration);\n\n test('hooks.client.ts contains sentry', () => {\n checkFileContents(path.resolve(projectDir, 'src/hooks.client.ts'), [\n `import * as Sentry from '@sentry/sveltekit';`,\n `Sentry.init({\n dsn: '${TEST_ARGS.PROJECT_DSN}',\n\n tracesSampleRate: 1.0,\n\n // Enable logs to be sent to Sentry\n enableLogs: true,\n\n // This sets the sample rate to be 10%. You may want this to be 100% while\n // in development and sample at a lower rate in production\n replaysSessionSampleRate: 0.1,\n\n // If the entire session is not sampled, use the below sample rate to sample\n // sessions when an error occurs.\n replaysOnErrorSampleRate: 1.0,\n\n // If you don't want to use Session Replay, just remove the line below:\n integrations: [replayIntegration()],\n\n // Enable sending user PII (Personally Identifiable Information)\n // https://docs.sentry.io/platforms/javascript/guides/sveltekit/configuration/options/#sendDefaultPii\n sendDefaultPii: true,\n});`,\n 'export const handleError = handleErrorWithSentry(',\n ]);\n });\n\n test('hooks.server.ts contains sentry', () => {\n checkFileContents(path.resolve(projectDir, 'src/hooks.server.ts'), [\n `import * as Sentry from '@sentry/sveltekit';`,\n `Sentry.init({\n dsn: '${TEST_ARGS.PROJECT_DSN}',\n\n tracesSampleRate: 1.0,\n\n // Enable logs to be sent to Sentry\n enableLogs: true,\n\n\n // Enable sending user PII (Personally Identifiable Information)\n // https://docs.sentry.io/platforms/javascript/guides/sveltekit/configuration/options/#sendDefaultPii\n sendDefaultPii: true,\n\n // uncomment the line below to enable Spotlight (https://spotlightjs.com)\n // spotlight: import.meta.env.DEV,\n});`,\n 'export const handleError = handleErrorWithSentry();',\n ]);\n });\n\n test('creates an example route and page', () => {\n checkFileExists(\n path.resolve(projectDir, 'src/routes/sentry-example-page/+page.svelte'),\n );\n checkFileContents(\n path.resolve(projectDir, 'src/routes/sentry-example-page/+page.svelte'),\n // Svelte <5 specific syntax\n ['let hasSentError = false;', 'on:click={getSentryData}'],\n );\n checkFileExists(\n path.resolve(projectDir, 'src/routes/sentry-example-page/+server.js'),\n );\n });\n });\n\n describe('with existing hooks', () => {\n const integration = Integration.sveltekit;\n const projectDir = path.resolve(\n __dirname,\n '../test-applications/sveltekit-hooks-test-app',\n );\n\n beforeAll(async () => {\n initGit(projectDir);\n revertLocalChanges(projectDir);\n\n await runWizardOnSvelteKitProject(\n projectDir,\n integration,\n (projectDir) => {\n createFile(\n path.resolve(projectDir, 'src/hooks.server.ts'),\n SERVER_HOOK_TEMPLATE,\n );\n\n createFile(\n path.resolve(projectDir, 'src/hooks.client.ts'),\n CLIENT_HOOK_TEMPLATE,\n );\n },\n );\n });\n\n afterAll(() => {\n revertLocalChanges(projectDir);\n cleanupGit(projectDir);\n });\n\n checkSvelteKitProject(projectDir, integration);\n\n // These are removed from the common tests as the content is different\n // when the hooks are merged instead of created from the template\n test('hooks.client.ts contains sentry instrumentation', () => {\n checkFileContents(path.resolve(projectDir, 'src/hooks.client.ts'), [\n `import * as Sentry from '@sentry/sveltekit';`,\n `Sentry.init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",\n tracesSampleRate: 1,\n replaysSessionSampleRate: 0.1,\n replaysOnErrorSampleRate: 1,\n integrations: [Sentry.replayIntegration()],\n enableLogs: true,\n sendDefaultPii: true\n})`,\n 'export const handleError = Sentry.handleErrorWithSentry(',\n ]);\n });\n\n test('hooks.server.ts contains sentry init', () => {\n checkFileContents(path.resolve(projectDir, 'src/hooks.server.ts'), [\n `import * as Sentry from '@sentry/sveltekit';`,\n `Sentry.init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",\n tracesSampleRate: 1,\n enableLogs: true,\n sendDefaultPii: true\n})`,\n 'export const handleError = Sentry.handleErrorWithSentry();',\n ]);\n });\n });\n});\n\nasync function runWizardOnSvelteKitProject(\n projectDir: string,\n integration: Integration,\n fileModificationFn?: (\n projectDir: string,\n integration: Integration,\n ) => unknown,\n) {\n const wizardInteraction = withEnv({\n cwd: projectDir,\n }).defineInteraction();\n\n if (fileModificationFn) {\n fileModificationFn(projectDir, integration);\n\n wizardInteraction\n .whenAsked('Do you want to continue anyway?')\n .respondWith(KEYS.ENTER);\n }\n\n wizardInteraction\n .whenAsked(\"It seems you're using a SvelteKit version\")\n .respondWith(KEYS.DOWN, KEYS.DOWN, KEYS.ENTER)\n .whenAsked('Please select your package manager.')\n .respondWith(KEYS.DOWN, KEYS.ENTER)\n .whenAsked('Do you want to enable Tracing', {\n timeout: 90_000, // package installation can take a while in CI\n })\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 create an example page')\n .respondWith(KEYS.ENTER)\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 SvelteKit SDK!');\n\n await wizardInteraction.run(getWizardCommand(Integration.sveltekit));\n}\n\nfunction checkSvelteKitProject(\n projectDir: string,\n integration: Integration,\n options?: {\n devModeExpectedOutput: string;\n prodModeExpectedOutput: string;\n },\n) {\n test('should have the correct package.json', () => {\n checkPackageJson(projectDir, integration);\n });\n\n test('should have the correct .env.sentry-build-plugin', () => {\n checkEnvBuildPlugin(projectDir);\n });\n\n test('vite.config contains sentry plugin', () => {\n checkFileContents(\n path.resolve(projectDir, 'vite.config.ts'),\n `plugins: [sentrySvelteKit({`,\n );\n });\n\n test('hook files created', () => {\n checkFileExists(path.resolve(projectDir, 'src/hooks.server.ts'));\n checkFileExists(path.resolve(projectDir, 'src/hooks.client.ts'));\n });\n\n test('builds successfully', async () => {\n await checkIfBuilds(projectDir);\n });\n\n test('runs on dev mode correctly', async () => {\n await checkIfRunsOnDevMode(\n projectDir,\n options?.devModeExpectedOutput || 'ready in',\n );\n });\n\n test('runs on prod mode correctly', async () => {\n await checkIfRunsOnProdMode(\n projectDir,\n options?.prodModeExpectedOutput || 'to expose',\n 'preview',\n );\n });\n}\n"]}
1
+ {"version":3,"file":"sveltekit-hooks.test.js","sourceRoot":"","sources":["../../../e2e-tests/tests/sveltekit-hooks.test.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'node:path';\nimport { Integration } from '../../lib/Constants';\nimport {\n TEST_ARGS,\n checkEnvBuildPlugin,\n checkFileContents,\n checkFileExists,\n checkIfBuilds,\n checkIfRunsOnDevMode,\n checkIfRunsOnProdMode,\n checkPackageJson,\n createFile,\n createIsolatedTestEnv,\n getWizardCommand,\n} from '../utils';\nimport { afterAll, beforeAll, describe, test } from 'vitest';\n//@ts-expect-error - clifty is ESM only\nimport { KEYS, withEnv } from 'clifty';\n\nconst SERVER_HOOK_TEMPLATE = `import type { Handle } from '@sveltejs/kit';\n\nexport const handle: Handle = async ({ event, resolve }) => {\n\tif (event.url.pathname.startsWith('/custom')) {\n\t\treturn new Response('custom response');\n\t}\n\n\tconst response = await resolve(event);\n\treturn response;\n};\n`;\nconst CLIENT_HOOK_TEMPLATE = `\nexport async function handleError({ error, event }) {\n // you can capture the \\`error\\` and \\`event\\` from the client\n // but it only runs if the unexpected error comes from \\`+ page.ts\\`\n console.log(error)\n\n return {\n // don't show sensitive data to the user\n message: 'Yikes! 💩',\n }\n}\n`;\n\ndescribe.sequential('Sveltekit', () => {\n describe('without existing hooks', () => {\n const integration = Integration.sveltekit;\n\n const { projectDir, cleanup } = createIsolatedTestEnv(\n 'sveltekit-hooks-test-app',\n );\n\n beforeAll(async () => {\n await runWizardOnSvelteKitProject(projectDir, integration);\n });\n\n afterAll(() => {\n cleanup();\n });\n\n test('has the correct package.json', () => {\n checkPackageJson(projectDir, '@sentry/sveltekit');\n });\n\n test('has the correct .env.sentry-build-plugin', () => {\n checkEnvBuildPlugin(projectDir);\n });\n\n test('vite.config contains sentry plugin', () => {\n checkFileContents(\n path.resolve(projectDir, 'vite.config.ts'),\n `plugins: [sentrySvelteKit({`,\n );\n });\n\n test('hook files created', () => {\n checkFileExists(path.resolve(projectDir, 'src/hooks.server.ts'));\n checkFileExists(path.resolve(projectDir, 'src/hooks.client.ts'));\n });\n\n test('builds successfully', async () => {\n await checkIfBuilds(projectDir);\n });\n\n test('runs on dev mode correctly', async () => {\n await checkIfRunsOnDevMode(projectDir, 'ready in');\n });\n\n test('runs on prod mode correctly', async () => {\n await checkIfRunsOnProdMode(projectDir, 'to expose', 'preview');\n });\n\n test('hooks.client.ts contains sentry', () => {\n checkFileContents(path.resolve(projectDir, 'src/hooks.client.ts'), [\n `import * as Sentry from '@sentry/sveltekit';`,\n `Sentry.init({\n dsn: '${TEST_ARGS.PROJECT_DSN}',\n\n tracesSampleRate: 1.0,\n\n // Enable logs to be sent to Sentry\n enableLogs: true,\n\n // This sets the sample rate to be 10%. You may want this to be 100% while\n // in development and sample at a lower rate in production\n replaysSessionSampleRate: 0.1,\n\n // If the entire session is not sampled, use the below sample rate to sample\n // sessions when an error occurs.\n replaysOnErrorSampleRate: 1.0,\n\n // If you don't want to use Session Replay, just remove the line below:\n integrations: [replayIntegration()],\n\n // Enable sending user PII (Personally Identifiable Information)\n // https://docs.sentry.io/platforms/javascript/guides/sveltekit/configuration/options/#sendDefaultPii\n sendDefaultPii: true,\n});`,\n 'export const handleError = handleErrorWithSentry(',\n ]);\n });\n\n test('hooks.server.ts contains sentry', () => {\n checkFileContents(path.resolve(projectDir, 'src/hooks.server.ts'), [\n `import * as Sentry from '@sentry/sveltekit';`,\n `Sentry.init({\n dsn: '${TEST_ARGS.PROJECT_DSN}',\n\n tracesSampleRate: 1.0,\n\n // Enable logs to be sent to Sentry\n enableLogs: true,\n\n\n // Enable sending user PII (Personally Identifiable Information)\n // https://docs.sentry.io/platforms/javascript/guides/sveltekit/configuration/options/#sendDefaultPii\n sendDefaultPii: true,\n\n // uncomment the line below to enable Spotlight (https://spotlightjs.com)\n // spotlight: import.meta.env.DEV,\n});`,\n 'export const handleError = handleErrorWithSentry();',\n ]);\n });\n\n test('creates an example route and page', () => {\n checkFileExists(\n path.resolve(projectDir, 'src/routes/sentry-example-page/+page.svelte'),\n );\n checkFileContents(\n path.resolve(projectDir, 'src/routes/sentry-example-page/+page.svelte'),\n // Svelte <5 specific syntax\n ['let hasSentError = false;', 'on:click={getSentryData}'],\n );\n checkFileExists(\n path.resolve(projectDir, 'src/routes/sentry-example-page/+server.js'),\n );\n });\n });\n\n describe('with existing hooks', () => {\n const integration = Integration.sveltekit;\n\n const { projectDir, cleanup } = createIsolatedTestEnv(\n 'sveltekit-hooks-test-app',\n );\n\n beforeAll(async () => {\n await runWizardOnSvelteKitProject(\n projectDir,\n integration,\n (projectDir) => {\n createFile(\n path.resolve(projectDir, 'src/hooks.server.ts'),\n SERVER_HOOK_TEMPLATE,\n );\n\n createFile(\n path.resolve(projectDir, 'src/hooks.client.ts'),\n CLIENT_HOOK_TEMPLATE,\n );\n },\n );\n });\n\n afterAll(() => {\n cleanup();\n });\n\n test('has the correct package.json', () => {\n checkPackageJson(projectDir, '@sentry/sveltekit');\n });\n\n test('has the correct .env.sentry-build-plugin', () => {\n checkEnvBuildPlugin(projectDir);\n });\n\n test('vite.config contains sentry plugin', () => {\n checkFileContents(\n path.resolve(projectDir, 'vite.config.ts'),\n `plugins: [sentrySvelteKit({`,\n );\n });\n\n test('hook files created', () => {\n checkFileExists(path.resolve(projectDir, 'src/hooks.server.ts'));\n checkFileExists(path.resolve(projectDir, 'src/hooks.client.ts'));\n });\n\n test('builds successfully', async () => {\n await checkIfBuilds(projectDir);\n });\n\n test('runs on dev mode correctly', async () => {\n await checkIfRunsOnDevMode(projectDir, 'ready in');\n });\n\n test('runs on prod mode correctly', async () => {\n await checkIfRunsOnProdMode(projectDir, 'to expose', 'preview');\n });\n\n // These are removed from the common tests as the content is different\n // when the hooks are merged instead of created from the template\n test('hooks.client.ts contains sentry instrumentation', () => {\n checkFileContents(path.resolve(projectDir, 'src/hooks.client.ts'), [\n `import * as Sentry from '@sentry/sveltekit';`,\n `Sentry.init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",\n tracesSampleRate: 1,\n replaysSessionSampleRate: 0.1,\n replaysOnErrorSampleRate: 1,\n integrations: [Sentry.replayIntegration()],\n enableLogs: true,\n sendDefaultPii: true\n})`,\n 'export const handleError = Sentry.handleErrorWithSentry(',\n ]);\n });\n\n test('hooks.server.ts contains sentry init', () => {\n checkFileContents(path.resolve(projectDir, 'src/hooks.server.ts'), [\n `import * as Sentry from '@sentry/sveltekit';`,\n `Sentry.init({\n dsn: \"${TEST_ARGS.PROJECT_DSN}\",\n tracesSampleRate: 1,\n enableLogs: true,\n sendDefaultPii: true\n})`,\n 'export const handleError = Sentry.handleErrorWithSentry();',\n ]);\n });\n });\n});\n\nasync function runWizardOnSvelteKitProject(\n projectDir: string,\n integration: Integration,\n fileModificationFn?: (\n projectDir: string,\n integration: Integration,\n ) => unknown,\n) {\n const wizardInteraction = withEnv({\n cwd: projectDir,\n }).defineInteraction();\n\n if (fileModificationFn) {\n fileModificationFn(projectDir, integration);\n\n wizardInteraction\n .whenAsked('Do you want to continue anyway?')\n .respondWith(KEYS.ENTER);\n }\n\n wizardInteraction\n .whenAsked(\"It seems you're using a SvelteKit version\")\n .respondWith(KEYS.DOWN, KEYS.DOWN, KEYS.ENTER)\n .whenAsked('Please select your package manager.')\n .respondWith(KEYS.DOWN, KEYS.ENTER)\n .whenAsked('Do you want to enable Tracing', {\n timeout: 90_000, // package installation can take a while in CI\n })\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 create an example page')\n .respondWith(KEYS.ENTER)\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 SvelteKit SDK!');\n\n await wizardInteraction.run(getWizardCommand(Integration.sveltekit));\n}\n"]}