Skip to content

Commit 13626d1

Browse files
committedJan 10, 2023
feat(src): add TEST_SKIP/TEST_NUM_SKIP/TEST_ONLY/TEST_NUM_ONLY env variable support
1 parent 435e993 commit 13626d1

File tree

2 files changed

+161
-23
lines changed

2 files changed

+161
-23
lines changed
 

‎src/index.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,11 @@ type PluginTesterSharedTestConfigProperties = {
804804
plugins: NonNullable<Babel.TransformOptions['plugins']>;
805805
presets: NonNullable<Babel.TransformOptions['presets']>;
806806
};
807-
testBlockTitle: NonNullable<TestObject['title'] | FixtureOptions['title']>;
807+
testBlockTitle: {
808+
numericPrefix: number | undefined;
809+
titleString: string;
810+
fullString: string;
811+
};
808812
only?: TestObject['only'] | FixtureOptions['only'];
809813
skip?: TestObject['skip'] | FixtureOptions['skip'];
810814
expectedError?: TestObject['throws'] | FixtureOptions['throws'];
@@ -924,3 +928,13 @@ export type PluginTesterTestConfig =
924928
| PluginTesterTestDescribeConfig
925929
| PluginTesterTestObjectConfig
926930
| PluginTesterTestFixtureConfig;
931+
932+
/**
933+
* An internal type describing an inclusive range of numbers.
934+
*
935+
* @internal
936+
*/
937+
export type Range = {
938+
start: number;
939+
end: number;
940+
};

‎src/plugin-tester.ts

+146-22
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
runPluginUnderTestHere,
1414
runPresetUnderTestHere,
1515
validTitleNumberingValues,
16+
type Range,
1617
type ResultFormatter,
1718
type PluginTesterOptions,
1819
type TestObject,
@@ -35,6 +36,10 @@ const parseErrorStackRegExp =
3536
const parseScriptFilepathRegExp =
3637
/\/babel-plugin-tester\/(dist|src)\/(index|plugin-tester)\.(j|t)s$/;
3738

39+
const isIntegerRegExp = /^\d+$/;
40+
41+
const isIntegerRangeRegExp = /^(?<startStr>\d+)-(?<endStr>\d+)$/;
42+
3843
export default pluginTester;
3944

