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);
+ });
+
});