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); `; }