@@ -14,8 +14,8 @@ import type {
14
14
import { normalize } from '@sentry/core' ;
15
15
import { createBasicSentryServer } from '@sentry-internal/test-utils' ;
16
16
import { execSync , spawn , spawnSync } from 'child_process' ;
17
- import { existsSync , readFileSync , unlinkSync , writeFileSync } from 'fs' ;
18
- import { join } from 'path' ;
17
+ import { existsSync , mkdirSync , readFileSync , rmSync , unlinkSync , writeFileSync } from 'fs' ;
18
+ import { basename , join } from 'path' ;
19
19
import { inspect } from 'util' ;
20
20
import { afterAll , beforeAll , describe , test } from 'vitest' ;
21
21
import {
@@ -174,7 +174,7 @@ export function createEsmAndCjsTests(
174
174
testFn : typeof test | typeof test . fails ,
175
175
mode : 'esm' | 'cjs' ,
176
176
) => void ,
177
- options ?: { failsOnCjs ?: boolean ; failsOnEsm ?: boolean } ,
177
+ options ?: { failsOnCjs ?: boolean ; failsOnEsm ?: boolean ; additionalDependencies ?: Record < string , string > } ,
178
178
) : void {
179
179
const mjsScenarioPath = join ( cwd , scenarioPath ) ;
180
180
const mjsInstrumentPath = join ( cwd , instrumentPath ) ;
@@ -187,33 +187,110 @@ export function createEsmAndCjsTests(
187
187
throw new Error ( `Instrument file not found: ${ mjsInstrumentPath } ` ) ;
188
188
}
189
189
190
- const cjsScenarioPath = join ( cwd , `tmp_${ scenarioPath . replace ( '.mjs' , '.cjs' ) } ` ) ;
191
- const cjsInstrumentPath = join ( cwd , `tmp_${ instrumentPath . replace ( '.mjs' , '.cjs' ) } ` ) ;
190
+ // If additionalDependencies are provided, we create a dedicated tmp directory that includes
191
+ // copied ESM & CJS scenario/instrument files and a nested package.json with those dependencies installed.
192
+ const useTmpDir = Boolean ( options ?. additionalDependencies && Object . keys ( options . additionalDependencies ) . length > 0 ) ;
193
+
194
+ let tmpDirPath : string | undefined ;
195
+ let esmScenarioPathForRun = mjsScenarioPath ;
196
+ let esmInstrumentPathForRun = mjsInstrumentPath ;
197
+ let cjsScenarioPath = join ( cwd , `tmp_${ scenarioPath . replace ( '.mjs' , '.cjs' ) } ` ) ;
198
+ let cjsInstrumentPath = join ( cwd , `tmp_${ instrumentPath . replace ( '.mjs' , '.cjs' ) } ` ) ;
199
+
200
+ if ( useTmpDir ) {
201
+ // Create unique tmp directory within the suite folder
202
+ const uniqueId = `${ Date . now ( ) . toString ( 36 ) } _${ Math . random ( ) . toString ( 36 ) . slice ( 2 , 8 ) } ` ;
203
+ tmpDirPath = join ( cwd , `tmp_${ uniqueId } ` ) ;
204
+ mkdirSync ( tmpDirPath ) ;
205
+
206
+ // Copy ESM files as-is into tmp dir
207
+ const esmScenarioBasename = basename ( scenarioPath ) ;
208
+ const esmInstrumentBasename = basename ( instrumentPath ) ;
209
+ esmScenarioPathForRun = join ( tmpDirPath , esmScenarioBasename ) ;
210
+ esmInstrumentPathForRun = join ( tmpDirPath , esmInstrumentBasename ) ;
211
+ writeFileSync ( esmScenarioPathForRun , readFileSync ( mjsScenarioPath , 'utf8' ) ) ;
212
+ writeFileSync ( esmInstrumentPathForRun , readFileSync ( mjsInstrumentPath , 'utf8' ) ) ;
213
+
214
+ // Pre-create CJS converted files inside tmp dir
215
+ cjsScenarioPath = join ( tmpDirPath , esmScenarioBasename . replace ( '.mjs' , '.cjs' ) ) ;
216
+ cjsInstrumentPath = join ( tmpDirPath , esmInstrumentBasename . replace ( '.mjs' , '.cjs' ) ) ;
217
+ convertEsmFileToCjs ( esmScenarioPathForRun , cjsScenarioPath ) ;
218
+ convertEsmFileToCjs ( esmInstrumentPathForRun , cjsInstrumentPath ) ;
219
+
220
+ // Create a minimal package.json with requested dependencies
221
+ const additionalDependencies = options ?. additionalDependencies ?? { } ;
222
+ const packageJson = {
223
+ name : 'tmp-integration-test' ,
224
+ private : true ,
225
+ version : '0.0.0' ,
226
+ dependencies : additionalDependencies ,
227
+ } as const ;
228
+
229
+ writeFileSync ( join ( tmpDirPath , 'package.json' ) , JSON . stringify ( packageJson , null , 2 ) ) ;
230
+
231
+ // Install the requested dependencies using yarn
232
+ try {
233
+ const deps = Object . entries ( additionalDependencies ) . map ( ( [ name , range ] ) => {
234
+ if ( ! range || typeof range !== 'string' ) {
235
+ throw new Error ( `Invalid version range for "${ name } ": ${ String ( range ) } ` ) ;
236
+ }
237
+ return `${ name } @${ range } ` ;
238
+ } ) ;
239
+
240
+ if ( deps . length > 0 ) {
241
+ // Run yarn add non-interactively, keep output visible when DEBUG is set
242
+ spawnSync ( 'yarn' , [ 'add' , '--non-interactive' , ...deps ] , {
243
+ cwd : tmpDirPath ,
244
+ stdio : process . env . DEBUG ? 'inherit' : 'ignore' ,
245
+ } ) ;
246
+ }
247
+ } catch ( e ) {
248
+ // eslint-disable-next-line no-console
249
+ console . error ( 'Failed to install additionalDependencies:' , e ) ;
250
+ throw e ;
251
+ }
252
+ }
192
253
193
254
describe ( 'esm' , ( ) => {
194
255
const testFn = options ?. failsOnEsm ? test . fails : test ;
195
- callback ( ( ) => createRunner ( mjsScenarioPath ) . withFlags ( '--import' , mjsInstrumentPath ) , testFn , 'esm' ) ;
256
+ callback ( ( ) => createRunner ( esmScenarioPathForRun ) . withFlags ( '--import' , esmInstrumentPathForRun ) , testFn , 'esm' ) ;
196
257
} ) ;
197
258
198
259
describe ( 'cjs' , ( ) => {
199
- beforeAll ( ( ) => {
200
- // For the CJS runner, we create some temporary files...
201
- convertEsmFileToCjs ( mjsScenarioPath , cjsScenarioPath ) ;
202
- convertEsmFileToCjs ( mjsInstrumentPath , cjsInstrumentPath ) ;
203
- } ) ;
204
-
205
- afterAll ( ( ) => {
206
- try {
207
- unlinkSync ( cjsInstrumentPath ) ;
208
- } catch {
209
- // Ignore errors here
210
- }
211
- try {
212
- unlinkSync ( cjsScenarioPath ) ;
213
- } catch {
214
- // Ignore errors here
215
- }
216
- } ) ;
260
+ if ( ! useTmpDir ) {
261
+ beforeAll ( ( ) => {
262
+ // For the CJS runner, we create some temporary files...
263
+ convertEsmFileToCjs ( mjsScenarioPath , cjsScenarioPath ) ;
264
+ convertEsmFileToCjs ( mjsInstrumentPath , cjsInstrumentPath ) ;
265
+ } ) ;
266
+
267
+ afterAll ( ( ) => {
268
+ try {
269
+ unlinkSync ( cjsInstrumentPath ) ;
270
+ } catch {
271
+ // Ignore errors here
272
+ }
273
+ try {
274
+ unlinkSync ( cjsScenarioPath ) ;
275
+ } catch {
276
+ // Ignore errors here
277
+ }
278
+ } ) ;
279
+ } else {
280
+ // When using a tmp dir, clean up the entire directory once CJS tests are finished
281
+ afterAll ( ( ) => {
282
+ if ( tmpDirPath ) {
283
+ try {
284
+ rmSync ( tmpDirPath , { recursive : true , force : true } ) ;
285
+ } catch {
286
+ if ( process . env . DEBUG ) {
287
+ // eslint-disable-next-line no-console
288
+ console . error ( `Failed to remove tmp dir: ${ tmpDirPath } ` ) ;
289
+ }
290
+ }
291
+ }
292
+ } ) ;
293
+ }
217
294
218
295
const testFn = options ?. failsOnCjs ? test . fails : test ;
219
296
callback ( ( ) => createRunner ( cjsScenarioPath ) . withFlags ( '--require' , cjsInstrumentPath ) , testFn , 'cjs' ) ;
0 commit comments