From 58e4e5630265a0192aec5299199e8be13de47608 Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Tue, 20 Jun 2023 13:35:04 +0200 Subject: [PATCH] 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 73f5c544c5f4218da48cea13bfb9008a9a51cbd9. * build: test change file that already has test * build: fix action script * Revert "build: test change file that already has test" This reverts commit 21be611abfdbce67fd7243a16d987bb21927de22. * build: update script * build: test checking with test file change * Revert "build: test checking with test file change" This reverts commit 995b64f6bab825fc16bed52c04baa7fcc15baa55. * 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 05974b67c7ac455fdd1f09b1141268a98085db89. * build: change file with no testable content * Revert "build: change file with no testable content" This reverts commit 187cc57291fc2e9756e6b5f7f1b3b7009df4f8c2. * build: change file with testable content * Revert "build: change file with testable content" This reverts commit ce716119b751041a18f4b0f1312de488dab780cf. * build: add vue file without test * Revert "build: add vue file without test" This reverts commit 756f28a373502a8cac44d0e5c0d1400f972423a9. * build: add vue file with test * Revert "build: add vue file with test" This reverts commit 8022b112fc5a15f564dddc08d7e11d2e8133bcac. * build: gather all missing tests at once * build: allow job to fail * build: update error message --- .github/scripts/check-tests.mjs | 76 +++++++++++++++++++++++++++++++ .github/scripts/package.json | 4 +- .github/workflows/check-tests.yml | 30 ++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 .github/scripts/check-tests.mjs create mode 100644 .github/workflows/check-tests.yml diff --git a/.github/scripts/check-tests.mjs b/.github/scripts/check-tests.mjs new file mode 100644 index 0000000000..b1b859c869 --- /dev/null +++ b/.github/scripts/check-tests.mjs @@ -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(); diff --git a/.github/scripts/package.json b/.github/scripts/package.json index 60f945f3f5..3c5740b2f7 100644 --- a/.github/scripts/package.json +++ b/.github/scripts/package.json @@ -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": "*" } } diff --git a/.github/workflows/check-tests.yml b/.github/workflows/check-tests.yml new file mode 100644 index 0000000000..e5f47651f4 --- /dev/null +++ b/.github/workflows/check-tests.yml @@ -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