Merge pull request #1219 from ethereum/runTab

Run tab
pull/3094/head
yann300 7 years ago committed by GitHub
commit a8b09803b5
  1. 2
      ci/browser_tests.sh
  2. 4
      nightwatch.js
  3. 3
      src/app.js
  4. 1
      src/app/panels/editor-panel.js
  5. 27
      src/app/panels/styles/terminal-styles.js
  6. 32
      src/app/panels/terminal.js
  7. 328
      src/app/tabs/run-tab.js
  8. 45
      src/app/tabs/styles/run-tab-styles.js
  9. 93
      src/app/ui/card.js
  10. 13
      src/app/ui/styles-guide/style-guide.js
  11. 1
      src/app/ui/styles/dropdown-styles.js
  12. 2
      src/recorder.js
  13. 17
      src/universal-dapp-styles.js
  14. 20
      src/universal-dapp-ui.js
  15. 2
      test-browser/helpers/contracts.js
  16. 5
      test-browser/tests/ballot.js
  17. 9
      test-browser/tests/compiling.js
  18. 7
      test-browser/tests/units/testRecorder.js

@ -42,7 +42,7 @@ done
npm run nightwatch_remote_chrome || TEST_EXITCODE=1
npm run nightwatch_remote_firefox || TEST_EXITCODE=1
npm run nightwatch_remote_safari || TEST_EXITCODE=1
# npm run nightwatch_remote_safari || TEST_EXITCODE=1
# npm run nightwatch_remote_ie || TEST_EXITCODE=1
# npm run nightwatch_remote_parallel || TEST_EXITCODE=1

@ -53,8 +53,8 @@ module.exports = {
'desiredCapabilities': {
'browserName': 'safari',
'javascriptEnabled': true,
'platform': 'OS X 10.11',
'version': '10.0',
'platform': 'macOS 10.13',
'version': '11.0',
'acceptSslCerts': true,
'build': 'build-' + buildId,
'tunnel-identifier': 'browsersolidity_tests_' + buildId

@ -523,7 +523,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
editor: self._components.editor,
config: self._api.config,
txListener: txlistener,
contextview: self._components.contextView
contextview: self._components.contextView,
udapp: () => { return udapp }
}
})
this._components.editorpanel.event.register('resize', direction => self._adjustLayout(direction))

