Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding custom path to Unit Testing #2906

Merged
merged 17 commits into from
Jul 2, 2020
5 changes: 3 additions & 2 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile())

// ----------------- fileManager servive ----------------------------
const fileManager = new FileManager(editor)
const fileManager = new FileManager(editor, appManager)
registry.put({api: fileManager, name: 'filemanager'})

const blockchain = new Blockchain(registry.get('config').api)
Expand Down Expand Up @@ -396,7 +396,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org

await appManager.activatePlugin(['contentImport', 'theme', 'editor', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter'])
await appManager.activatePlugin(['mainPanel', 'menuicons'])
await appManager.activatePlugin(['home', 'sidePanel', 'hiddenPanel', 'pluginManager', 'fileExplorers', 'settings', 'contextualListener', 'scriptRunner', 'terminal', 'fetchAndCompile'])
await appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await appManager.activatePlugin(['home', 'hiddenPanel', 'pluginManager', 'fileExplorers', 'settings', 'contextualListener', 'scriptRunner', 'terminal', 'fetchAndCompile'])

const queryParams = new QueryParams()
const params = queryParams.get()
Expand Down
4 changes: 2 additions & 2 deletions src/app/editor/example-contracts.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ contract Ballot {

var ballotTest = `pragma solidity >=0.4.22 <0.7.0;
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "./3_Ballot.sol";
import "../3_Ballot.sol";

contract BallotTest {

Expand Down Expand Up @@ -255,6 +255,6 @@ module.exports = {
storage: { name: '1_Storage.sol', content: storage },
owner: { name: '2_Owner.sol', content: owner },
ballot: { name: '3_Ballot.sol', content: ballot },
ballot_test: { name: '4_Ballot_test.sol', content: ballotTest },
ballot_test: { name: 'tests/4_Ballot_test.sol', content: ballotTest },
basic: { name: 'basic.sol', content: basic }
}
5 changes: 3 additions & 2 deletions src/app/files/file-explorer.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ function fileExplorer (localRegistry, files, menuItems) {
self.ensureRoot(() => {
const folderpath = filepath.split('/').slice(0, -1).join('/')
const currentTree = self.treeView.nodeAt(folderpath)

if (currentTree && self.treeView.isExpanded(folderpath)) {
if (!self.treeView.isExpanded(folderpath)) self.treeView.expand(folderpath)
if (currentTree) {
self.files.resolveDirectory(folderpath, (error, fileTree) => {
if (error) console.error(error)
if (!fileTree) return
Expand Down Expand Up @@ -136,6 +136,7 @@ function fileExplorer (localRegistry, files, menuItems) {
if (!fileTree) return
fileTree = normalize(folderpath, fileTree)
self.treeView.updateNodeFromJSON(folderpath, fileTree, true)
if (!self.treeView.isExpanded(folderpath)) self.treeView.expand(folderpath)
})
})
}
Expand Down
29 changes: 28 additions & 1 deletion src/app/files/fileManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,15 @@ const createError = (err) => {
}

class FileManager extends Plugin {
constructor (editor) {
constructor (editor, appManager) {
super(profile)
this.openedFiles = {} // list all opened files
this.events = new EventEmitter()
this.editor = editor
this._components = {}
this._components.compilerImport = new CompilerImport()
this._components.registry = globalRegistry
this.appManager = appManager
this.init()
}

Expand Down Expand Up @@ -493,6 +494,32 @@ class FileManager extends Plugin {
return this._deps.filesProviders['browser']
}

// returns the list of directories inside path
dirList (path) {
const dirPaths = []
const collectList = (path) => {
return new Promise((resolve, reject) => {
this.readdir(path).then((ls) => {
const promises = Object.keys(ls).map((item, index) => {
const root = (path.indexOf('/') === -1) ? path : path.substr(0, path.indexOf('/'))
const curPath = `${root}/${item}` // adding 'browser' or 'localhost'
if (ls[item].isDirectory && !dirPaths.includes(curPath)) {
dirPaths.push(curPath)
resolve(dirPaths)
}
return new Promise((resolve, reject) => { resolve() })
})
Promise.all(promises).then(() => { resolve(dirPaths) })
})
})
}
return collectList(path)
}

isRemixDActive () {
return this.appManager.isActive('remixd')
}

saveCurrentFile () {
var currentFile = this._deps.config.get('currentFile')
if (currentFile && this.editor.current()) {
Expand Down
8 changes: 4 additions & 4 deletions src/app/files/fileProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ class FileProvider {
return true
}
if (!exists && unprefixedpath.indexOf('/') !== -1) {
this.createDir(path)
// the last element is the filename and we should remove it
this.createDir(path.substr(0, path.lastIndexOf('/')))
}
try {
window.remixFileSystem.writeFileSync(unprefixedpath, content)
Expand All @@ -116,9 +117,8 @@ class FileProvider {
}

createDir (path, cb) {
var unprefixedpath = this.removePrefix(path)
const unprefixedpath = this.removePrefix(path)
const paths = unprefixedpath.split('/')
paths.pop() // last element should the filename
if (paths.length && paths[0] === '') paths.shift()
let currentCheck = ''
paths.forEach((value) => {
Expand Down Expand Up @@ -226,7 +226,7 @@ class FileProvider {
if (files) {
files.forEach(element => {
const absPath = (path === '/' ? '' : path) + '/' + element
ret[absPath.indexOf('/') === 0 ? absPath.replace('/', '') : absPath] = { isDirectory: window.remixFileSystem.statSync(absPath).isDirectory() }
ret[absPath.indexOf('/') === 0 ? absPath.substr(1, absPath.length) : absPath] = { isDirectory: window.remixFileSystem.statSync(absPath).isDirectory() }
// ^ ret does not accept path starting with '/'
})
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/tabs/settings-tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ module.exports = class SettingsTab extends ViewPlugin {

// Gist settings
const token = this.config.get('settings/gist-access-token')
const gistAccessToken = yo`<input id="gistaccesstoken" data-id="settingsTabGistAccessToken" type="password" class="border form-control-sm mb-2 ${css.inline}" placeholder="Token">`
const gistAccessToken = yo`<input id="gistaccesstoken" data-id="settingsTabGistAccessToken" type="password" class="form-control border form-control-sm mb-2 ${css.inline}" placeholder="Token">`
if (token) gistAccessToken.value = token
const removeToken = () => { self.config.set('settings/gist-access-token', ''); gistAccessToken.value = ''; tooltip('Access token removed') }
const saveToken = () => { this.config.set('settings/gist-access-token', gistAccessToken.value); tooltip('Access token saved') }
Expand Down
83 changes: 59 additions & 24 deletions src/app/tabs/test-tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ module.exports = class TestTab extends ViewPlugin {
this.runningTestsNumber = 0
this.readyTestsNumber = 0
this.areTestsRunning = false
this.defaultPath = 'browser/tests'

appManager.event.on('activate', (name) => {
if (name === 'solidity') this.updateRunAction(fileManager.currentFile())
if (name === 'solidity') this.updateRunAction()
})
appManager.event.on('deactivate', (name) => {
if (name === 'solidity') this.updateRunAction(fileManager.currentFile())
if (name === 'solidity') this.updateRunAction()
})
}

Expand All @@ -56,9 +58,6 @@ module.exports = class TestTab extends ViewPlugin {
})

this.fileManager.events.on('noFileSelected', () => {
this.updateGenerateFileAction()
this.updateRunAction()
this.updateTestFileList()
})

this.fileManager.events.on('currentFileChanged', (file, provider) => this.updateForNewCurrent(file))
Expand Down Expand Up @@ -86,6 +85,7 @@ module.exports = class TestTab extends ViewPlugin {
}

listTests () {
if (!this.data.allTests) return []
return this.data.allTests.map(
testFile => this.createSingleTest(testFile)
)
Expand Down Expand Up @@ -351,6 +351,13 @@ module.exports = class TestTab extends ViewPlugin {
})
}

updateCurrentPath (e) {
const newValue = e.target.value === '' ? this.defaultPath : e.target.value
this.testTabLogic.setCurrentPath(newValue)
this.updateRunAction()
this.updateForNewCurrent()
}

runTests () {
this.areTestsRunning = true
this.hasBeenStopped = false
Expand Down Expand Up @@ -386,21 +393,16 @@ module.exports = class TestTab extends ViewPlugin {
}

updateGenerateFileAction (currentFile) {
let el = yo`<button
class="btn border w-50"
data-id="testTabGenerateTestFile"
title="Generate sample test file."
onclick="${this.testTabLogic.generateTestFile.bind(this.testTabLogic)}"
>
Generate
</button>`
if (
!currentFile ||
(currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')
) {
el.setAttribute('disabled', 'disabled')
el.setAttribute('title', 'No solidity file selected')
}
let el = yo`
<button
class="btn border w-50"
data-id="testTabGenerateTestFile"
title="Generate sample test file."
onclick="${this.testTabLogic.generateTestFile.bind(this.testTabLogic)}"
>
Generate
</button>
`
if (!this.generateFileActionElement) {
this.generateFileActionElement = el
} else {
Expand All @@ -411,13 +413,13 @@ module.exports = class TestTab extends ViewPlugin {

updateRunAction (currentFile) {
let el = yo`
<button id="runTestsTabRunAction" title="Run tests" data-id="testTabRunTestsTabRunAction" class="w-50 btn btn-primary" onclick="${() => this.runTests()}">
<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.actives.includes('solidity')
if (!currentFile || !isSolidityActive || (currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')) {
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')
Expand Down Expand Up @@ -451,6 +453,7 @@ module.exports = class TestTab extends ViewPlugin {
} else {
yo.update(this.testFilesListElement, el)
}
this.updateRunAction()
return this.testFilesListElement
}

Expand Down Expand Up @@ -482,11 +485,40 @@ module.exports = class TestTab extends ViewPlugin {
return yo`<span class='text-info h6'>Progress: ${ready} finished (of ${this.runningTestsNumber})</span>`
}

updateDirList () {
for (var o of this.uiPathList.querySelectorAll('option')) o.remove()
this.uiPathList.appendChild(yo`<option>browser</option>`)
if (this.testTabLogic.isRemixDActive()) this.uiPathList.appendChild(yo`<option>localhost</option>`)
if (!this._view.el) return
this.testTabLogic.dirList(this.inputPath.value).then((options) => {
options.forEach((path) => this.uiPathList.appendChild(yo`<option>${path}</option>`))
})
}

render () {
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="custom-select"
id="utPath"
data-id="uiPathInput"
name="utPath"
style="background-image: var(--primary);"
onkeydown=${(e) => { if (e.keyCode === 191) this.updateDirList() }}
onchange=${(e) => this.updateCurrentPath(e)}/>`

const availablePaths = yo`
<div>
${this.inputPath}
${this.uiPathList}
</div>
`
this.updateDirList()
this.testsExecutionStopped.hidden = true
this.testsExecutionStoppedError.hidden = true
this.resultStatistics = this.createResultLabel()
Expand All @@ -495,7 +527,9 @@ module.exports = class TestTab extends ViewPlugin {
<div class="${css.testTabView} px-2" id="testView">
<div class="${css.infoBox}">
<p class="text-lg"> Test your smart contract in Solidity.</p>
<p> Click on "Generate" to generate a sample test file.</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">
Expand All @@ -518,6 +552,7 @@ module.exports = class TestTab extends ViewPlugin {
</div>
`
this._view.el = el
this.testTabLogic.setCurrentPath(this.defaultPath)
this.updateForNewCurrent(this.fileManager.currentFile())
return el
}
Expand Down
Loading