Merge branch 'master' into github

pull/1585/head
bunsenstraat 3 years ago committed by GitHub
commit 0af6378044
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      apps/debugger/src/app/debugger-api.ts
  2. 2
      apps/remix-ide-e2e/src/commands/noWorkerErrorFor.ts
  3. 153
      apps/remix-ide-e2e/src/tests/solidityUnittests.spec.ts
  4. 2
      apps/remix-ide/src/app/tabs/compile-tab.js
  5. 2
      apps/remix-ide/src/app/tabs/styles/test-tab-styles.js
  6. 59
      apps/remix-ide/src/app/tabs/test-tab.js
  7. 3
      apps/remix-ide/src/app/tabs/testTab/testTab.js
  8. 3
      apps/remix-ide/src/blockchain/blockchain.js
  9. 5
      libs/remix-core-plugin/src/lib/compiler-artefacts.ts
  10. 2
      libs/remix-solidity/package.json
  11. 3
      libs/remix-solidity/src/compiler/compiler.ts
  12. 1
      libs/remix-tests/src/compiler.ts
  13. 2
      libs/remix-tests/src/index.ts
  14. 227
      libs/remix-tests/src/runTestSources.ts
  15. 6
      libs/remix-tests/src/testRunner.ts
  16. 1
      libs/remix-tests/src/types.ts
  17. 18
      libs/remix-tests/tests/testRunner.spec.ts
  18. 12
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  19. 2
      libs/remix-ui/debugger-ui/src/lib/idebugger-api.ts
  20. 2
      libs/remix-ui/debugger-ui/src/lib/vm-debugger/function-panel.tsx
  21. 9
      libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts
  22. 9
      libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx

