From 3100f8dfab0953b5eb4243518b198b32593aae45 Mon Sep 17 00:00:00 2001 From: Andrew Bernat Date: Tue, 9 Jun 2026 22:01:23 -0700 Subject: [PATCH 1/3] Add stryker dashboard to ci workflow. Also emit warnings for survived mutants on PRs. --- .github/workflows/ci.yml | 33 ++++++++++++++++----- clients/passkeys-browser/stryker.config.mjs | 4 ++- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e51d81b..5997359 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,13 +44,9 @@ jobs: path: '**/build/reports/jacoco/**' if-no-files-found: ignore - # Run mutation tests and output mutation testing artifact report - # In the future, consider: - # Stryker has a free hosted dashboard at stryker-mutator.io that tracks mutation score over time with a badge. - # Requires adding STRYKER_DASHBOARD_API_KEY as a repo secret and adding "dashboard" to reporters in - # stryker.config.mjs. - # The job then passes --dashboard.module passkeys-browser to distinguish it. - # + # Run mutation tests and submit results to the Stryker Dashboard (stryker-mutator.io), + # which tracks mutation score over time with a badge. Requires STRYKER_DASHBOARD_TOKEN + # set as a repo secret. browser-mutation: runs-on: ubuntu-latest steps: @@ -67,6 +63,29 @@ jobs: - name: Run Stryker mutation tests run: npx stryker run working-directory: clients/passkeys-browser + env: + STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_TOKEN }} + + - name: Annotate surviving mutants + if: always() + working-directory: clients/passkeys-browser + run: | + node -e " + const fs = require('fs'); + if (!fs.existsSync('reports/mutation/mutation.json')) process.exit(0); + const report = JSON.parse(fs.readFileSync('reports/mutation/mutation.json', 'utf8')); + for (const [file, data] of Object.entries(report.files || {})) { + for (const mutant of data.mutants || []) { + if (mutant.status !== 'Survived' && mutant.status !== 'NoCoverage') continue; + const {start, end} = mutant.location; + const title = mutant.status === 'NoCoverage' ? 'No Coverage' : 'Surviving Mutant'; + const msg = (mutant.mutatorName + ': ' + (mutant.replacement || '(removed)')) + .replace(/%/g, '%25').replace(/\r/g, '%0D').replace(/\n/g, '%0A'); + const path = 'clients/passkeys-browser/' + file; + process.stdout.write('::warning file=' + path + ',line=' + start.line + ',col=' + start.column + ',endLine=' + end.line + ',endColumn=' + end.column + ',title=' + title + '::' + msg + '\n'); + } + } + " - name: Upload mutation report if: always() diff --git a/clients/passkeys-browser/stryker.config.mjs b/clients/passkeys-browser/stryker.config.mjs index 1499247..c09b813 100644 --- a/clients/passkeys-browser/stryker.config.mjs +++ b/clients/passkeys-browser/stryker.config.mjs @@ -2,6 +2,8 @@ export default { testRunner: "vitest", plugins: ["@stryker-mutator/vitest-runner"], mutate: ["src/**/*.ts"], - reporters: ["html", "progress"], + reporters: ["html", "progress", "dashboard", "json"], htmlReporter: { fileName: "reports/mutation/index.html" }, + jsonReporter: { fileName: "reports/mutation/mutation.json" }, + dashboard: { module: "passkeys-browser" }, }; From b3c7e3e26b8f190f7151c55c3080e43f6c295566 Mon Sep 17 00:00:00 2001 From: Andrew Bernat <31580217+bernata@users.noreply.github.com> Date: Tue, 9 Jun 2026 22:06:28 -0700 Subject: [PATCH 2/3] use STRYKER_DASHBOARD_API_KEY --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5997359..e3f00bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: run: npx stryker run working-directory: clients/passkeys-browser env: - STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_TOKEN }} + STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - name: Annotate surviving mutants if: always() From 828c1984adc7e0445e29f5846685407a24ff3a0c Mon Sep 17 00:00:00 2001 From: Ned Wolpert Date: Wed, 10 Jun 2026 06:06:21 -0700 Subject: [PATCH 3/3] Apply suggestion from @wolpert The secret itself --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e3f00bc..5997359 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: run: npx stryker run working-directory: clients/passkeys-browser env: - STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} + STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_TOKEN }} - name: Annotate surviving mutants if: always()