Skip to content

Commit 1709df2

Browse files
lobsterkatieonurtemizkan
authored andcommitted
ref(nextjs): Improve integration test debugging (#4144)
This includes two changes I made to help myself while debugging the nextjs integration tests for a recent PR: 1) Add the ability to set `debug: true` in `Sentry.init()` via a command line flag. In order to do this, I changed the `--debug` flag from a boolean to one which can appear multiple times and which accepts an optional string, either `requests` or `logs`, with the following behavior: - `yarn test:integration` -> no debug logging of any kind (matches current behavior) - `yarn test:integration --debug` -> sets `debug: true` in `Sentry.init()` (change in behavior) - `yarn test:integration --debug logs` -> sets `debug: true` in `Sentry.init()` (change in behavior) - `yarn test:integration --debug requests` -> logs intercepted requests (current behavior of `--debug` flag) I chose to make SDK debug logging the default (what you get if you just use `--debug` without an argument) because of the two options, it is significantly more useful in my experience. (Logging intercepted requests gets unwieldy fast, as they are large objects and end up just creating a lot of noise to sift through in order to find the one piece of data you might want.) Since bare `--debug` has up until now logged intercepted requests, this is technically a breaking change, but since these tests have a userbase of under 5 people, all of them on our team, I figured we could probably roll with it. :-) 2) Improve the VSCode debugger setup for the integration tests. This involved: - Adding a bash function to link sentry packages into the test app, so that we can test against any changes we make without having to reinstall all of the test app's dependencies again just to use the updated files. - Creating a task to be executed before each run of the debugger, which uses the above script to link monorepo packages (if not already linked) and then rebuilds the test app. (This happens by calling a new `yarn` script.) - Setting up the debugger's sourcemap config, so we can step through TS files rather than the generated JS files. - Adding more comments to the debug config to explain what each bit does and why.
1 parent a32460b commit 1709df2

File tree

12 files changed

+106
-19
lines changed

12 files changed

+106
-19
lines changed

.vscode/launch.json

+27-8
Original file line numberDiff line numberDiff line change
@@ -51,28 +51,47 @@
5151

5252

5353
// @sentry/nextjs - Run a specific integration test file
54-
// Must have file in currently active tab when hitting the play button
54+
// Must have test file in currently active tab when hitting the play button, and must already have run `yarn` in test app directory
5555
{
5656
"name": "Debug @sentry/nextjs integration tests - just open file",
5757
"type": "node",
5858
"cwd": "${workspaceFolder}/packages/nextjs",
5959
"request": "launch",
60-
// TODO create a build task
61-
// "preLaunchTask": "yarn build",
62-
63-
// this is going straight to `server.js` (rather than running the tests through yarn) in order to be able to skip
64-
// having to reinstall dependencies on every new test run
60+
// since we're not using the normal test runner, we need to make sure we're using the current version of all local
61+
// SDK packages and then manually rebuild the test app
62+
"preLaunchTask": "Prepare nextjs integration test app for debugging",
63+
// running `server.js` directly (rather than running the tests through yarn) allows us to skip having to reinstall
64+
// dependencies on every new test run
6565
"program": "${workspaceFolder}/packages/nextjs/test/integration/test/server.js",
6666
"args": [
6767
"--debug",
6868
// remove these two lines to run all integration tests
6969
"--filter",
7070
"${fileBasename}"
7171
],
72-
"sourceMaps": true,
72+
7373
"skipFiles": [
74-
"<node_internals>/**", "**/tslib/**"
74+
"<node_internals>/**",
75+
// this prevents us from landing in a neverending cycle of TS async-polyfill functions as we're stepping through
76+
// our code
77+
"${workspaceFolder}/node_modules/tslib/**/*"
7578
],
79+
"sourceMaps": true,
80+
// this controls which files are sourcemapped
81+
"outFiles": [
82+
// our SDK code
83+
"${workspaceFolder}/**/dist/**/*.js",
84+
// the built test app
85+
"${workspaceFolder}/packages/nextjs/test/integration/.next/**/*.js",
86+
"!**/node_modules/**"
87+
],
88+
"resolveSourceMapLocations": [
89+
"${workspaceFolder}/**/dist/**",
90+
"${workspaceFolder}/packages/nextjs/test/integration/.next/**",
91+
"!**/node_modules/**"
92+
],
93+
"internalConsoleOptions": "openOnSessionStart"
94+
7695
},
7796
]
7897
}

.vscode/tasks.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
// See https://go.microsoft.com/fwlink/?LinkId=733558 for documentation about `tasks.json` syntax
3+
"version": "2.0.0",
4+
"tasks": [
5+
{
6+
"label": "Prepare nextjs integration test app for VSCode debugger",
7+
"type": "npm",
8+
"script": "predebug",
9+
"path": "packages/nextjs/test/integration/",
10+
"detail": "Link the SDK (if not already linked) and build test app"
11+
}
12+
]
13+
}