@ -121,9 +121,10 @@ export const DebuggerApiMixin = (Base) => class extends Base {
return await debug.debugger.traceManager.getTrace(hash) return await debug.debugger.traceManager.getTrace(hash)
} }
debug (hash) { debug (hash, web3?) {
this.debugHash = hash this.debugHash = hash
if (this.onDebugRequestedListener) this.onDebugRequestedListener(hash) if (web3) remixDebug.init.extendWeb3(web3)
if (this.onDebugRequestedListener) this.onDebugRequestedListener(hash, web3)
} }
onActivation () { onActivation () {

@ -18,7 +18,7 @@ function noWorkerErrorFor (browser: NightwatchBrowser, version: string, callback
.setSolidityCompilerVersion(version) .setSolidityCompilerVersion(version)
.click('*[data-id="compilerContainerCompileBtn"]') .click('*[data-id="compilerContainerCompileBtn"]')
.waitForElementPresent('*[data-id="compilationFinishedWith_' + version + '"]', 60000) .waitForElementPresent('*[data-id="compilationFinishedWith_' + version + '"]', 60000)
.notContainsText('*[data-id="compiledErrors"]', 'worker error:undefined') .notContainsText('*[data-id="compiledErrors"]', `Worker error: Uncaught NetworkError: Failed to execute 'importScripts' on 'WorkerGlobalScope': The script at 'https://binaries.soliditylang.org/wasm/${version}' failed to load.`)
.notContainsText('*[data-id="compiledErrors"]', 'Uncaught RangeError: Maximum call stack size exceeded') .notContainsText('*[data-id="compiledErrors"]', 'Uncaught RangeError: Maximum call stack size exceeded')
.notContainsText('*[data-id="compiledErrors"]', 'RangeError: Maximum call stack size exceeded') .notContainsText('*[data-id="compiledErrors"]', 'RangeError: Maximum call stack size exceeded')
.perform(() => { .perform(() => {

@ -47,13 +47,13 @@ module.exports = {
.click('*[data-id="testTabCheckAllTests"]') .click('*[data-id="testTabCheckAllTests"]')
.clickElementAtPosition('.singleTestLabel', 1) .clickElementAtPosition('.singleTestLabel', 1)
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]') .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"]', '✓ Initial value should be100', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✓ Value is set200', 120000) .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✓ Value is set200', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Should fail for wrong value200', 120000) .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Should fail for wrong value200', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'Passing: 2', 120000) .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'Passing: 2', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'Failing: 1', 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) { '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"]') .click('*[data-id="testTabCheckAllTests"]')
.clickElementAtPosition('.singleTestLabel', 2) .clickElementAtPosition('.singleTestLabel', 2)
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]') .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 project exists', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Check wrong project owner', 120000) .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Check wrong project owner', 120000)
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Check wrong sender', 120000) .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Check wrong sender', 120000)
@ -84,9 +84,9 @@ module.exports = {
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]') .scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.pause(2000) .pause(2000)
.click('*[data-id="testTabRunTestsTabStopAction"]') .click('*[data-id="testTabRunTestsTabStopAction"]')
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '/tests/ks2b_test.sol', 200000) .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'tests/ks2b_test.sol', 200000)
.notContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '/tests/4_Ballot_test.sol') .notContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'tests/4_Ballot_test.sol')
.notContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', '/tests/simple_storage_test.sol') .notContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'tests/simple_storage_test.sol')
.waitForElementContainsText('*[data-id="testTabTestsExecutionStopped"]', 'The test execution has been stopped', 60000) .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) .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"]') browser.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
.addFile('tests/methodFailure_test.sol', sources[0]['tests/methodFailure_test.sol']) .addFile('tests/methodFailure_test.sol', sources[0]['tests/methodFailure_test.sol'])
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
@ -178,12 +178,65 @@ module.exports = {
.scrollAndClick('#runTestsTabRunAction') .scrollAndClick('#runTestsTabRunAction')
.waitForElementVisible('*[data-id="testTabSolidityUnitTestsOutputheader"]', 120000) .waitForElementVisible('*[data-id="testTabSolidityUnitTestsOutputheader"]', 120000)
.waitForElementPresent('#solidityUnittestsOutput div[class^="testPass"]', 60000) .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 winning proposal', 60000)
.waitForElementContainsText('#solidityUnittestsOutput', '✓ Check winnin proposal with return value', 60000) .waitForElementContainsText('#solidityUnittestsOutput', '✓ Check winnin proposal with return value', 60000)
}, },
'Solidity Unit tests Basic Basic with local compiler': function (browser: NightwatchBrowser) { 'Solidity Unit tests with hardhat console log': function (browser: NightwatchBrowser) {
browser
.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
.addFile('tests/hhLogs_test.sol', sources[0]['tests/hhLogs_test.sol'])
.clickLaunchIcon('solidityUnitTesting')
.waitForElementVisible('*[id="singleTesttests/4_Ballot_test.sol"]', 60000)
.click('*[id="singleTesttests/4_Ballot_test.sol"]')
.click('#runTestsTabRunAction')
.pause(2000)
.waitForElementVisible('*[data-id="testTabSolidityUnitTestsOutputheader"]', 120000)
.waitForElementPresent('#solidityUnittestsOutput div[class^="testPass"]', 60000)
.waitForElementContainsText('#solidityUnittestsOutput', 'tests/hhLogs_test.sol', 60000)
.assert.containsText('#journal > div:nth-child(3) > span > div', 'Before all:')
.assert.containsText('#journal > div:nth-child(3) > span > div', 'Inside beforeAll')
.assert.containsText('#journal > div:nth-child(4) > span > div', 'Check sender:')
.assert.containsText('#journal > div:nth-child(4) > span > div', 'msg.sender is 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4')
.assert.containsText('#journal > div:nth-child(5) > span > div', 'Check int logs:')
.assert.containsText('#journal > div:nth-child(5) > span > div', '10 20')
.assert.containsText('#journal > div:nth-child(5) > span > div', 'Number is 25')
.openFile('tests/hhLogs_test.sol')
.removeFile('tests/hhLogs_test.sol', 'workspace_new')
},
'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 browser
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.setSolidityCompilerVersion('builtin') .setSolidityCompilerVersion('builtin')
@ -196,7 +249,7 @@ module.exports = {
.scrollAndClick('#runTestsTabRunAction') .scrollAndClick('#runTestsTabRunAction')
.waitForElementVisible('*[data-id="testTabSolidityUnitTestsOutputheader"]', 120000) .waitForElementVisible('*[data-id="testTabSolidityUnitTestsOutputheader"]', 120000)
.waitForElementPresent('#solidityUnittestsOutput div[class^="testPass"]', 60000) .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 winning proposal', 60000)
.waitForElementContainsText('#solidityUnittestsOutput', '✓ Check winnin proposal with return value', 60000) .waitForElementContainsText('#solidityUnittestsOutput', '✓ Check winnin proposal with return value', 60000)
.end() .end()
@ -401,6 +454,86 @@ 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;
}
}`
},
'tests/hhLogs_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 "hardhat/console.sol";
contract hhLogs {
function beforeAll () public {
console.log('Inside beforeAll');
}
function checkSender () public {
console.log('msg.sender is %s', msg.sender);
Assert.ok(true, "should be true");
}
function checkIntLogs () public {
console.log(10,20);
console.log('Number is %d', 25);
Assert.ok(true, "should be true");
}
}`
} }
} }
] ]
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'
}
}

