Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/actions/configure-nodejs/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: 'Configure Node.js'
description: 'Install Node.js and install Node.js modules or restore cache'

inputs:
node-version:
description: 'NodeJS Version'
default: '18'
lookup-only:
description: 'If true, only checks if cache entry exists and skips download. Does not change save cache behavior'
default: 'false'

runs:
using: 'composite'
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}

- name: Restore Node Modules from Cache
id: cache-node-modules
uses: actions/cache@v4
with:
path: |
node_modules
packages/**/node_modules
!node_modules/.cache
key: node-modules-${{ inputs.node-version }}-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('package.json', 'package-lock.json', '**/package-lock.json') }}
lookup-only: ${{ inputs.lookup-only }}

- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
shell: bash
run: npm ci
48 changes: 48 additions & 0 deletions .github/actions/coverage-report/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: 'Parse Coverage and Post Comment'
description: 'Parses a coverage report and posts a comment on a PR'
inputs:
lcov-file:
description: 'Path to the lcov.info file'
required: true
title:
description: 'Title of the comment'
default: 'Code Coverage Report'

runs:
using: 'composite'
steps:
- name: Parse Coverage
shell: bash
if: github.event_name == 'pull_request'
id: parse
run: |
./.github/actions/coverage-report/scripts/parse-coverage.js ${{ inputs.lcov-file }} > coverage-summary.txt
echo "coverage-summary<<EOF" >> $GITHUB_OUTPUT
cat coverage-summary.txt >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Find Coverage Comment
if: github.event_name == 'pull_request'
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: '### 📊 ${{ inputs.title }}'

- name: Post Coverage Comment
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
edit-mode: replace
issue-number: ${{ github.event.pull_request.number }}
body: |
### 📊 ${{ inputs.title }}
${{ steps.parse.outputs.coverage-summary }}

- name: Upload Coverage Report
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
126 changes: 126 additions & 0 deletions .github/actions/coverage-report/scripts/parse-coverage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#!/usr/bin/env node
const lcovParse = require("lcov-parse");
const fs = require("fs");

const lcovPath = process.argv[2];
const needsImprovementBelow = parseFloat(
process.argv.length >= 4 ? process.argv[3] : "90"
);
const poorBelow = parseFloat(process.argv[4] >= 5 ? process.argv[4] : "50");

if (!lcovPath || isNaN(needsImprovementBelow) || isNaN(poorBelow)) {
console.error(
"Please provide the path to the lcov.info file and the 'needs-improvement-below' and 'poor-below' percentages as command-line arguments."
);
process.exit(1);
}

if (!fs.existsSync(lcovPath)) {
console.error(
`The file ${lcovPath} does not exist. Please provide the path to the lcov.info file.`
);
process.exit(1);
}

const outputFormat = "markdown";

if (outputFormat === "markdown") {
console.log(
"| File | Lines | Lines Hit / Found | Uncovered Lines | Branches |"
);
console.log("| --- | --- | --- | --- | --- |");
}

function shortenPath(path, maxLength) {
if (path.length <= maxLength) {
return path;
}

const start = path.substring(0, maxLength / 2 - 2); // -2 for the '..' in the middle
const end = path.substring(path.length - maxLength / 2, path.length);

return `${start}..${end}`;
}

function getEmoji(lineCoverage, needsImprovementBelow, poorBelow) {
if (lineCoverage >= needsImprovementBelow) {
return "✅"; // white-check emoji
} else if (
lineCoverage < needsImprovementBelow &&
lineCoverage >= poorBelow
) {
return "🟡"; // yellow-ball emoji
} else {
return "❌"; // red-x emoji
}
}

lcovParse(lcovPath, function (err, data) {
if (err) {
console.error(err);
} else {
let totalLinesHit = 0;
let totalLinesFound = 0;
let totalBranchesHit = 0;
let totalBranchesFound = 0;

data.forEach((file) => {
totalLinesHit += file.lines.hit;
totalLinesFound += file.lines.found;
totalBranchesHit += file.branches.hit;
totalBranchesFound += file.branches.found;
const relativePath = shortenPath(
file.file.replace(process.cwd(), ""),
50
);
const lineCoverage = ((file.lines.hit / file.lines.found) * 100).toFixed(
1
);
const branchCoverage = (
(file.branches.hit / file.branches.found) *
100
).toFixed(1);
let emoji = getEmoji(lineCoverage, needsImprovementBelow, poorBelow);
if (outputFormat === "markdown") {
console.log(
`| ${relativePath} | ${emoji}&nbsp;${lineCoverage}% | ${
file.lines.hit
}&nbsp;/&nbsp;${file.lines.found} | ${
file.lines.found - file.lines.hit
} | ${file.branches.found === 0 ? "-" : `${branchCoverage}%`} |`
);
} else {
console.log(
`${emoji} File: ${relativePath}, Line Coverage: ${lineCoverage}%, Branch Coverage: ${branchCoverage}%`
);
}
});

const overallLineCoverage = (
(totalLinesHit / totalLinesFound) *
100
).toFixed(1);
const overallBranchCoverage = (
(totalBranchesHit / totalBranchesFound) *
100
).toFixed(1);
const totalUncoveredLines = totalLinesFound - totalLinesHit;
const overallEmoji = getEmoji(
overallLineCoverage,
needsImprovementBelow,
poorBelow
);

if (outputFormat === "markdown") {
console.log(
`| Overall | ${overallEmoji}&nbsp;${overallLineCoverage}% | ${totalLinesHit}&nbsp;/&nbsp;${totalLinesFound} | ${totalUncoveredLines} | ${
totalBranchesFound === 0 ? "-" : `${overallBranchCoverage}%`
} |`
);
} else {
console.log(
`Overall Line Coverage: ${overallLineCoverage}%, Overall Branch Coverage: ${overallBranchCoverage}%`
);
}
}
});
45 changes: 28 additions & 17 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,33 @@ on:
- main

