Skip to content

Commit fb46556

Browse files
authored
1 parent cbf33d9 commit fb46556

File tree

3 files changed

+213
-71
lines changed

3 files changed

+213
-71
lines changed

static/app/components/onboarding/productSelection.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export const platformProductAvailability = {
137137
'javascript-react-router': [
138138
ProductSolution.PERFORMANCE_MONITORING,
139139
ProductSolution.SESSION_REPLAY,
140+
ProductSolution.LOGS,
140141
],
141142
'javascript-vue': [
142143
ProductSolution.PERFORMANCE_MONITORING,
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout';
2+
import {screen} from 'sentry-test/reactTestingLibrary';
3+
import {textWithMarkupMatcher} from 'sentry-test/utils';
4+
5+
import {ProductSolution} from 'sentry/components/onboarding/gettingStartedDoc/types';
6+
7+
import docs from './react-router';
8+
9+
describe('javascript-react-router onboarding docs', function () {
10+
it('renders onboarding docs correctly', () => {
11+
renderWithOnboardingLayout(docs);
12+
13+
// Renders main headings
14+
expect(screen.getByRole('heading', {name: 'Install'})).toBeInTheDocument();
15+
expect(screen.getByRole('heading', {name: 'Configure SDK'})).toBeInTheDocument();
16+
expect(
17+
screen.getByRole('heading', {name: /Upload Source Maps/i})
18+
).toBeInTheDocument();
19+
expect(screen.getByRole('heading', {name: 'Verify'})).toBeInTheDocument();
20+
21+
// Includes Sentry import statement in multiple configure sections
22+
expect(
23+
screen.getAllByText(
24+
textWithMarkupMatcher(/import \* as Sentry from ["']@sentry\/react-router["'];/)
25+
)
26+
).toHaveLength(4);
27+
});
28+
29+
it('displays sample rates by default', () => {
30+
renderWithOnboardingLayout(docs, {
31+
selectedProducts: [
32+
ProductSolution.ERROR_MONITORING,
33+
ProductSolution.PERFORMANCE_MONITORING,
34+
ProductSolution.SESSION_REPLAY,
35+
],
36+
});
37+
38+
expect(
39+
screen.getAllByText(textWithMarkupMatcher(/tracesSampleRate/)).length
40+
).toBeGreaterThan(0);
41+
expect(
42+
screen.getAllByText(textWithMarkupMatcher(/replaysSessionSampleRate/)).length
43+
).toBeGreaterThan(0);
44+
expect(
45+
screen.getAllByText(textWithMarkupMatcher(/replaysOnErrorSampleRate/)).length
46+
).toBeGreaterThan(0);
47+
});
48+
49+
it('enables performance by setting tracesSampleRate to 1 and adding integration', () => {
50+
renderWithOnboardingLayout(docs, {
51+
selectedProducts: [
52+
ProductSolution.ERROR_MONITORING,
53+
ProductSolution.PERFORMANCE_MONITORING,
54+
],
55+
});
56+
57+
expect(
58+
screen.getAllByText(textWithMarkupMatcher(/tracesSampleRate: 1\.0/)).length
59+
).toBeGreaterThan(0);
60+
expect(
61+
screen.getByText(textWithMarkupMatcher(/Sentry\.reactRouterTracingIntegration\(\)/))
62+
).toBeInTheDocument();
63+
});
64+
65+
it('enables replay by setting replay sample rates', () => {
66+
renderWithOnboardingLayout(docs, {
67+
selectedProducts: [
68+
ProductSolution.ERROR_MONITORING,
69+
ProductSolution.SESSION_REPLAY,
70+
],
71+
});
72+
73+
expect(
74+
screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate: 0\.1/))
75+
).toBeInTheDocument();
76+
expect(
77+
screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate: 1\.0/))
78+
).toBeInTheDocument();
79+
});
80+
81+
it('enables profiling by setting profiling sample rates', () => {
82+
renderWithOnboardingLayout(docs, {
83+
selectedProducts: [ProductSolution.ERROR_MONITORING, ProductSolution.PROFILING],
84+
});
85+
86+
expect(
87+
screen.getByText(textWithMarkupMatcher(/profilesSampleRate: 1\.0/))
88+
).toBeInTheDocument();
89+
expect(
90+
screen.getByText(textWithMarkupMatcher(/nodeProfilingIntegration\(\)/))
91+
).toBeInTheDocument();
92+
});
93+
94+
it('enables logs by setting enableLogs to true and shows logger usage in verify step', () => {
95+
renderWithOnboardingLayout(docs, {
96+
selectedProducts: [ProductSolution.ERROR_MONITORING, ProductSolution.LOGS],
97+
});
98+
99+
expect(
100+
screen.getAllByText(textWithMarkupMatcher(/enableLogs: true/)).length
101+
).toBeGreaterThan(0);
102+
103+
// When logs are selected, verify step includes a Sentry logger call
104+
expect(
105+
screen.getByText(textWithMarkupMatcher(/Sentry\.logger\.info\(/))
106+
).toBeInTheDocument();
107+
});
108+
});

static/app/gettingStartedDocs/javascript/react-router.tsx

Lines changed: 104 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -25,61 +25,68 @@ import {getNodeAgentMonitoringOnboarding} from 'sentry/utils/gettingStartedDocs/
2525

2626
type Params = DocsParams;
2727

28-
const getClientSetupSnippet = (params: Params) => `
29-
import * as Sentry from "@sentry/react-router";
30-
import { startTransition, StrictMode } from "react";
31-
import { hydrateRoot } from "react-dom/client";
32-
import { HydratedRouter } from "react-router/dom";
28+
const getClientSetupSnippet = (params: Params) => {
29+
const logsSnippet = params.isLogsSelected
30+
? `
31+
// Enable logs to be sent to Sentry
32+
enableLogs: true,`
33+
: '';
3334

34-
Sentry.init({
35-
dsn: "${params.dsn.public}",
35+
const performanceSnippet = params.isPerformanceSelected
36+
? `
37+
tracesSampleRate: 1.0, // Capture 100% of the transactions
38+
// Set \`tracePropagationTargets\` to declare which URL(s) should have trace propagation enabled
39+
tracePropagationTargets: [/^\\//, /^https:\\/\\/yourserver\\.io\\/api/],`
40+
: '';
3641

37-
// Adds request headers and IP for users, for more info visit:
38-
// https://docs.sentry.io/platforms/javascript/guides/react-router/configuration/options/#sendDefaultPii
39-
sendDefaultPii: true,
42+
const replaySnippet = params.isReplaySelected
43+
? `
44+
replaysSessionSampleRate: 0.1, // Capture 10% of all sessions
45+
replaysOnErrorSampleRate: 1.0, // Capture 100% of sessions with an error`
46+
: '';
4047

41-
integrations: [${
42-
params.isPerformanceSelected
43-
? `
44-
// Performance
45-
Sentry.reactRouterTracingIntegration(),`
46-
: ''
47-
}${
48-
params.isReplaySelected
49-
? `
48+
const integrationsList = [];
49+
50+
if (params.isPerformanceSelected) {
51+
integrationsList.push(`
52+
// Tracing
53+
Sentry.reactRouterTracingIntegration(),`);
54+
}
55+
56+
if (params.isReplaySelected) {
57+
integrationsList.push(`
5058
// Session Replay
51-
Sentry.replayIntegration(${getReplayConfigOptions(params.replayOptions)}),`
52-
: ''
53-
}${
54-
params.isFeedbackSelected
55-
? `
59+
Sentry.replayIntegration(${getReplayConfigOptions(params.replayOptions)}),`);
60+
}
61+
62+
if (params.isFeedbackSelected) {
63+
integrationsList.push(`
5664
// User Feedback
5765
Sentry.feedbackIntegration({
5866
// Additional SDK configuration goes in here, for example:
5967
colorScheme: "system",
6068
${getFeedbackConfigOptions(params.feedbackOptions)}
61-
}),`
62-
: ''
69+
}),`);
6370
}
64-
],${
65-
params.isPerformanceSelected
66-
? `
67-
68-
tracesSampleRate: 1.0, // Capture 100% of the transactions
6971

70-
// Set \`tracePropagationTargets\` to declare which URL(s) should have trace propagation enabled
71-
tracePropagationTargets: [/^\\//, /^https:\\/\\/yourserver\\.io\\/api/],`
72-
: ''
73-
}${
74-
params.isReplaySelected
72+
const integrationsCode =
73+
integrationsList.length > 0
7574
? `
75+
integrations: [${integrationsList.join('')}
76+
],`
77+
: '';
7678

77-
// Capture Replay for 10% of all sessions,
78-
// plus 100% of sessions with an error
79-
replaysSessionSampleRate: 0.1,
80-
replaysOnErrorSampleRate: 1.0,`
81-
: ''
82-
}
79+
return `
80+
import * as Sentry from "@sentry/react-router";
81+
import { startTransition, StrictMode } from "react";
82+
import { hydrateRoot } from "react-dom/client";
83+
import { HydratedRouter } from "react-router/dom";
84+
85+
Sentry.init({
86+
dsn: "${params.dsn.public}",
87+
// Adds request headers and IP for users, for more info visit:
88+
// https://docs.sentry.io/platforms/javascript/guides/react-router/configuration/options/#sendDefaultPii
89+
sendDefaultPii: true,${integrationsCode}${logsSnippet}${performanceSnippet}${replaySnippet}
8390
});
8491
8592
startTransition(() => {
@@ -90,6 +97,7 @@ startTransition(() => {
9097
</StrictMode>
9198
);
9299
});`;
100+
};
93101

94102
const getRootErrorBoundarySnippet = () => `
95103
import * as Sentry from "@sentry/react-router";
@@ -127,37 +135,53 @@ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
127135
);
128136
}`;
129137

130-
const getServerSetupSnippet = (params: Params) => `
131-
import * as Sentry from "@sentry/react-router";${
132-
params.isProfilingSelected
138+
const getServerSetupSnippet = (params: Params) => {
139+
const logsSnippet = params.isLogsSelected
140+
? `
141+
// Enable logs to be sent to Sentry
142+
enableLogs: true,`
143+
: '';
144+
145+
const performanceSnippet = params.isPerformanceSelected
146+
? `
147+
tracesSampleRate: 1.0, // Capture 100% of the transactions`
148+
: '';
149+
150+
const profilingSnippet = params.isProfilingSelected
151+
? `
152+
profilesSampleRate: 1.0, // profile every transaction`
153+
: '';
154+
155+
const profilingImport = params.isProfilingSelected
133156
? `
134157
import { nodeProfilingIntegration } from '@sentry/profiling-node';`
135-
: ''
136-
}
158+
: '';
159+
160+
const integrationsList = [];
161+
162+
if (params.isProfilingSelected) {
163+
integrationsList.push(`
164+
// Profiling
165+
nodeProfilingIntegration(),`);
166+
}
167+
168+
const integrationsCode =
169+
integrationsList.length > 0
170+
? `
171+
integrations: [${integrationsList.join('')}
172+
],`
173+
: '';
174+
175+
return `
176+
import * as Sentry from "@sentry/react-router";${profilingImport}
137177
138178
Sentry.init({
139179
dsn: "${params.dsn.public}",
140-
141180
// Adds request headers and IP for users, for more info visit:
142181
// https://docs.sentry.io/platforms/javascript/guides/react-router/configuration/options/#sendDefaultPii
143-
sendDefaultPii: true,${
144-
params.isProfilingSelected
145-
? `
146-
147-
integrations: [nodeProfilingIntegration()],`
148-
: ''
149-
}${
150-
params.isPerformanceSelected
151-
? `
152-
tracesSampleRate: 1.0, // Capture 100% of the transactions`
153-
: ''
154-
}${
155-
params.isProfilingSelected
156-
? `
157-
profilesSampleRate: 1.0, // profile every transaction`
158-
: ''
159-
}
182+
sendDefaultPii: true,${integrationsCode}${logsSnippet}${performanceSnippet}${profilingSnippet}
160183
});`;
184+
};
161185

162186
const getServerEntrySnippet = () => `
163187
import * as Sentry from '@sentry/react-router';
@@ -178,21 +202,30 @@ export const handleError: HandleErrorFunction = (error, { request }) => {
178202
// React Router may abort some interrupted requests, don't log those
179203
if (!request.signal.aborted) {
180204
Sentry.captureException(error);
181-
// optionally log the error so you can see it
205+
// optionally log the error to the console so you can see it
182206
console.error(error);
183207
}
184208
};`;
185209

186-
const getVerifySnippet = () => `
210+
const getVerifySnippet = (params: Params) => {
211+
const logsCode = params.isLogsSelected
212+
? `
213+
// Send a log before throwing the error
214+
Sentry.logger.info("User triggered test error", {
215+
'action': 'test_loader_error',
216+
});`
217+
: '';
218+
return `
187219
import type { Route } from "./+types/error-page";
188220
189-
export async function loader() {
221+
export async function loader() {${logsCode}
190222
throw new Error("Sentry Test Error");
191223
}
192224
193225
export default function ErrorPage() {
194226
return <div>This page will throw an error!</div>;
195227
}`;
228+
};
196229

197230
const getPackageJsonScriptsSnippet = () => `
198231
{
@@ -297,7 +330,7 @@ const onboarding: OnboardingConfig = {
297330
},
298331
{
299332
description: tct(
300-
'Initialize the Sentry React Router SDK in your [code:entry.client.tsx] file:',
333+
'Initialize the Sentry React Router SDK in your [code:entry.client.tsx] file, above where you call [code:hydrateRoot]:',
301334
{code: <code />}
302335
),
303336
title: t('Client Setup'),
@@ -369,7 +402,7 @@ const onboarding: OnboardingConfig = {
369402
collapsible: true,
370403
},
371404
],
372-
verify: () => [
405+
verify: params => [
373406
{
374407
type: StepType.VERIFY,
375408
description: t(
@@ -378,7 +411,7 @@ const onboarding: OnboardingConfig = {
378411
configurations: [
379412
{
380413
language: 'tsx',
381-
code: getVerifySnippet(),
414+
code: getVerifySnippet(params),
382415
},
383416
],
384417
},

0 commit comments

Comments
 (0)