From d4f9c3688068f7da923d47400cba3d1ab8197c30 Mon Sep 17 00:00:00 2001 From: Gael Girodon Date: Mon, 20 May 2024 00:12:18 +0200 Subject: [PATCH] Improve reports loading and clean code - Improve JUnit test reports loading - Refine file patterns - Load all reports instead of only the first one - Read only top-level `` tags - Exclude some directories from file search - Improve the documentation - Clarify how reports are loaded - Add more examples - Clean code (quotes, semicolons, imports, tests, ...) - Update dependencies - Update ESLint to v9 --- .eslintrc.yml | 9 - README.md | 32 +- eslint.config.js | 8 + package-lock.json | 283 +++++++----------- package.json | 6 +- src/badges/coverage.test.js | 10 +- src/badges/index.test.js | 46 +-- src/badges/tests.test.js | 10 +- src/reports/cobertura.js | 4 +- src/reports/cobertura.test.js | 14 +- src/reports/go.js | 6 +- src/reports/go.test.js | 14 +- src/reports/index.js | 2 +- src/reports/jacoco.js | 4 +- src/reports/jacoco.test.js | 14 +- src/reports/junit.js | 57 +++- src/reports/junit.test.js | 16 +- src/util/index.js | 24 +- .../{test-results.xml => test-results-1.xml} | 0 test/data/junit/test-results-2.xml | 9 + test/data/junit/vendor/test-results-3.xml | 5 + test/e2e.test.js | 8 +- 22 files changed, 290 insertions(+), 291 deletions(-) delete mode 100644 .eslintrc.yml create mode 100644 eslint.config.js rename test/data/junit/{test-results.xml => test-results-1.xml} (100%) create mode 100644 test/data/junit/test-results-2.xml create mode 100644 test/data/junit/vendor/test-results-3.xml diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index 5dc5758..0000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,9 +0,0 @@ -env: - es2021: true - node: true - mocha: true -extends: eslint:recommended -parserOptions: - ecmaVersion: latest - sourceType: module -rules: {} diff --git a/README.md b/README.md index 7c2096c..d1f9e1b 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Only matched report formats will get a file uploaded to the Gist. ### Go -Write the verbose test output (`>` or `tee`) with coverage enabled to a +Write the verbose test output (`>` or `tee`) with coverage enabled to a single `test*.{out,txt}` file next to the `go.mod` file: - `RUN`, `PASS` and `FAIL` flags will be used to count tests @@ -100,21 +100,26 @@ sure the last percentage is the global coverage value. ### JUnit -Write the test report to a file matching: +Write test reports to files matching: +- `**/TEST-*.xml` - `**/report.xml` -- `**/*TEST*.xml` - `**/*test*.xml` - `**/*junit*.xml` This is the default format and location with JUnit, but most test runners -support this format too, natively or using an additional reporter, e.g.: - -- **Mocha**: `mocha --reporter mocha-junit-reporter` -- **Jest**: `jest --reporters="jest-junit"` +support this format too, natively or using an additional reporter: + +- **Maven**: `mvn test` → `target/{surefire,failsafe}-reports/TEST-*.xml` +- **Gradle**: `gradle test` → `build/test-results/test/**/TEST-*.xml` +- **Node.js**: `node --test --test-reporter=junit --test-reporter-destination=report.xml` +- **Mocha**: `mocha --reporter mocha-junit-reporter` → `test-results.xml` +- **Jest**: `jest --reporters="jest-junit"` → `junit.xml` +- **Deno**: `deno test --junit-path=report.xml` - **PHPUnit**: `phpunit --log-junit report.xml` -The number of tests and failures will be extracted from `` tags. +The number of tests and failures will be extracted from top-level `` +tags, from all matching and valid report files. ➡️ `{repo}-[{ref}-]junit-tests.json` @@ -126,14 +131,14 @@ Write the coverage report to a file matching: - `**/*coverage*.xml` This is the default format and location with Cobertura, but most code coverage -tools support this format too, natively or using an additional reporter, e.g.: +tools support this format too, natively or using an additional reporter: -- **c8**: `c8 --reporter cobertura [...]` -- **nyc**: `nyc --reporter cobertura [...]` +- **c8**: `c8 --reporter cobertura [...]` → `coverage/cobertura-coverage.xml` +- **nyc**: `nyc --reporter cobertura [...]` → `coverage/cobertura-coverage.xml` - **PHPUnit**: `phpunit --coverage-cobertura coverage.xml` The coverage will be extracted from the `line-rate` attribute of the -`` tag. +`` tag, from the first matching and valid report file. ➡️ `{repo}-[{ref}-]cobertura-coverage.json` @@ -147,7 +152,8 @@ Write the coverage report to a file matching: This is the default format and location with JaCoCo, but some code coverage tools may support this format too. -The coverage will be extracted from the last `` tag with type `LINE`. +The coverage will be extracted from the last `` tag with type `LINE`, +from the first matching and valid report file. ➡️ `{repo}-[{ref}-]jacoco-coverage.json` diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..d4c528f --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,8 @@ +import globals from 'globals'; +import pluginJs from '@eslint/js'; + +export default [ + { languageOptions: { globals: globals.node } }, + { languageOptions: { globals: globals.mocha } }, + pluginJs.configs.recommended +]; diff --git a/package-lock.json b/package-lock.json index 15ea016..dbf7195 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,11 @@ "@actions/glob": "^0.4.0" }, "devDependencies": { + "@eslint/js": "^9.3.0", "@vercel/ncc": "^0.38.1", "c8": "^9.1.0", - "eslint": "^8.57.0", + "eslint": "^9.3.0", + "globals": "^15.3.0", "mocha": "^10.4.0", "mocha-junit-reporter": "^2.2.1" } @@ -80,6 +82,18 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", @@ -90,15 +104,15 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -106,19 +120,31 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.3.0.tgz", + "integrity": "sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@fastify/busboy": { @@ -130,12 +156,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -162,6 +188,19 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -282,9 +321,9 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "22.1.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.1.0.tgz", - "integrity": "sha512-pGUdSP+eEPfZiQHNkZI0U01HLipxncisdJQB4G//OAmfeO8sqTQ9KRa0KF03TUPCziNsoXUrTg4B2Q1EX++T0Q==" + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" }, "node_modules/@octokit/plugin-paginate-rest": { "version": "9.2.1", @@ -368,11 +407,11 @@ } }, "node_modules/@octokit/types": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.4.1.tgz", - "integrity": "sha512-Y73oOAzRBAUzR/iRAbGULzpNkX8vaxKCqEtg6K74Ff3w9f5apFnWtE/2nade7dMWWW3bS5Kkd6DJS4HF04xreg==", + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", + "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", "dependencies": { - "@octokit/openapi-types": "^22.1.0" + "@octokit/openapi-types": "^22.2.0" } }, "node_modules/@types/istanbul-lib-coverage": { @@ -381,12 +420,6 @@ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, "node_modules/@vercel/ncc": { "version": "0.38.1", "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.1.tgz", @@ -517,12 +550,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -759,18 +792,6 @@ "node": ">=0.3.1" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -799,41 +820,37 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.3.0.tgz", + "integrity": "sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.3.0", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", + "eslint-scope": "^8.0.1", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", @@ -847,52 +864,52 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", + "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", "dev": true, "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.11.3", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -968,21 +985,21 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -1017,17 +1034,16 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { @@ -1134,26 +1150,17 @@ } }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.3.0.tgz", + "integrity": "sha512-cCdyVjIUVTtX8ZsPkq1oCsOsLmGIswqnjZYMJJTGaNApj1yHtLSymKhwH51ttirREn75z3p4k051clwg7rvNKA==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1446,18 +1453,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -1855,41 +1850,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -1934,13 +1894,10 @@ ] }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -2112,18 +2069,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/undici": { "version": "5.28.4", "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", @@ -2238,12 +2183,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index 636584d..eca86a2 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "type": "module", "scripts": { "build": "ncc build index.js", - "lint": "eslint \"**/*.js\" --ignore-path \".gitignore\"", + "lint": "eslint", "test": "mocha src", "test:e2e": "mocha test", "test:ci": "c8 --reporter=cobertura --all --include \"src/**/*.js\" mocha --reporter mocha-junit-reporter src test || exit 0" @@ -36,9 +36,11 @@ "@actions/glob": "^0.4.0" }, "devDependencies": { + "@eslint/js": "^9.3.0", "@vercel/ncc": "^0.38.1", "c8": "^9.1.0", - "eslint": "^8.57.0", + "eslint": "^9.3.0", + "globals": "^15.3.0", "mocha": "^10.4.0", "mocha-junit-reporter": "^2.2.1" } diff --git a/src/badges/coverage.test.js b/src/badges/coverage.test.js index b04ef00..f904edb 100644 --- a/src/badges/coverage.test.js +++ b/src/badges/coverage.test.js @@ -1,8 +1,8 @@ -import assert from 'assert'; +import assert from 'node:assert/strict'; import { buildBadge } from './coverage.js'; -describe('badges/coverage', function () { - describe('#buildBadge()', function () { +describe('badges/coverage', () => { + describe('#buildBadge()', () => { const tests = [ { data: { coverage: 0 }, expected: { message: '0%', color: 'red' } }, { data: { coverage: 45.2 }, expected: { message: '45%', color: 'orange' } }, @@ -13,9 +13,9 @@ describe('badges/coverage', function () { { data: { coverage: 100 }, expected: { message: '100%', color: 'brightgreen' } } ]; for (const { data, expected } of tests) { - it(`should return a ${expected.color} "${expected.message}" badge for ${data.coverage}% coverage`, function () { + it(`should return a ${expected.color} "${expected.message}" badge for ${data.coverage}% coverage`, () => { const actual = buildBadge(data); - assert.deepStrictEqual(actual, expected); + assert.deepEqual(actual, expected); }); } }); diff --git a/src/badges/index.test.js b/src/badges/index.test.js index 6a6f9da..a688a3c 100644 --- a/src/badges/index.test.js +++ b/src/badges/index.test.js @@ -1,9 +1,9 @@ -import assert from 'assert'; +import assert from 'node:assert/strict'; import { buildBadge } from './index.js'; -describe('badges/index', function () { - describe('#buildBadge()', function () { - const env = { +describe('badges/index', () => { + describe('#buildBadge()', () => { + const envBackup = { GITHUB_REPOSITORY: process.env.GITHUB_REPOSITORY, GITHUB_REF_NAME: process.env.GITHUB_REF_NAME, INPUT_PREFIX: process.env.INPUT_PREFIX, @@ -12,46 +12,46 @@ describe('badges/index', function () { const prefix = '$GITHUB_REPOSITORY_NAME'; // Default prefix value const tests = [ // Default settings - { env: { repository: 'user/repo', ref_name: 'main', prefix, ref: 'false' }, expected: { name: 'repo-go-coverage.json' } }, + { env: { repository: 'user/repo', refName: 'main', prefix, ref: 'false' }, expected: { name: 'repo-go-coverage.json' } }, // Custom filenames prefix - { env: { repository: 'user/repo', ref_name: 'main', prefix: 'prefix', ref: 'false' }, expected: { name: 'prefix-go-coverage.json' } }, + { env: { repository: 'user/repo', refName: 'main', prefix: 'prefix', ref: 'false' }, expected: { name: 'prefix-go-coverage.json' } }, // Empty custom filenames prefix - { env: { repository: 'user/repo', ref_name: 'main', prefix: '', ref: 'false' }, expected: { name: 'go-coverage.json' } }, + { env: { repository: 'user/repo', refName: 'main', prefix: '', ref: 'false' }, expected: { name: 'go-coverage.json' } }, // Include ref in filenames - { env: { repository: 'user/repo', ref_name: 'main', prefix, ref: 'true' }, expected: { name: 'repo-main-go-coverage.json' } }, + { env: { repository: 'user/repo', refName: 'main', prefix, ref: 'true' }, expected: { name: 'repo-main-go-coverage.json' } }, // Custom filenames prefix and include ref in filenames - { env: { repository: 'user/repo', ref_name: 'main', prefix: 'prefix', ref: 'true' }, expected: { name: 'prefix-main-go-coverage.json' } }, + { env: { repository: 'user/repo', refName: 'main', prefix: 'prefix', ref: 'true' }, expected: { name: 'prefix-main-go-coverage.json' } }, // Empty custom filenames prefix and include ref in filenames - { env: { repository: 'user/repo', ref_name: 'main', prefix: '', ref: 'true' }, expected: { name: 'main-go-coverage.json' } }, + { env: { repository: 'user/repo', refName: 'main', prefix: '', ref: 'true' }, expected: { name: 'main-go-coverage.json' } }, // Ref with special characters - { env: { repository: 'user/repo', ref_name: 'feature/task', prefix, ref: 'true' }, expected: { name: 'repo-feature-task-go-coverage.json' } }, + { env: { repository: 'user/repo', refName: 'feature/task', prefix, ref: 'true' }, expected: { name: 'repo-feature-task-go-coverage.json' } }, // Include ref in filenames but empty - { env: { repository: 'user/repo', ref_name: '', prefix, ref: 'true' }, expected: { name: 'repo-go-coverage.json' } }, + { env: { repository: 'user/repo', refName: '', prefix, ref: 'true' }, expected: { name: 'repo-go-coverage.json' } }, // Empty repository variable - { env: { repository: '', ref_name: 'main', prefix, ref: 'true' }, expected: { name: 'main-go-coverage.json' } }, + { env: { repository: '', refName: 'main', prefix, ref: 'true' }, expected: { name: 'main-go-coverage.json' } }, // Empty repository variable but custom filenames prefix - { env: { repository: '', ref_name: 'main', prefix: 'prefix', ref: 'true' }, expected: { name: 'prefix-main-go-coverage.json' } }, + { env: { repository: '', refName: 'main', prefix: 'prefix', ref: 'true' }, expected: { name: 'prefix-main-go-coverage.json' } }, // Empty repository variable and ref - { env: { repository: '', ref_name: '', prefix, ref: 'true' }, expected: { name: 'go-coverage.json' } } + { env: { repository: '', refName: '', prefix, ref: 'true' }, expected: { name: 'go-coverage.json' } } ]; for (const { env, expected } of tests) { - it(`should return a badge with name "${expected.name}" for ${JSON.stringify(env)}`, function () { + it(`should return a badge with name "${expected.name}" for ${JSON.stringify(env)}`, () => { process.env.GITHUB_REPOSITORY = env.repository; - process.env.GITHUB_REF_NAME = env.ref_name; + process.env.GITHUB_REF_NAME = env.refName; process.env.INPUT_PREFIX = env.prefix; process.env.INPUT_REF = env.ref; const actual = buildBadge({ type: 'coverage', format: 'go', data: { coverage: 0 } }); - assert.deepStrictEqual(actual, { + assert.deepEqual(actual, { name: expected.name, content: { schemaVersion: 1, label: 'coverage', message: '0%', color: 'red' } }); }); } - after(function () { - process.env.GITHUB_REPOSITORY = env.GITHUB_REPOSITORY; - process.env.GITHUB_REF_NAME = env.GITHUB_REF_NAME; - process.env.INPUT_PREFIX = env.INPUT_PREFIX; - process.env.INPUT_REF = env.INPUT_REF; + after(() => { + process.env.GITHUB_REPOSITORY = envBackup.GITHUB_REPOSITORY; + process.env.GITHUB_REF_NAME = envBackup.GITHUB_REF_NAME; + process.env.INPUT_PREFIX = envBackup.INPUT_PREFIX; + process.env.INPUT_REF = envBackup.INPUT_REF; }); }); }); diff --git a/src/badges/tests.test.js b/src/badges/tests.test.js index 5e4f22a..097fbdd 100644 --- a/src/badges/tests.test.js +++ b/src/badges/tests.test.js @@ -1,16 +1,16 @@ -import assert from 'assert'; +import assert from 'node:assert/strict'; import { buildBadge } from './tests.js'; -describe('badges/tests', function () { - describe('#buildBadge()', function () { +describe('badges/tests', () => { + describe('#buildBadge()', () => { const tests = [ { data: { passed: 5, failed: 0 }, expected: { message: '5 passed', color: 'brightgreen' } }, { data: { passed: 4, failed: 1 }, expected: { message: '4 passed, 1 failed', color: 'red' } } ]; for (const { data, expected } of tests) { - it(`should return a ${expected.color} "${expected.message}" badge for ${data.passed}/${data.failed} tests`, function () { + it(`should return a ${expected.color} "${expected.message}" badge for ${data.passed}/${data.failed} tests`, () => { const actual = buildBadge(data); - assert.deepStrictEqual(actual, expected); + assert.deepEqual(actual, expected); }); } }); diff --git a/src/reports/cobertura.js b/src/reports/cobertura.js index 55dd42c..1a99ae3 100644 --- a/src/reports/cobertura.js +++ b/src/reports/cobertura.js @@ -1,6 +1,6 @@ import * as core from '@actions/core'; -import { promises as fs } from 'fs'; -import { join } from 'path'; +import * as fs from 'node:fs/promises'; +import { join } from 'node:path'; import { globNearest } from '../util/index.js'; /** diff --git a/src/reports/cobertura.test.js b/src/reports/cobertura.test.js index 446248f..84bc89a 100644 --- a/src/reports/cobertura.test.js +++ b/src/reports/cobertura.test.js @@ -1,13 +1,13 @@ -import assert from 'assert'; -import { join } from 'path'; +import assert from 'node:assert/strict'; +import { join } from 'node:path'; import { getReports } from './cobertura.js'; -describe('reports/cobertura', function () { - describe('#getReports()', function () { - it(`should return coverage report`, async function () { +describe('reports/cobertura', () => { + describe('#getReports()', () => { + it('should return coverage report', async () => { const reports = await getReports(join(process.cwd(), 'test/data/cobertura')); - assert.equal(reports.length, 1) - assert.deepStrictEqual(reports, [ + assert.equal(reports.length, 1); + assert.deepEqual(reports, [ { type: 'coverage', data: { coverage: 92.09999999999999 } } ]); }); diff --git a/src/reports/go.js b/src/reports/go.js index c33b6eb..c8d6b39 100644 --- a/src/reports/go.js +++ b/src/reports/go.js @@ -1,6 +1,6 @@ import * as core from '@actions/core'; -import { promises as fs } from 'fs'; -import { dirname, join } from 'path'; +import * as fs from 'node:fs/promises'; +import { dirname, join } from 'node:path'; import { globNearest } from '../util/index.js'; /** @@ -29,7 +29,7 @@ export async function getReports(root) { } const passed = (report.match(/--- PASS/g) || []).length; const failed = (report.match(/--- FAIL/g) || []).length; - badges.push({ type: 'tests', data: { passed, failed, tests } }) + badges.push({ type: 'tests', data: { passed, failed, tests } }); const percentages = report.match(/(?<=\s)[0-9.]+(?=%)/g); if (percentages && percentages.length >= 1) { const coverage = parseFloat(percentages.slice(-1)[0]); diff --git a/src/reports/go.test.js b/src/reports/go.test.js index 4a17483..fe353eb 100644 --- a/src/reports/go.test.js +++ b/src/reports/go.test.js @@ -1,13 +1,13 @@ -import assert from 'assert'; -import { join } from 'path'; +import assert from 'node:assert/strict'; +import { join } from 'node:path'; import { getReports } from './go.js'; -describe('reports/go', function () { - describe('#getReports()', function () { - it(`should return tests and coverage reports`, async function () { +describe('reports/go', () => { + describe('#getReports()', () => { + it('should return tests and coverage reports', async () => { const reports = await getReports(join(process.cwd(), 'test/data/go')); - assert.equal(reports.length, 2) - assert.deepStrictEqual(reports, [ + assert.equal(reports.length, 2); + assert.deepEqual(reports, [ { type: 'tests', data: { passed: 3, failed: 0, tests: 3 } }, { type: 'coverage', data: { coverage: 96.5 } } ]); diff --git a/src/reports/index.js b/src/reports/index.js index 9d20f73..0cf2aee 100644 --- a/src/reports/index.js +++ b/src/reports/index.js @@ -30,6 +30,6 @@ export async function getReports() { core.warning(`Skipping ${id} report format: ${error}`); } } - core.info(`Loaded ${all.length} reports`); + core.info(`Loaded ${all.length} report(s)`); return all; } diff --git a/src/reports/jacoco.js b/src/reports/jacoco.js index 9b614d6..ddddd9d 100644 --- a/src/reports/jacoco.js +++ b/src/reports/jacoco.js @@ -1,6 +1,6 @@ import * as core from '@actions/core'; -import { promises as fs } from 'fs'; -import { join } from 'path'; +import * as fs from 'node:fs/promises'; +import { join } from 'node:path'; import { globNearest } from '../util/index.js'; /** diff --git a/src/reports/jacoco.test.js b/src/reports/jacoco.test.js index 2d89ec7..d9e2cab 100644 --- a/src/reports/jacoco.test.js +++ b/src/reports/jacoco.test.js @@ -1,13 +1,13 @@ -import assert from 'assert'; -import { join } from 'path'; +import assert from 'node:assert/strict'; +import { join } from 'node:path'; import { getReports } from './jacoco.js'; -describe('reports/jacoco', function () { - describe('#getReports()', function () { - it(`should return coverage report`, async function () { +describe('reports/jacoco', () => { + describe('#getReports()', () => { + it('should return coverage report', async () => { const reports = await getReports(join(process.cwd(), 'test/data/jacoco')); - assert.equal(reports.length, 1) - assert.deepStrictEqual(reports, [ + assert.equal(reports.length, 1); + assert.deepEqual(reports, [ { type: 'coverage', data: { coverage: 65.61056105610561 } } ]); }); diff --git a/src/reports/junit.js b/src/reports/junit.js index ddb2a66..5891a6b 100644 --- a/src/reports/junit.js +++ b/src/reports/junit.js @@ -1,6 +1,6 @@ import * as core from '@actions/core'; -import { promises as fs } from 'fs'; -import { join } from 'path'; +import * as fs from 'node:fs/promises'; +import { join } from 'node:path'; import { globNearest } from '../util/index.js'; /** @@ -11,31 +11,56 @@ import { globNearest } from '../util/index.js'; export async function getReports(root) { core.info('Load JUnit tests report'); const patterns = [ + join(root, '**/TEST-*.xml'), join(root, '**/report.xml'), - join(root, '**/*TEST*.xml'), join(root, '**/*test*.xml'), join(root, '**/*junit*.xml') ]; const reports = await globNearest(patterns); - const badges = []; + const data = { passed: 0, failed: 0, tests: 0 }; + let count = 0; for (const r of reports) { core.info(`Load JUnit report '${r}'`); - const report = await fs.readFile(r, { encoding: 'utf8' }); - const testSuites = report.match(/]+)>/g); - if (!testSuites) { + const testSuites = await getTestSuiteTags(r); + if (testSuites.length === 0) { core.info('Report is not a valid JUnit report'); continue; // Invalid report file, trying the next one } - const data = { passed: 0, failed: 0, tests: 0 }; for (const ts of testSuites) { - data.failed += parseInt(ts.match(/failures="([0-9]+)"/)?.[1] ?? "0"); - data.failed += parseInt(ts.match(/errors="([0-9]+)"/)?.[1] ?? "0"); - data.tests += parseInt(ts.match(/tests="([0-9]+)"/)?.[1] ?? "0"); + data.failed += parseInt(ts.match(/failures="([0-9]+)"/)?.[1] ?? '0'); + data.failed += parseInt(ts.match(/errors="([0-9]+)"/)?.[1] ?? '0'); + data.tests += parseInt(ts.match(/tests="([0-9]+)"/)?.[1] ?? '0'); } - data.passed = data.tests - data.failed; - badges.push({ type: 'tests', data }) - break; // Successfully loaded a report file, can return now + count++; } - core.info(`Loaded ${badges.length} JUnit report(s)`); - return badges; + data.passed = data.tests - data.failed; + core.info(`Loaded ${count} JUnit report(s)`); + return [{ type: 'tests', data }]; +} + +/** + * Extract top-level `` opening tags + * from the given JUnit test report file. + * Some test runners output nested `` tags + * (e.g. Node.js test runner), these nested tags must be + * ignored as values are aggregated in top-level ones. + * @param {string} path Path to the JUnit test report file + * @returns {Promise} Top-level `` opening tags + */ +async function getTestSuiteTags(path) { + const testSuites = []; + let depth = 0; + const report = await fs.readFile(path, { encoding: 'utf8' }); + const tags = report.match(/<\/?testsuite(?:[^s>][^>]+|\s*)>/g) ?? []; + for (const tag of tags) { + if (tag.startsWith(' { + describe('#getReports()', () => { + it('should return tests report', async () => { const reports = await getReports(join(process.cwd(), 'test/data/junit')); - assert.equal(reports.length, 1) - assert.deepStrictEqual(reports, [ - { type: 'tests', data: { passed: 6, failed: 4, tests: 10 } } + assert.equal(reports.length, 1); + assert.deepEqual(reports, [ + { type: 'tests', data: { passed: 8, failed: 4, tests: 12 } } ]); }); }); diff --git a/src/util/index.js b/src/util/index.js index 17cb5f3..760ce50 100644 --- a/src/util/index.js +++ b/src/util/index.js @@ -1,15 +1,29 @@ import * as glob from '@actions/glob'; /** - * Returns files and directories matching the glob patterns, - * sorted by the nearest. + * Returns files matching the glob patterns, + * excluding some common unwanted directories from the search, + * sorted by ascending depth and name. * @param {string[]} patterns Glob patterns * @return {Promise} Files sorted by the nearest. */ export async function globNearest(patterns) { - const globber = await glob.create(patterns.join('\n')); + const safePatterns = [ + ...patterns, + '!**/.git/**', + '!**/.idea/**', + '!**/.vscode/**', + '!**/node_modules/**', + '!**/vendor/**' + ]; + const globber = await glob.create(safePatterns.join('\n'), { + followSymbolicLinks: false, + implicitDescendants: false, + matchDirectories: false + }); const files = await globber.glob(); return files.sort((a, b) => { - return (a.match(/[\\/]/g)?.length ?? 0) - (b.match(/[\\/]/g)?.length ?? 0); - }) + const depthDiff = (a.match(/[\\/]/g)?.length ?? 0) - (b.match(/[\\/]/g)?.length ?? 0); + return depthDiff !== 0 ? depthDiff : a.localeCompare(b); + }); } diff --git a/test/data/junit/test-results.xml b/test/data/junit/test-results-1.xml similarity index 100% rename from test/data/junit/test-results.xml rename to test/data/junit/test-results-1.xml diff --git a/test/data/junit/test-results-2.xml b/test/data/junit/test-results-2.xml new file mode 100644 index 0000000..0d98714 --- /dev/null +++ b/test/data/junit/test-results-2.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/test/data/junit/vendor/test-results-3.xml b/test/data/junit/vendor/test-results-3.xml new file mode 100644 index 0000000..06ab939 --- /dev/null +++ b/test/data/junit/vendor/test-results-3.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/test/e2e.test.js b/test/e2e.test.js index 82d13c9..e1fa4d0 100644 --- a/test/e2e.test.js +++ b/test/e2e.test.js @@ -1,12 +1,12 @@ -import assert from 'assert'; import * as core from '@actions/core'; import * as github from '@actions/github'; +import assert from 'node:assert'; import { main } from '../src/index.js'; describe('CI Badges action', function () { this.timeout(10000); - describe('#main()', function () { - it('should work end-to-end', async function () { + describe('#main()', () => { + it('should work end-to-end', async () => { // Set inputs process.env['INPUT_GIST-ID'] = process.env.GIST_ID; process.env['INPUT_TOKEN'] = process.env.GIST_TOKEN; @@ -24,7 +24,7 @@ describe('CI Badges action', function () { // Check update date const updatedAt = new Date(response.data.updated_at).getTime(); const now = new Date().getTime(); - assert.ok((now - updatedAt) / 1000 < 10) + assert.ok((now - updatedAt) / 1000 < 10); // Check uploaded files const files = Object.keys(response.data.files); [