jobs:
build:
check-access:
runs-on: ubuntu-latest
outputs:
has-token-access: ${{ steps.check.outputs.has-token-access }}
steps:
- name: Checkout code
uses: actions/checkout@v3
- id: check
run: |
echo "has-token-access=$(if [[ '${{ github.event.pull_request.head.repo.fork }}' != 'true' && '${{ github.actor }}' != 'dependabot[bot]' ]]; then echo 'true'; else echo 'false'; fi)" >> $GITHUB_OUTPUT

- name: Use Node.js 16
uses: actions/setup-node@v3
install-deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/configure-nodejs
with:
node-version: 16
lookup-only: 'true' # We only want to lookup from the cache - if a hit, this job does nothing

- name: Cache Node Modules
id: cache-node-modules
uses: actions/cache@v3
with:
path: |
node_modules
!node_modules/.cache
key: node-modules-${{ hashFiles('package.json', 'package-lock.json') }}
build:
needs:
- install-deps
- check-access
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Modules
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci
- uses: ./.github/actions/configure-nodejs

- name: Build
run: npm run build
Expand All @@ -41,3 +45,10 @@ jobs:

- name: Test
run: npm run test

- name: Upload code coverage
if: github.event_name == 'pull_request' && needs.check-access.outputs.has-token-access == 'true'
uses: ./.github/actions/coverage-report
with:
lcov-file: coverage/lcov.info
title: Node.js Code Coverage Report
28 changes: 10 additions & 18 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,21 @@ on:
workflow_dispatch:

jobs:
build:
install-deps:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Use Node.js 16
uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/configure-nodejs
with:
node-version: 16
lookup-only: 'true' # We only want to lookup from the cache - if a hit, this job does nothing

- name: Cache Node Modules
id: cache-node-modules
uses: actions/cache@v3
with:
path: |
node_modules
!node_modules/.cache
key: node-modules-${{ hashFiles('package.json', 'package-lock.json') }}
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Modules
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci
- uses: ./.github/actions/configure-nodejs

- name: Build Docs
run: npm run build:docs
Expand Down
28 changes: 10 additions & 18 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,21 @@ on:
types: [published]

jobs:
install-deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/configure-nodejs
with:
lookup-only: 'true' # We only want to lookup from the cache - if a hit, this job does nothing

build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Use Node.js 16
uses: actions/setup-node@v3
with:
node-version: 16

- name: Cache Node Modules
id: cache-node-modules
uses: actions/cache@v3
with:
path: |
node_modules
!node_modules/.cache
key: node-modules-${{ hashFiles('package.json', 'package-lock.json') }}

- name: Install Modules
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci
- uses: ./.github/actions/configure-nodejs

- name: Use the Release Tag Version
run: |
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v16.17.1
v18
17 changes: 8 additions & 9 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ module.exports = {
// "lcov",
// "clover"
// ],
coverageReporters: ['html', 'text', 'clover'],
coverageReporters: ['lcov', 'html', 'text'],

// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
Expand All @@ -64,13 +64,7 @@ module.exports = {

// A set of global variables that need to be available in all test environments
// globals: {},
globals: {
'ts-jest': {
tsconfig: 'tsconfig.json',
// Reduces test time from 85 seconds to 20-25 seconds on a 2018 MacBook Pro 13" Core i7
isolatedModules: true,
},
},
globals: {},

// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
Expand Down Expand Up @@ -190,7 +184,12 @@ module.exports = {
// A map from regular expressions to paths to transformers
// transform: undefined,
transform: {
'^.+\\.tsx?$': 'ts-jest',
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: 'tsconfig.jest.json',
},
],
},

// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
Expand Down
Loading