@@ -37,6 +37,32 @@ 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+ const mkdtempSyncSpy = jest . spyOn ( fs , 'mkdtempSync' ) ;
61+
62+ afterEach ( ( ) => {
63+ mkdtempSyncSpy . mockClear ( ) ;
64+ } ) ;
65+
4066/** Mocks of the arguments passed to `withSentryConfig` */
4167const userNextConfig : Partial < NextConfigObject > = {
4268 publicRuntimeConfig : { location : 'dogpark' , activities : [ 'fetch' , 'chasing' , 'digging' ] } ,
@@ -103,7 +129,12 @@ function getBuildContext(
103129 dev : false ,
104130 buildId : 'sItStAyLiEdOwN' ,
105131 dir : '/Users/Maisey/projects/squirrelChasingSimulator' ,
106- config : { target : 'server' , ...userNextConfig } as NextConfigObject ,
132+ config : {
133+ // nextjs's default values
134+ target : 'server' ,
135+ distDir : '.next' ,
136+ ...userNextConfig ,
137+ } as NextConfigObject ,
107138 webpack : { version : webpackVersion } ,
108139 isServer : buildTarget === 'server' ,
109140 } ;
@@ -279,26 +310,35 @@ describe('webpack config', () => {
279310 incomingWebpackBuildContext : serverBuildContext ,
280311 } ) ;
281312
313+ const tempDir = mkdtempSyncSpy . mock . results [ 0 ] . value ;
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,62 @@ 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+
409+ const tempDir = mkdtempSyncSpy . mock . results [ 0 ] . value ;
410+ const rewriteFramesHelper = path . join ( tempDir , 'rewriteFramesHelper.js' ) ;
411+
412+ expect ( fs . existsSync ( rewriteFramesHelper ) ) . toBe ( true ) ;
413+
414+ const injectedCode = fs . readFileSync ( rewriteFramesHelper ) . toString ( ) ;
415+ expect ( injectedCode ) . toEqual ( `global.__rewriteFramesDistDir__ = '${ expectedInjectedValue } ';\n` ) ;
416+ } ,
417+ ) ;
418+
419+ describe ( '`RewriteFrames` ends up with correct `distDir` value' , ( ) => {
420+ // TODO: this, along with any number of other parts of the build process, should be tested with an integration
421+ // test which actually runs webpack and inspects the resulting bundles (and that integration test should test
422+ // custom `distDir` values with and without a `.`, to make sure the regex escaping is working)
423+ } ) ;
424+ } ) ;
335425} ) ;
336426
337427describe ( 'Sentry webpack plugin config' , ( ) => {
@@ -587,12 +677,11 @@ describe('Sentry webpack plugin config', () => {
587677 } ) ;
588678
589679 beforeEach ( ( ) => {
680+ // these will get cleaned up by the file's overall `afterAll` function, and the `mkdtempSync` mock above ensures
681+ // that the location of the created folder is stored in `tempDir`
590682 const tempDirPathPrefix = path . join ( os . tmpdir ( ) , 'sentry-nextjs-test-' ) ;
591- tempDir = fs . mkdtempSync ( tempDirPathPrefix ) ;
592- } ) ;
593-
594- afterEach ( ( ) => {
595- rimraf . sync ( tempDir ) ;
683+ fs . mkdtempSync ( tempDirPathPrefix ) ;
684+ tempDir = mkdtempSyncSpy . mock . results [ 0 ] . value ;
596685 } ) ;
597686
598687 afterAll ( ( ) => {
0 commit comments