Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(reporter): set exit code when thresholds aren't met #420

Closed
wants to merge 2 commits into from
Closed
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
101 changes: 58 additions & 43 deletions lib/reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var CoverageReporter = function (rootConfig, helper, logger, emitter) {
// ------------------

this.adapters = []
this.reportsDone = null

// Options
// -------
Expand Down Expand Up @@ -218,58 +219,72 @@ var CoverageReporter = function (rootConfig, helper, logger, emitter) {
coverageMap.merge(result.coverage)
}

this.onRunComplete = async function (browsers, results) {
const checkedCoverage = {}
this.onRunComplete = function (browsers /*, results */) {
this.reportsDone = Promise.resolve().then(async () => {
let exitCode = 0
const checkedCoverage = {}

for (const reporterConfig of reporters) {
await Promise.all(browsers.map(async (browser) => {
const coverageMap = coverageMaps[browser.id]
if (!coverageMap) {
return
}

const mainDir = reporterConfig.dir || config.dir
const subDir = reporterConfig.subdir || config.subdir
const outputPath = generateOutputPath(basePath, browser.name, mainDir, subDir)
const remappedCoverageMap = await sourceMapStore.transformCoverage(coverageMap)

const options = helper.merge(config, reporterConfig, {
dir: outputPath,
subdir: '',
browser: browser,
emitter: emitter,
coverageMap: remappedCoverageMap
})
for (const reporterConfig of reporters) {
await Promise.all(browsers.map(async (browser) => {
const coverageMap = coverageMaps[browser.id]
if (!coverageMap) {
return
}

// If config.check is defined, check coverage levels for each browser
if (hasOwnProperty.call(config, 'check') && !checkedCoverage[browser.id]) {
checkedCoverage[browser.id] = true
var coverageFailed = checkCoverage(browser, remappedCoverageMap)
if (coverageFailed && results) {
results.exitCode = 1
const mainDir = reporterConfig.dir || config.dir
const subDir = reporterConfig.subdir || config.subdir
const outputPath = generateOutputPath(basePath, browser.name, mainDir, subDir)
const remappedCoverageMap = await sourceMapStore.transformCoverage(coverageMap)

const options = helper.merge(config, reporterConfig, {
dir: outputPath,
subdir: '',
browser: browser,
emitter: emitter,
coverageMap: remappedCoverageMap
})

// If config.check is defined, check coverage levels for each browser
if (hasOwnProperty.call(config, 'check') && !checkedCoverage[browser.id]) {
checkedCoverage[browser.id] = true
var coverageFailed = checkCoverage(browser, remappedCoverageMap)
if (coverageFailed) {
exitCode = 1
}
}
}

const context = istanbulLibReport.createContext(options)
const report = reports.create(reporterConfig.type || 'html', options)
const context = istanbulLibReport.createContext(options)
const report = reports.create(reporterConfig.type || 'html', options)

// // If reporting to console or in-memory skip directory creation
const toDisk = !reporterConfig.type || !reporterConfig.type.match(/^(text|text-summary|in-memory)$/)
// // If reporting to console or in-memory skip directory creation
const toDisk = !reporterConfig.type || !reporterConfig.type.match(/^(text|text-summary|in-memory)$/)

if (!toDisk && reporterConfig.file === undefined) {
report.execute(context)
return
}
if (!toDisk && reporterConfig.file === undefined) {
report.execute(context)
return
}

helper.mkdirIfNotExists(outputPath, function () {
log.debug('Writing coverage to %s', outputPath)
report.execute(context)
})
}))
}
helper.mkdirIfNotExists(outputPath, function () {
log.debug('Writing coverage to %s', outputPath)
report.execute(context)
})
}))
}
return exitCode
})
}

this.onExit = function (done) {
this.onExit = function (exitDone) {
const done = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Custom done can be execute with 1 or 0. I think, it is regression

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As of now, the onExit done callback from karma-runner doesn't expect any value. This is a new feature proposal from my PR (karma-runner/karma#3541). So I don't understand how this can be a regression.

I don't know how the _onExit is used or how implements it. But I expected it to be a hook for some third-party and I intentionally did not allow it to provide an exit code value.

if (this.reportsDone) {
this.reportsDone.then(exitDone, (err) => {
log.error('Failed to create or write coverage reports\n' + err.stack || err)
exitDone(1)
})
} else {
exitDone(0)
}
}
if (typeof config._onExit === 'function') {
config._onExit(done)
} else {
Expand Down
38 changes: 22 additions & 16 deletions test/reporter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ describe('reporter', () => {
})

it('should make reports', async () => {
await reporter.onRunComplete(browsers)
reporter.onRunComplete(browsers)
await reporter.reportsDone
expect(mkdirIfNotExistsStub).to.have.been.calledTwice
const dir = rootConfig.coverageReporter.dir
expect(mkdirIfNotExistsStub.getCall(0).args[0]).to.deep.equal(resolve('/base', dir, fakeChrome.name))
Expand All @@ -186,7 +187,8 @@ describe('reporter', () => {
reporter.onRunStart()
browsers.forEach(b => reporter.onBrowserStart(b))

await reporter.onRunComplete(browsers)
reporter.onRunComplete(browsers)
await reporter.reportsDone
expect(mkdirIfNotExistsStub).to.have.been.calledTwice
const dir = customConfig.coverageReporter.dir
const subdir = customConfig.coverageReporter.subdir
Expand All @@ -208,7 +210,8 @@ describe('reporter', () => {
reporter.onRunStart()
browsers.forEach(b => reporter.onBrowserStart(b))

await reporter.onRunComplete(browsers)
reporter.onRunComplete(browsers)
await reporter.reportsDone
expect(mkdirIfNotExistsStub).to.have.been.calledTwice
const dir = customConfig.coverageReporter.dir
expect(mkdirIfNotExistsStub.getCall(0).args[0]).to.deep.equal(resolve('/base', dir, 'chrome'))
Expand Down Expand Up @@ -240,7 +243,8 @@ describe('reporter', () => {
reporter.onRunStart()
browsers.forEach(b => reporter.onBrowserStart(b))

await reporter.onRunComplete(browsers)
reporter.onRunComplete(browsers)
await reporter.reportsDone
expect(mkdirIfNotExistsStub.callCount).to.equal(4)
expect(mkdirIfNotExistsStub.getCall(0).args[0]).to.deep.equal(resolve('/base', 'reporter1', 'chrome'))
expect(mkdirIfNotExistsStub.getCall(1).args[0]).to.deep.equal(resolve('/base', 'reporter1', 'opera'))
Expand Down Expand Up @@ -271,7 +275,8 @@ describe('reporter', () => {
reporter.onRunStart()
browsers.forEach(b => reporter.onBrowserStart(b))

await reporter.onRunComplete(browsers)
reporter.onRunComplete(browsers)
await reporter.reportsDone
expect(mkdirIfNotExistsStub.callCount).to.equal(4)
expect(mkdirIfNotExistsStub.getCall(0).args[0]).to.deep.equal(resolve('/base', 'reporter1', 'defaultsubdir'))
expect(mkdirIfNotExistsStub.getCall(1).args[0]).to.deep.equal(resolve('/base', 'reporter1', 'defaultsubdir'))
Expand Down Expand Up @@ -303,7 +308,8 @@ describe('reporter', () => {
reporter = new m.CoverageReporter(rootConfig, mockHelper, mockLogger)
reporter.onRunStart()
browsers.forEach(b => reporter.onBrowserStart(b))
return reporter.onRunComplete(browsers)
reporter.onRunComplete(browsers)
return reporter.reportsDone
}

rootConfig.coverageReporter.reporters = [{ type: 'text', file: 'file' }]
Expand Down Expand Up @@ -393,7 +399,8 @@ describe('reporter', () => {
reporter = new m.CoverageReporter(customConfig, mockHelper, mockLogger)
reporter.onRunStart()
browsers.forEach(b => reporter.onBrowserStart(b))
await reporter.onRunComplete(browsers)
reporter.onRunComplete(browsers)
await reporter.reportsDone

expect(createContextStub).to.have.been.called
expect(reportCreateStub).to.have.been.called
Expand Down Expand Up @@ -424,7 +431,8 @@ describe('reporter', () => {
reporter = new m.CoverageReporter(customConfig, mockHelper, mockLogger)
reporter.onRunStart()
browsers.forEach(b => reporter.onBrowserStart(b))
await reporter.onRunComplete(browsers)
reporter.onRunComplete(browsers)
await reporter.reportsDone

expect(createContextStub).to.have.been.called
expect(reportCreateStub).to.have.been.called
Expand Down Expand Up @@ -467,15 +475,14 @@ describe('reporter', () => {
}
}

const results = { exitCode: 0 }

reporter = new m.CoverageReporter(customConfig, mockHelper, customLogger)
reporter.onRunStart()
browsers.forEach(b => reporter.onBrowserStart(b))
await reporter.onRunComplete(browsers, results)
reporter.onRunComplete(browsers)
const exitCode = await reporter.reportsDone

expect(spy1).to.have.been.called
expect(results.exitCode).to.not.equal(0)
expect(exitCode).to.not.equal(0)
})

it('should not log errors on sufficient coverage and not fail the build', async () => {
Expand Down Expand Up @@ -510,16 +517,15 @@ describe('reporter', () => {
}
}

const results = { exitCode: 0 }

reporter = new m.CoverageReporter(customConfig, mockHelper, customLogger)
reporter.onRunStart()
browsers.forEach(b => reporter.onBrowserStart(b))
await reporter.onRunComplete(browsers, results)
reporter.onRunComplete(browsers)
const exitCode = await reporter.reportsDone

expect(spy1).to.not.have.been.called

expect(results.exitCode).to.equal(0)
expect(exitCode).to.equal(0)
})
})
})