diff --git a/static/app/gettingStartedDocs/javascript/angular.spec.tsx b/static/app/gettingStartedDocs/javascript/angular.spec.tsx index 068d1fd8d4fea1..8ae6ab47ca97b6 100644 --- a/static/app/gettingStartedDocs/javascript/angular.spec.tsx +++ b/static/app/gettingStartedDocs/javascript/angular.spec.tsx @@ -4,7 +4,7 @@ import {textWithMarkupMatcher} from 'sentry-test/utils'; import {ProductSolution} from 'sentry/components/onboarding/productSelection'; -import docs from './angular'; +import docs, {AngularConfigType} from './angular'; describe('javascript-angular onboarding docs', function () { it('renders onboarding docs correctly', () => { @@ -18,14 +18,17 @@ describe('javascript-angular onboarding docs', function () { // Includes import statement expect( - screen.getByText( - textWithMarkupMatcher(/import { enableProdMode } from "@angular\/core"/) + screen.getAllByText( + textWithMarkupMatcher(/import \* as Sentry from "@sentry\/angular";/) ) - ).toBeInTheDocument(); + ).toHaveLength(2); }); it('displays sample rates by default', () => { renderWithOnboardingLayout(docs, { + selectedOptions: { + configType: AngularConfigType.APP, + }, selectedProducts: [ ProductSolution.ERROR_MONITORING, ProductSolution.PERFORMANCE_MONITORING, @@ -46,6 +49,9 @@ describe('javascript-angular onboarding docs', function () { it('enables performance setting the tracesSampleRate to 1', () => { renderWithOnboardingLayout(docs, { + selectedOptions: { + configType: AngularConfigType.APP, + }, selectedProducts: [ ProductSolution.ERROR_MONITORING, ProductSolution.PERFORMANCE_MONITORING, @@ -59,6 +65,9 @@ describe('javascript-angular onboarding docs', function () { it('enables replay by setting replay samplerates', () => { renderWithOnboardingLayout(docs, { + selectedOptions: { + configType: AngularConfigType.APP, + }, selectedProducts: [ ProductSolution.ERROR_MONITORING, ProductSolution.SESSION_REPLAY, @@ -75,6 +84,9 @@ describe('javascript-angular onboarding docs', function () { it('enables profiling by setting profiling sample rates', () => { renderWithOnboardingLayout(docs, { + selectedOptions: { + configType: AngularConfigType.APP, + }, selectedProducts: [ProductSolution.ERROR_MONITORING, ProductSolution.PROFILING], }); diff --git a/static/app/gettingStartedDocs/javascript/angular.tsx b/static/app/gettingStartedDocs/javascript/angular.tsx index ac1fd3dcf8de6d..9745e2d4b671d9 100644 --- a/static/app/gettingStartedDocs/javascript/angular.tsx +++ b/static/app/gettingStartedDocs/javascript/angular.tsx @@ -6,6 +6,7 @@ import { StepType, } from 'sentry/components/onboarding/gettingStartedDoc/step'; import type { + BasePlatformOptions, Docs, DocsParams, OnboardingConfig, @@ -31,71 +32,172 @@ import { import {ProductSolution} from 'sentry/components/onboarding/productSelection'; import {t, tct} from 'sentry/locale'; -type Params = DocsParams; +export enum AngularConfigType { + APP = 'standalone', + MODULE = 'module', +} + +const platformOptions = { + configType: { + label: t('Config Type'), + defaultValue: AngularConfigType.APP, + items: [ + { + label: 'App Config', + value: AngularConfigType.APP, + }, + { + label: 'NGModule Config', + value: AngularConfigType.MODULE, + }, + ], + }, +} satisfies BasePlatformOptions; + +type PlatformOptions = typeof platformOptions; +type Params = DocsParams; + +function isModuleConfig(params: Params) { + return params.platformOptions.configType === AngularConfigType.MODULE; +} function getSdkSetupSnippet(params: Params) { - return ` - import { enableProdMode } from "@angular/core"; - import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; - import * as Sentry from "@sentry/angular"; + const imports = isModuleConfig(params) + ? ` +import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; +import * as Sentry from "@sentry/angular"; + +import { AppModule } from "./app/app.module";` + : ` +import { bootstrapApplication } from '@angular/platform-browser'; +import * as Sentry from "@sentry/angular"; + +import { appConfig } from './app/app.config'; +import { AppComponent } from './app/app.component'; + `; + + const appInit = isModuleConfig(params) + ? ` +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err));` + : ` +bootstrapApplication(appConfig, AppComponent) + .catch((err) => console.error(err));`; - import { AppModule } from "./app/app.module"; + return `${imports.trim()} - Sentry.init({ - dsn: "${params.dsn.public}", - integrations: [${ - params.isPerformanceSelected - ? ` - Sentry.browserTracingIntegration(),` - : '' - }${ - params.isProfilingSelected - ? ` - Sentry.browserProfilingIntegration(),` - : '' - }${ - params.isFeedbackSelected - ? ` - Sentry.feedbackIntegration({ -// Additional SDK configuration goes in here, for example: -colorScheme: "system", -${getFeedbackConfigOptions(params.feedbackOptions)}}),` - : '' - }${ - params.isReplaySelected - ? ` - Sentry.replayIntegration(${getReplayConfigOptions(params.replayOptions)}),` - : '' - } +Sentry.init({ + dsn: "${params.dsn.public}", + integrations: [${ + params.isPerformanceSelected + ? ` + Sentry.browserTracingIntegration(),` + : '' + }${ + params.isProfilingSelected + ? ` + Sentry.browserProfilingIntegration(),` + : '' + }${ + params.isFeedbackSelected + ? ` + Sentry.feedbackIntegration({ + // Additional SDK configuration goes in here, for example: + colorScheme: "system", + ${getFeedbackConfigOptions(params.feedbackOptions)}}),` + : '' + }${ + params.isReplaySelected + ? ` + Sentry.replayIntegration(${getReplayConfigOptions(params.replayOptions)}),` + : '' + } ],${ params.isPerformanceSelected ? ` - // Tracing - tracesSampleRate: 1.0, // Capture 100% of the transactions - // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled - tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/],` + // Tracing + tracesSampleRate: 1.0, // Capture 100% of the transactions + // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled + tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/],` : '' }${ params.isReplaySelected ? ` - // Session Replay - replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production. - replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.` + // Session Replay + replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production. + replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.` : '' }${ params.isProfilingSelected ? ` - // Set profilesSampleRate to 1.0 to profile every transaction. - // Since profilesSampleRate is relative to tracesSampleRate, - // the final profiling rate can be computed as tracesSampleRate * profilesSampleRate - // For example, a tracesSampleRate of 0.5 and profilesSampleRate of 0.5 would - // results in 25% of transactions being profiled (0.5*0.5=0.25) - profilesSampleRate: 1.0,` + // Set profilesSampleRate to 1.0 to profile every transaction. + // Since profilesSampleRate is relative to tracesSampleRate, + // the final profiling rate can be computed as tracesSampleRate * profilesSampleRate + // For example, a tracesSampleRate of 0.5 and profilesSampleRate of 0.5 would + // results in 25% of transactions being profiled (0.5*0.5=0.25) + profilesSampleRate: 1.0,` : '' } - });`; +}); + + ${appInit.trim()}`; } +const getConfigureAppModuleSnippet = () => ` +import { APP_INITIALIZER, ErrorHandler, NgModule } from "@angular/core"; +import { Router } from "@angular/router"; +import * as Sentry from "@sentry/angular"; + +@NgModule({ + // ... + providers: [ + { + provide: ErrorHandler, + useValue: Sentry.createErrorHandler({ + showDialog: true, + }), + }, { + provide: Sentry.TraceService, + deps: [Router], + }, + { + provide: APP_INITIALIZER, + useFactory: () => () => {}, + deps: [Sentry.TraceService], + multi: true, + }, + ], +}) +export class AppModule {} +`; + +const getConfigureAppConfigSnippet = () => ` +import { APP_INITIALIZER, ApplicationConfig, ErrorHandler } from '@angular/core'; +import { Router } from '@angular/router'; +import * as Sentry from "@sentry/angular"; + +export const appConfig: ApplicationConfig = { + providers: [ + // ... + { + provide: ErrorHandler, + useValue: Sentry.createErrorHandler(), + }, + { + provide: Sentry.TraceService, + deps: [Router], + }, + { + provide: APP_INITIALIZER, + useFactory: () => () => {}, + deps: [Sentry.TraceService], + multi: true, + }, + ] +}; +`; + const getVerifySnippetTemplate = () => ` `; @@ -194,18 +296,14 @@ const getInstallConfig = () => [ }, ]; -const onboarding: OnboardingConfig = { +const onboarding: OnboardingConfig = { introduction: MaybeBrowserProfilingBetaWarning, install: () => [ { type: StepType.INSTALL, description: tct( - 'Add the Sentry SDK as a dependency using [codeNpm:npm], [codeYarn:yarn] or [codePnpm:pnpm]:', - { - codeYarn: , - codeNpm: , - codePnpm: , - } + 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn] or [code:pnpm]:', + {code: } ), configurations: getInstallConfig(), }, @@ -214,42 +312,30 @@ const onboarding: OnboardingConfig = { { type: StepType.CONFIGURE, configurations: [ - getSetupConfiguration(params), { description: tct( - "Register the Sentry Angular SDK's ErrorHandler and Tracing providers in your [codeModule:app.module.ts] file:", + `Initialize the Sentry Angular SDK in your [code:main.ts] file as early as possible, before initializing Angular:`, { - codeModule: , + code: , } ), language: 'javascript', - code: ` - import { APP_INITIALIZER, ErrorHandler, NgModule } from "@angular/core"; - import { Router } from "@angular/router"; - import * as Sentry from "@sentry/angular"; - - @NgModule({ - // ... - providers: [ - { - provide: ErrorHandler, - useValue: Sentry.createErrorHandler({ - showDialog: true, - }), - }, { - provide: Sentry.TraceService, - deps: [Router], - }, - { - provide: APP_INITIALIZER, - useFactory: () => () => {}, - deps: [Sentry.TraceService], - multi: true, - }, - ], - // ... - }) - export class AppModule {}`, + code: getSdkSetupSnippet(params), + }, + { + description: isModuleConfig(params) + ? tct( + "Register the Sentry Angular SDK's ErrorHandler and Tracing providers in your [code:app.module.ts] file:", + {code: } + ) + : tct( + "Register the Sentry Angular SDK's ErrorHandler and Tracing providers in your [code:app.config.ts] file:", + {code: } + ), + language: 'javascript', + code: isModuleConfig(params) + ? getConfigureAppModuleSnippet() + : getConfigureAppConfigSnippet(), }, ...(params.isProfilingSelected ? [getProfilingDocumentHeaderConfigurationStep()] @@ -302,27 +388,7 @@ export const nextSteps = [ }, ]; -function getSetupConfiguration(params: Params) { - const configuration = { - description: tct( - `Initialize the Sentry Angular SDK in your [code:main.ts] file as early as possible, before initializing Angular:`, - { - code: , - } - ), - language: 'javascript', - code: ` - ${getSdkSetupSnippet(params)} - - enableProdMode(); - platformBrowserDynamic() - .bootstrapModule(AppModule) - .catch((err) => console.error(err));`, - }; - return configuration; -} - -const replayOnboarding: OnboardingConfig = { +const replayOnboarding: OnboardingConfig = { install: () => [ { type: StepType.INSTALL, @@ -361,7 +427,7 @@ const replayOnboarding: OnboardingConfig = { nextSteps: () => [], }; -const feedbackOnboarding: OnboardingConfig = { +const feedbackOnboarding: OnboardingConfig = { install: () => [ { type: StepType.INSTALL, @@ -405,7 +471,7 @@ const feedbackOnboarding: OnboardingConfig = { nextSteps: () => [], }; -const crashReportOnboarding: OnboardingConfig = { +const crashReportOnboarding: OnboardingConfig = { introduction: () => getCrashReportModalIntroduction(), install: (params: Params) => getCrashReportJavaScriptInstallStep(params), configure: () => [ @@ -423,12 +489,13 @@ const crashReportOnboarding: OnboardingConfig = { nextSteps: () => [], }; -const docs: Docs = { +const docs: Docs = { onboarding, feedbackOnboardingNpm: feedbackOnboarding, replayOnboarding, customMetricsOnboarding: getJSMetricsOnboarding({getInstallConfig}), crashReportOnboarding, + platformOptions, }; export default docs;