mocha-decaf
Version:
Run Mocha tests in dry run mode
207 lines (190 loc) • 5.38 kB
JavaScript
const Mocha = require('mocha')
const path = require('path')
const fs = require('fs')
/**
* does the file has the extension of JS files (*.js)
* @param {string} filePath path to a file
* @returns {boolean} true if the file has .js extension, false otherwise
* @example
* isJSFile('./index.js')
* // true
* isJSFile('./package.json')
* // false
*/
const isJSFile = (filePath) => filePath.substr(-3) === '.js'
/**
* List all the files in a given directory, by default in recursive mode
* @param {string} dir path to a dir
* @param {boolean} [recursive=true] search in nested directories
* @returns {string[]} array of files found in the given directory
* @example
* listFiles('./')
* // ['main.js', 'package.json', 'README.md', 'test/main.test.js']
* listFiles('./', false)
* // ['main.js', 'package.json', 'README.md']
*/
const listFiles = (dir, recursive = true) => {
let JSFiles = []
const dirContents = fs.readdirSync(dir)
for (const item of dirContents) {
const itemPath = path.join(dir, item)
const itemStat = fs.statSync(itemPath)
if (itemStat.isFile()) JSFiles.push(itemPath)
else if (itemStat.isDirectory() && recursive) JSFiles = JSFiles.concat(listFiles(itemPath))
}
return JSFiles
}
/**
* Patch the runTest method of the Mocha Test Runner to pass all tests
* @example
* patchTestRunner()
*/
const patchTestRunner = () => {
Mocha.Runner.prototype.runTest = (callback) => {
callback()
}
}
/**
* Patch beforeEach() to do nothing
* @example
* patchBeforeEach()
*/
const patchBeforeEach = () => {
Mocha.Suite.prototype.beforeEach = () => {
return this
}
}
/**
* Patch afterEach() to do nothing
* @example
* patchAfterEach()
*/
const patchAfterEach = () => {
Mocha.Suite.prototype.afterEach = () => {
return this
}
}
/**
* Patch before() to do nothing
* @example
* patchBeforeAll()
*/
const patchBeforeAll = () => {
Mocha.Suite.prototype.beforeAll = () => {
return this
}
}
/**
* Patch after() to do nothing
* @example
* patchAfterAll()
*/
const patchAfterAll = () => {
Mocha.Suite.prototype.afterAll = () => {
return this
}
}
/**
* This function is ported from Mocha run helpers
*
* Exits Mocha when Mocha itself has finished execution, regardless of
* what the tests or code under test is doing.
* @param {number} code - Exit code; typically # of failures
* @private
*/
const exitMocha = code => {
const clampedCode = Math.min(code, 255)
let draining = 0
// Eagerly set the process's exit code in case stream.write doesn't
// execute its callback before the process terminates.
process.exitCode = clampedCode
// flush output for Node.js Windows pipe bug
const done = () => {
if (!draining--) {
process.exit(clampedCode)
}
}
const streams = [process.stdout, process.stderr]
streams.forEach(stream => {
// submit empty write request and wait for completion
draining += 1
stream.write('', done)
})
done()
}
/**
* Run mocha programatically on given test files
* @param {Array} [files=[]] array of test files
* @param {Object} [mochaOptions={}] mocha options, refer to {@link https://mochajs.org/api/mocha|mocha's api documentation}
* @returns {Mocha.Runner} mocha {@link https://mochajs.org/api/runner|runner} instance that ran all the tests
* @see {@link https://mochajs.org/api/|Mocha API documentation}
*
* @example <caption>To run the tests in ./test/main.test.js with default mocha options</caption>
* runMocha(['./test/main.test.js'])
* //
* // test suite
* // ✓ test case
* //
* // 1 passing (7ms)
*
* @example <caption>To run the tests in ./test/main.test.js with the nyan mocha reporter</caption>
* runMocha(['./test/main.test.js'], { reporter: 'nyan' })
* //
* // 1 -__,------,
* // 0 -__| /\_/\
* // 0 -_~|_( ^ .^)
* // -_ "" ""
* //
* // 1 passing (11ms)
*/
const runMocha = (files = [], mochaOptions = {}) => {
const mocha = new Mocha(mochaOptions)
files.forEach((testFile) => {
mocha.addFile(testFile)
})
patchBeforeEach()
patchAfterEach()
patchBeforeAll()
patchAfterAll()
patchTestRunner()
return mocha.run(exitMocha)
}
/**
* List all tests that were executed by a mocha test runner
* @see {@link https://mochajs.org/api/runner|Mocha Runner}
* @param {Mocha.Runner} runner {@link https://mochajs.org/api/runner|Mocha Runner instance}
* @returns {string[]} array of the full test names that were executed (Root suite name + child suite names + test name)
* @example
* listExecutedTests(runMocha(['./test/main.test.js'], { reporter: 'base' }))
* // [ 'test suite test case' ]
*/
const listExecutedTests = (runner) => {
const listTests = (suite) => {
let tests = []
if (suite === null || suite === undefined) return tests
if (Array.isArray(suite.tests)) suite.tests.forEach(t => tests.push(suite.fullTitle() + ' ' + t.title))
if (Array.isArray(suite.suites)) suite.suites.forEach(s => { tests = tests.concat(listTests(s)) })
return tests
}
return listTests(runner.suite)
}
/**
* Start mocha by requiring the _mocha script
* @example
* startMocha()
*/
const startMocha = () => {
require('mocha/bin/_mocha')
}
module.exports = {
isJSFile,
listFiles,
listExecutedTests,
patchTestRunner,
patchBeforeEach,
patchBeforeAll,
patchAfterEach,
patchAfterAll,
runMocha,
startMocha
}