From 37b39b032c386f8cc82e1a4d1c554f8e39db54f5 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Wed, 2 Sep 2020 20:29:56 +0200 Subject: [PATCH] build: add a script to flag missing unit tests in MDC packages 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. --- package.json | 3 +- scripts/check-mdc-tests.ts | 87 ++++++++++++++++++++++++++++++++++++++ scripts/tsconfig.json | 1 + 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 scripts/check-mdc-tests.ts diff --git a/package.json b/package.json index 81c0c2adc3a3..66caa77d3da4 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "approve-size-tests": "node ./scripts/approve-size-golden.js", "integration-tests": "bazel test --test_tag_filters=-view-engine-only --build_tests_only -- //integration/... -//integration/size-test/...", "integration-tests:view-engine": "bazel test --test_tag_filters=view-engine-only --build_tests_only -- //integration/... -//integration/size-test/...", - "integration-tests:size-test": "bazel test //integration/size-test/..." + "integration-tests:size-test": "bazel test //integration/size-test/...", + "check-mdc-tests": "ts-node --project scripts/tsconfig.json scripts/check-mdc-tests.ts" }, "version": "11.0.0-next.0", "dependencies": { diff --git a/scripts/check-mdc-tests.ts b/scripts/check-mdc-tests.ts new file mode 100644 index 000000000000..420f72e49f12 --- /dev/null +++ b/scripts/check-mdc-tests.ts @@ -0,0 +1,87 @@ +import {readdirSync, readFileSync} from 'fs'; +import {join, basename} from 'path'; +import {sync as glob} from 'glob'; +import chalk from 'chalk'; +import * as ts from 'typescript'; + +const srcDirectory = join(__dirname, '../src'); +const materialDirectories = readdirSync(join(srcDirectory, 'material')); + +// Goes through all the unit tests and flags the ones that don't exist in the MDC components. +readdirSync(join(srcDirectory, 'material-experimental'), {withFileTypes: true}) + .reduce((matches, entity) => { + // Go through all the `material-experimental` directories and match them to ones in `material`. + if (entity.isDirectory()) { + const materialName = entity.name.replace(/^mdc-/, ''); + + if (materialDirectories.indexOf(materialName) > -1) { + matches.set(materialName, entity.name); + } + } + + return matches; + }, new Map()) + .forEach((mdcPackage, materialPackage) => { + const mdcTestFiles = getUnitTestFiles(`material-experimental/${mdcPackage}`); + + // MDC entry points that don't have test files may not have been implemented yet. + if (mdcTestFiles.length > 0) { + // Filter out files that don't exist in the MDC package, allowing + // us to ignore some files which may not need to be ported to MDC. + const materialTestFiles = getUnitTestFiles(`material/${materialPackage}`).filter(path => { + const fileName = basename(path); + return mdcTestFiles.some(file => basename(file) === fileName); + }); + const materialTests = getTestNames(materialTestFiles); + const mdcTests = getTestNames(mdcTestFiles); + const missingTests = materialTests.filter(test => !mdcTests.includes(test)); + + if (missingTests.length > 0) { + console.log(chalk.redBright(`\nMissing tests for ${mdcPackage}:`)); + console.log(missingTests.join('\n')); + } + } + }); + +/** + * Gets all the names of all unit test files inside a + * package name, excluding `testing` packages and e2e tests. + */ +function getUnitTestFiles(name: string): string[] { + return glob('{,!(testing)/**/}!(*.e2e).spec.ts', { + absolute: true, + cwd: join(srcDirectory, name) + }); +} + +/** Gets the name of all unit tests within a set of files. */ +function getTestNames(files: string[]): string[] { + const testNames: string[] = []; + + files.forEach(file => { + const content = readFileSync(file, 'utf-8'); + const sourceFile = ts.createSourceFile(file, content, ts.ScriptTarget.ES2015); + + sourceFile.forEachChild(function walk(node: ts.Node) { + if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && + node.expression.text === 'it') { + // Note that this is a little naive since it'll take the literal text of the test + // name expression which could include things like string concatenation. It's fine + // for the limited use cases of the script. + testNames.push(node.arguments[0].getText(sourceFile) + // Replace the quotes around the test name. + .replace(/^['`]|['`]$/g, '') + // Strip newlines followed by indentation. + .replace(/\n\s+/g, ' ') + // Strip escape characters. + .replace(/\\/g, '') + // Collapse concatenated strings. + .replace(/['`]\s+\+\s+['`]/g, '')); + } else { + node.forEachChild(walk); + } + }); + }); + + return testNames; +} diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json index ac3e3866cf2e..15ae9e292329 100644 --- a/scripts/tsconfig.json +++ b/scripts/tsconfig.json @@ -3,6 +3,7 @@ "outDir": "../dist/dev-infra-scripts", "target": "es2015", "lib": ["es2016"], + "moduleResolution": "node", "types": ["node"], "strictNullChecks": true, "downlevelIteration": true