@@ -13,6 +13,7 @@ import {
13
13
runPluginUnderTestHere ,
14
14
runPresetUnderTestHere ,
15
15
validTitleNumberingValues ,
16
+ type Range ,
16
17
type ResultFormatter ,
17
18
type PluginTesterOptions ,
18
19
type TestObject ,
@@ -35,6 +36,10 @@ const parseErrorStackRegExp =
35
36
const parseScriptFilepathRegExp =
36
37
/ \/ b a b e l - p l u g i n - t e s t e r \/ ( d i s t | s r c ) \/ ( i n d e x | p l u g i n - t e s t e r ) \. ( j | t ) s $ / ;
37
38
39
+ const isIntegerRegExp = / ^ \d + $ / ;
40
+
41
+ const isIntegerRangeRegExp = / ^ (?< startStr > \d + ) - (?< endStr > \d + ) $ / ;
42
+
38
43
export default pluginTester ;
39
44
40
45
/**
@@ -90,8 +95,11 @@ export function pluginTester(options: PluginTesterOptions = {}) {
90
95
91
96
let hasTests = false ;
92
97
const baseConfig = resolveBaseConfig ( ) ;
98
+ const envConfig = resolveConfigFromEnvironmentVariables ( ) ;
93
99
const normalizedTests = normalizeTests ( ) ;
94
100
101
+ // TODO: debug statements here printing resolved configs
102
+
95
103
if ( ! hasTests ) {
96
104
// TODO: debug statement here
97
105
return ;
@@ -277,6 +285,59 @@ export function pluginTester(options: PluginTesterOptions = {}) {
277
285
}
278
286
}
279
287
288
+ function resolveConfigFromEnvironmentVariables ( ) {
289
+ return {
290
+ skipTestsByRegExp : stringToRegExp ( process . env . TEST_SKIP ) ,
291
+ onlyTestsByRegExp : stringToRegExp ( process . env . TEST_ONLY ) ,
292
+ skipTestsByRange : stringToRanges ( 'TEST_NUM_SKIP' , process . env . TEST_NUM_SKIP ) ,
293
+ onlyTestsByRange : stringToRanges ( 'TEST_NUM_ONLY' , process . env . TEST_NUM_ONLY )
294
+ } ;
295
+
296
+ function stringToRegExp ( str : string | undefined ) {
297
+ return str === undefined ? undefined : new RegExp ( str , 'u' ) ;
298
+ }
299
+
300
+ function stringToRanges ( name : string , str : string | undefined ) : ( number | Range ) [ ] {
301
+ if ( typeof str != 'string' ) {
302
+ return [ ] ;
303
+ }
304
+
305
+ return str
306
+ . split ( ',' )
307
+ . map ( ( s ) => {
308
+ s = s . trim ( ) ;
309
+
310
+ if ( s ) {
311
+ if ( isIntegerRegExp . test ( s ) ) {
312
+ return Number ( s ) ;
313
+ }
314
+
315
+ const { startStr, endStr } = s . match ( isIntegerRangeRegExp ) ?. groups || { } ;
316
+
317
+ if ( startStr && endStr ) {
318
+ const start = Number ( startStr ) ;
319
+ const end = Number ( endStr ) ;
320
+
321
+ if ( start > end ) {
322
+ throw new TypeError (
323
+ `invalid environment variable "${ name } ": invalid range ${ s } : ${ start } is greater than ${ end } `
324
+ ) ;
325
+ } else if ( start == end ) {
326
+ return start ;
327
+ }
328
+
329
+ return { start, end } ;
330
+ }
331
+
332
+ throw new TypeError (
333
+ `invalid environment variable "${ name } ": invalid range ${ s } `
334
+ ) ;
335
+ }
336
+ } )
337
+ . filter ( ( s ) : s is NonNullable < typeof s > => Boolean ( s ) ) ;
338
+ }
339
+ }
340
+
280
341
function normalizeTests ( ) {
281
342
const { describeBlockTitle, filepath, tests, fixtures } = baseConfig ;
282
343
const testsIsArray = Array . isArray ( tests ) ;
@@ -490,12 +551,9 @@ export function pluginTester(options: PluginTesterOptions = {}) {
490
551
? trimAndFixLineEndings ( fs . readFileSync ( outputPath , 'utf8' ) , endOfLine , code )
491
552
: undefined ;
492
553
493
- const titleNumberPrefix = useFixtureTitleNumbering
494
- ? `${ currentTestNumber ++ } . `
495
- : '' ;
496
-
497
554
const testConfig : MaybePluginTesterTestFixtureConfig = mergeWith (
498
555
{ [ $type ] : 'fixture-object' } as const ,
556
+ // ! Keep the # of source objects to <=4 to get type inference
499
557
{ babelOptions : baseBabelOptions } ,
500
558
{
501
559
babelOptions : {
@@ -506,7 +564,26 @@ export function pluginTester(options: PluginTesterOptions = {}) {
506
564
} ,
507
565
{ babelOptions : babelOptions || { } } ,
508
566
{
509
- testBlockTitle : `${ titleNumberPrefix } ${ title || blockTitle } ` ,
567
+ // ? This is last to ensure plugins/presets babelOptions are
568
+ // ? always arrays
569
+ babelOptions : { plugins : [ ] , presets : [ ] } ,
570
+ testBlockTitle : ( ( ) => {
571
+ const titleString = title || blockTitle ;
572
+ if ( useFixtureTitleNumbering ) {
573
+ const numericPrefix = currentTestNumber ++ ;
574
+ return {
575
+ numericPrefix,
576
+ titleString,
577
+ fullString : `${ numericPrefix } . ${ titleString } `
578
+ } ;
579
+ } else {
580
+ return {
581
+ numericPrefix : undefined ,
582
+ titleString,
583
+ fullString : titleString
584
+ } ;
585
+ }
586
+ } ) ( ) ,
510
587
only,
511
588
skip,
512
589
expectedError : throws ?? error ,
@@ -521,8 +598,6 @@ export function pluginTester(options: PluginTesterOptions = {}) {
521
598
exec,
522
599
execFixture : execPath
523
600
} ,
524
- // ? This is last to ensure plugins/presets babelOptions are arrays
525
- { babelOptions : { plugins : [ ] , presets : [ ] } } ,
526
601
mergeCustomizer
527
602
) ;
528
603
@@ -603,12 +678,9 @@ export function pluginTester(options: PluginTesterOptions = {}) {
603
678
: readCode ( filepath , outputFixture ) ;
604
679
const exec = rawExec ?? readCode ( filepath , execFixture ) ;
605
680
606
- const titleNumberPrefix = useTestObjectTitleNumbering
607
- ? `${ currentTestNumber ++ } . `
608
- : '' ;
609
-
610
681
const testConfig : MaybePluginTesterTestObjectConfig = mergeWith (
611
682
{ [ $type ] : 'test-object' } as const ,
683
+ // ! Keep the # of source objects to <=4 to get type inference
612
684
{ babelOptions : baseBabelOptions } ,
613
685
{
614
686
babelOptions : {
@@ -620,8 +692,27 @@ export function pluginTester(options: PluginTesterOptions = {}) {
620
692
} ,
621
693
{ babelOptions : babelOptions || { } } ,
622
694
{
695
+ // ? This is last to ensure plugins/presets babelOptions are always
696
+ // ? arrays
697
+ babelOptions : { plugins : [ ] , presets : [ ] } ,
623
698
snapshot : snapshot ?? baseSnapshot ,
624
- testBlockTitle : `${ titleNumberPrefix } ${ title || pluginName || presetName } ` ,
699
+ testBlockTitle : ( ( ) => {
700
+ const titleString = ( title || pluginName || presetName ) as string ;
701
+ if ( useTestObjectTitleNumbering ) {
702
+ const numericPrefix = currentTestNumber ++ ;
703
+ return {
704
+ numericPrefix,
705
+ titleString,
706
+ fullString : `${ numericPrefix } . ${ titleString } `
707
+ } ;
708
+ } else {
709
+ return {
710
+ numericPrefix : undefined ,
711
+ titleString,
712
+ fullString : titleString
713
+ } ;
714
+ }
715
+ } ) ( ) ,
625
716
only,
626
717
skip,
627
718
expectedError : throws ?? error ,
@@ -639,8 +730,6 @@ export function pluginTester(options: PluginTesterOptions = {}) {
639
730
exec : exec ? trimAndFixLineEndings ( exec , endOfLine ) : undefined ,
640
731
execFixture
641
732
} ,
642
- // ? This is last to ensure plugins/presets babelOptions are arrays
643
- { babelOptions : { plugins : [ ] , presets : [ ] } } ,
644
733
mergeCustomizer
645
734
) ;
646
735
@@ -671,15 +760,31 @@ export function pluginTester(options: PluginTesterOptions = {}) {
671
760
registerTestsWithTestingFramework ( testConfig . tests ) ;
672
761
} ) ;
673
762
} else {
674
- const { skip, only, testBlockTitle } = testConfig ;
763
+ const {
764
+ skip,
765
+ only,
766
+ testBlockTitle : { numericPrefix, titleString, fullString }
767
+ } = testConfig ;
675
768
676
- if ( skip ) {
677
- it . skip ( testBlockTitle , frameworkTestWrapper ( testConfig ) ) ;
769
+ let method : 'skip' | 'only' | undefined = undefined ;
770
+
771
+ if (
772
+ envConfig . skipTestsByRegExp ?. test ( titleString ) ||
773
+ numericPrefixInRanges ( numericPrefix , envConfig . skipTestsByRange )
774
+ ) {
775
+ method = 'skip' ;
776
+ } else if (
777
+ envConfig . onlyTestsByRegExp ?. test ( titleString ) ||
778
+ numericPrefixInRanges ( numericPrefix , envConfig . onlyTestsByRange )
779
+ ) {
780
+ method = 'only' ;
781
+ } else if ( skip ) {
782
+ method = 'skip' ;
678
783
} else if ( only ) {
679
- it . only ( testBlockTitle , frameworkTestWrapper ( testConfig ) ) ;
680
- } else {
681
- it ( testBlockTitle , frameworkTestWrapper ( testConfig ) ) ;
784
+ method = 'only' ;
682
785
}
786
+
787
+ ( method ? it [ method ] : it ) ( fullString , frameworkTestWrapper ( testConfig ) ) ;
683
788
}
684
789
} ) ;
685
790
}
@@ -812,7 +917,7 @@ export function pluginTester(options: PluginTesterOptions = {}) {
812
917
const separator = '\n\n ↓ ↓ ↓ ↓ ↓ ↓\n\n' ;
813
918
const formattedOutput = [ code , separator , result ] . join ( '' ) ;
814
919
815
- expect ( `\n${ formattedOutput } \n` ) . toMatchSnapshot ( testBlockTitle ) ;
920
+ expect ( `\n${ formattedOutput } \n` ) . toMatchSnapshot ( testBlockTitle . fullString ) ;
816
921
} else if ( expectedError ) {
817
922
if ( typeof expectedError === 'function' ) {
818
923
if ( expectedError === Error || expectedError . prototype instanceof Error ) {
@@ -973,7 +1078,7 @@ export function pluginTester(options: PluginTesterOptions = {}) {
973
1078
974
1079
function throwTypeError ( message : string ) {
975
1080
throw new TypeError (
976
- `failed to validate configuration for test "${ testBlockTitle } ": ${ message } `
1081
+ `failed to validate configuration for test "${ testBlockTitle . fullString } ": ${ message } `
977
1082
) ;
978
1083
}
979
1084
}
@@ -1095,3 +1200,22 @@ function finalizePluginAndPresetRunOrder(
1095
1200
}
1096
1201
}
1097
1202
}
1203
+
1204
+ /**
1205
+ * Determines if `numericPrefix` equals at least one number or is covered by at
1206
+ * least one range Range in the `ranges` array.
1207
+ */
1208
+ function numericPrefixInRanges (
1209
+ numericPrefix : number | undefined ,
1210
+ ranges : ( number | Range ) [ ]
1211
+ ) {
1212
+ if ( typeof numericPrefix == 'number' ) {
1213
+ return ranges . some ( ( range ) => {
1214
+ return typeof range == 'number'
1215
+ ? numericPrefix == range
1216
+ : numericPrefix >= range . start && numericPrefix <= range . end ;
1217
+ } ) ;
1218
+ }
1219
+
1220
+ return false ;
1221
+ }
0 commit comments