-
Notifications
You must be signed in to change notification settings - Fork 30.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test_runner: add support for coverage via run()
- Loading branch information
1 parent
dc74f17
commit ffe1e56
Showing
3 changed files
with
263 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
import * as common from '../common/index.mjs'; | ||
import * as fixtures from '../common/fixtures.mjs'; | ||
import { describe, it, run } from 'node:test'; | ||
import assert from 'node:assert'; | ||
|
||
const files = [fixtures.path('test-runner', 'coverage.js')]; | ||
const skipIfNoInspector = { | ||
skip: !process.features.inspector ? 'inspector disabled' : false | ||
}; | ||
|
||
describe('require(\'node:test\').run Coverage settings', { concurrency: true }, () => { | ||
describe('validation', () => { | ||
it('should only allow boolean in options.coverage', async () => { | ||
[Symbol(), {}, () => {}, 0, 1, 0n, 1n, '', '1', Promise.resolve(true), []] | ||
.forEach((coverage) => assert.throws(() => run({ coverage }), { | ||
code: 'ERR_INVALID_ARG_TYPE' | ||
})); | ||
}); | ||
|
||
it('should only allow coverage options when coverage is true', async () => { | ||
assert.throws( | ||
() => run({ coverage: false, coverageIncludeGlobs: [] }), | ||
{ code: 'ERR_INVALID_ARG_VALUE' }, | ||
); | ||
assert.throws( | ||
() => run({ coverage: false, coverageExcludeGlobs: [] }), | ||
{ code: 'ERR_INVALID_ARG_VALUE' }, | ||
); | ||
assert.throws( | ||
() => run({ coverage: false, lineCoverage: 0 }), | ||
{ code: 'ERR_INVALID_ARG_VALUE' }, | ||
); | ||
assert.throws( | ||
() => run({ coverage: false, branchCoverage: 0 }), | ||
{ code: 'ERR_INVALID_ARG_VALUE' }, | ||
); | ||
assert.throws( | ||
() => run({ coverage: false, functionCoverage: 0 }), | ||
{ code: 'ERR_INVALID_ARG_VALUE' }, | ||
); | ||
}); | ||
|
||
it('should only allow string|string[] in options.coverageExcludeGlobs', async () => { | ||
[Symbol(), {}, () => {}, 0, 1, 0n, 1n, Promise.resolve([]), true, false] | ||
.forEach((coverageExcludeGlobs) => { | ||
assert.throws(() => run({ coverage: true, coverageExcludeGlobs }), { | ||
code: 'ERR_INVALID_ARG_TYPE' | ||
}); | ||
assert.throws(() => run({ coverage: true, coverageExcludeGlobs: [coverageExcludeGlobs] }), { | ||
code: 'ERR_INVALID_ARG_TYPE' | ||
}); | ||
}); | ||
run({ files: [], signal: AbortSignal.abort(), coverage: true, coverageExcludeGlobs: [''] }); | ||
run({ files: [], signal: AbortSignal.abort(), coverage: true, coverageExcludeGlobs: '' }); | ||
}); | ||
|
||
it('should only allow string|string[] in options.coverageIncludeGlobs', async () => { | ||
[Symbol(), {}, () => {}, 0, 1, 0n, 1n, Promise.resolve([]), true, false] | ||
.forEach((coverageIncludeGlobs) => { | ||
assert.throws(() => run({ coverage: true, coverageIncludeGlobs }), { | ||
code: 'ERR_INVALID_ARG_TYPE' | ||
}); | ||
assert.throws(() => run({ coverage: true, coverageIncludeGlobs: [coverageIncludeGlobs] }), { | ||
code: 'ERR_INVALID_ARG_TYPE' | ||
}); | ||
}); | ||
|
||
run({ files: [], signal: AbortSignal.abort(), coverage: true, coverageIncludeGlobs: [''] }); | ||
run({ files: [], signal: AbortSignal.abort(), coverage: true, coverageIncludeGlobs: '' }); | ||
}); | ||
|
||
it('should only allow an int in options.lineCoverage', async () => { | ||
[Symbol(), {}, () => {}, [], 0n, 1n, Promise.resolve([]), true, false] | ||
.forEach((lineCoverage) => { | ||
assert.throws(() => run({ coverage: true, lineCoverage }), { | ||
code: 'ERR_INVALID_ARG_TYPE' | ||
}); | ||
assert.throws(() => run({ coverage: true, lineCoverage: [lineCoverage] }), { | ||
code: 'ERR_INVALID_ARG_TYPE' | ||
}); | ||
}); | ||
|
||
run({ files: [], signal: AbortSignal.abort(), coverage: true, lineCoverage: 0 }); | ||
}); | ||
|
||
it('should only allow an int in options.branchCoverage', async () => { | ||
[Symbol(), {}, () => {}, [], 0n, 1n, Promise.resolve([]), true, false] | ||
.forEach((branchCoverage) => { | ||
assert.throws(() => run({ coverage: true, branchCoverage }), { | ||
code: 'ERR_INVALID_ARG_TYPE' | ||
}); | ||
assert.throws(() => run({ coverage: true, branchCoverage: [branchCoverage] }), { | ||
code: 'ERR_INVALID_ARG_TYPE' | ||
}); | ||
}); | ||
|
||
run({ files: [], signal: AbortSignal.abort(), coverage: true, branchCoverage: 0 }); | ||
}); | ||
|
||
it('should only allow an int in options.functionCoverage', async () => { | ||
[Symbol(), {}, () => {}, [], 0n, 1n, Promise.resolve([]), true, false] | ||
.forEach((functionCoverage) => { | ||
assert.throws(() => run({ coverage: true, functionCoverage }), { | ||
code: 'ERR_INVALID_ARG_TYPE' | ||
}); | ||
assert.throws(() => run({ coverage: true, functionCoverage: [functionCoverage] }), { | ||
code: 'ERR_INVALID_ARG_TYPE' | ||
}); | ||
}); | ||
|
||
run({ files: [], signal: AbortSignal.abort(), coverage: true, functionCoverage: 0 }); | ||
}); | ||
}); | ||
|
||
describe('run with coverage', skipIfNoInspector, () => { | ||
it('should run with coverage', async () => { | ||
const stream = run({ files, coverage: true }); | ||
stream.on('test:fail', common.mustNotCall()); | ||
stream.on('test:pass', common.mustCall()); | ||
stream.on('test:coverage', common.mustCall()); | ||
// eslint-disable-next-line no-unused-vars | ||
for await (const _ of stream); | ||
}); | ||
|
||
it('should run with coverage and exclude by glob', async () => { | ||
const stream = run({ files, coverage: true, coverageExcludeGlobs: ['test/*/test-runner/invalid-tap.js'] }); | ||
stream.on('test:fail', common.mustNotCall()); | ||
stream.on('test:pass', common.mustCall(1)); | ||
stream.on('test:coverage', common.mustCall(({ summary: { files } }) => { | ||
const filesPaths = files.map(({ path }) => path); | ||
assert.strictEqual(filesPaths.some((path) => path.includes('test-runner/invalid-tap.js')), false); | ||
})); | ||
// eslint-disable-next-line no-unused-vars | ||
for await (const _ of stream); | ||
}); | ||
|
||
it('should run with coverage and include by glob', async () => { | ||
const stream = run({ files, coverage: true, coverageIncludeGlobs: ['test/*/test-runner/invalid-tap.js'] }); | ||
stream.on('test:fail', common.mustNotCall()); | ||
stream.on('test:pass', common.mustCall(1)); | ||
stream.on('test:coverage', common.mustCall(({ summary: { files } }) => { | ||
const filesPaths = files.map(({ path }) => path); | ||
assert.strictEqual(filesPaths.some((path) => path.includes('test-runner/invalid-tap.js')), true); | ||
})); | ||
// eslint-disable-next-line no-unused-vars | ||
for await (const _ of stream); | ||
}); | ||
}); | ||
}); | ||
|
||
|
||
// exitHandler doesn't run until after the tests / after hooks finish. | ||
process.on('exit', () => { | ||
assert.strictEqual(process.listeners('uncaughtException').length, 0); | ||
assert.strictEqual(process.listeners('unhandledRejection').length, 0); | ||
assert.strictEqual(process.listeners('beforeExit').length, 0); | ||
assert.strictEqual(process.listeners('SIGINT').length, 0); | ||
assert.strictEqual(process.listeners('SIGTERM').length, 0); | ||
}); |