build: Check test files (#6455)

* build: Check test files

* build: update test file checker glob pattern

* build: ignore changed files in test folders

* build: fix  gh workflow

* build: update gh events in workflow

* build: fix gh workflow

* build: fix gh workflow action script path

* build: fix gh workflow script

* build: fix gh workflow script

* build: fix gh workflow script

* build: fix gh workflow script

* build: fix gh workflow script

* build: update gh actions

* build: update gh actions

* build: test change file without test

* Revert "build: test change file without test"

This reverts commit 73f5c544c5.

* build: test change file that already has test

* build: fix action script

* Revert "build: test change file that already has test"

This reverts commit 21be611abf.

* build: update script

* build: test checking with test file change

* Revert "build: test checking with test file change"

This reverts commit 995b64f6ba.

* build: change file with no testable content

* build: use typescript to traverse a file

* Revert "build: change file with no testable content"

This reverts commit 05974b67c7.

* build: change file with no testable content

* Revert "build: change file with no testable content"

This reverts commit 187cc57291.

* build: change file with testable content

* Revert "build: change file with testable content"

This reverts commit ce716119b7.

* build: add vue file without test

* Revert "build: add vue file without test"

This reverts commit 756f28a373.

* build: add vue file with test

* Revert "build: add vue file with test"

This reverts commit 8022b112fc.

* build: gather all missing tests at once

* build: allow job to fail

* build: update error message
This commit is contained in:
Csaba Tuncsik 2023-06-20 13:35:04 +02:00 committed by GitHub
parent 0154a97773
commit 58e4e56302
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 1 deletions

76
.github/scripts/check-tests.mjs vendored Normal file
View file

@ -0,0 +1,76 @@
import fs from 'fs';
import path from 'path';
import util from 'util';
import { exec } from 'child_process';
import { glob } from "glob";
import ts from 'typescript';
const readFileAsync = util.promisify(fs.readFile);
const execAsync = util.promisify(exec);
const filterAsync = async (asyncPredicate, arr) => {
const filterResults = await Promise.all(arr.map(async item => ({
item,
shouldKeep: await asyncPredicate(item)
})));
return filterResults.filter(({shouldKeep}) => shouldKeep).map(({item}) => item);
}
// Function to check if a file has a function declaration, function expression, object method or class
const hasFunctionOrClass = async filePath => {
const fileContent = await readFileAsync(filePath, 'utf-8');
const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true);
let hasFunctionOrClass = false;
const visit = node => {
if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isMethodDeclaration(node) || ts.isClassDeclaration(node)) {
hasFunctionOrClass = true;
}
node.forEachChild(visit);
}
visit(sourceFile);
return hasFunctionOrClass;
}
const main = async () => {
// Run a git command to get a list of all changed files in the branch (branch has to be up to date with master)
const changedFiles = await execAsync('git diff --name-only --diff-filter=d origin/master..HEAD')
.then(({stdout}) => stdout.trim().split('\n').filter(Boolean));
// Get all .spec.ts and .test.ts files from the packages
const specAndTestTsFiles = await glob('packages/*/**/{test,__tests__}/*.{spec,test}.ts');
const specAndTestTsFilesNames = specAndTestTsFiles.map(file => path.parse(file).name.replace(/\.(test|spec)/, ''));
// Filter out the .ts and .vue files from the changed files
const changedVueFiles = changedFiles.filter(file => file.endsWith('.vue'));
// .ts files with any kind of function declaration or class and not in any of the test folders
const changedTsFilesWithFunction = await filterAsync(
async filePath =>
filePath.endsWith('.ts') &&
!(await glob('packages/*/**/{test,__tests__}/*.ts')).includes(filePath) &&
await hasFunctionOrClass(filePath),
changedFiles
);
// For each .ts or .vue file, check if there's a corresponding .test.ts or .spec.ts file in the repository
const missingTests = changedVueFiles.concat(changedTsFilesWithFunction).reduce((filesList, nextFile) => {
const fileName = path.parse(nextFile).name;
if (!specAndTestTsFilesNames.includes(fileName)) {
filesList.push(nextFile);
}
return filesList;
}, []);
if(missingTests.length) {
console.error(`Missing tests for:\n${missingTests.join('\n')}`);
process.exit(1);
}
};
main();

View file

@ -1,6 +1,8 @@
{
"dependencies": {
"conventional-changelog-cli": "^2.2.2",
"glob": "^10.2.7",
"semver": "^7.3.8",
"conventional-changelog-cli": "^2.2.2"
"typescript": "*"
}
}

30
.github/workflows/check-tests.yml vendored Normal file
View file

@ -0,0 +1,30 @@
name: Check Test Files
on:
pull_request:
branches:
- '**'
- '!release/*'
pull_request_target:
branches:
- master
jobs:
check-tests:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: 18.x
- run: npm install --prefix=.github/scripts --no-package-lock
- name: Check for test files
run: node .github/scripts/check-tests.mjs