packages/nextjs/test/integration/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"scripts": {
55
"dev": "next",
66
"build": "next build",
7+
"predebug": "source ../integration_test_utils.sh && link_monorepo_packages '../../..' && yarn build",
78
"start": "next start"
89
},
910
"dependencies": {

packages/nextjs/test/integration/pages/api/http/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { get } from 'http';
33
import { NextApiRequest, NextApiResponse } from 'next';
44

55
const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
6+
// make an outgoing request in order to test that the `Http` integration creates a span
67
await new Promise(resolve => get('http://example.com', resolve));
8+
79
res.status(200).json({});
810
};
911

packages/nextjs/test/integration/pages/fetch.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const ButtonPage = (): JSX.Element => (
22
<button
33
onClick={() => {
4+
// test that a span is created in the pageload transaction for this fetch request
45
fetch('http://example.com').catch(() => {
56
// no-empty
67
});

packages/nextjs/test/integration/sentry.client.config.js

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { Integrations } from '@sentry/tracing';
44
Sentry.init({
55
dsn: 'https://[email protected]/1337',
66
tracesSampleRate: 1,
7+
debug: process.env.SDK_DEBUG,
8+
79
integrations: [
810
new Integrations.BrowserTracing({
911
// Used for testing http tracing

packages/nextjs/test/integration/sentry.server.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as Sentry from '@sentry/nextjs';
33
Sentry.init({
44
dsn: 'https://[email protected]/1337',
55
tracesSampleRate: 1,
6+
debug: process.env.SDK_DEBUG,
67

78
integrations: defaults => [
89
...defaults.filter(

packages/nextjs/test/integration/test/runner.js

+21-2
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ const argv = yargs(process.argv.slice(2))
1414
.option('silent', {
1515
type: 'boolean',
1616
description: 'Hide all stdout and console logs except test results',
17+
conflicts: ['debug'],
1718
})
1819
.option('debug', {
19-
type: 'boolean',
20-
description: 'Log intercepted requests and debug messages',
20+
type: 'string',
21+
description: 'Log intercepted requests and/or debug messages',
22+
choices: ['', 'requests', 'logs'], // empty string will be equivalent to "logs"
23+
conflicts: ['silent'],
2124
})
2225
.option('depth', {
2326
type: 'number',
@@ -82,6 +85,22 @@ module.exports.run = async ({
8285
scenarios.forEach(s => log(`⊙ Scenario found: ${path.basename(s)}`));
8386
}
8487
}
88+
89+
// Turn on the SDK's `debug` option or the logging of intercepted requests, or both
90+
91+
// `yarn test:integration --debug` or
92+
// `yarn test:integration --debug logs` or
93+
// `yarn test:integration --debug logs --debug requests`
94+
if (argv.debug === '' || argv.debug === 'logs' || (Array.isArray(argv.debug) && argv.debug.includes('logs'))) {
95+
process.env.SDK_DEBUG = true; // will set `debug: true` in `Sentry.init()
96+
}
97+
98+
// `yarn test:integration --debug requests` or
99+
// `yarn test:integration --debug logs --debug requests`
100+
if (argv.debug === 'requests' || (Array.isArray(argv.debug) && argv.debug.includes('requests'))) {
101+
process.env.LOG_REQUESTS = true;
102+
}
103+
85104
// Silence all the unnecessary server noise. We are capturing errors manualy anyway.
86105
if (argv.silent) {
87106
for (const level of ['log', 'warn', 'info', 'error']) {

packages/nextjs/test/integration/test/server/tracingHttp.js

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const { getAsync, interceptTracingRequest } = require('../utils/server');
88
module.exports = async ({ url: urlBase, argv }) => {
99
const url = `${urlBase}/api/http`;
1010

11+
// this intercepts the outgoing request made by the route handler (which it makes in order to test span creation)
1112
nock('http://example.com')
1213
.get('/')
1314
.reply(200, 'ok');

packages/nextjs/test/integration/test/utils/client.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ const createRequestInterceptor = env => {
2020
}
2121

2222
if (isEventRequest(request)) {
23-
logIf(env.argv.debug, 'Intercepted Event', extractEventFromRequest(request), env.argv.depth);
23+
logIf(process.env.LOG_REQUESTS, 'Intercepted Event', extractEventFromRequest(request), env.argv.depth);
2424
env.requests.events.push(request);
2525
} else if (isSessionRequest(request)) {
26-
logIf(env.argv.debug, 'Intercepted Session', extractEnvelopeFromRequest(request), env.argv.depth);
26+
logIf(process.env.LOG_REQUESTS, 'Intercepted Session', extractEnvelopeFromRequest(request), env.argv.depth);
2727
env.requests.sessions.push(request);
2828
} else if (isTransactionRequest(request)) {
29-
logIf(env.argv.debug, 'Intercepted Transaction', extractEnvelopeFromRequest(request), env.argv.depth);
29+
logIf(process.env.LOG_REQUESTS, 'Intercepted Transaction', extractEnvelopeFromRequest(request), env.argv.depth);
3030
env.requests.transactions.push(request);
3131
}
3232

packages/nextjs/test/integration/test/utils/server.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const interceptEventRequest = (expectedEvent, argv, testName = '') => {
2727
return nock('https://dsn.ingest.sentry.io')
2828
.post('/api/1337/store/', body => {
2929
logIf(
30-
argv.debug,
30+
process.env.LOG_REQUESTS,
3131
'\nIntercepted Event' + (testName.length ? ` (from test \`${testName}\`)` : ''),
3232
body,
3333
argv.depth,
@@ -42,7 +42,7 @@ const interceptSessionRequest = (expectedItem, argv, testName = '') => {
4242
.post('/api/1337/envelope/', body => {
4343
const { envelopeHeader, itemHeader, item } = parseEnvelope(body);
4444
logIf(
45-
argv.debug,
45+
process.env.LOG_REQUESTS,
4646
'\nIntercepted Session' + (testName.length ? ` (from test \`${testName}\`)` : ''),
4747
{ envelopeHeader, itemHeader, item },
4848
argv.depth,
@@ -57,7 +57,7 @@ const interceptTracingRequest = (expectedItem, argv, testName = '') => {
5757
.post('/api/1337/envelope/', body => {
5858
const { envelopeHeader, itemHeader, item } = parseEnvelope(body);
5959
logIf(
60-
argv.debug,
60+
process.env.LOG_REQUESTS,
6161
'\nIntercepted Transaction' + (testName.length ? ` (from test \`${testName}\`)` : ''),
6262
{ envelopeHeader, itemHeader, item },
6363
argv.depth,

packages/nextjs/test/integration_test_utils.sh

+31-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
function link_package() {
22
local package_abs_path=$1
3-
local package_name=$2
3+
# strip the 'sentry-' prefix from the repo name of packages not in the monorepo (`cli`, `webpack-plugin`, and `wizard`)
4+
local package_name=$(basename $package_abs_path | sed s/sentry-//)
45

56
echo "Setting up @sentry/${package_name} for linking"
67
pushd $package_abs_path
@@ -21,7 +22,7 @@ function linkcli() {
2122

2223
# check to make sure the repo directory exists
2324
if [[ -d $LINKED_CLI_REPO ]]; then
24-
link_package $LINKED_CLI_REPO "cli"
25+
link_package $LINKED_CLI_REPO
2526
else
2627
# the $1 lets us insert a string in that spot if one is passed to `linkcli` (useful for when we're calling this from
2728
# within another linking function)
@@ -36,7 +37,7 @@ function linkplugin() {
3637

3738
# check to make sure the repo directory exists
3839
if [[ -d $LINKED_PLUGIN_REPO ]]; then
39-
link_package $LINKED_PLUGIN_REPO "webpack-plugin"
40+
link_package $LINKED_PLUGIN_REPO
4041

4142
# the webpack plugin depends on `@sentry/cli`, so if we're also using a linked version of the cli package, the
4243
# plugin needs to link to it, too
@@ -49,3 +50,30 @@ function linkplugin() {
4950
echo "ERROR: Can't link @sentry/wepack-plugin because $LINKED_PLUGIN_REPO does not exist."
5051
fi
5152
}
53+
54+
# This is only really useful for running tests in the debugger, as the normal test runner reinstalls all SDK packages
55+
# from the local files on each test run
56+
function link_monorepo_packages() {
57+
local repo_packages_dir=$1
58+
59+
for abs_package_path in ${repo_packages_dir}/*; do
60+
local package_name=$(basename $abs_package_path)
61+
62+
# Skip packages under the `@sentry-internal` namespace (our function is only linking packages in the `@sentry`
63+
# namespace, and besides, there's no reason to link such packages, as they're purely SDK dev dependencies).
64+
#
65+
# (The regex test ( `=~` ) is a sneaky way of testing if `package_name` is any of the three packages listed: if the
66+
# string containing all of the packages containes a match to the regex solely consisting of the current package
67+
# name, the current package must be in the list.)
68+
if [[ "eslint-config-sdk eslint-plugin-sdk typescript" =~ $package_name ]]; then
69+
continue
70+
fi
71+
72+
# `-L` tests if the given file is a symbolic link, to see if linking has already been done
73+
if [[ ! -L node_modules/@sentry/$package_name ]]; then
74+
echo "Linking @sentry/$package_name"
75+
link_package $abs_package_path >/dev/null 2>&1
76+
fi
77+
78+
done
79+
}

0 commit comments

Comments
 (0)