diff --git a/package.json b/package.json index ed1ff650..0f126a7c 100644 --- a/package.json +++ b/package.json @@ -35,9 +35,9 @@ "istanbul": "^0.4.5", "jsonschema": "^1.2.4", "lodash": "^4.17.15", - "node-dir": "^0.1.17", "node-emoji": "^1.10.0", "pify": "^4.0.1", + "recursive-readdir": "^2.2.2", "shelljs": "^0.8.3", "solidity-parser-antlr": "0.4.7", "web3": "1.2.1", diff --git a/plugins/resources/truffle.utils.js b/plugins/resources/truffle.utils.js index 9c00b594..f33f109f 100644 --- a/plugins/resources/truffle.utils.js +++ b/plugins/resources/truffle.utils.js @@ -1,8 +1,7 @@ const PluginUI = require('./truffle.ui'); - const globalModules = require('global-modules'); const TruffleProvider = require('@truffle/provider'); -const dir = require('node-dir'); +const recursive = require('recursive-readdir'); const globby = require('globby'); const path = require('path'); @@ -15,7 +14,7 @@ const path = require('path'); * @param {Object} config truffleConfig * @return {String[]} list of files to pass to mocha */ -function getTestFilePaths(config){ +async function getTestFilePaths(config){ let target; let ui = new PluginUI(config.logger.log); @@ -23,7 +22,7 @@ function getTestFilePaths(config){ // Handle --file cli option (subset of tests) (typeof config.file === 'string') ? target = globby.sync([config.file]) - : target = dir.files(config.testDir, { sync: true }) || []; + : target = await recursive(config.testDir); // Filter native solidity tests and warn that they're skipped const solregex = /.*\.(sol)$/; diff --git a/plugins/truffle.plugin.js b/plugins/truffle.plugin.js index 06bbadeb..5df66dc4 100644 --- a/plugins/truffle.plugin.js +++ b/plugins/truffle.plugin.js @@ -96,7 +96,7 @@ async function plugin(config){ ); config.all = true; - config.test_files = truffleUtils.getTestFilePaths(config); + config.test_files = await truffleUtils.getTestFilePaths(config); config.compilers.solc.settings.optimizer.enabled = false; // Compile Instrumented Contracts diff --git a/test/integration/projects/tests-folder/.solcover.js b/test/integration/projects/tests-folder/.solcover.js new file mode 100644 index 00000000..71b990cc --- /dev/null +++ b/test/integration/projects/tests-folder/.solcover.js @@ -0,0 +1,4 @@ +module.exports = { + "silent": false, + "istanbulReporter": [ "json-summary", "text"] +} diff --git a/test/integration/projects/tests-folder/buidler.config.js b/test/integration/projects/tests-folder/buidler.config.js new file mode 100644 index 00000000..d1f48df0 --- /dev/null +++ b/test/integration/projects/tests-folder/buidler.config.js @@ -0,0 +1,8 @@ +const { loadPluginFile } = require("@nomiclabs/buidler/plugins-testing"); +loadPluginFile(__dirname + "/../plugins/buidler.plugin"); +usePlugin("@nomiclabs/buidler-truffle5"); + +module.exports={ + defaultNetwork: "buidlerevm", + logger: process.env.SILENT ? { log: () => {} } : console, +}; \ No newline at end of file diff --git a/test/integration/projects/tests-folder/contracts/ContractA.sol b/test/integration/projects/tests-folder/contracts/ContractA.sol new file mode 100644 index 00000000..9d8d1344 --- /dev/null +++ b/test/integration/projects/tests-folder/contracts/ContractA.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.5.0; + + +contract ContractA { + uint x; + constructor() public { + } + + function sendFn() public { + x = 5; + } + + function callFn() public pure returns (uint){ + uint y = 5; + return y; + } +} diff --git a/test/integration/projects/tests-folder/contracts/ContractB.sol b/test/integration/projects/tests-folder/contracts/ContractB.sol new file mode 100644 index 00000000..daa42f7d --- /dev/null +++ b/test/integration/projects/tests-folder/contracts/ContractB.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.5.0; + + +contract ContractB { + uint x; + constructor() public { + } + + function sendFn() public { + x = 5; + } + + function callFn() public pure returns (uint){ + uint y = 5; + return y; + } +} diff --git a/test/integration/projects/tests-folder/contracts/ContractC.sol b/test/integration/projects/tests-folder/contracts/ContractC.sol new file mode 100644 index 00000000..454c86cd --- /dev/null +++ b/test/integration/projects/tests-folder/contracts/ContractC.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.5.0; + + +contract ContractC { + uint x; + constructor() public { + } + + function sendFn() public { + x = 5; + } + + function callFn() public pure returns (uint){ + uint y = 5; + return y; + } +} diff --git a/test/integration/projects/tests-folder/contracts/Migrations.sol b/test/integration/projects/tests-folder/contracts/Migrations.sol new file mode 100644 index 00000000..c378ffb0 --- /dev/null +++ b/test/integration/projects/tests-folder/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity >=0.4.21 <0.6.0; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + constructor() public { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/test/integration/projects/tests-folder/test/contracta.js b/test/integration/projects/tests-folder/test/contracta.js new file mode 100644 index 00000000..cc778027 --- /dev/null +++ b/test/integration/projects/tests-folder/test/contracta.js @@ -0,0 +1,15 @@ +const ContractA = artifacts.require("ContractA"); + +contract("contracta", function(accounts) { + let instance; + + before(async () => instance = await ContractA.new()) + + it('sends [ @skipForCoverage ]', async function(){ + await instance.sendFn(); + }); + + it('calls [ @skipForCoverage ]', async function(){ + await instance.callFn(); + }) +}); diff --git a/test/integration/projects/tests-folder/test/folder/contractb.js b/test/integration/projects/tests-folder/test/folder/contractb.js new file mode 100644 index 00000000..71ebfb7a --- /dev/null +++ b/test/integration/projects/tests-folder/test/folder/contractb.js @@ -0,0 +1,15 @@ +const ContractB = artifacts.require("ContractB"); + +contract("contractB [ @skipForCoverage ]", function(accounts) { + let instance; + + before(async () => instance = await ContractB.new()) + + it('sends', async function(){ + await instance.sendFn(); + }); + + it('calls', async function(){ + await instance.callFn(); + }) +}); diff --git a/test/integration/projects/tests-folder/test/folder/contractc.js b/test/integration/projects/tests-folder/test/folder/contractc.js new file mode 100644 index 00000000..9b3d950d --- /dev/null +++ b/test/integration/projects/tests-folder/test/folder/contractc.js @@ -0,0 +1,20 @@ +const ContractC = artifacts.require("ContractC"); + +contract("contractc", function(accounts) { + let instance; + + before(async () => instance = await ContractC.new()) + + it('sends', async function(){ + await instance.sendFn(); + }); + + it('calls', async function(){ + await instance.callFn(); + }) + + it('sends', async function(){ + await instance.sendFn(); + }); + +}); diff --git a/test/integration/projects/tests-folder/truffle-config.js b/test/integration/projects/tests-folder/truffle-config.js new file mode 100644 index 00000000..b398b071 --- /dev/null +++ b/test/integration/projects/tests-folder/truffle-config.js @@ -0,0 +1,7 @@ +module.exports = { + networks: {}, + mocha: {}, + compilers: { + solc: {} + } +} diff --git a/test/units/truffle/standard.js b/test/units/truffle/standard.js index 436a457e..8a19dd94 100644 --- a/test/units/truffle/standard.js +++ b/test/units/truffle/standard.js @@ -79,6 +79,29 @@ describe('Truffle Plugin: standard use cases', function() { verify.lineCoverage(expected); }); + + it('tests in first layer and in a sub-folder', async function() { + mock.installFullProject('tests-folder'); + await plugin(truffleConfig); + + const expected = [ + { + file: mock.pathToContract(truffleConfig, 'ContractA.sol'), + pct: 100 + }, + { + file: mock.pathToContract(truffleConfig, 'ContractB.sol'), + pct: 100, + }, + { + file: mock.pathToContract(truffleConfig, 'ContractC.sol'), + pct: 100, + }, + ]; + + verify.lineCoverage(expected); + }); + it('with relative path solidity imports', async function() { mock.installFullProject('import-paths'); await plugin(truffleConfig); diff --git a/yarn.lock b/yarn.lock index 5c3e4251..a7ad95dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5038,7 +5038,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -5229,13 +5229,6 @@ node-alias@^1.0.4: chalk "^1.1.1" lodash "^4.2.0" -node-dir@^0.1.17: - version "0.1.17" - resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" - integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU= - dependencies: - minimatch "^3.0.2" - node-emoji@^1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" @@ -6222,6 +6215,13 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" +recursive-readdir@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" + integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== + dependencies: + minimatch "3.0.4" + regenerate@^1.2.1: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"