@@ -37,6 +37,33 @@ const mockExistsSync = (path: fs.PathLike) => {
3737} ;
3838const exitsSync = jest . spyOn ( fs , 'existsSync' ) . mockImplementation ( mockExistsSync ) ;
3939
40+ // Make it so that all temporary folders, either created directly by tests or by the code they're testing, will go into
41+ // one spot that we know about, which we can then clean up when we're done
42+ const realTmpdir = jest . requireActual ( 'os' ) . tmpdir ;
43+ const TEMP_DIR_PATH = path . join ( realTmpdir ( ) , 'sentry-nextjs-test' ) ;
44+ jest . spyOn ( os , 'tmpdir' ) . mockReturnValue ( TEMP_DIR_PATH ) ;
45+ // In theory, we should always land in the `else` here, but this saves the cases where the prior run got interrupted and
46+ // the `afterAll` below didn't happen.
47+ if ( fs . existsSync ( TEMP_DIR_PATH ) ) {
48+ rimraf . sync ( path . join ( TEMP_DIR_PATH , '*' ) ) ;
49+ } else {
50+ fs . mkdirSync ( TEMP_DIR_PATH ) ;
51+ }
52+
53+ afterAll ( ( ) => {
54+ rimraf . sync ( TEMP_DIR_PATH ) ;
55+ } ) ;
56+
57+ // In order to know what to expect in the webpack config `entry` property, we need to know the path of the temporary
58+ // directory created when doing the file injection, so wrap the real `mkdtempSync` and store the resulting path where we
59+ // can access it
60+ let tempDir : string ;
61+ const realMkdtempSync = jest . requireActual ( 'fs' ) . mkdtempSync ;
62+ jest . spyOn ( fs , 'mkdtempSync' ) . mockImplementation ( prefix => {
63+ tempDir = realMkdtempSync ( prefix ) ;
64+ return tempDir ;
65+ } ) ;
66+
4067/** Mocks of the arguments passed to `withSentryConfig` */
4168const userNextConfig : Partial < NextConfigObject > = {
4269 publicRuntimeConfig : { location : 'dogpark' , activities : [ 'fetch' , 'chasing' , 'digging' ] } ,
@@ -103,7 +130,12 @@ function getBuildContext(
103130 dev : false ,
104131 buildId : 'sItStAyLiEdOwN' ,
105132 dir : '/Users/Maisey/projects/squirrelChasingSimulator' ,
106- config : { target : 'server' , ...userNextConfig } as NextConfigObject ,
133+ config : {
134+ // nextjs's default values
135+ target : 'server' ,
136+ distDir : '.next' ,
137+ ...userNextConfig ,
138+ } as NextConfigObject ,
107139 webpack : { version : webpackVersion } ,
108140 isServer : buildTarget === 'server' ,
109141 } ;
@@ -279,26 +311,34 @@ describe('webpack config', () => {
279311 incomingWebpackBuildContext : serverBuildContext ,
280312 } ) ;
281313
314+ const rewriteFramesHelper = path . join ( tempDir , 'rewriteFramesHelper.js' ) ;
315+
282316 expect ( finalWebpackConfig . entry ) . toEqual (
283317 expect . objectContaining ( {
284318 // original entry point value is a string
285319 // (was 'private-next-pages/api/dogs/[name].js')
286- 'pages/api/dogs/[name]' : [ serverConfigFilePath , 'private-next-pages/api/dogs/[name].js' ] ,
320+ 'pages/api/dogs/[name]' : [ rewriteFramesHelper , serverConfigFilePath , 'private-next-pages/api/dogs/[name].js' ] ,
287321
288322 // original entry point value is a string array
289323 // (was ['./node_modules/smellOVision/index.js', 'private-next-pages/_app.js'])
290- 'pages/_app' : [ serverConfigFilePath , './node_modules/smellOVision/index.js' , 'private-next-pages/_app.js' ] ,
324+ 'pages/_app' : [
325+ rewriteFramesHelper ,
326+ serverConfigFilePath ,
327+ './node_modules/smellOVision/index.js' ,
328+ 'private-next-pages/_app.js' ,
329+ ] ,
291330
292331 // original entry point value is an object containing a string `import` value
293332 // (`import` was 'private-next-pages/api/simulator/dogStats/[name].js')
294333 'pages/api/simulator/dogStats/[name]' : {
295- import : [ serverConfigFilePath , 'private-next-pages/api/simulator/dogStats/[name].js' ] ,
334+ import : [ rewriteFramesHelper , serverConfigFilePath , 'private-next-pages/api/simulator/dogStats/[name].js' ] ,
296335 } ,
297336
298337 // original entry point value is an object containing a string array `import` value
299338 // (`import` was ['./node_modules/dogPoints/converter.js', 'private-next-pages/api/simulator/leaderboard.js'])
300339 'pages/api/simulator/leaderboard' : {
301340 import : [
341+ rewriteFramesHelper ,
302342 serverConfigFilePath ,
303343 './node_modules/dogPoints/converter.js' ,
304344 'private-next-pages/api/simulator/leaderboard.js' ,
@@ -308,14 +348,14 @@ describe('webpack config', () => {
308348 // original entry point value is an object containg properties besides `import`
309349 // (`dependOn` remains untouched)
310350 'pages/api/tricks/[trickName]' : {
311- import : [ serverConfigFilePath , 'private-next-pages/api/tricks/[trickName].js' ] ,
351+ import : [ rewriteFramesHelper , serverConfigFilePath , 'private-next-pages/api/tricks/[trickName].js' ] ,
312352 dependOn : 'treats' ,
313353 } ,
314354 } ) ,
315355 ) ;
316356 } ) ;
317357
318- it ( 'does not inject into non-_app, non-API routes' , async ( ) => {
358+ it ( 'does not inject anything into non-_app, non-API routes' , async ( ) => {
319359 const finalWebpackConfig = await materializeFinalWebpackConfig ( {
320360 userNextConfig,
321361 incomingWebpackConfig : clientWebpackConfig ,
@@ -326,12 +366,60 @@ describe('webpack config', () => {
326366 expect . objectContaining ( {
327367 // no injected file
328368 main : './src/index.ts' ,
329- // was 'next-client-pages-loader?page=%2F_app'
369+ } ) ,
370+ ) ;
371+ } ) ;
372+
373+ it ( 'does not inject `RewriteFrames` helper into client routes' , async ( ) => {
374+ const finalWebpackConfig = await materializeFinalWebpackConfig ( {
375+ userNextConfig,
376+ incomingWebpackConfig : clientWebpackConfig ,
377+ incomingWebpackBuildContext : clientBuildContext ,
378+ } ) ;
379+
380+ expect ( finalWebpackConfig . entry ) . toEqual (
381+ expect . objectContaining ( {
382+ // was 'next-client-pages-loader?page=%2F_app', and now has client config but not`RewriteFrames` helper injected
330383 'pages/_app' : [ clientConfigFilePath , 'next-client-pages-loader?page=%2F_app' ] ,
331384 } ) ,
332385 ) ;
333386 } ) ;
334387 } ) ;
388+
389+ describe ( '`distDir` value in default server-side `RewriteFrames` integration' , ( ) => {
390+ it . each ( [
391+ [ 'no custom `distDir`' , undefined , '.next' ] ,
392+ [ 'custom `distDir`' , 'dist' , 'dist' ] ,
393+ ] ) (
394+ 'creates file injecting `distDir` value into `global` - %s' ,
395+ async ( _name , customDistDir , expectedInjectedValue ) => {
396+ // Note: the fact that the file tested here gets injected correctly is covered in the 'webpack `entry` property
397+ // config' tests above
398+
399+ const userNextConfigDistDir = {
400+ ...userNextConfig ,
401+ ...( customDistDir && { distDir : customDistDir } ) ,
402+ } ;
403+ await materializeFinalWebpackConfig ( {
404+ userNextConfig : userNextConfigDistDir ,
405+ incomingWebpackConfig : serverWebpackConfig ,
406+ incomingWebpackBuildContext : getBuildContext ( 'server' , userNextConfigDistDir ) ,
407+ } ) ;
408+ const rewriteFramesHelper = path . join ( tempDir , 'rewriteFramesHelper.js' ) ;
409+
410+ expect ( fs . existsSync ( rewriteFramesHelper ) ) . toBe ( true ) ;
411+
412+ const injectedCode = fs . readFileSync ( rewriteFramesHelper ) . toString ( ) ;
413+ expect ( injectedCode ) . toEqual ( `global.__rewriteFramesDistDir__ = '${ expectedInjectedValue } ';\n` ) ;
414+ } ,
415+ ) ;
416+
417+ describe ( '`RewriteFrames` ends up with correct `distDir` value' , ( ) => {
418+ // TODO: this, along with any number of other parts of the build process, should be tested with an integration
419+ // test which actually runs webpack and inspects the resulting bundles (and that integration test should test
420+ // custom `distDir` values with and without a `.`, to make sure the regex escaping is working)
421+ } ) ;
422+ } ) ;
335423} ) ;
336424
337425describe ( 'Sentry webpack plugin config' , ( ) => {
@@ -580,19 +668,15 @@ describe('Sentry webpack plugin config', () => {
580668 } ) ;
581669
582670 describe ( 'getUserConfigFile' , ( ) => {
583- let tempDir : string ;
584-
585671 beforeAll ( ( ) => {
586672 exitsSync . mockImplementation ( realExistsSync ) ;
587673 } ) ;
588674
589675 beforeEach ( ( ) => {
676+ // these will get cleaned up by the file's overall `afterAll` function, and the `mkdtempSync` mock above ensures
677+ // that the location of the created folder is stored in `tempDir`
590678 const tempDirPathPrefix = path . join ( os . tmpdir ( ) , 'sentry-nextjs-test-' ) ;
591- tempDir = fs . mkdtempSync ( tempDirPathPrefix ) ;
592- } ) ;
593-
594- afterEach ( ( ) => {
595- rimraf . sync ( tempDir ) ;
679+ fs . mkdtempSync ( tempDirPathPrefix ) ;
596680 } ) ;
597681
598682 afterAll ( ( ) => {
0 commit comments