@sentry/wizard 3.32.0 → 3.33.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 +8 -0
- package/README.md +1 -1
- package/bin.ts +59 -27
- package/dist/bin.js +97 -70
- package/dist/bin.js.map +1 -1
- package/dist/e2e-tests/jest.config.d.ts +15 -0
- package/dist/e2e-tests/jest.config.js +17 -0
- package/dist/e2e-tests/jest.config.js.map +1 -0
- package/dist/e2e-tests/tests/remix.test.d.ts +1 -0
- package/dist/e2e-tests/tests/remix.test.js +187 -0
- package/dist/e2e-tests/tests/remix.test.js.map +1 -0
- package/dist/e2e-tests/utils/index.d.ts +117 -0
- package/dist/e2e-tests/utils/index.js +353 -0
- package/dist/e2e-tests/utils/index.js.map +1 -0
- package/dist/lib/Steps/SentryProjectSelector.js +1 -1
- package/dist/lib/Steps/SentryProjectSelector.js.map +1 -1
- package/dist/package.json +7 -3
- package/dist/src/remix/sdk-example.js +5 -1
- package/dist/src/remix/sdk-example.js.map +1 -1
- package/dist/src/run.d.ts +12 -1
- package/dist/src/run.js +27 -1
- package/dist/src/run.js.map +1 -1
- package/dist/src/sourcemaps/sourcemaps-wizard.d.ts +2 -2
- package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
- package/dist/src/sourcemaps/tools/remix.d.ts +2 -2
- package/dist/src/sourcemaps/tools/remix.js.map +1 -1
- package/dist/src/utils/clack-utils.js +22 -17
- package/dist/src/utils/clack-utils.js.map +1 -1
- package/dist/src/utils/types.d.ts +6 -14
- package/dist/src/utils/types.js.map +1 -1
- package/e2e-tests/jest.config.ts +14 -0
- package/e2e-tests/test-applications/remix-test-app/app/entry.client.tsx +18 -0
- package/e2e-tests/test-applications/remix-test-app/app/entry.server.tsx +140 -0
- package/e2e-tests/test-applications/remix-test-app/app/root.tsx +30 -0
- package/e2e-tests/test-applications/remix-test-app/app/routes/_index.tsx +48 -0
- package/e2e-tests/test-applications/remix-test-app/app/tailwind.css +3 -0
- package/e2e-tests/test-applications/remix-test-app/package.json +37 -0
- package/e2e-tests/test-applications/remix-test-app/postcss.config.js +6 -0
- package/e2e-tests/test-applications/remix-test-app/tailwind.config.ts +9 -0
- package/e2e-tests/test-applications/remix-test-app/vite.config.ts +16 -0
- package/e2e-tests/tests/remix.test.ts +163 -0
- package/e2e-tests/utils/index.ts +302 -0
- package/lib/Steps/SentryProjectSelector.ts +1 -1
- package/package.json +7 -3
- package/src/remix/sdk-example.ts +6 -0
- package/src/run.ts +45 -5
- package/src/sourcemaps/sourcemaps-wizard.ts +4 -3
- package/src/sourcemaps/tools/remix.ts +2 -2
- package/src/utils/clack-utils.ts +17 -8
- package/src/utils/types.ts +8 -14
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { MetaFunction } from "@remix-run/node";
|
|
2
|
+
|
|
3
|
+
export const meta: MetaFunction = () => {
|
|
4
|
+
return [
|
|
5
|
+
{ title: "New Remix App" },
|
|
6
|
+
{ name: "description", content: "Welcome to Remix!" },
|
|
7
|
+
];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default function Index() {
|
|
11
|
+
return (
|
|
12
|
+
<div className="font-sans p-4">
|
|
13
|
+
<h1 className="text-3xl">Welcome to Remix</h1>
|
|
14
|
+
<ul className="list-disc mt-4 pl-6 space-y-2">
|
|
15
|
+
<li>
|
|
16
|
+
<a
|
|
17
|
+
className="text-blue-700 underline visited:text-purple-900"
|
|
18
|
+
target="_blank"
|
|
19
|
+
href="https://remix.run/start/quickstart"
|
|
20
|
+
rel="noreferrer"
|
|
21
|
+
>
|
|
22
|
+
5m Quick Start
|
|
23
|
+
</a>
|
|
24
|
+
</li>
|
|
25
|
+
<li>
|
|
26
|
+
<a
|
|
27
|
+
className="text-blue-700 underline visited:text-purple-900"
|
|
28
|
+
target="_blank"
|
|
29
|
+
href="https://remix.run/start/tutorial"
|
|
30
|
+
rel="noreferrer"
|
|
31
|
+
>
|
|
32
|
+
30m Tutorial
|
|
33
|
+
</a>
|
|
34
|
+
</li>
|
|
35
|
+
<li>
|
|
36
|
+
<a
|
|
37
|
+
className="text-blue-700 underline visited:text-purple-900"
|
|
38
|
+
target="_blank"
|
|
39
|
+
href="https://remix.run/docs"
|
|
40
|
+
rel="noreferrer"
|
|
41
|
+
>
|
|
42
|
+
Remix Docs
|
|
43
|
+
</a>
|
|
44
|
+
</li>
|
|
45
|
+
</ul>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "remix-test-app",
|
|
3
|
+
"private": true,
|
|
4
|
+
"sideEffects": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "remix vite:build",
|
|
8
|
+
"dev": "remix vite:dev",
|
|
9
|
+
"lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
|
|
10
|
+
"start": "remix-serve ./build/server/index.js",
|
|
11
|
+
"typecheck": "tsc"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@remix-run/node": "^2.11.1",
|
|
15
|
+
"@remix-run/react": "^2.11.1",
|
|
16
|
+
"@remix-run/serve": "^2.11.1",
|
|
17
|
+
"isbot": "^4.1.0",
|
|
18
|
+
"react": "^18.2.0",
|
|
19
|
+
"react-dom": "^18.2.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@remix-run/dev": "^2.11.1",
|
|
23
|
+
"@types/react": "^18.2.20",
|
|
24
|
+
"@types/react-dom": "^18.2.7",
|
|
25
|
+
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
|
26
|
+
"@typescript-eslint/parser": "^6.7.4",
|
|
27
|
+
"autoprefixer": "^10.4.19",
|
|
28
|
+
"postcss": "^8.4.38",
|
|
29
|
+
"tailwindcss": "^3.4.4",
|
|
30
|
+
"typescript": "^5.1.6",
|
|
31
|
+
"vite": "^5.1.0",
|
|
32
|
+
"vite-tsconfig-paths": "^4.2.1"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { vitePlugin as remix } from "@remix-run/dev";
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
import tsconfigPaths from "vite-tsconfig-paths";
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [
|
|
7
|
+
remix({
|
|
8
|
+
future: {
|
|
9
|
+
v3_fetcherPersist: true,
|
|
10
|
+
v3_relativeSplatPath: true,
|
|
11
|
+
v3_throwAbortReason: true,
|
|
12
|
+
},
|
|
13
|
+
}),
|
|
14
|
+
tsconfigPaths(),
|
|
15
|
+
],
|
|
16
|
+
});
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/* eslint-disable jest/expect-expect */
|
|
2
|
+
import { Integration } from '../../lib/Constants';
|
|
3
|
+
import {
|
|
4
|
+
checkEnvBuildPlugin,
|
|
5
|
+
cleanupGit,
|
|
6
|
+
KEYS,
|
|
7
|
+
revertLocalChanges,
|
|
8
|
+
} from '../utils';
|
|
9
|
+
import { startWizardInstance } from '../utils';
|
|
10
|
+
import {
|
|
11
|
+
checkFileContents,
|
|
12
|
+
checkFileExists,
|
|
13
|
+
checkIfBuilds,
|
|
14
|
+
checkIfRunsOnDevMode,
|
|
15
|
+
checkIfRunsOnProdMode,
|
|
16
|
+
checkPackageJson,
|
|
17
|
+
TEST_ARGS,
|
|
18
|
+
} from '../utils';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
|
|
21
|
+
describe('Remix', () => {
|
|
22
|
+
const integration = Integration.remix;
|
|
23
|
+
const projectDir = path.resolve(
|
|
24
|
+
__dirname,
|
|
25
|
+
'../test-applications/remix-test-app',
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
beforeAll(async () => {
|
|
29
|
+
const wizardInstance = startWizardInstance(integration, projectDir);
|
|
30
|
+
const packageManagerPrompted = await wizardInstance.waitForOutput(
|
|
31
|
+
'Please select your package manager.',
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
if (packageManagerPrompted) {
|
|
35
|
+
// Selecting `yarn` as the package manager
|
|
36
|
+
wizardInstance.sendStdin(KEYS.DOWN);
|
|
37
|
+
wizardInstance.sendStdin(KEYS.ENTER);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const tracingOptionPrompted = await wizardInstance.waitForOutput(
|
|
41
|
+
'Do you want to enable Tracing',
|
|
42
|
+
{
|
|
43
|
+
timeout: 240_000,
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if (tracingOptionPrompted) {
|
|
48
|
+
wizardInstance.sendStdin(KEYS.ENTER);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const replayOptionPrompted = await wizardInstance.waitForOutput(
|
|
52
|
+
'Do you want to enable Sentry Session Replay',
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (replayOptionPrompted) {
|
|
56
|
+
wizardInstance.sendStdin(KEYS.ENTER);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const examplePagePrompted = await wizardInstance.waitForOutput(
|
|
60
|
+
'Do you want to create an example page',
|
|
61
|
+
{
|
|
62
|
+
optional: true,
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
if (examplePagePrompted) {
|
|
67
|
+
wizardInstance.sendStdin(KEYS.ENTER);
|
|
68
|
+
wizardInstance.sendStdin(KEYS.ENTER);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await wizardInstance.waitForOutput(
|
|
72
|
+
'Sentry has been successfully configured for your Remix project',
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
wizardInstance.kill();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
afterAll(() => {
|
|
79
|
+
revertLocalChanges(projectDir);
|
|
80
|
+
cleanupGit(projectDir);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('package.json is updated correctly', () => {
|
|
84
|
+
checkPackageJson(projectDir, integration);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('.env-sentry-build-plugin is created and contains the auth token', () => {
|
|
88
|
+
checkEnvBuildPlugin(projectDir);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('example page exists', () => {
|
|
92
|
+
checkFileExists(`${projectDir}/app/routes/sentry-example-page.tsx`);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('instrumentation.server file exists', () => {
|
|
96
|
+
checkFileExists(`${projectDir}/instrumentation.server.mjs`);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('entry.client file contains Sentry initialization', () => {
|
|
100
|
+
checkFileContents(`${projectDir}/app/entry.client.tsx`, [
|
|
101
|
+
'import * as Sentry from "@sentry/remix";',
|
|
102
|
+
`Sentry.init({
|
|
103
|
+
dsn: "${TEST_ARGS.PROJECT_DSN}",
|
|
104
|
+
tracesSampleRate: 1,
|
|
105
|
+
|
|
106
|
+
integrations: [Sentry.browserTracingIntegration({
|
|
107
|
+
useEffect,
|
|
108
|
+
useLocation,
|
|
109
|
+
useMatches
|
|
110
|
+
}), Sentry.replayIntegration({
|
|
111
|
+
maskAllText: true,
|
|
112
|
+
blockAllMedia: true
|
|
113
|
+
})],
|
|
114
|
+
|
|
115
|
+
replaysSessionSampleRate: 0.1,
|
|
116
|
+
replaysOnErrorSampleRate: 1
|
|
117
|
+
})`,
|
|
118
|
+
]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('entry.server file contains Sentry code', () => {
|
|
122
|
+
checkFileContents(`${projectDir}/app/entry.server.tsx`, [
|
|
123
|
+
'import * as Sentry from "@sentry/remix";',
|
|
124
|
+
`export const handleError = Sentry.wrapHandleErrorWithSentry((error, { request }) => {
|
|
125
|
+
// Custom handleError implementation
|
|
126
|
+
});`,
|
|
127
|
+
]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('instrumentation.server file contains Sentry initialization', () => {
|
|
131
|
+
checkFileContents(`${projectDir}/instrumentation.server.mjs`, [
|
|
132
|
+
'import * as Sentry from "@sentry/remix";',
|
|
133
|
+
`Sentry.init({
|
|
134
|
+
dsn: "${TEST_ARGS.PROJECT_DSN}",
|
|
135
|
+
tracesSampleRate: 1,
|
|
136
|
+
autoInstrumentRemix: true
|
|
137
|
+
})`,
|
|
138
|
+
]);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('root file contains Sentry ErrorBoundary', () => {
|
|
142
|
+
checkFileContents(`${projectDir}/app/root.tsx`, [
|
|
143
|
+
'import { captureRemixErrorBoundaryError } from "@sentry/remix";',
|
|
144
|
+
`export const ErrorBoundary = () => {
|
|
145
|
+
const error = useRouteError();
|
|
146
|
+
captureRemixErrorBoundaryError(error);
|
|
147
|
+
return <div>Something went wrong</div>;
|
|
148
|
+
};`,
|
|
149
|
+
]);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('builds correctly', async () => {
|
|
153
|
+
await checkIfBuilds(projectDir, 'built');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test('runs on dev mode correctly', async () => {
|
|
157
|
+
await checkIfRunsOnDevMode(projectDir, 'to expose');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test('runs on prod mode correctly', async () => {
|
|
161
|
+
await checkIfRunsOnProdMode(projectDir, '[remix-serve]');
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
import type { Integration } from '../../lib/Constants';
|
|
5
|
+
import { spawn, execSync } from 'child_process';
|
|
6
|
+
import type { ChildProcess } from 'child_process';
|
|
7
|
+
import { dim, green, red } from '../../lib/Helper/Logging';
|
|
8
|
+
|
|
9
|
+
export const KEYS = {
|
|
10
|
+
UP: '\u001b[A',
|
|
11
|
+
DOWN: '\u001b[B',
|
|
12
|
+
LEFT: '\u001b[D',
|
|
13
|
+
RIGHT: '\u001b[C',
|
|
14
|
+
ENTER: '\r',
|
|
15
|
+
SPACE: ' ',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const TEST_ARGS = {
|
|
19
|
+
AUTH_TOKEN: 'TEST_AUTH_TOKEN',
|
|
20
|
+
PROJECT_DSN: 'https://public@dsn.ingest.sentry.io/1337',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const log = {
|
|
24
|
+
success: (message: string) => {
|
|
25
|
+
green(`[SUCCESS] ${message}`);
|
|
26
|
+
},
|
|
27
|
+
info: (message: string) => {
|
|
28
|
+
dim(`[INFO] ${message}`);
|
|
29
|
+
},
|
|
30
|
+
error: (message: string) => {
|
|
31
|
+
red(`[ERROR] ${message}`);
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export class WizardTestEnv {
|
|
36
|
+
taskHandle: ChildProcess;
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
cmd: string,
|
|
40
|
+
args: string[],
|
|
41
|
+
opts?: {
|
|
42
|
+
cwd?: string;
|
|
43
|
+
debug?: boolean;
|
|
44
|
+
},
|
|
45
|
+
) {
|
|
46
|
+
this.taskHandle = spawn(cmd, args, { cwd: opts?.cwd, stdio: 'pipe' });
|
|
47
|
+
|
|
48
|
+
if (opts?.debug) {
|
|
49
|
+
this.taskHandle.stdout.pipe(process.stdout);
|
|
50
|
+
this.taskHandle.stderr.pipe(process.stderr);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
sendStdin(input: string) {
|
|
55
|
+
this.taskHandle.stdin.write(input);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Waits for the provided output with `.includes()` logic.
|
|
60
|
+
*
|
|
61
|
+
* @returns a promise that resolves to `true` if the output was found, `false` if the output was not found within the
|
|
62
|
+
* timeout and `optional: true` is set, or it rejects when the timeout was reached with `optional: false`
|
|
63
|
+
*/
|
|
64
|
+
waitForOutput(
|
|
65
|
+
output: string,
|
|
66
|
+
options: {
|
|
67
|
+
/** Timeout in ms */
|
|
68
|
+
timeout?: number;
|
|
69
|
+
/** Whether to always resolve after the timeout, no matter whether the input was actually found or not. */
|
|
70
|
+
optional?: boolean;
|
|
71
|
+
} = {},
|
|
72
|
+
) {
|
|
73
|
+
const { timeout, optional } = {
|
|
74
|
+
timeout: 30_000,
|
|
75
|
+
optional: false,
|
|
76
|
+
...options,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return new Promise<boolean>((resolve, reject) => {
|
|
80
|
+
let outputBuffer = '';
|
|
81
|
+
const timeoutId = setTimeout(() => {
|
|
82
|
+
if (optional) {
|
|
83
|
+
// The output is not found but it's optional so we can resolve the promise with false
|
|
84
|
+
resolve(false);
|
|
85
|
+
} else {
|
|
86
|
+
reject(new Error(`Timeout waiting for output: ${output}`));
|
|
87
|
+
}
|
|
88
|
+
}, timeout);
|
|
89
|
+
|
|
90
|
+
this.taskHandle.stdout.on('data', (data) => {
|
|
91
|
+
outputBuffer += data;
|
|
92
|
+
if (outputBuffer.includes(output)) {
|
|
93
|
+
clearTimeout(timeoutId);
|
|
94
|
+
// The output is found so we can resolve the promise with true
|
|
95
|
+
resolve(true);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
kill() {
|
|
102
|
+
this.taskHandle.stdin.destroy();
|
|
103
|
+
this.taskHandle.stderr.destroy();
|
|
104
|
+
this.taskHandle.stdout.destroy();
|
|
105
|
+
this.taskHandle.kill('SIGINT');
|
|
106
|
+
this.taskHandle.unref();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Initialize a git repository in the given directory
|
|
112
|
+
* @param projectDir
|
|
113
|
+
*/
|
|
114
|
+
export function initGit(projectDir: string): void {
|
|
115
|
+
try {
|
|
116
|
+
execSync('git init', { cwd: projectDir });
|
|
117
|
+
// Add all files to the git repo
|
|
118
|
+
execSync('git add -A', { cwd: projectDir });
|
|
119
|
+
// Add author info to avoid git commit error
|
|
120
|
+
execSync('git config user.email test@test.sentry.io', { cwd: projectDir });
|
|
121
|
+
execSync('git config user.name Test', { cwd: projectDir });
|
|
122
|
+
execSync('git commit -m init', { cwd: projectDir });
|
|
123
|
+
} catch (e) {
|
|
124
|
+
log.error('Error initializing git');
|
|
125
|
+
throw e;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Cleanup the git repository in the given directory
|
|
131
|
+
*
|
|
132
|
+
* Caution! Make sure `projectDir` is a test project directory,
|
|
133
|
+
* if in doubt, please commit your local non-test changes first!
|
|
134
|
+
* @param projectDir
|
|
135
|
+
*/
|
|
136
|
+
export function cleanupGit(projectDir: string): void {
|
|
137
|
+
try {
|
|
138
|
+
// Remove the .git directory
|
|
139
|
+
execSync(`rm -rf ${projectDir}/.git`);
|
|
140
|
+
} catch (e) {
|
|
141
|
+
log.error('Error cleaning up git');
|
|
142
|
+
throw e;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Revert local changes in the given directory
|
|
148
|
+
*
|
|
149
|
+
* Caution! Make sure `projectDir` is a test project directory,
|
|
150
|
+
* if in doubt, please commit your local non-test changes first!
|
|
151
|
+
*
|
|
152
|
+
* @param projectDir
|
|
153
|
+
*/
|
|
154
|
+
export function revertLocalChanges(projectDir: string): void {
|
|
155
|
+
try {
|
|
156
|
+
// Revert tracked files
|
|
157
|
+
execSync('git checkout .', { cwd: projectDir });
|
|
158
|
+
// Revert untracked files
|
|
159
|
+
execSync('git clean -fd .', { cwd: projectDir });
|
|
160
|
+
} catch (e) {
|
|
161
|
+
log.error('Error reverting local changes');
|
|
162
|
+
throw e;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Start the wizard instance with the given integration and project directory
|
|
168
|
+
* @param integration
|
|
169
|
+
* @param projectDir
|
|
170
|
+
*
|
|
171
|
+
* @returns WizardTestEnv
|
|
172
|
+
*/
|
|
173
|
+
export function startWizardInstance(
|
|
174
|
+
integration: Integration,
|
|
175
|
+
projectDir: string,
|
|
176
|
+
): WizardTestEnv {
|
|
177
|
+
const binPath = path.join(__dirname, '../../dist/bin.js');
|
|
178
|
+
|
|
179
|
+
revertLocalChanges(projectDir);
|
|
180
|
+
cleanupGit(projectDir);
|
|
181
|
+
initGit(projectDir);
|
|
182
|
+
|
|
183
|
+
return new WizardTestEnv(
|
|
184
|
+
'node',
|
|
185
|
+
[
|
|
186
|
+
binPath,
|
|
187
|
+
'--debug',
|
|
188
|
+
'-i',
|
|
189
|
+
integration,
|
|
190
|
+
'--preSelectedProject.authToken',
|
|
191
|
+
TEST_ARGS.AUTH_TOKEN,
|
|
192
|
+
'--preSelectedProject.dsn',
|
|
193
|
+
TEST_ARGS.PROJECT_DSN,
|
|
194
|
+
],
|
|
195
|
+
{ cwd: projectDir },
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Read the file contents and check if it contains the given content
|
|
201
|
+
*
|
|
202
|
+
* @param filePath
|
|
203
|
+
* @param content
|
|
204
|
+
*/
|
|
205
|
+
export function checkFileContents(
|
|
206
|
+
filePath: string,
|
|
207
|
+
content: string | string[],
|
|
208
|
+
) {
|
|
209
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
210
|
+
const contentArray = Array.isArray(content) ? content : [content];
|
|
211
|
+
|
|
212
|
+
for (const c of contentArray) {
|
|
213
|
+
expect(fileContent).toContain(c);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Check if the file exists
|
|
219
|
+
*
|
|
220
|
+
* @param filePath
|
|
221
|
+
*/
|
|
222
|
+
export function checkFileExists(filePath: string) {
|
|
223
|
+
expect(fs.existsSync(filePath)).toBe(true);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Check if the package.json contains the given integration
|
|
228
|
+
* @param projectDir
|
|
229
|
+
* @param integration
|
|
230
|
+
*/
|
|
231
|
+
export function checkPackageJson(projectDir: string, integration: Integration) {
|
|
232
|
+
checkFileContents(`${projectDir}/package.json`, `@sentry/${integration}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Check if the .sentryclirc contains the auth token
|
|
237
|
+
* @param projectDir
|
|
238
|
+
*/
|
|
239
|
+
export function checkSentryCliRc(projectDir: string) {
|
|
240
|
+
checkFileContents(
|
|
241
|
+
`${projectDir}/.sentryclirc`,
|
|
242
|
+
`token=${TEST_ARGS.AUTH_TOKEN}`,
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Check if the .env.sentry-build-plugin contains the auth token
|
|
248
|
+
* @param projectDir
|
|
249
|
+
*/
|
|
250
|
+
export function checkEnvBuildPlugin(projectDir: string) {
|
|
251
|
+
checkFileContents(
|
|
252
|
+
`${projectDir}/.env.sentry-build-plugin`,
|
|
253
|
+
`SENTRY_AUTH_TOKEN=${TEST_ARGS.AUTH_TOKEN}`,
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Check if the project builds
|
|
259
|
+
* @param projectDir
|
|
260
|
+
*/
|
|
261
|
+
export async function checkIfBuilds(
|
|
262
|
+
projectDir: string,
|
|
263
|
+
expectedOutput: string,
|
|
264
|
+
) {
|
|
265
|
+
const testEnv = new WizardTestEnv('npm', ['run', 'build'], {
|
|
266
|
+
cwd: projectDir,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
await expect(testEnv.waitForOutput(expectedOutput)).resolves.toBe(true);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Check if the project runs on dev mode
|
|
274
|
+
* @param projectDir
|
|
275
|
+
* @param expectedOutput
|
|
276
|
+
*/
|
|
277
|
+
export async function checkIfRunsOnDevMode(
|
|
278
|
+
projectDir: string,
|
|
279
|
+
expectedOutput: string,
|
|
280
|
+
) {
|
|
281
|
+
const testEnv = new WizardTestEnv('npm', ['run', 'dev'], { cwd: projectDir });
|
|
282
|
+
|
|
283
|
+
await expect(testEnv.waitForOutput(expectedOutput)).resolves.toBe(true);
|
|
284
|
+
testEnv.kill();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Check if the project runs on prod mode
|
|
289
|
+
* @param projectDir
|
|
290
|
+
* @param expectedOutput
|
|
291
|
+
*/
|
|
292
|
+
export async function checkIfRunsOnProdMode(
|
|
293
|
+
projectDir: string,
|
|
294
|
+
expectedOutput: string,
|
|
295
|
+
) {
|
|
296
|
+
const testEnv = new WizardTestEnv('npm', ['run', 'start'], {
|
|
297
|
+
cwd: projectDir,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
await expect(testEnv.waitForOutput(expectedOutput)).resolves.toBe(true);
|
|
301
|
+
testEnv.kill();
|
|
302
|
+
}
|
|
@@ -13,7 +13,7 @@ export class SentryProjectSelector extends BaseStep {
|
|
|
13
13
|
this.debug(answers);
|
|
14
14
|
|
|
15
15
|
if (!_.has(answers, 'wizard')) {
|
|
16
|
-
// we skip this
|
|
16
|
+
// we skip this completely because the wizard wasn't running
|
|
17
17
|
return {};
|
|
18
18
|
}
|
|
19
19
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/wizard",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.33.0",
|
|
4
4
|
"homepage": "https://github.com/getsentry/sentry-wizard",
|
|
5
5
|
"repository": "https://github.com/getsentry/sentry-wizard",
|
|
6
6
|
"description": "Sentry wizard helping you to configure your project",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@clack/prompts": "0.7.0",
|
|
28
28
|
"@sentry/cli": "^1.72.0",
|
|
29
29
|
"@sentry/node": "^7.69.0",
|
|
30
|
-
"axios": "1.
|
|
30
|
+
"axios": "1.7.4",
|
|
31
31
|
"chalk": "^2.4.1",
|
|
32
32
|
"glob": "^8.1.0",
|
|
33
33
|
"inquirer": "^6.2.0",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@sentry-internal/eslint-config-sdk": "^7.48.0",
|
|
47
|
+
"@types/chai": "^4.3.17",
|
|
47
48
|
"@types/glob": "^7.2.0",
|
|
48
49
|
"@types/inquirer": "^0.0.43",
|
|
49
50
|
"@types/jest": "^29.5.0",
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
"rimraf": "^3.0.2",
|
|
63
64
|
"ts-jest": "^29.1.0",
|
|
64
65
|
"ts-node": "^10.9.1",
|
|
66
|
+
"tsx": "^3.14.0",
|
|
65
67
|
"typescript": "^5.0.4"
|
|
66
68
|
},
|
|
67
69
|
"resolutions": {
|
|
@@ -85,6 +87,7 @@
|
|
|
85
87
|
"fix:prettier": "prettier --write \"{lib,src,test}/**/*.ts\"",
|
|
86
88
|
"fix:eslint": "eslint . --format stylish --fix",
|
|
87
89
|
"test": "yarn build && jest",
|
|
90
|
+
"test:e2e": "yarn build && jest -c=\"./e2e-tests/jest.config.ts\" ./e2e-tests/",
|
|
88
91
|
"try": "ts-node bin.ts",
|
|
89
92
|
"try:uninstall": "ts-node bin.ts --uninstall",
|
|
90
93
|
"test:watch": "jest --watch"
|
|
@@ -112,7 +115,8 @@
|
|
|
112
115
|
"/dist/",
|
|
113
116
|
"/node_modules/",
|
|
114
117
|
"\\.d\\.(jsx?|tsx?)$",
|
|
115
|
-
"\\.no-jest\\.(jsx?|tsx?)$"
|
|
118
|
+
"\\.no-jest\\.(jsx?|tsx?)$",
|
|
119
|
+
"/e2e-tests/"
|
|
116
120
|
],
|
|
117
121
|
"testEnvironment": "node"
|
|
118
122
|
},
|
package/src/remix/sdk-example.ts
CHANGED
|
@@ -13,6 +13,12 @@ export async function createExamplePage(options: {
|
|
|
13
13
|
url: string;
|
|
14
14
|
isTS: boolean;
|
|
15
15
|
}) {
|
|
16
|
+
const routesPath = 'app/routes';
|
|
17
|
+
|
|
18
|
+
if (!fs.existsSync(routesPath)) {
|
|
19
|
+
fs.mkdirSync(routesPath, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
const exampleRoutePath = `app/routes/sentry-example-page.${
|
|
17
23
|
options.isTS ? 'ts' : 'js'
|
|
18
24
|
}x`;
|