@ -28,6 +28,7 @@ class EditorPanel {
terminal: new Terminal({
api: {
cmdInterpreter: self._api.cmdInterpreter,
udapp: self._api.udapp,
getPosition (event) {
var limitUp = 36
var limitDown = 20

@ -27,20 +27,20 @@ var css = csjs`
display : flex;
align-items : center;
width : 100%;
padding : 5px;
}
.clear {
margin-left : 10px;
margin-right : 10px;
margin-right : 20px;
width : 10px;
cursor : pointer;
color : ${styles.terminal.icon_Color_TogglePanel};
display : flex;
}
.clear:hover {
color : ${styles.terminal.icon_HoverColor_Menu};
}
.toggleTerminal {
margin-right : 10px;
margin-right : 20px;
margin-left : 20px;
font-size : 14px;
font-weight : bold;
cursor : pointer;
@ -56,6 +56,7 @@ var css = csjs`
height : 100%;
overflow-y : auto;
font-family : monospace;
margin : 0px;
}
.terminal_bg {
display : flex;
@ -137,9 +138,21 @@ var css = csjs`
align-items : center;
justify-content : center;
}
.listen {
min-width : 120px;
display : flex;
.listen {}
.verticalLine {
border-left : 1px solid ${styles.colors.veryLightGrey};
height : 65%;
margin-right : 30px; }
.pendingTx {
border : 1px solid ${styles.terminal.icon_HoverColor_Menu};
border-radius: 50%;
margin-right: 30px;
min-width: 13px;
height: 13px;
display: flex;
justify-content: center;
align-items: center;
font-size: 10px;
}
.dragbarHorizontal {
position : absolute;

@ -105,23 +105,40 @@ class Terminal {
${self._view.input}
</div>
`
self._view.icon = yo`<i onmouseenter=${hover} onmouseleave=${hover} onmousedown=${minimize} class="${css.toggleTerminal} fa fa-angle-double-down"></i>`
self._view.dragbar = yo`<div onmousedown=${mousedown} class=${css.dragbarHorizontal}></div>`
self._view.icon = yo`
<i onmouseenter=${hover} onmouseleave=${hover} onmousedown=${minimize}
class="${css.toggleTerminal} fa fa-angle-double-down"></i>`
self._view.dragbar = yo`
<div onmousedown=${mousedown} class=${css.dragbarHorizontal}></div>`
self._view.dropdown = self._components.dropdown.render()
self._view.pendingTxCount = yo`<div class=${css.pendingTx} title='Pending Transactions'>${self._view.pendingTxCount}</div>`
self._view.bar = yo`
<div class=${css.bar}>
${self._view.dragbar}
<div class=${css.menu}>
${self._view.icon}
<div class=${css.clear} onclick=${clear}>
<i class="fa fa-ban" aria-hidden="true" onmouseenter=${hover} onmouseleave=${hover}></i>
<i class="fa fa-ban" aria-hidden="true" title="Clear console"
onmouseenter=${hover} onmouseleave=${hover}></i>
</div>
${self._view.pendingTxCount}
<div class=${css.verticalLine}></div>
<div class=${css.listen}>
<input onchange=${listenOnNetwork} type="checkbox"
title="If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you">
</div>
${self._view.dropdown}
<div class=${css.search}><i class="fa fa-search ${css.searchIcon}" aria-hidden="true"></i><input type="text" class=${css.filter} onkeydown=${filter} placeholder="Search transactions"></div>
<div class=${css.listen}><input onchange=${listenOnNetwork} type="checkbox"><label title="If checked Remix will listen on all transactions mined in the current environment and not only transactions created from the GUI">Listen on network</label></div>
<div class=${css.search}>
<i class="fa fa-search ${css.searchIcon}" aria-hidden="true"></i>
<input type="text" class=${css.filter} onkeydown=${filter} placeholder="Search transactions">
</div>
</div>
</div>
`
setInterval(() => {
updatePendingTxs(self._api, self._view.pendingTxCount)
}, 5000)
function listenOnNetwork (ev) {
self.event.trigger('listenOnNetWork', [ev.currentTarget.checked])
}
@ -563,5 +580,10 @@ function domTerminalFeatures (self, scopedCommands) {
}
function blockify (el) { return yo`<div class=${css.block}>${el}</div>` }
// PENDING TX
function updatePendingTxs (api, el) {
var count = Object.keys(api.udapp().pendingTransactions()).length
el.innerText = count
}
module.exports = Terminal

@ -1,68 +1,116 @@
'use strict'
var $ = require('jquery')
var yo = require('yo-yo')
var helper = require('../../lib/helper.js')
var remixLib = require('remix-lib')
var ethJSUtil = require('ethereumjs-util')
var csjs = require('csjs-inject')
var txExecution = remixLib.execution.txExecution
var txFormat = remixLib.execution.txFormat
var txHelper = remixLib.execution.txHelper
var EventManager = remixLib.EventManager
var helper = require('../../lib/helper.js')
var executionContext = require('../../execution-context')
var modalDialogCustom = require('../ui/modal-dialog-custom')
var copyToClipboard = require('../ui/copy-to-clipboard')
var Card = require('../ui/card')
var Recorder = require('../../recorder')
var EventManager = remixLib.EventManager
var addTooltip = require('../ui/tooltip')
var ethJSUtil = require('ethereumjs-util')
var MultiParamManager = require('../../multiParamManager')
var csjs = require('csjs-inject')
var css = require('./styles/run-tab-styles')
var MultiParamManager = require('../../multiParamManager')
var instanceContainer = yo`<div class="${css.instanceContainer}"></div>`
var noInstancesText = yo`<div class="${css.noInstancesText}">0 contract Instances</div>`
function runTab (appAPI = {}, appEvents = {}, opts = {}) {
/* -------------------------
VARIABLES
--------------------------- */
var self = this
var event = new EventManager()
appEvents.eventManager = event
self._view = {}
self.data = {
count: 0,
text: `All transactions (deployed contracts and function executions)
in this environment can be saved and replayed in
another environment. i.e. Transactions created in
Javascript VM can be replayed in the Injected Web3.`
}
var pendingTxsText = yo`<span>0 pending transactions</span>`
self._view.recorderCount = yo`<span>0</span>`
self._view.instanceContainer = yo`<div class="${css.instanceContainer}"></div>`
self._view.clearInstanceElement = yo`
<i class="${css.clearinstance} ${css.icon} fa fa-trash" onclick=${() => clearInstanceList(self)}
title="Clear instances list and reset recorder" aria-hidden="true">
</i>`
self._view.instanceContainerTitle = yo`
<div class=${css.instanceContainerTitle}
title="Autogenerated generic user interfaces for interaction with deployed contracts">
UI for Deployed Contracts
${self._view.clearInstanceElement}
</div>`
self._view.noInstancesText = yo`
<div class="${css.noInstancesText}">
Currently you have no contract instances to interact with.
</div>`
function runTab (appAPI = {}, appEvents = {}, opts = {}) {
var container = yo`<div class="${css.runTabView}" id="runTabView" ></div>`
var event = new EventManager()
var recorderInterface = makeRecorder(appAPI, appEvents, opts, self)
var clearInstanceElement = yo`<i class="${css.clearinstance} ${css.icon} fa fa-trash" title="Clear Instances List" aria-hidden="true"></i>`
clearInstanceElement.addEventListener('click', () => {
event.trigger('clearInstance', [])
})
var recorderInterface = makeRecorder(event, appAPI, appEvents, opts)
var pendingTxsContainer = yo`
<div class="${css.pendingTxsContainer}">
<div class="${css.pendingTxsText}">
${pendingTxsText}
<span class="${css.transactionActions}">
self._view.collapsedView = yo`
<div class=${css.recorderCollapsedView}>
<div class=${css.recorderCount}>${self._view.recorderCount}</div>
</div>`
self._view.expandedView = yo`
<div class=${css.recorderExpandedView}>
<div class=${css.recorderDescription}>
${self.data.text}
</div>
<div class="${css.transactionActions}">
${recorderInterface.recordButton}
${recorderInterface.runButton}
${clearInstanceElement}
</span>
</div>
</div>`
</div>
</div>
</div>`
self.recorderOpts = {
title: 'Events recorded:',
collapsedView: self._view.collapsedView
}
var recorderCard = new Card({}, {}, self.recorderOpts)
recorderCard.event.register('expandCollapseCard', (arrow, body, status) => {
body.innerHTML = ''
status.innerHTML = ''
if (arrow === 'up') {
status.appendChild(self._view.collapsedView)
body.appendChild(self._view.expandedView)
} else if (arrow === 'down') {
status.appendChild(self._view.collapsedView)
}
})
/* -------------------------
MAIN HTML ELEMENT
--------------------------- */
var el = yo`
<div>
${settings(container, appAPI, appEvents, opts)}
${contractDropdown(event, appAPI, appEvents, opts, instanceContainer)}
${pendingTxsContainer}
${instanceContainer}
${contractDropdown(event, appAPI, appEvents, opts, self)}
${recorderCard.render()}
${self._view.instanceContainer}
</div>
`
container.appendChild(el)
// PENDING transactions
function updatePendingTxs (container, appAPI) {
var pendingCount = Object.keys(opts.udapp.pendingTransactions()).length
pendingTxsText.innerText = pendingCount + ' pending transactions'
}
/* -------------------------
HELPER FUNCTIONS
--------------------------- */
// DROPDOWN
var selectExEnv = el.querySelector('#selectExEnvOptions')
function clearInstanceList (self) {
event.trigger('clearInstance', [])
}
function setFinalContext () {
// set the final context. Cause it is possible that this is not the one we've originaly selected
selectExEnv.value = executionContext.getProvider()
@ -87,20 +135,23 @@ function runTab (appAPI = {}, appEvents = {}, opts = {}) {
modalDialogCustom.alert(alertMsg)
}, setFinalContext)
})
selectExEnv.value = executionContext.getProvider()
executionContext.event.register('contextChanged', (context, silent) => {
setFinalContext()
})
fillAccountsList(appAPI, opts, el)
setInterval(() => {
updateAccountBalances(container, appAPI)
updatePendingTxs(container, appAPI)
}, 10000)
event.register('clearInstance', () => {
var instanceContainer = self._view.instanceContainer
var instanceContainerTitle = self._view.instanceContainerTitle
instanceContainer.innerHTML = '' // clear the instances list
noInstancesText.style.display = 'block'
instanceContainer.appendChild(noInstancesText)
instanceContainer.appendChild(instanceContainerTitle)
instanceContainer.appendChild(self._view.noInstancesText)
})
return { render () { return container } }
}
@ -133,31 +184,41 @@ function updateAccountBalances (container, appAPI) {
}
/* ------------------------------------------------
RECORDER
RECORDER
------------------------------------------------ */
function makeRecorder (events, appAPI, appEvents, opts) {
function makeRecorder (appAPI, appEvents, opts, self) {
var recorder = new Recorder(opts.compiler, {
events: {
udapp: appEvents.udapp,
executioncontext: executionContext.event,
runtab: events
runtab: appEvents.eventManager
},
api: appAPI
})
recorder.event.register('newTxRecorded', (count) => {
self.data.count = count
self._view.recorderCount.innerText = count
})
recorder.event.register('cleared', () => {
self.data.count = 0
self._view.recorderCount.innerText = 0
})
var css2 = csjs`
.container,
.runTxs,
.recorder {
}
.container {}
.runTxs {}
.recorder {}
`
var recordButton = yo`<i class="fa fa-floppy-o savetransaction ${css2.recorder} ${css.icon}" title="Save Transactions" aria-hidden="true"></i>`
var runButton = yo`<i class="fa fa-play runtransaction ${css2.runTxs} ${css.icon}" title="Run Transactions" aria-hidden="true"></i>`
var recordButton = yo`
<i class="fa fa-floppy-o savetransaction ${css2.recorder} ${css.icon}"
onclick=${triggerRecordButton} title="Save Transactions" aria-hidden="true">
</i>`
recordButton.onclick = () => {
function triggerRecordButton () {
var txJSON = JSON.stringify(recorder.getAll(), null, 2)
var path = appAPI.currentPath()
modalDialogCustom.prompt(null, 'save ran transactions to file (e.g. `scenario.json`). The file is going to be saved under ' + path, 'scenario.json', input => {
modalDialogCustom.prompt(null, 'Transactions will be saved in a file under ' + path, 'scenario.json', input => {
var fileProvider = appAPI.fileProviderOf(path)
if (fileProvider) {
var newFile = path + input
@ -172,7 +233,13 @@ function makeRecorder (events, appAPI, appEvents, opts) {
}
})
}
runButton.onclick = () => {
/*
@TODO
update account address in scenario.json
popup if scenario.json not open - "Open a file with transactions you want to replay and click play again"
*/
var currentFile = appAPI.config.get('currentFile')
appAPI.fileProviderOf(currentFile).get(currentFile, (error, json) => {
if (error) {
@ -183,16 +250,17 @@ function makeRecorder (events, appAPI, appEvents, opts) {
var obj = JSON.parse(json)
var txArray = obj.transactions || []
var accounts = obj.accounts || []
var options = obj.options
var abis = obj.abis
var options = obj.options || {}
var abis = obj.abis || {}
var linkReferences = obj.linkReferences || {}
} catch (e) {
return modalDialogCustom.alert('Invalid Scenario File, please try again')
}
if (txArray.length) {
noInstancesText.style.display = 'none'
var noInstancesText = self._view.noInstancesText
if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) }
recorder.run(txArray, accounts, options, abis, linkReferences, opts.udapp, (abi, address, contractName) => {
instanceContainer.appendChild(opts.udappUI.renderInstanceFromABI(abi, address, contractName))
self._view.instanceContainer.appendChild(opts.udappUI.renderInstanceFromABI(abi, address, contractName))
})
}
} else {
@ -201,14 +269,18 @@ function makeRecorder (events, appAPI, appEvents, opts) {
}
})
}
return { recordButton, runButton }
}
/* ------------------------------------------------
section CONTRACT DROPDOWN and BUTTONS
CONTRACT (deploy or access deployed)
------------------------------------------------ */
function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
instanceContainer.appendChild(noInstancesText)
function contractDropdown (events, appAPI, appEvents, opts, self) {
var instanceContainer = self._view.instanceContainer
var instanceContainerTitle = self._view.instanceContainerTitle
instanceContainer.appendChild(instanceContainerTitle)
instanceContainer.appendChild(self._view.noInstancesText)
var compFails = yo`<i title="Contract compilation failed. Please check the compile tab for more information." class="fa fa-times-circle ${css.errorIcon}" ></i>`
appEvents.compiler.register('compilationFinished', function (success, data, source) {
getContractNames(success, data)
@ -222,7 +294,6 @@ function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
})
var atAddressButtonInput = yo`<input class="${css.input} ataddressinput" placeholder="Load contract from Address" title="atAddress" />`
var selectContractNames = yo`<select class="${css.contractNames}" disabled></select>`
function getSelectedContract () {
@ -236,6 +307,7 @@ function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
return null
}
appAPI.getSelectedContract = getSelectedContract
var createPanel = yo`<div class="${css.button}"></div>`
var el = yo`
@ -247,7 +319,7 @@ function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
${createPanel}
<div class="${css.button}">
${atAddressButtonInput}
<div class="${css.atAddress}" onclick=${function () { loadFromAddress(appAPI) }}>At Address</div>
<div class="${css.atAddress}" onclick=${function () { loadFromAddress(appAPI) }}>Access</div>
</div>
</div>
</div>
@ -269,7 +341,7 @@ function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
selectContractNames.addEventListener('change', setInputParamsPlaceHolder)
// ADD BUTTONS AT ADDRESS AND CREATE
// DEPLOY INSTANCE
function createInstance (args) {
var selectedContract = getSelectedContract()
@ -292,7 +364,8 @@ function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
return
}
}
noInstancesText.style.display = 'none'
var noInstancesText = self._view.noInstancesText
if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) }
var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress
instanceContainer.appendChild(opts.udappUI.renderInstance(selectedContract.contract.object, address, selectContractNames.value))
} else {
@ -310,8 +383,10 @@ function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
})
}
// ACCESS DEPLOYED INSTANCE
function loadFromAddress (appAPI) {
noInstancesText.style.display = 'none'
var noInstancesText = self._view.noInstancesText
if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) }
var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`)
var address = atAddressButtonInput.value
if (!ethJSUtil.isValidAddress(address)) {
@ -353,12 +428,11 @@ function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
return el
}
/* ------------------------------------------------
section SETTINGS: Environment, Account, Gas, Value
------------------------------------------------ */
function settings (container, appAPI, appEvents, opts) {
// SETTINGS HTML
// VARIABLES
var net = yo`<span class=${css.network}></span>`
const updateNetwork = () => {
executionContext.detectNetwork((err, { id, name } = {}) => {
@ -370,82 +444,86 @@ function settings (container, appAPI, appEvents, opts) {
}
})
}
setInterval(updateNetwork, 5000)
function newAccount () {
opts.udapp.newAccount('', (error, address) => {
if (!error) {
container.querySelector('#txorigin').appendChild(yo`<option value=${address}>${address}</option>`)
addTooltip(`account ${address} created`)
} else {
addTooltip('Cannot create an account: ' + error)
}
})
}
var el = yo`
<div class="${css.settings}">
<div class="${css.crow}">
<div id="selectExEnv" class="${css.col1_1}">
Environment
</div>
<div class=${css.environment}>
${net}
<select id="selectExEnvOptions" onchange=${updateNetwork} class="${css.select}">
<option id="vm-mode"
title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm"
checked name="executionContext">
JavaScript VM
</option>
<option id="injected-mode"
title="Execution environment has been provided by Mist or similar provider."
value="injected"
checked name="executionContext">
Injected Web3
</option>
<option id="web3-mode"
title="Execution environment connects to node at localhost (or via IPC if available), transactions will be sent to the network and can cause loss of money or worse!
If this page is served via https and you access your node via http, it might not work. In this case, try cloning the repository and serving it via http."
value="web3"
name="executionContext">
Web3 Provider
</option>
</select>
<a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md" target="_blank"><i class="${css.icon} fa fa-info"></i></a>
</div>
</div>
<div class="${css.crow}">
<div class="${css.col1_1}">Account</div>
<select name="txorigin" class="${css.select}" id="txorigin"></select>
${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)}
<i class="fa fa-plus-square-o ${css.createAccount} ${css.icon}" aria-hidden="true" onclick=${newAccount} title="Create a new account"></i>
var environmentEl = yo`
<div class="${css.crow}">
<div id="selectExEnv" class="${css.col1_1}">
Environment
</div>
<div class="${css.crow}">
<div class="${css.col1_1}">Gas limit</div>
<input type="number" class="${css.col2}" id="gasLimit" value="3000000">
</div>
<div class="${css.crow}" style="display: none">
<div class="${css.col1_1}">Gas Price</div>
<input type="number" class="${css.col2}" id="gasPrice" value="0">
</div>
<div class="${css.crow}">
<div class="${css.col1_1}">Value</div>
<input type="text" class="${css.col2_1}" id="value" value="0" title="Enter the value and choose the unit">
<select name="unit" class="${css.col2_2}" id="unit">
<option data-unit="wei">wei</option>
<option data-unit="gwei">gwei</option>
<option data-unit="finney">finney</option>
<option data-unit="ether">ether</option>
<div class=${css.environment}>
${net}
<select id="selectExEnvOptions" onchange=${updateNetwork} class="${css.select}">
<option id="vm-mode"
title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm" checked name="executionContext"> JavaScript VM
</option>
<option id="injected-mode"
title="Execution environment has been provided by Metamask or similar provider."
value="injected" checked name="executionContext"> Injected Web3
</option>
<option id="web3-mode"
title="Execution environment connects to node at localhost (or via IPC if available), transactions will be sent to the network and can cause loss of money or worse!
If this page is served via https and you access your node via http, it might not work. In this case, try cloning the repository and serving it via http."
value="web3" name="executionContext"> Web3 Provider
</option>
</select>
<a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md" target="_blank"><i class="${css.icon} fa fa-info"></i></a>
</div>
</div>
`
// EVENTS
var accountEl = yo`
<div class="${css.crow}">
<div class="${css.col1_1}">Account</div>
<select name="txorigin" class="${css.select}" id="txorigin"></select>
${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)}
<i class="fa fa-plus-circle ${css.icon}" aria-hidden="true" onclick=${newAccount} title="Create a new account"></i>
</div>
`
var gasPriceEl = yo`
<div class="${css.crow}">
<div class="${css.col1_1}">Gas limit</div>
<input type="number" class="${css.col2}" id="gasLimit" value="3000000">
</div>
`
var valueEl = yo`
<div class="${css.crow}">
<div class="${css.col1_1}">Value</div>
<input type="text" class="${css.col2_1}" id="value" value="0" title="Enter the value and choose the unit">
<select name="unit" class="${css.col2_2}" id="unit">
<option data-unit="wei">wei</option>
<option data-unit="gwei">gwei</option>
<option data-unit="finney">finney</option>
<option data-unit="ether">ether</option>
</select>
</div>
`
// DOM ELEMENT
var el = yo`
<div class="${css.settings}">
${environmentEl}
${accountEl}
${gasPriceEl}
${valueEl}
</div>
`
// HELPER FUNCTIONS AND EVENTS
appEvents.udapp.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => {
if (error) return
if (!lookupOnly) el.querySelector('#value').value = '0'
updateAccountBalances(container, appAPI)
})
setInterval(updateNetwork, 5000)
function newAccount () {
appAPI.newAccount('', (error, address) => {
if (!error) {
container.querySelector('#txorigin').appendChild(yo`<option value=${address}>${address}</option>`)
addTooltip(`account ${address} created`)
} else {
addTooltip('Cannot create an account: ' + error)
}
})
}
return el
}

@ -8,11 +8,29 @@ var css = csjs`
display: flex;
flex-direction: column;
}
.instanceContainerTitle {
font-weight: bold;
margin-bottom: 5%;
font-size: 12px;
display: flex;
justify-content: space-between;
}
.settings {
${styles.rightPanel.runTab.box_RunTab}
margin-bottom: 2%;
padding: 10px 15px 15px 15px;
}
.recorderCount {
border: 1px solid ${styles.rightPanel.runTab.icon_HoverColor};
border-radius: 50%;
margin-right: 30px;
min-width: 13px;
height: 13px;
display: flex;
justify-content: center;
align-items: center;
font-size: 10px;
}
.crow {
margin-top: .5em;
display: flex;
@ -54,11 +72,13 @@ var css = csjs`
width: 250px;
}
.instanceContainer {
${styles.rightPanel.runTab.box_Instance}
display: flex;
flex-direction: column;
margin-top: 2%;
margin-bottom: 2%;
border: none;
text-align: center;
padding: 10px 0px 15px 15px;
}
.pendingTxsContainer {
${styles.rightPanel.runTab.box_Instance}
@ -70,8 +90,16 @@ var css = csjs`
}
.container {
${styles.rightPanel.runTab.box_RunTab}
margin-top: 2%;
margin-bottom: 2%;
}
.recorderCollapsedView,
.recorderExpandedView {
display: flex;
flex-direction: column;
}
.recorderDescription {
margin: 0 15px 15px 0;
}
.contractNames {
${styles.rightPanel.runTab.dropdown_RunTab}
width: 100%;
@ -105,6 +133,7 @@ var css = csjs`
.noInstancesText {
${styles.rightPanel.runTab.box_Instance}
font-style: italic;
text-align: left;
}
.pendingTxsText {
${styles.rightPanel.runTab.borderBox_Instance}
@ -176,14 +205,14 @@ var css = csjs`
.networkItem {
margin-right: 5px;
}
.clearinstance {}
.clearinstance {
margin-right: 15px;
}
.transactionActions {
display: flex;
width: 70px;
justify-content: space-between;
border: 1px solid ${styles.rightPanel.runTab.additionalText_Color};
padding: 5px;
border-radius: 3px;
justify-content: space-evenly;
${styles.rightPanel.runTab.box_Info_RunTab};
width: 145px;
}
`

@ -0,0 +1,93 @@
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var styleGuide = require('./styles-guide/theme-chooser')
var styles = styleGuide.chooser()
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
module.exports = class Card {
constructor (api, events, opts) {
const self = this
self._api = api
self._events = events
self._opts = opts
self._view = {}
self.event = new EventManager()
}
render () {
const self = this
if (self._view.el) return self._view.el
self._view.cardBody = yo`<div class=${css.cardBody}></div>`
self._view.arrow = yo`<i class="${css.arrow} fa fa-angle-up"
onclick=${(ev) => trigger(ev.target)}></i>`
self._view.expandCollapseButton = yo`
<div class=${css.expandCollapseButton}>${self._view.arrow}</div>`
self._view.statusBar = yo`<div class=${css.statusBar}>${self._opts.collapsedView}</div>`
self._view.cardHeader = yo`
<div class=${css.cardHeader}>
<div class=${css.cardTitles}>
<div class=${css.cardTitle}>${self._opts.title}</div>
${self._view.statusBar}
</div>
<div class=${css.expandCollapseButton}>${self._view.expandCollapseButton}</div>
</div>`
function trigger (el) {
var body = self._view.cardBody
var status = self._view.statusBar
el.classList.toggle('fa-angle-down')
var arrow = el.classList.toggle('fa-angle-up') ? 'down' : 'up'
self.event.trigger('expandCollapseCard', [arrow, body, status])
}
// HTML
self._view.el = yo`
<div class=${css.cardContainer}>
${self._view.cardHeader}
${self._view.cardBody}
</div>`
return self._view.el
}
}
const css = csjs`
.cardContainer {
${styles.remix.solidBox};
margin-bottom : 2%;
}
.cardHeader {
display : flex;
justify-content : space-between;
}
.statusBar {}
.cardBody {}
.cardTitles {
display : flex;
flex-direction : row;
align-items : center;
}
.cardTitle {
font-size : 13px;
font-weight : bold;
color : ${styles.appProperties.mainText_Color};
margin-right : 5px;
}
.expandCollapseButton {}
.arrow {
margin-right : 15px;
color : ${styles.appProperties.icon_Color};
font-weight : bold;
cursor : pointer;
font-size : 14px;
}
.arrow:hover {
color : ${styles.appProperties.icon_HoverColor};
}
`

@ -390,7 +390,12 @@ function styleGuide () {
tooltip_CopyToClipboard_Color: appProperties.tooltip_Color,
icon_Color_CopyToClipboard: appProperties.icon_Color,
icon_HoverColor_CopyToClipboard: appProperties.icon_HoverColor
icon_HoverColor_CopyToClipboard: appProperties.icon_HoverColor,
solidBox: appProperties.uiElements.solidBorderBox({
BackgroundColor: appProperties.solidBox_BackgroundColor,
Color: appProperties.solidBox_TextColor
})
},
/* ------------------------------------------------------
@ -569,6 +574,12 @@ function styleGuide () {
Color: appProperties.solidBox_TextColor
}),
box_Info_RunTab: appProperties.uiElements.dottedBorderBox({
BackgroundColor: appProperties.solidBorderBox_BackgroundColor,
BorderColor: appProperties.solidBorderBox_BorderColor,
Color: appProperties.solidBorderBox_TextColor
}),
dropdown_RunTab: appProperties.uiElements.dropdown({
BackgroundColor: appProperties.dropdown_BackgroundColor,
BorderColor: appProperties.dropdown_BorderColor,

@ -10,6 +10,7 @@ var css = csjs`
display : flex;
flex-direction : column;
margin-right : 10px;
width : auto;
}
.selectbox {
display : flex;

@ -131,6 +131,7 @@ class Recorder {
append (timestamp, record) {
var self = this
self.data.journal.push({ timestamp, record })
self.event.trigger('newTxRecorded', [self.data.journal.length])
}
/**
@ -167,6 +168,7 @@ class Recorder {
self.data._abis = {}
self.data._contractABIReferences = {}
self.data._linkReferences = {}
self.event.trigger('cleared', [])
}
/**

@ -10,7 +10,7 @@ var css = csjs`
.title {
${styles.rightPanel.runTab.titlebox_RunTab}
display: flex;
justify-content: end;
justify-content: space-between;
align-items: center;
font-size: 11px;
height: 30px;
@ -20,6 +20,9 @@ var css = csjs`
line-height: initial;
overflow: visible;
margin-bottom: 10px;
}
.noInstancesText {
}
.titleLine {
display: flex;
@ -35,11 +38,9 @@ var css = csjs`
color: ${styles.rightPanel.runTab.icon_AltColor_Instance_CopyToClipboard};
}
.instance {
${styles.rightPanel.runTab.box_Instance};
margin-bottom: 10px;
padding: 10px 15px 15px 15px;
position: relative;
overflow: visible;
min-width: 310px;
display: flex;
flex-direction: column;
}
.instance .title:before {
content: "\\25BE";
@ -81,7 +82,9 @@ var css = csjs`
.closeIcon {
font-size: 12px;
cursor: pointer;
margin-left: 5px;
}
.udapp {}
.udappClose {
display: flex;
justify-content: flex-end;
@ -211,7 +214,7 @@ var css = csjs`
margin-right: 5%;
font-size: 10px;
border-width: 1px;
width: inherit;
width: inherit;
}
.multiHeader button {
display: inline-block;

@ -14,7 +14,6 @@ var MultiParamManager = require('./multiParamManager')
function UniversalDAppUI (udapp, opts = {}) {
var self = this
this.udapp = udapp
self.el = yo`<div class=${css.udapp}></div>`
}
@ -23,6 +22,10 @@ UniversalDAppUI.prototype.reset = function () {
}
UniversalDAppUI.prototype.renderInstance = function (contract, address, contractName) {
var noInstances = document.querySelector('[class^="noInstancesText"]')
if (noInstances) {
noInstances.parentNode.removeChild(noInstances)
}
var abi = this.udapp.getABI(contract)
return this.renderInstanceFromABI(abi, address, contractName)
}
@ -33,22 +36,25 @@ UniversalDAppUI.prototype.renderInstance = function (contract, address, contract
// this returns a DOM element
UniversalDAppUI.prototype.renderInstanceFromABI = function (contractABI, address, contractName) {
var self = this
function remove () { instance.remove() }
address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex')
var instance = yo`<div class="instance ${css.instance}" id="instance${address}"></div>`
var instance = yo`<div class="instance ${css.instance} ${css.hidesub}" id="instance${address}"></div>`
var context = self.udapp.context()
var shortAddress = helper.shortenAddress(address)
var title = yo`<div class="${css.title}" onclick=${toggleClass}>
var title = yo`
<div class="${css.title}" onclick=${toggleClass}>
<div class="${css.titleText}"> ${contractName} at ${shortAddress} (${context}) </div>
${copyToClipboard(() => address)}
</div>`
if (self.udapp.removable_instances) {
var close = yo`<div class="${css.udappClose}" onclick=${remove}><i class="${css.closeIcon} fa fa-close" aria-hidden="true"></i></div>`
instance.appendChild(close)
title.appendChild(close)
}
function remove () {
instance.remove()
// @TODO perhaps add a callack here to warn the caller that the instance has been removed
}
function toggleClass () {

@ -43,7 +43,7 @@ function getCompiledContracts (browser, compiled, callback) {
function createContract (browser, inputParams, callback) {
browser.click('.runView')
.setValue('div[class^="contractActionsContainerSingle"] input', inputParams, function () {
browser.click('#runTabView button[class^="instanceButton"]').perform(function () { callback() })
browser.click('#runTabView button[class^="instanceButton"]').pause(500).perform(function () { callback() })
})
}

@ -36,6 +36,8 @@ function runTests (browser, testData) {
}).click('.runView')
.setValue('input[placeholder="uint8 _numProposals"]', '1')
.click('#runTabView button[class^="instanceButton"]')
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2)')
.testFunction('delegate - transact (not payable)', '0x0571a2439ea58bd349dd130afb8aff62a33af14c06de0dbc3928519bdf13ce2e',
`[vm]\nfrom:0xca3...a733c\nto:Ballot.delegate(address) 0x692...77b3a\nvalue:0 wei\ndata:0x5c1...4d2db\nlogs:0\nhash:0x057...3ce2e`,
{types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'}, null, null)
@ -83,9 +85,10 @@ function runTests (browser, testData) {
done()
})
})
.pause(500)
.perform((client, done) => {
console.log('delegate - transact (not payable)')
browser.testFunction('delegate - transact (not payable)', '0xd3cd54e2f76f3993078ecf9e1b54a148def4520afc141a182293b3610bddf10f',
browser.waitForElementPresent('.instance:nth-of-type(2)').click('.instance:nth-of-type(2)').testFunction('delegate - transact (not payable)', '0xd3cd54e2f76f3993078ecf9e1b54a148def4520afc141a182293b3610bddf10f',
`[vm]\nfrom:0xca3...a733c\nto:Ballot.delegate(address) 0x692...77b3a\nvalue:0 wei\ndata:0x5c1...4d2db\nlogs:0\nhash:0xd3c...df10f`,
{types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'}, null, null, () => { done() })
}).end()

@ -39,7 +39,8 @@ function testSimpleContract (browser, callback) {
contractHelper.testContracts(browser, 'Untitled.sol', sources[0]['browser/Untitled.sol'], ['TestContract'], function () {
browser.click('.runView')
.click('#runTabView button[class^="instanceButton"]')
.pause(500)
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2)')
.click('#runTabView .instance div[class^="title"]')
.click('#runTabView .instance div[class^="title"]')
.testFunction('f - transact (not payable)',
@ -69,7 +70,8 @@ function testReturnValues (browser, callback) {
contractHelper.testContracts(browser, 'returnValues.sol', sources[1]['browser/returnValues.sol'], ['testReturnValues'], function () {
browser.click('.runView')
.click('#runTabView button[class^="instanceButton"]')
.pause(500)
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2)')
.testFunction('retunValues1 - transact (not payable)',
'0x79dc928d149d2ade02ab610a8ae290636222d034d4adce0bb08a68401e3d1f7f',
`[vm]\nfrom:0xca3...a733c\nto:testReturnValues.retunValues1() 0x5e7...26e9f\nvalue:0 wei\ndata:0x9ed...59eb7\nlogs:0\nhash:0x79d...d1f7f`,
@ -107,7 +109,8 @@ function testInputValues (browser, callback) {
contractHelper.testContracts(browser, 'inputValues.sol', sources[2]['browser/inputValues.sol'], ['test'], function () {
browser.click('.runView')
.click('#runTabView button[class^="instanceButton"]')
.pause(500)
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2)')
.testFunction('inputValue1 - transact (not payable)',
'0x917a873d27d105213eaf5461e14780387ccceb66fed574f8432d1963917832ae',
`[vm]\nfrom:0xca3...a733c\nto:test.inputValue1(uint256,int256,string) 0x8c1...401f5\nvalue:0 wei\ndata:0xd69...00000\nlogs:0\nhash:0x917...832ae`,

@ -10,7 +10,12 @@ module.exports = {
contractHelper.addFile(browser, 'scenario.json', {content: records}, () => {
browser
.click('.runView')
.click('div[class^="cardContainer"] i[class^="arrow"]')
.click('#runTabView .runtransaction')
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2)')
.waitForElementPresent('.instance:nth-of-type(3)')
.click('.instance:nth-of-type(3)')
.clickFunction('getInt - call')
.clickFunction('getAddress - call')
.clickFunction('getFromLib - call')
@ -31,6 +36,8 @@ module.exports = {
done()
})
})
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2)')
.perform((client, done) => {
browser.clickFunction('set - transact (not payable)', {types: 'uint256 _p', values: '34'})
.click('i.savetransaction').modalFooterOKClick().getEditorValue(function (result) {

Loading…
Cancel
Save