code cleaning

pull/5370/head
aniket-engg 3 years ago committed by Aniket
parent f528bc1656
commit c5337d2cef
  1. 543
      apps/remix-ide/src/app/tabs/test-tab.js
  2. 138
      apps/remix-ide/src/app/tabs/testTab/testTab.js
  3. 6
      libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx

@ -17,8 +17,6 @@ var { UnitTestRunner, assertLibCode } = require('@remix-project/remix-tests')
const _paq = window._paq = window._paq || [] const _paq = window._paq = window._paq || []
// const TestTabLogic = require('./testTab/testTab')
const profile = { const profile = {
name: 'solidityUnitTesting', name: 'solidityUnitTesting',
displayName: 'Solidity unit testing', displayName: 'Solidity unit testing',
@ -43,15 +41,8 @@ module.exports = class TestTab extends ViewPlugin {
this.renderer = new Renderer(this) this.renderer = new Renderer(this)
this.testRunner = new UnitTestRunner() this.testRunner = new UnitTestRunner()
this.testTabLogic = new TestTabLogic(this.fileManager, helper) this.testTabLogic = new TestTabLogic(this.fileManager, helper)
this.hasBeenStopped = false
this.runningTestsNumber = 0
this.readyTestsNumber = 0
this.areTestsRunning = false
this.defaultPath = 'tests'
this.offsetToLineColumnConverter = offsetToLineColumnConverter this.offsetToLineColumnConverter = offsetToLineColumnConverter
this.allFilesInvolved = ['.deps/remix-tests/remix_tests.sol', '.deps/remix-tests/remix_accounts.sol'] this.allFilesInvolved = ['.deps/remix-tests/remix_tests.sol', '.deps/remix-tests/remix_accounts.sol']
this.isDebugging = false
this.currentErrors = []
this.element = document.createElement('div') this.element = document.createElement('div')
appManager.event.on('activate', (name) => { appManager.event.on('activate', (name) => {
@ -150,300 +141,6 @@ module.exports = class TestTab extends ViewPlugin {
this.fileManager.events.on('currentFileChanged', (file, provider) => this.updateForNewCurrent(file)) this.fileManager.events.on('currentFileChanged', (file, provider) => this.updateForNewCurrent(file))
} }
async updateForNewCurrent (file) {
// Ensure that when someone clicks on compilation error and that opens a new file
// Test result, which is compilation error in this case, is not cleared
if (this.currentErrors) {
if (Array.isArray(this.currentErrors) && this.currentErrors.length > 0) {
const errFiles = this.currentErrors.map(err => { if (err.sourceLocation && err.sourceLocation.file) return err.sourceLocation.file })
if (errFiles.includes(file)) return
} else if (this.currentErrors.sourceLocation && this.currentErrors.sourceLocation.file && this.currentErrors.sourceLocation.file === file) return
}
// 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()
this.updateGenerateFileAction()
if (!this.areTestsRunning) this.updateRunAction(file)
try {
await this.testTabLogic.getTests((error, tests) => {
if (error) return tooltip(error)
this.data.allTests = tests
this.data.selectedTests = [...this.data.allTests]
this.updateTestFileList(tests)
if (!this.testsOutput) return // eslint-disable-line
})
} catch (e) {
console.log(e)
}
}
createSingleTest (testFile) {
return yo`
<div class="d-flex align-items-center py-1">
<input class="singleTest" id="singleTest${testFile}" onchange=${(e) => this.toggleCheckbox(e.target.checked, testFile)} type="checkbox" checked="true">
<label class="singleTestLabel text-nowrap pl-2 mb-0" for="singleTest${testFile}">${testFile}</label>
</div>
`
}
listTests () {
if (!this.data.allTests || !this.data.allTests.length) return []
return this.data.allTests.map(
testFile => this.createSingleTest(testFile)
)
}
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 = `<b>${testName}:</b>\n`
for (const log of logsArr) {
let formattedLog
// Hardhat implements the same formatting options that can be found in Node.js' console.log,
// which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args
// For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6'
// We check first arg to determine if 'util.format' is needed
if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) {
formattedLog = format(log[0], ...log.slice(1))
} else {
formattedLog = log.join(' ')
}
finalLogs = finalLogs + '&emsp;' + formattedLog + '\n'
}
_paq.push(['trackEvent', 'solidityUnitTesting', 'hardhat', 'console.log'])
this.call('terminal', 'log', { type: 'info', value: finalLogs })
}
testCallback (result, runningTests) {
this.testsOutput.hidden = false
let debugBtn = yo``
if ((result.type === 'testPass' || result.type === 'testFailure') && result.debugTxHash) {
const { web3, debugTxHash } = result
debugBtn = yo`<div id=${result.value.replaceAll(' ', '_')} class="btn border btn btn-sm ml-1" title="Start debugging" onclick=${() => this.startDebug(debugTxHash, web3)}>
<i class="fas fa-bug"></i>
</div>`
debugBtn.style.cursor = 'pointer'
}
if (result.type === 'contract') {
this.testSuite = result.value
if (this.testSuites) {
this.testSuites.push(this.testSuite)
} else {
this.testSuites = [this.testSuite]
}
this.rawFileName = result.filename
this.runningTestFileName = this.cleanFileName(this.rawFileName, this.testSuite)
this.outputHeader = yo`
<div id="${this.runningTestFileName}" data-id="testTabSolidityUnitTestsOutputheader" class="pt-1">
<span class="font-weight-bold">${this.testSuite} (${this.rawFileName})</span>
</div>
`
this.testsOutput.appendChild(this.outputHeader)
} else if (result.type === 'testPass') {
if (result.hhLogs && result.hhLogs.length) this.printHHLogs(result.hhLogs, result.value)
this.testsOutput.appendChild(yo`
<div
id="${this.runningTestFileName}"
data-id="testTabSolidityUnitTestsOutputheader"
class="${css.testPass} ${css.testLog} bg-light mb-2 px-2 text-success border-0"
onclick=${() => this.discardHighlight()}
>
<div class="d-flex my-1 align-items-start justify-content-between">
<span style="margin-block: auto" > ${result.value}</span>
${debugBtn}
</div>
</div>
`)
} else if (result.type === 'testFailure') {
if (result.hhLogs && result.hhLogs.length) this.printHHLogs(result.hhLogs, result.value)
if (!result.assertMethod) {
this.testsOutput.appendChild(yo`
<div
class="bg-light mb-2 px-2 ${css.testLog} d-flex flex-column text-danger border-0"
id="UTContext${result.context}"
onclick=${() => this.highlightLocation(result.location, runningTests, result.filename)}
>
<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="pb-2 text-break">"${result.errMsg}"</span>
</div>
`)
} else {
const preposition = result.assertMethod === 'equal' || result.assertMethod === 'notEqual' ? 'to' : ''
const method = result.assertMethod === 'ok' ? '' : result.assertMethod
const expected = result.assertMethod === 'ok' ? '\'true\'' : result.expected
this.testsOutput.appendChild(yo`
<div
class="bg-light mb-2 px-2 ${css.testLog} d-flex flex-column text-danger border-0"
id="UTContext${result.context}"
onclick=${() => this.highlightLocation(result.location, runningTests, result.filename)}
>
<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="pb-2 text-break">"${result.errMsg}"</span>
<span class="text-dark">Assertion:</span>
<div class="d-flex flex-wrap">
<span>Expected value should be</span>
<div class="mx-1 font-weight-bold">${method}</div>
<div>${preposition} ${expected}</div>
</div>
<span class="text-dark">Received value:</span>
<span>${result.returned}</span>
<span class="text-dark text-sm pb-2">Skipping the remaining tests of the function.</span>
</div>
`)
}
} else if (result.type === 'logOnly') {
if (result.hhLogs && result.hhLogs.length) this.printHHLogs(result.hhLogs, result.value)
}
}
resultsCallback (_err, result, cb) {
// total stats for the test
// result.passingNum
// result.failureNum
// result.timePassed
cb()
}
cleanFileName (fileName, testSuite) {
return fileName ? fileName.replace(/\//g, '_').replace(/\./g, '_') + testSuite : fileName
}
setHeader (status) {
if (status) {
const label = yo`
<div
class="alert-success d-inline-block mb-1 mr-1 p-1 passed_${this.runningTestFileName}"
title="All contract tests passed"
>
PASS
</div>
`
this.outputHeader && yo.update(this.outputHeader, yo`
<div id="${this.runningTestFileName}" data-id="testTabSolidityUnitTestsOutputheader" class="pt-1">
${label} <span class="font-weight-bold">${this.testSuite} (${this.rawFileName})</span>
</div>
`)
} else {
const label = yo`
<div
class="alert-danger d-inline-block mb-1 mr-1 p-1 failed_${this.runningTestFileName}"
title="At least one contract test failed"
>
FAIL
</div>
`
this.outputHeader && yo.update(this.outputHeader, yo`
<div id="${this.runningTestFileName}" data-id="testTabSolidityUnitTestsOutputheader" class="pt-1">
${label} <span class="font-weight-bold">${this.testSuite} (${this.rawFileName})</span>
</div>
`)
}
}
updateFinalResult (_errors, result, filename) {
++this.readyTestsNumber
this.testsOutput.hidden = false
if (!result && (_errors && (_errors.errors || (Array.isArray(_errors) && (_errors[0].message || _errors[0].formattedMessage))))) {
this.testCallback({ type: 'contract', filename })
this.currentErrors = _errors.errors
this.setHeader(false)
}
if (_errors && _errors.errors) {
_errors.errors.forEach((err) => this.renderer.error(err.formattedMessage || err.message, this.testsOutput, { type: err.severity, errorType: err.type }))
} else if (_errors && Array.isArray(_errors) && (_errors[0].message || _errors[0].formattedMessage)) {
_errors.forEach((err) => this.renderer.error(err.formattedMessage || err.message, this.testsOutput, { type: err.severity, errorType: err.type }))
} else if (_errors && !_errors.errors && !Array.isArray(_errors)) {
// To track error like this: https://github.com/ethereum/remix/pull/1438
this.renderer.error(_errors.formattedMessage || _errors.message, this.testsOutput, { type: 'error' })
}
yo.update(this.resultStatistics, this.createResultLabel())
if (result) {
const totalTime = parseFloat(result.totalTime).toFixed(2)
if (result.totalPassing > 0 && result.totalFailing > 0) {
this.testsOutput.appendChild(yo`
<div class="d-flex alert-secondary mb-3 p-3 flex-column">
<span class="font-weight-bold">Result for ${filename}</span>
<span class="text-success">Passing: ${result.totalPassing}</span>
<span class="text-danger">Failing: ${result.totalFailing}</span>
<span>Total time: ${totalTime}s</span>
</div>
`)
} else if (result.totalPassing > 0 && result.totalFailing <= 0) {
this.testsOutput.appendChild(yo`
<div class="d-flex alert-secondary mb-3 p-3 flex-column">
<span class="font-weight-bold">Result for ${filename}</span>
<span class="text-success">Passing: ${result.totalPassing}</span>
<span>Total time: ${totalTime}s</span>
</div>
`)
} else if (result.totalPassing <= 0 && result.totalFailing > 0) {
this.testsOutput.appendChild(yo`
<div class="d-flex alert-secondary mb-3 p-3 flex-column">
<span class="font-weight-bold">Result for ${filename}</span>
<span class="text-danger">Failing: ${result.totalFailing}</span>
<span>Total time: ${totalTime}s</span>
</div>
`)
}
// fix for displaying right label for multiple tests (testsuites) in a single file
this.testSuites.forEach(testSuite => {
this.testSuite = testSuite
this.runningTestFileName = this.cleanFileName(filename, this.testSuite)
this.outputHeader = document.querySelector(`#${this.runningTestFileName}`)
this.setHeader(true)
})
result.errors.forEach((error, index) => {
this.testSuite = error.context
this.runningTestFileName = this.cleanFileName(filename, error.context)
this.outputHeader = document.querySelector(`#${this.runningTestFileName}`)
const isFailingLabel = document.querySelector(`.failed_${this.runningTestFileName}`)
if (!isFailingLabel) this.setHeader(false)
})
this.testsOutput.appendChild(yo`
<div>
<p class="text-info mb-2 border-top m-0"></p>
</div>
`)
}
if (this.hasBeenStopped && (this.readyTestsNumber !== this.runningTestsNumber)) {
// if all tests has been through before stopping no need to print this.
this.testsExecutionStopped.hidden = false
}
if (_errors) this.testsExecutionStoppedError.hidden = false
if (_errors || this.hasBeenStopped || this.readyTestsNumber === this.runningTestsNumber) {
// All tests are ready or the operation has been canceled or there was a compilation error in one of the test files.
const stopBtn = document.getElementById('runTestsTabStopAction')
stopBtn.setAttribute('disabled', 'disabled')
const stopBtnLabel = document.getElementById('runTestsTabStopActionLabel')
stopBtnLabel.innerText = 'Stop'
if (this.data.selectedTests.length !== 0) {
const runBtn = document.getElementById('runTestsTabRunAction')
runBtn.removeAttribute('disabled')
}
this.areTestsRunning = false
}
}
async testFromPath (path) { async testFromPath (path) {
const fileContent = await this.fileManager.readFile(path) const fileContent = await this.fileManager.readFile(path)
return this.testFromSource(fileContent, path) return this.testFromSource(fileContent, path)
@ -485,167 +182,6 @@ module.exports = class TestTab extends ViewPlugin {
}) })
} }
runTest (testFilePath, callback) {
this.isDebugging = false
if (this.hasBeenStopped) {
this.updateFinalResult()
return
}
this.resultStatistics.hidden = false
this.fileManager.readFile(testFilePath).then((content) => {
const runningTests = {}
runningTests[testFilePath] = { content }
const { currentVersion, evmVersion, optimize, runs, isUrl } = this.compileTab.getCurrentCompilerConfig()
const currentCompilerUrl = isUrl ? currentVersion : urlFromVersion(currentVersion)
const compilerConfig = {
currentCompilerUrl,
evmVersion,
optimize,
usingWorker: canUseWorker(currentVersion),
runs
}
const deployCb = async (file, contractAddress) => {
const compilerData = await this.call('compilerArtefacts', 'getCompilerAbstract', file)
await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compilerData)
}
this.testRunner.runTestSources(
runningTests,
compilerConfig,
(result) => this.testCallback(result, runningTests),
(_err, result, cb) => this.resultsCallback(_err, result, cb),
deployCb,
(error, result) => {
this.updateFinalResult(error, result, testFilePath)
callback(error)
}, (url, cb) => {
return this.contentImport.resolveAndSave(url).then((result) => cb(null, result)).catch((error) => cb(error.message))
}, { testFilePath }
)
}).catch((error) => {
if (error) return // eslint-disable-line
})
}
clearResults () {
yo.update(this.resultStatistics, yo`<span></span>`)
this.call('editor', 'clearAnnotations')
this.testsOutput.innerHTML = ''
this.testsOutput.hidden = true
this.testsExecutionStopped.hidden = true
this.testsExecutionStoppedError.hidden = true
}
runTests () {
this.areTestsRunning = true
this.hasBeenStopped = false
this.readyTestsNumber = 0
this.runningTestsNumber = this.data.selectedTests.length
const stopBtn = document.getElementById('runTestsTabStopAction')
stopBtn.removeAttribute('disabled')
const runBtn = document.getElementById('runTestsTabRunAction')
runBtn.setAttribute('disabled', 'disabled')
this.clearResults()
yo.update(this.resultStatistics, this.createResultLabel())
const tests = this.data.selectedTests
if (!tests) return
this.resultStatistics.hidden = tests.length === 0
_paq.push(['trackEvent', 'solidityUnitTesting', 'runTests'])
async.eachOfSeries(tests, (value, key, callback) => {
if (this.hasBeenStopped) return
this.runTest(value, callback)
})
}
stopTests () {
this.hasBeenStopped = true
const stopBtnLabel = document.getElementById('runTestsTabStopActionLabel')
stopBtnLabel.innerText = 'Stopping'
const stopBtn = document.getElementById('runTestsTabStopAction')
stopBtn.setAttribute('disabled', 'disabled')
const runBtn = document.getElementById('runTestsTabRunAction')
runBtn.setAttribute('disabled', 'disabled')
}
updateRunAction (currentFile) {
const el = yo`
<button id="runTestsTabRunAction" title="Run tests" data-id="testTabRunTestsTabRunAction" class="w-50 btn btn-primary" onclick="${() => this.runTests()}">
<span class="fas fa-play ml-2"></span>
<label class="${css.labelOnBtn} btn btn-primary p-1 ml-2 m-0">Run</label>
</button>
`
const isSolidityActive = this.appManager.isActive('solidity')
if (!isSolidityActive || !this.listTests().length) {
el.setAttribute('disabled', 'disabled')
if (!currentFile || (currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')) {
el.setAttribute('title', 'No solidity file selected')
} else {
el.setAttribute('title', 'The "Solidity Plugin" should be activated')
}
}
if (!this.runActionElement) {
this.runActionElement = el
} else {
yo.update(this.runActionElement, el)
}
return this.runActionElement
}
updateStopAction () {
return yo`
<button id="runTestsTabStopAction" data-id="testTabRunTestsTabStopAction" class="w-50 pl-2 ml-2 btn btn-secondary" disabled="disabled" title="Stop running tests" onclick=${() => this.stopTests()}">
<span class="fas fa-stop ml-2"></span>
<label class="${css.labelOnBtn} btn btn-secondary p-1 ml-2 m-0" id="runTestsTabStopActionLabel">Stop</label>
</button>
`
}
updateTestFileList (tests) {
const testsMessage = (tests && tests.length ? this.listTests() : 'No test file available')
const el = yo`<div class="${css.testList} py-2 mt-0 border-bottom">${testsMessage}</div>`
if (!this.testFilesListElement) {
this.testFilesListElement = el
} else {
yo.update(this.testFilesListElement, el)
}
this.updateRunAction()
return this.testFilesListElement
}
selectAll () {
return yo`
<div class="d-flex align-items-center mx-3 pb-2 mt-2 border-bottom">
<input id="checkAllTests"
type="checkbox"
data-id="testTabCheckAllTests"
onclick="${(event) => { this.checkAll(event) }}"
checked="true"
>
<label class="text-nowrap pl-2 mb-0" for="checkAllTests"> Select all </label>
</div>
`
}
infoButton () {
return yo`
<a class="btn border text-decoration-none pr-0 d-flex w-50 ml-2" title="Check out documentation." target="__blank" href="https://remix-ide.readthedocs.io/en/latest/unittesting.html#test-directory">
<label class="btn p-1 ml-2 m-0">How to use...</label>
</a>
`
}
createResultLabel () {
if (!this.data.selectedTests) return yo`<span></span>`
const ready = this.readyTestsNumber ? `${this.readyTestsNumber}` : '0'
return yo`<span class='text-info h6'>Progress: ${ready} finished (of ${this.runningTestsNumber})</span>`
}
pathAdded (text) {
for (const option of this.uiPathList.querySelectorAll('option')) {
if (option.innerHTML === text) return true
}
return false
}
render () { render () {
this.onActivationInternal() this.onActivationInternal()
this.renderComponent() this.renderComponent()
@ -657,83 +193,4 @@ module.exports = class TestTab extends ViewPlugin {
<SolidityUnitTesting testTab={this} helper={helper} /> <SolidityUnitTesting testTab={this} helper={helper} />
, this.element) , this.element)
} }
render2 () {
this.onActivationInternal()
this.testsOutput = yo`<div class="mx-3 mb-2 pb-4 border-top border-primary" hidden='true' id="solidityUnittestsOutput" data-id="testTabSolidityUnitTestsOutput"></a>`
this.testsExecutionStopped = yo`<label class="text-warning h6" data-id="testTabTestsExecutionStopped">The test execution has been stopped</label>`
this.testsExecutionStoppedError = yo`<label class="text-danger h6" data-id="testTabTestsExecutionStoppedError">The test execution has been stopped because of error(s) in your test file</label>`
this.uiPathList = yo`<datalist id="utPathList"></datalist>`
this.inputPath = yo`<input
placeholder=${this.defaultPath}
list="utPathList"
class="${css.inputFolder} custom-select"
id="utPath"
data-id="uiPathInput"
name="utPath"
title="Press 'Enter' to change the path for test files."
style="background-image: var(--primary);"
onkeyup=${(e) => this.handleTestDirInput(e)}
onchange=${async (e) => this.handleEnter(e)}
/>`
this.createTestFolder = yo`
<button
class="btn border ml-2"
data-id="testTabGenerateTestFolder"
title="Create a test folder"
disabled=true
onclick=${(e) => this.handleCreateFolder()}
>
Create
</button>
`
const availablePaths = yo`
<div>
<div class="d-flex p-2">
${this.inputPath}
${this.createTestFolder}
${this.uiPathList}
</div>
</div>
`
this.updateDirList('/')
this.testsExecutionStopped.hidden = true
this.testsExecutionStoppedError.hidden = true
this.resultStatistics = this.createResultLabel()
this.resultStatistics.hidden = true
const el = yo`
<div class="${css.testTabView} px-2" id="testView">
<div class="${css.infoBox}">
<p class="text-lg"> Test your smart contract in Solidity.</p>
<p> Select directory to load and generate test files.</p>
<label>Test directory:</label>
${availablePaths}
</div>
<div class="${css.tests}">
<div class="d-flex p-2">
${this.updateGenerateFileAction()}
${this.infoButton()}
</div>
<div class="d-flex p-2">
${this.updateRunAction()}
${this.updateStopAction()}
</div>
${this.selectAll()}
${this.updateTestFileList()}
<div class="align-items-start flex-column mt-2 mx-3 mb-0">
${this.resultStatistics}
${this.testsExecutionStopped}
${this.testsExecutionStoppedError}
</div>
${this.testsOutput}
</div>
</div>
`
this._view.el = el
this.testTabLogic.setCurrentPath(this.defaultPath)
this.updateForNewCurrent(this.fileManager.currentFile())
return el
}
} }

