Merge pull request #1702 from ethereum/refactor_tabs_2

refactor/simplify code in the tabs
pull/3094/head
yann300 6 years ago committed by GitHub
commit dceb5c8652
  1. 24
      src/app.js
  2. 96
      src/app/debugger/debuggerUI.js
  3. 4
      src/app/files/fileManager.js
  4. 3
      src/app/tabs/analysis-tab.js
  5. 41
      src/app/tabs/compile-tab.js
  6. 208
      src/app/tabs/settings-tab.js
  7. 65
      src/app/tabs/styles/settings-tab-styles.js
  8. 36
      src/app/tabs/styles/tabbed-menu-styles.js
  9. 90
      src/app/tabs/support-tab.js
  10. 90
      src/app/tabs/tabbed-menu.js
  11. 336
      src/app/tabs/test-tab.js
  12. 85
      src/app/tabs/testTab/testTab.js

@ -46,7 +46,6 @@ const CompileTab = require('./app/tabs/compile-tab')
const SettingsTab = require('./app/tabs/settings-tab')
const AnalysisTab = require('./app/tabs/analysis-tab')
const DebuggerTab = require('./app/tabs/debugger-tab')
// const SupportTab = require('./app/tabs/support-tab')
const TestTab = require('./app/tabs/test-tab')
const RunTab = require('./app/tabs/run-tab')
const FilePanel = require('./app/panels/file-panel')
@ -432,7 +431,15 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
let filePanel = new FilePanel()
registry.put({api: filePanel, name: 'filepanel'})
let compileTab = new CompileTab(registry)
let compileTab = new CompileTab(
registry.get('editor').api,
registry.get('config').api,
registry.get('renderer').api,
registry.get('fileproviders/swarm').api,
registry.get('filemanager').api,
registry.get('fileproviders').api,
registry.get('pluginmanager').api
)
let run = new RunTab(
registry.get('udapp').api,
registry.get('udappUI').api,
@ -444,12 +451,19 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
registry.get('pluginmanager').api,
registry.get('compilersartefacts').api
)
let settings = new SettingsTab(self._components.registry)
let settings = new SettingsTab(
registry.get('config').api,
registry.get('editor').api,
appManager
)
let analysis = new AnalysisTab(registry)
let debug = new DebuggerTab()
const landingPage = new LandingPage(appManager, appStore)
// let support = new SupportTab()
let test = new TestTab(self._components.registry, compileTab)
let test = new TestTab(
registry.get('filemanager').api,
registry.get('filepanel').api,
compileTab
)
let sourceHighlighters = registry.get('editor').api.sourceHighlighters
let configProvider = self._components.filesProviders['config']

