Skip to content

Commit

Permalink
Merge pull request #2906 from ethereum/utworkshop
Browse files Browse the repository at this point in the history
adding custom path to Unit Testing
  • Loading branch information
yann300 authored Jul 2, 2020
2 parents b451f09 + a0083b1 commit a255bc8
Show file tree
Hide file tree
Showing 13 changed files with 178 additions and 85 deletions.
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

0 comments on commit a255bc8

Please sign in to comment.