@ -1,138 +0,0 @@
const helper = require('../../../lib/helper.js')
const modalDialogCustom = require('../../ui/modal-dialog-custom')
const remixPath = require('path')
class TestTabLogic {
constructor (fileManager) {
console.log('Inside TestTabLogic constructor---fileManager--->', fileManager)
this.fileManager = fileManager
this.currentPath = '/tests'
}
setCurrentPath (path) {
if (path.indexOf('/') === 0) return
this.currentPath = helper.removeMultipleSlashes(helper.removeTrailingSlashes(path))
}
generateTestFolder (path) {
// Todo move this check to File Manager after refactoring
// Checking to ignore the value which contains only whitespaces
if (!path || !(/\S/.test(path))) return
path = helper.removeMultipleSlashes(path)
const fileProvider = this.fileManager.fileProviderOf(path.split('/')[0])
fileProvider.exists(path).then(res => {
if (!res) fileProvider.createDir(path)
})
}
async pathExists (path) {
// Checking to ignore the value which contains only whitespaces
if (!path || !(/\S/.test(path))) return
const fileProvider = this.fileManager.fileProviderOf(path.split('/')[0])
const res = await fileProvider.exists(path, (e, res) => { return res })
return res
}
generateTestFile () {
console.log('Inside generateTestFile-1-')
console.log('Inside generateTestFile---fileManager--->', this.fileManager)
let fileName = this.fileManager.currentFile()
const hasCurrent = !!fileName && this.fileManager.currentFile().split('.').pop().toLowerCase() === 'sol'
if (!hasCurrent) fileName = this.currentPath + '/newFile.sol'
const fileProvider = this.fileManager.fileProviderOf(this.currentPath)
if (!fileProvider) return
const splittedFileName = fileName.split('/')
const fileNameToImport = (!hasCurrent) ? fileName : this.currentPath + '/' + splittedFileName[splittedFileName.length - 1]
helper.createNonClashingNameWithPrefix(fileNameToImport, fileProvider, '_test', (error, newFile) => {
if (error) return modalDialogCustom.alert('Failed to create file. ' + newFile + ' ' + error)
if (!fileProvider.set(newFile, this.generateTestContractSample(hasCurrent, fileName))) return modalDialogCustom.alert('Failed to create test file ' + newFile)
this.fileManager.open(newFile)
this.fileManager.syncEditor(newFile)
})
}
dirList (path) {
return this.fileManager.dirList(path)
}
isRemixDActive () {
return this.fileManager.isRemixDActive()
}
async getTests (cb) {
if (!this.currentPath) return cb(null, [])
const provider = this.fileManager.fileProviderOf(this.currentPath)
if (!provider) return cb(null, [])
const tests = []
let files = []
try {
if (await this.fileManager.exists(this.currentPath)) files = await this.fileManager.readdir(this.currentPath)
} catch (e) {
cb(e.message)
}
for (var file in files) {
const filepath = provider && provider.type ? provider.type + '/' + file : file
if (/.(_test.sol)$/.exec(file)) tests.push(filepath)
}
cb(null, tests, this.currentPath)
}
// @todo(#2758): If currently selected file is compiled and compilation result is available,
// 'contractName' should be <compiledContractName> + '_testSuite'
generateTestContractSample (hasCurrent, fileToImport, contractName = 'testSuite') {
let relative = remixPath.relative(this.currentPath, remixPath.dirname(fileToImport))
if (relative === '') relative = '.'
const comment = hasCurrent ? `import "${relative}/${remixPath.basename(fileToImport)}";` : '// <import file to test>'
return `// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;
// This import is automatically injected by Remix
import "remix_tests.sol";
// This import is required to use custom transaction context
// Although it may fail compilation in 'Solidity Compiler' plugin
// But it will work fine in 'Solidity Unit Testing' plugin
import "remix_accounts.sol";
${comment}
// File name has to end with '_test.sol', this file can contain more than one testSuite contracts
contract ${contractName} {
/// 'beforeAll' runs before all other tests
/// More special functions are: 'beforeEach', 'beforeAll', 'afterEach' & 'afterAll'
function beforeAll() public {
// <instantiate contract>
Assert.equal(uint(1), uint(1), "1 should be equal to 1");
}
function checkSuccess() public {
// Use 'Assert' methods: https://remix-ide.readthedocs.io/en/latest/assert_library.html
Assert.ok(2 == 2, 'should be true');
Assert.greaterThan(uint(2), uint(1), "2 should be greater than to 1");
Assert.lesserThan(uint(2), uint(3), "2 should be lesser than to 3");
}
function checkSuccess2() public pure returns (bool) {
// Use the return value (true or false) to test the contract
return true;
}
function checkFailure() public {
Assert.notEqual(uint(1), uint(1), "1 should not be equal to 1");
}
/// Custom Transaction Context: https://remix-ide.readthedocs.io/en/latest/unittesting.html#customization
/// #sender: account-1
/// #value: 100
function checkSenderAndValue() public payable {
// account index varies 0-9, value is in wei
Assert.equal(msg.sender, TestsAccounts.getAccount(1), "Invalid sender");
Assert.equal(msg.value, 100, "Invalid value");
}
}
`
}
}
module.exports = TestTabLogic

@ -49,7 +49,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => {
let areTestsRunning = false let areTestsRunning = false
let isDebugging = false let isDebugging = false
let allTests: any = [] let allTests: any = []
let currentErrors: any let currentErrors: any = []
let runningTestFileName: any let runningTestFileName: any
const filesContent: any = {} const filesContent: any = {}
@ -78,8 +78,8 @@ export const SolidityUnitTesting = (props: Record<string, any>) => {
if (errFiles.includes(file)) return if (errFiles.includes(file)) return
} else if (currentErrors.sourceLocation && currentErrors.sourceLocation.file && currentErrors.sourceLocation.file === file) return } else if (currentErrors.sourceLocation && currentErrors.sourceLocation.file && currentErrors.sourceLocation.file === file) return
} }
// // if current file is changed while debugging and one of the files imported in test file are opened // 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 // do not clear the test results in SUT plugin
if (isDebugging && testTab.allFilesInvolved.includes(file)) return if (isDebugging && testTab.allFilesInvolved.includes(file)) return
allTests = [] allTests = []
updateTestFileList() updateTestFileList()

Loading…
Cancel
Save