diff --git a/.github/actions/aggregate-report/action.yml b/.github/actions/aggregate-report/action.yml new file mode 100644 index 00000000..9567d802 --- /dev/null +++ b/.github/actions/aggregate-report/action.yml @@ -0,0 +1,78 @@ +name: Aggregate report +description: 'Process downloaded artifacts (downloaded with actions/download-artifact) and produce a markdown report section' +inputs: + package-name: + description: 'Full package name (workspace)' + required: true + working-directory: + description: 'Path to the ./packages/name_of_your_package_folder' + required: true +outputs: + report: + description: 'Markdown report section generated by the action' + value: ${{ steps.section-report.outputs.report }} +runs: + using: 'composite' + steps: + - name: Download artifacts + uses: actions/download-artifact@v8 + + - name: Find current PR's number + uses: jwalton/gh-find-current-pr@v1 + id: find_pr + + - name: Process coverage reports + uses: actions/github-script@v9 + id: process_test_report + env: + PACKAGE_NAME: ${{ inputs.package-name }} + with: + script: | + const processReports = require('./.github/scripts/compare-coverage.js').processReports; + + core.setOutput('message', processReports(process.env.PACKAGE_NAME, 'test-report', 'test-report-base')); + + - name: Process mutation report + uses: actions/github-script@v9 + id: process_mutation_report + env: + WORKING_DIR: ${{ inputs.working-directory }} + PR_NUMBER: ${{ steps.find_pr.outputs.number }} + PACKAGE_NAME: ${{ inputs.package-name }} + with: + script: | + const processMutationReport = require('./.github/scripts/process-mutation.js').processMutationReport; + + core.setOutput('message', processMutationReport('mutation-report', `${process.env.WORKING_DIR}/reports/mutation/mutation.json`, 'changed-files.json', process.env.PR_NUMBER, process.env.PACKAGE_NAME)); + + - name: Acquire comment-update lock + id: acquire_lock + uses: softprops/turnstyle@v3 + with: + queue-name: aggregated-comment-${{ steps.find_pr.outputs.number || github.ref_name || github.ref }}}} + + - name: Create or update PR comment with report + uses: actions/github-script@v9 + env: + REPORT: ${{ steps.section-report.outputs.report }} + PACKAGE: ${{ inputs.package-name }} + PR_NUMBER: ${{ steps.find_pr.outputs.number }} + MUTATION_MESSAGE: ${{ steps.process_mutation_report.outputs.message }} + UNIT_TEST_MESSAGE: ${{ steps.process_test_report.outputs.message }} + with: + script: | + const updater = require('./.github/scripts/update-aggregated-comment.js'); + + (async () => { + try { + const packageName = process.env.PACKAGE; + const mutationRow = process.env.MUTATION_MESSAGE; + const unitTestRow = process.env.UNIT_TEST_MESSAGE; + const pr = process.env.PR_NUMBER || undefined; + // Pass the injected core, github and context objects + await updater.updateAggregatedComment(unitTestRow, mutationRow, packageName, pr, core, github, context); + } catch (err) { + core.error(err && err.stack ? err.stack : String(err)); + throw err; + } + })(); diff --git a/.github/actions/base-coverage/action.yml b/.github/actions/base-coverage/action.yml new file mode 100644 index 00000000..9f6c0336 --- /dev/null +++ b/.github/actions/base-coverage/action.yml @@ -0,0 +1,45 @@ +name: Base coverage +description: "Checkout base ref, run package coverage and upload artifact" +inputs: + package-name: + description: 'Full package name (workspace)' + required: true + working-directory: + description: 'Working directory relative to repo root (package folder)' + required: true + +runs: + using: "composite" + steps: + - name: Checkout base ref + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.base.ref }} + fetch-depth: 0 + + - name: Setup Node + uses: actions/setup-node@v6 + with: + node-version-file: .nvmrc + + - name: Setup environment + uses: ./.github/actions/setup + + - name: Build the package + shell: bash + run: yarn workspace ${{ inputs.package-name }} run build + working-directory: ${{ inputs.working-directory }} + + - name: Run coverage + shell: bash + run: yarn workspace ${{ inputs.package-name }} test:coverage + working-directory: ${{ inputs.working-directory }} + + - name: Upload base coverage artifact + uses: actions/upload-artifact@v7 + with: + name: test-report-base + path: | + ${{ inputs.working-directory }}/coverage + ${{ inputs.working-directory }}/jest-report.json + retention-days: 3 diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 7ce0aca8..5b06b560 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -7,7 +7,7 @@ runs: using: "composite" steps: - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v6 with: node-version-file: .nvmrc diff --git a/.github/actions/lint/action.yml b/.github/actions/lint/action.yml index 765cd6f6..9dfc4998 100644 --- a/.github/actions/lint/action.yml +++ b/.github/actions/lint/action.yml @@ -7,7 +7,7 @@ runs: using: "composite" steps: - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v6 with: node-version-file: .nvmrc diff --git a/.github/actions/mutation-tests-all-files/action.yml b/.github/actions/mutation-tests-all-files/action.yml index 7f0bfa52..a0b2c52d 100644 --- a/.github/actions/mutation-tests-all-files/action.yml +++ b/.github/actions/mutation-tests-all-files/action.yml @@ -8,11 +8,13 @@ inputs: stryker_dashboard_api_key: description: 'Stryker dashboard api key' required: true - + working-directory: + description: 'Path to the ./packages/name_of_your_package_folder' + required: true runs: using: "composite" steps: - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v6 with: node-version-file: .nvmrc @@ -24,7 +26,7 @@ runs: package-name: ${{ inputs.package-name }} - name: Get changed files - uses: tj-actions/changed-files@v39.0.0 + uses: tj-actions/changed-files@v47 id: changed-files with: files_yaml: | diff --git a/.github/actions/mutation-tests-changed-files/action.yml b/.github/actions/mutation-tests-changed-files/action.yml index 1d764581..fee294eb 100644 --- a/.github/actions/mutation-tests-changed-files/action.yml +++ b/.github/actions/mutation-tests-changed-files/action.yml @@ -15,7 +15,7 @@ inputs: runs: using: "composite" steps: - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v6 with: node-version-file: .nvmrc @@ -27,7 +27,7 @@ runs: package-name: ${{ inputs.package-name }} - name: Get changed files - uses: tj-actions/changed-files@v39.0.0 + uses: tj-actions/changed-files@v47 id: changed-files with: files_yaml: | @@ -35,7 +35,7 @@ runs: - 'src/**/*.ts' - '!src/**/*.spec.ts' - '!src/**/__mocks__/**' - separator: "','" + separator: ',' path: ${{ inputs.working-directory }} - name: Run mutation tests @@ -44,58 +44,30 @@ runs: STRYKER_DASHBOARD_API_KEY: ${{ inputs.stryker_dashboard_api_key }} shell: bash id: run-mutation-tests - run: yarn workspace ${{ inputs.package-name }} test:mutations --mutate ${{format('''{0}''', steps.changed-files.outputs.src_all_changed_files)}} --dashboard.module ${{ inputs.package-name }} + run: yarn workspace ${{ inputs.package-name }} test:mutations --mutate "${{ steps.changed-files.outputs.src_all_changed_files }}" --dashboard.module ${{ inputs.package-name }} continue-on-error: true - - name: Find current PR's number - uses: jwalton/gh-find-current-pr@v1 - id: findPr - - - name: Comment on successful mutation testing - uses: thollander/actions-comment-pull-request@v2 - if: steps.changed-files.outputs.src_any_changed == 'true' && steps.run-mutation-tests.outcome == 'success' && ${{ steps.findPr.outputs.number != '' }} - with: - message: | - ✅ Mutation testing passed for `${{ inputs.working-directory }}` - - Report: https://dashboard.stryker-mutator.io/reports/github.com/editor-js/document-model/PR-${{ steps.findPr.outputs.number }}?module=${{ inputs.package-name }} - -
- Mutated files -
-            ${{ join(fromJson(format('[{0}]', format('''{0}''', steps.changed-files.outputs.src_all_changed_files))), '
') }} -
-
- comment_tag: mutation-tests for `${{ inputs.working-directory }}` - pr_number: ${{ steps.findPr.outputs.number != '' && steps.findPr.outputs.number || '1'}} - - - name: Comment on failed mutation testing - uses: thollander/actions-comment-pull-request@v2 - if: steps.changed-files.outputs.src_any_changed == 'true' && steps.run-mutation-tests.outcome == 'failure' && ${{ steps.findPr.outputs.number != '' }} - with: - message: | - ❌ Mutation testing hasn't passed score threshold for `${{ inputs.working-directory }}` - - Report: https://dashboard.stryker-mutator.io/reports/github.com/editor-js/document-model/PR-${{ steps.findPr.outputs.number }}?module=${{ inputs.package-name }} + - name: Save mutated files list + if: steps.changed-files.outputs.src_any_changed == 'true' + shell: bash + run: | + echo "${{ steps.changed-files.outputs.src_all_changed_files }}" | sed 's/,/","/g' | sed 's/^/["/;s/$/"]/' > changed-files.json -
- Mutated files -
-            ${{ join(fromJson(format('[{0}]', format('''{0}''', steps.changed-files.outputs.src_all_changed_files))), '
') }} -
-
- comment_tag: mutation-tests for `${{ inputs.working-directory }}` - pr_number: ${{ steps.findPr.outputs.number != '' && steps.findPr.outputs.number || '1'}} + - name: Save empty list if no files changed + if: steps.changed-files.outputs.src_any_changed == 'false' + shell: bash + run: echo "[]" > changed-files.json - - name: Comment on empty changes - uses: thollander/actions-comment-pull-request@v2 - if: steps.changed-files.outputs.src_any_changed == 'false' && ${{ steps.findPr.outputs.number != '' }} + - name: Upload mutation report artifact + uses: actions/upload-artifact@v7 with: - message: | - ⏭️ No files to mutate for `${{ inputs.working-directory }}` + name: mutation-report + path: | + ${{ inputs.working-directory }}/reports/mutation/mutation.json + changed-files.json + retention-days: 3 - comment_tag: mutation-tests for `${{ inputs.working-directory }}` - pr_number: ${{ steps.findPr.outputs.number != '' && steps.findPr.outputs.number || '1'}} + # reporting will be triggered by workflow_run on workflow completion - if: steps.changed-files.outputs.src_any_changed == 'true' && steps.run-mutation-tests.outcome == 'failure' shell: bash diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 076996d2..b4fc78a0 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -8,7 +8,7 @@ runs: shell: bash - name: Restore yarn cache folder - uses: actions/cache@v3 + uses: actions/cache@v5 id: yarn-cache with: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} diff --git a/.github/actions/unit-tests/action.yml b/.github/actions/unit-tests/action.yml index c6760569..d28bc017 100644 --- a/.github/actions/unit-tests/action.yml +++ b/.github/actions/unit-tests/action.yml @@ -10,7 +10,7 @@ inputs: runs: using: "composite" steps: - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v6 with: node-version-file: .nvmrc - uses: ./.github/actions/setup @@ -19,15 +19,26 @@ runs: with: package-name: ${{ inputs.package-name }} - # Find current PR's number - - uses: jwalton/gh-find-current-pr@v1 - id: findPr + - name: Run unit tests with coverage + if: ${{ github.ref != 'refs/heads/main' }} # Skip coverage on main branch to save time and resources + shell: bash + run: | + yarn workspace ${{ inputs.package-name }} test:coverage - - name: Run unit tests - uses: ArtiomTr/jest-coverage-report-action@v2 + - name: Upload coverage artifact (PRs only) + if: ${{ github.event_name == 'pull_request' }} + uses: actions/upload-artifact@v7 with: - custom-title: Coverage report for `${{ inputs.working-directory }}` + name: test-report + path: | + ${{ inputs.working-directory }}/coverage + ${{ inputs.working-directory }}/jest-report.json + retention-days: 3 + + - name: Run base coverage action + if: ${{ github.event_name == 'pull_request' }} + uses: ./.github/actions/base-coverage + with: + package-name: ${{ inputs.package-name }} working-directory: ${{ inputs.working-directory }} - test-script: yarn workspace ${{ inputs.package-name }} test - package-manager: yarn - prnumber: ${{ steps.findPr.outputs.number }} + diff --git a/.github/scripts/compare-coverage.js b/.github/scripts/compare-coverage.js new file mode 100644 index 00000000..31e861a6 --- /dev/null +++ b/.github/scripts/compare-coverage.js @@ -0,0 +1,71 @@ +import fs from 'fs'; +import path from 'path'; + +function findCoverageJson(dir) { + if (!dir) return null; + const report = path.join(dir, 'coverage', 'coverage-summary.json'); + + + if (fs.existsSync(report)) { + try { + return JSON.parse(fs.readFileSync(report, 'utf8')); + } catch (report) { + console.error(report); + } + } + + return null; +} + + +function pctFromCoverageSummary(obj, category) { + if (!obj) return null; + + if (obj.total && obj.total[category] && typeof obj.total[category].pct === 'number') return obj.total[category].pct; + + if (obj[category] && typeof obj[category].pct === 'number') return obj[category].pct; + + return null; +} + +function compute(headSummary, baseSummary) { + const categories = ['statements', 'branches', 'functions', 'lines']; + + const out = {}; + + for (const cat of categories) { + const headPct = pctFromCoverageSummary(headSummary, cat); + const basePct = pctFromCoverageSummary(baseSummary, cat); + + let delta = 'N/A'; + + if (headPct != null && basePct != null) { + delta = Number((headPct - basePct).toFixed(2)); + } + + out[cat] = { head: headPct, base: basePct, delta }; + } + return out; +} + +export function processReports(pkg, headDir, baseDir) { + const headCov = findCoverageJson(headDir); + const baseCov = findCoverageJson(baseDir); + + const categories = compute(headCov, baseCov); + + let delta = categories.branches.delta; + + if (delta < 0) { + delta = `+${delta}% 🟢`; + } else if (delta > 0) { + delta = `-${delta}% 🔴`; + } else if (delta === 0) { + delta = `0% ⚪️`; + } + + // | Package | Branches coverage | Delta | + return `| ${pkg} | ${categories.branches.head}% | ${delta} |`; +} + + diff --git a/.github/scripts/process-mutation.js b/.github/scripts/process-mutation.js new file mode 100644 index 00000000..4c043125 --- /dev/null +++ b/.github/scripts/process-mutation.js @@ -0,0 +1,81 @@ +import fs from 'fs'; +import path from 'path'; + +function getMetrics(obj) { + const mutants = []; + + if (obj.files) { + for (const fileEntry of Object.values(obj.files)) { + if (fileEntry && Array.isArray(fileEntry.mutants)) { + mutants.push(...fileEntry.mutants); + } + } + } + + const total = mutants.length; + const survived = mutants.filter(m => m.status === 'Survived').length; + const notCovered = mutants.filter(m => m.status === 'NoCoverage').length; + const killed = mutants.filter(m => m.status === 'Killed').length; + const timedOut = mutants.filter(m => m.status === 'TimedOut').length; + + const score = (killed + timedOut) / (killed + timedOut + notCovered + survived).toFixed(2); + + return { + total, + score, + survived, + notCovered, + thresholds: obj.thresholds, + }; +} + + +function processMutationReport(artitfactName, reportPath, changedFilesPath, prNumber, packageName) { + const reportFile = path.join(artitfactName, reportPath); + const changedFilesFile = path.join(artitfactName, changedFilesPath); + + if (!fs.existsSync(changedFilesFile)) { + return ''; + } + const changedFiles = JSON.parse(fs.readFileSync(changedFilesFile, 'utf8')); + + if (changedFiles.length === 0) { + return `| ${packageName} | No files to mutate found. | |`; + } + + if (!fs.existsSync(reportFile)) { + return `| ${packageName} | Report artifact not found. | |`; + } + + const raw = fs.readFileSync(reportFile, 'utf8'); + const obj = JSON.parse(raw); + + + const metrics = getMetrics(obj); + + const encodedPackageName = encodeURIComponent(packageName); + const dashboardUrl = `[Dashboard](https://dashboard.stryker-mutator.io/reports/github.com/editor-js/document-model/PR-${prNumber}?module=${encodedPackageName})`; + + const score = (metrics.score * 100).toFixed(2); + let scoreMessage = `${score}%`; + + switch (true) { + case (score < metrics.thresholds.break): + scoreMessage += ` `; + break; + case (score < metrics.thresholds.low): + scoreMessage += ` 🔴`; + break; + case (score < metrics.thresholds.high): + scoreMessage += ` 🟡`; + break; + case (score > metrics.thresholds.high): + scoreMessage += ` 🟢`; + break; + } + + // | Package | Mutation score | Dashboard URL | + return `| ${packageName} | ${scoreMessage} | ${dashboardUrl} |`; +} + +export { processMutationReport }; diff --git a/.github/scripts/should-run-ci.sh b/.github/scripts/should-run-ci.sh new file mode 100644 index 00000000..d39a79c9 --- /dev/null +++ b/.github/scripts/should-run-ci.sh @@ -0,0 +1,136 @@ +#!/bin/bash + +# Detect if a package needs CI to run +# Checks if the package or any of its dependencies (direct or transitive) have changed +# Usage: bash .github/scripts/should-run-ci.sh [--verbose] + +# Don't exit on error - we'll handle errors explicitly +set +e + +BASE_REF="${1}" +PKG_DIR="${2}" +VERBOSE="${3}" + +if [ -z "$BASE_REF" ] || [ -z "$PKG_DIR" ]; then + echo "Usage: bash .github/scripts/should-run-ci.sh [--verbose]" + exit 1 +fi + +# Function for verbose logging +debug() { + if [ "$VERBOSE" = "--verbose" ] || [ "$VERBOSE" = "-v" ]; then + echo "[DEBUG] $@" >&2 + fi +} + +# Try to resolve the base ref - try multiple variants +RESOLVED_BASE_REF="" + +# Try 1: As-is (for local branches like "main") +if git rev-parse --verify "$BASE_REF" >/dev/null 2>&1; then + RESOLVED_BASE_REF="$BASE_REF" + debug "Resolved base ref as: $BASE_REF" +fi + +# Try 2: With origin/ prefix (for remote-tracking branches) +if [ -z "$RESOLVED_BASE_REF" ] && git rev-parse --verify "origin/$BASE_REF" >/dev/null 2>&1; then + RESOLVED_BASE_REF="origin/$BASE_REF" + debug "Resolved base ref as: origin/$BASE_REF" +fi + +# Try 3: Hardcoded main/master (for GitHub Actions or when base_ref not provided properly) +if [ -z "$RESOLVED_BASE_REF" ]; then + if git rev-parse --verify "origin/main" >/dev/null 2>&1; then + RESOLVED_BASE_REF="origin/main" + debug "Resolved base ref as: origin/main" + elif git rev-parse --verify "main" >/dev/null 2>&1; then + RESOLVED_BASE_REF="main" + debug "Resolved base ref as: main" + fi +fi + +# If still couldn't resolve, assume all changes are relevant (safe default for CI) +if [ -z "$RESOLVED_BASE_REF" ]; then + debug "Warning: Could not resolve base ref '$BASE_REF'" + debug "Assuming all changes are relevant (safe for CI)" + exit 0 +fi + +# Helper function to get all dependencies (direct and transitive) +get_all_deps() { + local pkg=$1 + local visited=$2 + local depth=${3:-0} + + # Limit recursion depth to prevent infinite loops + if [ "$depth" -gt 10 ]; then + return + fi + + # Avoid infinite loops + if echo "$visited" | grep -q "^$pkg$"; then + return + fi + + visited="$visited $pkg" + + # Get direct dependencies (if package.json exists) + if [ ! -f "packages/$pkg/package.json" ]; then + return + fi + + local deps=$(grep -o '"@editorjs/[^"]*"' "packages/$pkg/package.json" 2>/dev/null | sed 's/"//g' | sed 's/@editorjs\///' || true) + + echo "$deps" + + # Recursively get dependencies of dependencies + for dep in $deps; do + get_all_deps "$dep" "$visited" $((depth + 1)) + done +} + +# Check if this package changed +# Normalize the package directory path for comparison (remove leading ./) +NORMALIZED_PKG_DIR=$(echo "$PKG_DIR" | sed 's|^\.\/||') + +# Get the package name for dependency checking +PKG_NAME=$(basename "$PKG_DIR") + +debug "Checking package: $PKG_NAME (dir: $PKG_DIR)" +debug "Base ref: $RESOLVED_BASE_REF" + +GIT_DIFF_OUTPUT=$(git diff --name-only "$RESOLVED_BASE_REF...HEAD" -- "$PKG_DIR" 2>/dev/null) + +if echo "$GIT_DIFF_OUTPUT" | grep -q "^$NORMALIZED_PKG_DIR/"; then + debug "✓ Package $PKG_NAME has changed" + exit 0 +fi + +debug "Package $PKG_NAME has not changed, checking dependencies..." + +# Get ALL dependencies (direct and transitive) +ALL_DEPS=$(get_all_deps "$PKG_NAME" "" 0 | sort -u) + +if [ -n "$ALL_DEPS" ]; then + debug "Dependencies: $(echo $ALL_DEPS | tr '\n' ' ')" +else + debug "No dependencies found" +fi + +# Check if any dependency (direct or indirect) changed +for dep in $ALL_DEPS; do + # Map package name to directory - try exact match and underscore variant + DEP_DIR=$(find packages -maxdepth 1 -type d \( -name "$dep" -o -name "$(echo $dep | sed 's/-/_/g')" \) 2>/dev/null | head -1) + if [ -n "$DEP_DIR" ]; then + # Normalize for git diff output (remove leading ./) + NORMALIZED_DEP_DIR=$(echo "$DEP_DIR" | sed 's|^\.\/||') + if git diff --name-only "$RESOLVED_BASE_REF...HEAD" -- "$DEP_DIR" 2>/dev/null | grep -q "^$NORMALIZED_DEP_DIR/"; then + debug "✓ Dependency $dep (in $DEP_DIR) has changed" + exit 0 + fi + fi +done + +debug "✗ No changes detected in $PKG_NAME or its dependencies" +exit 1 + diff --git a/.github/scripts/update-aggregated-comment.js b/.github/scripts/update-aggregated-comment.js new file mode 100644 index 00000000..bb6199d8 --- /dev/null +++ b/.github/scripts/update-aggregated-comment.js @@ -0,0 +1,120 @@ +async function updateAggregatedComment(unitTestRow, mutationRow, packageName, pr, core, github, context) { + // repo info (owner/repo) available from context + const owner = context.repo.owner; + const repo = context.repo.repo; + + if (!pr) { + core.info('No pull request found in context or associated with the commit; skipping comment step.'); + return; + } + + // list comments on the PR (first page) + const res = await github.rest.issues.listComments({ owner, repo, issue_number: Number(pr), per_page: 100 }); + const comments = res && res.data ? res.data : []; + + const globalStart = ``; + const globalEnd = ``; + + // Table section markers + const unitTestsStart = ``; + const unitTestsEnd = ``; + const mutationTestsStart = ``; + const mutationTestsEnd = ``; + + // Table headers and separators + const unitTestsHeader = `| Package | Coverage | Delta |`; + const mutationTestsHeader = `| Package | Mutation score | Dashboard URL |`; + const separator = `| --- | --- | --- |`; + + // find existing global comment containing the aggregated markers + let existing = comments.find(c => c.body && c.body.includes(globalStart) && c.body.includes(globalEnd)); + + let commentBody; + + if (!existing) { + // create a new global comment with both tables + const unitTestsTable = `${unitTestsStart}\n${unitTestsHeader}\n${separator}\n${unitTestRow}\n${unitTestsEnd}`; + const mutationTestsTable = `${mutationTestsStart}\n${mutationTestsHeader}\n${separator}\n${mutationRow}\n${mutationTestsEnd}`; + + commentBody = `${globalStart}\n## Unit Tests\n${unitTestsTable}\n\n## Mutation Tests\n${mutationTestsTable}\n${globalEnd}`; + await github.rest.issues.createComment({ owner, repo, issue_number: Number(pr), body: commentBody }); + core.info('Created new aggregated PR comment with test tables.'); + return; + } + + // Update existing comment + commentBody = existing.body || ''; + + // Helper function to update or create a table section + function updateTableSection(body, sectionStart, sectionEnd, header, separator, newRow, pkgName) { + const sectionStartIdx = body.indexOf(sectionStart); + + if (sectionStartIdx === -1) { + // Table section doesn't exist, create it with header, separator, and new row + const tableContent = `${sectionStart}\n${header}\n${separator}\n${newRow}\n${sectionEnd}`; + + // Insert before the global end marker + const globalEndIdx = body.indexOf(globalEnd); + if (globalEndIdx === -1) { + return body + '\n\n' + tableContent; + } + return body.substring(0, globalEndIdx) + '\n' + tableContent + '\n' + body.substring(globalEndIdx); + } + + // Table section exists, find and update or append row + const sectionEndIdx = body.indexOf(sectionEnd, sectionStartIdx); + const beforeSection = body.substring(0, sectionStartIdx + sectionStart.length); + const afterSection = body.substring(sectionEndIdx); + const sectionContent = body.substring(sectionStartIdx + sectionStart.length, sectionEndIdx); + + // Split into lines, trim, and filter out empty lines + let lines = sectionContent.split('\n').map(l => l.trim()).filter(l => l.length > 0); + let packageRowLineIdx = -1; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + // Data rows: contain pipes, contain package name, and don't contain dashes (separator indicator) + if (line.includes('|') && line.includes(pkgName) && !line.includes('---')) { + packageRowLineIdx = i; + break; + } + } + + if (packageRowLineIdx !== -1) { + // Replace existing package row + lines[packageRowLineIdx] = newRow; + } else { + // Append new package row + lines.push(newRow); + } + + return beforeSection + '\n' + lines.join('\n') + '\n' + afterSection; + } + + // Update unit tests table + commentBody = updateTableSection( + commentBody, + unitTestsStart, + unitTestsEnd, + unitTestsHeader, + separator, + unitTestRow, + packageName + ); + + // Update mutation tests table + commentBody = updateTableSection( + commentBody, + mutationTestsStart, + mutationTestsEnd, + mutationTestsHeader, + separator, + mutationRow, + packageName + ); + + await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body: commentBody }); + core.info('Updated aggregated PR comment with test tables.'); +} + +export { updateAggregatedComment }; diff --git a/.github/workflows/build-and-push-docker-image.yml b/.github/workflows/build-and-push-docker-image.yml index 7ddf2fb4..ecdd4c3b 100644 --- a/.github/workflows/build-and-push-docker-image.yml +++ b/.github/workflows/build-and-push-docker-image.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Login to GitHub registry uses: docker/login-action@v3 diff --git a/.github/workflows/collaboration-manager.yml b/.github/workflows/collaboration-manager.yml index 82af2c4a..28409812 100644 --- a/.github/workflows/collaboration-manager.yml +++ b/.github/workflows/collaboration-manager.yml @@ -4,45 +4,12 @@ on: merge_group: jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 + package-check: + uses: ./.github/workflows/package-check.yml + with: + package-name: '@editorjs/collaboration-manager' + working-directory: './packages/collaboration-manager' + include-mutations: false + secrets: + stryker_dashboard_api_key: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - - run: yarn - - - name: Build the package - uses: ./.github/actions/build - with: - package-name: '@editorjs/collaboration-manager' - - - name: Run ESLint check - uses: ./.github/actions/lint - with: - package-name: '@editorjs/collaboration-manager' - - tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - run: yarn - - - name: Build the package - uses: ./.github/actions/build - with: - package-name: '@editorjs/collaboration-manager' - - - name: Run unit tests - uses: ./.github/actions/unit-tests - with: - package-name: '@editorjs/collaboration-manager' - working-directory: './packages/collaboration-manager' - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Build the package - uses: ./.github/actions/build - with: - package-name: '@editorjs/collaboration-manager' diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 6aaaf7fd..0904e81a 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -3,52 +3,12 @@ on: pull_request: jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run ESLint check - uses: ./.github/actions/lint - with: - package-name: '@editorjs/core' + package-check: + uses: ./.github/workflows/package-check.yml + with: + package-name: '@editorjs/core' + working-directory: './packages/core' + include-mutations: true + secrets: + stryker_dashboard_api_key: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Run unit tests - uses: ./.github/actions/unit-tests - with: - package-name: '@editorjs/core' - working-directory: './packages/core' - - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Build the package - uses: ./.github/actions/build - with: - package-name: '@editorjs/core' - - mutation-tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Run mutation tests for changed files - if: ${{ github.event_name == 'pull_request' }} - uses: ./.github/actions/mutation-tests-changed-files - with: - package-name: '@editorjs/core' - working-directory: './packages/core' - stryker_dashboard_api_key: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - - - name: Run mutation tests for all files - if: ${{ github.event_name == 'merge_group' }} - uses: ./.github/actions/mutation-tests-all-files - with: - package-name: '@editorjs/core' - working-directory: './packages/core' - stryker_dashboard_api_key: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} diff --git a/.github/workflows/dom-adapters.yml b/.github/workflows/dom-adapters.yml index eba7f2ff..eb2c8dc7 100644 --- a/.github/workflows/dom-adapters.yml +++ b/.github/workflows/dom-adapters.yml @@ -3,56 +3,12 @@ on: pull_request: jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run ESLint check - uses: ./.github/actions/lint - with: - package-name: '@editorjs/dom-adapters' + package-check: + uses: ./.github/workflows/package-check.yml + with: + package-name: '@editorjs/dom-adapters' + working-directory: './packages/dom-adapters' + include-mutations: true + secrets: + stryker_dashboard_api_key: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run unit tests - uses: ./.github/actions/unit-tests - with: - working-directory: './packages/dom-adapters' - package-name: '@editorjs/dom-adapters' - - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Build the package - uses: ./.github/actions/build - with: - package-name: '@editorjs/dom-adapters' - - mutation-tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Build dependencies - uses: ./.github/actions/build - with: - package-name: '@editorjs/dom-adapters' - - - name: Run mutation tests for changed files - if: ${{ github.event_name == 'pull_request' }} - uses: ./.github/actions/mutation-tests-changed-files - with: - package-name: '@editorjs/dom-adapters' - working-directory: './packages/dom-adapters' - stryker_dashboard_api_key: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - - - name: Run mutation tests for all files - if: ${{ github.event_name == 'merge_group' }} - uses: ./.github/actions/mutation-tests-all-files - with: - package-name: '@editorjs/dom-adapters' - working-directory: './packages/dom-adapters' - stryker_dashboard_api_key: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} diff --git a/.github/workflows/model.yml b/.github/workflows/model.yml index 1e29c373..685744f2 100644 --- a/.github/workflows/model.yml +++ b/.github/workflows/model.yml @@ -4,51 +4,12 @@ on: merge_group: jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run ESLint check - uses: ./.github/actions/lint - with: - package-name: '@editorjs/model' + package-check: + uses: ./.github/workflows/package-check.yml + with: + package-name: '@editorjs/model' + working-directory: './packages/model' + include-mutations: true + secrets: + stryker_dashboard_api_key: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Run unit tests - uses: ./.github/actions/unit-tests - with: - package-name: '@editorjs/model' - working-directory: './packages/model' - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Build the package - uses: ./.github/actions/build - with: - package-name: '@editorjs/model' - - mutation-tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Run mutation tests for changed files - if: ${{ github.event_name == 'pull_request' }} - uses: ./.github/actions/mutation-tests-changed-files - with: - package-name: '@editorjs/model' - working-directory: './packages/model' - stryker_dashboard_api_key: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - - - name: Run mutation tests for all files - if: ${{ github.event_name == 'merge_group' }} - uses: ./.github/actions/mutation-tests-all-files - with: - package-name: '@editorjs/model' - working-directory: './packages/model' - stryker_dashboard_api_key: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 979b6e9d..2b1d47a2 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -10,9 +10,9 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' registry-url: https://registry.npmjs.org/ diff --git a/.github/workflows/ot-server.yml b/.github/workflows/ot-server.yml index 36d1d63a..2b102881 100644 --- a/.github/workflows/ot-server.yml +++ b/.github/workflows/ot-server.yml @@ -4,45 +4,12 @@ on: merge_group: jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 + package-check: + uses: ./.github/workflows/package-check.yml + with: + package-name: '@editorjs/ot-server' + working-directory: './packages/ot-server' + include-mutations: false + secrets: + stryker_dashboard_api_key: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} - - run: yarn - - - name: Build the package - uses: ./.github/actions/build - with: - package-name: '@editorjs/ot-server' - - - name: Run ESLint check - uses: ./.github/actions/lint - with: - package-name: '@editorjs/ot-server' - - tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - run: yarn - - - name: Build the package - uses: ./.github/actions/build - with: - package-name: '@editorjs/ot-server' - - - name: Run unit tests - uses: ./.github/actions/unit-tests - with: - package-name: '@editorjs/ot-server' - working-directory: './packages/ot-server' - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Build the package - uses: ./.github/actions/build - with: - package-name: '@editorjs/ot-server' diff --git a/.github/workflows/package-check.yml b/.github/workflows/package-check.yml new file mode 100644 index 00000000..0c5be432 --- /dev/null +++ b/.github/workflows/package-check.yml @@ -0,0 +1,128 @@ +name: Package check +on: + workflow_call: + inputs: + package-name: + description: 'Full package name (e.g., @editorjs/core)' + required: true + type: string + working-directory: + description: 'Package working directory (e.g., ./packages/core)' + required: true + type: string + include-mutations: + description: 'Whether to include mutation tests' + required: false + type: boolean + default: false + secrets: + stryker_dashboard_api_key: + description: 'Stryker dashboard API key for mutation tests' + required: false + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + should-run: + name: Check if package needs CI + runs-on: ubuntu-latest + outputs: + run: ${{ steps.check.outputs.run }} + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Determine if CI should run + id: check + run: | + # Always run for merge_group + if [ "${{ github.event_name }}" != "pull_request" ]; then + echo "run=true" >> $GITHUB_OUTPUT + exit 0 + fi + + # Check if package or dependencies changed (returns 0 if yes, 1 if no) + if bash .github/scripts/should-run-ci.sh "${{ github.base_ref }}" "${{ inputs.working-directory }}"; then + echo "run=true" >> $GITHUB_OUTPUT + else + echo "run=false" >> $GITHUB_OUTPUT + fi + + lint: + needs: should-run + if: ${{ needs.should-run.outputs.run == 'true' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Run ESLint check + uses: ./.github/actions/lint + with: + package-name: ${{ inputs.package-name }} + + tests: + needs: should-run + if: ${{ needs.should-run.outputs.run == 'true' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Run unit tests + uses: ./.github/actions/unit-tests + with: + package-name: ${{ inputs.package-name }} + working-directory: ${{ inputs.working-directory }} + + build: + needs: should-run + if: ${{ needs.should-run.outputs.run == 'true' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Build the package + uses: ./.github/actions/build + with: + package-name: ${{ inputs.package-name }} + + mutation-tests: + needs: should-run + if: ${{ inputs.include-mutations && needs.should-run.outputs.run == 'true' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Run mutation tests for changed files + if: ${{ github.event_name == 'pull_request' }} + uses: ./.github/actions/mutation-tests-changed-files + with: + package-name: ${{ inputs.package-name }} + working-directory: ${{ inputs.working-directory }} + stryker_dashboard_api_key: ${{ secrets.stryker_dashboard_api_key }} + + - name: Run mutation tests for all files + if: ${{ github.event_name == 'merge_group' }} + uses: ./.github/actions/mutation-tests-all-files + with: + package-name: ${{ inputs.package-name }} + working-directory: ${{ inputs.working-directory }} + stryker_dashboard_api_key: ${{ secrets.stryker_dashboard_api_key }} + + report: + name: Aggregate and post report + needs: [tests, mutation-tests] + if: ${{ always() && github.event_name == 'pull_request' && (needs.tests.result != 'skipped' || needs.mutation-tests.result != 'skipped') }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Generate aggregated report and comment + uses: ./.github/actions/aggregate-report + with: + package-name: ${{ inputs.package-name }} + working-directory: ${{ inputs.working-directory }} + + + diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 4811f740..d652ab1f 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -3,11 +3,15 @@ on: pull_request: merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Run ESLint check uses: ./.github/actions/lint with: @@ -16,7 +20,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Build the package uses: ./.github/actions/build with: diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index 9985de18..0bca6549 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -2,11 +2,15 @@ name: SDK check on: pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Run ESLint check uses: ./.github/actions/lint with: @@ -15,8 +19,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Build the package uses: ./.github/actions/build with: - package-name: '@editorjs/sdk' \ No newline at end of file + package-name: '@editorjs/sdk' + diff --git a/.gitignore b/.gitignore index f91aca7a..ccbe8ec4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ dist/* # tests coverage/ reports/ +jest-report.json # stryker temp files .stryker-tmp diff --git a/packages/collaboration-manager/eslint.config.mjs b/packages/collaboration-manager/eslint.config.mjs index 13f36895..0ea434ea 100644 --- a/packages/collaboration-manager/eslint.config.mjs +++ b/packages/collaboration-manager/eslint.config.mjs @@ -21,6 +21,12 @@ export default [ ], ignoreTypeImport: true, }], + 'n/no-missing-import': ['error', { + allowModules: [ + '@editorjs/model', + '@editorjs/sdk', + ], + }], // @todo: remove when we setup eslint to correctly handle the types '@typescript-eslint/no-unsafe-call': 'off', '@typescript-eslint/no-unsafe-member-access': 'off', diff --git a/packages/collaboration-manager/jest.config.ts b/packages/collaboration-manager/jest.config.ts index 940d6f69..98e86574 100644 --- a/packages/collaboration-manager/jest.config.ts +++ b/packages/collaboration-manager/jest.config.ts @@ -8,6 +8,7 @@ export default { tsconfig: '/tsconfig.test.json', }, }, + coverageReporters: ['lcov', 'json-summary', 'text-summary'], testMatch: ['/src/**/*.spec.ts'], modulePathIgnorePatterns: ['/.*/__mocks__', '/.*/mocks'], extensionsToTreatAsEsm: ['.ts'], diff --git a/packages/collaboration-manager/src/UndoRedoManager.spec.ts b/packages/collaboration-manager/src/UndoRedoManager.spec.ts index a0ef5f0f..aaa4f341 100644 --- a/packages/collaboration-manager/src/UndoRedoManager.spec.ts +++ b/packages/collaboration-manager/src/UndoRedoManager.spec.ts @@ -222,9 +222,7 @@ describe('UndoRedoManager', () => { .addDocumentId(doc) .addBlockIndex(0) .addDataKey(createDataKey('text')) - /* eslint-disable @typescript-eslint/no-magic-numbers */ .addTextRange([0, 0]) - /* eslint-enable @typescript-eslint/no-magic-numbers */ .build(), { payload: 'xx' }, 'remote' diff --git a/packages/collaboration-manager/src/client/OTClient.spec.ts b/packages/collaboration-manager/src/client/OTClient.spec.ts index 0bce6234..18c9a6c4 100644 --- a/packages/collaboration-manager/src/client/OTClient.spec.ts +++ b/packages/collaboration-manager/src/client/OTClient.spec.ts @@ -24,13 +24,13 @@ describe('OTClient', () => { let OriginalWebSocket: typeof WebSocket; beforeEach(() => { - OriginalWebSocket = globalThis.WebSocket; // eslint-disable-line no-undef - globalThis.WebSocket = MockWebSocket as unknown as typeof WebSocket; // eslint-disable-line no-undef + OriginalWebSocket = globalThis.WebSocket; + globalThis.WebSocket = MockWebSocket as unknown as typeof WebSocket; MockWebSocket.lastInstance = null; }); afterEach(() => { - globalThis.WebSocket = OriginalWebSocket; // eslint-disable-line no-undef + globalThis.WebSocket = OriginalWebSocket; MockWebSocket.lastInstance = null; }); diff --git a/packages/core/jest.config.ts b/packages/core/jest.config.ts index 098e06d9..f409e005 100644 --- a/packages/core/jest.config.ts +++ b/packages/core/jest.config.ts @@ -9,6 +9,7 @@ export default { moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1', }, + coverageReporters: ['lcov', 'json-summary', 'text-summary'], transform: { '^.+\\.tsx?$': [ 'ts-jest', diff --git a/packages/core/stryker.conf.mjs b/packages/core/stryker.conf.mjs index 83093f77..34e79b4a 100644 --- a/packages/core/stryker.conf.mjs +++ b/packages/core/stryker.conf.mjs @@ -11,6 +11,7 @@ const config = { allowEmojis: true, }, reporters: [ + 'json', 'html', 'clear-text', 'progress', diff --git a/packages/dom-adapters/jest.config.ts b/packages/dom-adapters/jest.config.ts index 77c0e2ac..999ea6af 100644 --- a/packages/dom-adapters/jest.config.ts +++ b/packages/dom-adapters/jest.config.ts @@ -9,6 +9,7 @@ export default { moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1', }, + coverageReporters: ['lcov', 'json-summary', 'text-summary'], transform: { // '^.+\\.[tj]sx?$' to process js/ts with `ts-jest` // '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest` diff --git a/packages/dom-adapters/stryker.conf.mjs b/packages/dom-adapters/stryker.conf.mjs index 53d6167b..e8addcef 100644 --- a/packages/dom-adapters/stryker.conf.mjs +++ b/packages/dom-adapters/stryker.conf.mjs @@ -2,29 +2,30 @@ /** @type {import('@stryker-mutator/api/core').PartialStrykerOptions} */ const config = { _comment: - "This config was generated using 'stryker init'. Please take a look at: https://stryker-mutator.io/docs/stryker-js/configuration/ for more information.", - packageManager: "yarn", + 'This config was generated using \'stryker init\'. Please take a look at: https://stryker-mutator.io/docs/stryker-js/configuration/ for more information.', + packageManager: 'yarn', thresholds: { break: 0, }, - thresholds_comment: "Minimum required coverage. Increase once we're closer to 100%.", + thresholds_comment: 'Minimum required coverage. Increase once we\'re closer to 100%.', clearTextReporter: { allowEmojis: true, }, reporters: [ - "html", - "clear-text", - "progress", - "dashboard", + 'json', + 'html', + 'clear-text', + 'progress', + 'dashboard', ], - testRunner: "jest", + testRunner: 'jest', testRunner_comment: - "Take a look at https://stryker-mutator.io/docs/stryker-js/jest-runner for information about the jest plugin.", - coverageAnalysis: "perTest", - tsconfigFile: "tsconfig.json", - checkers: ["typescript"], + 'Take a look at https://stryker-mutator.io/docs/stryker-js/jest-runner for information about the jest plugin.', + coverageAnalysis: 'perTest', + tsconfigFile: 'tsconfig.json', + checkers: ['typescript'], timeoutMS: 10000, - mutate: ["./src/**/*.ts", "!./src/**/__mocks__/*.ts", "!./src/**/*.spec.ts"], + mutate: ['./src/**/*.ts', '!./src/**/__mocks__/*.ts', '!./src/**/*.spec.ts'], /* * In some cases PRs might not have any unit-tests */ diff --git a/packages/model/jest.config.ts b/packages/model/jest.config.ts index caf62a9d..e8a9f307 100644 --- a/packages/model/jest.config.ts +++ b/packages/model/jest.config.ts @@ -9,6 +9,7 @@ export default { moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1', }, + coverageReporters: ['lcov', 'json-summary', 'text-summary'], transform: { // '^.+\\.[tj]sx?$' to process js/ts with `ts-jest` // '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest` diff --git a/packages/model/stryker.conf.mjs b/packages/model/stryker.conf.mjs index 63c04dfb..9b1f4ca1 100644 --- a/packages/model/stryker.conf.mjs +++ b/packages/model/stryker.conf.mjs @@ -12,6 +12,7 @@ const config = { allowEmojis: true, }, reporters: [ + "json", "html", "clear-text", "progress", diff --git a/packages/ot-server/eslint.config.mjs b/packages/ot-server/eslint.config.mjs index 66fdff6e..965e3471 100644 --- a/packages/ot-server/eslint.config.mjs +++ b/packages/ot-server/eslint.config.mjs @@ -21,6 +21,12 @@ export default [ ], ignoreTypeImport: true, }], + 'n/no-missing-import': ['error', { + allowModules: [ + '@editorjs/model', + '@editorjs/collaboration-manager', + ], + }], 'n/no-unsupported-features/node-builtins': ['error', { version: '>=24.0.0', ignores: [], diff --git a/packages/ot-server/jest.config.ts b/packages/ot-server/jest.config.ts index 0a49adc9..5bf9a48d 100644 --- a/packages/ot-server/jest.config.ts +++ b/packages/ot-server/jest.config.ts @@ -11,6 +11,7 @@ export default { '^(\\.{1,2}/.*)\\.js$': '$1', '^codex-tooltip$': '/test/mocks/codex-tooltip.ts', }, + coverageReporters: ['lcov', 'json-summary', 'text-summary'], transform: { ...createDefaultEsmPreset().transform, }, diff --git a/packages/sdk/jest.config.ts b/packages/sdk/jest.config.ts index 22791c2c..6269e54d 100644 --- a/packages/sdk/jest.config.ts +++ b/packages/sdk/jest.config.ts @@ -8,6 +8,7 @@ export default { '^(\\.{1,2}/.*)\\.js$': '$1', '^@/(.*)$': '/src/$1', }, + coverageReporters: ['lcov', 'json-summary', 'text-summary'], transform: { '^.+\\.tsx?$': [ 'ts-jest', diff --git a/yarn.lock b/yarn.lock index b1d6018d..9c6c0d57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2789,13 +2789,20 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.11.0, @eslint-community/regexpp@npm:^4.12.1, @eslint-community/regexpp@npm:^4.6.1": +"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.11.0, @eslint-community/regexpp@npm:^4.12.1": version: 4.12.1 resolution: "@eslint-community/regexpp@npm:4.12.1" checksum: c08f1dd7dd18fbb60bdd0d85820656d1374dd898af9be7f82cb00451313402a22d5e30569c150315b4385907cdbca78c22389b2a72ab78883b3173be317620cc languageName: node linkType: hard +"@eslint-community/regexpp@npm:^4.6.1": + version: 4.12.2 + resolution: "@eslint-community/regexpp@npm:4.12.2" + checksum: 049b280fddf71dd325514e0a520024969431dc3a8b02fa77476e6820e9122f28ab4c9168c11821f91a27982d2453bcd7a66193356ea84e84fb7c8d793be1ba0c + languageName: node + linkType: hard + "@eslint/compat@npm:^1.1.1": version: 1.2.8 resolution: "@eslint/compat@npm:1.2.8" @@ -4311,7 +4318,14 @@ __metadata: languageName: node linkType: hard -"@ungap/structured-clone@npm:^1.2.0, @ungap/structured-clone@npm:^1.3.0": +"@ungap/structured-clone@npm:^1.2.0": + version: 1.3.1 + resolution: "@ungap/structured-clone@npm:1.3.1" + checksum: 64df206f50aef71c176f9059c1b29e1694821419c6728c446ecf39c80a811eeef156668bf51421b676494a12fd0129ccf09a44f0c641f13c27f50d5f0db6de4e + languageName: node + linkType: hard + +"@ungap/structured-clone@npm:^1.3.0": version: 1.3.0 resolution: "@ungap/structured-clone@npm:1.3.0" checksum: 80d6910946f2b1552a2406650051c91bbd1f24a6bf854354203d84fe2714b3e8ce4618f49cc3410494173a1c1e8e9777372fe68dce74bd45faf0a7a1a6ccf448 @@ -6844,7 +6858,7 @@ __metadata: languageName: node linkType: hard -"esquery@npm:^1.4.0, esquery@npm:^1.4.2, esquery@npm:^1.5.0, esquery@npm:^1.6.0": +"esquery@npm:^1.4.0, esquery@npm:^1.5.0, esquery@npm:^1.6.0": version: 1.6.0 resolution: "esquery@npm:1.6.0" dependencies: @@ -6853,6 +6867,15 @@ __metadata: languageName: node linkType: hard +"esquery@npm:^1.4.2": + version: 1.7.0 + resolution: "esquery@npm:1.7.0" + dependencies: + estraverse: "npm:^5.1.0" + checksum: 4afaf3089367e1f5885caa116ef386dffd8bfd64da21fd3d0e56e938d2667cfb2e5400ab4a825aa70e799bb3741e5b5d63c0b94d86e2d4cf3095c9e64b2f5a15 + languageName: node + linkType: hard + "esrecurse@npm:^4.3.0": version: 4.3.0 resolution: "esrecurse@npm:4.3.0" @@ -9134,7 +9157,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -9143,6 +9166,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^3.0.5": + version: 3.1.5 + resolution: "minimatch@npm:3.1.5" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: b11a7ee5773cd34c1a0c8436cdbe910901018fb4b6cb47aa508a18d567f6efd2148507959e35fba798389b161b8604a2d704ccef751ea36bd4582f9852b7d63f + languageName: node + linkType: hard + "minimatch@npm:^5.0.1": version: 5.1.6 resolution: "minimatch@npm:5.1.6"