diff --git a/static/app/gettingStartedDocs/javascript-ember/onboarding.spec.tsx b/static/app/gettingStartedDocs/javascript-ember/onboarding.spec.tsx
index 74a7036de76e..e93c0129d055 100644
--- a/static/app/gettingStartedDocs/javascript-ember/onboarding.spec.tsx
+++ b/static/app/gettingStartedDocs/javascript-ember/onboarding.spec.tsx
@@ -18,12 +18,79 @@ describe('javascript-ember onboarding docs', () => {
).toBeInTheDocument();
expect(screen.getByRole('heading', {name: 'Verify'})).toBeInTheDocument();
- // Includes import statement in multiple places
expect(
- screen.getAllByText(
- textWithMarkupMatcher(/import \* as Sentry from "@sentry\/ember"/)
- )
- ).toHaveLength(2); // Appears in configure and verify steps
+ screen.getByText(textWithMarkupMatcher(/import \* as Sentry from "@sentry\/ember"/))
+ ).toBeInTheDocument();
+ });
+
+ it('initializes the SDK directly and loads application initializers', () => {
+ renderWithOnboardingLayout(docs);
+
+ const setup = screen.getByText(textWithMarkupMatcher(/Sentry\.init\(/));
+ expect(setup).toHaveTextContent('import config from "./config/environment"');
+ expect(setup).toHaveTextContent('loadInitializers(App, config.modulePrefix)');
+ expect(setup).toHaveTextContent('dataCollection:');
+ expect(setup).not.toHaveTextContent(/sendDefaultPii|enableLogs|enableMetrics/);
+ });
+
+ it('registers a performance instance initializer when tracing is selected', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [ProductSolution.PERFORMANCE_MONITORING],
+ });
+
+ const initializer = screen.getByText(
+ textWithMarkupMatcher(/export function initialize\(appInstance\)/)
+ );
+ expect(initializer).toHaveTextContent(
+ 'import { instrumentAppInstancePerformance } from "@sentry/ember"'
+ );
+ expect(initializer).toHaveTextContent(
+ 'instrumentAppInstancePerformance(appInstance)'
+ );
+ expect(initializer).toHaveTextContent('export default { initialize }');
+ });
+
+ it('omits performance instrumentation when tracing is not selected', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [ProductSolution.ERROR_MONITORING],
+ });
+
+ expect(
+ screen.queryByText(textWithMarkupMatcher(/instrumentAppInstancePerformance/))
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByText(textWithMarkupMatcher(/tracesSampleRate/))
+ ).not.toBeInTheDocument();
+ });
+
+ it('verifies errors with a component action and button', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [ProductSolution.ERROR_MONITORING],
+ });
+
+ const component = screen.getByText(textWithMarkupMatcher(/throw new Error/));
+ expect(component).toHaveTextContent('extends Component');
+ expect(component).toHaveTextContent(/@action\s*triggerError\(\)/);
+ expect(component).not.toHaveTextContent(/setTimeout|@sentry\/ember/);
+ expect(
+ screen.getByText(textWithMarkupMatcher(/\{\{on "click" this\.triggerError\}\}/))
+ ).toHaveTextContent('Break the world');
+ });
+
+ it('sends both selected signals before the verification error', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [
+ ProductSolution.ERROR_MONITORING,
+ ProductSolution.LOGS,
+ ProductSolution.METRICS,
+ ],
+ });
+
+ const component = screen.getByText(textWithMarkupMatcher(/throw new Error/));
+ expect(component).toHaveTextContent('import * as Sentry from "@sentry/ember"');
+ expect(component).toHaveTextContent(
+ /Sentry\.logger\.info.*Sentry\.metrics\.count.*throw new Error/
+ );
});
it('displays sample rates by default', () => {
diff --git a/static/app/gettingStartedDocs/javascript-ember/onboarding.tsx b/static/app/gettingStartedDocs/javascript-ember/onboarding.tsx
index 27a29df8fe94..41e3f73d949b 100644
--- a/static/app/gettingStartedDocs/javascript-ember/onboarding.tsx
+++ b/static/app/gettingStartedDocs/javascript-ember/onboarding.tsx
@@ -1,4 +1,5 @@
import type {
+ ContentBlock,
DocsParams,
OnboardingConfig,
} from 'sentry/components/onboarding/gettingStartedDoc/types';
@@ -10,30 +11,37 @@ import {getSdkSetupSnippet, installSnippetBlock} from './utils';
const getVerifyEmberSnippet = (params: DocsParams) => {
const logsCode = params.isLogsSelected
- ? `// Send a log before throwing the error
- Sentry.logger.info(Sentry.logger.fmt\`User \${"sentry-test"} triggered test error button\`, {
- action: "test_error_button_click",
+ ? ` // Send a log before throwing the error
+ Sentry.logger.info('User triggered test error', {
+ action: 'test_error_button_click',
});
`
: '';
const metricsCode = params.isMetricsSelected
- ? `// Send a test metric before throwing the error
+ ? ` // Send a test metric before throwing the error
Sentry.metrics.count('test_counter', 1);
`
: '';
- return `
-import * as Sentry from "@sentry/ember";
-
-setTimeout(() => {
- ${logsCode}${metricsCode}throw new Error("Sentry Test Error");
-});`;
+ return `import Component from "@glimmer/component";
+import { action } from "@ember/object";
+${
+ params.isLogsSelected || params.isMetricsSelected
+ ? 'import * as Sentry from "@sentry/ember";\n'
+ : ''
+}
+export default class SentryTestComponent extends Component {
+ @action
+ triggerError() {
+${logsCode}${metricsCode} throw new Error("Sentry Test Error");
+ }
+}`;
};
export const onboarding: OnboardingConfig = {
introduction: () =>
- tct("In this quick guide you'll use [strong:npm] or [strong:yarn] to set up:", {
+ tct("In this quick guide you'll use the [strong:Ember CLI] to set up:", {
strong: ,
}),
install: () => [
@@ -57,7 +65,7 @@ export const onboarding: OnboardingConfig = {
{
type: 'text',
text: tct(
- 'You should [code:init] the Sentry SDK as soon as possible during your application load up in [code:app.js], before initializing Ember:',
+ 'Initialize Sentry in [code:app/app.js], before the Application class. Keep the [code:loadInitializers] call so Ember loads your initializers:',
{
code: ,
}
@@ -69,10 +77,39 @@ export const onboarding: OnboardingConfig = {
{
label: 'JavaScript',
language: 'javascript',
+ filename: 'app/app.js',
code: getSdkSetupSnippet(params),
},
],
},
+ ...((params.isPerformanceSelected
+ ? [
+ {
+ type: 'text',
+ text: tct(
+ 'To enable tracing, create [code:app/instance-initializers/sentry-performance.js]. The v2 addon does not register performance instrumentation automatically:',
+ {code: }
+ ),
+ },
+ {
+ type: 'code',
+ tabs: [
+ {
+ label: 'JavaScript',
+ language: 'javascript',
+ filename: 'app/instance-initializers/sentry-performance.js',
+ code: `import { instrumentAppInstancePerformance } from "@sentry/ember";
+
+export function initialize(appInstance) {
+ instrumentAppInstancePerformance(appInstance);
+}
+
+export default { initialize };`,
+ },
+ ],
+ },
+ ]
+ : []) satisfies ContentBlock[]),
],
},
getUploadSourceMapsStep({
@@ -86,8 +123,9 @@ export const onboarding: OnboardingConfig = {
content: [
{
type: 'text',
- text: t(
- "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
+ text: tct(
+ 'Create a [code:SentryTest] component with the following class and template:',
+ {code: }
),
},
{
@@ -96,10 +134,31 @@ export const onboarding: OnboardingConfig = {
{
label: 'JavaScript',
language: 'javascript',
+ filename: 'app/components/sentry-test.js',
code: getVerifyEmberSnippet(params),
},
],
},
+ {
+ type: 'code',
+ tabs: [
+ {
+ label: 'Handlebars',
+ language: 'html',
+ filename: 'app/components/sentry-test.hbs',
+ code: ``,
+ },
+ ],
+ },
+ {
+ type: 'text',
+ text: tct(
+ 'Render [code:] in an application template, then click "Break the world" to send a test error to Sentry. If you selected Logs or Metrics, clicking the button sends those too.',
+ {code: }
+ ),
+ },
],
},
],
diff --git a/static/app/gettingStartedDocs/javascript-ember/utils.tsx b/static/app/gettingStartedDocs/javascript-ember/utils.tsx
index 1d7f6c3cc2dd..e5978eda4d8b 100644
--- a/static/app/gettingStartedDocs/javascript-ember/utils.tsx
+++ b/static/app/gettingStartedDocs/javascript-ember/utils.tsx
@@ -43,7 +43,7 @@ const getDynamicParts = (params: DocsParams): string[] => {
if (params.isPerformanceSelected) {
dynamicParts.push(`
// Tracing
- tracesSampleRate: 1.0, // Capture 100% of the transactions
+ tracesSampleRate: 1.0, // Capture 100% of the traces
// Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/]`);
}
@@ -91,6 +91,8 @@ export default class App extends Application {
podModulePrefix = config.podModulePrefix;
Resolver = Resolver;
}
+
+loadInitializers(App, config.modulePrefix);
`;
}