UNPKG

6.01 kBPlain TextView Raw
1import async, { ErrorCallback } from 'async'
2require('colors')
3
4import { compileContractSources } from './compiler'
5import { deployAll } from './deployer'
6import { runTest } from './testRunner'
7
8import Web3 from 'web3';
9import { Provider } from 'remix-simulator'
10import { FinalResult, SrcIfc, compilationInterface, ASTInterface, Options,
11 TestResultInterface, AstNode, CompilerConfiguration } from './types'
12
13const createWeb3Provider = async function () {
14 let web3 = new Web3()
15 let provider = new Provider()
16 await provider.init()
17 web3.setProvider(provider)
18 return web3
19}
20
21/**
22 * @dev Run tests from source of a test contract file (used for IDE)
23 * @param contractSources Sources of contract
24 * @param compilerConfig current compiler configuration
25 * @param testCallback Test callback
26 * @param resultCallback Result Callback
27 * @param finalCallback Final Callback
28 * @param importFileCb Import file callback
29 * @param opts Options
30 */
31export async function runTestSources(contractSources: SrcIfc, compilerConfig: CompilerConfiguration, testCallback: Function, resultCallback: Function, finalCallback: any, importFileCb: Function, opts: Options) {
32 opts = opts || {}
33 const sourceASTs: any = {}
34 let web3 = opts.web3 || await createWeb3Provider()
35 let accounts: string[] | null = opts.accounts || null
36 async.waterfall([
37 function getAccountList (next) {
38 if (accounts) return next()
39 web3.eth.getAccounts((_err, _accounts) => {
40 accounts = _accounts
41 next()
42 })
43 },
44 function compile (next) {
45 compileContractSources(contractSources, compilerConfig, importFileCb, { accounts }, next)
46 },
47 function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next) {
48 for(const filename in asts) {
49 if(filename.endsWith('_test.sol'))
50 sourceASTs[filename] = asts[filename].ast
51 }
52 deployAll(compilationResult, web3, false, (err, contracts) => {
53 if (err) {
54 // If contract deployment fails because of 'Out of Gas' error, try again with double gas
55 // This is temporary, should be removed when remix-tests will have a dedicated UI to
56 // accept deployment params from UI
57 if(err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) {
58 deployAll(compilationResult, web3, true, (error, contracts) => {
59 if (error) next([{message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error'}]) // IDE expects errors in array
60 else next(null, compilationResult, contracts)
61 })
62 } else
63 next([{message: 'contract deployment failed: ' + err.message, severity: 'error'}]) // IDE expects errors in array
64 } else
65 next(null, compilationResult, contracts)
66 })
67 },
68 function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) {
69 let contractsToTest: string[] = []
70 let contractsToTestDetails: any[] = []
71
72 for (let filename in compilationResult) {
73 if (!filename.endsWith('_test.sol')) {
74 continue
75 }
76 Object.keys(compilationResult[filename]).forEach(contractName => {
77 contractsToTestDetails.push(compilationResult[filename][contractName])
78 contractsToTest.push(contractName)
79 })
80 }
81 next(null, contractsToTest, contractsToTestDetails, contracts)
82 },
83 function runTests(contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) {
84 let totalPassing = 0
85 let totalFailing = 0
86 let totalTime = 0
87 let errors: any[] = []
88
89 const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) {
90 if (result.type === 'testFailure') {
91 errors.push(result)
92 }
93 testCallback(result)
94 }
95
96 const _resultsCallback = function (_err, result, cb) {
97 resultCallback(_err, result, () => {})
98 totalPassing += result.passingNum
99 totalFailing += result.failureNum
100 totalTime += result.timePassed
101 cb()
102 }
103
104 async.eachOfLimit(contractsToTest, 1, (contractName: string, index: string | number, cb: ErrorCallback) => {
105 const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']]
106 runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts }, _testCallback, (err, result) => {
107 if (err) {
108 return cb(err)
109 }
110 _resultsCallback(null, result, cb)
111 })
112 }, function (err) {
113 if (err) {
114 return next(err)
115 }
116
117 let finalResults: FinalResult = {
118 totalPassing: 0,
119 totalFailing: 0,
120 totalTime: 0,
121 errors: [],
122 }
123
124 finalResults.totalPassing = totalPassing || 0
125 finalResults.totalFailing = totalFailing || 0
126 finalResults.totalTime = totalTime || 0
127 finalResults.errors = []
128
129 errors.forEach((error, _index) => {
130 finalResults.errors.push({context: error.context, value: error.value, message: error.errMsg})
131 })
132
133 next(null, finalResults)
134 })
135 }
136 ], finalCallback)
137}