@ -162,7 +162,7 @@ class CompileTab extends ViewPlugin {
) )
}) })
} else { } else {
const count = (data.errors ? data.errors.filter(error => error.severity === 'error').length : 0 + data.error ? 1 : 0) const count = (data.errors ? data.errors.filter(error => error.severity === 'error').length : 0) + data.error ? 1 : 0
this.emit('statusChanged', { key: count, title: `compilation failed with ${count} error${count.length > 1 ? 's' : ''}`, type: 'error' }) this.emit('statusChanged', { key: count, title: `compilation failed with ${count} error${count.length > 1 ? 's' : ''}`, type: 'error' })
} }
// Update contract Selection // Update contract Selection

@ -31,8 +31,6 @@ var css = csjs`
border-radius: 4px; border-radius: 4px;
padding: 1% 1% 1% 5%; padding: 1% 1% 1% 5%;
} }
.testFailure {
}
.testFailureSummary { .testFailureSummary {
} }
.title { .title {

@ -7,7 +7,9 @@ var async = require('async')
var tooltip = require('../ui/tooltip') var tooltip = require('../ui/tooltip')
var Renderer = require('../ui/renderer') var Renderer = require('../ui/renderer')
var css = require('./styles/test-tab-styles') var css = require('./styles/test-tab-styles')
var remixTests = require('@remix-project/remix-tests') var { UnitTestRunner } = require('@remix-project/remix-tests')
const _paq = window._paq = window._paq || []
const TestTabLogic = require('./testTab/testTab') const TestTabLogic = require('./testTab/testTab')
@ -33,12 +35,15 @@ module.exports = class TestTab extends ViewPlugin {
this.data = {} this.data = {}
this.appManager = appManager this.appManager = appManager
this.renderer = new Renderer(this) this.renderer = new Renderer(this)
this.testRunner = new UnitTestRunner()
this.hasBeenStopped = false this.hasBeenStopped = false
this.runningTestsNumber = 0 this.runningTestsNumber = 0
this.readyTestsNumber = 0 this.readyTestsNumber = 0
this.areTestsRunning = false this.areTestsRunning = false
this.defaultPath = 'tests' this.defaultPath = 'tests'
this.offsetToLineColumnConverter = offsetToLineColumnConverter this.offsetToLineColumnConverter = offsetToLineColumnConverter
this.allFilesInvolved = []
this.isDebugging = false
appManager.event.on('activate', (name) => { appManager.event.on('activate', (name) => {
if (name === 'solidity') this.updateRunAction() if (name === 'solidity') this.updateRunAction()
@ -71,7 +76,8 @@ module.exports = class TestTab extends ViewPlugin {
onDeactivation () { onDeactivation () {
this.off('filePanel', 'newTestFileCreated') this.off('filePanel', 'newTestFileCreated')
this.off('filePanel', 'setWorkspace') this.off('filePanel', 'setWorkspace')
this.fileManager.events.removeListener('currentFileChanged', this.updateForNewCurrent) // 'currentFileChanged' event is added more than once
this.fileManager.events.removeAllListeners('currentFileChanged')
} }
listenToEvents () { listenToEvents () {
@ -95,6 +101,15 @@ module.exports = class TestTab extends ViewPlugin {
this.setCurrentPath(this.defaultPath) 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', () => { this.fileManager.events.on('noFileSelected', () => {
}) })
@ -102,6 +117,9 @@ module.exports = class TestTab extends ViewPlugin {
} }
async updateForNewCurrent (file) { 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.data.allTests = []
this.updateTestFileList() this.updateTestFileList()
this.clearResults() this.clearResults()
@ -192,6 +210,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) { printHHLogs (logsArr, testName) {
let finalLogs = `<b>${testName}:</b>\n` let finalLogs = `<b>${testName}:</b>\n`
for (const log of logsArr) { for (const log of logsArr) {
@ -207,6 +232,7 @@ module.exports = class TestTab extends ViewPlugin {
} }
finalLogs = finalLogs + '&emsp;' + formattedLog + '\n' finalLogs = finalLogs + '&emsp;' + formattedLog + '\n'
} }
_paq.push(['trackEvent', 'solidityUnitTesting', 'hardhat', 'console.log'])
this.call('terminal', 'log', { type: 'info', value: finalLogs }) this.call('terminal', 'log', { type: 'info', value: finalLogs })
} }
@ -242,13 +268,30 @@ module.exports = class TestTab extends ViewPlugin {
} else if (result.type === 'testFailure') { } else if (result.type === 'testFailure') {
if (result.hhLogs && result.hhLogs.length) this.printHHLogs(result.hhLogs, result.value) if (result.hhLogs && result.hhLogs.length) this.printHHLogs(result.hhLogs, result.value)
if (!result.assertMethod) { 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`<div
class="btn border btn btn-sm ml-1"
title="Start debugging"
onclick=${() => this.startDebug(txHash, web3)}
>
<i class="fas fa-bug"></i>
</div>`
debugBtn.style.visibility = 'visible'
debugBtn.style.cursor = 'pointer'
} else debugBtn.style.visibility = 'hidden'
this.testsOutput.appendChild(yo` this.testsOutput.appendChild(yo`
<div <div
class="bg-light mb-2 ${css.testFailure} ${css.testLog} d-flex flex-column text-danger border-0" class="bg-light mb-2 px-2 ${css.testLog} d-flex flex-column text-danger border-0"
id="UTContext${result.context}" id="UTContext${result.context}"
onclick=${() => this.highlightLocation(result.location, runningTests, result.filename)} onclick=${() => this.highlightLocation(result.location, runningTests, result.filename)}
> >
<span> ${result.value}</span> <div class="d-flex my-1 align-items-start justify-content-between">
<span> ${result.value}</span>
${debugBtn}
</div>
<span class="text-dark">Error Message:</span> <span class="text-dark">Error Message:</span>
<span class="pb-2 text-break">"${result.errMsg}"</span> <span class="pb-2 text-break">"${result.errMsg}"</span>
</div> </div>
@ -259,7 +302,7 @@ module.exports = class TestTab extends ViewPlugin {
const expected = result.assertMethod === 'ok' ? '\'true\'' : result.expected const expected = result.assertMethod === 'ok' ? '\'true\'' : result.expected
this.testsOutput.appendChild(yo` this.testsOutput.appendChild(yo`
<div <div
class="bg-light mb-2 ${css.testFailure} ${css.testLog} d-flex flex-column text-danger border-0" class="bg-light mb-2 px-2 ${css.testLog} d-flex flex-column text-danger border-0"
id="UTContext${result.context}" id="UTContext${result.context}"
onclick=${() => this.highlightLocation(result.location, runningTests, result.filename)} onclick=${() => this.highlightLocation(result.location, runningTests, result.filename)}
> >
@ -446,7 +489,7 @@ module.exports = class TestTab extends ViewPlugin {
usingWorker: canUseWorker(currentVersion), usingWorker: canUseWorker(currentVersion),
runs runs
} }
remixTests.runTestSources(runningTest, compilerConfig, () => {}, () => {}, (error, result) => { this.testRunner.runTestSources(runningTest, compilerConfig, () => {}, () => {}, (error, result) => {
if (error) return reject(error) if (error) return reject(error)
resolve(result) resolve(result)
}, (url, cb) => { }, (url, cb) => {
@ -456,6 +499,7 @@ module.exports = class TestTab extends ViewPlugin {
} }
runTest (testFilePath, callback) { runTest (testFilePath, callback) {
this.isDebugging = false
if (this.hasBeenStopped) { if (this.hasBeenStopped) {
this.updateFinalResult() this.updateFinalResult()
return return
@ -473,7 +517,7 @@ module.exports = class TestTab extends ViewPlugin {
usingWorker: canUseWorker(currentVersion), usingWorker: canUseWorker(currentVersion),
runs runs
} }
remixTests.runTestSources( this.testRunner.runTestSources(
runningTests, runningTests,
compilerConfig, compilerConfig,
(result) => this.testCallback(result, runningTests), (result) => this.testCallback(result, runningTests),
@ -528,6 +572,7 @@ module.exports = class TestTab extends ViewPlugin {
const tests = this.data.selectedTests const tests = this.data.selectedTests
if (!tests) return if (!tests) return
this.resultStatistics.hidden = tests.length === 0 this.resultStatistics.hidden = tests.length === 0
_paq.push(['trackEvent', 'solidityUnitTesting', 'runTests'])
async.eachOfSeries(tests, (value, key, callback) => { async.eachOfSeries(tests, (value, key, callback) => {
if (this.hasBeenStopped) return if (this.hasBeenStopped) return
this.runTest(value, callback) this.runTest(value, callback)

@ -68,7 +68,8 @@ class TestTabLogic {
cb(e.message) cb(e.message)
} }
for (var file in files) { 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) cb(null, tests, this.currentPath)
} }

@ -13,6 +13,8 @@ const { txFormat, txExecution, typeConversion, txListener: Txlistener, TxRunner,
const { txResultHelper: resultToRemixTx } = helpers const { txResultHelper: resultToRemixTx } = helpers
const packageJson = require('../../../../package.json') const packageJson = require('../../../../package.json')
const _paq = window._paq = window._paq || [] //eslint-disable-line
const profile = { const profile = {
name: 'blockchain', name: 'blockchain',
displayName: 'Blockchain', displayName: 'Blockchain',
@ -515,6 +517,7 @@ class Blockchain extends Plugin {
} }
finalLogs = finalLogs + '&emsp;' + formattedLog + '\n' finalLogs = finalLogs + '&emsp;' + formattedLog + '\n'
} }
_paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log'])
this.call('terminal', 'log', { type: 'info', value: finalLogs }) this.call('terminal', 'log', { type: 'info', value: finalLogs })
} }
execResult = await this.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash) execResult = await this.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash)

@ -52,6 +52,11 @@ export class CompilerArtefacts extends Plugin {
this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source)
saveCompilationPerFileResult(file, source, languageVersion, data) 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 () { getAllContractDatas () {

@ -23,6 +23,8 @@
"eslint-scope": "^5.0.0", "eslint-scope": "^5.0.0",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
"ethers": "^5.4.2", "ethers": "^5.4.2",
"minixhr": "^3.2.2",
"semver": "^6.3.0",
"solc": "^0.7.4", "solc": "^0.7.4",
"string-similarity": "^4.0.4", "string-similarity": "^4.0.4",
"web3": "^1.5.1", "web3": "^1.5.1",

@ -281,7 +281,8 @@ export class Compiler {
}) })
this.state.worker.addEventListener('error', (msg: Record <'data', MessageFromWorker>) => { this.state.worker.addEventListener('error', (msg: Record <'data', MessageFromWorker>) => {
this.onCompilationFinished({ error: { formattedMessage: 'Worker error: ' + msg.data } }) const formattedMessage = `Worker error: ${msg.data && msg.data !== undefined ? msg.data : msg['message']}`
this.onCompilationFinished({ error: { formattedMessage } })
}) })
this.state.compileJSON = (source: SourceWithTarget) => { this.state.compileJSON = (source: SourceWithTarget) => {

@ -205,6 +205,7 @@ export function compileContractSources (sources: SrcIfc, compilerConfig: Compile
function doCompilation (next) { function doCompilation (next) {
// @ts-ignore // @ts-ignore
compiler.event.register('compilationFinished', this, (success, data, source) => { compiler.event.register('compilationFinished', this, (success, data, source) => {
if (opts && opts.event) opts.event.emit('compilationFinished', success, data, source)
next(null, data) next(null, data)
}) })
compiler.compile(sources, filepath) compiler.compile(sources, filepath)

@ -1,5 +1,5 @@
export { runTestFiles } from './runTestFiles' export { runTestFiles } from './runTestFiles'
export { runTestSources } from './runTestSources' export { UnitTestRunner } from './runTestSources'
export { runTest } from './testRunner' export { runTest } from './testRunner'
export * from './types' export * from './types'
export const assertLibCode = require('../sol/tests.sol') export const assertLibCode = require('../sol/tests.sol')

@ -5,6 +5,7 @@ import { deployAll } from './deployer'
import { runTest } from './testRunner' import { runTest } from './testRunner'
import Web3 from 'web3' import Web3 from 'web3'
import { EventEmitter } from 'events'
import { Provider, extend } from '@remix-project/remix-simulator' import { Provider, extend } from '@remix-project/remix-simulator'
import { import {
FinalResult, SrcIfc, compilationInterface, ASTInterface, Options, FinalResult, SrcIfc, compilationInterface, ASTInterface, Options,
@ -12,126 +13,134 @@ import {
} from './types' } from './types'
require('colors') require('colors')
const createWeb3Provider = async function () { export class UnitTestRunner {
const web3 = new Web3() event
const provider: any = new Provider()
await provider.init()
web3.setProvider(provider)
extend(web3)
return web3
}
/** constructor () {
* @dev Run tests from source of a test contract file (used for IDE) this.event = new EventEmitter()
* @param contractSources Sources of contract }
* @param compilerConfig current compiler configuration
* @param testCallback Test callback async createWeb3Provider () {
* @param resultCallback Result Callback const web3 = new Web3()
* @param finalCallback Final Callback const provider: any = new Provider()
* @param importFileCb Import file callback await provider.init()
* @param opts Options web3.setProvider(provider)
*/ extend(web3)
export async function runTestSources (contractSources: SrcIfc, compilerConfig: CompilerConfiguration, testCallback, resultCallback, finalCallback: any, importFileCb, opts: Options) { return web3
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[] = []
for (const filename in compilationResult) { /**
if (!filename.endsWith('_test.sol')) { * @dev Run tests from source of a test contract file (used for IDE)
continue * @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 => { deployAll(compilationResult, web3, false, (err, contracts) => {
contractsToTestDetails.push(compilationResult[filename][contractName]) if (err) {
contractsToTest.push(contractName) // 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 determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) {
}, const contractsToTest: string[] = []
function runTests (contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) { const contractsToTestDetails: any[] = []
let totalPassing = 0
let totalFailing = 0 for (const filename in compilationResult) {
let totalTime = 0 if (!filename.endsWith('_test.sol')) {
const errors: any[] = [] continue
// eslint-disable-next-line handle-callback-err }
const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) { Object.keys(compilationResult[filename]).forEach(contractName => {
if (result.type === 'testFailure') { contractsToTestDetails.push(compilationResult[filename][contractName])
errors.push(result) 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) { const _resultsCallback = function (_err, result, cb) {
resultCallback(_err, result, () => {}) // eslint-disable-line @typescript-eslint/no-empty-function resultCallback(_err, result, () => {}) // eslint-disable-line @typescript-eslint/no-empty-function
totalPassing += result.passingNum totalPassing += result.passingNum
totalFailing += result.failureNum totalFailing += result.failureNum
totalTime += result.timePassed totalTime += result.timePassed
cb() cb()
} }
async.eachOfLimit(contractsToTest, 1, (contractName: string, index: string | number, cb: ErrorCallback) => { async.eachOfLimit(contractsToTest, 1, (contractName: string, index: string | number, cb: ErrorCallback) => {
const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']] const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']]
runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts, web3 }, _testCallback, (err, result) => { 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) { if (err) {
return cb(err) return next(err)
} }
_resultsCallback(null, result, cb)
})
}, function (err) {
if (err) {
return next(err)
}
const finalResults: FinalResult = { const finalResults: FinalResult = {
totalPassing: 0, totalPassing: 0,
totalFailing: 0, totalFailing: 0,
totalTime: 0, totalTime: 0,
errors: [] errors: []
} }
finalResults.totalPassing = totalPassing || 0 finalResults.totalPassing = totalPassing || 0
finalResults.totalFailing = totalFailing || 0 finalResults.totalFailing = totalFailing || 0
finalResults.totalTime = totalTime || 0 finalResults.totalTime = totalTime || 0
finalResults.errors = [] finalResults.errors = []
errors.forEach((error, _index) => { errors.forEach((error, _index) => {
finalResults.errors.push({ context: error.context, value: error.value, message: error.errMsg }) finalResults.errors.push({ context: error.context, value: error.value, message: error.errMsg })
}) })
next(null, finalResults) next(null, finalResults)
}) })
} }
], finalCallback) ], finalCallback)
}
} }

@ -322,7 +322,8 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
assertMethod, assertMethod,
returned: testEvent[3], returned: testEvent[3],
expected: testEvent[4], expected: testEvent[4],
location location,
web3
} }
if (hhLogs) resp.hhLogs = hhLogs if (hhLogs) resp.hhLogs = hhLogs
testCallback(undefined, resp) testCallback(undefined, resp)
@ -373,7 +374,8 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
filename: testObject.filename, filename: testObject.filename,
time: time, time: time,
errMsg: err.message, errMsg: err.message,
context: testName context: testName,
web3
} }
testCallback(undefined, resp) testCallback(undefined, resp)
failureNum += 1 failureNum += 1

@ -37,6 +37,7 @@ export interface TestResultInterface {
expected?: string | number expected?: string | number
location?: string location?: string
hhLogs?: [] hhLogs?: []
web3?: any
} }
export interface TestCbInterface { export interface TestCbInterface {
(error: Error | null | undefined, result: TestResultInterface) : void; (error: Error | null | undefined, result: TestResultInterface) : void;

@ -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: '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'}, { 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: '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: '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'} { 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: '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: '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'}, { 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: '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: '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'} { 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: '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: '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'}, { 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: '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: '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' } { 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: '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: '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' } { 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: '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: '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' } { 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: '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 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' } { type: 'testPass', value: 'Check if odd', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' }
], ['time']) ], ['time', 'web3'])
}) })
}) })

@ -39,8 +39,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
return unLoad() return unLoad()
}, []) }, [])
debuggerModule.onDebugRequested((hash) => { debuggerModule.onDebugRequested((hash, web3?) => {
if (hash) debug(hash) if (hash) debug(hash, web3)
}) })
debuggerModule.onRemoveHighlights(async () => { 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 (state.debugger) unLoad()
if (!txNumber) return if (!txNumber) return
setState(prevState => { setState(prevState => {
@ -181,7 +181,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
return 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 { try {
const networkId = await web3.eth.net.getId() const networkId = await web3.eth.net.getId()
_paq.push(['trackEvent', 'debugger', 'startDebugging', networkId]) _paq.push(['trackEvent', 'debugger', 'startDebugging', networkId])
@ -259,7 +259,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
} }
} }
const debug = (txHash) => { const debug = (txHash, web3?) => {
setState(prevState => { setState(prevState => {
return { return {
...prevState, ...prevState,
@ -267,7 +267,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
txNumber: txHash txNumber: txHash
} }
}) })
startDebugging(null, txHash, null) startDebugging(null, txHash, null, web3)
} }
const stepManager = { const stepManager = {

@ -45,7 +45,7 @@ export interface TransactionReceipt {
export type onBreakpointClearedListener = (params: string, row: number) => void export type onBreakpointClearedListener = (params: string, row: number) => void
export type onBreakpointAddedListener = (params: string, row: number) => void export type onBreakpointAddedListener = (params: string, row: number) => void
export type onEditorContentChanged = () => 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 type onEnvChangedListener = (provider: string) => void
export interface IDebuggerApi { export interface IDebuggerApi {

@ -10,7 +10,7 @@ export const FunctionPanel = ({ data }) => {
}, [data]) }, [data])
return ( return (
<div id='FunctionPanel'> <div id='FunctionPanel' data-id='functionPanel'>
<DropdownPanel dropdownName='Function Stack' calldata={calldata || {}} /> <DropdownPanel dropdownName='Function Stack' calldata={calldata || {}} />
</div> </div>
) )

@ -10,6 +10,14 @@ const profile = {
methods: ['getCompilerState'], methods: ['getCompilerState'],
version: packageJson.version version: packageJson.version
} }
declare global {
interface Window {
_paq: any
}
}
const _paq = window._paq = window._paq || [] //eslint-disable-line
export class CompileTab extends Plugin { export class CompileTab extends Plugin {
public compiler public compiler
public optimize public optimize
@ -115,6 +123,7 @@ export class CompileTab extends Plugin {
` `
const configFilePath = 'remix-compiler.config.js' const configFilePath = 'remix-compiler.config.js'
this.api.writeFile(configFilePath, fileContent) this.api.writeFile(configFilePath, fileContent)
_paq.push(['trackEvent', 'compiler', 'compileWithHardhat'])
this.call('hardhat', 'compile', configFilePath).then((result) => { this.call('hardhat', 'compile', configFilePath).then((result) => {
this.call('terminal', 'log', { type: 'info', value: result }) this.call('terminal', 'log', { type: 'info', value: result })
}).catch((error) => { }).catch((error) => {

@ -11,6 +11,13 @@ import { OverlayTrigger, Tooltip } from 'react-bootstrap'// eslint-disable-line
const StaticAnalysisRunner = require('@remix-project/remix-analyzer').CodeAnalysis const StaticAnalysisRunner = require('@remix-project/remix-analyzer').CodeAnalysis
const utils = remixLib.util const utils = remixLib.util
declare global {
interface Window {
_paq: any
}
}
const _paq = window._paq = window._paq || [] //eslint-disable-line
/* eslint-disable-next-line */ /* eslint-disable-next-line */
export interface RemixUiStaticAnalyserProps { export interface RemixUiStaticAnalyserProps {
registry: any, registry: any,
@ -154,6 +161,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
const warningErrors = [] const warningErrors = []
// Remix Analysis // Remix Analysis
_paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyzeWithRemixAnalyzer'])
runner.run(lastCompilationResult, categoryIndex, results => { runner.run(lastCompilationResult, categoryIndex, results => {
results.map((result) => { results.map((result) => {
let moduleName let moduleName
@ -212,6 +220,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
props.analysisModule.call('solidity-logic', 'getCompilerState').then(async (compilerState) => { props.analysisModule.call('solidity-logic', 'getCompilerState').then(async (compilerState) => {
const { currentVersion, optimize, evmVersion } = compilerState const { currentVersion, optimize, evmVersion } = compilerState
props.analysisModule.call('terminal', 'log', { type: 'info', value: '[Slither Analysis]: Running...' }) props.analysisModule.call('terminal', 'log', { type: 'info', value: '[Slither Analysis]: Running...' })
_paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyzeWithSlither'])
props.analysisModule.call('slither', 'analyse', state.file, { currentVersion, optimize, evmVersion }).then(async (result) => { props.analysisModule.call('slither', 'analyse', state.file, { currentVersion, optimize, evmVersion }).then(async (result) => {
if (result.status) { if (result.status) {
props.analysisModule.call('terminal', 'log', { type: 'info', value: `[Slither Analysis]: Analysis Completed!! ${result.count} warnings found.` }) props.analysisModule.call('terminal', 'log', { type: 'info', value: `[Slither Analysis]: Analysis Completed!! ${result.count} warnings found.` })

Loading…
Cancel
Save