@ -12,8 +12,6 @@ var executionContext = require('../../execution-context')
var globalRegistry = require('../../global/registry')
var remixLib = require('remix-lib')
var Web3Providers = remixLib.vm.Web3Providers
var DummyProvider = remixLib.vm.DummyProvider
var init = remixLib.init
@ -30,72 +28,12 @@ var css = csjs`
}
`
class ContextManager {
constructor () {
this.executionContext = executionContext
this.web3 = this.executionContext.web3()
this.event = new EventManager()
}
initProviders () {
this.web3Providers = new Web3Providers()
this.addProvider('DUMMYWEB3', new DummyProvider())
this.switchProvider('DUMMYWEB3')
this.addProvider('vm', this.executionContext.vm())
this.addProvider('injected', this.executionContext.internalWeb3())
this.addProvider('web3', this.executionContext.internalWeb3())
this.switchProvider(this.executionContext.getProvider())
}
getWeb3 () {
return this.web3
}
addProvider (type, obj) {
this.web3Providers.addProvider(type, obj)
this.event.trigger('providerAdded', [type])
}
switchProvider (type) {
var self = this
this.web3Providers.get(type, function (error, obj) {
if (error) {
console.log('provider ' + type + ' not defined')
} else {
self.web3 = obj
self.executionContext.detectNetwork((error, network) => {
if (error || !network) {
self.web3 = obj
} else {
var webDebugNode = init.web3DebugNode(network.name)
self.web3 = (!webDebugNode ? obj : webDebugNode)
}
self.event.trigger('providerChanged', [type, self.web3])
})
self.event.trigger('providerChanged', [type, self.web3])
}
})
}
}
class DebuggerUI {
constructor (container) {
this.registry = globalRegistry
this.event = new EventManager()
this.executionContext = executionContext
this.contextManager = new ContextManager()
this.contextManager.initProviders()
this.contextManager.event.register('providerChanged', () => {
if (this.debugger) this.debugger.updateWeb3(this.contextManager.getWeb3())
})
this.isActive = false
this.sourceHighlighter = new SourceHighlighter()
@ -173,19 +111,29 @@ class DebuggerUI {
if (compilers['__last']) lastCompilationResult = compilers['__last']
// TODO debugging with source highlight is disabled. see line 98
this.debugger = new Debugger({
web3: this.contextManager.getWeb3(),
offsetToLineColumnConverter: this.registry.get('offsettolinecolumnconverter').api,
compiler: { lastCompilationResult }
})
this.listenToEvents()
this.debugger.debugger.updateWeb3(this.executionContext.web3())
this.debugger.debug(blockNumber, txNumber, tx, () => {
self.stepManager = new StepManagerUI(this.debugger.step_manager)
self.vmDebugger = new VmDebugger(this.debugger.vmDebuggerLogic)
self.renderDebugger()
executionContext.detectNetwork((error, network) => {
let web3
if (error || !network) {
web3 = init.web3DebugNode(executionContext.web3())
} else {
var webDebugNode = init.web3DebugNode(network.name)
web3 = (!webDebugNode ? executionContext.web3() : webDebugNode)
}
init.extendWeb3(web3)
this.debugger = new Debugger({
web3,
offsetToLineColumnConverter: this.registry.get('offsettolinecolumnconverter').api,
compiler: { lastCompilationResult }
})
this.listenToEvents()
this.debugger.debug(blockNumber, txNumber, tx, () => {
self.stepManager = new StepManagerUI(this.debugger.step_manager)
self.vmDebugger = new VmDebugger(this.debugger.vmDebuggerLogic)
self.renderDebugger()
})
})
}

@ -198,10 +198,10 @@ class FileManager extends ApiFactory {
}
getFilesFromPath (path) {
const provider = this.fileProviderOf(path)
if (!provider) throw new Error(`provider for path ${path} not found`)
// TODO : Change provider with promise
return new Promise((resolve, reject) => {
const provider = this.fileProviderOf(path)
if (!provider) return reject(`provider for path ${path} not found`)
provider.resolveDirectory(path, (error, filesTree) => {
if (error) reject(error)
resolve(filesTree)

@ -12,6 +12,7 @@ class AnalysisTab extends ApiFactory {
this.event = new EventManager()
this.registry = registry
}
get profile () {
return {
name: 'solidityStaticAnalysis',
@ -23,6 +24,7 @@ class AnalysisTab extends ApiFactory {
kind: 'analysis'
}
}
render () {
var staticanalysis = new StaticAnalysis()
this.registry.put({api: staticanalysis, name: 'staticanalysis'})
@ -31,6 +33,7 @@ class AnalysisTab extends ApiFactory {
this.el = yo`<div class="${css.analysisTabView}" id="staticanalysisView">${staticanalysis.render()}</div>`
return this.el
}
}
module.exports = AnalysisTab

@ -21,7 +21,7 @@ import { ApiFactory } from 'remix-plugin'
class CompileTab extends ApiFactory {
constructor (registry) {
constructor (editor, config, renderer, swarmfileProvider, fileManager, fileProviders, pluginManager) {
super()
this.events = new EventEmitter()
this._view = {
@ -34,27 +34,26 @@ class CompileTab extends ApiFactory {
this.queryParams = new QueryParams()
// dependencies
this._deps = {
editor: registry.get('editor').api,
config: registry.get('config').api,
renderer: registry.get('renderer').api,
swarmfileProvider: registry.get('fileproviders/swarm').api,
fileManager: registry.get('filemanager').api,
fileProviders: registry.get('fileproviders').api,
pluginManager: registry.get('pluginmanager').api
}
this.editor = editor
this.config = config
this.renderer = renderer
this.swarmfileProvider = swarmfileProvider
this.fileManager = fileManager
this.fileProviders = fileProviders
this.pluginManager = pluginManager
this.data = {
contractsDetails: {}
}
this.compileTabLogic = new CompileTabLogic(this.queryParams, this._deps.fileManager, this._deps.editor, this._deps.config, this._deps.fileProviders)
this.compileTabLogic = new CompileTabLogic(this.queryParams, this.fileManager, this.editor, this.config, this.fileProviders)
this.compiler = this.compileTabLogic.compiler
this.compileTabLogic.init()
this.compilerContainer = new CompilerContainer(
this.compileTabLogic,
this._deps.editor,
this._deps.config,
this.editor,
this.config,
this.queryParams
)
}
@ -82,7 +81,7 @@ class CompileTab extends ApiFactory {
}
})
this._deps.fileManager.events.on('currentFileChanged', (name) => {
this.fileManager.events.on('currentFileChanged', (name) => {
this.compilerContainer.currentFile = name
})
this.compiler.event.register('compilationFinished', (success, data, source) => {
@ -106,7 +105,7 @@ class CompileTab extends ApiFactory {
yo.update(this._view.contractSelection, contractSelection)
if (data['error']) {
this._deps.renderer.error(data['error'].formattedMessage, this._view.errorContainer, {type: data['error'].severity || 'error'})
this.renderer.error(data['error'].formattedMessage, this._view.errorContainer, {type: data['error'].severity || 'error'})
if (data['error'].mode === 'panic') {
return modalDialogCustom.alert(yo`<div><i class="fa fa-exclamation-circle ${css.panicError}" aria-hidden="true"></i>
The compiler returned with the following internal error: <br> <b>${data['error'].formattedMessage}.<br>
@ -117,12 +116,12 @@ class CompileTab extends ApiFactory {
}
if (data.errors && data.errors.length) {
data.errors.forEach((err) => {
if (this._deps.config.get('hideWarnings')) {
if (this.config.get('hideWarnings')) {
if (err.severity !== 'warning') {
this._deps.renderer.error(err.formattedMessage, this._view.errorContainer, {type: err.severity})
this.renderer.error(err.formattedMessage, this._view.errorContainer, {type: err.severity})
}
} else {
this._deps.renderer.error(err.formattedMessage, this._view.errorContainer, {type: err.severity})
this.renderer.error(err.formattedMessage, this._view.errorContainer, {type: err.severity})
}
})
}
@ -215,7 +214,7 @@ class CompileTab extends ApiFactory {
if (contract.metadata === undefined || contract.metadata.length === 0) {
modalDialogCustom.alert('This contract may be abstract, may not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.')
} else {
publishOnSwarm(contract, this._deps.fileManager, function (err, uploaded) {
publishOnSwarm(contract, this.fileManager, function (err, uploaded) {
if (err) {
try {
err = JSON.stringify(err)
@ -229,7 +228,7 @@ class CompileTab extends ApiFactory {
modalDialogCustom.alert(yo`<span>Metadata published successfully.<br> <pre>${result}</pre> </span>`)
}
}, (item) => { // triggered each time there's a new verified publish (means hash correspond)
this._deps.swarmfileProvider.addReadOnly(item.hash, item.content)
this.swarmfileProvider.addReadOnly(item.hash, item.content)
})
}
}
@ -339,7 +338,7 @@ class CompileTab extends ApiFactory {
this._view.errorContainer = yo`<div></div>`
this._view.contractSelection = this.contractSelection()
this._view.compilerContainer = this.compilerContainer.render()
const currentFile = this._deps.fileManager.currentFile()
const currentFile = this.fileManager.currentFile()
if (currentFile) this.compilerContainer.currentFile = currentFile
this._view.el = yo`

@ -1,45 +1,38 @@
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var remixLib = require('remix-lib')
var globalRegistry = require('../../global/registry')
var tooltip = require('../ui/tooltip')
var copyToClipboard = require('../ui/copy-to-clipboard')
var styleGuide = require('../ui/styles-guide/theme-chooser')
var Storage = remixLib.Storage
var EventManager = require('../../lib/events')
var css = require('./styles/settings-tab-styles')
import { ApiFactory } from 'remix-plugin'
module.exports = class SettingsTab extends ApiFactory {
constructor (localRegistry) {
constructor (config, editor, appManager) {
super()
const self = this
self._components = {}
self._components.registry = localRegistry || globalRegistry
// dependencies
self._deps = {
config: self._components.registry.get('config').api,
editorPanel: self._components.registry.get('editorpanel').api,
editor: self._components.registry.get('editor').api,
appManager: self._components.registry.get('appmanager').api
}
self._view = { /* eslint-disable */
this.config = config
this.editor = editor
this.appManager = appManager
this._components = {}
this._view = { /* eslint-disable */
el: null,
optionVM: null, personal: null, warnPersonalMode: null, generateContractMetadata: null,
pluginInput: null, versionSelector: null, version: null,
theme: { dark: null, light: null, clean: null },
plugins: {},
config: {
general: null, themes: null,
plugin: null
general: null, themes: null
}
} /* eslint-enable */
self.data = {}
self.event = new EventManager()
self._components.themeStorage = new Storage('style:')
self.data.currentTheme = self._components.themeStorage.get('theme') || 'light'
this.event = new EventManager()
this.initTheme()
}
initTheme () {
const themeStorage = new Storage('style:')
this.currentTheme = themeStorage.get('theme') || 'light'
}
get profile () {
return {
displayName: 'settings',
@ -59,109 +52,109 @@ module.exports = class SettingsTab extends ApiFactory {
// Gist settings
var gistAccessToken = yo`<input id="gistaccesstoken" type="password" class="form-control mb-2 ${css.inline}" placeholder="Token">`
var token = self._deps.config.get('settings/gist-access-token')
var token = this.config.get('settings/gist-access-token')
if (token) gistAccessToken.value = token
var gistAddToken = yo`<input class="${css.savegisttoken} btn btn-sm btn-primary" id="savegisttoken" onclick=${() => { self._deps.config.set('settings/gist-access-token', gistAccessToken.value); tooltip('Access token saved') }} value="Save" type="button">`
var gistRemoveToken = yo`<input class="btn btn-sm btn-primary" id="removegisttoken" onclick=${() => { gistAccessToken.value = ''; self._deps.config.set('settings/gist-access-token', ''); tooltip('Access token removed') }} value="Remove" type="button">`
self._view.gistToken = yo`<div class="${css.checkboxText}">${gistAccessToken}${copyToClipboard(() => self._deps.config.get('settings/gist-access-token'))}${gistAddToken}${gistRemoveToken}</div>`
var gistAddToken = yo`<input class="${css.savegisttoken} btn btn-sm btn-primary" id="savegisttoken" onclick=${() => { this.config.set('settings/gist-access-token', gistAccessToken.value); tooltip('Access token saved') }} value="Save" type="button">`
var gistRemoveToken = yo`<input class="btn btn-sm btn-primary" id="removegisttoken" onclick=${() => { gistAccessToken.value = ''; this.config.set('settings/gist-access-token', ''); tooltip('Access token removed') }} value="Remove" type="button">`
this._view.gistToken = yo`<div class="${css.checkboxText}">${gistAccessToken}${copyToClipboard(() => this.config.get('settings/gist-access-token'))}${gistAddToken}${gistRemoveToken}</div>`
//
self._view.optionVM = yo`<input onchange=${onchangeOption} class="align-middle form-check-input" id="alwaysUseVM" type="checkbox">`
if (self._deps.config.get('settings/always-use-vm')) self._view.optionVM.setAttribute('checked', '')
self._view.personal = yo`<input onchange=${onchangePersonal} id="personal" type="checkbox" class="align-middle form-check-input">`
if (self._deps.config.get('settings/personal-mode')) self._view.personal.setAttribute('checked', '')
this._view.optionVM = yo`<input onchange=${onchangeOption} class="align-middle form-check-input" id="alwaysUseVM" type="checkbox">`
if (this.config.get('settings/always-use-vm')) this._view.optionVM.setAttribute('checked', '')
this._view.personal = yo`<input onchange=${onchangePersonal} id="personal" type="checkbox" class="align-middle form-check-input">`
if (this.config.get('settings/personal-mode')) this._view.personal.setAttribute('checked', '')
var warnText = `Transaction sent over Web3 will use the web3.personal API - be sure the endpoint is opened before enabling it.
This mode allows to provide the passphrase in the Remix interface without having to unlock the account.
Although this is very convenient, you should completely trust the backend you are connected to (Geth, Parity, ...).
It is not recommended (and also most likely not relevant) to use this mode with an injected provider (Mist, Metamask, ...) or with JavaScript VM.
Remix never persist any passphrase.`.split('\n').map(s => s.trim()).join(' ')
self._view.warnPersonalMode = yo`<i title=${warnText} class="${css.icon} fa fa-exclamation-triangle text-warning" aria-hidden="true"></i>`
self._view.generateContractMetadata = yo`<input onchange=${onchangeGenerateContractMetadata} id="generatecontractmetadata" type="checkbox" class="form-check-input">`
this._view.warnPersonalMode = yo`<i title=${warnText} class="${css.icon} fa fa-exclamation-triangle text-warning" aria-hidden="true"></i>`
this._view.generateContractMetadata = yo`<input onchange=${onchangeGenerateContractMetadata} id="generatecontractmetadata" type="checkbox" class="form-check-input">`
if (self._deps.config.get('settings/generate-contract-metadata')) self._view.generateContractMetadata.setAttribute('checked', '')
if (this.config.get('settings/generate-contract-metadata')) this._view.generateContractMetadata.setAttribute('checked', '')
self._view.pluginInput = yo`<textarea rows="4" cols="70" id="plugininput" type="text" class="${css.pluginTextArea}" ></textarea>`
this._view.pluginInput = yo`<textarea rows="4" cols="70" id="plugininput" type="text" class="${css.pluginTextArea}" ></textarea>`
self._view.theme.light = yo`<input onchange=${onswitch2lightTheme} class="align-middle form-check-input" name="theme" id="themeLight" type="radio">`
self._view.theme.dark = yo`<input onchange=${onswitch2darkTheme} class="align-middle form-check-input" name="theme" id="themeDark" type="radio">`
self._view.theme.clean = yo`<input onchange=${onswitch2cleanTheme} class="align-middle form-check-input" name="theme" id="themeClean" type="radio">`
self._view.theme[self.data.currentTheme].setAttribute('checked', 'checked')
this._view.theme.light = yo`<input onchange=${onswitch2lightTheme} class="align-middle form-check-input" name="theme" id="themeLight" type="radio">`
this._view.theme.dark = yo`<input onchange=${onswitch2darkTheme} class="align-middle form-check-input" name="theme" id="themeDark" type="radio">`
this._view.theme.clean = yo`<input onchange=${onswitch2cleanTheme} class="align-middle form-check-input" name="theme" id="themeClean" type="radio">`
this._view.theme[this.currentTheme].setAttribute('checked', 'checked')
self._view.config.homePage = yo`
this._view.config.homePage = yo`
<div class="${css.info} card">
<div class="card-body">
<h6 class="${css.title} card-title">Home</h6>
<div class="btn-group">
<button class="btn btn-primary sm-1" onclick="${() => { this._deps.appManager.ensureActivated('home') }}" >Home</button>
<button class="btn btn-primary sm-1" onclick="${() => { this.appManager.ensureActivated('home') }}" >Home</button>
<button class="btn btn-primary sm-1" onclick="${() => { window.open('https://gitter.im/ethereum/remix') }}">Gitter Channel</button>
</div>
</div>
</div>`
self._view.config.general = yo`
this._view.config.general = yo`
<div class="${css.info} card">
<div class="card-body">
<h6 class="${css.title} card-title">General settings</h6>
<div class="form-check ${css.frow}">
<div>${self._view.generateContractMetadata}</div>
<div>${this._view.generateContractMetadata}</div>
<label class="form-check-label align-middle" for="generatecontractmetadata">Generate contract metadata. Generate a JSON file in the contract folder. Allows to specify library addresses the contract depends on. If nothing is specified, Remix deploys libraries automatically.</label>
</div>
<div class="form-check ${css.frow}">
<div>${self._view.optionVM}</div>
<div>${this._view.optionVM}</div>
<label class="form-check-label align-middle" for="alwaysUseVM">Always use Ethereum VM at Load</label>
</div>
<div class="form-check ${css.frow}">
<div><input id="editorWrap" class="form-check-input align-middle" type="checkbox" onchange=${function () { self._deps.editor.resize(this.checked) }}></div>
<div><input id="editorWrap" class="form-check-input align-middle" type="checkbox" onchange=${function () { this.editor.resize(this.checked) }}></div>
<label class="form-check-label align-middle" for="editorWrap">Text Wrap</label>
</div>
<div class="form-check ${css.frow}">
<div>${self._view.personal}></div>
<label class="form-check-label align-middle" for="personal">Enable Personal Mode ${self._view.warnPersonalMode}></label>
<div>${this._view.personal}></div>
<label class="form-check-label align-middle" for="personal">Enable Personal Mode ${this._view.warnPersonalMode}></label>
</div>
</div>
</div>
`
self._view.gistToken = yo`
this._view.gistToken = yo`
<div class="${css.info} card">
<div class="card-body">
<h6 class="${css.title} card-title">Gist Access Token</h6>
<p class="">Manage the access token used to publish to Gist and retrieve Github contents.</p>
<p class="">Go to github token page (link below) to create a new token and save it in Remix. Make sure this token has only 'create gist' permission.</p>
<p class="${css.crowNoFlex}"><a target="_blank" href="https://github.com/settings/tokens">https://github.com/settings/tokens</a></p>
<div class="${css.crowNoFlex}">${self._view.gistToken}</div>
<div class="${css.crowNoFlex}">${this._view.gistToken}</div>
</div>
</div>`
self._view.config.themes = yo`
this._view.config.themes = yo`
<div class="${css.info} card">
<div class="card-body">
<h6 class="${css.title} card-title">Themes</h6>
<div class="card-text">
<div class="${css.frow} form-check ${css.crow}">
${self._view.theme.light}
${this._view.theme.light}
<label class="form-check-label" for="themeLight">Light Theme</label>
</div>
<div class="${css.frow} form-check ${css.crow}">
${self._view.theme.dark}
${this._view.theme.dark}
<label class="form-check-label" for="themeDark">Dark Theme</label>
</div>
<div class="${css.frow} form-check ${css.crow}">
${self._view.theme.clean}
${this._view.theme.clean}
<label class="form-check-label" for="themeClean">Clean Theme</label>
</div>
</div>
</div>
</div>`
self._view.el = yo`
this._view.el = yo`
<div class="${css.settingsTabView}" id="settingsView">
${self._view.config.homePage}
${self._view.config.general}
${self._view.gistToken}
${self._view.config.themes}
${this._view.config.homePage}
${this._view.config.general}
${this._view.gistToken}
${this._view.config.themes}
</div>`
function onchangeGenerateContractMetadata (event) {
self._deps.config.set('settings/generate-contract-metadata', !self._deps.config.get('settings/generate-contract-metadata'))
self.config.set('settings/generate-contract-metadata', !self.config.get('settings/generate-contract-metadata'))
}
function onchangeOption (event) {
self._deps.config.set('settings/always-use-vm', !self._deps.config.get('settings/always-use-vm'))
self.config.set('settings/always-use-vm', !self.config.get('settings/always-use-vm'))
}
function onswitch2darkTheme (event) {
styleGuide.switchTheme('dark')
@ -173,100 +166,9 @@ module.exports = class SettingsTab extends ApiFactory {
styleGuide.switchTheme('clean')
}
function onchangePersonal (event) {
self._deps.config.set('settings/personal-mode', !self._deps.config.get('settings/personal-mode'))
self.config.set('settings/personal-mode', !self.config.get('settings/personal-mode'))
}
styleGuide.switchTheme()
return self._view.el
return this._view.el
}
}
const css = csjs`
.settingsTabView {
padding: 2%;
}
.info {
margin-bottom: .6rem;
word-break: break-word;
font-size: .8rem;
}
.info h7 {
margin-bottom: .5rem;
}
.title {
// font-size: 1.1em;
// font-weight: bold;
// margin-bottom: 1em;
}
.frow {
margin-bottom: .5rem;
}
.crow {
// display: flex;
// overflow: auto;
// clear: both;
// padding: .2em;
}
.checkboxText {
font-weight: normal;
}
.crow label {
cursor:pointer;
}
.crowNoFlex {
overflow: auto;
clear: both;
}
.attention {
margin-bottom: 1em;
padding: .5em;
font-weight: bold;
}
.heading {
margin-bottom: 0;
}
.explaination {
margin-top: 3px;
margin-bottom: 3px;
}
input {
margin-right: 5px;
cursor: pointer;
width: inherit;
}
input[type=radio] {
margin-top: 2px;
}
.pluginTextArea {
font-family: unset;
}
.removePlugin {
cursor: pointer;
}
.icon {
margin-right: .5em;
}
.savegisttoken {
margin-left: 5px;
}
.aPlugin {
display: inline-block;
padding-left: 10px;
padding-top: 4px;
padding-bottom: 6px;
max-width: 100px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
vertical-align: middle;
}
.removePlugin{
padding-left: 7px;
padding-right: 7px;
margin-left: 10px;
}
.inline {
display: inline;
width: 50%;
}
`

@ -1,24 +1,30 @@
var csjs = require('csjs-inject')
var css = csjs`
const css = csjs`
.settingsTabView {
padding: 2%;
display: flex;
}
.info {
margin-bottom: 1em;
margin-bottom: .6rem;
word-break: break-word;
font-size: .8rem;
}
.info h7 {
margin-bottom: .5rem;
}
.title {
font-size: 1.1em;
font-weight: bold;
margin-bottom: 1em;
// font-size: 1.1em;
// font-weight: bold;
// margin-bottom: 1em;
}
.frow {
margin-bottom: .5rem;
}
.crow {
display: flex;
overflow: auto;
clear: both;
padding: .2em;
// display: flex;
// overflow: auto;
// clear: both;
// padding: .2em;
}
.checkboxText {
font-weight: normal;
@ -35,10 +41,6 @@ var css = csjs`
padding: .5em;
font-weight: bold;
}
.select {
font-weight: bold;
margin-top: 1em;
}
.heading {
margin-bottom: 0;
}
@ -49,6 +51,7 @@ var css = csjs`
input {
margin-right: 5px;
cursor: pointer;
width: inherit;
}
input[type=radio] {
margin-top: 2px;
@ -56,24 +59,36 @@ var css = csjs`
.pluginTextArea {
font-family: unset;
}
.pluginLoad {
vertical-align: top;
}
i.warnIt {
color: var(--warning);
.removePlugin {
cursor: pointer;
}
.icon {
margin-right: .5em;
}
.remixdinstallation {
padding: 3px;
border-radius: 2px;
margin-left: 5px;
}
.savegisttoken {
margin-left: 5px;
}
}
.aPlugin {
display: inline-block;
padding-left: 10px;
padding-top: 4px;
padding-bottom: 6px;
max-width: 100px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
vertical-align: middle;
}
.removePlugin{
padding-left: 7px;
padding-right: 7px;
margin-left: 10px;
}
.inline {
display: inline;
width: 50%;
}
`
module.exports = css

@ -1,36 +0,0 @@
const csjs = require('csjs-inject')
const css = csjs`
.menu {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.active {
}
.options {
float: left;
padding-top: 0.7em;
min-width: 60px;
font-size: 0.9em;
cursor: pointer;
font-size: 1em;
text-align: center;
}
.optionViews {
overflow: scroll;
height: 100%;
}
.optionViews > div {
display: none;
}
.optionViews .pre {
word-wrap: break-word;
border-radius: 3px;
display: inline-block;
padding: 0 0.6em;
}
`
module.exports = css

@ -1,90 +0,0 @@
const yo = require('yo-yo')
var css = require('./styles/support-tab-styles')
import { ApiFactory } from 'remix-plugin'
class SupportTab extends ApiFactory {
constructor (localRegistry) {
super()
this.el = null
this.gitterIframe = ''
this.gitterIsLoaded = false
}
__showing () {
if (this.gitterIsLoaded) return
const iframe = yo`<iframe class="${css.chatIframe}" src='https://gitter.im/ethereum/remix/~embed'></iframe>`
this.gitterIframe.parentNode.replaceChild(iframe, this.gitterIframe)
this.gitterIframe = iframe
this.el.style.display = 'block'
this.gitterIsLoaded = true
}
get profile () {
return {
name: 'support',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik03MDQgMTkycTAtMjYtMTktNDV0LTQ1LTE5LTQ1IDE5LTE5IDQ1IDE5IDQ1IDQ1IDE5IDQ1LTE5IDE5LTQ1em04OTYtMzJ2MzIwcTAgMTYtMTIgMjUtOCA3LTIwIDctNCAwLTctMWwtNDQ4LTk2cS0xMS0yLTE4LTExdC03LTIwaC0yNTZ2MTAycTExMSAyMyAxODMuNSAxMTF0NzIuNSAyMDN2ODAwcTAgMjYtMTkgNDV0LTQ1IDE5aC01MTJxLTI2IDAtNDUtMTl0LTE5LTQ1di04MDBxMC0xMDYgNjIuNS0xOTAuNXQxNjEuNS0xMTQuNXYtMTExaC0zMnEtNTkgMC0xMTUgMjMuNXQtOTEuNSA1My02NiA2Ni41LTQwLjUgNTMuNS0xNCAyNC41cS0xNyAzNS01NyAzNS0xNiAwLTI5LTctMjMtMTItMzEuNS0zN3QzLjUtNDlxNS0xMCAxNC41LTI2dDM3LjUtNTMuNSA2MC41LTcwIDg1LTY3IDEwOC41LTUyLjVxLTI1LTQyLTI1LTg2IDAtNjYgNDctMTEzdDExMy00NyAxMTMgNDcgNDcgMTEzcTAgMzMtMTQgNjRoMzAycTAtMTEgNy0yMHQxOC0xMWw0NDgtOTZxMy0xIDctMSAxMiAwIDIwIDcgMTIgOSAxMiAyNXoiLz48L3N2Zz4=',
description: 'help center'
}
}
render () {
if (this.el) return this.el
this.gitterIframe = yo`<div></div>`
const remixd = yo`
<div class="${css.info}">
<div class=${css.title}>Accessing local files</div>
<div class="${css.crow}">
Remixd is a tool which allow Remix IDE to access files located in your local computer.
it can also be used to setup a development environment.
</div>
<div class="${css.crow}">More infos:</div>
<div class="${css.crow}"><a target="_blank" href="https://github.com/ethereum/remixd"> https://github.com/ethereum/remixd</a></div>
<div class="${css.crow}"><a target="_blank" href="https://remix.readthedocs.io/en/latest/tutorial_remixd_filesystem">http://remix.readthedocs.io/en/latest/tutorial_remixd_filesystem.html</a></div>
<div class="${css.crow}">Installation: <pre class=${css.remixdinstallation}>npm install remixd -g</pre></div>
</div>`
const localremixd = yo`
<div class="${css.info}">
<div class=${css.title}>Running Remix locally</div>
<div class="${css.crow}">
as a NPM module:
</div>
<a target="_blank" href="https://www.npmjs.com/package/remix-ide">https://www.npmjs.com/package/remix-ide</a>
<pre class=${css.remixdinstallation}>npm install remix-ide -g</pre>
<div class="${css.crow}">
as an electron app:
</div>
<a target="_blank" href="https://github.com/horizon-games/remix-app">https://github.com/horizon-games/remix-app</a>
</div>`
this.el = yo`
<div class="${css.supportTabView}" id="supportView">
<div class="${css.infoBox}">
Have a question, found a bug or want to propose a feature? Have a look at the
<a target="_blank" href='https://github.com/ethereum/remix-ide/issues'> issues</a> or check out
<a target="_blank" href='https://remix.readthedocs.io/en/latest/'> the documentation page on Remix</a> or
<a target="_blank" href='https://solidity.readthedocs.io/en/latest/'> Solidity</a>.
</div>
<div class="${css.chat}">
<div class="${css.chatTitle}" onclick=${() => { window.open('https://gitter.im/ethereum/remix') }} title='Click to open chat in Gitter'>
<div class="${css.chatTitleText}">ethereum/remix community chat</div>
</div>
${this.gitterIframe}
</div>
${remixd}
${localremixd}
</div>`
return this.el
}
}
module.exports = SupportTab

@ -1,90 +0,0 @@
var yo = require('yo-yo')
var css = require('./styles/tabbed-menu-styles')
var globalRegistry = require('../../global/registry')
var helper = require('../../lib/helper')
var EventManager = require('../../lib/events')
class TabbedMenu {
constructor (localRegistry) {
const self = this
self.event = new EventManager()
self._components = {}
self._components.registry = localRegistry || globalRegistry
self._deps = {
app: self._components.registry.get('app').api
}
self._view = { el: null, viewport: null, tabs: {}, contents: {} }
}
render () {
const self = this
if (self._view.el) return self._view.el
self._view.el = yo`<ul class=${css.menu}>${Object.values(self._view.tabs)}</ul>`
return self._view.el
}
renderViewport () {
const self = this
if (self._view.viewport) return self._view.viewport
self._view.viewport = yo`
<div id="optionViews" class=${css.optionViews}>
${Object.values(self._view.contents)}
</div>`
return self._view.viewport
}
addTab (title, cssClass, content) {
const self = this
if (helper.checkSpecialChars(title)) return
if (self._view.contents[title] || self._view.tabs[title]) throw new Error('tab already exists')
self._view.contents[title] = content
self._view.tabs[title] = yo`<li class="${css.options} ${cssClass}" onclick=${function (ev) { self.selectTab(this) }} title=${title}>${title}</li>`
if (self._view.el) self._view.el.appendChild(self._view.tabs[title])
if (self._view.viewport) self._view.viewport.appendChild(self._view.contents[title])
}
removeTabByTitle (title) {
const self = this
if (self._view.tabs[title]) {
self._view.tabs[title].parentNode.removeChild(self._view.tabs[title])
}
if (self._view.contents[title]) {
self._view.contents[title].parentNode.removeChild(self._view.contents[title])
}
delete self._view.contents[title]
delete self._view.tabs[title]
}
getTabByClass (tabClass) {
const self = this
return self._view.el.querySelector(`li.${tabClass}`)
}
updateTabTitle (tabClass, title) {
const self = this
var tab = self.getTabByClass(tabClass)
if (tab) tab.innerHTML = title
}
selectTabByTitle (title) {
const self = this
self.selectTab(self._view.tabs[title])
}
selectTabByClassName (tabClass) {
const self = this
var tab = self.getTabByClass(tabClass)
if (tab) self.selectTab(tab)
return tab
}
selectTab (el) {
const self = this
if (!el.classList.contains(css.active)) {
var nodes = Object.values(self._view.tabs)
for (var i = 0; i < nodes.length; ++i) {
nodes[i].classList.remove(css.active)
self._view.contents[nodes[i].getAttribute('title')].style.display = 'none'
}
}
var title = el.getAttribute('title')
self._view.contents[el.getAttribute('title')].style.display = 'block'
el.classList.add(css.active)
self._deps.app.event.trigger('tabChanged', [title])
}
}
module.exports = TabbedMenu

@ -1,30 +1,23 @@
var yo = require('yo-yo')
var async = require('async')
var helper = require('../../lib/helper.js')
var tooltip = require('../ui/tooltip')
var modalDialogCustom = require('../ui/modal-dialog-custom')
var globalRegistry = require('../../global/registry')
var css = require('./styles/test-tab-styles')
var remixTests = require('remix-tests')
import { ApiFactory } from 'remix-plugin'
const TestTabLogic = require('./testTab/testTab')
module.exports = class TestTab extends ApiFactory {
constructor (localRegistry, compileTab) {
constructor (fileManager, filePanel, compileTab) {
super()
// TODO here is a direct reference to compile tab, should be removed
const self = this
self.compileTab = compileTab
self._view = { el: null }
self._components = {}
self._components.registry = localRegistry || globalRegistry
// dependencies
self._deps = {
fileManager: self._components.registry.get('filemanager').api,
filePanel: self._components.registry.get('filepanel').api
}
self.data = {}
self.testList = yo`<div class=${css.testList}></div>`
this.compileTab = compileTab
this._view = { el: null }
this.compileTab = compileTab
this.fileManager = fileManager
this.filePanel = filePanel
this.testTabLogic = new TestTabLogic(fileManager)
this.data = {}
this.testList = yo`<div class=${css.testList}></div>`
}
get profile () {
@ -38,164 +31,135 @@ module.exports = class TestTab extends ApiFactory {
}
}
render () {
const self = this
var testsOutput = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>`
var testsSummary = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>`
var testCallback = function (result) {
testsOutput.hidden = false
if (result.type === 'contract') {
testsOutput.appendChild(yo`<div class="${css.outputTitle}">${result.filename} (${result.value})</div>`)
} else if (result.type === 'testPass') {
testsOutput.appendChild(yo`<div class="${css.testPass} ${css.testLog} bg-success">✓ (${result.value})</div>`)
} else if (result.type === 'testFailure') {
testsOutput.appendChild(yo`<div class="${css.testFailure} ${css.testLog} bg-danger">✘ (${result.value})</div>`)
}
}
var resultsCallback = function (_err, result, cb) {
// total stats for the test
// result.passingNum
// result.failureNum
// result.timePassed
cb()
}
var updateFinalResult = function (_err, result, filename) {
testsSummary.hidden = false
if (_err) {
testsSummary.appendChild(yo`<div class="${css.testFailureSummary} text-danger" >${_err.message}</div>`)
return
}
testsSummary.appendChild(yo`<div class=${css.summaryTitle}> ${filename} </div>`)
if (result.totalPassing > 0) {
testsSummary.appendChild(yo`<div class="text-success">${result.totalPassing} passing (${result.totalTime}s)</div>`)
testsSummary.appendChild(yo`<br>`)
}
if (result.totalFailing > 0) {
testsSummary.appendChild(yo`<div class="text-danger" >${result.totalFailing} failing</div>`)
testsSummary.appendChild(yo`<br>`)
}
result.errors.forEach((error, index) => {
testsSummary.appendChild(yo`<div class="text-danger" >${error.context} - ${error.value} </div>`)
testsSummary.appendChild(yo`<div class="${css.testFailureSummary} text-danger" >${error.message}</div>`)
testsSummary.appendChild(yo`<br>`)
})
}
function runTest (testFilePath, callback) {
self._deps.fileManager.fileProviderOf(testFilePath).get(testFilePath, (error, content) => {
if (!error) {
var runningTest = {}
runningTest[testFilePath] = { content }
remixTests.runTestSources(runningTest, testCallback, resultsCallback, (error, result) => {
updateFinalResult(error, result, testFilePath)
callback(error)
}, (url, cb) => { self.compileTab.compileTabLogic.importFileCb(url, cb) })
}
})
}
activate () {
this.listenToEvents()
}
function getTests (self, cb) {
var path = self._deps.fileManager.currentPath()
if (!path) return cb(null, [])
var provider = self._deps.fileManager.fileProviderOf(path)
if (!provider) return cb(null, [])
var tests = []
self._deps.fileManager.getFilesFromPath(path)
.then((files) => {
for (var file in files) {
if (/.(_test.sol)$/.exec(file)) tests.push(provider.type + '/' + file)
}
cb(null, tests)
})
.catch(err => cb(err))
}
deactivate () {
}
self._deps.filePanel.event.register('newTestFileCreated', file => {
var testList = self.view.querySelector("[class^='testList']")
var test = yo`<label class="singleTestLabel"><input class="singleTest" onchange=${(e) => toggleCheckbox(e.target.checked, file)} type="checkbox" checked="true">${file}</label>`
listenToEvents () {
this.filePanel.event.register('newTestFileCreated', file => {
var testList = this.view.querySelector("[class^='testList']")
var test = yo`<label class="singleTestLabel"><input class="singleTest" onchange=${(e) => this.toggleCheckbox(e.target.checked, file)} type="checkbox" checked="true">${file}</label>`
testList.appendChild(test)
self.data.allTests.push(file)
self.data.selectedTests.push(file)
this.data.allTests.push(file)
this.data.selectedTests.push(file)
})
self._deps.fileManager.events.on('currentFileChanged', (file) => {
getTests(self, (error, tests) => {
this.fileManager.events.on('currentFileChanged', (file, provider) => {
this.testTabLogic.getTests((error, tests) => {
if (error) return tooltip(error)
self.data.allTests = tests
self.data.selectedTests = [...self.data.allTests]
if (!tests.length) {
yo.update(self.testList, yo`<div class=${css.testList}>No test file available</div>`)
} else {
yo.update(self.testList, yo`<div class=${css.testList}>${listTests()}</div>`)
}
testsOutput.hidden = true
testsSummary.hidden = true
testsOutput.innerHTML = ''
testsSummary.innerHTML = ''
this.data.allTests = tests
this.data.selectedTests = [...this.data.allTests]
const testsMessage = (tests.length ? this.listTests() : 'No test file available')
yo.update(this.testList, yo`<div class=${css.testList}>${testsMessage}</div>`)
if (!this.testsOutput || !this.testsSummary) return
this.testsOutput.hidden = true
this.testsSummary.hidden = true
this.testsOutput.innerHTML = ''
this.testsSummary.innerHTML = ''
})
})
}
// self._events.filePanel.register('fileRenamed', (oldName, newName, isFolder) => {
// debugger
// self.data.allTests = self.data.allTests.filter(e => e != oldName)
// self.data.selectedTests = self.data.selectedTests.filter(e => e !== oldName)
// if (/.(_test.sol)$/.exec(newName)) self.data.allTests.push(newName)
// })
listTests () {
return this.data.allTests.map(test => yo`<label class="singleTestLabel"><input class="singleTest" onchange =${(e) => this.toggleCheckbox(e.target.checked, test)} type="checkbox" checked="true">${test} </label>`)
}
function listTests () {
var tests = self.data.allTests
return tests.map(test => yo`<label class="singleTestLabel"><input class="singleTest" onchange =${(e) => toggleCheckbox(e.target.checked, test)} type="checkbox" checked="true">${test} </label>`)
toggleCheckbox (eChecked, test) {
if (!this.data.selectedTests) {
this.data.selectedTests = this._view.el.querySelectorAll('.singleTest:checked')
}
function toggleCheckbox (eChecked, test) {
if (!self.data.selectedTests) {
self.data.selectedTests = self._view.el.querySelectorAll('.singleTest:checked')
}
let selectedTests = self.data.selectedTests
selectedTests = eChecked ? [...selectedTests, test] : selectedTests.filter(el => el !== test)
self.data.selectedTests = selectedTests
let checkAll = self._view.el.querySelector('[id="checkAllTests"]')
if (eChecked) {
checkAll.checked = true
} else if (!selectedTests.length) {
checkAll.checked = false
}
let selectedTests = this.data.selectedTests
selectedTests = eChecked ? [...selectedTests, test] : selectedTests.filter(el => el !== test)
this.data.selectedTests = selectedTests
let checkAll = this._view.el.querySelector('[id="checkAllTests"]')
if (eChecked) {
checkAll.checked = true
} else if (!selectedTests.length) {
checkAll.checked = false
}
}
function checkAll (event) {
let checkBoxes = self._view.el.querySelectorAll('.singleTest')
const checkboxesLabels = self._view.el.querySelectorAll('.singleTestLabel')
// checks/unchecks all
for (let i = 0; i < checkBoxes.length; i++) {
checkBoxes[i].checked = event.target.checked
toggleCheckbox(event.target.checked, checkboxesLabels[i].innerText)
}
checkAll (event) {
let checkBoxes = this._view.el.querySelectorAll('.singleTest')
const checkboxesLabels = this._view.el.querySelectorAll('.singleTestLabel')
// checks/unchecks all
for (let i = 0; i < checkBoxes.length; i++) {
checkBoxes[i].checked = event.target.checked
this.toggleCheckbox(event.target.checked, checkboxesLabels[i].innerText)
}
}
var runTests = function () {
testsOutput.innerHTML = 'Running tests ...'
var tests = self.data.selectedTests
async.eachOfSeries(tests, (value, key, callback) => { runTest(value, callback) })
testCallback (result) {
this.testsOutput.hidden = false
if (result.type === 'contract') {
this.testsOutput.appendChild(yo`<div class=${css.outputTitle}>${result.filename} (${result.value})</div>`)
} else if (result.type === 'testPass') {
this.testsOutput.appendChild(yo`<div class="${css.testPass} ${css.testLog} bg-success">✓ (${result.value})</div>`)
} else if (result.type === 'testFailure') {
this.testsOutput.appendChild(yo`<div class="${css.testFailure} ${css.testLog} bg-danger">✘ (${result.value})</div>`)
}
}
resultsCallback (_err, result, cb) {
// total stats for the test
// result.passingNum
// result.failureNum
// result.timePassed
cb()
}
var generateTestFile = function () {
var fileManager = self._deps.fileManager
var path = fileManager.currentPath()
var fileProvider = fileManager.fileProviderOf(path)
if (fileProvider) {
helper.createNonClashingNameWithPrefix(path + '/test.sol', fileProvider, '_test', (error, newFile) => {
if (error) return modalDialogCustom.alert('Failed to create file. ' + newFile + ' ' + error)
if (!fileProvider.set(newFile, testContractSample)) {
modalDialogCustom.alert('Failed to create test file ' + newFile)
} else {
fileManager.switchFile(newFile)
}
})
}
updateFinalResult (_err, result, filename) {
this.testsSummary.hidden = false
if (_err) {
this.testsSummary.appendChild(yo`<div class="${css.testFailureSummary} text-danger" >${_err.message}</div>`)
return
}
this.testsSummary.appendChild(yo`<div class=${css.summaryTitle}> ${filename} </div>`)
if (result.totalPassing > 0) {
this.testsSummary.appendChild(yo`<div class="text-success" >${result.totalPassing} passing (${result.totalTime}s)</div>`)
this.testsSummary.appendChild(yo`<br>`)
}
if (result.totalFailing > 0) {
this.testsSummary.appendChild(yo`<div class="text-danger" >${result.totalFailing} failing</div>`)
this.testsSummary.appendChild(yo`<br>`)
}
result.errors.forEach((error, index) => {
this.testsSummary.appendChild(yo`<div class="text-danger" >${error.context} - ${error.value} </div>`)
this.testsSummary.appendChild(yo`<div class="${css.testFailureSummary} text-danger" >${error.message}</div>`)
this.testsSummary.appendChild(yo`<br>`)
})
}
runTest (testFilePath, callback) {
this.fileManager.fileProviderOf(testFilePath).get(testFilePath, (error, content) => {
if (error) return
var runningTest = {}
runningTest[testFilePath] = { content }
remixTests.runTestSources(runningTest, (result) => { this.testCallback(result) }, (_err, result, cb) => { this.resultsCallback(_err, result, cb) }, (error, result) => {
this.updateFinalResult(error, result, testFilePath)
callback(error)
}, (url, cb) => {
return this.compileTab.compileTabLogic.importFileCb(url, cb)
})
})
}
runTests () {
this.testsOutput.innerHTML = ''
this.testsSummary.innerHTML = ''
var tests = this.data.selectedTests
async.eachOfSeries(tests, (value, key, callback) => { this.runTest(value, callback) })
}
render () {
this.testsOutput = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>`
this.testsSummary = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>`
var el = yo`
<div class="${css.testTabView} card" id="testView">
@ -208,70 +172,28 @@ module.exports = class TestTab extends ApiFactory {
<br/>
For more details, see
How to test smart contracts guide in our documentation.
<br/>
<button class="${css.generateTestFile} btn btn-primary m-1" onclick="${generateTestFile}">Generate test file</button>
<div class="${css.generateTestFile} btn btn-primary m-1" onclick="${this.testTabLogic.generateTestFile(this)}">Generate test file</div>
</div>
<div class="${css.tests}">
${self.testList}
${this.testList}
<div class="${css.buttons} btn-group">
<button class="${css.runButton} btn btn-primary m-1" onclick="${runTests}">Run Tests</button>
<div class="${css.runButton} btn btn-primary m-1" onclick="${this.runTests.bind(this)}">Run Tests</div>
<label class="${css.label}" for="checkAllTests">
<input id="checkAllTests"
type="checkbox"
onclick="${function (event) { checkAll(event) }}"
onclick="${(event) => { this.checkAll(event) }}"
checked="true"
>
Check/Uncheck all
</label>
</div>
${testsOutput}
${testsSummary}
${this.testsOutput}
${this.testsSummary}
</div>
</div>
`
if (!self._view.el) self._view.el = el
if (!this._view.el) this._view.el = el
return el
}
}
var testContractSample = `pragma solidity >=0.4.0 <0.6.0;
import "remix_tests.sol"; // this import is automatically injected by Remix.
// file name has to end with '_test.sol'
contract test_1 {
function beforeAll() public {
// here should instantiate tested contract
Assert.equal(uint(4), uint(3), "error in before all function");
}
function check1() public {
// use 'Assert' to test the contract
Assert.equal(uint(2), uint(1), "error message");
Assert.equal(uint(2), uint(2), "error message");
}
function check2() public view returns (bool) {
// use the return value (true or false) to test the contract
return true;
}
}
contract test_2 {
function beforeAll() public {
// here should instantiate tested contract
Assert.equal(uint(4), uint(3), "error in before all function");
}
function check1() public {
// use 'Assert' to test the contract
Assert.equal(uint(2), uint(1), "error message");
Assert.equal(uint(2), uint(2), "error message");
}
function check2() public view returns (bool) {
// use the return value (true or false) to test the contract
return true;
}
}`

@ -0,0 +1,85 @@
var helper = require('../../../lib/helper.js')
var modalDialogCustom = require('../../ui/modal-dialog-custom')
class TestTabLogic {
constructor (fileManager) {
this.fileManager = fileManager
}
generateTestFile () {
var path = this.fileManager.currentPath()
var fileProvider = this.fileManager.fileProviderOf(path)
if (!fileProvider) return
helper.createNonClashingNameWithPrefix(path + '/test.sol', fileProvider, '_test', (error, newFile) => {
if (error) return modalDialogCustom.alert('Failed to create file. ' + newFile + ' ' + error)
if (!fileProvider.set(newFile, this.generateTestContractSample())) return modalDialogCustom.alert('Failed to create test file ' + newFile)
this.fileManager.switchFile(newFile)
})
}
async getTests (cb) {
var path = this.fileManager.currentPath()
if (!path) return cb(null, [])
var provider = this.fileManager.fileProviderOf(path)
if (!provider) return cb(null, [])
var tests = []
let files
try {
files = await this.fileManager.getFilesFromPath(path)
} catch (e) {
cb(e.message)
}
for (var file in files) {
if (/.(_test.sol)$/.exec(file)) tests.push(provider.type + '/' + file)
}
cb(null, tests)
}
generateTestContractSample () {
return `pragma solidity >=0.4.0 <0.6.0;
import "remix_tests.sol"; // this import is automatically injected by Remix.
// file name has to end with '_test.sol'
contract test_1 {
function beforeAll() public {
// here should instantiate tested contract
Assert.equal(uint(4), uint(3), "error in before all function");
}
function check1() public {
// use 'Assert' to test the contract
Assert.equal(uint(2), uint(1), "error message");
Assert.equal(uint(2), uint(2), "error message");
}
function check2() public view returns (bool) {
// use the return value (true or false) to test the contract
return true;
}
}
contract test_2 {
function beforeAll() public {
// here should instantiate tested contract
Assert.equal(uint(4), uint(3), "error in before all function");
}
function check1() public {
// use 'Assert' to test the contract
Assert.equal(uint(2), uint(1), "error message");
Assert.equal(uint(2), uint(2), "error message");
}
function check2() public view returns (bool) {
// use the return value (true or false) to test the contract
return true;
}
}`
}
}
module.exports = TestTabLogic
Loading…
Cancel
Save