add embark solidity tests package (not ready yet)

This commit is contained in:
Andre Medeiros 2019-08-13 14:21:09 -04:00
parent a8e71e3528
commit 5736dc9498
4 changed files with 287 additions and 2 deletions

View File

@ -0,0 +1,64 @@
{
"name": "embark-solidity-tests",
"version": "5.0.0",
"description": "Plugin to run Embark solidity tests",
"main": "dist/lib/index.js",
"repository": {
"directory": "packages/embark-solidity-tests",
"type": "git",
"url": "https://github.com/embark-framework/embark/"
},
"author": "Iuri Matias <iuri.matias@gmail.com>",
"license": "MIT",
"bugs": "https://github.com/embark-framework/embark/issues",
"keywords": [
"blockchain",
"dapps",
"ethereum",
"ipfs",
"serverless",
"solc",
"solidity"
],
"files": [
"dist"
],
"scripts": {
"build": "cross-env BABEL_ENV=node babel src --extensions \".js\" --out-dir dist --root-mode upward --source-maps",
"ci": "npm run qa",
"clean": "npm run reset",
"lint": "npm-run-all lint:*",
"lint:js": "eslint src/",
"// lint:ts": "tslint -c tslint.json \"src/**/*.ts\"",
"package": "npm pack",
"// qa": "npm-run-all lint typecheck build package",
"qa": "npm-run-all lint build package",
"reset": "npx rimraf dist embark-*.tgz package",
"start": "npm run watch",
"// typecheck": "tsc",
"watch": "run-p watch:*",
"watch:build": "npm run build -- --verbose --watch",
"// watch:typecheck": "npm run typecheck -- --preserveWatchOutput --watch",
"test": "mocha dist/test/**/*.js"
},
"eslintConfig": {
"extends": "../../.eslintrc.json"
},
"devDependencies": {
"@babel/cli": "7.2.3",
"@babel/core": "7.2.2",
"@types/async": "2.0.50",
"cross-env": "5.2.0",
"eslint": "5.7.0",
"mocha": "6.2.0",
"npm-run-all": "4.1.5",
"rimraf": "2.6.3",
"tslint": "5.16.0",
"typescript": "3.4.5"
},
"engines": {
"node": ">=8.12.0 <12.0.0",
"npm": ">=6.4.1",
"yarn": ">=1.12.3"
}
}

View File

@ -0,0 +1,99 @@
const async = require("async");
const fs = require("fs");
const {
File,
Types
} = require("embark-utils");
const SOLIDITY_TEST_MATCH = /^.+_test\.sol$/i;
const PRAGMA_MATCH = /^pragma solidity (.*);$/mi;
const LIBRARY_NAME_MATCH = /remix_tests\.sol/i;
const IMPORT_REMIX_TESTS_STMT = `
// - INJECTED BY EMBARK -
import "remix_tests.sol";
// ----------------------
`;
class SolidityTestRunner {
constructor(embark, _options) {
this.embark = embark;
this.events = embark.events;
this.files = new Set();
this.events.request("tests:runner:register",
"Solidity",
this.match.bind(this),
this.addFile.bind(this),
this.run.bind(this));
}
addFile(path) {
if (!this.match(path)) {
throw new Error(`invalid Solidity test path: ${path}`);
}
this.files.add(path);
}
match(path) {
return SOLIDITY_TEST_MATCH.test(path);
}
run() {
const {events} = this;
let web3;
let contractFiles = [];
let compiledContracts = [];
this.files.forEach(f => {
const resolver = (cb) => {
fs.readFile(file, (err, data) => { cb(data.toString()); });
};
const file = new File({
path: f,
originalPath: f,
type: Types.dappFile,
resolver
});
contractFiles.add(file);
});
async.waterfall([
(next) => {
events.request("compiler:contracts:compile", contractFiles, next);
},
(cc, next) => {
events.request("contracts:build", {}, cc, next);
},
(contractsList, contractsDeps, next) => {
events.request("deployment:contracts:deploy", contractsList, contractDeps, next);
},
(next) => {
events.request("blockchain:client:provider", "ethereum", next);
},
(bcProvider, next) => {
web3 = new Web3(bcProvider);
},
]);
}
prepare(contents) {
if (LIBRARY_NAME_MATCH.test(contents)) {
return contents;
}
const start = contents.search(PRAGMA_MATCH);
const offset = contents.indexOf("\n", start);
// Here we offset the offset + 1 so that it doesn't double newline.
return contents.slice(0, offset) + IMPORT_REMIX_TESTS_STMT + contents.slice(offset + 1, contents.length);
}
}
module.exports = SolidityTestRunner;