4045
/**
@@ -90,8 +95,11 @@ export function pluginTester(options: PluginTesterOptions = {}) {
9095

9196
let hasTests = false;
9297
const baseConfig = resolveBaseConfig();
98+
const envConfig = resolveConfigFromEnvironmentVariables();
9399
const normalizedTests = normalizeTests();
94100

101+
// TODO: debug statements here printing resolved configs
102+
95103
if (!hasTests) {
96104
// TODO: debug statement here
97105
return;
@@ -277,6 +285,59 @@ export function pluginTester(options: PluginTesterOptions = {}) {
277285
}
278286
}
279287

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+
280341
function normalizeTests() {
281342
const { describeBlockTitle, filepath, tests, fixtures } = baseConfig;
282343
const testsIsArray = Array.isArray(tests);
@@ -490,12 +551,9 @@ export function pluginTester(options: PluginTesterOptions = {}) {
490551
? trimAndFixLineEndings(fs.readFileSync(outputPath, 'utf8'), endOfLine, code)
491552
: undefined;
492553

493-
const titleNumberPrefix = useFixtureTitleNumbering
494-
? `${currentTestNumber++}. `
495-
: '';
496-
497554
const testConfig: MaybePluginTesterTestFixtureConfig = mergeWith(
498555
{ [$type]: 'fixture-object' } as const,
556+
// ! Keep the # of source objects to <=4 to get type inference
499557
{ babelOptions: baseBabelOptions },
500558
{
501559
babelOptions: {
@@ -506,7 +564,26 @@ export function pluginTester(options: PluginTesterOptions = {}) {
506564
},
507565
{ babelOptions: babelOptions || {} },
508566
{
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+
})(),
510587
only,
511588
skip,
512589
expectedError: throws ?? error,
@@ -521,8 +598,6 @@ export function pluginTester(options: PluginTesterOptions = {}) {
521598
exec,
522599
execFixture: execPath
523600
},
524-
// ? This is last to ensure plugins/presets babelOptions are arrays
525-
{ babelOptions: { plugins: [], presets: [] } },
526601
mergeCustomizer
527602
);
528603

@@ -603,12 +678,9 @@ export function pluginTester(options: PluginTesterOptions = {}) {
603678
: readCode(filepath, outputFixture);
604679
const exec = rawExec ?? readCode(filepath, execFixture);
605680

606-
const titleNumberPrefix = useTestObjectTitleNumbering
607-
? `${currentTestNumber++}. `
608-
: '';
609-
610681
const testConfig: MaybePluginTesterTestObjectConfig = mergeWith(
611682
{ [$type]: 'test-object' } as const,
683+
// ! Keep the # of source objects to <=4 to get type inference
612684
{ babelOptions: baseBabelOptions },
613685
{
614686
babelOptions: {
@@ -620,8 +692,27 @@ export function pluginTester(options: PluginTesterOptions = {}) {
620692
},
621693
{ babelOptions: babelOptions || {} },
622694
{
695+
// ? This is last to ensure plugins/presets babelOptions are always
696+
// ? arrays
697+
babelOptions: { plugins: [], presets: [] },
623698
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+
})(),
625716
only,
626717
skip,
627718
expectedError: throws ?? error,
@@ -639,8 +730,6 @@ export function pluginTester(options: PluginTesterOptions = {}) {
639730
exec: exec ? trimAndFixLineEndings(exec, endOfLine) : undefined,
640731
execFixture
641732
},
642-
// ? This is last to ensure plugins/presets babelOptions are arrays
643-
{ babelOptions: { plugins: [], presets: [] } },
644733
mergeCustomizer
645734
);
646735

@@ -671,15 +760,31 @@ export function pluginTester(options: PluginTesterOptions = {}) {
671760
registerTestsWithTestingFramework(testConfig.tests);
672761
});
673762
} else {
674-
const { skip, only, testBlockTitle } = testConfig;
763+
const {
764+
skip,
765+
only,
766+
testBlockTitle: { numericPrefix, titleString, fullString }
767+
} = testConfig;
675768

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';
678783
} else if (only) {
679-
it.only(testBlockTitle, frameworkTestWrapper(testConfig));
680-
} else {
681-
it(testBlockTitle, frameworkTestWrapper(testConfig));
784+
method = 'only';
682785
}
786+
787+
(method ? it[method] : it)(fullString, frameworkTestWrapper(testConfig));
683788
}
684789
});
685790
}
@@ -812,7 +917,7 @@ export function pluginTester(options: PluginTesterOptions = {}) {
812917
const separator = '\n\n ↓ ↓ ↓ ↓ ↓ ↓\n\n';
813918
const formattedOutput = [code, separator, result].join('');
814919

815-
expect(`\n${formattedOutput}\n`).toMatchSnapshot(testBlockTitle);
920+
expect(`\n${formattedOutput}\n`).toMatchSnapshot(testBlockTitle.fullString);
816921
} else if (expectedError) {
817922
if (typeof expectedError === 'function') {
818923
if (expectedError === Error || expectedError.prototype instanceof Error) {
@@ -973,7 +1078,7 @@ export function pluginTester(options: PluginTesterOptions = {}) {
9731078

9741079
function throwTypeError(message: string) {
9751080
throw new TypeError(
976-
`failed to validate configuration for test "${testBlockTitle}": ${message}`
1081+
`failed to validate configuration for test "${testBlockTitle.fullString}": ${message}`
9771082
);
9781083
}
9791084
}
@@ -1095,3 +1200,22 @@ function finalizePluginAndPresetRunOrder(
10951200
}
10961201
}
10971202
}
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

Comments
 (0)
Please sign in to comment.