Skip to content

Commit fbc6e2a

Browse files
crisbetommalerba
authored andcommitted
build: add a script to flag missing unit tests in MDC packages (#20471)
Adds a script that will find and log out the names of Material unit tests that don't exist in the equivalent MDC component. Note that right now it's something we'd run manually, but the idea is to eventually have it on the CI, once all the tests have been added and we have the ability to exclude some tests that aren't relevant for MDC. (cherry picked from commit 42fb35b)
1 parent 4fa9618 commit fbc6e2a

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
"approve-size-tests": "node ./scripts/approve-size-golden.js",
4646
"integration-tests": "bazel test --test_tag_filters=-view-engine-only --build_tests_only -- //integration/... -//integration/size-test/...",
4747
"integration-tests:view-engine": "bazel test --test_tag_filters=view-engine-only --build_tests_only -- //integration/... -//integration/size-test/...",
48-
"integration-tests:size-test": "bazel test //integration/size-test/..."
48+
"integration-tests:size-test": "bazel test //integration/size-test/...",
49+
"check-mdc-tests": "ts-node --project scripts/tsconfig.json scripts/check-mdc-tests.ts"
4950
},
5051
"version": "10.1.3",
5152
"dependencies": {

scripts/check-mdc-tests.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {readdirSync, readFileSync} from 'fs';
2+
import {join, basename} from 'path';
3+
import {sync as glob} from 'glob';
4+
import chalk from 'chalk';
5+
import * as ts from 'typescript';
6+
7+
const srcDirectory = join(__dirname, '../src');
8+
const materialDirectories = readdirSync(join(srcDirectory, 'material'));
9+
10+
// Goes through all the unit tests and flags the ones that don't exist in the MDC components.
11+
readdirSync(join(srcDirectory, 'material-experimental'), {withFileTypes: true})
12+
.reduce((matches, entity) => {
13+
// Go through all the `material-experimental` directories and match them to ones in `material`.
14+
if (entity.isDirectory()) {
15+
const materialName = entity.name.replace(/^mdc-/, '');
16+
17+
if (materialDirectories.indexOf(materialName) > -1) {
18+
matches.set(materialName, entity.name);
19+
}
20+
}
21+
22+
return matches;
23+
}, new Map<string, string>())
24+
.forEach((mdcPackage, materialPackage) => {
25+
const mdcTestFiles = getUnitTestFiles(`material-experimental/${mdcPackage}`);
26+
27+
// MDC entry points that don't have test files may not have been implemented yet.
28+
if (mdcTestFiles.length > 0) {
29+
// Filter out files that don't exist in the MDC package, allowing
30+
// us to ignore some files which may not need to be ported to MDC.
31+
const materialTestFiles = getUnitTestFiles(`material/${materialPackage}`).filter(path => {
32+
const fileName = basename(path);
33+
return mdcTestFiles.some(file => basename(file) === fileName);
34+
});
35+
const materialTests = getTestNames(materialTestFiles);
36+
const mdcTests = getTestNames(mdcTestFiles);
37+
const missingTests = materialTests.filter(test => !mdcTests.includes(test));
38+
39+
if (missingTests.length > 0) {
40+
console.log(chalk.redBright(`\nMissing tests for ${mdcPackage}:`));
41+
console.log(missingTests.join('\n'));
42+
}
43+
}
44+
});
45+
46+
/**
47+
* Gets all the names of all unit test files inside a
48+
* package name, excluding `testing` packages and e2e tests.
49+
*/
50+
function getUnitTestFiles(name: string): string[] {
51+
return glob('{,!(testing)/**/}!(*.e2e).spec.ts', {
52+
absolute: true,
53+
cwd: join(srcDirectory, name)
54+
});
55+
}
56+
57+
/** Gets the name of all unit tests within a set of files. */
58+
function getTestNames(files: string[]): string[] {
59+
const testNames: string[] = [];
60+
61+
files.forEach(file => {
62+
const content = readFileSync(file, 'utf-8');
63+
const sourceFile = ts.createSourceFile(file, content, ts.ScriptTarget.ES2015);
64+
65+
sourceFile.forEachChild(function walk(node: ts.Node) {
66+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) &&
67+
node.expression.text === 'it') {
68+
// Note that this is a little naive since it'll take the literal text of the test
69+
// name expression which could include things like string concatenation. It's fine
70+
// for the limited use cases of the script.
71+
testNames.push(node.arguments[0].getText(sourceFile)
72+
// Replace the quotes around the test name.
73+
.replace(/^['`]|['`]$/g, '')
74+
// Strip newlines followed by indentation.
75+
.replace(/\n\s+/g, ' ')
76+
// Strip escape characters.
77+
.replace(/\\/g, '')
78+
// Collapse concatenated strings.
79+
.replace(/['`]\s+\+\s+['`]/g, ''));
80+
} else {
81+
node.forEachChild(walk);
82+
}
83+
});
84+
});
85+
86+
return testNames;
87+
}

scripts/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"outDir": "../dist/dev-infra-scripts",
44
"target": "es2015",
55
"lib": ["es2016"],
6+
"moduleResolution": "node",
67
"types": ["node"],
78
"strictNullChecks": true,
89
"downlevelIteration": true

0 commit comments

Comments
 (0)