View File

@ -0,0 +1,115 @@
/* globals beforeEach, describe, it */
const assert = require("assert").strict;
const sinon = require("sinon");
const SolidityTestRunner = require("../lib");
const validFiles = [
"SOME_UPPERCASE_TEST.sol",
"other_uppercase_test.SOL",
"some_file_test.sol",
"file_with/path_test.sol",
"c:\projects\cool-dapp\test\escrow_test.sol"
];
const invalidFiles = [
"escrow.sol",
"tricky.sol.js",
"somesol.js",
"c:\projects\cool-dapp\file.sol\test_file.md"
];
const cleanCode = (code) => {
return code
.trim()
.split("\n")
.map(l => l.replace(/^\s+/, ''))
.join("\n");
};
describe("SolidityTestRunner", () => {
let embark;
beforeEach(() => {
const events = { request: sinon.spy() };
embark = { events: events };
});
describe("constructor", () => {
it("registers the runner", () => {
const _ = new SolidityTestRunner(embark);
assert(embark.events.request.called);
});
});
describe("methods", () => {
let instance;
beforeEach(() => { instance = new SolidityTestRunner(embark); });
describe("match", () => {
it("matches .sol files", () => {
validFiles.forEach(f => {
assert(instance.match(f), `didn't match ${f} when it should`);
});
});
it("doesn't match non .sol files", () => {
invalidFiles.forEach(f => {
assert(!instance.match(f), `matched ${f} when it shouldn't`);
});
});
});
describe("prepare", () => {
it("doesn't mutate code with the import statement", () => {
const code = cleanCode(`
pragma solidity ^0.4.24;
import "remix_tests.sol";
import "escrow.sol";
contract Foo {}
`);
const modified = instance.prepare(code);
assert.equal(code, modified);
});
it("mutates code without the import statement", () => {
const code = cleanCode(`
pragma solidity ^0.4.24;
import "escrow.sol";
contract Foo {}
`);
const wanted = cleanCode(`
pragma solidity ^0.4.24;
// - INJECTED BY EMBARK -
import "remix_tests.sol";
// ----------------------
import "escrow.sol";
contract Foo {}
`);
const modified = instance.prepare(code);
assert.equal(wanted, modified);
});
});
describe("addFile", () => {
it("throws an exception with invalid files", () => {
invalidFiles.forEach(f => {
assert.throws(() => { instance.addFile(f); });
});
});
it("remembers files that are added", () => {
validFiles.forEach(f => instance.addFile(f));
assert.deepEqual(instance.files, new Set(validFiles));
});
});
describe("run", () => {
});
});
});

View File

@ -21,12 +21,16 @@ class TestRunner {
this.events = embark.events;
this.fs = embark.fs;
this.ipc = options.ipc;
this.runResults = [];
this.runners = {};
this.gasLimit = options.coverage ? COVERAGE_GAS_LIMIT : GAS_LIMIT;
this.events.setCommandHandler('tests:run', (options, callback) => {
this.run(options, callback);
});
this.events.setCommandHandler('tests:runner:register', (name, glob, addFn, runFn) => {
this.runners[name] = {glob, addFn, runFn};
});
}
run(options, cb) {
@ -45,6 +49,7 @@ class TestRunner {
self.getFilesFromDir(testPath, next);
},
(files, next) => { // group files by types
// TODO: figure out what files belong where
const types = { jsFiles: ".js", solidityFiles: "_test.sol" };
const groups = Object.entries(types).reduce((acc, [type, ext]) => {
acc[type] = files.filter(f => f.endsWith(ext));
@ -168,7 +173,8 @@ class TestRunner {
next();
}
], (_err) => {
acctCb(accounts);
console.log('=====================> finished config');
acctCb(null, accounts);
done();
});
});
@ -200,6 +206,7 @@ class TestRunner {
},
(cf, next) => { // compile contracts
console.log('compiling contracts');
console.dir(cf);
events.request("compiler:contracts:compile", cf, next);
},
(cc, next) => { // override require