diff --git a/apps/debugger/src/app/debugger-api.ts b/apps/debugger/src/app/debugger-api.ts
index ae75599bec..73c49b32e9 100644
--- a/apps/debugger/src/app/debugger-api.ts
+++ b/apps/debugger/src/app/debugger-api.ts
@@ -121,9 +121,10 @@ export const DebuggerApiMixin = (Base) => class extends Base {
return await debug.debugger.traceManager.getTrace(hash)
}
- debug (hash) {
+ debug (hash, web3?) {
this.debugHash = hash
- if (this.onDebugRequestedListener) this.onDebugRequestedListener(hash)
+ if (web3) remixDebug.init.extendWeb3(web3)
+ if (this.onDebugRequestedListener) this.onDebugRequestedListener(hash, web3)
}
onActivation () {
diff --git a/apps/remix-ide-e2e/src/tests/solidityUnittests.spec.ts b/apps/remix-ide-e2e/src/tests/solidityUnittests.spec.ts
index 551af0ea25..37ae60b073 100644
--- a/apps/remix-ide-e2e/src/tests/solidityUnittests.spec.ts
+++ b/apps/remix-ide-e2e/src/tests/solidityUnittests.spec.ts
@@ -47,13 +47,13 @@ module.exports = {
.click('*[data-id="testTabCheckAllTests"]')
.clickElementAtPosition('.singleTestLabel', 1)
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
- .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'MyTest (/tests/simple_storage_test.sol)', 120000)
+ .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'MyTest (tests/simple_storage_test.sol)', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✓ Initial value should be100', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✓ Value is set200', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Should fail for wrong value200', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'Passing: 2', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'Failing: 1', 120000)
- .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'FAIL MyTest (/tests/simple_storage_test.sol)', 120000)
+ .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'FAIL MyTest (tests/simple_storage_test.sol)', 120000)
},
'Should run advance unit test using natspec and experimental ABIEncoderV2 `ks2b_test.sol` ': function (browser: NightwatchBrowser) {
@@ -65,7 +65,7 @@ module.exports = {
.click('*[data-id="testTabCheckAllTests"]')
.clickElementAtPosition('.singleTestLabel', 2)
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
- .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '/tests/ks2b_test.sol', 120000)
+ .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'tests/ks2b_test.sol', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✓ Check project exists', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Check wrong project owner', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Check wrong sender', 120000)
@@ -84,9 +84,9 @@ module.exports = {
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.pause(2000)
.click('*[data-id="testTabRunTestsTabStopAction"]')
- .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '/tests/ks2b_test.sol', 200000)
- .notContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '/tests/4_Ballot_test.sol')
- .notContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '/tests/simple_storage_test.sol')
+ .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'tests/ks2b_test.sol', 200000)
+ .notContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'tests/4_Ballot_test.sol')
+ .notContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'tests/simple_storage_test.sol')
.waitForElementContainsText('*[data-id="testTabTestsExecutionStopped"]', 'The test execution has been stopped', 60000)
},
@@ -115,7 +115,7 @@ module.exports = {
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'contract deployment failed after trying twice', 120000)
},
- 'Should fail when parameters are to method in test contract': function (browser: NightwatchBrowser) {
+ 'Should fail when parameters are passed to method in test contract': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
.addFile('tests/methodFailure_test.sol', sources[0]['tests/methodFailure_test.sol'])
.clickLaunchIcon('filePanel')
@@ -181,12 +181,42 @@ module.exports = {
.scrollAndClick('#runTestsTabRunAction')
.waitForElementVisible('*[data-id="testTabSolidityUnitTestsOutputheader"]', 120000)
.waitForElementPresent('#solidityUnittestsOutput div[class^="testPass"]', 60000)
- .waitForElementContainsText('#solidityUnittestsOutput', '/tests/4_Ballot_test.sol', 60000)
+ .waitForElementContainsText('#solidityUnittestsOutput', 'tests/4_Ballot_test.sol', 60000)
.waitForElementContainsText('#solidityUnittestsOutput', '✓ Check winning proposal', 60000)
.waitForElementContainsText('#solidityUnittestsOutput', '✓ Check winnin proposal with return value', 60000)
},
- 'Solidity Unit tests Basic Basic with local compiler': function (browser: NightwatchBrowser) {
+ 'Debug failed test using debugger': function (browser: NightwatchBrowser) {
+ browser
+ .waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
+ .addFile('tests/ballotFailedDebug_test.sol', sources[0]['tests/ballotFailedDebug_test.sol'])
+ .clickLaunchIcon('solidityUnitTesting')
+ .waitForElementVisible('*[id="singleTesttests/4_Ballot_test.sol"]', 60000)
+ .click('*[id="singleTesttests/4_Ballot_test.sol"]')
+ .click('#runTestsTabRunAction')
+ .waitForElementVisible('*[data-id="testTabSolidityUnitTestsOutputheader"]', 120000)
+ .waitForElementContainsText('#solidityUnittestsOutput', 'tests/ballotFailedDebug_test.sol', 60000)
+ .waitForElementContainsText('#solidityUnittestsOutput', '✘ Check winning proposal', 60000)
+ .waitForElementContainsText('#solidityUnittestsOutput', '✓ Check winnin proposal with return value', 60000)
+ .click('.fa-bug')
+ .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000)
+ .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposal()', 60000)
+ .click('*[data-id="dropdownPanelSolidityLocals"]')
+ .waitForElementContainsText('*[data-id="solidityLocals"]', 'no locals', 60000)
+ // eslint-disable-next-line dot-notation
+ .execute(function () { document.getElementById('slider')['value'] = '235' }) // It only moves slider to 235 but vm traces are not updated
+ .setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW))
+ .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposal()', 60000)
+ .waitForElementContainsText('*[data-id="functionPanel"]', 'vote(proposal)', 60000)
+ .pause(2000)
+ .checkVariableDebug('soliditylocals', locals)
+ .clickLaunchIcon('filePanel')
+ .pause(2000)
+ .openFile('tests/ballotFailedDebug_test.sol')
+ .removeFile('tests/ballotFailedDebug_test.sol', 'workspace_new')
+ },
+
+ 'Basic Solidity Unit tests with local compiler': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('solidity')
.setSolidityCompilerVersion('builtin')
@@ -200,7 +230,7 @@ module.exports = {
.scrollAndClick('#runTestsTabRunAction')
.waitForElementVisible('*[data-id="testTabSolidityUnitTestsOutputheader"]', 120000)
.waitForElementPresent('#solidityUnittestsOutput div[class^="testPass"]', 60000)
- .waitForElementContainsText('#solidityUnittestsOutput', '/tests/4_Ballot_test.sol', 60000)
+ .waitForElementContainsText('#solidityUnittestsOutput', 'tests/4_Ballot_test.sol', 60000)
.waitForElementContainsText('#solidityUnittestsOutput', '✓ Check winning proposal', 60000)
.waitForElementContainsText('#solidityUnittestsOutput', '✓ Check winnin proposal with return value', 60000)
.end()
@@ -405,6 +435,61 @@ const sources = [
}
}
`
+ },
+ 'tests/ballotFailedDebug_test.sol': {
+ content: `// SPDX-License-Identifier: GPL-3.0
+
+ pragma solidity >=0.7.0 <0.9.0;
+ import "remix_tests.sol"; // this import is automatically injected by Remix.
+ import "../contracts/3_Ballot.sol";
+
+ contract BallotTest {
+
+ bytes32[] proposalNames;
+
+ Ballot ballotToTest;
+ function beforeAll () public {
+ proposalNames.push(bytes32("candidate1"));
+ ballotToTest = new Ballot(proposalNames);
+ }
+
+ function checkWinningProposal () public {
+ ballotToTest.vote(1); // This will revert the transaction
+ Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal");
+ }
+
+ function checkWinninProposalWithReturnValue () public view returns (bool) {
+ return ballotToTest.winningProposal() == 0;
+ }
+ }`
}
}
]
+
+const locals = {
+ sender: {
+ value: {
+ weight: {
+ value: '1',
+ type: 'uint256'
+ },
+ voted: {
+ value: false,
+ type: 'bool'
+ },
+ delegate: {
+ value: '0x0000000000000000000000000000000000000000',
+ type: 'address'
+ },
+ vote: {
+ value: '0',
+ type: 'uint256'
+ }
+ },
+ type: 'struct Ballot.Voter'
+ },
+ proposal: {
+ value: '1',
+ type: 'uint256'
+ }
+}
diff --git a/apps/remix-ide/src/app/tabs/styles/test-tab-styles.js b/apps/remix-ide/src/app/tabs/styles/test-tab-styles.js
index b6cce75aeb..2f0ddab770 100644
--- a/apps/remix-ide/src/app/tabs/styles/test-tab-styles.js
+++ b/apps/remix-ide/src/app/tabs/styles/test-tab-styles.js
@@ -31,8 +31,6 @@ var css = csjs`
border-radius: 4px;
padding: 1% 1% 1% 5%;
}
- .testFailure {
- }
.testFailureSummary {
}
.title {
diff --git a/apps/remix-ide/src/app/tabs/test-tab.js b/apps/remix-ide/src/app/tabs/test-tab.js
index 030745e5d1..95bd5d37a2 100644
--- a/apps/remix-ide/src/app/tabs/test-tab.js
+++ b/apps/remix-ide/src/app/tabs/test-tab.js
@@ -7,7 +7,7 @@ var async = require('async')
var tooltip = require('../ui/tooltip')
var Renderer = require('../ui/renderer')
var css = require('./styles/test-tab-styles')
-var remixTests = require('@remix-project/remix-tests')
+var { UnitTestRunner } = require('@remix-project/remix-tests')
const TestTabLogic = require('./testTab/testTab')
@@ -33,12 +33,15 @@ module.exports = class TestTab extends ViewPlugin {
this.data = {}
this.appManager = appManager
this.renderer = new Renderer(this)
+ this.testRunner = new UnitTestRunner()
this.hasBeenStopped = false
this.runningTestsNumber = 0
this.readyTestsNumber = 0
this.areTestsRunning = false
this.defaultPath = 'tests'
this.offsetToLineColumnConverter = offsetToLineColumnConverter
+ this.allFilesInvolved = []
+ this.isDebugging = false
appManager.event.on('activate', (name) => {
if (name === 'solidity') this.updateRunAction()
@@ -95,6 +98,15 @@ module.exports = class TestTab extends ViewPlugin {
this.setCurrentPath(this.defaultPath)
})
+ this.testRunner.event.on('compilationFinished', (success, data, source) => {
+ if (success) {
+ this.allFilesInvolved = Object.keys(data.sources)
+ // forwarding the event to the appManager infra
+ // This is listened by compilerArtefacts to show data while debugging
+ this.emit('compilationFinished', source.target, source, 'soljson', data)
+ }
+ })
+
this.fileManager.events.on('noFileSelected', () => {
})
@@ -102,6 +114,9 @@ module.exports = class TestTab extends ViewPlugin {
}
async updateForNewCurrent (file) {
+ // if current file is changed while debugging and one of the files imported in test file are opened
+ // do not clear the test results in SUT plugin
+ if (this.isDebugging && this.allFilesInvolved.includes(file)) return
this.data.allTests = []
this.updateTestFileList()
this.clearResults()
@@ -192,6 +207,13 @@ module.exports = class TestTab extends ViewPlugin {
}
}
+ async startDebug (txHash, web3) {
+ this.isDebugging = true
+ if (!await this.appManager.isActive('debugger')) await this.appManager.activatePlugin('debugger')
+ this.call('menuicons', 'select', 'debugger')
+ this.call('debugger', 'debug', txHash, web3)
+ }
+
printHHLogs (logsArr, testName) {
let finalLogs = `${testName}:\n`
for (const log of logsArr) {
@@ -242,13 +264,30 @@ module.exports = class TestTab extends ViewPlugin {
} else if (result.type === 'testFailure') {
if (result.hhLogs && result.hhLogs.length) this.printHHLogs(result.hhLogs, result.value)
if (!result.assertMethod) {
+ let debugBtn = yo``
+ if (result.errMsg.includes('Transaction has been reverted by the EVM')) {
+ const txHash = JSON.parse(result.errMsg.replace('Transaction has been reverted by the EVM:', '')).transactionHash
+ const { web3 } = result
+ debugBtn = yo`
this.highlightLocation(result.location, runningTests, result.filename)}
>
@@ -446,7 +485,7 @@ module.exports = class TestTab extends ViewPlugin {
usingWorker: canUseWorker(currentVersion),
runs
}
- remixTests.runTestSources(runningTest, compilerConfig, () => {}, () => {}, (error, result) => {
+ this.testRunner.runTestSources(runningTest, compilerConfig, () => {}, () => {}, (error, result) => {
if (error) return reject(error)
resolve(result)
}, (url, cb) => {
@@ -456,6 +495,7 @@ module.exports = class TestTab extends ViewPlugin {
}
runTest (testFilePath, callback) {
+ this.isDebugging = false
if (this.hasBeenStopped) {
this.updateFinalResult()
return
@@ -473,7 +513,7 @@ module.exports = class TestTab extends ViewPlugin {
usingWorker: canUseWorker(currentVersion),
runs
}
- remixTests.runTestSources(
+ this.testRunner.runTestSources(
runningTests,
compilerConfig,
(result) => this.testCallback(result, runningTests),
diff --git a/apps/remix-ide/src/app/tabs/testTab/testTab.js b/apps/remix-ide/src/app/tabs/testTab/testTab.js
index 7b6e1fd4fc..93cb49516b 100644
--- a/apps/remix-ide/src/app/tabs/testTab/testTab.js
+++ b/apps/remix-ide/src/app/tabs/testTab/testTab.js
@@ -68,7 +68,8 @@ class TestTabLogic {
cb(e.message)
}
for (var file in files) {
- if (/.(_test.sol)$/.exec(file)) tests.push(provider.type + '/' + file)
+ const filepath = provider && provider.type ? provider.type + '/' + file : file
+ if (/.(_test.sol)$/.exec(file)) tests.push(filepath)
}
cb(null, tests, this.currentPath)
}
diff --git a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts
index 47eb1d519b..da22ffb39e 100644
--- a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts
+++ b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts
@@ -52,6 +52,11 @@ export class CompilerArtefacts extends Plugin {
this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source)
saveCompilationPerFileResult(file, source, languageVersion, data)
})
+
+ this.on('solidityUnitTesting', 'compilationFinished', (file, source, languageVersion, data) => {
+ this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source)
+ saveCompilationPerFileResult(file, source, languageVersion, data)
+ })
}
getAllContractDatas () {
diff --git a/libs/remix-tests/src/compiler.ts b/libs/remix-tests/src/compiler.ts
index 3cf91bd9fb..e77fdcdcfc 100644
--- a/libs/remix-tests/src/compiler.ts
+++ b/libs/remix-tests/src/compiler.ts
@@ -205,6 +205,7 @@ export function compileContractSources (sources: SrcIfc, compilerConfig: Compile
function doCompilation (next) {
// @ts-ignore
compiler.event.register('compilationFinished', this, (success, data, source) => {
+ if (opts && opts.event) opts.event.emit('compilationFinished', success, data, source)
next(null, data)
})
compiler.compile(sources, filepath)
diff --git a/libs/remix-tests/src/index.ts b/libs/remix-tests/src/index.ts
index 854db0dcad..9552d5b5ee 100644
--- a/libs/remix-tests/src/index.ts
+++ b/libs/remix-tests/src/index.ts
@@ -1,5 +1,5 @@
export { runTestFiles } from './runTestFiles'
-export { runTestSources } from './runTestSources'
+export { UnitTestRunner } from './runTestSources'
export { runTest } from './testRunner'
export * from './types'
export const assertLibCode = require('../sol/tests.sol')
diff --git a/libs/remix-tests/src/runTestSources.ts b/libs/remix-tests/src/runTestSources.ts
index 1393148272..4894b713eb 100644
--- a/libs/remix-tests/src/runTestSources.ts
+++ b/libs/remix-tests/src/runTestSources.ts
@@ -5,6 +5,7 @@ import { deployAll } from './deployer'
import { runTest } from './testRunner'
import Web3 from 'web3'
+import { EventEmitter } from 'events'
import { Provider, extend } from '@remix-project/remix-simulator'
import {
FinalResult, SrcIfc, compilationInterface, ASTInterface, Options,
@@ -12,126 +13,134 @@ import {
} from './types'
require('colors')
-const createWeb3Provider = async function () {
- const web3 = new Web3()
- const provider: any = new Provider()
- await provider.init()
- web3.setProvider(provider)
- extend(web3)
- return web3
-}
+export class UnitTestRunner {
+ event
-/**
- * @dev Run tests from source of a test contract file (used for IDE)
- * @param contractSources Sources of contract
- * @param compilerConfig current compiler configuration
- * @param testCallback Test callback
- * @param resultCallback Result Callback
- * @param finalCallback Final Callback
- * @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) }
- })
- },
- function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) {
- const contractsToTest: string[] = []
- const contractsToTestDetails: any[] = []
+ constructor () {
+ this.event = new EventEmitter()
+ }
+
+ async createWeb3Provider () {
+ const web3 = new Web3()
+ const provider: any = new Provider()
+ await provider.init()
+ web3.setProvider(provider)
+ extend(web3)
+ return web3
+ }
- for (const filename in compilationResult) {
- if (!filename.endsWith('_test.sol')) {
- continue
+ /**
+ * @dev Run tests from source of a test contract file (used for IDE)
+ * @param contractSources Sources of contract
+ * @param compilerConfig current compiler configuration
+ * @param testCallback Test callback
+ * @param resultCallback Result Callback
+ * @param finalCallback Final Callback
+ * @param importFileCb Import file callback
+ * @param opts Options
+ */
+ async runTestSources (contractSources: SrcIfc, compilerConfig: CompilerConfiguration, testCallback, resultCallback, finalCallback: any, importFileCb, opts: Options) {
+ opts = opts || {}
+ const sourceASTs: any = {}
+ const web3 = opts.web3 || await this.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()
+ })
+ },
+ (next) => {
+ compileContractSources(contractSources, compilerConfig, importFileCb, { accounts, event: this.event }, next)
+ },
+ function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next) {
+ for (const filename in asts) {
+ if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast }
}
- Object.keys(compilationResult[filename]).forEach(contractName => {
- contractsToTestDetails.push(compilationResult[filename][contractName])
- contractsToTest.push(contractName)
+ 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) }
})
- }
- 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)
+ },
+ 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[] = []
+ // 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)
}
- 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, web3 }, _testCallback, (err, result) => {
+ 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, web3 }, _testCallback, (err, result) => {
+ if (err) {
+ return cb(err)
+ }
+ _resultsCallback(null, result, cb)
+ })
+ }, function (err) {
if (err) {
- return cb(err)
+ return next(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)
+ }
}
diff --git a/libs/remix-tests/src/testRunner.ts b/libs/remix-tests/src/testRunner.ts
index ffc2cecde4..dca4a5f748 100644
--- a/libs/remix-tests/src/testRunner.ts
+++ b/libs/remix-tests/src/testRunner.ts
@@ -322,7 +322,8 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
assertMethod,
returned: testEvent[3],
expected: testEvent[4],
- location
+ location,
+ web3
}
if (hhLogs) resp.hhLogs = hhLogs
testCallback(undefined, resp)
@@ -373,7 +374,8 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
filename: testObject.filename,
time: time,
errMsg: err.message,
- context: testName
+ context: testName,
+ web3
}
testCallback(undefined, resp)
failureNum += 1
diff --git a/libs/remix-tests/src/types.ts b/libs/remix-tests/src/types.ts
index 649ee921df..b0d4145dbf 100644
--- a/libs/remix-tests/src/types.ts
+++ b/libs/remix-tests/src/types.ts
@@ -37,6 +37,7 @@ export interface TestResultInterface {
expected?: string | number
location?: string
hhLogs?: []
+ web3?: any
}
export interface TestCbInterface {
(error: Error | null | undefined, result: TestResultInterface) : void;
diff --git a/libs/remix-tests/tests/testRunner.spec.ts b/libs/remix-tests/tests/testRunner.spec.ts
index b025fbfc0c..351daf23f4 100644
--- a/libs/remix-tests/tests/testRunner.spec.ts
+++ b/libs/remix-tests/tests/testRunner.spec.ts
@@ -131,7 +131,7 @@ describe('testRunner', () => {
{ type: 'testPass', value: 'Ok pass test', filename: __dirname + '/examples_0/assert_ok_test.sol', context: 'AssertOkTest', hhLogs: hhLogs1 },
{ type: 'testFailure', value: 'Ok fail test', filename: __dirname + '/examples_0/assert_ok_test.sol', errMsg: 'okFailTest fails', context: 'AssertOkTest', hhLogs: hhLogs2, assertMethod: 'ok', location: '370:36:0', expected: 'true', returned: 'false'},
- ], ['time'])
+ ], ['time', 'web3'])
})
})
@@ -170,7 +170,7 @@ describe('testRunner', () => {
{ type: 'testFailure', value: 'Equal bytes32 fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBytes32FailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1670:48:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6979000000000000000000000000000000000000000000000000000000'},
{ type: 'testPass', value: 'Equal string pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
{ type: 'testFailure', value: 'Equal string fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalStringFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1916:81:0', expected: 'remix-tests', returned: 'remix'}
- ], ['time'])
+ ], ['time', 'web3'])
})
})
@@ -209,7 +209,7 @@ describe('testRunner', () => {
{ type: 'testFailure', value: 'Not equal bytes32 fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBytes32FailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1756:54:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6978000000000000000000000000000000000000000000000000000000'},
{ type: 'testPass', value: 'Not equal string pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
{ type: 'testFailure', value: 'Not equal string fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualStringFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '2026:81:0', expected: 'remix', returned: 'remix'},
- ], ['time'])
+ ], ['time', 'web3'])
})
})
@@ -243,7 +243,7 @@ describe('testRunner', () => {
{ type: 'testFailure', value: 'Greater than uint int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '845:71:0', expected: '2', returned: '1'},
{ type: 'testPass', value: 'Greater than int uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' },
{ type: 'testFailure', value: 'Greater than int uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '1125:76:0', expected: '115792089237316195423570985008687907853269984665640564039457584007913129639836', returned: '100'}
- ], ['time'])
+ ], ['time', 'web3'])
})
})
@@ -278,7 +278,7 @@ describe('testRunner', () => {
{ type: 'testFailure', value: 'Lesser than uint int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '826:71:0', expected: '-1', returned: '115792089237316195423570985008687907853269984665640564039457584007913129639935'},
{ type: 'testPass', value: 'Lesser than int uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' },
{ type: 'testFailure', value: 'Lesser than int uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '1105:69:0', expected: '1', returned: '1'},
- ], ['time'])
+ ], ['time', 'web3'])
})
})
@@ -309,7 +309,7 @@ describe('testRunner', () => {
{ type: 'testPass', value: 'Initial value should not be200', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' },
{ type: 'testFailure', value: 'Should trigger one fail', filename: __dirname + '/examples_1/simple_storage_test.sol', errMsg: 'uint test 1 fails', context: 'MyTest', assertMethod: 'equal', location: '532:51:1', expected: '2', returned: '1'},
{ type: 'testPass', value: 'Should trigger one pass', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' }
- ], ['time'])
+ ], ['time', 'web3'])
})
})
@@ -338,7 +338,7 @@ describe('testRunner', () => {
{ type: 'contract', value: 'MyTest', filename: __dirname + '/examples_2/simple_storage_test.sol' },
{ type: 'testPass', value: 'Initial value should be100', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' },
{ type: 'testPass', value: 'Value is set200', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' }
- ], ['time'])
+ ], ['time', 'web3'])
})
})
@@ -364,7 +364,7 @@ describe('testRunner', () => {
{ type: 'contract', value: 'StringTest', filename: __dirname + '/examples_3/simple_string_test.sol' },
{ type: 'testPass', value: 'Initial value should be hello world', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' },
{ type: 'testPass', value: 'Value should not be hello wordl', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' }
- ], ['time'])
+ ], ['time', 'web3'])
})
})
@@ -391,7 +391,7 @@ describe('testRunner', () => {
{ type: 'testPass', value: 'Initial value should be100', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' },
{ type: 'testPass', value: 'Check if even', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' },
{ type: 'testPass', value: 'Check if odd', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' }
- ], ['time'])
+ ], ['time', 'web3'])
})
})
diff --git a/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx b/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
index e79ad8910c..a41066e56b 100644
--- a/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
+++ b/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
@@ -39,8 +39,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
return unLoad()
}, [])
- debuggerModule.onDebugRequested((hash) => {
- if (hash) debug(hash)
+ debuggerModule.onDebugRequested((hash, web3?) => {
+ if (hash) debug(hash, web3)
})
debuggerModule.onRemoveHighlights(async () => {
@@ -162,7 +162,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}
})
}
- const startDebugging = async (blockNumber, txNumber, tx) => {
+ const startDebugging = async (blockNumber, txNumber, tx, optWeb3?) => {
if (state.debugger) unLoad()
if (!txNumber) return
setState(prevState => {
@@ -181,7 +181,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
return
}
- const web3 = state.opt.debugWithLocalNode ? await debuggerModule.web3() : await debuggerModule.getDebugWeb3()
+ const web3 = optWeb3 || (state.opt.debugWithLocalNode ? await debuggerModule.web3() : await debuggerModule.getDebugWeb3())
try {
const networkId = await web3.eth.net.getId()
_paq.push(['trackEvent', 'debugger', 'startDebugging', networkId])
@@ -259,7 +259,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}
}
- const debug = (txHash) => {
+ const debug = (txHash, web3?) => {
setState(prevState => {
return {
...prevState,
@@ -267,7 +267,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
txNumber: txHash
}
})
- startDebugging(null, txHash, null)
+ startDebugging(null, txHash, null, web3)
}
const stepManager = {
diff --git a/libs/remix-ui/debugger-ui/src/lib/idebugger-api.ts b/libs/remix-ui/debugger-ui/src/lib/idebugger-api.ts
index 2e57aff5a6..5fa07cb4e8 100644
--- a/libs/remix-ui/debugger-ui/src/lib/idebugger-api.ts
+++ b/libs/remix-ui/debugger-ui/src/lib/idebugger-api.ts
@@ -45,7 +45,7 @@ export interface TransactionReceipt {
export type onBreakpointClearedListener = (params: string, row: number) => void
export type onBreakpointAddedListener = (params: string, row: number) => void
export type onEditorContentChanged = () => void
-export type onDebugRequested = (hash: string) => void
+export type onDebugRequested = (hash: string, web3?: any) => void
export type onEnvChangedListener = (provider: string) => void
export interface IDebuggerApi {
diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/function-panel.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/function-panel.tsx
index 6fa7686d5f..0511765ec5 100644
--- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/function-panel.tsx
+++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/function-panel.tsx
@@ -10,7 +10,7 @@ export const FunctionPanel = ({ data }) => {
}, [data])
return (
-