diff --git a/README.md b/README.md index 55a06a6..b43b825 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ npx hardhat coverage [command-options] | Option | Example | Description | |--------------|------------------------------------|--------------------------------| | testfiles | `--testfiles "test/registry/*.ts"` | Test file(s) to run. (Globs must be enclosed by quotes and use [globby matching patterns][38])| +| sources | `--sources myFolder` or `--sources myFile.sol` | Path to *single* folder or file to target for coverage. Path is relative to Hardhat's `paths.sources` (usually `contracts/`) | | solcoverjs | `--solcoverjs ./../.solcover.js` | Relative path from working directory to config. Useful for monorepo packages that share settings. (Path must be "./" prefixed) | | network | `--network development` | Use network settings defined in the Hardhat config | | temp[*][14] | `--temp build` | :warning: **Caution** :warning: Path to a *disposable* folder to store compilation artifacts in. Useful when your test setup scripts include hard-coded paths to a build directory. [More...][14] | diff --git a/plugins/hardhat.plugin.js b/plugins/hardhat.plugin.js index 7bca763..8fbda59 100644 --- a/plugins/hardhat.plugin.js +++ b/plugins/hardhat.plugin.js @@ -97,6 +97,7 @@ task("coverage", "Generates a code coverage report for tests") .addOptionalParam("testfiles", ui.flags.file, "", types.string) .addOptionalParam("solcoverjs", ui.flags.solcoverjs, "", types.string) .addOptionalParam('temp', ui.flags.temp, "", types.string) + .addOptionalParam('sources', ui.flags.sources, "", types.string) .addFlag('matrix', ui.flags.testMatrix) .addFlag('abi', ui.flags.abi) .setAction(async function(args, env){ diff --git a/plugins/resources/nomiclabs.utils.js b/plugins/resources/nomiclabs.utils.js index 0212a54..0c9dd87 100644 --- a/plugins/resources/nomiclabs.utils.js +++ b/plugins/resources/nomiclabs.utils.js @@ -30,8 +30,14 @@ function getTestFilePaths(files){ * @return {HardhatConfig} updated config */ function normalizeConfig(config, args={}){ + let sources; + + (args.sources) + ? sources = path.join(config.paths.sources, args.sources) + : sources = config.paths.sources; + config.workingDir = config.paths.root; - config.contractsDir = config.paths.sources; + config.contractsDir = sources; config.testDir = config.paths.tests; config.artifactsDir = config.paths.artifacts; config.logger = config.logger ? config.logger : {log: null}; diff --git a/plugins/resources/plugin.utils.js b/plugins/resources/plugin.utils.js index 6de507f..a296da0 100644 --- a/plugins/resources/plugin.utils.js +++ b/plugins/resources/plugin.utils.js @@ -131,8 +131,18 @@ function checkContext(config, tempContractsDir, tempArtifactsDir){ // ============================= function assembleFiles(config, skipFiles=[]){ - const targetsPath = path.join(config.contractsDir, '**', '*.{sol,vy}'); - const targets = shell.ls(targetsPath).map(path.normalize); + let targets; + let targetsPath; + + // The targets (contractsDir) could actually be a single named file (OR a folder) + const extName = path.extname(config.contractsDir); + + if (extName.length !== 0) { + targets = [ path.normalize(config.contractsDir) ]; + } else { + targetsPath = path.join(config.contractsDir, '**', '*.{sol,vy}'); + targets = shell.ls(targetsPath).map(path.normalize); + } skipFiles = assembleSkipped(config, targets, skipFiles); diff --git a/test/integration/projects/test-files/contracts/otherContracts/OtherContractA.sol b/test/integration/projects/test-files/contracts/otherContracts/OtherContractA.sol new file mode 100644 index 0000000..119c9f7 --- /dev/null +++ b/test/integration/projects/test-files/contracts/otherContracts/OtherContractA.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.7.0; + + +contract OtherContractA { + 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/test-files/test/other_contract_a.js b/test/integration/projects/test-files/test/other_contract_a.js new file mode 100644 index 0000000..4d78abf --- /dev/null +++ b/test/integration/projects/test-files/test/other_contract_a.js @@ -0,0 +1,15 @@ +const OtherContractA = artifacts.require("OtherContractA"); + +contract("otherContractA", function(accounts) { + let instance; + + before(async () => instance = await OtherContractA.new()) + + it('sends', async function(){ + await instance.sendFn(); + }); + + it('calls', async function(){ + await instance.callFn(); + }) +}); diff --git a/test/units/hardhat/flags.js b/test/units/hardhat/flags.js index c044f5a..f876555 100644 --- a/test/units/hardhat/flags.js +++ b/test/units/hardhat/flags.js @@ -230,5 +230,69 @@ describe('Hardhat Plugin: command line options', function() { const output = require(outputPath); assert.deepEqual(output, expected); }) + + it('--sources folder', async function() { + + const taskArgs = { + testfiles: path.join(hardhatConfig.paths.root, 'test/other_contract_a.js'), + sources: 'otherContracts' + }; + mock.installFullProject('test-files'); + mock.hardhatSetupEnv(this); + + await this.env.run("coverage", taskArgs); + + const expected = [ + { + file: mock.pathToContract(hardhatConfig, 'otherContracts/OtherContractA.sol'), + pct: 100 + } + ]; + + verify.lineCoverage(expected); + }); + + it('--sources folder/', async function() { + + const taskArgs = { + testfiles: path.join(hardhatConfig.paths.root, 'test/other_contract_a.js'), + sources: 'otherContracts/' + }; + mock.installFullProject('test-files'); + mock.hardhatSetupEnv(this); + + await this.env.run("coverage", taskArgs); + + const expected = [ + { + file: mock.pathToContract(hardhatConfig, 'otherContracts/OtherContractA.sol'), + pct: 100 + } + ]; + + verify.lineCoverage(expected); + }); + + it('--sources folder/filename.sol', async function() { + + const taskArgs = { + testfiles: path.join(hardhatConfig.paths.root, 'test/other_contract_a.js'), + sources: 'otherContracts/OtherContractA.sol' + }; + mock.installFullProject('test-files'); + mock.hardhatSetupEnv(this); + + await this.env.run("coverage", taskArgs); + + const expected = [ + { + file: mock.pathToContract(hardhatConfig, 'otherContracts/OtherContractA.sol'), + pct: 100 + } + ]; + + verify.lineCoverage(expected); + }); + });