Merge pull request #1128 from 0mkara/remix-tests-ts
Remix Tests Typescript implementationpull/7/head
commit
af9aae696e
@ -1,4 +1,4 @@ |
|||||||
#!/usr/bin/env node |
#!/usr/bin/env node |
||||||
|
|
||||||
require('../src/run.js'); |
require('../dist/run.js'); |
||||||
|
|
||||||
|
@ -1,129 +0,0 @@ |
|||||||
/* eslint no-extend-native: "warn" */ |
|
||||||
let fs = require('./fs') |
|
||||||
var async = require('async') |
|
||||||
var path = require('path') |
|
||||||
let RemixCompiler = require('remix-solidity').Compiler |
|
||||||
|
|
||||||
String.prototype.regexIndexOf = function (regex, startpos) { // eslint-disable-line
|
|
||||||
var indexOf = this.substring(startpos || 0).search(regex) |
|
||||||
return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf |
|
||||||
} |
|
||||||
|
|
||||||
function writeTestAccountsContract (accounts) { |
|
||||||
var testAccountContract = require('../sol/tests_accounts.sol.js') |
|
||||||
var body = 'address[' + accounts.length + '] memory accounts;' |
|
||||||
if (!accounts.length) body += ';' |
|
||||||
else { |
|
||||||
accounts.map((address, index) => { |
|
||||||
body += `\naccounts[${index}] = ${address};\n` |
|
||||||
}) |
|
||||||
} |
|
||||||
return testAccountContract.replace('>accounts<', body) |
|
||||||
} |
|
||||||
|
|
||||||
var userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-' |
|
||||||
var isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' electron/') > -1) |
|
||||||
|
|
||||||
// TODO: replace this with remix's own compiler code
|
|
||||||
function compileFileOrFiles (filename, isDirectory, opts, cb) { |
|
||||||
let compiler |
|
||||||
let accounts = opts.accounts || [] |
|
||||||
const sources = { |
|
||||||
'tests.sol': { content: require('../sol/tests.sol.js') }, |
|
||||||
'remix_tests.sol': { content: require('../sol/tests.sol.js') }, |
|
||||||
'remix_accounts.sol': { content: writeTestAccountsContract(accounts) } |
|
||||||
} |
|
||||||
const filepath = (isDirectory ? filename : path.dirname(filename)) |
|
||||||
// TODO: for now assumes filepath dir contains all tests, later all this
|
|
||||||
// should be replaced with remix's & browser solidity compiler code
|
|
||||||
|
|
||||||
// This logic is wrong
|
|
||||||
// We should only look into current file if a full file name with path is given
|
|
||||||
// We should only walk through directory if a directory name is passed
|
|
||||||
try { |
|
||||||
// walkSync only if it is a directory
|
|
||||||
fs.walkSync(filepath, foundpath => { |
|
||||||
// only process .sol files
|
|
||||||
if (foundpath.split('.').pop() === 'sol') { |
|
||||||
let c = fs.readFileSync(foundpath).toString() |
|
||||||
const s = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm |
|
||||||
let includeTestLibs = '\nimport \'remix_tests.sol\';\n' |
|
||||||
if (foundpath.indexOf('_test.sol') > 0 && c.regexIndexOf(s) < 0) { |
|
||||||
c = includeTestLibs.concat(c) |
|
||||||
} |
|
||||||
sources[foundpath] = { content: c } |
|
||||||
} |
|
||||||
}) |
|
||||||
} catch (e) { |
|
||||||
throw e |
|
||||||
} finally { |
|
||||||
async.waterfall([ |
|
||||||
function loadCompiler (next) { |
|
||||||
compiler = new RemixCompiler() |
|
||||||
compiler.onInternalCompilerLoaded() |
|
||||||
// compiler.event.register('compilerLoaded', this, function (version) {
|
|
||||||
next() |
|
||||||
// });
|
|
||||||
}, |
|
||||||
function doCompilation (next) { |
|
||||||
compiler.event.register('compilationFinished', this, function (success, data, source) { |
|
||||||
next(null, data) |
|
||||||
}) |
|
||||||
compiler.compile(sources, filepath) |
|
||||||
} |
|
||||||
], function (err, result) { |
|
||||||
let errors = (result.errors || []).filter((e) => e.type === 'Error' || e.severity === 'error') |
|
||||||
if (errors.length > 0) { |
|
||||||
if (!isBrowser) require('signale').fatal(errors) |
|
||||||
return cb(new Error('errors compiling')) |
|
||||||
} |
|
||||||
cb(err, result.contracts) |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function compileContractSources (sources, importFileCb, opts, cb) { |
|
||||||
let compiler, filepath |
|
||||||
let accounts = opts.accounts || [] |
|
||||||
// Iterate over sources keys. Inject test libraries. Inject test library import statements.
|
|
||||||
if (!('remix_tests.sol' in sources) && !('tests.sol' in sources)) { |
|
||||||
sources['remix_tests.sol'] = { content: require('../sol/tests.sol.js') } |
|
||||||
sources['remix_accounts.sol'] = { content: writeTestAccountsContract(accounts) } |
|
||||||
} |
|
||||||
const s = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm |
|
||||||
let includeTestLibs = '\nimport \'remix_tests.sol\';\n' |
|
||||||
for (let file in sources) { |
|
||||||
const c = sources[file].content |
|
||||||
if (file.indexOf('_test.sol') > 0 && c && c.regexIndexOf(s) < 0) { |
|
||||||
sources[file].content = includeTestLibs.concat(c) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
async.waterfall([ |
|
||||||
function loadCompiler (next) { |
|
||||||
compiler = new RemixCompiler(importFileCb) |
|
||||||
compiler.onInternalCompilerLoaded() |
|
||||||
// compiler.event.register('compilerLoaded', this, function (version) {
|
|
||||||
next() |
|
||||||
// });
|
|
||||||
}, |
|
||||||
function doCompilation (next) { |
|
||||||
compiler.event.register('compilationFinished', this, function (success, data, source) { |
|
||||||
next(null, data) |
|
||||||
}) |
|
||||||
compiler.compile(sources, filepath) |
|
||||||
} |
|
||||||
], function (err, result) { |
|
||||||
let errors = (result.errors || []).filter((e) => e.type === 'Error' || e.severity === 'error') |
|
||||||
if (errors.length > 0) { |
|
||||||
if (!isBrowser) require('signale').fatal(errors) |
|
||||||
return cb(new Error('errors compiling')) |
|
||||||
} |
|
||||||
cb(err, result.contracts) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = { |
|
||||||
compileFileOrFiles: compileFileOrFiles, |
|
||||||
compileContractSources: compileContractSources |
|
||||||
} |
|
@ -0,0 +1,126 @@ |
|||||||
|
import fs from './fileSystem' |
||||||
|
import async from 'async' |
||||||
|
import path from 'path' |
||||||
|
let RemixCompiler = require('remix-solidity').Compiler |
||||||
|
import { SrcIfc } from './types' |
||||||
|
|
||||||
|
function regexIndexOf (inputString: string, regex: RegExp, startpos: number = 0) { |
||||||
|
const indexOf = inputString.substring(startpos).search(regex) |
||||||
|
return (indexOf >= 0) ? (indexOf + (startpos)) : indexOf |
||||||
|
} |
||||||
|
|
||||||
|
function writeTestAccountsContract (accounts: string[]) { |
||||||
|
const testAccountContract = require('../sol/tests_accounts.sol.js') |
||||||
|
let body = `address[${accounts.length}] memory accounts;` |
||||||
|
if (!accounts.length) body += ';' |
||||||
|
else { |
||||||
|
accounts.map((address, index) => { |
||||||
|
body += `\naccounts[${index}] = ${address};\n` |
||||||
|
}) |
||||||
|
} |
||||||
|
return testAccountContract.replace('>accounts<', body) |
||||||
|
} |
||||||
|
|
||||||
|
const userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-' |
||||||
|
const isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' electron/') > -1) |
||||||
|
|
||||||
|
// TODO: replace this with remix's own compiler code
|
||||||
|
export function compileFileOrFiles(filename: string, isDirectory: boolean, opts: any, cb: Function) { |
||||||
|
let compiler: any |
||||||
|
let accounts = opts.accounts || [] |
||||||
|
const sources = { |
||||||
|
'tests.sol': { content: require('../sol/tests.sol.js') }, |
||||||
|
'remix_tests.sol': { content: require('../sol/tests.sol.js') }, |
||||||
|
'remix_accounts.sol': { content: writeTestAccountsContract(accounts) } |
||||||
|
} |
||||||
|
const filepath = (isDirectory ? filename : path.dirname(filename)) |
||||||
|
// TODO: for now assumes filepath dir contains all tests, later all this
|
||||||
|
// should be replaced with remix's & browser solidity compiler code
|
||||||
|
|
||||||
|
// This logic is wrong
|
||||||
|
// We should only look into current file if a full file name with path is given
|
||||||
|
// We should only walk through directory if a directory name is passed
|
||||||
|
try { |
||||||
|
// walkSync only if it is a directory
|
||||||
|
fs.walkSync(filepath, (foundpath: string) => { |
||||||
|
// only process .sol files
|
||||||
|
if (foundpath.split('.').pop() === 'sol') { |
||||||
|
let c = fs.readFileSync(foundpath).toString() |
||||||
|
const s = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm |
||||||
|
let includeTestLibs = '\nimport \'remix_tests.sol\';\n' |
||||||
|
if (foundpath.indexOf('_test.sol') > 0 && regexIndexOf(c, s) < 0) { |
||||||
|
c = includeTestLibs.concat(c) |
||||||
|
} |
||||||
|
sources[foundpath] = { content: c } |
||||||
|
} |
||||||
|
}) |
||||||
|
} catch (e) { |
||||||
|
throw e |
||||||
|
} finally { |
||||||
|
async.waterfall([ |
||||||
|
function loadCompiler(next: Function) { |
||||||
|
compiler = new RemixCompiler() |
||||||
|
compiler.onInternalCompilerLoaded() |
||||||
|
// compiler.event.register('compilerLoaded', this, function (version) {
|
||||||
|
next() |
||||||
|
// });
|
||||||
|
}, |
||||||
|
function doCompilation(next: Function) { |
||||||
|
// @ts-ignore
|
||||||
|
compiler.event.register('compilationFinished', this, (success, data, source) => { |
||||||
|
next(null, data) |
||||||
|
}) |
||||||
|
compiler.compile(sources, filepath) |
||||||
|
} |
||||||
|
], function (err: Error | null | undefined, result: any) { |
||||||
|
let errors = (result.errors || []).filter((e) => e.type === 'Error' || e.severity === 'error') |
||||||
|
if (errors.length > 0) { |
||||||
|
if (!isBrowser) require('signale').fatal(errors) |
||||||
|
return cb(new Error('errors compiling')) |
||||||
|
} |
||||||
|
cb(err, result.contracts) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export function compileContractSources(sources: SrcIfc, importFileCb: any, opts: any, cb: Function) { |
||||||
|
let compiler, filepath: string |
||||||
|
let accounts = opts.accounts || [] |
||||||
|
// Iterate over sources keys. Inject test libraries. Inject test library import statements.
|
||||||
|
if (!('remix_tests.sol' in sources) && !('tests.sol' in sources)) { |
||||||
|
sources['remix_tests.sol'] = { content: require('../sol/tests.sol.js') } |
||||||
|
sources['remix_accounts.sol'] = { content: writeTestAccountsContract(accounts) } |
||||||
|
} |
||||||
|
const s = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm |
||||||
|
let includeTestLibs = '\nimport \'remix_tests.sol\';\n' |
||||||
|
for (let file in sources) { |
||||||
|
const c = sources[file].content |
||||||
|
if (file.indexOf('_test.sol') > 0 && c && regexIndexOf(c, s) < 0) { |
||||||
|
sources[file].content = includeTestLibs.concat(c) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
async.waterfall([ |
||||||
|
function loadCompiler (next: Function) { |
||||||
|
compiler = new RemixCompiler(importFileCb) |
||||||
|
compiler.onInternalCompilerLoaded() |
||||||
|
// compiler.event.register('compilerLoaded', this, function (version) {
|
||||||
|
next() |
||||||
|
// });
|
||||||
|
}, |
||||||
|
function doCompilation (next: Function) { |
||||||
|
// @ts-ignore
|
||||||
|
compiler.event.register('compilationFinished', this, (success, data, source) => { |
||||||
|
next(null, data) |
||||||
|
}) |
||||||
|
compiler.compile(sources, filepath) |
||||||
|
} |
||||||
|
], function (err: Error | null | undefined , result: any) { |
||||||
|
let errors = (result.errors || []).filter((e) => e.type === 'Error' || e.severity === 'error') |
||||||
|
if (errors.length > 0) { |
||||||
|
if (!isBrowser) require('signale').fatal(errors) |
||||||
|
return cb(new Error('errors compiling')) |
||||||
|
} |
||||||
|
cb(err, result.contracts) |
||||||
|
}) |
||||||
|
} |
@ -1,109 +0,0 @@ |
|||||||
var async = require('async') |
|
||||||
var remixLib = require('remix-lib') |
|
||||||
|
|
||||||
function deployAll (compileResult, web3, callback) { |
|
||||||
let compiledObject = {} |
|
||||||
let contracts = {} |
|
||||||
let accounts = [] |
|
||||||
|
|
||||||
async.waterfall([ |
|
||||||
function getAccountList (next) { |
|
||||||
web3.eth.getAccounts((_err, _accounts) => { |
|
||||||
accounts = _accounts |
|
||||||
next() |
|
||||||
}) |
|
||||||
}, |
|
||||||
function getContractData (next) { |
|
||||||
for (let contractFile in compileResult) { |
|
||||||
for (let contractName in compileResult[contractFile]) { |
|
||||||
let contract = compileResult[contractFile][contractName] |
|
||||||
|
|
||||||
const className = contractName |
|
||||||
const filename = contractFile |
|
||||||
|
|
||||||
let abi = contract.abi |
|
||||||
let code = contract.evm.bytecode.object |
|
||||||
|
|
||||||
compiledObject[className] = {} |
|
||||||
compiledObject[className].abi = abi |
|
||||||
compiledObject[className].code = code |
|
||||||
compiledObject[className].filename = filename |
|
||||||
compiledObject[className].className = className |
|
||||||
compiledObject[className].raw = contract |
|
||||||
|
|
||||||
if (contractFile.indexOf('_test.sol') >= 0) { |
|
||||||
compiledObject[className].isTest = true |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
next() |
|
||||||
}, |
|
||||||
function determineContractsToDeploy (next) { |
|
||||||
let contractsToDeploy = ['Assert'] |
|
||||||
let allContracts = Object.keys(compiledObject) |
|
||||||
|
|
||||||
for (let contractName of allContracts) { |
|
||||||
if (contractName === 'Assert') { |
|
||||||
continue |
|
||||||
} |
|
||||||
if (compiledObject[contractName].isTest) { |
|
||||||
contractsToDeploy.push(contractName) |
|
||||||
} |
|
||||||
} |
|
||||||
next(null, contractsToDeploy) |
|
||||||
}, |
|
||||||
function deployContracts (contractsToDeploy, next) { |
|
||||||
var deployRunner = (deployObject, contractObject, contractName, filename, callback) => { |
|
||||||
deployObject.estimateGas().then((gasValue) => { |
|
||||||
deployObject.send({ |
|
||||||
from: accounts[0], |
|
||||||
gas: Math.ceil(gasValue * 1.2) |
|
||||||
}).on('receipt', function (receipt) { |
|
||||||
contractObject.options.address = receipt.contractAddress |
|
||||||
contractObject.options.from = accounts[0] |
|
||||||
contractObject.options.gas = 5000 * 1000 |
|
||||||
compiledObject[contractName].deployedAddress = receipt.contractAddress |
|
||||||
|
|
||||||
contracts[contractName] = contractObject |
|
||||||
contracts[contractName].filename = filename |
|
||||||
|
|
||||||
callback(null, { result: { createdAddress: receipt.contractAddress } }) // TODO this will only work with JavaScriptV VM
|
|
||||||
}).on('error', function (err) { |
|
||||||
console.dir(err) |
|
||||||
callback(err) |
|
||||||
}) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
async.eachOfLimit(contractsToDeploy, 1, function (contractName, index, nextEach) { |
|
||||||
let contract = compiledObject[contractName] |
|
||||||
let encodeDataFinalCallback = (error, contractDeployData) => { |
|
||||||
if (error) return nextEach(error) |
|
||||||
let contractObject = new web3.eth.Contract(contract.abi) |
|
||||||
let deployObject = contractObject.deploy({arguments: [], data: '0x' + contractDeployData.dataHex}) |
|
||||||
deployRunner(deployObject, contractObject, contractName, contract.filename, (error) => { nextEach(error) }) |
|
||||||
} |
|
||||||
|
|
||||||
let encodeDataStepCallback = (msg) => { console.dir(msg) } |
|
||||||
|
|
||||||
let encodeDataDeployLibraryCallback = (libData, callback) => { |
|
||||||
let abi = compiledObject[libData.data.contractName].abi |
|
||||||
let code = compiledObject[libData.data.contractName].code |
|
||||||
let libraryObject = new web3.eth.Contract(abi) |
|
||||||
let deployObject = libraryObject.deploy({arguments: [], data: '0x' + code}) |
|
||||||
deployRunner(deployObject, libraryObject, libData.data.contractName, contract.filename, callback) |
|
||||||
} |
|
||||||
|
|
||||||
let funAbi = null // no need to set the abi for encoding the constructor
|
|
||||||
let params = '' // we suppose that the test contract does not have any param in the constructor
|
|
||||||
remixLib.execution.txFormat.encodeConstructorCallAndDeployLibraries(contractName, contract.raw, compileResult, params, funAbi, encodeDataFinalCallback, encodeDataStepCallback, encodeDataDeployLibraryCallback) |
|
||||||
}, function () { |
|
||||||
next(null, contracts) |
|
||||||
}) |
|
||||||
} |
|
||||||
], callback) |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = { |
|
||||||
deployAll: deployAll |
|
||||||
} |
|
@ -0,0 +1,110 @@ |
|||||||
|
import async from 'async' |
||||||
|
var remixLib = require('remix-lib') |
||||||
|
import Web3 from 'web3' |
||||||
|
|
||||||
|
export function deployAll(compileResult: object, web3: Web3, callback) { |
||||||
|
let compiledObject = {} |
||||||
|
let contracts = {} |
||||||
|
let accounts: string[] = [] |
||||||
|
|
||||||
|
async.waterfall([ |
||||||
|
function getAccountList(next: Function) { |
||||||
|
web3.eth.getAccounts((_err, _accounts) => { |
||||||
|
accounts = _accounts |
||||||
|
next() |
||||||
|
}) |
||||||
|
}, |
||||||
|
function getContractData(next: Function) { |
||||||
|
for (let contractFile in compileResult) { |
||||||
|
for (let contractName in compileResult[contractFile]) { |
||||||
|
let contract = compileResult[contractFile][contractName] |
||||||
|
|
||||||
|
const className = contractName |
||||||
|
const filename = contractFile |
||||||
|
|
||||||
|
let abi = contract.abi |
||||||
|
let code = contract.evm.bytecode.object |
||||||
|
|
||||||
|
compiledObject[className] = {} |
||||||
|
compiledObject[className].abi = abi |
||||||
|
compiledObject[className].code = code |
||||||
|
compiledObject[className].filename = filename |
||||||
|
compiledObject[className].className = className |
||||||
|
compiledObject[className].raw = contract |
||||||
|
|
||||||
|
if (contractFile.indexOf('_test.sol') >= 0) { |
||||||
|
compiledObject[className].isTest = true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
next() |
||||||
|
}, |
||||||
|
function determineContractsToDeploy(next: Function) { |
||||||
|
let contractsToDeploy: string[] = ['Assert'] |
||||||
|
let allContracts = Object.keys(compiledObject) |
||||||
|
|
||||||
|
for (let contractName of allContracts) { |
||||||
|
if (contractName === 'Assert') { |
||||||
|
continue |
||||||
|
} |
||||||
|
if (compiledObject[contractName].isTest) { |
||||||
|
contractsToDeploy.push(contractName) |
||||||
|
} |
||||||
|
} |
||||||
|
next(null, contractsToDeploy) |
||||||
|
}, |
||||||
|
function deployContracts(contractsToDeploy: string[], next: Function) { |
||||||
|
const deployRunner = (deployObject, contractObject, contractName, filename, callback) => { |
||||||
|
deployObject.estimateGas().then((gasValue) => { |
||||||
|
deployObject.send({ |
||||||
|
from: accounts[0], |
||||||
|
gas: Math.ceil(gasValue * 1.2) |
||||||
|
}).on('receipt', function (receipt) { |
||||||
|
contractObject.options.address = receipt.contractAddress |
||||||
|
contractObject.options.from = accounts[0] |
||||||
|
contractObject.options.gas = 5000 * 1000 |
||||||
|
compiledObject[contractName].deployedAddress = receipt.contractAddress |
||||||
|
|
||||||
|
contracts[contractName] = contractObject |
||||||
|
contracts[contractName].filename = filename |
||||||
|
|
||||||
|
callback(null, { result: { createdAddress: receipt.contractAddress } }) // TODO this will only work with JavaScriptV VM
|
||||||
|
}).on('error', function (err) { |
||||||
|
console.error(err) |
||||||
|
callback(err) |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
async.eachOfLimit(contractsToDeploy, 1, function (contractName, index, nextEach) { |
||||||
|
let contract = compiledObject[contractName] |
||||||
|
let encodeDataFinalCallback = (error, contractDeployData) => { |
||||||
|
if (error) return nextEach(error) |
||||||
|
try { |
||||||
|
let contractObject = new web3.eth.Contract(contract.abi) |
||||||
|
let deployObject = contractObject.deploy({arguments: [], data: '0x' + contractDeployData.dataHex}) |
||||||
|
deployRunner(deployObject, contractObject, contractName, contract.filename, (error) => { nextEach(error) }) |
||||||
|
} catch (e) { |
||||||
|
throw e |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let encodeDataStepCallback = (msg) => { console.dir(msg) } |
||||||
|
|
||||||
|
let encodeDataDeployLibraryCallback = (libData, callback) => { |
||||||
|
let abi = compiledObject[libData.data.contractName].abi |
||||||
|
let code = compiledObject[libData.data.contractName].code |
||||||
|
let libraryObject = new web3.eth.Contract(abi) |
||||||
|
let deployObject = libraryObject.deploy({arguments: [], data: '0x' + code}) |
||||||
|
deployRunner(deployObject, libraryObject, libData.data.contractName, contract.filename, callback) |
||||||
|
} |
||||||
|
|
||||||
|
let funAbi = null // no need to set the abi for encoding the constructor
|
||||||
|
let params = '' // we suppose that the test contract does not have any param in the constructor
|
||||||
|
remixLib.execution.txFormat.encodeConstructorCallAndDeployLibraries(contractName, contract.raw, compileResult, params, funAbi, encodeDataFinalCallback, encodeDataStepCallback, encodeDataDeployLibraryCallback) |
||||||
|
}, function () { |
||||||
|
next(null, contracts) |
||||||
|
}) |
||||||
|
} |
||||||
|
], callback) |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
// Extend fs
|
||||||
|
let fs: any = require('fs') |
||||||
|
import path from 'path' |
||||||
|
|
||||||
|
// https://github.com/mikeal/node-utils/blob/master/file/lib/main.js
|
||||||
|
fs.walkSync = function (start: string, callback: Function) { |
||||||
|
fs.readdirSync(start).forEach((name: string) => { |
||||||
|
if (name === 'node_modules') { |
||||||
|
return // hack
|
||||||
|
} |
||||||
|
const abspath = path.join(start, name) |
||||||
|
if (fs.statSync(abspath).isDirectory()) { |
||||||
|
fs.walkSync(abspath, callback) |
||||||
|
} else { |
||||||
|
callback(abspath) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
export = fs |
@ -1,20 +0,0 @@ |
|||||||
// Extend fs
|
|
||||||
var fs = require('fs') |
|
||||||
const path = require('path') |
|
||||||
|
|
||||||
// https://github.com/mikeal/node-utils/blob/master/file/lib/main.js
|
|
||||||
fs.walkSync = function (start, callback) { |
|
||||||
fs.readdirSync(start).forEach(name => { |
|
||||||
if (name === 'node_modules') { |
|
||||||
return // hack
|
|
||||||
} |
|
||||||
var abspath = path.join(start, name) |
|
||||||
if (fs.statSync(abspath).isDirectory()) { |
|
||||||
fs.walkSync(abspath, callback) |
|
||||||
} else { |
|
||||||
callback(abspath) |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = fs |
|
@ -1,10 +0,0 @@ |
|||||||
const runTestFiles = require('./runTestFiles.js') |
|
||||||
const runTestSources = require('./runTestSources.js') |
|
||||||
const TestRunner = require('./testRunner.js') |
|
||||||
|
|
||||||
module.exports = { |
|
||||||
runTestFiles: runTestFiles, |
|
||||||
runTestSources: runTestSources, |
|
||||||
runTest: TestRunner.runTest, |
|
||||||
assertLibCode: require('../sol/tests.sol.js') |
|
||||||
} |
|
@ -0,0 +1,5 @@ |
|||||||
|
export { runTestFiles } from './runTestFiles' |
||||||
|
export { runTestSources } from './runTestSources' |
||||||
|
export { runTest } from './testRunner' |
||||||
|
export * from './types' |
||||||
|
export { assertLibCode } from '../sol/tests.sol.js' |
@ -1,58 +0,0 @@ |
|||||||
var gray = require('ansi-gray') |
|
||||||
const winston = require('winston') |
|
||||||
var timestamp = require('time-stamp') |
|
||||||
var supportsColor = require('color-support') |
|
||||||
|
|
||||||
function hasFlag (flag) { |
|
||||||
return ((typeof (process) !== 'undefined') && (process.argv.indexOf('--' + flag) !== -1)) |
|
||||||
} |
|
||||||
|
|
||||||
function addColor (str) { |
|
||||||
if (hasFlag('no-color')) { |
|
||||||
return str |
|
||||||
} |
|
||||||
|
|
||||||
if (hasFlag('color')) { |
|
||||||
return gray(str) |
|
||||||
} |
|
||||||
|
|
||||||
if (supportsColor()) { |
|
||||||
return gray(str) |
|
||||||
} |
|
||||||
|
|
||||||
return str |
|
||||||
} |
|
||||||
function getTimestamp () { |
|
||||||
return '[' + addColor(timestamp('HH:mm:ss')) + ']' |
|
||||||
} |
|
||||||
// create winston logger format
|
|
||||||
const logFmt = winston.format.printf((info) => { |
|
||||||
return `${getTimestamp()} ${info.level}: ${info.message}` |
|
||||||
}) |
|
||||||
|
|
||||||
class Log { |
|
||||||
constructor () { |
|
||||||
this.logger = winston.createLogger({ |
|
||||||
level: 'error', |
|
||||||
transports: [new winston.transports.Console()], |
|
||||||
format: winston.format.combine( |
|
||||||
winston.format.colorize({ all: true }), |
|
||||||
logFmt |
|
||||||
) |
|
||||||
}) |
|
||||||
} |
|
||||||
setVerbosity (v) { |
|
||||||
this.logger.configure({ |
|
||||||
level: v, |
|
||||||
transports: [new winston.transports.Console()], |
|
||||||
format: winston.format.combine( |
|
||||||
winston.format.colorize({ all: true }), |
|
||||||
logFmt |
|
||||||
) |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = { |
|
||||||
Log |
|
||||||
} |
|
@ -0,0 +1,57 @@ |
|||||||
|
import colors from 'colors' |
||||||
|
import winston, { Logger, LoggerOptions } from 'winston' |
||||||
|
import timestamp from 'time-stamp'; |
||||||
|
import supportsColor from 'color-support' |
||||||
|
|
||||||
|
function hasFlag (flag: string) { |
||||||
|
return ((typeof (process) !== 'undefined') && (process.argv.indexOf('--' + flag) !== -1)) |
||||||
|
} |
||||||
|
|
||||||
|
function addColor (str: string) { |
||||||
|
if (hasFlag('no-color')) { |
||||||
|
return str |
||||||
|
} |
||||||
|
|
||||||
|
if (hasFlag('color')) { |
||||||
|
return colors.gray(str) |
||||||
|
} |
||||||
|
|
||||||
|
if (supportsColor()) { |
||||||
|
return colors.gray(str) |
||||||
|
} |
||||||
|
|
||||||
|
return str |
||||||
|
} |
||||||
|
function getTimestamp () { |
||||||
|
return '[' + addColor(timestamp('HH:mm:ss')) + ']' |
||||||
|
} |
||||||
|
// create winston logger format
|
||||||
|
const logFmt = winston.format.printf((info) => { |
||||||
|
return `${getTimestamp()} ${info.level}: ${info.message}` |
||||||
|
}) |
||||||
|
|
||||||
|
class Log { |
||||||
|
logger: Logger; |
||||||
|
constructor () { |
||||||
|
this.logger = winston.createLogger({ |
||||||
|
level: 'error', |
||||||
|
transports: [new winston.transports.Console()], |
||||||
|
format: winston.format.combine( |
||||||
|
winston.format.colorize({ all: true }), |
||||||
|
logFmt |
||||||
|
) |
||||||
|
}) |
||||||
|
} |
||||||
|
setVerbosity (v: LoggerOptions["level"]) { |
||||||
|
this.logger.configure({ |
||||||
|
level: v, |
||||||
|
transports: [new winston.transports.Console()], |
||||||
|
format: winston.format.combine( |
||||||
|
winston.format.colorize({ all: true }), |
||||||
|
logFmt |
||||||
|
) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export = Log |
@ -1,65 +0,0 @@ |
|||||||
const commander = require('commander') |
|
||||||
const Web3 = require('web3') |
|
||||||
const RemixTests = require('./index.js') |
|
||||||
const fs = require('fs') |
|
||||||
const Provider = require('remix-simulator').Provider |
|
||||||
const { Log } = require('./logger.js') |
|
||||||
const logger = new Log() |
|
||||||
const log = logger.logger |
|
||||||
require('colors') |
|
||||||
|
|
||||||
// parse verbosity
|
|
||||||
function mapVerbosity (v) { |
|
||||||
const levels = { |
|
||||||
0: 'error', |
|
||||||
1: 'warn', |
|
||||||
2: 'info', |
|
||||||
3: 'verbose', |
|
||||||
4: 'debug', |
|
||||||
5: 'silly' |
|
||||||
} |
|
||||||
return levels[v] |
|
||||||
} |
|
||||||
const version = require('../package.json').version |
|
||||||
|
|
||||||
commander.version(version) |
|
||||||
|
|
||||||
commander.command('version').description('output the version number').action(function () { |
|
||||||
console.log(version) |
|
||||||
}) |
|
||||||
|
|
||||||
commander.command('help').description('output usage information').action(function () { |
|
||||||
commander.help() |
|
||||||
}) |
|
||||||
|
|
||||||
// get current version
|
|
||||||
commander |
|
||||||
.option('-v, --verbose <level>', 'run with verbosity', mapVerbosity) |
|
||||||
.action(function (filename) { |
|
||||||
// Console message
|
|
||||||
console.log(('\n\t👁 :: Running remix-tests - Unit testing for solidity :: 👁\t\n').white) |
|
||||||
// set logger verbosity
|
|
||||||
if (commander.verbose) { |
|
||||||
logger.setVerbosity(commander.verbose) |
|
||||||
log.info('verbosity level set to ' + commander.verbose.blue) |
|
||||||
} |
|
||||||
let web3 = new Web3() |
|
||||||
// web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545'))
|
|
||||||
web3.setProvider(new Provider()) |
|
||||||
// web3.setProvider(new web3.providers.WebsocketProvider('ws://localhost:8546'))
|
|
||||||
|
|
||||||
if (!fs.existsSync(filename)) { |
|
||||||
console.error(filename + ' not found') |
|
||||||
process.exit(1) |
|
||||||
} |
|
||||||
|
|
||||||
let isDirectory = fs.lstatSync(filename).isDirectory() |
|
||||||
RemixTests.runTestFiles(filename, isDirectory, web3) |
|
||||||
}) |
|
||||||
|
|
||||||
if (!process.argv.slice(2).length) { |
|
||||||
log.error('Please specify a filename') |
|
||||||
process.exit() |
|
||||||
} |
|
||||||
|
|
||||||
commander.parse(process.argv) |
|
@ -0,0 +1,65 @@ |
|||||||
|
import commander from 'commander' |
||||||
|
import Web3 from 'web3' |
||||||
|
import { runTestFiles } from './runTestFiles' |
||||||
|
import fs from './fileSystem' |
||||||
|
import { Provider } from 'remix-simulator' |
||||||
|
import Log from './logger' |
||||||
|
const logger = new Log() |
||||||
|
const log = logger.logger |
||||||
|
import colors from 'colors' |
||||||
|
|
||||||
|
// parse verbosity
|
||||||
|
function mapVerbosity (v: number) { |
||||||
|
const levels = { |
||||||
|
0: 'error', |
||||||
|
1: 'warn', |
||||||
|
2: 'info', |
||||||
|
3: 'verbose', |
||||||
|
4: 'debug', |
||||||
|
5: 'silly' |
||||||
|
} |
||||||
|
return levels[v] |
||||||
|
} |
||||||
|
const version = require('../package.json').version |
||||||
|
|
||||||
|
commander.version(version) |
||||||
|
|
||||||
|
commander.command('version').description('output the version number').action(function () { |
||||||
|
console.log(version) |
||||||
|
}) |
||||||
|
|
||||||
|
commander.command('help').description('output usage information').action(function () { |
||||||
|
commander.help() |
||||||
|
}) |
||||||
|
|
||||||
|
// get current version
|
||||||
|
commander |
||||||
|
.option('-v, --verbose <level>', 'run with verbosity', mapVerbosity) |
||||||
|
.action((filename) => { |
||||||
|
// Console message
|
||||||
|
console.log(colors.white('\n\t👁\t:: Running remix-tests - Unit testing for solidity ::\t👁\n')) |
||||||
|
// set logger verbosity
|
||||||
|
if (commander.verbose) { |
||||||
|
logger.setVerbosity(commander.verbose) |
||||||
|
log.info('verbosity level set to ' + commander.verbose.blue) |
||||||
|
} |
||||||
|
let web3 = new Web3() |
||||||
|
// web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545'))
|
||||||
|
web3.setProvider(new Provider()) |
||||||
|
// web3.setProvider(new web3.providers.WebsocketProvider('ws://localhost:8546'))
|
||||||
|
|
||||||
|
if (!fs.existsSync(filename)) { |
||||||
|
console.error(filename + ' not found') |
||||||
|
process.exit(1) |
||||||
|
} |
||||||
|
|
||||||
|
let isDirectory = fs.lstatSync(filename).isDirectory() |
||||||
|
runTestFiles(filename, isDirectory, web3) |
||||||
|
}) |
||||||
|
|
||||||
|
if (!process.argv.slice(2).length) { |
||||||
|
log.error('Please specify a filename') |
||||||
|
process.exit() |
||||||
|
} |
||||||
|
|
||||||
|
commander.parse(process.argv) |
@ -1,133 +0,0 @@ |
|||||||
const async = require('async') |
|
||||||
const path = require('path') |
|
||||||
const fs = require('./fs') |
|
||||||
const TestRunner = require('./testRunner.js') |
|
||||||
require('colors') |
|
||||||
|
|
||||||
let Compiler = require('./compiler.js') |
|
||||||
let Deployer = require('./deployer.js') |
|
||||||
|
|
||||||
const runTestFiles = function (filepath, isDirectory, web3, opts) { |
|
||||||
opts = opts || {} |
|
||||||
const { Signale } = require('signale') |
|
||||||
// signale configuration
|
|
||||||
const options = { |
|
||||||
types: { |
|
||||||
result: { |
|
||||||
badge: '\t✓', |
|
||||||
label: '', |
|
||||||
color: 'greenBright' |
|
||||||
}, |
|
||||||
name: { |
|
||||||
badge: '\n\t◼', |
|
||||||
label: '', |
|
||||||
color: 'white' |
|
||||||
}, |
|
||||||
error: { |
|
||||||
badge: '\t✘', |
|
||||||
label: '', |
|
||||||
color: 'redBright' |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
const signale = new Signale(options) |
|
||||||
let accounts = opts.accounts || null |
|
||||||
async.waterfall([ |
|
||||||
function getAccountList (next) { |
|
||||||
if (accounts) return next(null) |
|
||||||
web3.eth.getAccounts((_err, _accounts) => { |
|
||||||
accounts = _accounts |
|
||||||
next(null) |
|
||||||
}) |
|
||||||
}, |
|
||||||
function compile (next) { |
|
||||||
Compiler.compileFileOrFiles(filepath, isDirectory, { accounts }, next) |
|
||||||
}, |
|
||||||
function deployAllContracts (compilationResult, next) { |
|
||||||
Deployer.deployAll(compilationResult, web3, function (err, contracts) { |
|
||||||
if (err) { |
|
||||||
next(err) |
|
||||||
} |
|
||||||
next(null, compilationResult, contracts) |
|
||||||
}) |
|
||||||
}, |
|
||||||
function determineTestContractsToRun (compilationResult, contracts, next) { |
|
||||||
let contractsToTest = [] |
|
||||||
let contractsToTestDetails = [] |
|
||||||
const gatherContractsFrom = (filename) => { |
|
||||||
if (filename.indexOf('_test.sol') < 0) { |
|
||||||
return |
|
||||||
} |
|
||||||
Object.keys(compilationResult[path.basename(filename)]).forEach(contractName => { |
|
||||||
contractsToTest.push(contractName) |
|
||||||
contractsToTestDetails.push(compilationResult[path.basename(filename)][contractName]) |
|
||||||
}) |
|
||||||
} |
|
||||||
if (isDirectory) { |
|
||||||
fs.walkSync(filepath, foundpath => { |
|
||||||
gatherContractsFrom(foundpath) |
|
||||||
}) |
|
||||||
} else { |
|
||||||
gatherContractsFrom(filepath) |
|
||||||
} |
|
||||||
next(null, contractsToTest, contractsToTestDetails, contracts) |
|
||||||
}, |
|
||||||
function runTests (contractsToTest, contractsToTestDetails, contracts, next) { |
|
||||||
let totalPassing = 0 |
|
||||||
let totalFailing = 0 |
|
||||||
let totalTime = 0 |
|
||||||
let errors = [] |
|
||||||
|
|
||||||
var testCallback = function (result) { |
|
||||||
if (result.type === 'contract') { |
|
||||||
signale.name(result.value.white) |
|
||||||
} else if (result.type === 'testPass') { |
|
||||||
signale.result(result.value) |
|
||||||
} else if (result.type === 'testFailure') { |
|
||||||
signale.result(result.value.red) |
|
||||||
errors.push(result) |
|
||||||
} |
|
||||||
} |
|
||||||
var resultsCallback = function (_err, result, cb) { |
|
||||||
totalPassing += result.passingNum |
|
||||||
totalFailing += result.failureNum |
|
||||||
totalTime += result.timePassed |
|
||||||
cb() |
|
||||||
} |
|
||||||
|
|
||||||
async.eachOfLimit(contractsToTest, 1, (contractName, index, cb) => { |
|
||||||
TestRunner.runTest(contractName, contracts[contractName], contractsToTestDetails[index], { accounts }, testCallback, (err, result) => { |
|
||||||
if (err) { |
|
||||||
return cb(err) |
|
||||||
} |
|
||||||
resultsCallback(null, result, cb) |
|
||||||
}) |
|
||||||
}, function (err, _results) { |
|
||||||
if (err) { |
|
||||||
return next(err) |
|
||||||
} |
|
||||||
|
|
||||||
console.log('\n') |
|
||||||
if (totalPassing > 0) { |
|
||||||
console.log((' ' + totalPassing + ' passing ').green + ('(' + totalTime + 's)').grey) |
|
||||||
} |
|
||||||
if (totalFailing > 0) { |
|
||||||
console.log((' ' + totalFailing + ' failing').red) |
|
||||||
} |
|
||||||
console.log('') |
|
||||||
|
|
||||||
errors.forEach((error, index) => { |
|
||||||
console.log(' ' + (index + 1) + ') ' + error.context + ' ' + error.value) |
|
||||||
console.log('') |
|
||||||
console.log(('\t error: ' + error.errMsg).red) |
|
||||||
}) |
|
||||||
console.log('') |
|
||||||
|
|
||||||
next() |
|
||||||
}) |
|
||||||
} |
|
||||||
], function () { |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = runTestFiles |
|
@ -0,0 +1,142 @@ |
|||||||
|
import async from 'async' |
||||||
|
import fs from './fileSystem' |
||||||
|
import { runTest } from './testRunner' |
||||||
|
import { TestResultInterface, ResultsInterface } from './types' |
||||||
|
import colors from 'colors' |
||||||
|
import Web3 from 'web3' |
||||||
|
|
||||||
|
import { compileFileOrFiles } from './compiler' |
||||||
|
import { deployAll } from './deployer' |
||||||
|
|
||||||
|
export function runTestFiles(filepath: string, isDirectory: boolean, web3: Web3, opts?: object) { |
||||||
|
opts = opts || {} |
||||||
|
const { Signale } = require('signale') |
||||||
|
// signale configuration
|
||||||
|
const options = { |
||||||
|
types: { |
||||||
|
result: { |
||||||
|
badge: '\t✓', |
||||||
|
label: '', |
||||||
|
color: 'greenBright' |
||||||
|
}, |
||||||
|
name: { |
||||||
|
badge: '\n\t◼', |
||||||
|
label: '', |
||||||
|
color: 'white' |
||||||
|
}, |
||||||
|
error: { |
||||||
|
badge: '\t✘', |
||||||
|
label: '', |
||||||
|
color: 'redBright' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
const signale = new Signale(options) |
||||||
|
let accounts = opts['accounts'] || null |
||||||
|
async.waterfall([ |
||||||
|
function getAccountList (next: Function) { |
||||||
|
if (accounts) return next(null) |
||||||
|
web3.eth.getAccounts((_err: Error | null | undefined, _accounts) => { |
||||||
|
accounts = _accounts |
||||||
|
next(null) |
||||||
|
}) |
||||||
|
}, |
||||||
|
function compile(next: Function) { |
||||||
|
compileFileOrFiles(filepath, isDirectory, { accounts }, next) |
||||||
|
}, |
||||||
|
function deployAllContracts (compilationResult, next: Function) { |
||||||
|
deployAll(compilationResult, web3, (err, contracts) => { |
||||||
|
if (err) { |
||||||
|
next(err) |
||||||
|
} |
||||||
|
next(null, compilationResult, contracts) |
||||||
|
}) |
||||||
|
}, |
||||||
|
function determineTestContractsToRun (compilationResult, contracts, next: Function) { |
||||||
|
let contractsToTest: any[] = [] |
||||||
|
let contractsToTestDetails: any[] = [] |
||||||
|
const gatherContractsFrom = function(filename: string) { |
||||||
|
if (filename.indexOf('_test.sol') < 0) { |
||||||
|
return |
||||||
|
} |
||||||
|
try { |
||||||
|
Object.keys(compilationResult[filename]).forEach(contractName => { |
||||||
|
contractsToTest.push(contractName) |
||||||
|
contractsToTestDetails.push(compilationResult[filename][contractName]) |
||||||
|
}) |
||||||
|
} catch (e) { |
||||||
|
console.error(e) |
||||||
|
} |
||||||
|
} |
||||||
|
if (isDirectory) { |
||||||
|
fs.walkSync(filepath, (foundpath: string) => { |
||||||
|
gatherContractsFrom(foundpath) |
||||||
|
}) |
||||||
|
} else { |
||||||
|
gatherContractsFrom(filepath) |
||||||
|
} |
||||||
|
next(null, contractsToTest, contractsToTestDetails, contracts) |
||||||
|
}, |
||||||
|
function runTests(contractsToTest, contractsToTestDetails, contracts, next: Function) { |
||||||
|
let totalPassing: number = 0 |
||||||
|
let totalFailing: number = 0 |
||||||
|
let totalTime: number = 0 |
||||||
|
let errors: any[] = [] |
||||||
|
|
||||||
|
const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) { |
||||||
|
if(err) throw err; |
||||||
|
if (result.type === 'contract') { |
||||||
|
signale.name(result.value.white) |
||||||
|
} else if (result.type === 'testPass') { |
||||||
|
signale.result(result.value) |
||||||
|
} else if (result.type === 'testFailure') { |
||||||
|
signale.result(result.value.red) |
||||||
|
errors.push(result) |
||||||
|
} |
||||||
|
} |
||||||
|
const _resultsCallback = (_err: Error | null | undefined, result: ResultsInterface, cb) => { |
||||||
|
totalPassing += result.passingNum |
||||||
|
totalFailing += result.failureNum |
||||||
|
totalTime += result.timePassed |
||||||
|
cb() |
||||||
|
} |
||||||
|
|
||||||
|
async.eachOfLimit(contractsToTest, 1, (contractName: string, index, cb) => { |
||||||
|
try { |
||||||
|
runTest(contractName, contracts[contractName], contractsToTestDetails[index], { accounts }, _testCallback, (err, result) => { |
||||||
|
if (err) { |
||||||
|
console.log(err) |
||||||
|
return cb(err) |
||||||
|
} |
||||||
|
_resultsCallback(null, result, cb) |
||||||
|
}) |
||||||
|
} catch(e) { |
||||||
|
console.error(e) |
||||||
|
} |
||||||
|
}, function (err) { |
||||||
|
if (err) { |
||||||
|
return next(err) |
||||||
|
} |
||||||
|
|
||||||
|
console.log('\n') |
||||||
|
if (totalPassing > 0) { |
||||||
|
console.log(colors.green(totalPassing + ' passing ') + colors.grey('(' + totalTime + 's)')) |
||||||
|
} |
||||||
|
if (totalFailing > 0) { |
||||||
|
console.log(colors.red(totalFailing + ' failing')) |
||||||
|
} |
||||||
|
console.log('') |
||||||
|
|
||||||
|
errors.forEach((error, index) => { |
||||||
|
console.log(' ' + (index + 1) + ') ' + error.context + ' ' + error.value) |
||||||
|
console.log('') |
||||||
|
console.log(colors.red('\t error: ' + error.errMsg)) |
||||||
|
}) |
||||||
|
console.log('') |
||||||
|
|
||||||
|
next() |
||||||
|
}) |
||||||
|
} |
||||||
|
], () => { |
||||||
|
}) |
||||||
|
} |
@ -1,107 +0,0 @@ |
|||||||
const async = require('async') |
|
||||||
require('colors') |
|
||||||
|
|
||||||
let Compiler = require('./compiler.js') |
|
||||||
let Deployer = require('./deployer.js') |
|
||||||
let TestRunner = require('./testRunner.js') |
|
||||||
|
|
||||||
const Web3 = require('web3') |
|
||||||
const Provider = require('remix-simulator').Provider |
|
||||||
|
|
||||||
var createWeb3Provider = function () { |
|
||||||
let web3 = new Web3() |
|
||||||
web3.setProvider(new Provider()) |
|
||||||
return web3 |
|
||||||
} |
|
||||||
|
|
||||||
const runTestSources = function (contractSources, testCallback, resultCallback, finalCallback, importFileCb, opts) { |
|
||||||
opts = opts || {} |
|
||||||
let web3 = opts.web3 || createWeb3Provider() |
|
||||||
let accounts = opts.accounts || null |
|
||||||
async.waterfall([ |
|
||||||
function getAccountList (next) { |
|
||||||
if (accounts) return next() |
|
||||||
web3.eth.getAccounts((_err, _accounts) => { |
|
||||||
accounts = _accounts |
|
||||||
next() |
|
||||||
}) |
|
||||||
}, |
|
||||||
function compile (next) { |
|
||||||
Compiler.compileContractSources(contractSources, importFileCb, next) |
|
||||||
}, |
|
||||||
function deployAllContracts (compilationResult, next) { |
|
||||||
Deployer.deployAll(compilationResult, web3, function (err, contracts) { |
|
||||||
if (err) { |
|
||||||
next(err) |
|
||||||
} |
|
||||||
|
|
||||||
next(null, compilationResult, contracts) |
|
||||||
}) |
|
||||||
}, |
|
||||||
function determineTestContractsToRun (compilationResult, contracts, next) { |
|
||||||
let contractsToTest = [] |
|
||||||
let contractsToTestDetails = [] |
|
||||||
|
|
||||||
for (let filename in compilationResult) { |
|
||||||
if (filename.indexOf('_test.sol') < 0) { |
|
||||||
continue |
|
||||||
} |
|
||||||
Object.keys(compilationResult[filename]).forEach(contractName => { |
|
||||||
contractsToTestDetails.push(compilationResult[filename][contractName]) |
|
||||||
contractsToTest.push(contractName) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
next(null, contractsToTest, contractsToTestDetails, contracts) |
|
||||||
}, |
|
||||||
function runTests (contractsToTest, contractsToTestDetails, contracts, next) { |
|
||||||
let totalPassing = 0 |
|
||||||
let totalFailing = 0 |
|
||||||
let totalTime = 0 |
|
||||||
let errors = [] |
|
||||||
|
|
||||||
var _testCallback = function (result) { |
|
||||||
if (result.type === 'testFailure') { |
|
||||||
errors.push(result) |
|
||||||
} |
|
||||||
testCallback(result) |
|
||||||
} |
|
||||||
|
|
||||||
var _resultsCallback = function (_err, result, cb) { |
|
||||||
resultCallback(_err, result, () => {}) |
|
||||||
totalPassing += result.passingNum |
|
||||||
totalFailing += result.failureNum |
|
||||||
totalTime += result.timePassed |
|
||||||
cb() |
|
||||||
} |
|
||||||
|
|
||||||
async.eachOfLimit(contractsToTest, 1, (contractName, index, cb) => { |
|
||||||
TestRunner.runTest(contractName, contracts[contractName], contractsToTestDetails[index], { accounts }, _testCallback, (err, result) => { |
|
||||||
if (err) { |
|
||||||
return cb(err) |
|
||||||
} |
|
||||||
_resultsCallback(null, result, cb) |
|
||||||
}) |
|
||||||
}, function (err, _results) { |
|
||||||
if (err) { |
|
||||||
return next(err) |
|
||||||
} |
|
||||||
|
|
||||||
let finalResults = {} |
|
||||||
|
|
||||||
finalResults.totalPassing = totalPassing || 0 |
|
||||||
finalResults.totalFailing = totalFailing || 0 |
|
||||||
finalResults.totalTime = totalTime || 0 |
|
||||||
finalResults.errors = [] |
|
||||||
|
|
||||||
errors.forEach((error, _index) => { |
|
||||||
finalResults.errors.push({context: error.context, value: error.value, message: error.errMsg}) |
|
||||||
}) |
|
||||||
|
|
||||||
next(null, finalResults) |
|
||||||
}) |
|
||||||
} |
|
||||||
], finalCallback) |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = runTestSources |
|
@ -0,0 +1,110 @@ |
|||||||
|
import async from 'async' |
||||||
|
require('colors') |
||||||
|
|
||||||
|
import { compileContractSources } from './compiler' |
||||||
|
import { deployAll } from './deployer' |
||||||
|
import { runTest } from './testRunner' |
||||||
|
|
||||||
|
import Web3 = require('web3') |
||||||
|
import Provider from 'remix-simulator' |
||||||
|
import { FinalResult } from './types' |
||||||
|
|
||||||
|
const createWeb3Provider = function () { |
||||||
|
let web3 = new Web3() |
||||||
|
web3.setProvider(new Provider()) |
||||||
|
return web3 |
||||||
|
} |
||||||
|
|
||||||
|
export function runTestSources(contractSources, testCallback, resultCallback, finalCallback, importFileCb, opts) { |
||||||
|
opts = opts || {} |
||||||
|
let web3 = opts.web3 || createWeb3Provider() |
||||||
|
let accounts = opts.accounts || null |
||||||
|
async.waterfall([ |
||||||
|
function getAccountList (next) { |
||||||
|
if (accounts) return next() |
||||||
|
web3.eth.getAccounts((_err, _accounts) => { |
||||||
|
accounts = _accounts |
||||||
|
next() |
||||||
|
}) |
||||||
|
}, |
||||||
|
function compile (next) { |
||||||
|
compileContractSources(contractSources, importFileCb, opts, next) |
||||||
|
}, |
||||||
|
function deployAllContracts (compilationResult, next) { |
||||||
|
deployAll(compilationResult, web3, (err, contracts) => { |
||||||
|
if (err) { |
||||||
|
next(err) |
||||||
|
} |
||||||
|
|
||||||
|
next(null, compilationResult, contracts) |
||||||
|
}) |
||||||
|
}, |
||||||
|
function determineTestContractsToRun (compilationResult, contracts, next) { |
||||||
|
let contractsToTest: any[] = [] |
||||||
|
let contractsToTestDetails: any[] = [] |
||||||
|
|
||||||
|
for (let filename in compilationResult) { |
||||||
|
if (filename.indexOf('_test.sol') < 0) { |
||||||
|
continue |
||||||
|
} |
||||||
|
Object.keys(compilationResult[filename]).forEach(contractName => { |
||||||
|
contractsToTestDetails.push(compilationResult[filename][contractName]) |
||||||
|
contractsToTest.push(contractName) |
||||||
|
}) |
||||||
|
} |
||||||
|
next(null, contractsToTest, contractsToTestDetails, contracts) |
||||||
|
}, |
||||||
|
function runTests(contractsToTest, contractsToTestDetails, contracts, next) { |
||||||
|
let totalPassing = 0 |
||||||
|
let totalFailing = 0 |
||||||
|
let totalTime = 0 |
||||||
|
let errors: any[] = [] |
||||||
|
|
||||||
|
const _testCallback = function (result) { |
||||||
|
if (result.type === 'testFailure') { |
||||||
|
errors.push(result) |
||||||
|
} |
||||||
|
testCallback(result) |
||||||
|
} |
||||||
|
|
||||||
|
const _resultsCallback = function (_err, result, cb) { |
||||||
|
resultCallback(_err, result, () => {}) |
||||||
|
totalPassing += result.passingNum |
||||||
|
totalFailing += result.failureNum |
||||||
|
totalTime += result.timePassed |
||||||
|
cb() |
||||||
|
} |
||||||
|
|
||||||
|
async.eachOfLimit(contractsToTest, 1, (contractName, index, cb) => { |
||||||
|
runTest(contractName, contracts(contractName), contractsToTestDetails[index], { accounts }, _testCallback, (err, result) => { |
||||||
|
if (err) { |
||||||
|
return cb(err) |
||||||
|
} |
||||||
|
_resultsCallback(null, result, cb) |
||||||
|
}) |
||||||
|
}, function (err) { |
||||||
|
if (err) { |
||||||
|
return next(err) |
||||||
|
} |
||||||
|
|
||||||
|
let finalResults: FinalResult = { |
||||||
|
totalPassing: 0, |
||||||
|
totalFailing: 0, |
||||||
|
totalTime: 0, |
||||||
|
errors: [], |
||||||
|
} |
||||||
|
|
||||||
|
finalResults.totalPassing = totalPassing || 0 |
||||||
|
finalResults.totalFailing = totalFailing || 0 |
||||||
|
finalResults.totalTime = totalTime || 0 |
||||||
|
finalResults.errors = [] |
||||||
|
|
||||||
|
errors.forEach((error, _index) => { |
||||||
|
finalResults.errors.push({context: error.context, value: error.value, message: error.errMsg}) |
||||||
|
}) |
||||||
|
|
||||||
|
next(null, finalResults) |
||||||
|
}) |
||||||
|
} |
||||||
|
], finalCallback) |
||||||
|
} |
@ -1,149 +0,0 @@ |
|||||||
var async = require('async') |
|
||||||
var changeCase = require('change-case') |
|
||||||
var Web3 = require('web3') |
|
||||||
|
|
||||||
function getFunctionFullName (signature, methodIdentifiers) { |
|
||||||
for (var method in methodIdentifiers) { |
|
||||||
if (signature.replace('0x', '') === methodIdentifiers[method].replace('0x', '')) { |
|
||||||
return method |
|
||||||
} |
|
||||||
} |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
function getOverridedSender (userdoc, signature, methodIdentifiers) { |
|
||||||
let fullName = getFunctionFullName(signature, methodIdentifiers) |
|
||||||
let match = /sender: account-+(\d)/g |
|
||||||
let accountIndex = userdoc.methods[fullName] ? match.exec(userdoc.methods[fullName].notice) : null |
|
||||||
return fullName && accountIndex ? accountIndex[1] : null |
|
||||||
} |
|
||||||
|
|
||||||
function getAvailableFunctions (jsonInterface) { |
|
||||||
return jsonInterface.reverse().filter((x) => x.type === 'function').map((x) => x.name) |
|
||||||
} |
|
||||||
|
|
||||||
function getTestFunctions (jsonInterface) { |
|
||||||
let specialFunctions = ['beforeAll', 'beforeEach', 'afterAll', 'afterEach'] |
|
||||||
return jsonInterface.filter((x) => specialFunctions.indexOf(x.name) < 0 && x.type === 'function') |
|
||||||
} |
|
||||||
|
|
||||||
function createRunList (jsonInterface) { |
|
||||||
let availableFunctions = getAvailableFunctions(jsonInterface) |
|
||||||
let testFunctions = getTestFunctions(jsonInterface) |
|
||||||
let runList = [] |
|
||||||
|
|
||||||
if (availableFunctions.indexOf('beforeAll') >= 0) { |
|
||||||
runList.push({name: 'beforeAll', type: 'internal', constant: false}) |
|
||||||
} |
|
||||||
|
|
||||||
for (let func of testFunctions) { |
|
||||||
if (availableFunctions.indexOf('beforeEach') >= 0) { |
|
||||||
runList.push({name: 'beforeEach', type: 'internal', constant: false}) |
|
||||||
} |
|
||||||
runList.push({name: func.name, signature: func.signature, type: 'test', constant: func.constant}) |
|
||||||
if (availableFunctions.indexOf('afterEach') >= 0) { |
|
||||||
runList.push({name: 'afterEach', type: 'internal', constant: false}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (availableFunctions.indexOf('afterAll') >= 0) { |
|
||||||
runList.push({name: 'afterAll', type: 'internal', constant: false}) |
|
||||||
} |
|
||||||
|
|
||||||
return runList |
|
||||||
} |
|
||||||
|
|
||||||
function runTest (testName, testObject, contractDetails, opts, testCallback, resultsCallback) { |
|
||||||
let runList = createRunList(testObject._jsonInterface) |
|
||||||
|
|
||||||
let passingNum = 0 |
|
||||||
let failureNum = 0 |
|
||||||
let timePassed = 0 |
|
||||||
let web3 = new Web3() |
|
||||||
|
|
||||||
var userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-' |
|
||||||
var isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' electron/') > -1) |
|
||||||
if (!isBrowser) { |
|
||||||
let signale = require('signale') |
|
||||||
signale.warn('DO NOT TRY TO ACCESS (IN YOUR SOLIDITY TEST) AN ACCOUNT GREATER THAN THE LENGTH OF THE FOLLOWING ARRAY (' + opts.accounts.length + ') :') |
|
||||||
signale.warn(opts.accounts) |
|
||||||
signale.warn('e.g: the following code won\'t work in the current context:') |
|
||||||
signale.warn('TestsAccounts.getAccount(' + opts.accounts.length + ')') |
|
||||||
} |
|
||||||
|
|
||||||
testCallback({type: 'contract', value: testName, filename: testObject.filename}) |
|
||||||
async.eachOfLimit(runList, 1, function (func, index, next) { |
|
||||||
let sender |
|
||||||
if (func.signature) { |
|
||||||
sender = getOverridedSender(contractDetails.userdoc, func.signature, contractDetails.evm.methodIdentifiers) |
|
||||||
if (opts.accounts) { |
|
||||||
sender = opts.accounts[sender] |
|
||||||
} |
|
||||||
} |
|
||||||
let sendParams |
|
||||||
if (sender) sendParams = { from: sender } |
|
||||||
|
|
||||||
let method = testObject.methods[func.name].apply(testObject.methods[func.name], []) |
|
||||||
let startTime = Date.now() |
|
||||||
if (func.constant) { |
|
||||||
method.call(sendParams).then((result) => { |
|
||||||
let time = Math.ceil((Date.now() - startTime) / 1000.0) |
|
||||||
if (result) { |
|
||||||
testCallback({type: 'testPass', value: changeCase.sentenceCase(func.name), time: time, context: testName}) |
|
||||||
passingNum += 1 |
|
||||||
timePassed += time |
|
||||||
} else { |
|
||||||
testCallback({type: 'testFailure', value: changeCase.sentenceCase(func.name), time: time, errMsg: 'function returned false', context: testName}) |
|
||||||
failureNum += 1 |
|
||||||
} |
|
||||||
next() |
|
||||||
}) |
|
||||||
} else { |
|
||||||
method.send(sendParams).on('receipt', function (receipt) { |
|
||||||
try { |
|
||||||
let time = Math.ceil((Date.now() - startTime) / 1000.0) |
|
||||||
let topic = Web3.utils.sha3('AssertionEvent(bool,string)') |
|
||||||
|
|
||||||
let testPassed = false |
|
||||||
|
|
||||||
for (let i in receipt.events) { |
|
||||||
let event = receipt.events[i] |
|
||||||
if (event.raw.topics.indexOf(topic) >= 0) { |
|
||||||
var testEvent = web3.eth.abi.decodeParameters(['bool', 'string'], event.raw.data) |
|
||||||
if (!testEvent[0]) { |
|
||||||
testCallback({type: 'testFailure', value: changeCase.sentenceCase(func.name), time: time, errMsg: testEvent[1], context: testName}) |
|
||||||
failureNum += 1 |
|
||||||
return next() |
|
||||||
} |
|
||||||
testPassed = true |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (testPassed) { |
|
||||||
testCallback({type: 'testPass', value: changeCase.sentenceCase(func.name), time: time, context: testName}) |
|
||||||
passingNum += 1 |
|
||||||
} |
|
||||||
|
|
||||||
return next() |
|
||||||
} catch (err) { |
|
||||||
console.log('error!') |
|
||||||
console.dir(err) |
|
||||||
return next(err) |
|
||||||
} |
|
||||||
}).on('error', function (err) { |
|
||||||
console.error(err) |
|
||||||
next(err) |
|
||||||
}) |
|
||||||
} |
|
||||||
}, function (error) { |
|
||||||
resultsCallback(error, { |
|
||||||
passingNum: passingNum, |
|
||||||
failureNum: failureNum, |
|
||||||
timePassed: timePassed |
|
||||||
}) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = { |
|
||||||
runTest: runTest |
|
||||||
} |
|
@ -0,0 +1,174 @@ |
|||||||
|
import async from 'async' |
||||||
|
import * as changeCase from 'change-case' |
||||||
|
import Web3 from 'web3' |
||||||
|
import { RunListInterface, TestCbInterface, TestResultInterface, ResultCbInterface } from './types' |
||||||
|
|
||||||
|
function getFunctionFullName (signature: string, methodIdentifiers) { |
||||||
|
for (const method in methodIdentifiers) { |
||||||
|
if (signature.replace('0x', '') === methodIdentifiers[method].replace('0x', '')) { |
||||||
|
return method |
||||||
|
} |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
function getOverridedSender (userdoc, signature: string, methodIdentifiers) { |
||||||
|
let fullName: any = getFunctionFullName(signature, methodIdentifiers) |
||||||
|
let match = /sender: account-+(\d)/g |
||||||
|
let accountIndex = userdoc.methods[fullName] ? match.exec(userdoc.methods[fullName].notice) : null |
||||||
|
return fullName && accountIndex ? accountIndex[1] : null |
||||||
|
} |
||||||
|
|
||||||
|
function getAvailableFunctions (jsonInterface) { |
||||||
|
return jsonInterface.reverse().filter((x) => x.type === 'function').map((x) => x.name) |
||||||
|
} |
||||||
|
|
||||||
|
function getTestFunctions (jsonInterface) { |
||||||
|
let specialFunctions = ['beforeAll', 'beforeEach', 'afterAll', 'afterEach'] |
||||||
|
return jsonInterface.filter((x) => specialFunctions.indexOf(x.name) < 0 && x.type === 'function') |
||||||
|
} |
||||||
|
|
||||||
|
function createRunList (jsonInterface): RunListInterface[] { |
||||||
|
let availableFunctions = getAvailableFunctions(jsonInterface) |
||||||
|
let testFunctions = getTestFunctions(jsonInterface) |
||||||
|
let runList: RunListInterface[] = [] |
||||||
|
|
||||||
|
if (availableFunctions.indexOf('beforeAll') >= 0) { |
||||||
|
runList.push({ name: 'beforeAll', type: 'internal', constant: false }) |
||||||
|
} |
||||||
|
|
||||||
|
for (let func of testFunctions) { |
||||||
|
if (availableFunctions.indexOf('beforeEach') >= 0) { |
||||||
|
runList.push({ name: 'beforeEach', type: 'internal', constant: false }) |
||||||
|
} |
||||||
|
runList.push({ name: func.name, signature: func.signature, type: 'test', constant: func.constant }) |
||||||
|
if (availableFunctions.indexOf('afterEach') >= 0) { |
||||||
|
runList.push({ name: 'afterEach', type: 'internal', constant: false }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (availableFunctions.indexOf('afterAll') >= 0) { |
||||||
|
runList.push({ name: 'afterAll', type: 'internal', constant: false }) |
||||||
|
} |
||||||
|
|
||||||
|
return runList |
||||||
|
} |
||||||
|
|
||||||
|
export function runTest (testName, testObject: any, contractDetails: any, opts: any, testCallback: TestCbInterface, resultsCallback: ResultCbInterface) { |
||||||
|
let runList = createRunList(testObject._jsonInterface) |
||||||
|
|
||||||
|
let passingNum: number = 0 |
||||||
|
let failureNum: number = 0 |
||||||
|
let timePassed: number = 0 |
||||||
|
let web3 = new Web3() |
||||||
|
|
||||||
|
const userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-' |
||||||
|
const isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' electron/') > -1) |
||||||
|
if (!isBrowser) { |
||||||
|
let signale = require('signale') |
||||||
|
signale.warn('DO NOT TRY TO ACCESS (IN YOUR SOLIDITY TEST) AN ACCOUNT GREATER THAN THE LENGTH OF THE FOLLOWING ARRAY (' + opts.accounts.length + ') :') |
||||||
|
signale.warn(opts.accounts) |
||||||
|
signale.warn('e.g: the following code won\'t work in the current context:') |
||||||
|
signale.warn('TestsAccounts.getAccount(' + opts.accounts.length + ')') |
||||||
|
} |
||||||
|
const resp: TestResultInterface = { |
||||||
|
type: 'contract', |
||||||
|
value: testName, |
||||||
|
filename: testObject.filename |
||||||
|
} |
||||||
|
testCallback(undefined, resp) |
||||||
|
async.eachOfLimit(runList, 1, function (func, index, next) { |
||||||
|
let sender |
||||||
|
if (func.signature) { |
||||||
|
sender = getOverridedSender(contractDetails.userdoc, func.signature, contractDetails.evm.methodIdentifiers) |
||||||
|
if (opts.accounts) { |
||||||
|
sender = opts.accounts[sender] |
||||||
|
} |
||||||
|
} |
||||||
|
let sendParams |
||||||
|
if (sender) sendParams = { from: sender } |
||||||
|
|
||||||
|
let method = testObject.methods[func.name].apply(testObject.methods[func.name], []) |
||||||
|
let startTime = Date.now() |
||||||
|
if (func.constant) { |
||||||
|
method.call(sendParams).then((result) => { |
||||||
|
let time = Math.ceil((Date.now() - startTime) / 1000.0) |
||||||
|
if (result) { |
||||||
|
const resp: TestResultInterface = { |
||||||
|
type: 'testPass', |
||||||
|
value: changeCase.sentenceCase(func.name), |
||||||
|
time: time, |
||||||
|
context: testName |
||||||
|
} |
||||||
|
testCallback(undefined, resp) |
||||||
|
passingNum += 1 |
||||||
|
timePassed += time |
||||||
|
} else { |
||||||
|
const resp: TestResultInterface = { |
||||||
|
type: 'testFailure', |
||||||
|
value: changeCase.sentenceCase(func.name), |
||||||
|
time: time, |
||||||
|
errMsg: 'function returned false', |
||||||
|
context: testName |
||||||
|
} |
||||||
|
testCallback(undefined, resp) |
||||||
|
failureNum += 1 |
||||||
|
} |
||||||
|
next() |
||||||
|
}) |
||||||
|
} else { |
||||||
|
method.send(sendParams).on('receipt', (receipt) => { |
||||||
|
try { |
||||||
|
let time: number = Math.ceil((Date.now() - startTime) / 1000.0) |
||||||
|
let topic = Web3.utils.sha3('AssertionEvent(bool,string)') |
||||||
|
let testPassed: boolean = false |
||||||
|
|
||||||
|
for (let i in receipt.events) { |
||||||
|
let event = receipt.events[i] |
||||||
|
if (event.raw.topics.indexOf(topic) >= 0) { |
||||||
|
const testEvent = web3.eth.abi.decodeParameters(['bool', 'string'], event.raw.data) |
||||||
|
if (!testEvent[0]) { |
||||||
|
const resp: TestResultInterface = { |
||||||
|
type: 'testFailure', |
||||||
|
value: changeCase.sentenceCase(func.name), |
||||||
|
time: time, |
||||||
|
errMsg: testEvent[1], |
||||||
|
context: testName |
||||||
|
}; |
||||||
|
testCallback(undefined, resp) |
||||||
|
failureNum += 1 |
||||||
|
return next() |
||||||
|
} |
||||||
|
testPassed = true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (testPassed) { |
||||||
|
const resp: TestResultInterface = { |
||||||
|
type: 'testPass', |
||||||
|
value: changeCase.sentenceCase(func.name), |
||||||
|
time: time, |
||||||
|
context: testName |
||||||
|
} |
||||||
|
testCallback(undefined, resp) |
||||||
|
passingNum += 1 |
||||||
|
} |
||||||
|
|
||||||
|
return next() |
||||||
|
} catch (err) { |
||||||
|
console.error(err) |
||||||
|
return next(err) |
||||||
|
} |
||||||
|
}).on('error', function (err: Error | null | undefined) { |
||||||
|
console.error(err) |
||||||
|
next(err) |
||||||
|
}) |
||||||
|
} |
||||||
|
}, function(error) { |
||||||
|
resultsCallback(error, { |
||||||
|
passingNum: passingNum, |
||||||
|
failureNum: failureNum, |
||||||
|
timePassed: timePassed |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
/** sources object with name of the file and content **/ |
||||||
|
export interface SrcIfc { |
||||||
|
[key: string]: { |
||||||
|
content: string |
||||||
|
} |
||||||
|
} |
||||||
|
/** An object with final results of test **/ |
||||||
|
export interface FinalResult { |
||||||
|
totalPassing: number, |
||||||
|
totalFailing: number, |
||||||
|
totalTime: number, |
||||||
|
errors: any[], |
||||||
|
} |
||||||
|
/** List of tests to run **/ |
||||||
|
export interface RunListInterface { |
||||||
|
name: string, |
||||||
|
type: string, |
||||||
|
constant: boolean, |
||||||
|
signature?: any |
||||||
|
} |
||||||
|
export interface ResultsInterface { |
||||||
|
passingNum: number, |
||||||
|
failureNum: number, |
||||||
|
timePassed: number |
||||||
|
} |
||||||
|
export interface TestResultInterface { |
||||||
|
type: string, |
||||||
|
value: any, |
||||||
|
time?: number, |
||||||
|
context?: string, |
||||||
|
errMsg?: string |
||||||
|
filename?: string |
||||||
|
} |
||||||
|
export interface TestCbInterface { |
||||||
|
(error: Error | null | undefined, result: TestResultInterface) : void; |
||||||
|
} |
||||||
|
export interface ResultCbInterface { |
||||||
|
(error: Error | null | undefined, result: ResultsInterface) : void; |
||||||
|
} |
@ -1,198 +0,0 @@ |
|||||||
const async = require('async') |
|
||||||
const Web3 = require('web3') |
|
||||||
const assert = require('assert') |
|
||||||
|
|
||||||
let Compiler = require('../src/compiler.js') |
|
||||||
let Deployer = require('../src/deployer.js') |
|
||||||
let TestRunner = require('../src/testRunner.js') |
|
||||||
const Provider = require('remix-simulator').Provider |
|
||||||
|
|
||||||
function compileAndDeploy (filename, callback) { |
|
||||||
let web3 = new Web3() |
|
||||||
web3.setProvider(new Provider()) |
|
||||||
let compilationData |
|
||||||
let accounts |
|
||||||
async.waterfall([ |
|
||||||
function getAccountList (next) { |
|
||||||
web3.eth.getAccounts((_err, _accounts) => { |
|
||||||
accounts = _accounts |
|
||||||
next(_err) |
|
||||||
}) |
|
||||||
}, |
|
||||||
function compile (next) { |
|
||||||
Compiler.compileFileOrFiles(filename, false, {accounts}, next) |
|
||||||
}, |
|
||||||
function deployAllContracts (compilationResult, next) { |
|
||||||
compilationData = compilationResult |
|
||||||
Deployer.deployAll(compilationResult, web3, next) |
|
||||||
} |
|
||||||
], function (_err, contracts) { |
|
||||||
callback(null, compilationData, contracts, accounts) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
describe('testRunner', function () { |
|
||||||
describe('#runTest', function() { |
|
||||||
describe('test with beforeAll', function () { |
|
||||||
let filename = 'tests/examples_1/simple_storage_test.sol' |
|
||||||
let tests = [], results = {} |
|
||||||
|
|
||||||
before(function (done) { |
|
||||||
compileAndDeploy(filename, function (_err, compilationData, contracts, accounts) { |
|
||||||
var testCallback = function (test) { |
|
||||||
tests.push(test) |
|
||||||
} |
|
||||||
var resultsCallback = function (_err, _results) { |
|
||||||
results = _results |
|
||||||
done() |
|
||||||
} |
|
||||||
TestRunner.runTest('MyTest', contracts.MyTest, compilationData[filename]['MyTest'], { accounts }, testCallback, resultsCallback) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should 1 passing test', function () { |
|
||||||
assert.equal(results.passingNum, 2) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should 1 failing test', function () { |
|
||||||
assert.equal(results.failureNum, 2) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should returns 5 messages', function () { |
|
||||||
assert.deepEqual(tests, [ |
|
||||||
{ type: 'contract', value: 'MyTest', filename: 'tests/examples_1/simple_storage_test.sol' }, |
|
||||||
{ type: 'testFailure', value: 'Should trigger one fail', time: 1, context: 'MyTest', errMsg: 'the test 1 fails' }, |
|
||||||
{ type: 'testPass', value: 'Should trigger one pass', time: 1, context: 'MyTest'}, |
|
||||||
{ type: 'testPass', value: 'Initial value should be100', time: 1, context: 'MyTest' }, |
|
||||||
{ type: 'testFailure', value: 'Initial value should be200', time: 1, context: 'MyTest', errMsg: 'function returned false' } |
|
||||||
]) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('test with beforeEach', function () { |
|
||||||
let filename = 'tests/examples_2/simple_storage_test.sol' |
|
||||||
let tests = [], results = {} |
|
||||||
|
|
||||||
before(function (done) { |
|
||||||
compileAndDeploy(filename, function (_err, compilationData, contracts, accounts) { |
|
||||||
var testCallback = function (test) { |
|
||||||
tests.push(test) |
|
||||||
} |
|
||||||
var resultsCallback = function (_err, _results) { |
|
||||||
results = _results |
|
||||||
done() |
|
||||||
} |
|
||||||
TestRunner.runTest('MyTest', contracts.MyTest, compilationData[filename]['MyTest'], { accounts }, testCallback, resultsCallback) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should 2 passing tests', function () { |
|
||||||
assert.equal(results.passingNum, 2) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should 0 failing tests', function () { |
|
||||||
assert.equal(results.failureNum, 0) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should returns 3 messages', function () { |
|
||||||
assert.deepEqual(tests, [ |
|
||||||
{ type: 'contract', value: 'MyTest', filename: 'tests/examples_2/simple_storage_test.sol' }, |
|
||||||
{ type: 'testPass', value: 'Initial value should be100', time: 1, context: 'MyTest' }, |
|
||||||
{ type: 'testPass', value: 'Initial value should be200', time: 1, context: 'MyTest' } |
|
||||||
]) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
// Test string equality
|
|
||||||
describe('test string equality', function () { |
|
||||||
let filename = 'tests/examples_3/simple_string_test.sol' |
|
||||||
let tests = [], results = {} |
|
||||||
|
|
||||||
before(function (done) { |
|
||||||
compileAndDeploy(filename, function (_err, compilationData, contracts, accounts) { |
|
||||||
var testCallback = function (test) { |
|
||||||
tests.push(test) |
|
||||||
} |
|
||||||
var resultsCallback = function (_err, _results) { |
|
||||||
results = _results |
|
||||||
done() |
|
||||||
} |
|
||||||
TestRunner.runTest('StringTest', contracts.StringTest, compilationData[filename]['StringTest'], { accounts }, testCallback, resultsCallback) |
|
||||||
TestRunner.runTest('StringTest2', contracts.StringTest2, compilationData[filename]['StringTest2'], { accounts }, testCallback, resultsCallback) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should 2 passing tests', function () { |
|
||||||
assert.equal(results.passingNum, 2) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should 1 failing tests', function () { |
|
||||||
assert.equal(results.failureNum, 1) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should returns 3 messages', function () { |
|
||||||
assert.deepEqual(tests, [ |
|
||||||
{ type: 'contract', value: 'StringTest', filename: 'tests/examples_3/simple_string_test.sol' }, |
|
||||||
{ type: 'testFailure', value: 'Value should be hello world', time: 1, context: 'StringTest', "errMsg": "initial value is not correct" }, |
|
||||||
{ type: 'testPass', value: 'Value should not be hello wordl', time: 1, context: 'StringTest' }, |
|
||||||
{ type: 'testPass', value: 'Initial value should be hello', time: 1, context: 'StringTest' }, |
|
||||||
]) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
// Test signed/unsigned integer weight
|
|
||||||
describe('test number weight', function () { |
|
||||||
let filename = 'tests/number/number_test.sol' |
|
||||||
let tests = [], results = {} |
|
||||||
|
|
||||||
before(function (done) { |
|
||||||
compileAndDeploy(filename, function (_err, compilationData, contracts, accounts) { |
|
||||||
var testCallback = function (test) { |
|
||||||
tests.push(test) |
|
||||||
} |
|
||||||
var resultsCallback = function (_err, _results) { |
|
||||||
results = _results |
|
||||||
done() |
|
||||||
} |
|
||||||
TestRunner.runTest('IntegerTest', contracts.IntegerTest, compilationData[filename]['IntegerTest'], { accounts }, testCallback, resultsCallback) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should have 6 passing tests', function () { |
|
||||||
assert.equal(results.passingNum, 6) |
|
||||||
}) |
|
||||||
it('should have 2 failing tests', function () { |
|
||||||
assert.equal(results.failureNum, 2) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
// Test Transaction with different sender
|
|
||||||
describe('various sender', function () { |
|
||||||
let filename = 'tests/various_sender/sender_test.sol' |
|
||||||
let tests = [], results = {} |
|
||||||
|
|
||||||
before(function (done) { |
|
||||||
compileAndDeploy(filename, function (_err, compilationData, contracts, accounts) { |
|
||||||
var testCallback = function (test) { |
|
||||||
tests.push(test) |
|
||||||
} |
|
||||||
var resultsCallback = function (_err, _results) { |
|
||||||
results = _results |
|
||||||
done() |
|
||||||
} |
|
||||||
|
|
||||||
TestRunner.runTest('SenderTest', contracts.SenderTest, compilationData[filename]['SenderTest'], { accounts }, testCallback, resultsCallback) |
|
||||||
|
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should have 4 passing tests', function () { |
|
||||||
assert.equal(results.passingNum, 4) |
|
||||||
}) |
|
||||||
it('should have 1 failing tests', function () { |
|
||||||
assert.equal(results.failureNum, 0) |
|
||||||
}) |
|
||||||
}) |
|
||||||
}) |
|
||||||
}) |
|
@ -0,0 +1,214 @@ |
|||||||
|
import 'mocha' |
||||||
|
import * as async from 'async' |
||||||
|
import Web3 from 'web3' |
||||||
|
import * as assert from 'assert' |
||||||
|
import { Provider } from 'remix-simulator' |
||||||
|
|
||||||
|
import { compileFileOrFiles } from '../dist/compiler' |
||||||
|
import { deployAll } from '../dist/deployer' |
||||||
|
import { runTest } from '../dist/index' |
||||||
|
import { ResultsInterface, TestCbInterface, ResultCbInterface } from '../dist/index' |
||||||
|
|
||||||
|
function compileAndDeploy(filename: string, callback: Function) { |
||||||
|
let web3: Web3 = new Web3() |
||||||
|
web3.setProvider(new Provider()) |
||||||
|
let compilationData: object |
||||||
|
let accounts: string[] |
||||||
|
async.waterfall([ |
||||||
|
function getAccountList(next: Function): void { |
||||||
|
web3.eth.getAccounts((_err: Error | null | undefined, _accounts: string[]) => { |
||||||
|
accounts = _accounts |
||||||
|
next(_err) |
||||||
|
}) |
||||||
|
}, |
||||||
|
function compile(next: Function): void { |
||||||
|
compileFileOrFiles(filename, false, { accounts }, next) |
||||||
|
}, |
||||||
|
function deployAllContracts(compilationResult: object, next: Function): void { |
||||||
|
try { |
||||||
|
compilationData = compilationResult |
||||||
|
deployAll(compilationResult, web3, next) |
||||||
|
} catch (e) { |
||||||
|
throw e |
||||||
|
} |
||||||
|
} |
||||||
|
], function (_err: Error | null | undefined, contracts: any): void { |
||||||
|
callback(null, compilationData, contracts, accounts) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
describe('testRunner', () => { |
||||||
|
describe('#runTest', () => { |
||||||
|
describe('test with beforeAll', () => { |
||||||
|
let filename: string = 'tests/examples_1/simple_storage_test.sol' |
||||||
|
let tests: any[] = [], results: ResultsInterface; |
||||||
|
|
||||||
|
before((done) => { |
||||||
|
compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: object, contracts: any, accounts: object) => { |
||||||
|
const testCallback: TestCbInterface = (err, test) => { |
||||||
|
if (err) { throw err } |
||||||
|
tests.push(test) |
||||||
|
} |
||||||
|
const resultsCallback: ResultCbInterface = (err, _results) => { |
||||||
|
if (err) { throw err } |
||||||
|
results = _results |
||||||
|
done() |
||||||
|
} |
||||||
|
runTest('MyTest', contracts.MyTest, compilationData[filename]['MyTest'], { accounts }, testCallback, resultsCallback) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should 1 passing test', function () { |
||||||
|
assert.equal(results.passingNum, 2) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should 1 failing test', function () { |
||||||
|
assert.equal(results.failureNum, 2) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should returns 5 messages', function () { |
||||||
|
assert.deepEqual(tests, [ |
||||||
|
{ type: 'contract', value: 'MyTest', filename: 'tests/examples_1/simple_storage_test.sol' }, |
||||||
|
{ type: 'testFailure', value: 'Should trigger one fail', time: 1, context: 'MyTest', errMsg: 'the test 1 fails' }, |
||||||
|
{ type: 'testPass', value: 'Should trigger one pass', time: 1, context: 'MyTest' }, |
||||||
|
{ type: 'testPass', value: 'Initial value should be100', time: 1, context: 'MyTest' }, |
||||||
|
{ type: 'testFailure', value: 'Initial value should be200', time: 1, context: 'MyTest', errMsg: 'function returned false' } |
||||||
|
]) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
describe('test with beforeEach', function () { |
||||||
|
let filename = 'tests/examples_2/simple_storage_test.sol' |
||||||
|
let tests: any[] = [], results: ResultsInterface; |
||||||
|
|
||||||
|
before(function (done) { |
||||||
|
compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: object, contracts: any, accounts: object) { |
||||||
|
const testCallback: TestCbInterface = (err, test) => { |
||||||
|
if (err) { throw err } |
||||||
|
tests.push(test) |
||||||
|
} |
||||||
|
const resultsCallback: ResultCbInterface = (err, _results) => { |
||||||
|
if (err) { throw err } |
||||||
|
results = _results |
||||||
|
done() |
||||||
|
} |
||||||
|
runTest('MyTest', contracts.MyTest, compilationData[filename]['MyTest'], { accounts }, testCallback, resultsCallback) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should 2 passing tests', function () { |
||||||
|
assert.equal(results.passingNum, 2) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should 0 failing tests', function () { |
||||||
|
assert.equal(results.failureNum, 0) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should returns 3 messages', function () { |
||||||
|
assert.deepEqual(tests, [ |
||||||
|
{ type: 'contract', value: 'MyTest', filename: 'tests/examples_2/simple_storage_test.sol' }, |
||||||
|
{ type: 'testPass', value: 'Initial value should be100', time: 1, context: 'MyTest' }, |
||||||
|
{ type: 'testPass', value: 'Initial value should be200', time: 1, context: 'MyTest' } |
||||||
|
]) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
// Test string equality
|
||||||
|
describe('test string equality', function () { |
||||||
|
let filename = 'tests/examples_3/simple_string_test.sol' |
||||||
|
let tests: any[] = [], results: ResultsInterface; |
||||||
|
|
||||||
|
before(function (done) { |
||||||
|
compileAndDeploy(filename, (_err, compilationData, contracts, accounts) => { |
||||||
|
const testCallback: TestCbInterface = (err, test) => { |
||||||
|
if (err) { throw err } |
||||||
|
tests.push(test) |
||||||
|
} |
||||||
|
const resultsCallback: ResultCbInterface = (err, _results) => { |
||||||
|
if (err) { throw err } |
||||||
|
results = _results |
||||||
|
done() |
||||||
|
} |
||||||
|
runTest('StringTest', contracts.StringTest, compilationData[filename]['StringTest'], { accounts }, testCallback, resultsCallback) |
||||||
|
runTest('StringTest2', contracts.StringTest2, compilationData[filename]['StringTest2'], { accounts }, testCallback, resultsCallback) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should 2 passing tests', function () { |
||||||
|
assert.equal(results.passingNum, 2) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should 1 failing tests', function () { |
||||||
|
assert.equal(results.failureNum, 1) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should returns 3 messages', function () { |
||||||
|
assert.deepEqual(tests, [ |
||||||
|
{ type: 'contract', value: 'StringTest', filename: 'tests/examples_3/simple_string_test.sol' }, |
||||||
|
{ type: 'testFailure', value: 'Value should be hello world', time: 1, context: 'StringTest', "errMsg": "initial value is not correct" }, |
||||||
|
{ type: 'testPass', value: 'Value should not be hello wordl', time: 1, context: 'StringTest' }, |
||||||
|
{ type: 'testPass', value: 'Initial value should be hello', time: 1, context: 'StringTest' }, |
||||||
|
]) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
//Test signed/unsigned integer weight
|
||||||
|
describe('test number weight', function () { |
||||||
|
let filename = 'tests/number/number_test.sol' |
||||||
|
let tests: any[] = [], results: ResultsInterface; |
||||||
|
|
||||||
|
before(function (done) { |
||||||
|
compileAndDeploy(filename, (_err, compilationData, contracts, accounts) => { |
||||||
|
const testCallback: TestCbInterface = (err, test) => { |
||||||
|
if (err) { throw err } |
||||||
|
tests.push(test) |
||||||
|
} |
||||||
|
const resultsCallback: ResultCbInterface = (err, _results) => { |
||||||
|
if (err) { throw err } |
||||||
|
results = _results |
||||||
|
done() |
||||||
|
} |
||||||
|
runTest('IntegerTest', contracts.IntegerTest, compilationData[filename]['IntegerTest'], { accounts }, testCallback, resultsCallback) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should have 6 passing tests', function () { |
||||||
|
assert.equal(results.passingNum, 6) |
||||||
|
}) |
||||||
|
it('should have 2 failing tests', function () { |
||||||
|
assert.equal(results.failureNum, 2) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
// Test Transaction with different sender
|
||||||
|
describe('various sender', function () { |
||||||
|
let filename = 'tests/various_sender/sender_test.sol' |
||||||
|
let tests: any[] = [], results: ResultsInterface; |
||||||
|
|
||||||
|
before(function (done) { |
||||||
|
compileAndDeploy(filename, (_err, compilationData, contracts, accounts) => { |
||||||
|
const testCallback: TestCbInterface = (err, test) => { |
||||||
|
if (err) { throw err } |
||||||
|
tests.push(test) |
||||||
|
} |
||||||
|
const resultsCallback: ResultCbInterface = (err, _results) => { |
||||||
|
if (err) { throw err } |
||||||
|
results = _results |
||||||
|
done() |
||||||
|
} |
||||||
|
|
||||||
|
runTest('SenderTest', contracts.SenderTest, compilationData[filename]['SenderTest'], { accounts }, testCallback, resultsCallback) |
||||||
|
|
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should have 4 passing tests', function () { |
||||||
|
assert.equal(results.passingNum, 4) |
||||||
|
}) |
||||||
|
it('should have 1 failing tests', function () { |
||||||
|
assert.equal(results.failureNum, 0) |
||||||
|
}) |
||||||
|
}) |
||||||
|
}) |
||||||
|
}) |
@ -0,0 +1,24 @@ |
|||||||
|
{ |
||||||
|
"include": ["src"], |
||||||
|
"compilerOptions": { |
||||||
|
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ |
||||||
|
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ |
||||||
|
"lib": ["dom", "es2018"], /* Specify library files to be included in the compilation. */ |
||||||
|
"declaration": true, /* Generates corresponding '.d.ts' file. */ |
||||||
|
"sourceMap": true, /* Generates corresponding '.map' file. */ |
||||||
|
"outDir": "./dist", /* Redirect output structure to the directory. */ |
||||||
|
/* Strict Type-Checking Options */ |
||||||
|
"strict": true, /* Enable all strict type-checking options. */ |
||||||
|
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ |
||||||
|
/* Module Resolution Options */ |
||||||
|
"baseUrl": "./src", /* Base directory to resolve non-absolute module names. */ |
||||||
|
"paths": { "remix-tests": ["./"] }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ |
||||||
|
"typeRoots": [ |
||||||
|
"./@types", |
||||||
|
"./node_modules/@types" |
||||||
|
], |
||||||
|
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ |
||||||
|
/* Experimental Options */ |
||||||
|
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue