linting for remix-tests fixed

pull/861/head
aniket-engg 4 years ago
parent 84d0bac2fd
commit 2877354871
  1. 7
      libs/remix-tests/.eslintrc
  2. 74
      libs/remix-tests/src/assertionEvents.ts
  3. 323
      libs/remix-tests/src/compiler.ts
  4. 184
      libs/remix-tests/src/deployer.ts
  5. 24
      libs/remix-tests/src/fileSystem.ts
  6. 63
      libs/remix-tests/src/logger.ts
  7. 183
      libs/remix-tests/src/run.ts
  8. 269
      libs/remix-tests/src/runTestFiles.ts
  9. 213
      libs/remix-tests/src/runTestSources.ts
  10. 460
      libs/remix-tests/src/testRunner.ts
  11. 22
      libs/remix-tests/src/types.ts
  12. 2
      package.json
  13. 2
      workspace.json

@ -1,11 +1,8 @@
{
"extends": "../../.eslintrc",
"rules": {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/ban-ts-comment": "off"
"dot-notation": "off",
"no-unused-vars": "off"
},
"env": {
"browser": true,

@ -1,40 +1,40 @@
const assertionEvents = [
{
name: 'AssertionEvent',
params: ['bool', 'string', 'string']
},
{
name: 'AssertionEventUint',
params: ['bool', 'string', 'string', 'uint256', 'uint256']
},
{
name: 'AssertionEventInt',
params: ['bool', 'string', 'string', 'int256', 'int256']
},
{
name: 'AssertionEventBool',
params: ['bool', 'string', 'string', 'bool', 'bool']
},
{
name: 'AssertionEventAddress',
params: ['bool', 'string', 'string', 'address', 'address']
},
{
name: 'AssertionEventBytes32',
params: ['bool', 'string', 'string', 'bytes32', 'bytes32']
},
{
name: 'AssertionEventString',
params: ['bool', 'string', 'string', 'string', 'string']
},
{
name: 'AssertionEventUintInt',
params: ['bool', 'string', 'string', 'uint256', 'int256']
},
{
name: 'AssertionEventIntUint',
params: ['bool', 'string', 'string', 'int256', 'uint256']
}
{
name: 'AssertionEvent',
params: ['bool', 'string', 'string']
},
{
name: 'AssertionEventUint',
params: ['bool', 'string', 'string', 'uint256', 'uint256']
},
{
name: 'AssertionEventInt',
params: ['bool', 'string', 'string', 'int256', 'int256']
},
{
name: 'AssertionEventBool',
params: ['bool', 'string', 'string', 'bool', 'bool']
},
{
name: 'AssertionEventAddress',
params: ['bool', 'string', 'string', 'address', 'address']
},
{
name: 'AssertionEventBytes32',
params: ['bool', 'string', 'string', 'bytes32', 'bytes32']
},
{
name: 'AssertionEventString',
params: ['bool', 'string', 'string', 'string', 'string']
},
{
name: 'AssertionEventUintInt',
params: ['bool', 'string', 'string', 'uint256', 'int256']
},
{
name: 'AssertionEventIntUint',
params: ['bool', 'string', 'string', 'int256', 'uint256']
}
]
export default assertionEvents
export default assertionEvents

@ -2,26 +2,26 @@ import fs from './fileSystem'
import async from 'async'
import path from 'path'
import Log from './logger'
const logger = new Log()
const log = logger.logger
import { Compiler as RemixCompiler } from '@remix-project/remix-solidity'
import { SrcIfc, CompilerConfiguration, CompilationErrors } from './types'
const logger = new Log()
const log = logger.logger
function regexIndexOf (inputString: string, regex: RegExp, startpos = 0) {
const indexOf = inputString.substring(startpos).search(regex)
return (indexOf >= 0) ? (indexOf + (startpos)) : indexOf
const indexOf = inputString.substring(startpos).search(regex)
return (indexOf >= 0) ? (indexOf + (startpos)) : indexOf
}
function writeTestAccountsContract (accounts: string[]) {
const testAccountContract = require('../sol/tests_accounts.sol')
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 testAccountContract = require('../sol/tests_accounts.sol')
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)
}
/**
@ -29,47 +29,46 @@ function writeTestAccountsContract (accounts: string[]) {
* @param path file path to check
*/
function isRemixTestFile(path: string) {
return ['tests.sol', 'remix_tests.sol', 'remix_accounts.sol'].some(name => path.includes(name))
function isRemixTestFile (path: string) {
return ['tests.sol', 'remix_tests.sol', 'remix_accounts.sol'].some(name => path.includes(name))
}
/**
* @dev Process file to prepare sources object to be passed in solc compiler input
*
*
* See: https://solidity.readthedocs.io/en/latest/using-the-compiler.html#input-description
*
*
* @param filePath path of file to process
* @param sources existing 'sources' object in which keys are the "global" names of the source files and
* @param sources existing 'sources' object in which keys are the "global" names of the source files and
* value is object containing content of corresponding file with under key 'content'
* @param isRoot True, If file is a root test contract file which is getting processed, not an imported file
*/
function processFile(filePath: string, sources: SrcIfc, isRoot = false) {
const importRegEx = /import ['"](.+?)['"];/g
let group: RegExpExecArray| null = null
const isFileAlreadyInSources: boolean = Object.keys(sources).includes(filePath)
function processFile (filePath: string, sources: SrcIfc, isRoot = false) {
const importRegEx = /import ['"](.+?)['"];/g
let group: RegExpExecArray| null = null
const isFileAlreadyInSources: boolean = Object.keys(sources).includes(filePath)
// Return if file is a remix test file or already processed
if(isRemixTestFile(filePath) || isFileAlreadyInSources)
return
// Return if file is a remix test file or already processed
if (isRemixTestFile(filePath) || isFileAlreadyInSources) { return }
let content: string = fs.readFileSync(filePath, { encoding: 'utf-8' })
const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm
let content: string = fs.readFileSync(filePath, { encoding: 'utf-8' })
const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm
// import 'remix_tests.sol', if file is a root test contract file and doesn't already have it
if (isRoot && filePath.endsWith('_test.sol') && regexIndexOf(content, testFileImportRegEx) < 0) {
const includeTestLibs = '\nimport \'remix_tests.sol\';\n'
content = includeTestLibs.concat(content)
}
sources[filePath] = {content}
importRegEx.exec('') // Resetting state of RegEx
// Process each 'import' in file content
while ((group = importRegEx.exec(content))) {
const importedFile: string = group[1]
const importedFilePath: string = path.join(path.dirname(filePath), importedFile)
processFile(importedFilePath, sources)
}
// import 'remix_tests.sol', if file is a root test contract file and doesn't already have it
if (isRoot && filePath.endsWith('_test.sol') && regexIndexOf(content, testFileImportRegEx) < 0) {
const includeTestLibs = '\nimport \'remix_tests.sol\';\n'
content = includeTestLibs.concat(content)
}
sources[filePath] = { content }
importRegEx.exec('') // Resetting state of RegEx
// Process each 'import' in file content
while ((group = importRegEx.exec(content))) {
const importedFile: string = group[1]
const importedFilePath: string = path.join(path.dirname(filePath), importedFile)
processFile(importedFilePath, sources)
}
}
const userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-'
@ -81,88 +80,86 @@ const isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' elect
* @param isDirectory True, if path is a directory
* @param opts Options
* @param cb Callback
*
*
* TODO: replace this with remix's own compiler code
*/
export function compileFileOrFiles(filename: string, isDirectory: boolean, opts: any, compilerConfig: CompilerConfiguration, cb): void {
let compiler: any
const accounts: string[] = opts.accounts || []
const sources: SrcIfc = {
'tests.sol': { content: require('../sol/tests.sol') },
'remix_tests.sol': { content: require('../sol/tests.sol') },
'remix_accounts.sol': { content: writeTestAccountsContract(accounts) }
export function compileFileOrFiles (filename: string, isDirectory: boolean, opts: any, compilerConfig: CompilerConfiguration, cb): void {
let compiler: any
const accounts: string[] = opts.accounts || []
const sources: SrcIfc = {
'tests.sol': { content: require('../sol/tests.sol') },
'remix_tests.sol': { content: require('../sol/tests.sol') },
'remix_accounts.sol': { content: writeTestAccountsContract(accounts) }
}
const filepath: string = (isDirectory ? filename : path.dirname(filename))
try {
if (!isDirectory && fs.existsSync(filename)) {
if (filename.split('.').pop() === 'sol') {
processFile(filename, sources, true)
} else {
throw new Error('Not a solidity file')
}
} else {
// walkSync only if it is a directory
let testFileCount = 0
fs.walkSync(filepath, (foundpath: string) => {
// only process .sol files
if (foundpath.split('.').pop() === 'sol' && foundpath.endsWith('_test.sol')) {
testFileCount++
processFile(foundpath, sources, true)
}
})
if (testFileCount > 0) {
log.info(`${testFileCount} Solidity test file${testFileCount === 1 ? '' : 's'} found`)
} else {
log.error('No Solidity test file found. Make sure your test file ends with \'_test.sol\'')
process.exit()
}
}
const filepath: string = (isDirectory ? filename : path.dirname(filename))
try {
if(!isDirectory && fs.existsSync(filename)) {
if (filename.split('.').pop() === 'sol') {
processFile(filename, sources, true)
} else {
throw new Error('Not a solidity file')
}
} else {
// walkSync only if it is a directory
let testFileCount = 0
fs.walkSync(filepath, (foundpath: string) => {
// only process .sol files
if (foundpath.split('.').pop() === 'sol' && foundpath.endsWith('_test.sol')) {
testFileCount++
processFile(foundpath, sources, true)
}
} catch (e) { // eslint-disable-line no-useless-catch
throw e
} finally {
async.waterfall([
function loadCompiler (next) {
compiler = new RemixCompiler()
if (compilerConfig) {
const { currentCompilerUrl, evmVersion, optimize, runs } = compilerConfig
if (evmVersion) compiler.set('evmVersion', evmVersion)
if (optimize) compiler.set('optimize', optimize)
if (runs) compiler.set('runs', runs)
if (currentCompilerUrl) {
compiler.loadRemoteVersion(currentCompilerUrl)
compiler.event.register('compilerLoaded', this, function (version) {
next()
})
if(testFileCount > 0) {
log.info(`${testFileCount} Solidity test file${testFileCount===1?'':'s'} found`)
}
else {
log.error(`No Solidity test file found. Make sure your test file ends with '_test.sol'`)
process.exit()
}
} else {
compiler.onInternalCompilerLoaded()
next()
}
} else {
compiler.onInternalCompilerLoaded()
next()
}
} catch (e) { // eslint-disable-line no-useless-catch
throw e
} finally {
async.waterfall([
function loadCompiler(next) {
compiler = new RemixCompiler()
if(compilerConfig) {
const {currentCompilerUrl, evmVersion, optimize, runs} = compilerConfig
evmVersion ? compiler.set('evmVersion', evmVersion) : null
optimize ? compiler.set('optimize', optimize) : null
runs ? compiler.set('runs', runs) : null
if(currentCompilerUrl) {
compiler.loadRemoteVersion(currentCompilerUrl)
compiler.event.register('compilerLoaded', this, function (version) {
next()
})
} else {
compiler.onInternalCompilerLoaded()
next()
}
} else {
compiler.onInternalCompilerLoaded()
next()
}
},
function doCompilation(next) {
// @ts-ignore
compiler.event.register('compilationFinished', this, (success, data, source) => {
next(null, data)
})
compiler.compile(sources, filepath)
}
], function (err: Error | null | undefined, result: any) {
const error: Error[] = []
if (result.error) error.push(result.error)
const errors = (result.errors || error).filter((e) => e.type === 'Error' || e.severity === 'error')
if (errors.length > 0) {
if (!isBrowser) require('signale').fatal(errors)
return cb(new CompilationErrors(errors))
}
cb(err, result.contracts, result.sources) //return callback with contract details & ASTs
},
function doCompilation (next) {
// @ts-ignore
compiler.event.register('compilationFinished', this, (success, data, source) => {
next(null, data)
})
}
compiler.compile(sources, filepath)
}
], function (err: Error | null | undefined, result: any) {
const error: Error[] = []
if (result.error) error.push(result.error)
const errors = (result.errors || error).filter((e) => e.type === 'Error' || e.severity === 'error')
if (errors.length > 0) {
if (!isBrowser) require('signale').fatal(errors)
return cb(new CompilationErrors(errors))
}
cb(err, result.contracts, result.sources) // return callback with contract details & ASTs
})
}
}
/**
@ -173,53 +170,53 @@ export function compileFileOrFiles(filename: string, isDirectory: boolean, opts:
* @param opts Options
* @param cb Callback
*/
export function compileContractSources(sources: SrcIfc, compilerConfig: CompilerConfiguration, importFileCb: any, opts: any, cb): void {
let compiler, filepath: string
const accounts: string[] = 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['tests.sol'] = { content: require('../sol/tests.sol.js') }
sources['remix_tests.sol'] = { content: require('../sol/tests.sol.js') }
sources['remix_accounts.sol'] = { content: writeTestAccountsContract(accounts) }
export function compileContractSources (sources: SrcIfc, compilerConfig: CompilerConfiguration, importFileCb: any, opts: any, cb): void {
let compiler, filepath: string
const accounts: string[] = 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['tests.sol'] = { content: require('../sol/tests.sol.js') }
sources['remix_tests.sol'] = { content: require('../sol/tests.sol.js') }
sources['remix_accounts.sol'] = { content: writeTestAccountsContract(accounts) }
}
const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm
const includeTestLibs = '\nimport \'remix_tests.sol\';\n'
for (const file in sources) {
const c: string = sources[file].content
if (file.endsWith('_test.sol') && c && regexIndexOf(c, testFileImportRegEx) < 0) {
sources[file].content = includeTestLibs.concat(c)
}
const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm
const includeTestLibs = '\nimport \'remix_tests.sol\';\n'
for (const file in sources) {
const c: string = sources[file].content
if (file.endsWith('_test.sol') && c && regexIndexOf(c, testFileImportRegEx) < 0) {
sources[file].content = includeTestLibs.concat(c)
}
}
async.waterfall([
function loadCompiler (next) {
const { currentCompilerUrl, evmVersion, optimize, runs, usingWorker } = compilerConfig
compiler = new RemixCompiler(importFileCb)
compiler.set('evmVersion', evmVersion)
compiler.set('optimize', optimize)
compiler.set('runs', runs)
compiler.loadVersion(usingWorker, currentCompilerUrl)
// @ts-ignore
compiler.event.register('compilerLoaded', this, (version) => {
next()
})
},
function doCompilation (next) {
// @ts-ignore
compiler.event.register('compilationFinished', this, (success, data, source) => {
next(null, data)
})
compiler.compile(sources, filepath)
}
async.waterfall([
function loadCompiler (next) {
const {currentCompilerUrl, evmVersion, optimize, runs, usingWorker} = compilerConfig
compiler = new RemixCompiler(importFileCb)
compiler.set('evmVersion', evmVersion)
compiler.set('optimize', optimize)
compiler.set('runs', runs)
compiler.loadVersion(usingWorker, currentCompilerUrl)
// @ts-ignore
compiler.event.register('compilerLoaded', this, (version) => {
next()
})
},
function doCompilation (next) {
// @ts-ignore
compiler.event.register('compilationFinished', this, (success, data, source) => {
next(null, data)
})
compiler.compile(sources, filepath)
}
], function (err: Error | null | undefined , result: any) {
const error: Error[] = []
if (result.error) error.push(result.error)
const errors = (result.errors || error).filter((e) => e.type === 'Error' || e.severity === 'error')
if (errors.length > 0) {
if (!isBrowser) require('signale').fatal(errors)
return cb(new CompilationErrors(errors))
}
cb(err, result.contracts, result.sources) // return callback with contract details & ASTs
})
], function (err: Error | null | undefined, result: any) {
const error: Error[] = []
if (result.error) error.push(result.error)
const errors = (result.errors || error).filter((e) => e.type === 'Error' || e.severity === 'error')
if (errors.length > 0) {
if (!isBrowser) require('signale').fatal(errors)
return cb(new CompilationErrors(errors))
}
cb(err, result.contracts, result.sources) // return callback with contract details & ASTs
})
}

@ -11,108 +11,108 @@ import { compilationInterface } from './types'
* @param callback Callback
*/
export function deployAll(compileResult: compilationInterface, web3: Web3, withDoubleGas: boolean, callback) {
const compiledObject = {}
const contracts = {}
let accounts: string[] = []
export function deployAll (compileResult: compilationInterface, web3: Web3, withDoubleGas: boolean, callback) {
const compiledObject = {}
const contracts = {}
let accounts: string[] = []
async.waterfall([
function getAccountList(next) {
web3.eth.getAccounts((_err, _accounts) => {
accounts = _accounts
next()
})
},
function getContractData(next) {
for (const contractFile in compileResult) {
for (const contractName in compileResult[contractFile]) {
const contract = compileResult[contractFile][contractName]
async.waterfall([
function getAccountList (next) {
web3.eth.getAccounts((_err, _accounts) => {
accounts = _accounts
next()
})
},
function getContractData (next) {
for (const contractFile in compileResult) {
for (const contractName in compileResult[contractFile]) {
const contract = compileResult[contractFile][contractName]
const className = contractName
const filename = contractFile
const className = contractName
const filename = contractFile
const abi = contract.abi
const code = contract.evm.bytecode.object
const abi = contract.abi
const 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
compiledObject[className] = {}
compiledObject[className].abi = abi
compiledObject[className].code = code
compiledObject[className].filename = filename
compiledObject[className].className = className
compiledObject[className].raw = contract
if (contractFile.endsWith('_test.sol')) {
compiledObject[className].isTest = true
}
}
}
next()
},
function determineContractsToDeploy(next) {
const contractsToDeploy: string[] = ['Assert']
const allContracts = Object.keys(compiledObject)
for (const contractName of allContracts) {
if (contractName === 'Assert') {
continue
}
if (compiledObject[contractName].isTest) {
contractsToDeploy.push(contractName)
}
}
next(null, contractsToDeploy)
},
function deployContracts(contractsToDeploy: string[], next) {
const deployRunner = (deployObject, contractObject, contractName, filename, callback) => {
deployObject.estimateGas().then((gasValue) => {
const gasBase = Math.ceil(gasValue * 1.2)
const gas = withDoubleGas ? gasBase * 2 : gasBase
deployObject.send({
from: accounts[0],
gas: gas
}).on('receipt', function (receipt) {
contractObject.options.address = receipt.contractAddress
contractObject.options.from = accounts[0]
contractObject.options.gas = 5000 * 1000
compiledObject[contractName].deployedAddress = receipt.contractAddress
if (contractFile.endsWith('_test.sol')) {
compiledObject[className].isTest = true
}
}
}
next()
},
function determineContractsToDeploy (next) {
const contractsToDeploy: string[] = ['Assert']
const allContracts = Object.keys(compiledObject)
contracts[contractName] = contractObject
contracts[contractName].filename = filename
for (const contractName of allContracts) {
if (contractName === 'Assert') {
continue
}
if (compiledObject[contractName].isTest) {
contractsToDeploy.push(contractName)
}
}
next(null, contractsToDeploy)
},
function deployContracts (contractsToDeploy: string[], next) {
const deployRunner = (deployObject, contractObject, contractName, filename, callback) => {
deployObject.estimateGas().then((gasValue) => {
const gasBase = Math.ceil(gasValue * 1.2)
const gas = withDoubleGas ? gasBase * 2 : gasBase
deployObject.send({
from: accounts[0],
gas: gas
}).on('receipt', function (receipt) {
contractObject.options.address = receipt.contractAddress
contractObject.options.from = accounts[0]
contractObject.options.gas = 5000 * 1000
compiledObject[contractName].deployedAddress = receipt.contractAddress
callback(null, { result: { createdAddress: receipt.contractAddress } }) // TODO this will only work with JavaScriptV VM
}).on('error', function (err) {
console.error(err)
callback(err)
})
})
}
contracts[contractName] = contractObject
contracts[contractName].filename = filename
async.eachOfLimit(contractsToDeploy, 1, function (contractName, index, nextEach) {
const contract = compiledObject[contractName]
const encodeDataFinalCallback = (error, contractDeployData) => {
if (error) return nextEach(error)
const contractObject = new web3.eth.Contract(contract.abi)
const deployObject = contractObject.deploy({arguments: [], data: '0x' + contractDeployData.dataHex})
deployRunner(deployObject, contractObject, contractName, contract.filename, (error) => { nextEach(error) })
}
callback(null, { result: { createdAddress: receipt.contractAddress } }) // TODO this will only work with JavaScriptV VM
}).on('error', function (err) {
console.error(err)
callback(err)
})
})
}
const encodeDataStepCallback = (msg) => { console.dir(msg) }
async.eachOfLimit(contractsToDeploy, 1, function (contractName, index, nextEach) {
const contract = compiledObject[contractName]
const encodeDataFinalCallback = (error, contractDeployData) => {
if (error) return nextEach(error)
const contractObject = new web3.eth.Contract(contract.abi)
const deployObject = contractObject.deploy({ arguments: [], data: '0x' + contractDeployData.dataHex })
deployRunner(deployObject, contractObject, contractName, contract.filename, (error) => { nextEach(error) })
}
const encodeDataDeployLibraryCallback = (libData, callback) => {
const abi = compiledObject[libData.data.contractName].abi
const code = compiledObject[libData.data.contractName].code
const libraryObject = new web3.eth.Contract(abi)
const deployObject = libraryObject.deploy({arguments: [], data: '0x' + code})
deployRunner(deployObject, libraryObject, libData.data.contractName, contract.filename, callback)
}
const encodeDataStepCallback = (msg) => { console.dir(msg) }
const funAbi = null // no need to set the abi for encoding the constructor
const params = '' // we suppose that the test contract does not have any param in the constructor
execution.txFormat.encodeConstructorCallAndDeployLibraries(contractName, contract.raw, compileResult, params, funAbi, encodeDataFinalCallback, encodeDataStepCallback, encodeDataDeployLibraryCallback)
}, function (err) {
if(err) next(err)
next(null, contracts)
})
const encodeDataDeployLibraryCallback = (libData, callback) => {
const abi = compiledObject[libData.data.contractName].abi
const code = compiledObject[libData.data.contractName].code
const libraryObject = new web3.eth.Contract(abi)
const deployObject = libraryObject.deploy({ arguments: [], data: '0x' + code })
deployRunner(deployObject, libraryObject, libData.data.contractName, contract.filename, callback)
}
], callback)
const funAbi = null // no need to set the abi for encoding the constructor
const params = '' // we suppose that the test contract does not have any param in the constructor
execution.txFormat.encodeConstructorCallAndDeployLibraries(contractName, contract.raw, compileResult, params, funAbi, encodeDataFinalCallback, encodeDataStepCallback, encodeDataDeployLibraryCallback)
}, function (err) {
if (err) next(err)
next(null, contracts)
})
}
], callback)
}

@ -1,20 +1,20 @@
// Extend fs
const fs: any = require('fs')
import path from 'path'
const fs: any = require('fs')
// https://github.com/mikeal/node-utils/blob/master/file/lib/main.js
fs.walkSync = function (start: string, callback) {
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)
}
})
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,56 +1,57 @@
import colors from 'colors'
import winston, { Logger, LoggerOptions } from 'winston'
import timestamp from 'time-stamp';
import timestamp from 'time-stamp'
import supportsColor from 'color-support'
function hasFlag (flag: string) {
return ((typeof (process) !== 'undefined') && (process.argv.indexOf('--' + flag) !== -1))
return ((typeof (process) !== 'undefined') && (process.argv.indexOf('--' + flag) !== -1))
}
function addColor (str: string) {
if (hasFlag('no-color')) {
return str
}
if (hasFlag('no-color')) {
return str
}
if (hasFlag('color')) {
return colors.gray(str)
}
if (hasFlag('color')) {
return colors.gray(str)
}
if (supportsColor()) {
return colors.gray(str)
}
if (supportsColor()) {
return colors.gray(str)
}
return str
return str
}
function getTimestamp () {
return '[' + addColor(timestamp('HH:mm:ss')) + ']'
return '[' + addColor(timestamp('HH:mm:ss')) + ']'
}
// create winston logger format
const logFmt = winston.format.printf((info) => {
return `${getTimestamp()} ${info.level}: ${info.message}`
return `${getTimestamp()} ${info.level}: ${info.message}`
})
class Log {
logger: Logger;
constructor () {
this.logger = winston.createLogger({
level: 'info',
transports: [new winston.transports.Console()],
format: winston.format.combine(
winston.format.colorize({ all: true }),
logFmt
)
})
this.logger = winston.createLogger({
level: 'info',
transports: [new winston.transports.Console()],
format: winston.format.combine(
winston.format.colorize({ all: true }),
logFmt
)
})
}
setVerbosity (v: LoggerOptions["level"]): void {
this.logger.configure({
level: v,
transports: [new winston.transports.Console()],
format: winston.format.combine(
winston.format.colorize({ all: true }),
logFmt
)
})
setVerbosity (v: LoggerOptions['level']): void {
this.logger.configure({
level: v,
transports: [new winston.transports.Console()],
format: winston.format.combine(
winston.format.colorize({ all: true }),
logFmt
)
})
}
}

@ -7,29 +7,29 @@ import fs from './fileSystem'
import { Provider } from '@remix-project/remix-simulator'
import { CompilerConfiguration } from './types'
import Log from './logger'
import colors from 'colors'
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 levels = {
0: 'error',
1: 'warn',
2: 'info',
3: 'verbose',
4: 'debug',
5: 'silly'
}
return levels[v]
}
function mapOptimize (v: string) {
const optimize = {
'true': true,
'false': false
}
return optimize[v];
const optimize = {
true: true,
false: false
}
return optimize[v]
}
const version = require('../package.json').version
@ -37,92 +37,91 @@ const version = require('../package.json').version
commander.version(version)
commander.command('version').description('output the version number').action(function () {
console.log(version)
console.log(version)
})
commander.command('help').description('output usage information').action(function () {
commander.help()
commander.help()
})
// get current version
commander
.option('-c, --compiler <string>', 'set compiler version (e.g: 0.6.1, 0.7.1 etc)')
.option('-e, --evm <string>', 'set EVM version (e.g: petersburg, istanbul etc)')
.option('-o, --optimize <bool>', 'enable/disable optimization', mapOptimize)
.option('-r, --runs <number>', 'set runs (e.g: 150, 250 etc)')
.option('-v, --verbose <level>', 'set verbosity level (0 to 5)', mapVerbosity)
.action(async (testsPath) => {
// Check if path exists
if (!fs.existsSync(testsPath)) {
log.error(testsPath + ' not found')
process.exit(1)
}
// Check if path is for a directory
const isDirectory = fs.lstatSync(testsPath).isDirectory()
// If path is for a file, file name must have `_test.sol` suffix
if(!isDirectory && !testsPath.endsWith('_test.sol')) {
log.error('Test filename should end with "_test.sol"')
process.exit()
}
// 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 compilerConfig = {} as CompilerConfiguration
if (commander.compiler) {
const compVersion = commander.compiler
const baseURL = 'https://binaries.soliditylang.org/wasm/'
const response: AxiosResponse = await axios.get(baseURL + 'list.json')
const { releases, latestRelease } = response.data
const compString = releases[compVersion]
if(!compString) {
log.error(`No compiler found in releases with version ${compVersion}`)
process.exit()
} else {
compilerConfig.currentCompilerUrl = compString.replace('soljson-', '').replace('.js', '')
log.info(`Compiler version set to ${compVersion}. Latest version is ${latestRelease}`)
}
}
if (commander.evm) {
compilerConfig.evmVersion = commander.evm
log.info(`EVM set to ${compilerConfig.evmVersion}`)
}
if (commander.optimize) {
compilerConfig.optimize = commander.optimize
log.info(`Optimization is ${compilerConfig.optimize ? 'enabled' : 'disabled'}`)
}
if (commander.runs) {
if(!commander.optimize) {
log.error(`Optimization should be enabled for runs`)
process.exit()
}
compilerConfig.runs = commander.runs
log.info(`Runs set to ${compilerConfig.runs}`)
}
const web3 = new Web3()
const provider: any = new Provider()
await provider.init()
web3.setProvider(provider)
runTestFiles(path.resolve(testsPath), isDirectory, web3, compilerConfig)
})
.option('-c, --compiler <string>', 'set compiler version (e.g: 0.6.1, 0.7.1 etc)')
.option('-e, --evm <string>', 'set EVM version (e.g: petersburg, istanbul etc)')
.option('-o, --optimize <bool>', 'enable/disable optimization', mapOptimize)
.option('-r, --runs <number>', 'set runs (e.g: 150, 250 etc)')
.option('-v, --verbose <level>', 'set verbosity level (0 to 5)', mapVerbosity)
.action(async (testsPath) => {
// Check if path exists
if (!fs.existsSync(testsPath)) {
log.error(testsPath + ' not found')
process.exit(1)
}
// Check if path is for a directory
const isDirectory = fs.lstatSync(testsPath).isDirectory()
// If path is for a file, file name must have `_test.sol` suffix
if (!isDirectory && !testsPath.endsWith('_test.sol')) {
log.error('Test filename should end with "_test.sol"')
process.exit()
}
// 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)
}
const compilerConfig = {} as CompilerConfiguration
if (commander.compiler) {
const compVersion = commander.compiler
const baseURL = 'https://binaries.soliditylang.org/wasm/'
const response: AxiosResponse = await axios.get(baseURL + 'list.json')
const { releases, latestRelease } = response.data
const compString = releases[compVersion]
if (!compString) {
log.error(`No compiler found in releases with version ${compVersion}`)
process.exit()
} else {
compilerConfig.currentCompilerUrl = compString.replace('soljson-', '').replace('.js', '')
log.info(`Compiler version set to ${compVersion}. Latest version is ${latestRelease}`)
}
}
if (commander.evm) {
compilerConfig.evmVersion = commander.evm
log.info(`EVM set to ${compilerConfig.evmVersion}`)
}
if (commander.optimize) {
compilerConfig.optimize = commander.optimize
log.info(`Optimization is ${compilerConfig.optimize ? 'enabled' : 'disabled'}`)
}
if (commander.runs) {
if (!commander.optimize) {
log.error('Optimization should be enabled for runs')
process.exit()
}
compilerConfig.runs = commander.runs
log.info(`Runs set to ${compilerConfig.runs}`)
}
const web3 = new Web3()
const provider: any = new Provider()
await provider.init()
web3.setProvider(provider)
runTestFiles(path.resolve(testsPath), isDirectory, web3, compilerConfig)
})
if (!process.argv.slice(2).length) {
log.error('Please specify a file or directory path')
process.exit()
log.error('Please specify a file or directory path')
process.exit()
}
commander.parse(process.argv)

@ -3,7 +3,7 @@ import fs from './fileSystem'
import { runTest } from './testRunner'
import { TestResultInterface, ResultsInterface, CompilerConfiguration, compilationInterface, ASTInterface, Options, AstNode } from './types'
import colors from 'colors'
import Web3 from 'web3';
import Web3 from 'web3'
import { compileFileOrFiles } from './compiler'
import { deployAll } from './deployer'
@ -18,144 +18,143 @@ import { deployAll } from './deployer'
*/
// eslint-disable-next-line @typescript-eslint/no-empty-function
export function runTestFiles(filepath: string, isDirectory: boolean, web3: Web3, compilerConfig: CompilerConfiguration, finalCallback: any = () => {}, opts?: Options) {
opts = opts || {}
compilerConfig = compilerConfig || {} as CompilerConfiguration
const sourceASTs: any = {}
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'
}
}
export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3, compilerConfig: CompilerConfiguration, finalCallback: any = () => {}, opts?: Options) {
opts = opts || {}
compilerConfig = compilerConfig || {} as CompilerConfiguration
const sourceASTs: any = {}
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: Error | null | undefined, _accounts) => {
accounts = _accounts
next(null)
})
},
function compile(next) {
compileFileOrFiles(filepath, isDirectory, { accounts }, compilerConfig, next)
},
function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next) {
// Extract AST of test contract file source
for(const filename in asts) {
if(filename.endsWith('_test.sol'))
sourceASTs[filename] = asts[filename].ast
}
deployAll(compilationResult, web3, false, (err, contracts) => {
if (err) {
next(err)
}
next(null, compilationResult, contracts)
})
},
function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) {
const contractsToTest: string[] = []
const contractsToTestDetails: any[] = []
const gatherContractsFrom = function(filename: string) {
if (!filename.endsWith('_test.sol')) {
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: string[], contractsToTestDetails: any[], contracts: any, next) {
let totalPassing = 0
let totalFailing = 0
let totalTime = 0
const errors: any[] = []
}
const signale = new Signale(options)
let accounts = opts['accounts'] || null
async.waterfall([
function getAccountList (next) {
if (accounts) return next(null)
web3.eth.getAccounts((_err: Error | null | undefined, _accounts) => {
accounts = _accounts
next(null)
})
},
function compile (next) {
compileFileOrFiles(filepath, isDirectory, { accounts }, compilerConfig, next)
},
function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next) {
// Extract AST of test contract file source
for (const filename in asts) {
if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast }
}
deployAll(compilationResult, web3, false, (err, contracts) => {
if (err) {
next(err)
}
next(null, compilationResult, contracts)
})
},
function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) {
const contractsToTest: string[] = []
const contractsToTestDetails: any[] = []
const gatherContractsFrom = function (filename: string) {
if (!filename.endsWith('_test.sol')) {
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: string[], contractsToTestDetails: any[], contracts: any, next) {
let totalPassing = 0
let totalFailing = 0
let totalTime = 0
const 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.error(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()
}
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.error(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 {
const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']]
runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { 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)
}
async.eachOfLimit(contractsToTest, 1, (contractName: string, index, cb) => {
try {
const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']]
runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { 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('')
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) + ') ' + colors.bold(error.context + ': ') + error.value)
console.log('')
console.log(colors.red('\t error: ' + error.errMsg))
console.log(colors.green('\t expected value to be '+ error.assertMethod + ' to: ' + error.expected))
console.log(colors.red('\t returned: ' + error.returned))
})
console.log('')
errors.forEach((error, index) => {
console.log(' ' + (index + 1) + ') ' + colors.bold(error.context + ': ') + error.value)
console.log('')
console.log(colors.red('\t error: ' + error.errMsg))
console.log(colors.green('\t expected value to be ' + error.assertMethod + ' to: ' + error.expected))
console.log(colors.red('\t returned: ' + error.returned))
})
console.log('')
next()
})
}
], finalCallback)
next()
})
}
], finalCallback)
}

@ -1,21 +1,23 @@
import async, { ErrorCallback } from 'async'
require('colors')
import { compileContractSources } from './compiler'
import { deployAll } from './deployer'
import { runTest } from './testRunner'
import Web3 from 'web3';
import Web3 from 'web3'
import { Provider } from '@remix-project/remix-simulator'
import { FinalResult, SrcIfc, compilationInterface, ASTInterface, Options,
TestResultInterface, AstNode, CompilerConfiguration } from './types'
import {
FinalResult, SrcIfc, compilationInterface, ASTInterface, Options,
TestResultInterface, AstNode, CompilerConfiguration
} from './types'
require('colors')
const createWeb3Provider = async function () {
const web3 = new Web3()
const provider: any = new Provider()
await provider.init()
web3.setProvider(provider)
return web3
const web3 = new Web3()
const provider: any = new Provider()
await provider.init()
web3.setProvider(provider)
return web3
}
/**
@ -28,110 +30,107 @@ const createWeb3Provider = async function () {
* @param importFileCb Import file callback
* @param opts Options
*/
export async function runTestSources(contractSources: SrcIfc, compilerConfig: CompilerConfiguration, testCallback, resultCallback, finalCallback: any, importFileCb, opts: Options) {
opts = opts || {}
const sourceASTs: any = {}
const web3 = opts.web3 || await createWeb3Provider()
let accounts: string[] | null = 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, compilerConfig, importFileCb, { accounts }, next)
},
function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next) {
for(const filename in asts) {
if(filename.endsWith('_test.sol'))
sourceASTs[filename] = asts[filename].ast
}
deployAll(compilationResult, web3, false, (err, contracts) => {
if (err) {
// If contract deployment fails because of 'Out of Gas' error, try again with double gas
// This is temporary, should be removed when remix-tests will have a dedicated UI to
// accept deployment params from UI
if(err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) {
deployAll(compilationResult, web3, true, (error, contracts) => {
if (error) next([{message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error'}]) // IDE expects errors in array
else next(null, compilationResult, contracts)
})
} else
next([{message: 'contract deployment failed: ' + err.message, severity: 'error'}]) // IDE expects errors in array
} else
next(null, compilationResult, contracts)
export async function runTestSources (contractSources: SrcIfc, compilerConfig: CompilerConfiguration, testCallback, resultCallback, finalCallback: any, importFileCb, opts: Options) {
opts = opts || {}
const sourceASTs: any = {}
const web3 = opts.web3 || await createWeb3Provider()
let accounts: string[] | null = 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, compilerConfig, importFileCb, { accounts }, next)
},
function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next) {
for (const filename in asts) {
if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast }
}
deployAll(compilationResult, web3, false, (err, contracts) => {
if (err) {
// If contract deployment fails because of 'Out of Gas' error, try again with double gas
// This is temporary, should be removed when remix-tests will have a dedicated UI to
// accept deployment params from UI
if (err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) {
deployAll(compilationResult, web3, true, (error, contracts) => {
if (error) next([{ message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error' }]) // IDE expects errors in array
else next(null, compilationResult, contracts)
})
},
function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) {
const contractsToTest: string[] = []
const contractsToTestDetails: any[] = []
} else { next([{ message: 'contract deployment failed: ' + err.message, severity: 'error' }]) } // IDE expects errors in array
} else { next(null, compilationResult, contracts) }
})
},
function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) {
const contractsToTest: string[] = []
const contractsToTestDetails: any[] = []
for (const filename in compilationResult) {
if (!filename.endsWith('_test.sol')) {
continue
}
Object.keys(compilationResult[filename]).forEach(contractName => {
contractsToTestDetails.push(compilationResult[filename][contractName])
contractsToTest.push(contractName)
})
}
next(null, contractsToTest, contractsToTestDetails, contracts)
},
function runTests(contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) {
let totalPassing = 0
let totalFailing = 0
let totalTime = 0
const errors: any[] = []
const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) {
if (result.type === 'testFailure') {
errors.push(result)
}
testCallback(result)
}
for (const filename in compilationResult) {
if (!filename.endsWith('_test.sol')) {
continue
}
Object.keys(compilationResult[filename]).forEach(contractName => {
contractsToTestDetails.push(compilationResult[filename][contractName])
contractsToTest.push(contractName)
})
}
next(null, contractsToTest, contractsToTestDetails, contracts)
},
function runTests (contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) {
let totalPassing = 0
let totalFailing = 0
let totalTime = 0
const errors: any[] = []
// eslint-disable-next-line handle-callback-err
const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) {
if (result.type === 'testFailure') {
errors.push(result)
}
testCallback(result)
}
const _resultsCallback = function (_err, result, cb) {
resultCallback(_err, result, () => {}) //eslint-disable-line @typescript-eslint/no-empty-function
totalPassing += result.passingNum
totalFailing += result.failureNum
totalTime += result.timePassed
cb()
}
const _resultsCallback = function (_err, result, cb) {
resultCallback(_err, result, () => {}) // eslint-disable-line @typescript-eslint/no-empty-function
totalPassing += result.passingNum
totalFailing += result.failureNum
totalTime += result.timePassed
cb()
}
async.eachOfLimit(contractsToTest, 1, (contractName: string, index: string | number, cb: ErrorCallback) => {
const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']]
runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts }, _testCallback, (err, result) => {
if (err) {
return cb(err)
}
_resultsCallback(null, result, cb)
})
}, function (err) {
if (err) {
return next(err)
}
async.eachOfLimit(contractsToTest, 1, (contractName: string, index: string | number, cb: ErrorCallback) => {
const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']]
runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts }, _testCallback, (err, result) => {
if (err) {
return cb(err)
}
_resultsCallback(null, result, cb)
})
}, function (err) {
if (err) {
return next(err)
}
const finalResults: FinalResult = {
totalPassing: 0,
totalFailing: 0,
totalTime: 0,
errors: [],
}
const finalResults: FinalResult = {
totalPassing: 0,
totalFailing: 0,
totalTime: 0,
errors: []
}
finalResults.totalPassing = totalPassing || 0
finalResults.totalFailing = totalFailing || 0
finalResults.totalTime = totalTime || 0
finalResults.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})
})
errors.forEach((error, _index) => {
finalResults.errors.push({ context: error.context, value: error.value, message: error.errMsg })
})
next(null, finalResults)
})
}
], finalCallback)
next(null, finalResults)
})
}
], finalCallback)
}

@ -1,9 +1,11 @@
import async from 'async'
import * as changeCase from 'change-case'
import Web3 from 'web3';
import Web3 from 'web3'
import assertionEvents from './assertionEvents'
import { RunListInterface, TestCbInterface, TestResultInterface, ResultCbInterface,
CompiledContract, AstNode, Options, FunctionDescription, UserDocumentation } from './types'
import {
RunListInterface, TestCbInterface, TestResultInterface, ResultCbInterface,
CompiledContract, AstNode, Options, FunctionDescription, UserDocumentation
} from './types'
/**
* @dev Get function name using method signature
@ -12,12 +14,12 @@ import { RunListInterface, TestCbInterface, TestResultInterface, ResultCbInterfa
*/
function getFunctionFullName (signature: string, methodIdentifiers: Record <string, string>): string | null {
for (const method in methodIdentifiers) {
if (signature.replace('0x', '') === methodIdentifiers[method].replace('0x', '')) {
return method
}
for (const method in methodIdentifiers) {
if (signature.replace('0x', '') === methodIdentifiers[method].replace('0x', '')) {
return method
}
return null
}
return null
}
/**
@ -25,8 +27,8 @@ function getFunctionFullName (signature: string, methodIdentifiers: Record <stri
* @param funcABI function ABI
*/
function isConstant(funcABI: FunctionDescription): boolean {
return (funcABI.constant || funcABI.stateMutability === 'view' || funcABI.stateMutability === 'pure')
function isConstant (funcABI: FunctionDescription): boolean {
return (funcABI.constant || funcABI.stateMutability === 'view' || funcABI.stateMutability === 'pure')
}
/**
@ -34,8 +36,8 @@ function isConstant(funcABI: FunctionDescription): boolean {
* @param funcABI function ABI
*/
function isPayable(funcABI: FunctionDescription): boolean {
return (funcABI.payable || funcABI.stateMutability === 'payable')
function isPayable (funcABI: FunctionDescription): boolean {
return (funcABI.payable || funcABI.stateMutability === 'payable')
}
/**
@ -44,8 +46,8 @@ function isPayable(funcABI: FunctionDescription): boolean {
* @param name name
*/
function isNodeName(node: AstNode, name: string): boolean {
return node.name === name
function isNodeName (node: AstNode, name: string): boolean {
return node.name === name
}
/**
@ -54,8 +56,8 @@ function isNodeName(node: AstNode, name: string): boolean {
* @param type type
*/
function isNodeType(node: AstNode, type: string): boolean {
return node.nodeType === type
function isNodeType (node: AstNode, type: string): boolean {
return node.nodeType === type
}
/**
@ -64,8 +66,8 @@ function isNodeType(node: AstNode, type: string): boolean {
* @param typesList list of types
*/
function isNodeTypeIn(node: AstNode, typesList: string[]): boolean {
return typesList.includes(node.nodeType)
function isNodeTypeIn (node: AstNode, typesList: string[]): boolean {
return typesList.includes(node.nodeType)
}
/**
@ -76,10 +78,10 @@ function isNodeTypeIn(node: AstNode, typesList: string[]): boolean {
*/
function getOverridedSender (userdoc: UserDocumentation, signature: string, methodIdentifiers: Record <string, string>): string | null {
const fullName: string | null = getFunctionFullName(signature, methodIdentifiers)
const senderRegex = /#sender: account-+(\d)/g
const accountIndex: RegExpExecArray | null = fullName && userdoc.methods[fullName] ? senderRegex.exec(userdoc.methods[fullName].notice) : null
return fullName && accountIndex ? accountIndex[1] : null
const fullName: string | null = getFunctionFullName(signature, methodIdentifiers)
const senderRegex = /#sender: account-+(\d)/g
const accountIndex: RegExpExecArray | null = fullName && userdoc.methods[fullName] ? senderRegex.exec(userdoc.methods[fullName].notice) : null
return fullName && accountIndex ? accountIndex[1] : null
}
/**
@ -90,10 +92,10 @@ function getOverridedSender (userdoc: UserDocumentation, signature: string, meth
*/
function getProvidedValue (userdoc: UserDocumentation, signature: string, methodIdentifiers: Record <string, string>): string | null {
const fullName: string | null = getFunctionFullName(signature, methodIdentifiers)
const valueRegex = /#value: (\d+)/g
const value: RegExpExecArray | null = fullName && userdoc.methods[fullName] ? valueRegex.exec(userdoc.methods[fullName].notice) : null
return fullName && value ? value[1] : null
const fullName: string | null = getFunctionFullName(signature, methodIdentifiers)
const valueRegex = /#value: (\d+)/g
const value: RegExpExecArray | null = fullName && userdoc.methods[fullName] ? valueRegex.exec(userdoc.methods[fullName].notice) : null
return fullName && value ? value[1] : null
}
/**
@ -103,36 +105,36 @@ function getProvidedValue (userdoc: UserDocumentation, signature: string, method
*/
function getAvailableFunctions (fileAST: AstNode, testContractName: string): string[] {
let funcList: string[] = []
if(fileAST.nodes && fileAST.nodes.length > 0) {
const contractAST: AstNode[] = fileAST.nodes.filter(node => isNodeName(node, testContractName) && isNodeType(node, 'ContractDefinition'))
if(contractAST.length > 0 && contractAST[0].nodes) {
const funcNodes: AstNode[] = contractAST[0].nodes.filter(node => ((node.kind === "function" && isNodeType(node, 'FunctionDefinition')) || isNodeType(node, 'FunctionDefinition')))
funcList = funcNodes.map(node => node.name)
}
let funcList: string[] = []
if (fileAST.nodes && fileAST.nodes.length > 0) {
const contractAST: AstNode[] = fileAST.nodes.filter(node => isNodeName(node, testContractName) && isNodeType(node, 'ContractDefinition'))
if (contractAST.length > 0 && contractAST[0].nodes) {
const funcNodes: AstNode[] = contractAST[0].nodes.filter(node => ((node.kind === 'function' && isNodeType(node, 'FunctionDefinition')) || isNodeType(node, 'FunctionDefinition')))
funcList = funcNodes.map(node => node.name)
}
return funcList;
}
return funcList
}
function getAssertMethodLocation (fileAST: AstNode, testContractName: string, functionName: string, assertMethod: string): string {
if(fileAST.nodes?.length) {
const contractAST: AstNode = fileAST.nodes.find(node => isNodeName(node, testContractName) && isNodeType(node, 'ContractDefinition'))
if(contractAST?.nodes?.length) {
const funcNode: AstNode = contractAST.nodes.find(node => isNodeName(node, functionName) && isNodeType(node, 'FunctionDefinition'))
// Check if statement nodeType is 'ExpressionStatement' or 'Return', for examples:
// Assert.equal(foo.get(), 100, "initial value is not correct");
// return Assert.equal(foo.get(), 100, "initial value is not correct");
const expressions = funcNode.body.statements.filter(s =>
isNodeTypeIn(s, ['ExpressionStatement', 'Return'])
&& isNodeType(s.expression, 'FunctionCall'))
const assetExpression = expressions.find(e => e.expression.expression
&& isNodeType(e.expression.expression, 'MemberAccess')
&& e.expression.expression.memberName === assertMethod
&& isNodeName(e.expression.expression.expression, 'Assert')
)
return assetExpression?.expression?.src
}
if (fileAST.nodes?.length) {
const contractAST: AstNode = fileAST.nodes.find(node => isNodeName(node, testContractName) && isNodeType(node, 'ContractDefinition'))
if (contractAST?.nodes?.length) {
const funcNode: AstNode = contractAST.nodes.find(node => isNodeName(node, functionName) && isNodeType(node, 'FunctionDefinition'))
// Check if statement nodeType is 'ExpressionStatement' or 'Return', for examples:
// Assert.equal(foo.get(), 100, "initial value is not correct");
// return Assert.equal(foo.get(), 100, "initial value is not correct");
const expressions = funcNode.body.statements.filter(s =>
isNodeTypeIn(s, ['ExpressionStatement', 'Return']) &&
isNodeType(s.expression, 'FunctionCall'))
const assetExpression = expressions.find(e => e.expression.expression &&
isNodeType(e.expression.expression, 'MemberAccess') &&
e.expression.expression.memberName === assertMethod &&
isNodeName(e.expression.expression.expression, 'Assert')
)
return assetExpression?.expression?.src
}
}
}
/**
@ -142,15 +144,15 @@ function getAssertMethodLocation (fileAST: AstNode, testContractName: string, fu
*/
function getTestFunctionsInterface (jsonInterface: FunctionDescription[], funcList: string[]): FunctionDescription[] {
const functionsInterface: FunctionDescription[] = []
const specialFunctions: string[] = ['beforeAll', 'beforeEach', 'afterAll', 'afterEach']
for(const func of funcList){
if(!specialFunctions.includes(func)) {
const funcInterface: FunctionDescription | undefined = jsonInterface.find(node => node.type === 'function' && node.name === func)
if(funcInterface) functionsInterface.push(funcInterface)
}
const functionsInterface: FunctionDescription[] = []
const specialFunctions: string[] = ['beforeAll', 'beforeEach', 'afterAll', 'afterEach']
for (const func of funcList) {
if (!specialFunctions.includes(func)) {
const funcInterface: FunctionDescription | undefined = jsonInterface.find(node => node.type === 'function' && node.name === func)
if (funcInterface) functionsInterface.push(funcInterface)
}
return functionsInterface
}
return functionsInterface
}
/**
@ -159,15 +161,15 @@ function getTestFunctionsInterface (jsonInterface: FunctionDescription[], funcLi
*/
function getSpecialFunctionsInterface (jsonInterface: FunctionDescription[]): Record<string, FunctionDescription> {
const specialFunctionsInterface: Record<string, FunctionDescription> = {}
const funcList: string[] = ['beforeAll', 'beforeEach', 'afterAll', 'afterEach']
for(const func of funcList){
const funcInterface: FunctionDescription | undefined = jsonInterface.find(node => node.type === 'function' && node.name === func)
if(funcInterface) {
specialFunctionsInterface[func] = funcInterface
}
const specialFunctionsInterface: Record<string, FunctionDescription> = {}
const funcList: string[] = ['beforeAll', 'beforeEach', 'afterAll', 'afterEach']
for (const func of funcList) {
const funcInterface: FunctionDescription | undefined = jsonInterface.find(node => node.type === 'function' && node.name === func)
if (funcInterface) {
specialFunctionsInterface[func] = funcInterface
}
return specialFunctionsInterface
}
return specialFunctionsInterface
}
/**
@ -178,184 +180,182 @@ function getSpecialFunctionsInterface (jsonInterface: FunctionDescription[]): Re
*/
function createRunList (jsonInterface: FunctionDescription[], fileAST: AstNode, testContractName: string): RunListInterface[] {
const availableFunctions: string[] = getAvailableFunctions(fileAST, testContractName)
const testFunctionsInterface: FunctionDescription[] = getTestFunctionsInterface(jsonInterface, availableFunctions)
const specialFunctionsInterface: Record<string, FunctionDescription> = getSpecialFunctionsInterface(jsonInterface)
const runList: RunListInterface[] = []
if (availableFunctions.includes('beforeAll')) {
const func = specialFunctionsInterface['beforeAll']
runList.push({ name: 'beforeAll', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) })
const availableFunctions: string[] = getAvailableFunctions(fileAST, testContractName)
const testFunctionsInterface: FunctionDescription[] = getTestFunctionsInterface(jsonInterface, availableFunctions)
const specialFunctionsInterface: Record<string, FunctionDescription> = getSpecialFunctionsInterface(jsonInterface)
const runList: RunListInterface[] = []
if (availableFunctions.includes('beforeAll')) {
const func = specialFunctionsInterface['beforeAll']
runList.push({ name: 'beforeAll', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) })
}
for (const func of testFunctionsInterface) {
if (availableFunctions.includes('beforeEach')) {
const func = specialFunctionsInterface['beforeEach']
runList.push({ name: 'beforeEach', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) })
}
for (const func of testFunctionsInterface) {
if (availableFunctions.includes('beforeEach')) {
const func = specialFunctionsInterface['beforeEach']
runList.push({ name: 'beforeEach', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) })
}
if(func.name && func.inputs) runList.push({ name: func.name, inputs: func.inputs, signature: func.signature, type: 'test', constant: isConstant(func), payable: isPayable(func) })
if (availableFunctions.indexOf('afterEach') >= 0) {
const func = specialFunctionsInterface['afterEach']
runList.push({ name: 'afterEach', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) })
}
if (func.name && func.inputs) runList.push({ name: func.name, inputs: func.inputs, signature: func.signature, type: 'test', constant: isConstant(func), payable: isPayable(func) })
if (availableFunctions.indexOf('afterEach') >= 0) {
const func = specialFunctionsInterface['afterEach']
runList.push({ name: 'afterEach', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) })
}
}
if (availableFunctions.indexOf('afterAll') >= 0) {
const func = specialFunctionsInterface['afterAll']
runList.push({ name: 'afterAll', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) })
}
if (availableFunctions.indexOf('afterAll') >= 0) {
const func = specialFunctionsInterface['afterAll']
runList.push({ name: 'afterAll', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) })
}
return runList
return runList
}
export function runTest (testName: string, testObject: any, contractDetails: CompiledContract, fileAST: AstNode, opts: Options, testCallback: TestCbInterface, resultsCallback: ResultCbInterface): void {
let passingNum = 0
let failureNum = 0
let timePassed = 0
const isJSONInterfaceAvailable = testObject && testObject.options && testObject.options.jsonInterface
if(!isJSONInterfaceAvailable)
return resultsCallback(new Error('Contract interface not available'), { passingNum, failureNum, timePassed })
const runList: RunListInterface[] = createRunList(testObject.options.jsonInterface, fileAST, testName)
const web3 = new Web3()
const accts: TestResultInterface = {
type: 'accountList',
value: opts.accounts
}
testCallback(undefined, accts)
const resp: TestResultInterface = {
type: 'contract',
value: testName,
filename: testObject.filename
let passingNum = 0
let failureNum = 0
let timePassed = 0
const isJSONInterfaceAvailable = testObject && testObject.options && testObject.options.jsonInterface
if (!isJSONInterfaceAvailable) { return resultsCallback(new Error('Contract interface not available'), { passingNum, failureNum, timePassed }) }
const runList: RunListInterface[] = createRunList(testObject.options.jsonInterface, fileAST, testName)
const web3 = new Web3()
const accts: TestResultInterface = {
type: 'accountList',
value: opts.accounts
}
testCallback(undefined, accts)
const resp: TestResultInterface = {
type: 'contract',
value: testName,
filename: testObject.filename
}
testCallback(undefined, resp)
async.eachOfLimit(runList, 1, function (func, index, next) {
let sender: string | null = null
if (func.signature) {
sender = getOverridedSender(contractDetails.userdoc, func.signature, contractDetails.evm.methodIdentifiers)
if (opts.accounts && sender) {
sender = opts.accounts[sender]
}
}
testCallback(undefined, resp)
async.eachOfLimit(runList, 1, function (func, index, next) {
let sender: string | null = null
if (func.signature) {
sender = getOverridedSender(contractDetails.userdoc, func.signature, contractDetails.evm.methodIdentifiers)
if (opts.accounts && sender) {
sender = opts.accounts[sender]
}
}
let sendParams: Record<string, string> | null = null
if (sender) sendParams = { from: sender }
if(func.inputs && func.inputs.length > 0)
return resultsCallback(new Error(`Method '${func.name}' can not have parameters inside a test contract`), { passingNum, failureNum, timePassed })
const method = testObject.methods[func.name].apply(testObject.methods[func.name], [])
const startTime = Date.now()
if (func.constant) {
method.call(sendParams).then((result) => {
const time = (Date.now() - startTime) / 1000.0
if (result) {
const resp: TestResultInterface = {
type: 'testPass',
value: changeCase.sentenceCase(func.name),
filename: testObject.filename,
time: time,
context: testName
}
testCallback(undefined, resp)
passingNum += 1
timePassed += time
} else {
const resp: TestResultInterface = {
type: 'testFailure',
value: changeCase.sentenceCase(func.name),
filename: testObject.filename,
time: time,
errMsg: 'function returned false',
context: testName
}
testCallback(undefined, resp)
failureNum += 1
timePassed += time
}
next()
})
let sendParams: Record<string, string> | null = null
if (sender) sendParams = { from: sender }
if (func.inputs && func.inputs.length > 0) { return resultsCallback(new Error(`Method '${func.name}' can not have parameters inside a test contract`), { passingNum, failureNum, timePassed }) }
const method = testObject.methods[func.name].apply(testObject.methods[func.name], [])
const startTime = Date.now()
if (func.constant) {
method.call(sendParams).then((result) => {
const time = (Date.now() - startTime) / 1000.0
if (result) {
const resp: TestResultInterface = {
type: 'testPass',
value: changeCase.sentenceCase(func.name),
filename: testObject.filename,
time: time,
context: testName
}
testCallback(undefined, resp)
passingNum += 1
timePassed += time
} else {
if(func.payable) {
const value = getProvidedValue(contractDetails.userdoc, func.signature, contractDetails.evm.methodIdentifiers)
if(value) {
if(sendParams) sendParams.value = value
else sendParams = { value }
}
}
method.send(sendParams).on('receipt', (receipt) => {
try {
const time: number = (Date.now() - startTime) / 1000.0
const assertionEventHashes = assertionEvents.map(e => Web3.utils.sha3(e.name + '(' + e.params.join() + ')') )
let testPassed = false
for (const i in receipt.events) {
let events = receipt.events[i]
if (!Array.isArray(events)) events = [events]
for (const event of events) {
const eIndex = assertionEventHashes.indexOf(event.raw.topics[0]) // event name topic will always be at index 0
if (eIndex >= 0) {
const testEvent = web3.eth.abi.decodeParameters(assertionEvents[eIndex].params, event.raw.data)
if (!testEvent[0]) {
const assertMethod = testEvent[2]
if (assertMethod === 'ok') { // for 'Assert.ok' method
testEvent[3] = 'false'
testEvent[4] = 'true'
}
const location = getAssertMethodLocation(fileAST, testName, func.name, assertMethod)
const resp: TestResultInterface = {
type: 'testFailure',
value: changeCase.sentenceCase(func.name),
filename: testObject.filename,
time: time,
errMsg: testEvent[1],
context: testName,
assertMethod,
returned: testEvent[3],
expected: testEvent[4],
location
};
testCallback(undefined, resp)
failureNum += 1
timePassed += time
return next()
}
testPassed = true
}
}
}
if (testPassed) {
const resp: TestResultInterface = {
type: 'testPass',
value: changeCase.sentenceCase(func.name),
filename: testObject.filename,
time: time,
context: testName
}
testCallback(undefined, resp)
passingNum += 1
timePassed += time
}
return next()
} catch (err) {
console.error(err)
return next(err)
}
}).on('error', function (err: Error) {
const time: number = (Date.now() - startTime) / 1000.0
const resp: TestResultInterface = {
const resp: TestResultInterface = {
type: 'testFailure',
value: changeCase.sentenceCase(func.name),
filename: testObject.filename,
time: time,
errMsg: 'function returned false',
context: testName
}
testCallback(undefined, resp)
failureNum += 1
timePassed += time
}
next()
})
} else {
if (func.payable) {
const value = getProvidedValue(contractDetails.userdoc, func.signature, contractDetails.evm.methodIdentifiers)
if (value) {
if (sendParams) sendParams.value = value
else sendParams = { value }
}
}
method.send(sendParams).on('receipt', (receipt) => {
try {
const time: number = (Date.now() - startTime) / 1000.0
const assertionEventHashes = assertionEvents.map(e => Web3.utils.sha3(e.name + '(' + e.params.join() + ')'))
let testPassed = false
for (const i in receipt.events) {
let events = receipt.events[i]
if (!Array.isArray(events)) events = [events]
for (const event of events) {
const eIndex = assertionEventHashes.indexOf(event.raw.topics[0]) // event name topic will always be at index 0
if (eIndex >= 0) {
const testEvent = web3.eth.abi.decodeParameters(assertionEvents[eIndex].params, event.raw.data)
if (!testEvent[0]) {
const assertMethod = testEvent[2]
if (assertMethod === 'ok') { // for 'Assert.ok' method
testEvent[3] = 'false'
testEvent[4] = 'true'
}
const location = getAssertMethodLocation(fileAST, testName, func.name, assertMethod)
const resp: TestResultInterface = {
type: 'testFailure',
value: changeCase.sentenceCase(func.name),
filename: testObject.filename,
time: time,
errMsg: err.message,
context: testName
};
errMsg: testEvent[1],
context: testName,
assertMethod,
returned: testEvent[3],
expected: testEvent[4],
location
}
testCallback(undefined, resp)
failureNum += 1
timePassed += time
return next()
})
return next()
}
testPassed = true
}
}
}
if (testPassed) {
const resp: TestResultInterface = {
type: 'testPass',
value: changeCase.sentenceCase(func.name),
filename: testObject.filename,
time: time,
context: testName
}
testCallback(undefined, resp)
passingNum += 1
timePassed += time
}
return next()
} catch (err) {
console.error(err)
return next(err)
}
}, function(error) {
resultsCallback(error, { passingNum, failureNum, timePassed })
})
}).on('error', function (err: Error) {
const time: number = (Date.now() - startTime) / 1000.0
const resp: TestResultInterface = {
type: 'testFailure',
value: changeCase.sentenceCase(func.name),
filename: testObject.filename,
time: time,
errMsg: err.message,
context: testName
}
testCallback(undefined, resp)
failureNum += 1
timePassed += time
return next()
})
}
}, function (error) {
resultsCallback(error, { passingNum, failureNum, timePassed })
})
}

@ -51,8 +51,8 @@ export interface Options {
export interface CompilerConfiguration {
currentCompilerUrl: string,
evmVersion: string,
optimize: boolean,
evmVersion: string,
optimize: boolean,
usingWorker?: boolean,
runs: number
}
@ -64,7 +64,7 @@ export interface CompilationErrors {
}
export class CompilationErrors extends Error {
constructor(errors: Array<any>) {
constructor (errors: Array<any>) {
const mapError = errors.map((e) => { return e.formattedMessage || e.message })
super(mapError.join('\n'))
this.errors = errors
@ -74,9 +74,9 @@ export class CompilationErrors extends Error {
/** sources object with name of the file and content **/
////////////
/// /////////
// SOURCE //
////////////
/// /////////
export interface CompilationSource {
/** Identifier of the source (used in source maps) */
id: number
@ -162,9 +162,9 @@ export interface CompiledContract {
}
}
/////////
/// //////
// ABI //
/////////
/// //////
export type ABIDescription = FunctionDescription | EventDescription
export interface FunctionDescription {
@ -227,9 +227,9 @@ export type ABITypeParameter =
| 'tuple[]'
| string // Fallback
///////////////////////////
/// ////////////////////////
// NATURAL SPECIFICATION //
///////////////////////////
/// ////////////////////////
// Userdoc
export interface UserDocumentation {
@ -267,9 +267,9 @@ export interface DevMethodDoc {
}
}
//////////////
/// ///////////
// BYTECODE //
//////////////
/// ///////////
export interface BytecodeObject {
/** The bytecode as a hex string. */
object: string

@ -41,7 +41,7 @@
"workspace-schematic": "nx workspace-schematic",
"dep-graph": "nx dep-graph",
"help": "nx help",
"lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-file-explorer,remix-ui-debugger-ui",
"lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-file-explorer,remix-ui-debugger-ui",
"build:libs": "nx run-many --target=build --parallel=false --with-deps=true --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd",
"test:libs": "nx run-many --target=test --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd",
"publish:libs": "npm run build:libs & lerna publish --skip-git & npm run bumpVersion:libs",

@ -347,7 +347,7 @@
"linter": "eslint",
"config": "libs/remix-tests/.eslintrc",
"tsConfig": ["libs/remix-tests/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "libs/remix-tests/tests/**/*"]
"exclude": ["**/node_modules/**", "libs/remix-tests/tests/**/*", "**/dist/**"]
}
},
"test": {

Loading…
Cancel
Save