Compare commits

..

No commits in common. "0.x" and "v0.0.2" have entirely different histories.
0.x ... v0.0.2

12 changed files with 1019 additions and 1366 deletions

View file

@ -5,21 +5,16 @@ on:
pull_request: { branches: ["0.x"] } pull_request: { branches: ["0.x"] }
jobs: jobs:
lint: test:
name: Lint name: Test
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install yarn
run: npm i -g yarn
- name: Set up Node - name: Set up Node
uses: actions/setup-node@v3 uses: actions/setup-node@v2
with: with: { node-version: 18 }
node-version: 18
cache: yarn
- name: Install dependencies - name: Install dependencies
run: yarn install run: yarn install
@ -30,28 +25,5 @@ jobs:
- name: Type Check - name: Type Check
run: yarn build run: yarn build
test:
name: Test Node ${{ matrix.node-versions }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [16, 18, 20]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install yarn
run: npm i -g yarn
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: yarn
- name: Install dependencies
run: yarn install
- name: Test - name: Test
run: yarn test --coverage run: yarn test --coverage

View file

@ -8,14 +8,11 @@ jobs:
commits: commits:
name: Commitlint name: Commitlint
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: practically/conventional-tools:1.x@sha256:647d6e4b3edfcbac6054b90f74d2c61a022152751b94484d54e13695a9e27377 container: registry.k1.zportal.co.uk/practically-oss/conventional-tools:1.x
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with: {fetch-depth: 1000} with: {fetch-depth: 1000}
- name: Git safe.directory
run: git config --global --add safe.directory $PWD
- name: Lint commits - name: Lint commits
run: conventional-tools commitlint -l1 run: conventional-tools commitlint -l1

View file

@ -11,7 +11,7 @@ jobs:
release: release:
name: Release name: Release
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: practically/conventional-tools:1.x@sha256:647d6e4b3edfcbac6054b90f74d2c61a022152751b94484d54e13695a9e27377 container: registry.k1.zportal.co.uk/practically-oss/conventional-tools:1.x
env: env:
CT_TOKEN: ${{ secrets.GITHUB_TOKEN }} CT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps: steps:
@ -20,9 +20,6 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Git safe.directory
run: git config --global --add safe.directory $PWD
- name: Setup Git - name: Setup Git
run: | run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.email "github-actions[bot]@users.noreply.github.com"

View file

@ -8,17 +8,15 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v3 uses: actions/setup-node@v2
with: with:
node-version: 18 node-version: '14'
registry-url: 'https://registry.npmjs.org' registry-url: 'https://registry.npmjs.org'
cache: yarn
- name: Install dependencies and build - name: Install dependencies and build
run: yarn install && yarn build run: yarn install && yarn build

View file

@ -1,20 +1,3 @@
# [v0.2.0](https://github.com/AdeAttwood/DiffCov/compare/v0.1.0...v0.2.0) (2023-06-13)
### Features
* update the coverage to exclude "normal" lines ([9192e62](https://github.com/AdeAttwood/DiffCov/commit/9192e6299547bfb18106af22363f1d10ecfa6ec6))
# [v0.1.0](https://github.com/AdeAttwood/DiffCov/compare/v0.0.2...v0.1.0) (2023-05-31)
### Bug Fixes
* **deps:** update dependency yargs to v17.7.2 ([cb7441d](https://github.com/AdeAttwood/DiffCov/commit/cb7441db1d886d39a9fba5c3dd908cf75efd8173))
### Features
* add better error message when the `coverageFile` is not found ([e20a3d3](https://github.com/AdeAttwood/DiffCov/commit/e20a3d3747157a9c4830cd4454850f19aa2b2819))
## [v0.0.2](https://github.com/AdeAttwood/DiffCov/compare/v0.0.1...v0.0.2) (2022-10-24) ## [v0.0.2](https://github.com/AdeAttwood/DiffCov/compare/v0.0.1...v0.0.2) (2022-10-24)
### Bug Fixes ### Bug Fixes

View file

@ -2,87 +2,6 @@
# Diff Cov # Diff Cov
Simple CLI to print git and lcov diffs highlighted with test coverage status Simple CLI to print diffs highlighted with test coverage status
![Example Output](assets/example-output.png)
</div> </div>
## Installation
You can install the package with npm
```bash
npm i -g @adeattwood/diff-cov
```
You can run it directly with `npx`
```bash
npx @adeattwood/diff-cov
```
## Setup
For diff-cov to get the diff to your default branch, you must set up your
`origin/HEAD` ref. To test to see if you have this set up, run.
```bash
git rev-parse --abbrev-ref origin/HEAD
```
This should print out the origin ref to your default branch for this repo it
will print `origin/0.x`. If you get an unknown ref error, then you will need to
link `origin/HEAD` to your default branch with the below command.
```bash
git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/0.x
```
> **Note**:
> Make sure you change `0.x` for your default branch name typically `main` or
> `development`
## Usage
### Git diff
Before you run `diff-cov` you must run your test suite with coverage and output
a `lcov` coverage file. You must also have all your changes committed to ensure
it's included in the output.
Once you are ready to go, you can run `diff-cov` to print your diff highlighted
with coverage status. Any line not included in the coverage report will not be
highlighted and hit and missed lines will be colored green and red. You can use
the `--coverageFile` flag to specify the path to your coverage report if its
not in the default location of `./lcov.info`
```bash
diff-cov
diff-cov --coverageFile coverage/lcov.info
```
A report is printed at the bottom and colored with a threshold of `90%`
anything below this percentage coverage will be colored red.
### LCov diff
You can also print the coverage difference between to lcov.info files. To use
this run your tests with coverage for a first time. After its finished you can
copy your lcov.info file somewhere for later for example:
```shell
cp ./lcov.info /tmp/lcov.info
```
Then you can work on your test and run the tests once more with coverage to
generate you a new `lcov.info` file. Then you can print the difference in
coverage between the two files with:
```shell
diff-cov --compare /tmp/lcov.info
```
This can come in handy when you need to find out what a test is testing. You
can create your base coverage file, comment out a test, run the tests again and
diff the results.

View file

@ -1,6 +1,6 @@
{ {
"name": "@adeattwood/diff-cov", "name": "@adeattwood/diff-cov",
"version": "0.2.0", "version": "0.0.2",
"description": "Simple CLI to get test coverage on a diff", "description": "Simple CLI to get test coverage on a diff",
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
@ -28,20 +28,20 @@
"build": "tsc" "build": "tsc"
}, },
"dependencies": { "dependencies": {
"yargs": "^17.6.2" "yargs": "^17.3.1"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.4", "@types/jest": "^29.1.1",
"@types/node": "^20.5.0", "@types/node": "^18.11.4",
"@typescript-eslint/eslint-plugin": "^6.4.1", "@typescript-eslint/eslint-plugin": "^5.41.0",
"@typescript-eslint/parser": "^6.4.1", "@typescript-eslint/parser": "^5.41.0",
"eslint": "^8.56.0", "eslint": "^8.25.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^4.2.1",
"jest": "^29.6.4", "jest": "^29.1.2",
"prettier": "^3.0.2", "prettier": "^2.7.1",
"ts-jest": "^29.1.1", "ts-jest": "^29.0.3",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^5.1.6" "typescript": "^4.8.4"
} }
} }

View file

@ -1,24 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:base"],
"lockFileMaintenance": { "enabled": true },
"packageRules": [
{
"matchPackageNames": ["node"],
"allowedVersions": "<=16"
},
{
"matchPackageNames": ["ts-jest", "@types/jest"],
"matchPackagePatterns": ["^jest", "^@testing-library/"],
"groupName": "testing packages"
},
{
"matchPackagePatterns": ["^eslint", "^@typescript-eslint/"],
"groupName": "eslint packages"
},
{
"matchPackageNames": ["react", "react-dom", "@types/react", "@types/react-dom"],
"groupName": "react packages"
}
]
}

View file

@ -1,11 +1,9 @@
import yargs from "yargs"; import yargs from "yargs";
import fs from "fs"; import type { Options } from "yargs";
import type { Options, Arguments } from "yargs";
import { hideBin } from "yargs/helpers"; import { hideBin } from "yargs/helpers";
import exec from "./exec"; import exec from "./exec";
import report from "./report"; import report from "./report";
import lcovDiff from "./lcov-diff";
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const parseDiff = require("./diff-parser"); const parseDiff = require("./diff-parser");
@ -13,7 +11,7 @@ const parseDiff = require("./diff-parser");
const parseLcov = require("./lcov-parser"); const parseLcov = require("./lcov-parser");
export const error = (message: string) => { export const error = (message: string) => {
console.error("[ERROR] " + message); console.error(message);
}; };
const options: { [key: string]: Options } = { const options: { [key: string]: Options } = {
@ -22,87 +20,18 @@ const options: { [key: string]: Options } = {
description: "The path to the lcov report file", description: "The path to the lcov report file",
type: "string", type: "string",
}, },
compare: {
description: "The path to the lcov report you wish to compare to the `coverage-file`",
type: "string",
},
}; };
type Argv = Arguments<
Partial<{
coverageFile: string;
compare?: string;
}>
>;
export const validate = async (argv: Argv) => {
if (!fs.existsSync(argv.coverageFile || "")) {
return new Error(
`Lcov file must be a valid file '${argv.coverageFile}' provided. ` +
`Please ensure you have run tests with coverage enabled.`
);
}
if (argv.compare && !fs.existsSync(argv.compare)) {
return new Error(`Lcov compare file must be a valid file '${argv.compare}' provided.`);
}
};
async function isGitRepo() {
const isGit = await exec(`git rev-parse --is-inside-work-tree`);
return isGit.code === 0;
}
async function isSaplingRepo() {
const isSapling = await exec(`sl root`);
return isSapling.code === 0;
}
async function getDiff(): Promise<string | undefined> {
if (await isGitRepo()) {
const diffText = await exec(`git diff origin/HEAD...HEAD`);
if (diffText.code > 0) {
error("Error loading the diff\n\n" + diffText.stderr);
return undefined;
}
return diffText.stdout;
}
if (await isSaplingRepo()) {
const diffText = await exec(`sl diff -g -r '. % public()'`);
if (diffText.code > 0) {
error("Error loading the diff\n\n" + diffText.stderr);
return undefined;
}
return diffText.stdout;
}
error("Unable to get a diff no repo was found\n");
}
export const run = async (argv = process.argv) => { export const run = async (argv = process.argv) => {
const parsed: Argv = await yargs(hideBin(argv)).options(options).argv; const parsed: any = yargs(hideBin(argv)).options(options).argv;
const validationError = await validate(parsed);
if (validationError instanceof Error) { const diffText = await exec(`git diff origin/HEAD...HEAD`);
return error(validationError.message); if (diffText.code > 0) {
return error("Error loading the diff\n\n" + diffText.stderr);
} }
const baseCoverage = await parseLcov.default(parsed.coverageFile); const diff = parseDiff.default(diffText.stdout);
const coverage = await parseLcov.default(parsed.coverageFile);
if (parsed.compare) { report(diff, coverage);
const compareCoverage = await parseLcov.default(parsed.compare);
return lcovDiff(baseCoverage, compareCoverage);
}
const diffText = await getDiff();
if (!diffText) {
return;
}
const diff = parseDiff.default(diffText);
const { percentage } = report(diff, baseCoverage);
process.exit(percentage > 90 ? 0 : 1);
}; };

View file

@ -1,40 +0,0 @@
const buildCoverageSet = (report: any) => {
const set = new Set<string>();
for (const coverage of report) {
for (const detail of coverage.lines.details) {
if (detail.hit > 0) {
set.add(`${coverage.file}:${detail.line}`);
}
}
}
return set;
};
function setDiff<T>(a: Set<T>, b: Set<T>) {
return new Set([...a].filter((x) => !b.has(x)));
}
export const lcovDiff = async (baseCoverage: any, compareCoverage: any) => {
const baseSet = buildCoverageSet(baseCoverage);
const compareSet = buildCoverageSet(compareCoverage);
const map = new Map<string, number>();
const added = setDiff(baseSet, compareSet);
for (const key of added) {
map.set(key, 1);
}
const removed = setDiff(compareSet, baseSet);
for (const key of removed) {
map.set(key, 0);
}
for (const [key, value] of [...map.entries()].sort()) {
const color = value > 0 ? "\x1b[32m" : "\x1b[31m";
console.log(color, key, "\x1b[0m");
}
};
export default lcovDiff;

View file

@ -1,7 +1,7 @@
const getCoverageForFile = (file: any, coverage: any) => { const getCoverageForFile = (file: any, coverage: any) => {
for (const cov of coverage) { for (const cov of coverage) {
const report: { [k: number]: number } = {}; const report: { [k: number]: number } = {};
if (cov.file.replace("./", "") === file.to) { if (cov.file === file.to) {
for (const detail of cov.lines.details) { for (const detail of cov.lines.details) {
report[detail.line] = detail.hit; report[detail.line] = detail.hit;
} }
@ -35,7 +35,7 @@ interface Diff {
} }
export const printReport = (diff: Diff[], coverage: any) => { export const printReport = (diff: Diff[], coverage: any) => {
const report = { total: 0, covered: 0, percentage: 0 }; const report = { total: 0, covered: 0 };
for (const file of diff) { for (const file of diff) {
const fileCoverage = getCoverageForFile(file, coverage); const fileCoverage = getCoverageForFile(file, coverage);
@ -54,14 +54,7 @@ export const printReport = (diff: Diff[], coverage: any) => {
if (change.type === "del") { if (change.type === "del") {
continue; continue;
} }
const line = change.ln || change.ln2; const line = change.ln || change.ln2;
if (change.type === "normal") {
console.log(line.toString().padStart(4, " "), "\x1b[0m", change.content.substring(1), "\x1b[0m");
continue;
}
let color = "\x1b[0m"; let color = "\x1b[0m";
if (typeof fileCoverage[line] !== "undefined") { if (typeof fileCoverage[line] !== "undefined") {
color = fileCoverage[line] > 0 ? "\x1b[32m" : "\x1b[31m"; color = fileCoverage[line] > 0 ? "\x1b[32m" : "\x1b[31m";
@ -77,13 +70,13 @@ export const printReport = (diff: Diff[], coverage: any) => {
} }
} }
report.percentage = (report.covered / report.total) * 100; const percentage = (report.covered / report.total) * 100;
const color = report.percentage > 90 ? "\x1b[32m" : "\x1b[31m"; const color = percentage > 90 ? "\x1b[32m" : "\x1b[31m";
console.log(""); console.log("");
console.log("Total Lines: ", report.total.toString()); console.log("Total Lines: ", report.total.toString());
console.log("Lines Covered: ", report.covered.toString()); console.log("Lines Covered: ", report.covered.toString());
console.log("Coverage Percentage", color, report.percentage.toString() + "%", "\x1b[0m"); console.log("Coverage Percentage", color, percentage.toString() + "%", "\x1b[0m");
return report; return report;
}; };

2033
yarn.lock

File diff suppressed because it is too large Load diff