danger-plugin-coverage
Advanced tools
Comparing version 1.5.0 to 1.6.0
@@ -93,4 +93,3 @@ "use strict"; | ||
const getShortPath = filePath => { | ||
const maxChars = 40; | ||
const getShortPath = (filePath, maxChars) => { | ||
const parts = filePath.split('/').reverse().filter(x => x); | ||
@@ -104,10 +103,14 @@ | ||
let currentChars = 0; | ||
parts.forEach(part => { | ||
if (currentChars < maxChars) { | ||
parts.forEach((part, index) => { | ||
const isLastPart = parts.length - 1 === index; | ||
currentChars += part.length + 1; // +1 for the path seperator | ||
const prefixLength = !isLastPart ? 3 : 0; | ||
if (currentChars + prefixLength < maxChars) { | ||
shortParts.push(part); | ||
currentChars += part.length + 1; // +1 for the path seperator | ||
} | ||
}); | ||
if (shortParts.length < parts.length - 1) { | ||
if (shortParts.length < parts.length) { | ||
shortParts.push('..'); | ||
@@ -119,2 +122,26 @@ } | ||
/** | ||
* Insert whitespace into a path so that it wraps within the Markdown table. | ||
*/ | ||
const getWrappedPath = filePath => { | ||
const parts = filePath.split('/'); | ||
const maxPerLine = 25; | ||
let currentChars = 0; | ||
return parts.map((pathPart, i) => { | ||
var _parts; | ||
const isLastPart = parts.length - 1 === i; | ||
const newPart = isLastPart ? pathPart : `${pathPart}/`; | ||
currentChars += pathPart.length; | ||
if (currentChars + (((_parts = parts[i + 1]) === null || _parts === void 0 ? void 0 : _parts.length) || 0) > maxPerLine) { | ||
currentChars = 0; | ||
return `${newPart}<br>`; | ||
} | ||
return newPart; | ||
}).join(''); | ||
}; | ||
/** | ||
* Check if we have passed the thresholds for the given percentages. | ||
@@ -135,3 +162,8 @@ */ | ||
const buildRow = (file, threshold) => { | ||
const buildRow = (file, { | ||
threshold, | ||
maxChars, | ||
maxUncovered, | ||
wrapFilenames | ||
}) => { | ||
var _danger$git, _danger$git$commits; | ||
@@ -144,7 +176,8 @@ | ||
const filePath = _path.default.relative(process.cwd(), file.$.path); | ||
const longPath = _path.default.relative(process.cwd(), file.$.path); | ||
const shortPath = getShortPath(filePath); | ||
const fileLink = `../blob/${sha}/${filePath}`; | ||
const fileCell = sha ? `[${shortPath}](${fileLink})` : shortPath; | ||
const shortPath = getShortPath(longPath, maxChars); | ||
const readablePath = wrapFilenames ? getWrappedPath(shortPath) : shortPath; | ||
const fileLink = `../blob/${sha}/${longPath}`; | ||
const fileCell = sha ? `[${readablePath}](${fileLink})` : readablePath; | ||
const percentages = getMetricPercentages(fileMetrics); | ||
@@ -158,3 +191,2 @@ const noLines = !fileMetrics.lines; | ||
const maxUncovered = 3; | ||
let uncoveredCell = fileMetrics.uncoveredLines.slice(0, maxUncovered).map(line => { | ||
@@ -183,3 +215,7 @@ const lineNumber = line.$.num; | ||
const buildTable = (files, maxRows, threshold, showAllFiles) => { | ||
const buildTable = (files, opts) => { | ||
const { | ||
maxRows, | ||
showAllFiles | ||
} = opts; | ||
const headings = [`${showAllFiles ? '' : 'Impacted '}Files`, '% Stmts', '% Branch', '% Funcs', '% Lines', 'Uncovered Lines', '']; | ||
@@ -189,3 +225,3 @@ const headingRow = joinRow(headings); | ||
], [])); | ||
const allFileRows = files.map(file => buildRow(file, threshold)); | ||
const allFileRows = files.map(file => buildRow(file, opts)); | ||
const mainFileRows = allFileRows.slice(0, maxRows); | ||
@@ -220,3 +256,7 @@ const extraFileRows = allFileRows.slice(maxRows); | ||
const buildSummary = (metrics, successMessage, failureMessage, threshold) => { | ||
const buildSummary = (metrics, { | ||
successMessage, | ||
failureMessage, | ||
threshold | ||
}) => { | ||
const percentages = getMetricPercentages(metrics); | ||
@@ -249,3 +289,5 @@ const passed = hasPassed(threshold, percentages); | ||
const getRelevantFiles = (coverageXml, showAllFiles) => { | ||
const getRelevantFiles = (coverageXml, { | ||
showAllFiles | ||
}) => { | ||
var _danger$git2, _danger$git3; | ||
@@ -268,20 +310,25 @@ | ||
const coverage = async ({ | ||
successMessage = ':+1: Test coverage is looking good.', | ||
failureMessage = 'Test coverage is looking a little low for the files created ' + 'or modified in this PR, perhaps we need to improve this.', | ||
cloverReportPath, | ||
maxRows = 5, | ||
showAllFiles = false, | ||
warnOnNoReport = true, | ||
threshold = { | ||
statements: 80, | ||
branches: 80, | ||
functions: 80, | ||
lines: 80 | ||
} | ||
} = {}) => { | ||
const coverageXml = await (0, _report.getCoverageReport)(cloverReportPath); | ||
const coverage = async (initialOpts = {}) => { | ||
const opts = { | ||
successMessage: ':+1: Test coverage is looking good.', | ||
failureMessage: 'Test coverage is looking a little low for the files created ' + 'or modified in this PR, perhaps we need to improve this.', | ||
cloverReportPath: null, | ||
maxRows: 3, | ||
maxChars: 100, | ||
maxUncovered: 10, | ||
wrapFilenames: true, | ||
showAllFiles: false, | ||
warnOnNoReport: true, | ||
threshold: { | ||
statements: 80, | ||
branches: 80, | ||
functions: 80, | ||
lines: 80 | ||
}, | ||
...initialOpts | ||
}; | ||
const coverageXml = await (0, _report.getCoverageReport)(opts.cloverReportPath); | ||
if (!coverageXml) { | ||
if (warnOnNoReport) { | ||
if (opts.warnOnNoReport) { | ||
warn('No coverage report was detected. ' + 'Please output a report in the `clover.xml` format before running danger'); | ||
@@ -293,3 +340,3 @@ } | ||
const relevantFiles = getRelevantFiles(coverageXml, showAllFiles); | ||
const relevantFiles = getRelevantFiles(coverageXml, opts); | ||
@@ -301,4 +348,4 @@ if (!relevantFiles.length) { | ||
const combinedMetrics = getCombinedMetrics(relevantFiles); | ||
const table = buildTable(relevantFiles, maxRows, threshold, showAllFiles); | ||
const summary = buildSummary(combinedMetrics, successMessage, failureMessage, threshold); | ||
const table = buildTable(relevantFiles, opts); | ||
const summary = buildSummary(combinedMetrics, opts); | ||
const report = ['## Coverage Report', summary, table].join(newLine + newLine); | ||
@@ -305,0 +352,0 @@ markdown(report); |
{ | ||
"name": "danger-plugin-coverage", | ||
"version": "1.5.0", | ||
"version": "1.6.0", | ||
"description": "A Danger plugin to report code coverage.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -86,2 +86,5 @@ # danger-plugin-coverage | ||
| `maxRows` | The number of rows to show (additional rows will be collapsed within a `<details>` element). | | ||
| `maxChars` | The maximum number of characters to allow in a file name cell. | | ||
| `maxUncovered` | The maximum number of uncovered lines to show. | | ||
| `wrapFilenames` | Wrap long file names to help the table fit in a PR comment. | | ||
| `threshold` | The thresholds at which to show the failure messaging. | | ||
@@ -100,3 +103,6 @@ | `warnOnNoReport` | Show a warning if no coverage report was detected. | | ||
cloverReportPath: './coverage/clover.xml', | ||
maxRows: 5, | ||
maxRows: 3, | ||
maxChars: 100, | ||
maxUncovered: 10, | ||
wrapFilenames: true, | ||
warnOnNoReport: true, | ||
@@ -103,0 +109,0 @@ showAllFiles: false, |
@@ -76,4 +76,3 @@ import path from 'path'; | ||
*/ | ||
const getShortPath = (filePath) => { | ||
const maxChars = 40; | ||
const getShortPath = (filePath, maxChars) => { | ||
const parts = filePath.split('/').reverse().filter((x) => x); | ||
@@ -89,10 +88,13 @@ | ||
parts.forEach((part) => { | ||
if (currentChars < maxChars) { | ||
parts.forEach((part, index) => { | ||
const isLastPart = parts.length - 1 === index; | ||
currentChars += part.length + 1; // +1 for the path seperator | ||
const prefixLength = (!isLastPart ? 3 : 0); | ||
if (currentChars + prefixLength < maxChars) { | ||
shortParts.push(part); | ||
currentChars += part.length + 1; // +1 for the path seperator | ||
} | ||
}); | ||
if (shortParts.length < parts.length - 1) { | ||
if (shortParts.length < parts.length) { | ||
shortParts.push('..'); | ||
@@ -105,2 +107,24 @@ } | ||
/** | ||
* Insert whitespace into a path so that it wraps within the Markdown table. | ||
*/ | ||
const getWrappedPath = (filePath) => { | ||
const parts = filePath.split('/'); | ||
const maxPerLine = 25; | ||
let currentChars = 0; | ||
return parts.map((pathPart, i) => { | ||
const isLastPart = parts.length - 1 === i; | ||
const newPart = isLastPart ? pathPart : `${pathPart}/`; | ||
currentChars += pathPart.length; | ||
if (currentChars + (parts[i + 1]?.length || 0) > maxPerLine) { | ||
currentChars = 0; | ||
return `${newPart}<br>`; | ||
} | ||
return newPart; | ||
}).join(''); | ||
}; | ||
/** | ||
* Check if we have passed the thresholds for the given percentages. | ||
@@ -123,11 +147,19 @@ */ | ||
*/ | ||
const buildRow = (file, threshold) => { | ||
const buildRow = (file, { | ||
threshold, | ||
maxChars, | ||
maxUncovered, | ||
wrapFilenames, | ||
}) => { | ||
const fileMetrics = getFileMetrics(file); | ||
const { sha } = danger.git?.commits?.[danger.git.commits.length - 1] || {}; | ||
const filePath = path.relative(process.cwd(), file.$.path); | ||
const shortPath = getShortPath(filePath); | ||
const fileLink = `../blob/${sha}/${filePath}`; | ||
const fileCell = sha ? `[${shortPath}](${fileLink})` : shortPath; | ||
const longPath = path.relative(process.cwd(), file.$.path); | ||
const shortPath = getShortPath(longPath, maxChars); | ||
const readablePath = wrapFilenames ? getWrappedPath(shortPath) : shortPath; | ||
const fileLink = `../blob/${sha}/${longPath}`; | ||
const fileCell = sha ? `[${readablePath}](${fileLink})` : readablePath; | ||
const percentages = getMetricPercentages(fileMetrics); | ||
@@ -142,3 +174,2 @@ | ||
const maxUncovered = 3; | ||
let uncoveredCell = fileMetrics | ||
@@ -180,3 +211,8 @@ .uncoveredLines | ||
*/ | ||
const buildTable = (files, maxRows, threshold, showAllFiles) => { | ||
const buildTable = (files, opts) => { | ||
const { | ||
maxRows, | ||
showAllFiles, | ||
} = opts; | ||
const headings = [ | ||
@@ -200,3 +236,3 @@ `${showAllFiles ? '' : 'Impacted '}Files`, | ||
const allFileRows = files.map((file) => buildRow(file, threshold)); | ||
const allFileRows = files.map((file) => buildRow(file, opts)); | ||
const mainFileRows = allFileRows.slice(0, maxRows); | ||
@@ -245,3 +281,3 @@ const extraFileRows = allFileRows.slice(maxRows); | ||
*/ | ||
const buildSummary = (metrics, successMessage, failureMessage, threshold) => { | ||
const buildSummary = (metrics, { successMessage, failureMessage, threshold }) => { | ||
const percentages = getMetricPercentages(metrics); | ||
@@ -288,3 +324,3 @@ const passed = hasPassed(threshold, percentages); | ||
*/ | ||
const getRelevantFiles = (coverageXml, showAllFiles) => { | ||
const getRelevantFiles = (coverageXml, { showAllFiles }) => { | ||
const files = getFlatFiles(coverageXml); | ||
@@ -310,21 +346,27 @@ const allFiles = [ | ||
*/ | ||
export const coverage = async ({ | ||
successMessage = ':+1: Test coverage is looking good.', | ||
failureMessage = 'Test coverage is looking a little low for the files created ' | ||
+ 'or modified in this PR, perhaps we need to improve this.', | ||
cloverReportPath, | ||
maxRows = 5, | ||
showAllFiles = false, | ||
warnOnNoReport = true, | ||
threshold = { | ||
statements: 80, | ||
branches: 80, | ||
functions: 80, | ||
lines: 80, | ||
}, | ||
} = {}) => { | ||
const coverageXml = await getCoverageReport(cloverReportPath); | ||
export const coverage = async (initialOpts = {}) => { | ||
const opts = { | ||
successMessage: ':+1: Test coverage is looking good.', | ||
failureMessage: 'Test coverage is looking a little low for the files created ' | ||
+ 'or modified in this PR, perhaps we need to improve this.', | ||
cloverReportPath: null, | ||
maxRows: 3, | ||
maxChars: 100, | ||
maxUncovered: 10, | ||
wrapFilenames: true, | ||
showAllFiles: false, | ||
warnOnNoReport: true, | ||
threshold: { | ||
statements: 80, | ||
branches: 80, | ||
functions: 80, | ||
lines: 80, | ||
}, | ||
...initialOpts, | ||
}; | ||
const coverageXml = await getCoverageReport(opts.cloverReportPath); | ||
if (!coverageXml) { | ||
if (warnOnNoReport) { | ||
if (opts.warnOnNoReport) { | ||
warn('No coverage report was detected. ' | ||
@@ -336,3 +378,3 @@ + 'Please output a report in the `clover.xml` format before running danger'); | ||
const relevantFiles = getRelevantFiles(coverageXml, showAllFiles); | ||
const relevantFiles = getRelevantFiles(coverageXml, opts); | ||
@@ -344,4 +386,4 @@ if (!relevantFiles.length) { | ||
const combinedMetrics = getCombinedMetrics(relevantFiles); | ||
const table = buildTable(relevantFiles, maxRows, threshold, showAllFiles); | ||
const summary = buildSummary(combinedMetrics, successMessage, failureMessage, threshold); | ||
const table = buildTable(relevantFiles, opts); | ||
const summary = buildSummary(combinedMetrics, opts); | ||
const report = [ | ||
@@ -348,0 +390,0 @@ '## Coverage Report', |
@@ -228,3 +228,3 @@ import path from 'path'; | ||
expect(lines).toContain(successMessage); | ||
expect(lines).toContain('and 5 more...'); | ||
expect(lines).toContain('and 7 more...'); | ||
}); | ||
@@ -298,4 +298,4 @@ | ||
it('shortens long paths', async () => { | ||
const seg = 'xxxxxxxxxx'; | ||
it('shortens and wraps long paths', async () => { | ||
const seg = new Array(10).fill('x').join(''); | ||
const longPath = new Array(10).fill(seg).join('/'); | ||
@@ -325,3 +325,3 @@ const file = getFileXml(longPath, DEFAULT_METRICS, [DEFAULT_LINE]); | ||
expect(lines).toContain( | ||
`|../${seg}/${seg}/${seg}/${seg}|100|100|100|100||:white_check_mark:|`, | ||
`|../${seg}/${seg}/<br>${seg}/${seg}/<br>${seg}/${seg}/<br>${seg}/${seg}|100|100|100|100||:white_check_mark:|`, | ||
); | ||
@@ -452,33 +452,14 @@ }); | ||
it('reports uncovered lines', async () => { | ||
const fileOne = getFileXml(path.join(process.cwd(), '/src/one.js'), DEFAULT_METRICS, [ | ||
{ | ||
num: 1, | ||
count: 0, | ||
type: 'stmt', | ||
}, | ||
{ | ||
num: 2, | ||
count: 0, | ||
type: 'stmt', | ||
}, | ||
{ | ||
num: 3, | ||
count: 0, | ||
type: 'stmt', | ||
}, | ||
{ | ||
num: 4, | ||
count: 0, | ||
type: 'stmt', | ||
}, | ||
]); | ||
const getUncoveredLine = (num) => ({ | ||
num, | ||
count: 0, | ||
type: 'stmt', | ||
}); | ||
const fileTwo = getFileXml(path.join(process.cwd(), '/src/two.js'), DEFAULT_METRICS, [ | ||
{ | ||
num: 1, | ||
count: 0, | ||
type: 'stmt', | ||
}, | ||
]); | ||
const fileOneLines = new Array(11).fill().map((_, i) => getUncoveredLine(i + 1)); | ||
const fileOne = getFileXml(path.join(process.cwd(), '/src/one.js'), DEFAULT_METRICS, fileOneLines); | ||
const fileTwoLines = [getUncoveredLine(1)]; | ||
const fileTwo = getFileXml(path.join(process.cwd(), '/src/two.js'), DEFAULT_METRICS, fileTwoLines); | ||
const xmlReport = wrapXmlReport([ | ||
@@ -509,3 +490,3 @@ fileOne, | ||
expect(report).toMatchSnapshot(); | ||
expect(lines).toContain('|src/one.js|100|100|100|0|1, 2, 3...|:x:|'); | ||
expect(lines).toContain('|src/one.js|100|100|100|0|1, 2, 3, 4, 5, 6, 7, 8, 9, 10...|:x:|'); | ||
expect(lines).toContain('|src/two.js|100|100|100|0|1|:x:|'); | ||
@@ -512,0 +493,0 @@ }); |
@@ -201,2 +201,28 @@ import path from 'path'; | ||
}); | ||
it('makes paths even shorter', async () => { | ||
const longPath = 'ab/cd/ef/gh/ij/kl/mn'; // 20 chars | ||
const file = getFileXml(longPath, DEFAULT_METRICS, [DEFAULT_LINE]); | ||
const xmlReport = wrapXmlReport(file); | ||
mockFs({ | ||
[CLOVER_PATH]: xmlReport, | ||
}); | ||
Object.assign(danger, { | ||
git: { | ||
created_files: [longPath], | ||
modified_files: [], | ||
}, | ||
}); | ||
await coverage({ maxChars: 10 }); | ||
const report = getMarkdownReport(); | ||
const lines = report.split('\n'); | ||
expect(report).toMatchSnapshot(); | ||
expect(lines).toContain('|../kl/mn|100|100|100|100||:white_check_mark:|'); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
58353
1394
116