diff --git a/.github/workflows/__test-action-lint.yml b/.github/workflows/__test-action-lint.yml index 724e8600..ba371b92 100644 --- a/.github/workflows/__test-action-lint.yml +++ b/.github/workflows/__test-action-lint.yml @@ -59,7 +59,9 @@ jobs: fs.writeFileSync('lint-fixture/lint.cjs', ` require('node:fs').writeFileSync('command-ran.txt', 'lint ran'); if (process.env.WRITE_REPORT === 'true') { - require('node:fs').writeFileSync('eslint-report.json', '[]'); + require('node:fs').writeFileSync('eslint-report.json', JSON.stringify([ + { filePath: 'src/index.js', messages: [], errorCount: 0, warningCount: 0 }, + ])); } process.exit(process.env.EXPECT_FAILURE === 'true' ? 1 : 0); `); diff --git a/.github/workflows/__test-action-test.yml b/.github/workflows/__test-action-test.yml index 8b76c14a..16d34512 100644 --- a/.github/workflows/__test-action-test.yml +++ b/.github/workflows/__test-action-test.yml @@ -43,7 +43,8 @@ jobs: reports: true report-file: ".reports/junit.xml,.reports/missing.xml" expected: failure - artifact: false + artifact: true + artifact-files: '["junit.xml"]' - scenario: no automatic reports failure: false reports: false @@ -101,6 +102,7 @@ jobs: EXPECT_REPORTS: ${{ matrix.reports }} EXPECT_FAILURE: ${{ matrix.failure }} ARTIFACT_ID: ${{ steps.test.outputs.report-artifact-id }} + ARTIFACT_FILES: ${{ matrix.artifact-files || '["junit.xml","lcov.info"]' }} with: script: | const assert = require('node:assert/strict'); @@ -116,7 +118,10 @@ jobs: assert.match(process.env.ARTIFACT_ID, /^\d+$/); const globber = await glob.create('downloaded-reports/**/*', { matchDirectories: false }); const files = await globber.glob(); - assert.equal(files.length, 2); + const expectedFiles = JSON.parse(process.env.ARTIFACT_FILES); + assert.deepEqual(files.map(file => require('node:path').basename(file)).sort(), expectedFiles.sort()); const junit = fs.readFileSync(files.find(file => file.endsWith('junit.xml')), 'utf8'); assert.equal(junit.includes(' file.endsWith('lcov.info')), 'utf8'), /SF:/); + if (expectedFiles.includes('lcov.info')) { + assert.match(fs.readFileSync(files.find(file => file.endsWith('lcov.info')), 'utf8'), /SF:/); + } diff --git a/.github/workflows/__test-workflow-continuous-integration.yml b/.github/workflows/__test-workflow-continuous-integration.yml index de2ab7db..4b3d13d7 100644 --- a/.github/workflows/__test-workflow-continuous-integration.yml +++ b/.github/workflows/__test-workflow-continuous-integration.yml @@ -290,7 +290,7 @@ jobs: "artifact": "dist" } test: | - {"coverage": "github"} + {"comment": false, "report-group": "act-without-container-and-github-reports"} # jscpd:ignore-end assert-package-disabled: @@ -302,6 +302,19 @@ jobs: - env: PACKAGE_ARTIFACT_ID: ${{ needs.act-without-container-and-github-reports.outputs.package-tarball-artifact-id }} run: test -z "$PACKAGE_ARTIFACT_ID" + - name: Retrieve coverage by the outer calling job ID + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + pattern: ci-reports-${{ github.run_attempt }}-act-without-container-and-github-reports-* + path: native-reports + - name: Check that consolidated reporting can find the native coverage + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const assert = require('node:assert/strict'); + const files = await (await glob.create('native-reports/**/lcov.info')).glob(); + assert.equal(files.length, 1); + assert.match(require('node:fs').readFileSync(files[0], 'utf8'), /SF:/); arrange-with-container: name: Arrange - Build container image for CI workflow diff --git a/.github/workflows/continuous-integration.md b/.github/workflows/continuous-integration.md index 1e6efe9e..bbac397f 100644 --- a/.github/workflows/continuous-integration.md +++ b/.github/workflows/continuous-integration.md @@ -3,7 +3,7 @@ # GitHub Reusable Workflow: Node.js Continuous Integration
- Node.js Continuous Integration + Node.js Continuous Integration
--- @@ -16,7 +16,6 @@ [![License](https://img.shields.io/github/license/hoverkraft-tech/ci-github-nodejs)](http://choosealicense.com/licenses/mit/) [![Stars](https://img.shields.io/github/stars/hoverkraft-tech/ci-github-nodejs?style=social)](https://img.shields.io/github/stars/hoverkraft-tech/ci-github-nodejs?style=social) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/hoverkraft-tech/ci-github-nodejs/blob/main/CONTRIBUTING.md) -![GitHub Verified Creator](https://img.shields.io/badge/GitHub-Verified%20Creator-4493F8?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJyZ2IoNjgsIDE0NywgMjQ4KSI+CiAgPHBhdGggZD0ibTkuNTg1LjUyLjkyOS42OGMuMTUzLjExMi4zMzEuMTg2LjUxOC4yMTVsMS4xMzguMTc1YTIuNjc4IDIuNjc4IDAgMCAxIDIuMjQgMi4yNGwuMTc0IDEuMTM5Yy4wMjkuMTg3LjEwMy4zNjUuMjE1LjUxOGwuNjguOTI4YTIuNjc3IDIuNjc3IDAgMCAxIDAgMy4xN2wtLjY4LjkyOGExLjE3NCAxLjE3NCAwIDAgMC0uMjE1LjUxOGwtLjE3NSAxLjEzOGEyLjY3OCAyLjY3OCAwIDAgMS0yLjI0MSAyLjI0MWwtMS4xMzguMTc1YTEuMTcgMS4xNyAwIDAgMC0uNTE4LjIxNWwtLjkyOC42OGEyLjY3NyAyLjY3NyAwIDAgMS0zLjE3IDBsLS45MjgtLjY4YTEuMTc0IDEuMTc0IDAgMCAwLS41MTgtLjIxNUwzLjgzIDE0LjQxYTIuNjc4IDIuNjc4IDAgMCAxLTIuMjQtMi4yNGwtLjE3NS0xLjEzOGExLjE3IDEuMTcgMCAwIDAtLjIxNS0uNTE4bC0uNjgtLjkyOGEyLjY3NyAyLjY3NyAwIDAgMSAwLTMuMTdsLjY4LS45MjhjLjExMi0uMTUzLjE4Ni0uMzMxLjIxNS0uNTE4bC4xNzUtMS4xNGEyLjY3OCAyLjY3OCAwIDAgMSAyLjI0LTIuMjRsMS4xMzktLjE3NWMuMTg3LS4wMjkuMzY1LS4xMDMuNTE4LS4yMTVsLjkyOC0uNjhhMi42NzcgMi42NzcgMCAwIDEgMy4xNyAwWk03LjMwMyAxLjcyOGwtLjkyNy42OGEyLjY3IDIuNjcgMCAwIDEtMS4xOC40ODlsLTEuMTM3LjE3NGExLjE3OSAxLjE3OSAwIDAgMC0uOTg3Ljk4N2wtLjE3NCAxLjEzNmEyLjY3NyAyLjY3NyAwIDAgMS0uNDg5IDEuMThsLS42OC45MjhhMS4xOCAxLjE4IDAgMCAwIDAgMS4zOTRsLjY4LjkyN2MuMjU2LjM0OC40MjQuNzUzLjQ4OSAxLjE4bC4xNzQgMS4xMzdjLjA3OC41MDkuNDc4LjkwOS45ODcuOTg3bDEuMTM2LjE3NGEyLjY3IDIuNjcgMCAwIDEgMS4xOC40ODlsLjkyOC42OGMuNDE0LjMwNS45NzkuMzA1IDEuMzk0IDBsLjkyNy0uNjhhMi42NyAyLjY3IDAgMCAxIDEuMTgtLjQ4OWwxLjEzNy0uMTc0YTEuMTggMS4xOCAwIDAgMCAuOTg3LS45ODdsLjE3NC0xLjEzNmEyLjY3IDIuNjcgMCAwIDEgLjQ4OS0xLjE4bC42OC0uOTI4YTEuMTc2IDEuMTc2IDAgMCAwIDAtMS4zOTRsLS42OC0uOTI3YTIuNjg2IDIuNjg2IDAgMCAxLS40ODktMS4xOGwtLjE3NC0xLjEzN2ExLjE3OSAxLjE3OSAwIDAgMC0uOTg3LS45ODdsLTEuMTM2LS4xNzRhMi42NzcgMi42NzcgMCAwIDEtMS4xOC0uNDg5bC0uOTI4LS42OGExLjE3NiAxLjE3NiAwIDAgMC0xLjM5NCAwWk0xMS4yOCA2Ljc4bC0zLjc1IDMuNzVhLjc1Ljc1IDAgMCAxLTEuMDYgMEw0LjcyIDguNzhhLjc1MS43NTEgMCAwIDEgLjAxOC0xLjA0Mi43NTEuNzUxIDAgMCAxIDEuMDQyLS4wMThMNyA4Ljk0bDMuMjItMy4yMmEuNzUxLjc1MSAwIDAgMSAxLjA0Mi4wMTguNzUxLjc1MSAwIDAgMSAuMDE4IDEuMDQyWiI+PC9wYXRoPgo8L3N2Zz4K) @@ -193,6 +192,8 @@ jobs: # Set to `null` or empty to disable. # Accepts a JSON object for test options. See [test action](../../actions/test/README.md). # If coverage is enabled, it should generate test and coverage reports in standard formats. + # For consolidated reports, set `comment` to false and `report-group` to the calling job ID + # used in the common `ci-reports` workflow's `job-results`. # # Example: # @@ -326,6 +327,8 @@ jobs: | | Set to `null` or empty to disable. | | | | | | Accepts a JSON object for test options. See [test action](../../actions/test/README.md). | | | | | | If coverage is enabled, it should generate test and coverage reports in standard formats. | | | | +| | For consolidated reports, set `comment` to false and `report-group` to the calling job ID | | | | +| | used in the common `ci-reports` workflow's `job-results`. | | | | | | | | | | | | Example: | | | | | | | | | | diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 80e6a09d..09f08745 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -128,6 +128,8 @@ on: Set to `null` or empty to disable. Accepts a JSON object for test options. See [test action](../../actions/test/README.md). If coverage is enabled, it should generate test and coverage reports in standard formats. + For consolidated reports, set `comment` to false and `report-group` to the calling job ID + used in the common `ci-reports` workflow's `job-results`. Example: @@ -680,7 +682,16 @@ jobs: if (testOptions.coverage === undefined) { testOptions.coverage = 'github'; } + if (testOptions.comment !== undefined && typeof testOptions.comment !== 'boolean') { + throw new Error('test.comment must be a boolean.'); + } + if (testOptions['report-group'] !== undefined && + (typeof testOptions['report-group'] !== 'string' || !/^[\w-]+$/.test(testOptions['report-group']))) { + throw new Error('test.report-group must be a job ID containing letters, numbers, underscores or hyphens.'); + } core.setOutput('coverage', testOptions.coverage ); + core.setOutput('comment', testOptions.comment ?? true); + core.setOutput('report-group', testOptions['report-group'] || ''); core.setOutput('report-file', testOptions['report-file'] || ''); core.setOutput('command', testOptions.command || 'test:ci'); @@ -692,6 +703,8 @@ jobs: container: ${{ inputs.container != '' && 'true' || 'false' }} command: ${{ steps.prepare-test-options.outputs.command }} coverage: ${{ steps.prepare-test-options.outputs.coverage }} + comment: ${{ steps.prepare-test-options.outputs.comment }} + report-group: ${{ steps.prepare-test-options.outputs.report-group }} report-file: ${{ steps.prepare-test-options.outputs.report-file }} path-mapping: ${{ needs.prepare.outputs.path-mapping || '' }} github-token: ${{ secrets.github-token || github.token }} diff --git a/actions/test/README.md b/actions/test/README.md index 745430f8..919a73f3 100644 --- a/actions/test/README.md +++ b/actions/test/README.md @@ -3,7 +3,7 @@ # ![Icon](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJmZWF0aGVyIGZlYXRoZXItY2hlY2stc3F1YXJlIiBjb2xvcj0iYmx1ZSI+PHBvbHlsaW5lIHBvaW50cz0iOSAxMSAxMiAxNCAyMiA0Ij48L3BvbHlsaW5lPjxwYXRoIGQ9Ik0yMSAxMnY3YTIgMiAwIDAgMS0yIDJINWEyIDIgMCAwIDEtMi0yVjVhMiAyIDAgMCAxIDItMmgxMSI+PC9wYXRoPjwvc3ZnPg==) GitHub Action: Test
- Test + Test
--- @@ -16,7 +16,6 @@ [![License](https://img.shields.io/github/license/hoverkraft-tech/ci-github-nodejs)](http://choosealicense.com/licenses/mit/) [![Stars](https://img.shields.io/github/stars/hoverkraft-tech/ci-github-nodejs?style=social)](https://img.shields.io/github/stars/hoverkraft-tech/ci-github-nodejs?style=social) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/hoverkraft-tech/ci-github-nodejs/blob/main/CONTRIBUTING.md) -![GitHub Verified Creator](https://img.shields.io/badge/GitHub-Verified%20Creator-4493F8?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJyZ2IoNjgsIDE0NywgMjQ4KSI+CiAgPHBhdGggZD0ibTkuNTg1LjUyLjkyOS42OGMuMTUzLjExMi4zMzEuMTg2LjUxOC4yMTVsMS4xMzguMTc1YTIuNjc4IDIuNjc4IDAgMCAxIDIuMjQgMi4yNGwuMTc0IDEuMTM5Yy4wMjkuMTg3LjEwMy4zNjUuMjE1LjUxOGwuNjguOTI4YTIuNjc3IDIuNjc3IDAgMCAxIDAgMy4xN2wtLjY4LjkyOGExLjE3NCAxLjE3NCAwIDAgMC0uMjE1LjUxOGwtLjE3NSAxLjEzOGEyLjY3OCAyLjY3OCAwIDAgMS0yLjI0MSAyLjI0MWwtMS4xMzguMTc1YTEuMTcgMS4xNyAwIDAgMC0uNTE4LjIxNWwtLjkyOC42OGEyLjY3NyAyLjY3NyAwIDAgMS0zLjE3IDBsLS45MjgtLjY4YTEuMTc0IDEuMTc0IDAgMCAwLS41MTgtLjIxNUwzLjgzIDE0LjQxYTIuNjc4IDIuNjc4IDAgMCAxLTIuMjQtMi4yNGwtLjE3NS0xLjEzOGExLjE3IDEuMTcgMCAwIDAtLjIxNS0uNTE4bC0uNjgtLjkyOGEyLjY3NyAyLjY3NyAwIDAgMSAwLTMuMTdsLjY4LS45MjhjLjExMi0uMTUzLjE4Ni0uMzMxLjIxNS0uNTE4bC4xNzUtMS4xNGEyLjY3OCAyLjY3OCAwIDAgMSAyLjI0LTIuMjRsMS4xMzktLjE3NWMuMTg3LS4wMjkuMzY1LS4xMDMuNTE4LS4yMTVsLjkyOC0uNjhhMi42NzcgMi42NzcgMCAwIDEgMy4xNyAwWk03LjMwMyAxLjcyOGwtLjkyNy42OGEyLjY3IDIuNjcgMCAwIDEtMS4xOC40ODlsLTEuMTM3LjE3NGExLjE3OSAxLjE3OSAwIDAgMC0uOTg3Ljk4N2wtLjE3NCAxLjEzNmEyLjY3NyAyLjY3NyAwIDAgMS0uNDg5IDEuMThsLS42OC45MjhhMS4xOCAxLjE4IDAgMCAwIDAgMS4zOTRsLjY4LjkyN2MuMjU2LjM0OC40MjQuNzUzLjQ4OSAxLjE4bC4xNzQgMS4xMzdjLjA3OC41MDkuNDc4LjkwOS45ODcuOTg3bDEuMTM2LjE3NGEyLjY3IDIuNjcgMCAwIDEgMS4xOC40ODlsLjkyOC42OGMuNDE0LjMwNS45NzkuMzA1IDEuMzk0IDBsLjkyNy0uNjhhMi42NyAyLjY3IDAgMCAxIDEuMTgtLjQ4OWwxLjEzNy0uMTc0YTEuMTggMS4xOCAwIDAgMCAuOTg3LS45ODdsLjE3NC0xLjEzNmEyLjY3IDIuNjcgMCAwIDEgLjQ4OS0xLjE4bC42OC0uOTI4YTEuMTc2IDEuMTc2IDAgMCAwIDAtMS4zOTRsLS42OC0uOTI3YTIuNjg2IDIuNjg2IDAgMCAxLS40ODktMS4xOGwtLjE3NC0xLjEzN2ExLjE3OSAxLjE3OSAwIDAgMC0uOTg3LS45ODdsLTEuMTM2LS4xNzRhMi42NzcgMi42NzcgMCAwIDEtMS4xOC0uNDg5bC0uOTI4LS42OGExLjE3NiAxLjE3NiAwIDAgMC0xLjM5NCAwWk0xMS4yOCA2Ljc4bC0zLjc1IDMuNzVhLjc1Ljc1IDAgMCAxLTEuMDYgMEw0LjcyIDguNzhhLjc1MS43NTEgMCAwIDEgLjAxOC0xLjA0Mi43NTEuNzUxIDAgMCAxIDEuMDQyLS4wMThMNyA4Ljk0bDMuMjItMy4yMmEuNzUxLjc1MSAwIDAgMSAxLjA0Mi4wMTguNzUxLjc1MSAwIDAgMSAuMDE4IDEuMDQyWiI+PC9wYXRoPgo8L3N2Zz4K) @@ -46,7 +45,7 @@ permissions: ## Usage ```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/test@0340fba99303d160fd476ff1e7766125b632dcf9 # ci/adopt-common-3 +- uses: hoverkraft-tech/ci-github-nodejs/actions/test@02a04ad25ceaacdf3b7200e45fb7fec07fadf4fa # 2.1.0 with: # Disable project and dependency lifecycle scripts during installation. # Set false to allow installation hooks, subject to the package manager's script approval settings. @@ -100,6 +99,11 @@ permissions: # Default: `true` comment: "true" + # Job ID used to associate native reports with the common ci-reports workflow's job-results. + # Defaults to the current job ID. Set the outer calling job ID for nested reusable workflows. + # Matrix runs keep separate artifacts labeled with their working directory. + report-group: "" + # GitHub token for coverage PR comments. # Required when coverage is `github` and individual comments are enabled. # Requires permissions to create and update PR comments: @@ -142,6 +146,9 @@ permissions: | **`path-mapping`** | Optional path mapping to adjust file paths in test and coverage reports. | **false** | - | | | See the [parse-ci-reports documentation](https://github.com/hoverkraft-tech/ci-github-common/tree/main/actions/quality/parse-ci-reports) for details. | | | | **`comment`** | Publish an individual coverage PR comment. Set false when using the common ci-reports workflow; GitHub summaries and report artifacts remain enabled. | **false** | `true` | +| **`report-group`** | Job ID used to associate native reports with the common ci-reports workflow's job-results. | **false** | - | +| | Defaults to the current job ID. Set the outer calling job ID for nested reusable workflows. | | | +| | Matrix runs keep separate artifacts labeled with their working directory. | | | | **`github-token`** | GitHub token for coverage PR comments. | **false** | - | | | Required when coverage is `github` and individual comments are enabled. | | | | | Requires permissions to create and update PR comments: | | | diff --git a/actions/test/action.yml b/actions/test/action.yml index 1c7b633f..bfc6ad25 100644 --- a/actions/test/action.yml +++ b/actions/test/action.yml @@ -73,6 +73,11 @@ inputs: comment: description: Publish an individual coverage PR comment. Set false when using the common ci-reports workflow; GitHub summaries and report artifacts remain enabled. default: "true" + report-group: + description: | + Job ID used to associate native reports with the common ci-reports workflow's job-results. + Defaults to the current job ID. Set the outer calling job ID for nested reusable workflows. + Matrix runs keep separate artifacts labeled with their working directory. github-token: description: | GitHub token for coverage PR comments. @@ -183,17 +188,26 @@ runs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: REPORT_FILES: ${{ steps.parse-coverage-reports.outputs.parsed-files }} + REPORT_GROUP: ${{ inputs.report-group || github.job }} + WORKING_DIRECTORY: ${{ steps.working-directory.outputs.absolute-path }} with: script: | + const group = process.env.REPORT_GROUP; + if (!/^[\w-]+$/.test(group)) { + throw new Error('report-group must be a job ID containing letters, numbers, underscores or hyphens.'); + } + const path = require('node:path'); + const source = path.relative(process.env.GITHUB_WORKSPACE, process.env.WORKING_DIRECTORY) + .replace(/[^\w-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 120) || 'root'; core.setOutput('files', JSON.parse(process.env.REPORT_FILES).join('\n')); - core.setOutput('id', require('node:crypto').randomUUID()); + core.setOutput('name', `ci-reports-${process.env.GITHUB_RUN_ATTEMPT}-${group}-${source}-${require('node:crypto').randomUUID()}`); - name: Upload native test and coverage reports id: upload-reports if: always() && steps.report-artifact.outputs.files != '' uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: - name: ci-reports-${{ github.run_attempt }}-${{ github.job }}-${{ steps.report-artifact.outputs.id }} + name: ${{ steps.report-artifact.outputs.name }} path: ${{ steps.report-artifact.outputs.files }} include-hidden-files: true if-no-files-found: error diff --git a/tests/continuous-integration/workflow.test.js b/tests/continuous-integration/workflow.test.js index 2f017d09..4105a441 100644 --- a/tests/continuous-integration/workflow.test.js +++ b/tests/continuous-integration/workflow.test.js @@ -18,6 +18,88 @@ const defaults = Object.fromEntries( ); const require = createRequire(import.meta.url); +test("GitHub reports default to individual comments", async () => { + const { outputs } = await runScript("test", "prepare-test-options", { + TEST_INPUT: "true", + }); + assert.equal(outputs.coverage, "github"); + assert.equal(outputs.comment, true); + assert.equal(outputs["report-group"], ""); +}); + +test("nested callers can group native reports without individual comments", async () => { + const { outputs } = await runScript("test", "prepare-test-options", { + TEST_INPUT: '{"comment":false,"report-group":"action-tests"}', + }); + assert.equal(outputs.coverage, "github"); + assert.equal(outputs.comment, false); + assert.equal(outputs["report-group"], "action-tests"); + const action = workflow.jobs.test.steps.find( + (step) => step.name === "Run tests", + ); + for (const name of ["comment", "report-group"]) { + assert.equal( + evaluate(action.with[name], { + steps: { "prepare-test-options": { outputs } }, + }).value, + outputs[name], + ); + } +}); + +for (const options of [ + { comment: "false" }, + { "report-group": "../bad" }, + { "report-group": "one,two" }, + { "report-group": false }, +]) { + test(`rejects invalid report options ${JSON.stringify(options)}`, async () => { + await assert.rejects( + runScript("test", "prepare-test-options", { + TEST_INPUT: JSON.stringify(options), + }), + /test\.(comment|report-group)/, + ); + }); +} + +test("report artifacts retain group, package and attempt without matrix collisions", async () => { + const action = readYaml("actions/test/action.yml"); + const step = action.runs.steps.find((item) => item.id === "report-artifact"); + const names = []; + for (const source of ["first", "second", "first"]) { + const outputs = {}; + await runInNewContext(`(async () => { ${step.with.script} })()`, { + require, + core: { + setOutput: (key, value) => { + outputs[key] = value; + }, + }, + process: { + env: { + GITHUB_WORKSPACE: "/workspace", + GITHUB_RUN_ATTEMPT: "2", + WORKING_DIRECTORY: `/workspace/actions/${source}`, + REPORT_GROUP: "action-tests", + REPORT_FILES: + '["/workspace/coverage/lcov.info","/workspace/coverage/junit.xml"]', + }, + }, + }); + assert.match( + outputs.name, + new RegExp(`^ci-reports-2-action-tests-actions-${source}-[a-f0-9-]+$`), + ); + assert.equal( + outputs.files, + "/workspace/coverage/lcov.info\n/workspace/coverage/junit.xml", + ); + names.push(outputs.name); + } + assert.equal(new Set(names).size, 3); +}); + async function runScript(job, stepId, env = {}) { const step = workflow.jobs[job].steps.find((item) => item.id === stepId); const outputs = {};