Merge pull request #1029 from ethereum/terminalUI

Terminal ui
pull/3094/head
yann300 7 years ago committed by GitHub
commit 5ba27619a3
  1. 244
      src/app/execution/txLogger.js
  2. 4
      src/app/panels/styles/terminal-styles.js
  3. 2
      src/app/tabs/run-tab.js
  4. 2
      src/universal-dapp-ui.js
  5. 2
      test-browser/helpers/contracts.js
  6. 11
      test-browser/helpers/init.js
  7. 6
      test-browser/tests/ballot.js
  8. 20
      test-browser/tests/compiling.js

@ -17,18 +17,43 @@ var typeConversion = remixLib.execution.typeConversion
var css = csjs`
.log {
display: flex;
align-items: end;
justify-content: space-between;
cursor: pointer;
}
.log:hover {
opacity: 0.8;
}
.caret {
color: ${styles.terminal.icon_Color_Menu};
font-size: 15px;
cursor: pointer;
display: flex;
position: absolute;
left: 7px;
}
.caret:hover {
color: ${styles.terminal.icon_HoverColor_Menu};
}
.txLog {
width: 75%;
}
.txItem {
color: ${styles.terminal.text_Primary};
margin-right: 5px;
float: left;
}
.txItemTitle {
font-weight: bold;
}
.tx {
color: ${styles.terminal.text_Title_TransactionLog};
font-weight: bold;
width: 45%;
float: left;
margin: 0 10px;
}
.txTable, .tr, .td {
.txTable,
.tr,
.td {
border-collapse: collapse;
font-size: 10px;
color: ${styles.terminal.text_Primary};
@ -38,27 +63,36 @@ var css = csjs`
margin-top: 1%;
margin-bottom: 5%;
align-self: center;
width: 85%;
}
.tr, .td {
padding: 4px;
vertical-align: baseline;
}
.td:first-child {
min-width: 30%;
width: 30%;
align-items: baseline;
font-weight: bold;
}
.tableTitle {
width: 25%;
}
.buttons {
display: flex;
margin-right: 10px;
}
.debug, .details {
.debug {
${styles.terminal.button_Log_Debug}
margin-left: 5px;
width: 55px;
min-width: 55px;
min-height: 20px;
max-height: 20px;
font-size: 11px;
}
`
.debug:hover {
opacity: 0.8;
}`
/**
* This just export a function that register to `newTransaction` and forward them to the logger.
* Emit debugRequested
@ -132,6 +166,15 @@ class TxLogger {
}
}
function debug (e, data, self) {
e.stopPropagation()
if (data.tx.isCall && data.tx.envMode !== 'vm') {
modalDialog.alert('Cannot debug this call. Debugging calls is only possible in JavaScript VM mode.')
} else {
self.event.trigger('debugRequested', [data.tx.hash])
}
}
function log (self, tx, api) {
var resolvedTransaction = api.resolvedTransaction(tx.hash)
if (resolvedTransaction) {
@ -149,142 +192,68 @@ function log (self, tx, api) {
function renderKnownTransaction (self, data) {
var from = data.tx.from
var to = data.resolvedData.contractName + '.' + data.resolvedData.fn
function debug () {
self.event.trigger('debugRequested', [data.tx.hash])
}
var obj = {from, to}
var tx = yo`
<span id="tx${data.tx.hash}">
<div class="${css.log}">
<div class="${css.log}" onclick=${e => txDetails(e, tx, data, obj)}>
<i class="${css.caret} fa fa-caret-right"></i>
${context(self, {from, to, data})}
<div class=${css.buttons}>
<button class=${css.details} onclick=${txDetails}>Details</button>
<button class=${css.debug} onclick=${debug}>Debug</button>
<div class=${css.debug} onclick=${(e) => debug(e, data, self)}>Debug</div>
</div>
</div>
</span>
`
var table
function txDetails () {
if (table && table.parentNode) {
tx.removeChild(table)
} else {
table = createTable({
contractAddress: data.tx.contractAddress,
data: data.tx,
from,
to,
gas: data.tx.gas,
hash: data.tx.hash,
input: data.tx.input,
'decoded input': data.resolvedData && data.resolvedData.params ? JSON.stringify(typeConversion.stringify(data.resolvedData.params), null, '\t') : ' - ',
'decoded output': data.resolvedData && data.resolvedData.decodedReturnValue ? JSON.stringify(typeConversion.stringify(data.resolvedData.decodedReturnValue), null, '\t') : ' - ',
logs: data.logs,
val: data.tx.value,
transactionCost: data.tx.transactionCost,
executionCost: data.tx.executionCost,
status: data.tx.status
})
tx.appendChild(table)
}
}
return tx
}
function renderCall (self, data) {
function debug () {
if (data.tx.envMode === 'vm') {
self.event.trigger('debugRequested', [data.tx.hash])
} else {
modalDialog.alert('Cannot debug this call. Debugging calls is only possible in JavaScript VM mode.')
}
}
var to = data.resolvedData.contractName + '.' + data.resolvedData.fn
var from = data.tx.from ? data.tx.from : ' - '
var input = data.tx.input ? helper.shortenHexData(data.tx.input) : ''
var obj = {from, to}
var tx = yo`
<span id="tx${data.tx.hash}">
<div class="${css.log}">
<span class=${css.txLog}><span class=${css.tx}>[call]</span> from:${from}, to:${to}, data:${input}, return: </span>
<div class="${css.log}" onclick=${e => txDetails(e, tx, data, obj)}>
<i class="${css.caret} fa fa-caret-right"></i>
<span class=${css.txLog}>
<span class=${css.tx}>[call]</span>
<div class=${css.txItem}><span class=${css.txItemTitle}>from:</span> ${from}</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>to:</span> ${to}</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>data:</span> ${input}</div>
</span>
<div class=${css.buttons}>
<button class=${css.details} onclick=${txDetails}>Details</button>
<button class=${css.debug} onclick=${debug}>Debug</button>
<div class=${css.debug} onclick=${(e) => debug(e, data, self)}>Debug</div>
</div>
</div>
<div> ${JSON.stringify(typeConversion.stringify(data.resolvedData.decodedReturnValue), null, '\t')}</div>
</span>
`
var table
function txDetails () {
if (table && table.parentNode) {
tx.removeChild(table)
} else {
table = createTable({
isCall: data.tx.isCall,
contractAddress: data.tx.contractAddress,
data: data.tx,
from,
to,
gas: data.tx.gas,
input: data.tx.input,
'decoded input': data.resolvedData && data.resolvedData.params ? JSON.stringify(typeConversion.stringify(data.resolvedData.params), null, '\t') : ' - ',
'decoded output': data.resolvedData && data.resolvedData.decodedReturnValue ? JSON.stringify(typeConversion.stringify(data.resolvedData.decodedReturnValue), null, '\t') : ' - ',
logs: data.logs,
val: data.tx.value,
transactionCost: data.tx.transactionCost,
executionCost: data.tx.executionCost
})
tx.appendChild(table)
}
}
return tx
}
function renderUnknownTransaction (self, data) {
var from = data.tx.from
var to = data.tx.to
function debug () {
self.event.trigger('debugRequested', [data.tx.hash])
}
var obj = {from, to}
var tx = yo`
<span id="tx${data.tx.hash}">
<div class="${css.log}">
<i class="${css.caret} fa fa-caret-right"></i>
<div class="${css.log}" onclick=${e => txDetails(e, tx, data, obj)}>
${context(self, {from, to, data})}
<div class=${css.buttons}>
<button class=${css.details} onclick=${txDetails}>Details</button>
<button class=${css.debug} onclick=${debug}>Debug</button>
<div class=${css.debug} onclick=${(e) => debug(e, data, self)}>Debug</div>
</div>
</div>
</span>
`
var table
function txDetails () {
if (table && table.parentNode) {
tx.removeChild(table)
} else {
table = createTable({
data: data.tx,
from,
to,
val: data.tx.value,
input: data.tx.input,
hash: data.tx.hash,
gas: data.tx.gas,
logs: data.tx.logs,
transactionCost: data.tx.transactionCost,
executionCost: data.tx.executionCost,
status: data.tx.status
})
tx.appendChild(table)
}
}
return tx
}
function renderEmptyBlock (self, data) {
return yo`<span class=${css.txLog}><span class='${css.tx}'>[block:${data.block.number} - 0 transactions]</span></span>`
return yo`
<span class=${css.txLog}>
<span class='${css.tx}'><div class=${css.txItem}>[<span class=${css.txItemTitle}>block:${data.block.number} - </span> 0 transactions]</span></span>
</span>`
}
function context (self, opts) {
@ -300,13 +269,44 @@ function context (self, opts) {
var i = data.tx.transactionIndex
var value = val ? typeConversion.toInt(val) : 0
if (executionContext.getProvider() === 'vm') {
return yo`<span class=${css.txLog}><span class=${css.tx}>[vm]</span> from:${from}, to:${to}, value:${value} wei, data:${input}, ${logs} logs, hash:${hash}</span>`
return yo`
<div>
<span class=${css.txLog}>
<span class=${css.tx}>[vm]</span>
<div class=${css.txItem}><span class=${css.txItemTitle}>from:</span> ${from}</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>to:</span> ${to}</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>value:</span> ${value} wei</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>data:</span> ${input}</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>logs:</span> ${logs}</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>hash:</span> ${hash}</div>
</span>
</div>`
} else if (executionContext.getProvider() !== 'vm' && data.resolvedData) {
return yo`<span class=${css.txLog}><span class='${css.tx}'>[block:${block} txIndex:${i}]</span> from:${from}, to:${to}, value:${value} wei, ${logs} logs, data:${input}, hash:${hash}</span>`
return yo`
<div>
<span class=${css.txLog}>
<span class='${css.tx}'>[block:${block} txIndex:${i}]</span>
<div class=${css.txItem}><span class=${css.txItemTitle}>from:</span> ${from}</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>to:</span> ${to}</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>value:</span> ${value} wei</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>data:</span> ${input}</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>logs:</span> ${logs}</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>hash:</span> ${hash}</div>
</span>
</div>`
} else {
to = helper.shortenHexData(to)
hash = helper.shortenHexData(data.tx.blockHash)
return yo`<span class=${css.txLog}><span class='${css.tx}'>[block:${block} txIndex:${i}]</span> from:${from}, to:${to}, value:${value} wei</span>`
return yo`
<div>
<i class="${css.caret} fa fa-caret-right"></i>
<span class=${css.txLog}>
<span class='${css.tx}'>[block:${block} txIndex:${i}]</span>
<div class=${css.txItem}><span class=${css.txItemTitle}>from:</span> ${from}</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>to:</span> ${to}</div>
<div class=${css.txItem}><span class=${css.txItemTitle}>value:</span> ${value} wei</div>
</span>
</div>`
}
}
@ -314,6 +314,40 @@ module.exports = TxLogger
// helpers
function txDetails (e, tx, data, obj) {
var table = document.querySelector(`#${tx.id} [class^="txTable"]`)
var from = obj.from
var to = obj.to
var log = document.querySelector(`#${tx.id} [class^='log']`)
var caret = document.querySelector(`#${tx.id} [class^='caret']`)
var caretDown = yo`<i class="${css.caret} fa fa-caret-down"></i>`
var caretRight = yo`<i class="${css.caret} fa fa-caret-right"></i>`
if (table && table.parentNode) {
tx.removeChild(table)
log.removeChild(caret)
log.appendChild(caretRight)
} else {
log.removeChild(caret)
log.appendChild(caretDown)
table = createTable({
isCall: data.tx.isCall,
contractAddress: data.tx.contractAddress,
data: data.tx,
from,
to,
gas: data.tx.gas,
input: data.tx.input,
'decoded input': data.resolvedData && data.resolvedData.params ? JSON.stringify(typeConversion.stringify(data.resolvedData.params), null, '\t') : ' - ',
'decoded output': data.resolvedData && data.resolvedData.decodedReturnValue ? JSON.stringify(typeConversion.stringify(data.resolvedData.decodedReturnValue), null, '\t') : ' - ',
logs: data.logs,
val: data.tx.value,
transactionCost: data.tx.transactionCost,
executionCost: data.tx.executionCost
})
tx.appendChild(table)
}
}
function createTable (opts) {
var table = yo`<table class="${css.txTable}" id="txTable"></table>`

@ -31,7 +31,7 @@ var css = csjs`
}
.clear {
margin-left : 10px;
margin-right : 10px;
margin-right : 10px;
width : 10px;
cursor : pointer;
color : ${styles.terminal.icon_Color_TogglePanel};
@ -105,7 +105,7 @@ var css = csjs`
margin-right : 0.5em;
font-family : monospace;
font-weight : bold;
font-size : large;
font-size : 14px;
color : ${styles.appProperties.supportText_OppositeColor};
}
.input {

@ -419,7 +419,7 @@ function settings (container, appAPI, appEvents, opts) {
<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>
<i class="fa fa-plus-square-o ${css.createAccount} ${css.icon}" aria-hidden="true" onclick=${newAccount} title="Create a new account"></i>
</div>
<div class="${css.crow}">
<div class="${css.col1_1}">Gas limit</div>

@ -47,7 +47,7 @@ UniversalDAppUI.prototype.renderInstanceFromABI = function (contractABI, address
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.append(close)
instance.appendChild(close)
}
function toggleClass () {

@ -132,7 +132,7 @@ function testFunction (fnFullName, txHash, log, expectedInput, expectedReturn, e
.pause(500)
.waitForElementPresent('#editor-container div[class^="terminal"] span[id="tx' + txHash + '"]')
.assert.containsText('#editor-container div[class^="terminal"] span[id="tx' + txHash + '"] span', log)
.click('#editor-container div[class^="terminal"] span[id="tx' + txHash + '"] button[class^="details"]')
.click('#editor-container div[class^="terminal"] span[id="tx' + txHash + '"] div[class^="log"]')
.perform(function (client, done) {
if (expectedReturn) {
client.assert.containsText('#editor-container div[class^="terminal"] span[id="tx' + txHash + '"] table[class^="txTable"] #decodedoutput', expectedReturn)

@ -2,10 +2,11 @@ module.exports = function (browser, callback) {
browser
.url('http://127.0.0.1:8080/#version=builtin')
.injectScript('test-browser/helpers/applytestmode.js', function () {
browser.resizeWindow(2560, 1440, callback)
.click('#autoCompile')
.perform(function () {
callback()
})
browser.resizeWindow(2560, 1440, () => {
browser.click('#autoCompile')
.perform(function () {
callback()
})
})
})
}

@ -37,10 +37,10 @@ function runTests (browser, testData) {
.setValue('input[placeholder="uint8 _numProposals"]', '1')
.click('#runTabView div[class^="create"]')
.testFunction('delegate - transact (not payable)', '0x0571a2439ea58bd349dd130afb8aff62a33af14c06de0dbc3928519bdf13ce2e',
'[vm] from:0xca3...a733c, to:Ballot.delegate(address) 0x692...77b3a, value:0 wei, data:0x5c1...4d2db, 0 logs, hash:0x057...3ce2e',
`[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)
.pause(500)
.click('span#tx0x0571a2439ea58bd349dd130afb8aff62a33af14c06de0dbc3928519bdf13ce2e button[class^="debug"]')
.click('span#tx0x0571a2439ea58bd349dd130afb8aff62a33af14c06de0dbc3928519bdf13ce2e div[class^="debug"]')
.pause(1000)
.click('#jumppreviousbreakpoint')
.click('#stepdetail .title .fa')
@ -86,7 +86,7 @@ function runTests (browser, testData) {
.perform((client, done) => {
console.log('delegate - transact (not payable)')
browser.testFunction('delegate - transact (not payable)', '0xd3cd54e2f76f3993078ecf9e1b54a148def4520afc141a182293b3610bddf10f',
'[vm] from:0xca3...a733c, to:Ballot.delegate(address) 0x692...77b3a, value:0 wei, data:0x5c1...4d2db, 0 logs, hash:0xd3c...df10f',
`[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()
}

@ -44,7 +44,7 @@ function testSimpleContract (browser, callback) {
.click('#runTabView .instance div[class^="title"]')
.testFunction('f - transact (not payable)',
'0xa178c603400a184ce5fedbcfab392d9b77822f6ffa7facdec693aded214523bc',
'[vm] from:0xca3...a733c, to:TestContract.f() 0x692...77b3a, value:0 wei, data:0x261...21ff0, 0 logs, hash:0xa17...523bc', null,
`[vm]\nfrom:0xca3...a733c\nto:TestContract.f() 0x692...77b3a\nvalue:0 wei\ndata:0x261...21ff0\nlogs:0\nhash:0xa17...523bc`, null,
`{
"0": "uint256: 8"
}`)
@ -56,12 +56,12 @@ function testSimpleContract (browser, callback) {
})
.testFunction('g - transact (not payable)',
'0xb1532162e2e31397dc1e07ed0a1cf08f728e9b4487c6f9ed79d2f39410c92781',
'[vm] from:0xca3...a733c, to:TestContract.g() 0x692...77b3a, value:0 wei, data:0xe21...79b8e, 0 logs, hash:0xb15...92781', null, `{
`[vm]\nfrom:0xca3...a733c\nto:TestContract.g() 0x692...77b3a\nvalue:0 wei\ndata:0xe21...79b8e\nlogs:0\nhash:0xb15...92781`, null, `{
"0": "uint256: 345",
"1": "string: comment_comment_",
"2": "bool: true",
"3": "uint256: 4"
}`).perform(() => { callback(null, browser) })
}`).click('i[class^="clearinstance"]').perform(() => { callback(null, browser) })
})
}
@ -72,7 +72,7 @@ function testReturnValues (browser, callback) {
.pause(500)
.testFunction('retunValues1 - transact (not payable)',
'0x79dc928d149d2ade02ab610a8ae290636222d034d4adce0bb08a68401e3d1f7f',
'[vm] from:0xca3...a733c, to:testReturnValues.retunValues1() 0x5e7...26e9f, value:0 wei, data:0x9ed...59eb7, 0 logs, hash:0x79d...d1f7f',
`[vm]\nfrom:0xca3...a733c\nto:testReturnValues.retunValues1() 0x5e7...26e9f\nvalue:0 wei\ndata:0x9ed...59eb7\nlogs:0\nhash:0x79d...d1f7f`,
null,
`{
"0": "bool: _b true",
@ -83,7 +83,7 @@ function testReturnValues (browser, callback) {
.pause(500)
.testFunction('retunValues2 - transact (not payable)',
'0x09175dcb30227b3af422d75786dbba3b0549985e5c7f59f86d12c7e1043ccb8c',
'[vm] from:0xca3...a733c, to:testReturnValues.retunValues2() 0x5e7...26e9f, value:0 wei, data:0xf57...4036c, 0 logs, hash:0x091...ccb8c', null, `{
`[vm]\nfrom:0xca3...a733c\nto:testReturnValues.retunValues2() 0x5e7...26e9f\nvalue:0 wei\ndata:0xf57...4036c\nlogs:0\nhash:0x091...ccb8c`, null, `{
"0": "bytes1: _b 0x12",
"1": "bytes2: _b2 0x1223",
"2": "bytes3: _b3 0x000000",
@ -96,10 +96,10 @@ function testReturnValues (browser, callback) {
"9": "bytes32: _b32 0x0000000000000000000000000000000000032523532532523532523532523532"
}`).pause(500).testFunction('retunValues3 - transact (not payable)',
'0x7faab07aeaafc8afe6bf283bb83be70c000dff381dec04e779354e354da14aff',
'[vm] from:0xca3...a733c, to:testReturnValues.retunValues3() 0x5e7...26e9f, value:0 wei, data:0x033...e0a7d, 0 logs, hash:0x7fa...14aff', null, `{
'[vm]\nfrom:0xca3...a733c\nto:testReturnValues.retunValues3() 0x5e7...26e9f\nvalue:0 wei\ndata:0x033...e0a7d\nlogs:0\nhash:0x7fa...14aff', null, `{
"0": "uint8: _en 2",
"1": "int256[5][]: _a1 1,-45,-78,56,60, -1,42,334,-45455,-446, 1,10,-5435,45,-7"
}`).perform(() => { callback(null, browser) })
}`).click('i[class^="clearinstance"]').perform(() => { callback(null, browser) })
})
}
@ -110,7 +110,7 @@ function testInputValues (browser, callback) {
.pause(500)
.testFunction('inputValue1 - transact (not payable)',
'0x917a873d27d105213eaf5461e14780387ccceb66fed574f8432d1963917832ae',
'[vm] from:0xca3...a733c, to:test.inputValue1(uint256,int256,string) 0x8c1...401f5, value:0 wei, data:0xd69...00000, 0 logs, hash:0x917...832ae',
`[vm]\nfrom:0xca3...a733c\nto:test.inputValue1(uint256,int256,string) 0x8c1...401f5\nvalue:0 wei\ndata:0xd69...00000\nlogs:0\nhash:0x917...832ae`,
{types: 'uint256 _u, int256 _i, string _str', values: '"2343242", "-4324324", "string _ string _ string _ string _ string _ string _ string _ string _ string _ string _"'},
`{
"0": "uint256: _uret 2343242",
@ -118,7 +118,7 @@ function testInputValues (browser, callback) {
"2": "string: _strret string _ string _ string _ string _ string _ string _ string _ string _ string _ string _"
}`).pause(500).testFunction('inputValue2 - transact (not payable)',
'0x487d09e244853bcb108b3a22cd6ee57b6431e50869619c9b918e9764fc16ef7f',
'[vm] from:0xca3...a733c, to:test.inputValue2(uint256[3],bytes8[4]) 0x8c1...401f5, value:0 wei, data:0x1b7...00000, 1 logs, hash:0x487...6ef7f',
`[vm]\nfrom:0xca3...a733c\nto:test.inputValue2(uint256[3],bytes8[4]) 0x8c1...401f5\nvalue:0 wei\ndata:0x1b7...00000\nlogs:1\nhash:0x487...6ef7f`,
{types: 'uint256[3] _n, bytes8[4] _b8', values: '[1,2,3], ["0x1234", "0x1234","0x1234","0x1234"]'},
`{
"0": "uint256[3]: _nret 1, 2, 3",
@ -136,7 +136,7 @@ function testInputValues (browser, callback) {
]
}
]`)
.perform(() => { callback(null, browser) })
.click('i[class^="clearinstance"]').perform(() => { callback(null, browser) })
})
}

Loading…
Cancel
Save