diff --git a/dist/truffle.plugin.js b/dist/truffle.plugin.js index 40bbd31..8e9d379 100644 --- a/dist/truffle.plugin.js +++ b/dist/truffle.plugin.js @@ -22,6 +22,7 @@ async function plugin(truffleConfig){ let app; let error; let truffle; + let solcoverjs; let testsErrored = false; // This needs it's own try block because this logic @@ -221,6 +222,16 @@ function loadSolcoverJS(ui, truffleConfig){ coverageConfig.cwd = truffleConfig.working_directory; coverageConfig.originalContractsDir = truffleConfig.contracts_directory; + //Merge solcoverjs mocha opts into truffle's + truffleConfig.mocha = truffleConfig.mocha || {}; + + if (coverageConfig.mocha && typeof coverageConfig.mocha === 'object'){ + truffleConfig.mocha = Object.assign( + truffleConfig.mocha, + coverageConfig.mocha + ); + } + return coverageConfig; } diff --git a/test/integration/projects/multiple-migrations/.solcover.js b/test/integration/projects/multiple-migrations/.solcover.js index 7a54eb3..71b990c 100644 --- a/test/integration/projects/multiple-migrations/.solcover.js +++ b/test/integration/projects/multiple-migrations/.solcover.js @@ -1,4 +1,4 @@ module.exports = { - silent: process.env.SILENT ? true : false, - istanbulReporter: ['json-summary', 'text'] -}; + "silent": false, + "istanbulReporter": [ "json-summary", "text"] +} diff --git a/test/integration/projects/multiple-migrations/test/contracta.js b/test/integration/projects/multiple-migrations/test/contracta.js index 0559b95..c79d402 100644 --- a/test/integration/projects/multiple-migrations/test/contracta.js +++ b/test/integration/projects/multiple-migrations/test/contracta.js @@ -5,11 +5,11 @@ contract("contracta", function(accounts) { before(async () => instance = await ContractA.deployed()) - it('sends', async function(){ + it('sends [ @skipForCoverage ]', async function(){ await instance.sendFn(); }); - it('calls', async function(){ + it('calls [ @skipForCoverage ]', async function(){ await instance.callFn(); }) }); diff --git a/test/integration/projects/multiple-migrations/test/contractb.js b/test/integration/projects/multiple-migrations/test/contractb.js index 14b4cc2..494cf93 100644 --- a/test/integration/projects/multiple-migrations/test/contractb.js +++ b/test/integration/projects/multiple-migrations/test/contractb.js @@ -1,6 +1,6 @@ const ContractB = artifacts.require("ContractB"); -contract("contractB", function(accounts) { +contract("contractB [ @skipForCoverage ]", function(accounts) { let instance; before(async () => instance = await ContractB.deployed()) diff --git a/test/units/truffle/errors.js b/test/units/truffle/errors.js index 5c00d9b..497e9eb 100644 --- a/test/units/truffle/errors.js +++ b/test/units/truffle/errors.js @@ -7,6 +7,10 @@ const verify = require('../../util/verifiers') const mock = require('../../util/integration.truffle'); const plugin = require('../../../dist/truffle.plugin'); +// ======= +// Errors +// ======= + describe('Truffle Plugin: error cases', function() { let truffleConfig; let solcoverConfig; @@ -31,12 +35,12 @@ describe('Truffle Plugin: error cases', function() { } catch(err){ assert( err.message.includes('Cannot locate expected contract sources folder'), - `Should error when contract sources cannot be found: (output --> ${err.message}` + `Should error when contract sources cannot be found:: ${err.message}` ); assert( err.message.includes('sc_temp/contracts'), - `Error message should contain path: (output --> ${err.message}` + `Error message should contain path:: ${err.message}` ); } @@ -53,7 +57,7 @@ describe('Truffle Plugin: error cases', function() { } catch(err){ assert( err.message.includes('Could not load .solcover.js config file.'), - `Should notify when solcoverjs has syntax error: (output --> ${err.message}` + `Should notify when solcoverjs has syntax error:: ${err.message}` ); } @@ -72,8 +76,8 @@ describe('Truffle Plugin: error cases', function() { assert.fail() } catch (err) { assert( - err.message.includes('Unable to load plugin copy of Truffle library module'), - `Should error on failed lib module load (output --> ${err.message}` + err.message.includes('Unable to load plugin copy'), + `Should error on failed lib module load: ${err.message}` ); } }); @@ -140,7 +144,7 @@ describe('Truffle Plugin: error cases', function() { } catch(err){ assert( err.toString().includes('/Unparseable.sol.'), - `Should throw instrumentation errors with file name (output --> ${err.toString()}` + `Should throw instrumentation errors with file name: ${err.toString()}` ); assert(err.stack !== undefined, 'Should have error trace') diff --git a/test/units/truffle/flags.js b/test/units/truffle/flags.js index 7f4830f..f6d968e 100644 --- a/test/units/truffle/flags.js +++ b/test/units/truffle/flags.js @@ -8,7 +8,7 @@ const mock = require('../../util/integration.truffle'); const plugin = require('../../../dist/truffle.plugin'); // ======================= -// Standard Use-case Tests +// CLI Options / Flags // ======================= describe('Truffle Plugin: command line options', function() { @@ -28,8 +28,11 @@ describe('Truffle Plugin: command line options', function() { it('truffle run coverage --file test/', async function() { verify.cleanInitialState(); - const testPath = path.join(truffleConfig.working_directory, 'test/specific_a.js'); - truffleConfig.file = testPath; + truffleConfig.file = path.join( + truffleConfig.working_directory, + 'test/specific_a.js' + ); + mock.installFullProject('test-files'); await plugin(truffleConfig); @@ -54,8 +57,11 @@ describe('Truffle Plugin: command line options', function() { it('truffle run coverage --file test/', async function() { verify.cleanInitialState(); - const testPath = path.join(truffleConfig.working_directory, 'test/globby*'); - truffleConfig.file = testPath; + truffleConfig.file = path.join( + truffleConfig.working_directory, + 'test/globby*' + ); + mock.installFullProject('test-files'); await plugin(truffleConfig); @@ -80,8 +86,11 @@ describe('Truffle Plugin: command line options', function() { it('truffle run coverage --file test/gl{o,b}*.js', async function() { verify.cleanInitialState(); - const testPath = path.join(truffleConfig.working_directory, 'test/gl{o,b}*.js'); - truffleConfig.file = testPath; + truffleConfig.file = path.join( + truffleConfig.working_directory, + 'test/gl{o,b}*.js' + ); + mock.installFullProject('test-files'); await plugin(truffleConfig); @@ -110,17 +119,20 @@ describe('Truffle Plugin: command line options', function() { silent: process.env.SILENT ? true : false, istanbulReporter: ['json-summary', 'text'] }; - fs.writeFileSync('.solcover.js', `module.exports=${JSON.stringify(solcoverConfig)}`); - // This relative path has to be ./ prefixed - // (because it's path.joined to truffle's working_directory) + // Write solcoverjs to parent dir of sc_temp (where the test project is installed) + fs.writeFileSync( + '.solcover.js', + `module.exports=${JSON.stringify(solcoverConfig)}` + ); + + // This relative path has to be ./ prefixed (it's path.joined to truffle's working_directory) truffleConfig.solcoverjs = './../.solcover.js'; mock.install('Simple', 'simple.js'); await plugin(truffleConfig); - // The relative solcoverjs uses the json-summary reporter which - // this assertion requires + // The relative solcoverjs uses the json-summary reporter const expected = [{ file: mock.pathToContract(truffleConfig, 'Simple.sol'), pct: 100 @@ -132,68 +144,72 @@ describe('Truffle Plugin: command line options', function() { it('truffle run coverage --help', async function(){ verify.cleanInitialState(); - truffleConfig.help = "true"; + truffleConfig.help = "true"; truffleConfig.logger = mock.testLogger; + mock.install('Simple', 'simple.js', solcoverConfig); await plugin(truffleConfig); assert( mock.loggerOutput.val.includes('Usage'), - `Should output help with Usage instruction (output --> ${mock.loggerOutput.val}` + `Should output help with Usage instruction : ${mock.loggerOutput.val}` ); }) it('truffle run coverage --version', async function(){ verify.cleanInitialState(); - truffleConfig.version = "true"; + truffleConfig.version = "true"; truffleConfig.logger = mock.testLogger; + mock.install('Simple', 'simple.js', solcoverConfig); await plugin(truffleConfig); assert( mock.loggerOutput.val.includes('truffle'), - `Should output truffle version (output --> ${mock.loggerOutput.val}` + `Should output truffle version: ${mock.loggerOutput.val}` ); assert( mock.loggerOutput.val.includes('ganache-core'), - `Should output ganache-core version (output --> ${mock.loggerOutput.val}` + `Should output ganache-core version: ${mock.loggerOutput.val}` ); assert( mock.loggerOutput.val.includes('solidity-coverage'), - `Should output solidity-coverage version (output --> ${mock.loggerOutput.val}` + `Should output solidity-coverage version: ${mock.loggerOutput.val}` ); }) it('truffle run coverage --useGlobalTruffle', async function(){ verify.cleanInitialState(); - truffleConfig.useGlobalTruffle = true; + truffleConfig.useGlobalTruffle = true; truffleConfig.logger = mock.testLogger; + mock.install('Simple', 'simple.js', solcoverConfig); await plugin(truffleConfig); assert( mock.loggerOutput.val.includes('global node_modules'), - `Should notify it's using global truffle (output --> ${mock.loggerOutput.val}` + `Should notify it's using global truffle: ${mock.loggerOutput.val}` ); }); it('truffle run coverage --usePluginTruffle', async function(){ verify.cleanInitialState(); - truffleConfig.usePluginTruffle = true; + truffleConfig.usePluginTruffle = true; truffleConfig.logger = mock.testLogger; + mock.install('Simple', 'simple.js', solcoverConfig); await plugin(truffleConfig); assert( mock.loggerOutput.val.includes('fallback Truffle library module'), - `Should notify it's using plugin truffle lib copy (output --> ${mock.loggerOutput.val}` + `Should notify it's using plugin truffle lib copy: ${mock.loggerOutput.val}` ); }); }); diff --git a/test/units/truffle/standard.js b/test/units/truffle/standard.js index 8f717c7..167274d 100644 --- a/test/units/truffle/standard.js +++ b/test/units/truffle/standard.js @@ -82,6 +82,40 @@ describe('Truffle Plugin: standard use cases', function() { verify.lineCoverage(expected); }); + // This project has [ @skipForCoverage ] tags in the test descriptions + // at selected 'contract' and 'it' blocks. + it('uses solcoverjs mocha options', async function() { + verify.cleanInitialState(); + + solcoverConfig.mocha = { + grep: '@skipForCoverage', + invert: true, + }; + + solcoverConfig.silent = process.env.SILENT ? true : false, + solcoverConfig.istanbulReporter = ['json-summary', 'text'] + + mock.installFullProject('multiple-migrations', solcoverConfig); + await plugin(truffleConfig); + + const expected = [ + { + file: mock.pathToContract(truffleConfig, 'ContractA.sol'), + pct: 0 + }, + { + file: mock.pathToContract(truffleConfig, 'ContractB.sol'), + pct: 0, + }, + { + file: mock.pathToContract(truffleConfig, 'ContractC.sol'), + pct: 100, + }, + ]; + + verify.lineCoverage(expected); + }); + it('project skips a folder', async function() { verify.cleanInitialState(); mock.installFullProject('skipping'); @@ -116,7 +150,7 @@ describe('Truffle Plugin: standard use cases', function() { assert( mock.loggerOutput.val.includes('native solidity tests'), - `Should warn it is skipping native solidity tests (output --> ${mock.loggerOutput.val}` + `Should warn it is skipping native solidity tests: ${mock.loggerOutput.val}` ); }); diff --git a/test/util/integration.truffle.js b/test/util/integration.truffle.js index 6f5bd84..98b823b 100644 --- a/test/util/integration.truffle.js +++ b/test/util/integration.truffle.js @@ -187,10 +187,15 @@ function installDouble(contracts, test, config) { decacheConfigs(); }; -function installFullProject(name) { +function installFullProject(name, config) { shell.mkdir(temp); shell.cp('-Rf', `${projectPath}${name}/{.,}*`, temp); + if (config){ + const configjs = getSolcoverJS(config); + fs.writeFileSync(`${temp}/.solcover.js`, configjs); + } + decacheConfigs(); } diff --git a/test/util/verifiers.js b/test/util/verifiers.js index e201745..3d3afa5 100644 --- a/test/util/verifiers.js +++ b/test/util/verifiers.js @@ -7,7 +7,15 @@ function pathExists(path) { return shell.test('-e', path); } function lineCoverage(expected=[]){ let summary = JSON.parse(fs.readFileSync('coverage/coverage-summary.json')); - expected.forEach(item => assert(summary[item.file].lines.pct === item.pct)) + + expected.forEach((item, idx) => { + assert( + summary[item.file].lines.pct === item.pct, + + `For condition ${idx} - expected ${item.pct} %,` + + `saw - ${summary[item.file].lines.pct} %` + ) + }); } function coverageMissing(expected=[]){