@@ -7,7 +7,7 @@ import { getGlobalObject, logger } from '@sentry/utils';
77import { JSDOM } from 'jsdom' ;
88
99import { init , Integrations , nextRouterInstrumentation } from '../src/index.client' ;
10- import { NextjsOptions } from '../src/utils/nextjsOptions ' ;
10+ import { UserIntegrationsFunction } from '../src/utils/userIntegrations ' ;
1111
1212const { BrowserTracing } = TracingIntegrations ;
1313
@@ -32,6 +32,10 @@ afterAll(() => {
3232 Object . defineProperty ( global , 'location' , { value : originalGlobalLocation } ) ;
3333} ) ;
3434
35+ function findIntegrationByName ( integrations : Integration [ ] = [ ] , name : string ) : Integration | undefined {
36+ return integrations . find ( integration => integration . name === name ) ;
37+ }
38+
3539describe ( 'Client init()' , ( ) => {
3640 afterEach ( ( ) => {
3741 jest . clearAllMocks ( ) ;
@@ -95,84 +99,101 @@ describe('Client init()', () => {
9599 } ) ;
96100
97101 describe ( 'integrations' , ( ) => {
98- it ( 'does not add BrowserTracing integration by default if tracesSampleRate is not set' , ( ) => {
99- init ( { } ) ;
100-
101- const reactInitOptions : NextjsOptions = reactInit . mock . calls [ 0 ] [ 0 ] ;
102- expect ( reactInitOptions . integrations ) . toBeUndefined ( ) ;
103- } ) ;
102+ // Options passed by `@sentry/nextjs`'s `init` to `@sentry/react`'s `init` after modifying them
103+ type ModifiedInitOptionsIntegrationArray = { integrations : Integration [ ] } ;
104+ type ModifiedInitOptionsIntegrationFunction = { integrations : UserIntegrationsFunction } ;
104105
105- it ( 'adds BrowserTracing integration by default if tracesSampleRate is set ' , ( ) => {
106- init ( { tracesSampleRate : 1.0 } ) ;
106+ it ( 'supports passing unrelated integrations through options ' , ( ) => {
107+ init ( { integrations : [ new Integrations . Breadcrumbs ( { console : false } ) ] } ) ;
107108
108- const reactInitOptions : NextjsOptions = reactInit . mock . calls [ 0 ] [ 0 ] ;
109- expect ( reactInitOptions . integrations ) . toHaveLength ( 1 ) ;
109+ const reactInitOptions = reactInit . mock . calls [ 0 ] [ 0 ] as ModifiedInitOptionsIntegrationArray ;
110+ const breadcrumbsIntegration = findIntegrationByName ( reactInitOptions . integrations , 'Breadcrumbs' ) ;
110111
111- const integrations = reactInitOptions . integrations as Integration [ ] ;
112- expect ( integrations [ 0 ] ) . toEqual ( expect . any ( BrowserTracing ) ) ;
113- // eslint-disable-next-line @typescript-eslint/unbound-method
114- expect ( ( integrations [ 0 ] as InstanceType < typeof BrowserTracing > ) . options . routingInstrumentation ) . toEqual (
115- nextRouterInstrumentation ,
116- ) ;
112+ expect ( breadcrumbsIntegration ) . toBeDefined ( ) ;
117113 } ) ;
118114
119- it ( 'adds BrowserTracing integration by default if tracesSampler is set' , ( ) => {
120- init ( { tracesSampler : ( ) => true } ) ;
115+ describe ( '`BrowserTracing` integration' , ( ) => {
116+ it ( 'adds `BrowserTracing` integration if `tracesSampleRate` is set' , ( ) => {
117+ init ( { tracesSampleRate : 1.0 } ) ;
118+
119+ const reactInitOptions = reactInit . mock . calls [ 0 ] [ 0 ] as ModifiedInitOptionsIntegrationArray ;
120+ const browserTracingIntegration = findIntegrationByName ( reactInitOptions . integrations , 'BrowserTracing' ) ;
121+
122+ expect ( browserTracingIntegration ) . toBeDefined ( ) ;
123+ expect ( browserTracingIntegration ) . toEqual (
124+ expect . objectContaining ( {
125+ options : expect . objectContaining ( {
126+ routingInstrumentation : nextRouterInstrumentation ,
127+ } ) ,
128+ } ) ,
129+ ) ;
130+ } ) ;
121131
122- const reactInitOptions : NextjsOptions = reactInit . mock . calls [ 0 ] [ 0 ] ;
123- expect ( reactInitOptions . integrations ) . toHaveLength ( 1 ) ;
132+ it ( 'adds `BrowserTracing` integration if `tracesSampler` is set' , ( ) => {
133+ init ( { tracesSampler : ( ) => true } ) ;
124134
125- const integrations = reactInitOptions . integrations as Integration [ ] ;
126- expect ( integrations [ 0 ] ) . toEqual ( expect . any ( BrowserTracing ) ) ;
127- // eslint-disable-next-line @typescript-eslint/unbound-method
128- expect ( ( integrations [ 0 ] as InstanceType < typeof BrowserTracing > ) . options . routingInstrumentation ) . toEqual (
129- nextRouterInstrumentation ,
130- ) ;
131- } ) ;
135+ const reactInitOptions = reactInit . mock . calls [ 0 ] [ 0 ] as ModifiedInitOptionsIntegrationArray ;
136+ const browserTracingIntegration = findIntegrationByName ( reactInitOptions . integrations , 'BrowserTracing' ) ;
132137
133- it ( 'supports passing integration through options' , ( ) => {
134- init ( { tracesSampleRate : 1.0 , integrations : [ new Integrations . Breadcrumbs ( { console : false } ) ] } ) ;
135- const reactInitOptions : NextjsOptions = reactInit . mock . calls [ 0 ] [ 0 ] ;
136- expect ( reactInitOptions . integrations ) . toHaveLength ( 2 ) ;
138+ expect ( browserTracingIntegration ) . toBeDefined ( ) ;
139+ expect ( browserTracingIntegration ) . toEqual (
140+ expect . objectContaining ( {
141+ options : expect . objectContaining ( {
142+ routingInstrumentation : nextRouterInstrumentation ,
143+ } ) ,
144+ } ) ,
145+ ) ;
146+ } ) ;
137147
138- const integrations = reactInitOptions . integrations as Integration [ ] ;
139- expect ( integrations ) . toEqual ( [ expect . any ( Integrations . Breadcrumbs ) , expect . any ( BrowserTracing ) ] ) ;
140- } ) ;
148+ it ( 'does not add `BrowserTracing` integration if tracing not enabled in SDK' , ( ) => {
149+ init ( { } ) ;
141150
142- it ( 'uses custom BrowserTracing with array option with nextRouterInstrumentation' , ( ) => {
143- init ( {
144- tracesSampleRate : 1.0 ,
145- integrations : [ new BrowserTracing ( { idleTimeout : 5000 , startTransactionOnLocationChange : false } ) ] ,
146- } ) ;
151+ const reactInitOptions = reactInit . mock . calls [ 0 ] [ 0 ] as ModifiedInitOptionsIntegrationArray ;
152+ const browserTracingIntegration = findIntegrationByName ( reactInitOptions . integrations , 'BrowserTracing' ) ;
147153
148- const reactInitOptions : NextjsOptions = reactInit . mock . calls [ 0 ] [ 0 ] ;
149- expect ( reactInitOptions . integrations ) . toHaveLength ( 1 ) ;
150- const integrations = reactInitOptions . integrations as Integration [ ] ;
151- expect ( ( integrations [ 0 ] as InstanceType < typeof BrowserTracing > ) . options ) . toEqual (
152- expect . objectContaining ( {
153- idleTimeout : 5000 ,
154- startTransactionOnLocationChange : false ,
155- routingInstrumentation : nextRouterInstrumentation ,
156- } ) ,
157- ) ;
158- } ) ;
154+ expect ( browserTracingIntegration ) . toBeUndefined ( ) ;
155+ } ) ;
159156
160- it ( 'uses custom BrowserTracing with function option with nextRouterInstrumentation' , ( ) => {
161- init ( {
162- tracesSampleRate : 1.0 ,
163- integrations : ( ) => [ new BrowserTracing ( { idleTimeout : 5000 , startTransactionOnLocationChange : false } ) ] ,
157+ it ( 'forces correct router instrumentation if user provides `BrowserTracing` in an array' , ( ) => {
158+ init ( {
159+ tracesSampleRate : 1.0 ,
160+ integrations : [ new BrowserTracing ( { startTransactionOnLocationChange : false } ) ] ,
161+ } ) ;
162+
163+ const reactInitOptions = reactInit . mock . calls [ 0 ] [ 0 ] as ModifiedInitOptionsIntegrationArray ;
164+ const browserTracingIntegration = findIntegrationByName ( reactInitOptions . integrations , 'BrowserTracing' ) ;
165+
166+ expect ( browserTracingIntegration ) . toEqual (
167+ expect . objectContaining ( {
168+ options : expect . objectContaining ( {
169+ routingInstrumentation : nextRouterInstrumentation ,
170+ // This proves it's still the user's copy
171+ startTransactionOnLocationChange : false ,
172+ } ) ,
173+ } ) ,
174+ ) ;
164175 } ) ;
165176
166- const reactInitOptions : NextjsOptions = reactInit . mock . calls [ 0 ] [ 0 ] ;
167- const integrationFunc = reactInitOptions . integrations as ( ) => Integration [ ] ;
168- const integrations = integrationFunc ( ) ;
169- expect ( ( integrations [ 0 ] as InstanceType < typeof BrowserTracing > ) . options ) . toEqual (
170- expect . objectContaining ( {
171- idleTimeout : 5000 ,
172- startTransactionOnLocationChange : false ,
173- routingInstrumentation : nextRouterInstrumentation ,
174- } ) ,
175- ) ;
177+ it ( 'forces correct router instrumentation if user provides `BrowserTracing` in a function' , ( ) => {
178+ init ( {
179+ tracesSampleRate : 1.0 ,
180+ integrations : defaults => [ ...defaults , new BrowserTracing ( { startTransactionOnLocationChange : false } ) ] ,
181+ } ) ;
182+
183+ const reactInitOptions = reactInit . mock . calls [ 0 ] [ 0 ] as ModifiedInitOptionsIntegrationFunction ;
184+ const materializedIntegrations = reactInitOptions . integrations ( SentryReact . defaultIntegrations ) ;
185+ const browserTracingIntegration = findIntegrationByName ( materializedIntegrations , 'BrowserTracing' ) ;
186+
187+ expect ( browserTracingIntegration ) . toEqual (
188+ expect . objectContaining ( {
189+ options : expect . objectContaining ( {
190+ routingInstrumentation : nextRouterInstrumentation ,
191+ // This proves it's still the user's copy
192+ startTransactionOnLocationChange : false ,
193+ } ) ,
194+ } ) ,
195+ ) ;
196+ } ) ;
176197 } ) ;
177198 } ) ;
178199} ) ;
0 commit comments