@sentry/wizard 6.10.0 → 6.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/ci-ensure-runtime-loaded.sh +82 -0
- package/dist/e2e-tests/tests/angular-17.test.js +72 -82
- package/dist/e2e-tests/tests/angular-17.test.js.map +1 -1
- package/dist/e2e-tests/tests/angular-19.test.js +71 -80
- package/dist/e2e-tests/tests/angular-19.test.js.map +1 -1
- package/dist/e2e-tests/tests/cloudflare-worker.test.d.ts +1 -0
- package/dist/e2e-tests/tests/cloudflare-worker.test.js +64 -0
- package/dist/e2e-tests/tests/cloudflare-worker.test.js.map +1 -0
- package/dist/e2e-tests/tests/cloudflare-wrangler-sourcemaps.test.js +2 -5
- package/dist/e2e-tests/tests/cloudflare-wrangler-sourcemaps.test.js.map +1 -1
- package/dist/e2e-tests/tests/expo.test.js +36 -61
- package/dist/e2e-tests/tests/expo.test.js.map +1 -1
- package/dist/e2e-tests/tests/flutter.test.js +63 -70
- package/dist/e2e-tests/tests/flutter.test.js.map +1 -1
- package/dist/e2e-tests/tests/help-message.test.js +2 -2
- package/dist/e2e-tests/tests/help-message.test.js.map +1 -1
- package/dist/e2e-tests/tests/nextjs-14.test.js +48 -76
- package/dist/e2e-tests/tests/nextjs-14.test.js.map +1 -1
- package/dist/e2e-tests/tests/nextjs-15.test.js +89 -99
- package/dist/e2e-tests/tests/nextjs-15.test.js.map +1 -1
- package/dist/e2e-tests/tests/nextjs-16.test.js +48 -45
- package/dist/e2e-tests/tests/nextjs-16.test.js.map +1 -1
- package/dist/e2e-tests/tests/nuxt-3.test.js +45 -58
- package/dist/e2e-tests/tests/nuxt-3.test.js.map +1 -1
- package/dist/e2e-tests/tests/nuxt-4.test.js +59 -73
- package/dist/e2e-tests/tests/nuxt-4.test.js.map +1 -1
- package/dist/e2e-tests/tests/pnpm-workspace.test.js +4 -7
- package/dist/e2e-tests/tests/pnpm-workspace.test.js.map +1 -1
- package/dist/e2e-tests/tests/react-native.test.js +44 -80
- package/dist/e2e-tests/tests/react-native.test.js.map +1 -1
- package/dist/e2e-tests/tests/react-router.test.js +163 -145
- package/dist/e2e-tests/tests/react-router.test.js.map +1 -1
- package/dist/e2e-tests/tests/remix.test.js +162 -132
- package/dist/e2e-tests/tests/remix.test.js.map +1 -1
- package/dist/e2e-tests/tests/sveltekit-hooks.test.js +48 -36
- package/dist/e2e-tests/tests/sveltekit-hooks.test.js.map +1 -1
- package/dist/e2e-tests/tests/sveltekit-tracing.test.js +3 -6
- package/dist/e2e-tests/tests/sveltekit-tracing.test.js.map +1 -1
- package/dist/e2e-tests/utils/index.d.ts +15 -43
- package/dist/e2e-tests/utils/index.js +95 -185
- package/dist/e2e-tests/utils/index.js.map +1 -1
- package/dist/get-e2e-test-matrix.mjs +11 -0
- package/dist/lib/Constants.d.ts +1 -0
- package/dist/lib/Constants.js +5 -0
- package/dist/lib/Constants.js.map +1 -1
- package/dist/src/android/android-wizard.js +2 -4
- package/dist/src/android/android-wizard.js.map +1 -1
- package/dist/src/angular/angular-wizard.js +4 -6
- package/dist/src/angular/angular-wizard.js.map +1 -1
- package/dist/src/angular/sdk-setup.js +1 -1
- package/dist/src/angular/sdk-setup.js.map +1 -1
- package/dist/src/apple/apple-wizard.js +2 -4
- package/dist/src/apple/apple-wizard.js.map +1 -1
- package/dist/src/cloudflare/cloudflare-wizard.d.ts +3 -0
- package/dist/src/cloudflare/cloudflare-wizard.js +99 -0
- package/dist/src/cloudflare/cloudflare-wizard.js.map +1 -0
- package/dist/src/cloudflare/sdk-setup.d.ts +7 -0
- package/dist/src/cloudflare/sdk-setup.js +47 -0
- package/dist/src/cloudflare/sdk-setup.js.map +1 -0
- package/dist/src/cloudflare/templates.d.ts +4 -0
- package/dist/src/cloudflare/templates.js +44 -0
- package/dist/src/cloudflare/templates.js.map +1 -0
- package/dist/src/cloudflare/wrangler/create-wrangler-config.d.ts +4 -0
- package/dist/src/cloudflare/wrangler/create-wrangler-config.js +27 -0
- package/dist/src/cloudflare/wrangler/create-wrangler-config.js.map +1 -0
- package/dist/src/cloudflare/wrangler/ensure-wrangler-config.d.ts +4 -0
- package/dist/src/cloudflare/wrangler/ensure-wrangler-config.js +25 -0
- package/dist/src/cloudflare/wrangler/ensure-wrangler-config.js.map +1 -0
- package/dist/src/cloudflare/wrangler/find-wrangler-config.d.ts +4 -0
- package/dist/src/cloudflare/wrangler/find-wrangler-config.js +23 -0
- package/dist/src/cloudflare/wrangler/find-wrangler-config.js.map +1 -0
- package/dist/src/cloudflare/wrangler/get-entry-point-from-wrangler-config.d.ts +6 -0
- package/dist/src/cloudflare/wrangler/get-entry-point-from-wrangler-config.js +52 -0
- package/dist/src/cloudflare/wrangler/get-entry-point-from-wrangler-config.js.map +1 -0
- package/dist/src/cloudflare/wrangler/update-wrangler-config.d.ts +17 -0
- package/dist/src/cloudflare/wrangler/update-wrangler-config.js +173 -0
- package/dist/src/cloudflare/wrangler/update-wrangler-config.js.map +1 -0
- package/dist/src/cloudflare/wrap-worker.d.ts +32 -0
- package/dist/src/cloudflare/wrap-worker.js +109 -0
- package/dist/src/cloudflare/wrap-worker.js.map +1 -0
- package/dist/src/flutter/flutter-wizard.js +3 -6
- package/dist/src/flutter/flutter-wizard.js.map +1 -1
- package/dist/src/nextjs/nextjs-wizard.js +0 -2
- package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
- package/dist/src/nuxt/nuxt-wizard.js +3 -5
- package/dist/src/nuxt/nuxt-wizard.js.map +1 -1
- package/dist/src/react-native/react-native-wizard.js +2 -4
- package/dist/src/react-native/react-native-wizard.js.map +1 -1
- package/dist/src/react-router/react-router-wizard.js +3 -5
- package/dist/src/react-router/react-router-wizard.js.map +1 -1
- package/dist/src/react-router/sdk-setup.d.ts +1 -1
- package/dist/src/react-router/sdk-setup.js +3 -4
- package/dist/src/react-router/sdk-setup.js.map +1 -1
- package/dist/src/remix/remix-wizard.js +2 -4
- package/dist/src/remix/remix-wizard.js.map +1 -1
- package/dist/src/run.d.ts +1 -1
- package/dist/src/run.js +5 -0
- package/dist/src/run.js.map +1 -1
- package/dist/src/sveltekit/sveltekit-wizard.js +2 -4
- package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
- package/dist/src/utils/abort-if-sportlight-not-supported.d.ts +5 -0
- package/dist/src/utils/abort-if-sportlight-not-supported.js +40 -0
- package/dist/src/utils/abort-if-sportlight-not-supported.js.map +1 -0
- package/dist/src/utils/ast-utils.d.ts +1 -1
- package/dist/src/utils/ast-utils.js.map +1 -1
- package/dist/src/utils/clack/index.d.ts +2 -2
- package/dist/src/utils/clack/index.js.map +1 -1
- package/dist/src/utils/clack/mcp-config.js +117 -59
- package/dist/src/utils/clack/mcp-config.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/test/angular/angular-wizard.test.js +2 -4
- package/dist/test/angular/angular-wizard.test.js.map +1 -1
- package/dist/test/cloudflare/create-wrangler-config.test.d.ts +1 -0
- package/dist/test/cloudflare/create-wrangler-config.test.js +48 -0
- package/dist/test/cloudflare/create-wrangler-config.test.js.map +1 -0
- package/dist/test/cloudflare/ensure-wrangler-config.test.d.ts +1 -0
- package/dist/test/cloudflare/ensure-wrangler-config.test.js +61 -0
- package/dist/test/cloudflare/ensure-wrangler-config.test.js.map +1 -0
- package/dist/test/cloudflare/find-wrangler-config.test.d.ts +1 -0
- package/dist/test/cloudflare/find-wrangler-config.test.js +77 -0
- package/dist/test/cloudflare/find-wrangler-config.test.js.map +1 -0
- package/dist/test/cloudflare/get-entry-point-from-wrangler-config.test.d.ts +1 -0
- package/dist/test/cloudflare/get-entry-point-from-wrangler-config.test.js +81 -0
- package/dist/test/cloudflare/get-entry-point-from-wrangler-config.test.js.map +1 -0
- package/dist/test/cloudflare/sdk-setup.test.d.ts +1 -0
- package/dist/test/cloudflare/sdk-setup.test.js +152 -0
- package/dist/test/cloudflare/sdk-setup.test.js.map +1 -0
- package/dist/test/cloudflare/templates.test.d.ts +1 -0
- package/dist/test/cloudflare/templates.test.js +68 -0
- package/dist/test/cloudflare/templates.test.js.map +1 -0
- package/dist/test/cloudflare/update-wrangler-config.test.d.ts +1 -0
- package/dist/test/cloudflare/update-wrangler-config.test.js +216 -0
- package/dist/test/cloudflare/update-wrangler-config.test.js.map +1 -0
- package/dist/test/cloudflare/wrap-worker.test.d.ts +1 -0
- package/dist/test/cloudflare/wrap-worker.test.js +143 -0
- package/dist/test/cloudflare/wrap-worker.test.js.map +1 -0
- package/dist/test/react-router/sdk-setup.test.js +2 -2
- package/dist/test/react-router/sdk-setup.test.js.map +1 -1
- package/dist/test/utils/clack/mcp-config.test.js +176 -51
- package/dist/test/utils/clack/mcp-config.test.js.map +1 -1
- package/package.json +5 -4
|
@@ -37,6 +37,7 @@ vitest_1.vi.mock('../../../src/utils/clack', () => ({
|
|
|
37
37
|
vitest_1.vi.mock('@clack/prompts', () => ({
|
|
38
38
|
confirm: vitest_1.vi.fn(),
|
|
39
39
|
select: vitest_1.vi.fn(),
|
|
40
|
+
multiselect: vitest_1.vi.fn(),
|
|
40
41
|
isCancel: vitest_1.vi.fn(() => false),
|
|
41
42
|
cancel: vitest_1.vi.fn(),
|
|
42
43
|
log: {
|
|
@@ -77,9 +78,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
77
78
|
});
|
|
78
79
|
(0, vitest_1.it)('should configure for Cursor when selected', async () => {
|
|
79
80
|
const { clack, clackUtils } = await getMocks();
|
|
80
|
-
vitest_1.vi.mocked(clack.select)
|
|
81
|
-
|
|
82
|
-
.mockResolvedValueOnce('cursor');
|
|
81
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
82
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['cursor']);
|
|
83
83
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
84
84
|
const mockReadFile = vitest_1.vi
|
|
85
85
|
.fn()
|
|
@@ -90,9 +90,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
90
90
|
vitest_1.vi.spyOn(fs.promises, 'writeFile').mockImplementation(mockWriteFile);
|
|
91
91
|
vitest_1.vi.spyOn(fs, 'mkdirSync').mockImplementation(mockMkdirSync);
|
|
92
92
|
await (0, mcp_config_1.offerProjectScopedMcpConfig)();
|
|
93
|
-
(0, vitest_1.expect)(clack.
|
|
94
|
-
|
|
95
|
-
message: 'Which editor do you want to configure?',
|
|
93
|
+
(0, vitest_1.expect)(clack.multiselect).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
|
|
94
|
+
message: 'Which editor(s) do you want to configure?',
|
|
96
95
|
options: vitest_1.expect.arrayContaining([
|
|
97
96
|
vitest_1.expect.objectContaining({ value: 'cursor' }),
|
|
98
97
|
vitest_1.expect.objectContaining({ value: 'vscode' }),
|
|
@@ -101,14 +100,13 @@ vitest_1.vi.mock('node:child_process');
|
|
|
101
100
|
}));
|
|
102
101
|
(0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(vitest_1.expect.stringContaining('.cursor/mcp.json'), vitest_1.expect.stringContaining('"mcpServers"'), 'utf8');
|
|
103
102
|
(0, vitest_1.expect)(clack.log.success).toHaveBeenCalledWith(vitest_1.expect.stringContaining('.cursor/mcp.json'));
|
|
104
|
-
(0, vitest_1.expect)(clack.log.success).toHaveBeenCalledWith('Added project-scoped Sentry MCP configuration.');
|
|
103
|
+
(0, vitest_1.expect)(clack.log.success).toHaveBeenCalledWith('Added project-scoped Sentry MCP configuration for Cursor.');
|
|
105
104
|
(0, vitest_1.expect)(clack.log.info).toHaveBeenCalledWith(vitest_1.expect.stringContaining('reload your editor'));
|
|
106
105
|
});
|
|
107
106
|
(0, vitest_1.it)('should configure for VS Code when selected', async () => {
|
|
108
107
|
const { clack, clackUtils } = await getMocks();
|
|
109
|
-
vitest_1.vi.mocked(clack.select)
|
|
110
|
-
|
|
111
|
-
.mockResolvedValueOnce('vscode');
|
|
108
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
109
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['vscode']);
|
|
112
110
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
113
111
|
const mockReadFile = vitest_1.vi
|
|
114
112
|
.fn()
|
|
@@ -124,9 +122,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
124
122
|
});
|
|
125
123
|
(0, vitest_1.it)('should configure for Claude Code when selected', async () => {
|
|
126
124
|
const { clack, clackUtils } = await getMocks();
|
|
127
|
-
vitest_1.vi.mocked(clack.select)
|
|
128
|
-
|
|
129
|
-
.mockResolvedValueOnce('claudeCode');
|
|
125
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
126
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['claudeCode']);
|
|
130
127
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
131
128
|
const mockReadFile = vitest_1.vi
|
|
132
129
|
.fn()
|
|
@@ -142,9 +139,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
142
139
|
});
|
|
143
140
|
(0, vitest_1.it)('should update existing Cursor config file', async () => {
|
|
144
141
|
const { clack, clackUtils } = await getMocks();
|
|
145
|
-
vitest_1.vi.mocked(clack.select)
|
|
146
|
-
|
|
147
|
-
.mockResolvedValueOnce('cursor');
|
|
142
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
143
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['cursor']);
|
|
148
144
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
149
145
|
const existingConfig = JSON.stringify({
|
|
150
146
|
mcpServers: {
|
|
@@ -169,9 +165,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
169
165
|
});
|
|
170
166
|
(0, vitest_1.it)('should update existing VS Code config file', async () => {
|
|
171
167
|
const { clack, clackUtils } = await getMocks();
|
|
172
|
-
vitest_1.vi.mocked(clack.select)
|
|
173
|
-
|
|
174
|
-
.mockResolvedValueOnce('vscode');
|
|
168
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
169
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['vscode']);
|
|
175
170
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
176
171
|
const existingConfig = JSON.stringify({
|
|
177
172
|
servers: {
|
|
@@ -196,9 +191,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
196
191
|
});
|
|
197
192
|
(0, vitest_1.it)('should update existing Claude Code config file', async () => {
|
|
198
193
|
const { clack, clackUtils } = await getMocks();
|
|
199
|
-
vitest_1.vi.mocked(clack.select)
|
|
200
|
-
|
|
201
|
-
.mockResolvedValueOnce('claudeCode');
|
|
194
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
195
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['claudeCode']);
|
|
202
196
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
203
197
|
const existingConfig = JSON.stringify({
|
|
204
198
|
mcpServers: {
|
|
@@ -219,11 +213,85 @@ vitest_1.vi.mock('node:child_process');
|
|
|
219
213
|
(0, vitest_1.expect)(writtenContent.mcpServers).toHaveProperty('Sentry');
|
|
220
214
|
(0, vitest_1.expect)(clack.log.success).toHaveBeenCalledWith('Updated .mcp.json');
|
|
221
215
|
});
|
|
216
|
+
(0, vitest_1.it)('should configure for OpenCode when selected', async () => {
|
|
217
|
+
const { clack, clackUtils } = await getMocks();
|
|
218
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
219
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['openCode']);
|
|
220
|
+
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
221
|
+
const mockReadFile = vitest_1.vi
|
|
222
|
+
.fn()
|
|
223
|
+
.mockRejectedValue(new Error('File not found'));
|
|
224
|
+
const mockWriteFile = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
225
|
+
const mockMkdirSync = vitest_1.vi.fn();
|
|
226
|
+
vitest_1.vi.spyOn(fs.promises, 'readFile').mockImplementation(mockReadFile);
|
|
227
|
+
vitest_1.vi.spyOn(fs.promises, 'writeFile').mockImplementation(mockWriteFile);
|
|
228
|
+
vitest_1.vi.spyOn(fs, 'mkdirSync').mockImplementation(mockMkdirSync);
|
|
229
|
+
await (0, mcp_config_1.offerProjectScopedMcpConfig)();
|
|
230
|
+
(0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(vitest_1.expect.stringContaining('opencode.json'), vitest_1.expect.stringContaining('"mcp"'), 'utf8');
|
|
231
|
+
// Verify the written content has the correct structure for OpenCode
|
|
232
|
+
const writtenContent = JSON.parse(mockWriteFile.mock.calls[0][1]);
|
|
233
|
+
(0, vitest_1.expect)(writtenContent.$schema).toBe('https://opencode.ai/config.json');
|
|
234
|
+
(0, vitest_1.expect)(writtenContent.mcp).toHaveProperty('Sentry');
|
|
235
|
+
(0, vitest_1.expect)(writtenContent.mcp?.Sentry).toHaveProperty('type', 'remote');
|
|
236
|
+
(0, vitest_1.expect)(writtenContent.mcp?.Sentry).toHaveProperty('url');
|
|
237
|
+
(0, vitest_1.expect)(writtenContent.mcp?.Sentry?.oauth).toEqual({});
|
|
238
|
+
(0, vitest_1.expect)(clack.log.success).toHaveBeenCalledWith(vitest_1.expect.stringContaining('opencode.json'));
|
|
239
|
+
(0, vitest_1.expect)(clack.log.success).toHaveBeenCalledWith('Added project-scoped Sentry MCP configuration for OpenCode.');
|
|
240
|
+
(0, vitest_1.expect)(clack.log.info).toHaveBeenCalledWith(vitest_1.expect.stringContaining('restart OpenCode'));
|
|
241
|
+
});
|
|
242
|
+
(0, vitest_1.it)('should update existing OpenCode config file', async () => {
|
|
243
|
+
const { clack, clackUtils } = await getMocks();
|
|
244
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
245
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['openCode']);
|
|
246
|
+
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
247
|
+
const existingConfig = JSON.stringify({
|
|
248
|
+
mcp: {
|
|
249
|
+
OtherServer: {
|
|
250
|
+
type: 'remote',
|
|
251
|
+
url: 'https://other.example.com',
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
const mockReadFile = vitest_1.vi.fn().mockResolvedValue(existingConfig);
|
|
256
|
+
const mockWriteFile = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
257
|
+
const mockMkdirSync = vitest_1.vi.fn();
|
|
258
|
+
vitest_1.vi.spyOn(fs.promises, 'readFile').mockImplementation(mockReadFile);
|
|
259
|
+
vitest_1.vi.spyOn(fs.promises, 'writeFile').mockImplementation(mockWriteFile);
|
|
260
|
+
vitest_1.vi.spyOn(fs, 'mkdirSync').mockImplementation(mockMkdirSync);
|
|
261
|
+
await (0, mcp_config_1.offerProjectScopedMcpConfig)();
|
|
262
|
+
const writtenContent = JSON.parse(mockWriteFile.mock.calls[0][1]);
|
|
263
|
+
(0, vitest_1.expect)(writtenContent.mcp).toHaveProperty('OtherServer');
|
|
264
|
+
(0, vitest_1.expect)(writtenContent.mcp).toHaveProperty('Sentry');
|
|
265
|
+
(0, vitest_1.expect)(writtenContent.mcp?.Sentry).toHaveProperty('type', 'remote');
|
|
266
|
+
(0, vitest_1.expect)(clack.log.success).toHaveBeenCalledWith('Updated opencode.json');
|
|
267
|
+
});
|
|
268
|
+
(0, vitest_1.it)('should handle file write errors gracefully for OpenCode', async () => {
|
|
269
|
+
const { clack, clackUtils } = await getMocks();
|
|
270
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
271
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['openCode']);
|
|
272
|
+
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
273
|
+
const mockReadFile = vitest_1.vi
|
|
274
|
+
.fn()
|
|
275
|
+
.mockRejectedValue(new Error('File not found'));
|
|
276
|
+
const mockWriteFile = vitest_1.vi
|
|
277
|
+
.fn()
|
|
278
|
+
.mockRejectedValue(new Error('Permission denied'));
|
|
279
|
+
const mockMkdirSync = vitest_1.vi.fn();
|
|
280
|
+
vitest_1.vi.spyOn(fs.promises, 'readFile').mockImplementation(mockReadFile);
|
|
281
|
+
vitest_1.vi.spyOn(fs.promises, 'writeFile').mockImplementation(mockWriteFile);
|
|
282
|
+
vitest_1.vi.spyOn(fs, 'mkdirSync').mockImplementation(mockMkdirSync);
|
|
283
|
+
await (0, vitest_1.expect)((0, mcp_config_1.offerProjectScopedMcpConfig)()).resolves.toBeUndefined();
|
|
284
|
+
(0, vitest_1.expect)(clack.log.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Failed to write MCP config for openCode'));
|
|
285
|
+
(0, vitest_1.expect)(clackUtils.showCopyPasteInstructions).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
|
|
286
|
+
filename: 'opencode.json',
|
|
287
|
+
codeSnippet: vitest_1.expect.stringContaining('"mcp"'),
|
|
288
|
+
hint: 'create the file if it does not exist',
|
|
289
|
+
}));
|
|
290
|
+
});
|
|
222
291
|
(0, vitest_1.it)('should handle file write errors gracefully for Cursor', async () => {
|
|
223
292
|
const { clack, clackUtils } = await getMocks();
|
|
224
|
-
vitest_1.vi.mocked(clack.select)
|
|
225
|
-
|
|
226
|
-
.mockResolvedValueOnce('cursor');
|
|
293
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
294
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['cursor']);
|
|
227
295
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
228
296
|
const mockReadFile = vitest_1.vi
|
|
229
297
|
.fn()
|
|
@@ -236,7 +304,7 @@ vitest_1.vi.mock('node:child_process');
|
|
|
236
304
|
vitest_1.vi.spyOn(fs.promises, 'writeFile').mockImplementation(mockWriteFile);
|
|
237
305
|
vitest_1.vi.spyOn(fs, 'mkdirSync').mockImplementation(mockMkdirSync);
|
|
238
306
|
await (0, vitest_1.expect)((0, mcp_config_1.offerProjectScopedMcpConfig)()).resolves.toBeUndefined();
|
|
239
|
-
(0, vitest_1.expect)(clack.log.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Failed to write MCP config
|
|
307
|
+
(0, vitest_1.expect)(clack.log.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Failed to write MCP config'));
|
|
240
308
|
(0, vitest_1.expect)(clackUtils.showCopyPasteInstructions).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
|
|
241
309
|
filename: path.join('.cursor', 'mcp.json'),
|
|
242
310
|
codeSnippet: vitest_1.expect.stringContaining('mcpServers'),
|
|
@@ -245,9 +313,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
245
313
|
});
|
|
246
314
|
(0, vitest_1.it)('should handle file write errors gracefully for VS Code', async () => {
|
|
247
315
|
const { clack, clackUtils } = await getMocks();
|
|
248
|
-
vitest_1.vi.mocked(clack.select)
|
|
249
|
-
|
|
250
|
-
.mockResolvedValueOnce('vscode');
|
|
316
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
317
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['vscode']);
|
|
251
318
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
252
319
|
const mockReadFile = vitest_1.vi
|
|
253
320
|
.fn()
|
|
@@ -268,9 +335,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
268
335
|
});
|
|
269
336
|
(0, vitest_1.it)('should handle file write errors gracefully for Claude Code', async () => {
|
|
270
337
|
const { clack, clackUtils } = await getMocks();
|
|
271
|
-
vitest_1.vi.mocked(clack.select)
|
|
272
|
-
|
|
273
|
-
.mockResolvedValueOnce('claudeCode');
|
|
338
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
339
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['claudeCode']);
|
|
274
340
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
275
341
|
const mockReadFile = vitest_1.vi
|
|
276
342
|
.fn()
|
|
@@ -291,9 +357,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
291
357
|
});
|
|
292
358
|
(0, vitest_1.it)('should handle update errors and show copy-paste instructions', async () => {
|
|
293
359
|
const { clack, clackUtils } = await getMocks();
|
|
294
|
-
vitest_1.vi.mocked(clack.select)
|
|
295
|
-
|
|
296
|
-
.mockResolvedValueOnce('cursor');
|
|
360
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
361
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['cursor']);
|
|
297
362
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
298
363
|
// Mock existing file and simulate write error during update
|
|
299
364
|
const existingConfig = JSON.stringify({
|
|
@@ -312,14 +377,13 @@ vitest_1.vi.mock('node:child_process');
|
|
|
312
377
|
vitest_1.vi.spyOn(fs.promises, 'writeFile').mockImplementation(mockWriteFile);
|
|
313
378
|
vitest_1.vi.spyOn(fs, 'mkdirSync').mockImplementation(mockMkdirSync);
|
|
314
379
|
await (0, vitest_1.expect)((0, mcp_config_1.offerProjectScopedMcpConfig)()).resolves.toBeUndefined();
|
|
315
|
-
(0, vitest_1.expect)(clack.log.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Failed to write MCP config
|
|
380
|
+
(0, vitest_1.expect)(clack.log.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Failed to write MCP config'));
|
|
316
381
|
(0, vitest_1.expect)(clackUtils.showCopyPasteInstructions).toHaveBeenCalled();
|
|
317
382
|
});
|
|
318
383
|
(0, vitest_1.it)('should handle mkdirSync errors', async () => {
|
|
319
384
|
const { clack, clackUtils } = await getMocks();
|
|
320
|
-
vitest_1.vi.mocked(clack.select)
|
|
321
|
-
|
|
322
|
-
.mockResolvedValueOnce('cursor');
|
|
385
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
386
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['cursor']);
|
|
323
387
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
324
388
|
const mockReadFile = vitest_1.vi
|
|
325
389
|
.fn()
|
|
@@ -332,14 +396,13 @@ vitest_1.vi.mock('node:child_process');
|
|
|
332
396
|
vitest_1.vi.spyOn(fs.promises, 'writeFile').mockImplementation(mockWriteFile);
|
|
333
397
|
vitest_1.vi.spyOn(fs, 'mkdirSync').mockImplementation(mockMkdirSync);
|
|
334
398
|
await (0, vitest_1.expect)((0, mcp_config_1.offerProjectScopedMcpConfig)()).resolves.toBeUndefined();
|
|
335
|
-
(0, vitest_1.expect)(clack.log.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Failed to write MCP config
|
|
399
|
+
(0, vitest_1.expect)(clack.log.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Failed to write MCP config'));
|
|
336
400
|
(0, vitest_1.expect)(clackUtils.showCopyPasteInstructions).toHaveBeenCalled();
|
|
337
401
|
});
|
|
338
402
|
(0, vitest_1.it)('should create config with empty servers/mcpServers when existing config lacks them', async () => {
|
|
339
403
|
const { clack, clackUtils } = await getMocks();
|
|
340
|
-
vitest_1.vi.mocked(clack.select)
|
|
341
|
-
|
|
342
|
-
.mockResolvedValueOnce('vscode');
|
|
404
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
405
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['vscode']);
|
|
343
406
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
344
407
|
const existingConfig = JSON.stringify({
|
|
345
408
|
otherProperty: 'value',
|
|
@@ -361,8 +424,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
361
424
|
const { clack, clackUtils } = await getMocks();
|
|
362
425
|
vitest_1.vi.mocked(clack.select)
|
|
363
426
|
.mockResolvedValueOnce('yes')
|
|
364
|
-
.mockResolvedValueOnce('jetbrains')
|
|
365
427
|
.mockResolvedValueOnce(true); // For the clipboard copy prompt
|
|
428
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['jetbrains']);
|
|
366
429
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
367
430
|
// Mock clipboard copy
|
|
368
431
|
const mockSpawn = vitest_1.vi.fn().mockReturnValue({
|
|
@@ -393,8 +456,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
393
456
|
const { clack, clackUtils } = await getMocks();
|
|
394
457
|
vitest_1.vi.mocked(clack.select)
|
|
395
458
|
.mockResolvedValueOnce('yes')
|
|
396
|
-
.mockResolvedValueOnce('other')
|
|
397
459
|
.mockResolvedValueOnce(true); // For the clipboard copy prompt
|
|
460
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['other']);
|
|
398
461
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
399
462
|
// Mock clipboard copy failure to test fallback
|
|
400
463
|
const mockSpawn = vitest_1.vi.fn().mockReturnValue({
|
|
@@ -426,8 +489,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
426
489
|
const { clack, clackUtils } = await getMocks();
|
|
427
490
|
vitest_1.vi.mocked(clack.select)
|
|
428
491
|
.mockResolvedValueOnce('yes')
|
|
429
|
-
.mockResolvedValueOnce('jetbrains')
|
|
430
492
|
.mockResolvedValueOnce(true); // For clipboard copy prompt
|
|
493
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['jetbrains']);
|
|
431
494
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
432
495
|
// Mock clipboard copy to throw error
|
|
433
496
|
const mockSpawn = vitest_1.vi.fn().mockImplementation(() => {
|
|
@@ -450,8 +513,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
450
513
|
const { clack, clackUtils } = await getMocks();
|
|
451
514
|
vitest_1.vi.mocked(clack.select)
|
|
452
515
|
.mockResolvedValueOnce('explain') // User selects "What is MCP?"
|
|
453
|
-
.mockResolvedValueOnce(true) // User selects "Yes" after explanation
|
|
454
|
-
|
|
516
|
+
.mockResolvedValueOnce(true); // User selects "Yes" after explanation
|
|
517
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['cursor']); // User selects Cursor
|
|
455
518
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
456
519
|
const mockReadFile = vitest_1.vi
|
|
457
520
|
.fn()
|
|
@@ -477,8 +540,8 @@ vitest_1.vi.mock('node:child_process');
|
|
|
477
540
|
const { clack, clackUtils } = await getMocks();
|
|
478
541
|
vitest_1.vi.mocked(clack.select)
|
|
479
542
|
.mockResolvedValueOnce('yes')
|
|
480
|
-
.mockResolvedValueOnce('jetbrains')
|
|
481
543
|
.mockResolvedValueOnce(false); // User declines to copy to clipboard
|
|
544
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['jetbrains']);
|
|
482
545
|
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
483
546
|
const mockSpawn = vitest_1.vi.fn();
|
|
484
547
|
vitest_1.vi.spyOn(childProcess, 'spawn').mockImplementation(mockSpawn);
|
|
@@ -511,8 +574,70 @@ vitest_1.vi.mock('node:child_process');
|
|
|
511
574
|
message: 'Would you like to configure MCP for your IDE now?',
|
|
512
575
|
}));
|
|
513
576
|
// Should NOT proceed with editor selection
|
|
514
|
-
(0, vitest_1.expect)(clack.
|
|
515
|
-
|
|
577
|
+
(0, vitest_1.expect)(clack.multiselect).not.toHaveBeenCalled();
|
|
578
|
+
});
|
|
579
|
+
(0, vitest_1.it)('should configure multiple editors when selected', async () => {
|
|
580
|
+
const { clack, clackUtils } = await getMocks();
|
|
581
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
582
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce([
|
|
583
|
+
'cursor',
|
|
584
|
+
'vscode',
|
|
585
|
+
'claudeCode',
|
|
586
|
+
]);
|
|
587
|
+
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
588
|
+
const mockReadFile = vitest_1.vi
|
|
589
|
+
.fn()
|
|
590
|
+
.mockRejectedValue(new Error('File not found'));
|
|
591
|
+
const mockWriteFile = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
592
|
+
const mockMkdirSync = vitest_1.vi.fn();
|
|
593
|
+
vitest_1.vi.spyOn(fs.promises, 'readFile').mockImplementation(mockReadFile);
|
|
594
|
+
vitest_1.vi.spyOn(fs.promises, 'writeFile').mockImplementation(mockWriteFile);
|
|
595
|
+
vitest_1.vi.spyOn(fs, 'mkdirSync').mockImplementation(mockMkdirSync);
|
|
596
|
+
await (0, mcp_config_1.offerProjectScopedMcpConfig)();
|
|
597
|
+
// Should write config for all three editors
|
|
598
|
+
(0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledTimes(3);
|
|
599
|
+
(0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(vitest_1.expect.stringContaining('.cursor/mcp.json'), vitest_1.expect.stringContaining('"mcpServers"'), 'utf8');
|
|
600
|
+
(0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(vitest_1.expect.stringContaining('.vscode/mcp.json'), vitest_1.expect.stringContaining('"servers"'), 'utf8');
|
|
601
|
+
(0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(vitest_1.expect.stringContaining('.mcp.json'), vitest_1.expect.stringContaining('"mcpServers"'), 'utf8');
|
|
602
|
+
// Should show success messages for each (twice per editor: filename + editor-specific message)
|
|
603
|
+
(0, vitest_1.expect)(clack.log.success).toHaveBeenCalledWith('Added project-scoped Sentry MCP configuration for Cursor.');
|
|
604
|
+
(0, vitest_1.expect)(clack.log.success).toHaveBeenCalledWith('Added project-scoped Sentry MCP configuration for VS Code.');
|
|
605
|
+
(0, vitest_1.expect)(clack.log.success).toHaveBeenCalledWith('Added project-scoped Sentry MCP configuration for Claude Code.');
|
|
606
|
+
});
|
|
607
|
+
(0, vitest_1.it)('should return early when no editors are selected', async () => {
|
|
608
|
+
const { clack, clackUtils } = await getMocks();
|
|
609
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
610
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce([]);
|
|
611
|
+
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
612
|
+
const mockWriteFile = vitest_1.vi.fn();
|
|
613
|
+
vitest_1.vi.spyOn(fs.promises, 'writeFile').mockImplementation(mockWriteFile);
|
|
614
|
+
await (0, mcp_config_1.offerProjectScopedMcpConfig)();
|
|
615
|
+
(0, vitest_1.expect)(clack.log.info).toHaveBeenCalledWith('No editors selected. You can add MCP configuration later.');
|
|
616
|
+
(0, vitest_1.expect)(mockWriteFile).not.toHaveBeenCalled();
|
|
617
|
+
});
|
|
618
|
+
(0, vitest_1.it)('should handle mixed success and failure for multiple editors', async () => {
|
|
619
|
+
const { clack, clackUtils } = await getMocks();
|
|
620
|
+
vitest_1.vi.mocked(clack.select).mockResolvedValueOnce('yes');
|
|
621
|
+
vitest_1.vi.mocked(clack.multiselect).mockResolvedValueOnce(['cursor', 'vscode']);
|
|
622
|
+
vitest_1.vi.mocked(clackUtils.abortIfCancelled).mockImplementation((value) => Promise.resolve(value));
|
|
623
|
+
const mockReadFile = vitest_1.vi
|
|
624
|
+
.fn()
|
|
625
|
+
.mockRejectedValue(new Error('File not found'));
|
|
626
|
+
const mockWriteFile = vitest_1.vi
|
|
627
|
+
.fn()
|
|
628
|
+
.mockResolvedValueOnce(undefined) // Cursor succeeds
|
|
629
|
+
.mockRejectedValueOnce(new Error('Permission denied')); // VS Code fails
|
|
630
|
+
const mockMkdirSync = vitest_1.vi.fn();
|
|
631
|
+
vitest_1.vi.spyOn(fs.promises, 'readFile').mockImplementation(mockReadFile);
|
|
632
|
+
vitest_1.vi.spyOn(fs.promises, 'writeFile').mockImplementation(mockWriteFile);
|
|
633
|
+
vitest_1.vi.spyOn(fs, 'mkdirSync').mockImplementation(mockMkdirSync);
|
|
634
|
+
await (0, mcp_config_1.offerProjectScopedMcpConfig)();
|
|
635
|
+
// Cursor should succeed
|
|
636
|
+
(0, vitest_1.expect)(clack.log.success).toHaveBeenCalledWith(vitest_1.expect.stringContaining('.cursor/mcp.json'));
|
|
637
|
+
// VS Code should fail and show fallback
|
|
638
|
+
(0, vitest_1.expect)(clack.log.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Failed to write MCP config for vscode'));
|
|
639
|
+
(0, vitest_1.expect)(clackUtils.showCopyPasteInstructions).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
|
|
640
|
+
filename: path.join('.vscode', 'mcp.json'),
|
|
516
641
|
}));
|
|
517
642
|
});
|
|
518
643
|
});
|