Merge pull request #50 from yann300/browserTests

browser tests
pull/7/head
yann300 9 years ago committed by GitHub
commit 7fad9dc864
  1. 1
      .gitignore
  2. 6
      .travis.yml
  3. 36
      ci/browser_tests.sh
  4. 72
      ci/sauceDisconnect.js
  5. 97
      nightwatch.js
  6. 15
      package.json
  7. 4
      src/ASMCode.js
  8. 5
      src/BasicPanel.js
  9. 29
      src/ButtonNavigator.js
  10. 6
      src/CalldataPanel.js
  11. 6
      src/CallstackPanel.js
  12. 6
      src/MemoryPanel.js
  13. 18
      src/Slider.js
  14. 6
      src/StackPanel.js
  15. 14
      src/Sticker.js
  16. 6
      src/StoragePanel.js
  17. 42
      src/TxBrowser.js
  18. 27
      src/VmDebugger.js
  19. 4
      src/code/codeUtils.js
  20. 7
      src/index.js
  21. 7
      test-browser/index.js
  22. 195
      test-browser/init.js
  23. 61
      test-browser/sauce.js
  24. 173
      test-browser/vmdebugger.js
  25. 38
      test/TestTraceRetriever.js
  26. 16
      test/codeManager.js
  27. 31
      test/init.js
  28. 1
      test/resources/contractInvokationCode.js
  29. 2
      test/resources/contractInvokationTrace.js
  30. 2
      test/resources/contractInvokationTx.js
  31. 43
      test/resources/testWeb3.js
  32. 17
      test/resources/testWeb3.json
  33. 12
      test/traceManager.js

1
.gitignore vendored

@ -3,3 +3,4 @@ node_modules
npm-debug.log
lint.xml
.vscode
test-browser/reports/*

@ -1,7 +1,9 @@
language: node_js
node_js:
- stable
script: npm run test && npm run build
- stable
script:
- npm run test
- bash ci/browser_tests.sh
deploy:
provider: script
script: ci/deploy_from_travis.sh

@ -0,0 +1,36 @@
#!/bin/bash
SAUCECONNECT_URL="http://saucelabs.com/downloads/sc-4.3.16-linux.tar.gz"
SAUCECONNECT_USERNAME="yann300"
SAUCECONNECT_ACCESSKEY="e6f430f2-daa0-48bb-90fd-8bee20f429eb"
SAUCECONNECT_JOBIDENTIFIER="remix_tests_${TRAVIS_JOB_NUMBER}"
SAUCECONNECT_READYFILE="sc.ready"
TEST_EXITCODE=0
npm run build
npm run serve &
wget $SAUCECONNECT_URL
tar -zxvf sc-4.3.16-linux.tar.gz
./sc-4.3.16-linux/bin/sc -u $SAUCECONNECT_USERNAME -k $SAUCECONNECT_ACCESSKEY -i $SAUCECONNECT_JOBIDENTIFIER --readyfile $SAUCECONNECT_READYFILE &
while [ ! -f $SAUCECONNECT_READYFILE ]; do
sleep .5
done
npm run nightwatch_remote_firefox
npm run nightwatch_remote_chrome
npm run nightwatch_remote_safari
npm run nightwatch_remote_ie
if [ $? -eq 1 ]
then
TEST_EXITCODE=1
fi
node ci/sauceDisconnect.js $SAUCECONNECT_USERNAME $SAUCECONNECT_ACCESSKEY $SAUCECONNECT_JOBIDENTIFIER
if [ $TEST_EXITCODE -eq 1 ]
then
exit 1
fi

@ -0,0 +1,72 @@
const https = require('https')
var userName = process.argv[2]
var accessKey = process.argv[3]
var tunnelName = process.argv[4]
function removeTunnel () {
const requestPath = `/rest/v1/${userName}/tunnels`
console.log(requestPath)
callSauce(requestPath, 'GET', function (error, result) {
if (error) {
console.log(error)
} else {
var data = JSON.parse(result)
for (var k in data) {
retrieveTunnel(data[k], function (error, result) {
if (error) {
console.log(error)
} else if (result.identtifier === tunnelName) {
deleteTunnel(result.id, function () {
console.log('tunnel deleted ' + data[k] + ' ' + tunnelName)
})
}
})
}
}
})
}
function retrieveTunnel (tunnelid, callback) {
const requestPath = `/rest/v1/${userName}/tunnels/${tunnelid}`
callSauce(requestPath, 'GET', function (error, result) {
if (error) {
callback(error)
} else {
callback(null, {'identtifier': JSON.parse(result).tunnel_identifier, 'id': tunnelid})
}
})
}
function deleteTunnel (tunnelid, callback) {
const requestPath = `/rest/v1/${userName}/tunnels/${tunnelid}`
callSauce(requestPath, 'DELETE', callback)
}
function callSauce (requestPath, type, callback) {
function responseCallback (res) {
res.setEncoding('utf8')
console.log('Response: ', res.statusCode, JSON.stringify(res.headers))
res.on('data', function onData (chunk) {
console.log('BODY: ' + chunk)
callback(null, chunk)
})
res.on('end', function onEnd () {})
}
var req = https.request({
hostname: 'saucelabs.com',
path: requestPath,
method: type,
auth: userName + ':' + accessKey
}, responseCallback)
req.on('error', function onError (e) {
console.log('problem with request: ' + e.message)
callback(e.message)
})
req.write('')
req.end()
}
removeTunnel()

@ -0,0 +1,97 @@
'use strict'
var TRAVIS_JOB_NUMBER = process.env.TRAVIS_JOB_NUMBER
module.exports = {
'src_folders': ['./test-browser'],
'output_folder': './test-browser/reports',
'custom_commands_path': '',
'custom_assertions_path': '',
'globals_path': '',
'page_objects_path': '',
'selenium': {
'start_process': false,
'server_path': '',
'log_path': '',
'host': '127.0.0.1',
'port': 4444,
'cli_args': {
'webdriver.chrome.driver': '',
'webdriver.ie.driver': '',
'webdriver.firefox.profile': ''
}
},
'test_settings': {
'default': {
'launch_url': 'http://ondemand.saucelabs.com:80',
'selenium_host': 'ondemand.saucelabs.com',
'selenium_port': 80,
'silent': true,
'username': 'yann300',
'access_key': 'e6f430f2-daa0-48bb-90fd-8bee20f429eb',
'use_ssl': false,
'globals': {
'waitForConditionTimeout': 10000,
'asyncHookTimeout': 100000
},
'screenshots': {
'enabled': false,
'path': ''
},
'desiredCapabilities': {
'browserName': 'firefox',
'javascriptEnabled': true,
'acceptSslCerts': true,
'build': 'build-' + TRAVIS_JOB_NUMBER,
'tunnel-identifier': 'remix_tests_' + TRAVIS_JOB_NUMBER
}
},
'chrome': {
'desiredCapabilities': {
'browserName': 'chrome',
'javascriptEnabled': true,
'acceptSslCerts': true,
'build': 'build-' + TRAVIS_JOB_NUMBER,
'tunnel-identifier': 'remix_tests_' + TRAVIS_JOB_NUMBER
}
},
'safari': {
'desiredCapabilities': {
'browserName': 'safari',
'javascriptEnabled': true,
'acceptSslCerts': true,
'build': 'build-' + TRAVIS_JOB_NUMBER,
'tunnel-identifier': 'remix_tests_' + TRAVIS_JOB_NUMBER
}
},
'ie': {
'desiredCapabilities': {
'browserName': 'internet explorer',
'javascriptEnabled': true,
'acceptSslCerts': true,
'build': 'build-' + TRAVIS_JOB_NUMBER,
'tunnel-identifier': 'remix_tests_' + TRAVIS_JOB_NUMBER
}
},
'local': {
'launch_url': 'http://localhost',
'selenium_host': '127.0.0.1',
'selenium_port': 4444,
'silent': true,
'screenshots': {
'enabled': false,
'path': ''
},
'desiredCapabilities': {
'browserName': 'firefox',
'javascriptEnabled': true,
'acceptSslCerts': true
}
}
}
}

@ -19,16 +19,23 @@
"yo-yo": "^1.2.1"
},
"devDependencies": {
"nightwatch": "^0.9.4",
"http-server": "^0.9.0",
"nightwatch": "^0.9.5",
"standard": "^7.0.1",
"standard-reporter": "^1.0.5",
"tape": "^4.6.0"
"tape": "^4.6.0",
"yo-yoify": "^3.1.0"
},
"scripts": {
"start_node": "eth --rpccorsdomain \"*\" -j -v 0",
"build": "mkdir -p build; browserify src/index.js -o build/app.js",
"build": "mkdir -p build; browserify src/index.js -g yo-yoify -o build/app.js",
"test": "standard && tape ./test/tests.js",
"serve": "http-server ."
"serve": "http-server .",
"nightwatch_local": "nightwatch --config nightwatch.js --env local",
"nightwatch_remote_firefox": "nightwatch --config nightwatch.js --env default",
"nightwatch_remote_chrome": "nightwatch --config nightwatch.js --env chrome",
"nightwatch_remote_safari": "nightwatch --config nightwatch.js --env safari",
"nightwatch_remote_ie": "nightwatch --config nightwatch.js --env ie"
},
"repository": {
"type": "git",

@ -51,9 +51,7 @@ ASMCode.prototype.codeChanged = function (code, address, index) {
ASMCode.prototype.renderAssemblyItems = function () {
if (this.code) {
this.codeView = this.code.map(function (item, i) {
return yo`<option key=${i} value=${i}>
${item}
</option>`
return yo`<option key=${i} value=${i}>${item}</option>`
})
return this.codeView
}

@ -3,9 +3,10 @@ var style = require('./styles/basicStyles')
var yo = require('yo-yo')
var ui = require('./helpers/ui')
function BasicPanel (_name) {
function BasicPanel (_name, _id) {
this.data
this.name = _name
this.id = _id
this.view
}
@ -20,7 +21,7 @@ BasicPanel.prototype.render = function () {
${this.name}
</div>
<div style=${ui.formatCss(style.panel.tableContainer)}>
<pre style=${ui.formatCss(style.panel.table, style.font)} >${this.data}</pre>
<pre style=${ui.formatCss(style.panel.table, style.font)} id='${this.id}basicpanel' >${this.data}</pre>
</div>
</div>`
if (!this.view) {

@ -21,19 +21,19 @@ module.exports = ButtonNavigator
ButtonNavigator.prototype.render = function () {
var self = this
var view = yo`<div>
<button ref='intoback' onclick=${function () { self.trigger('stepIntoBack') }} disabled=${this.intoBackDisabled} >
<button id='intoback' onclick=${function () { self.trigger('stepIntoBack') }} disabled=${this.intoBackDisabled} >
Step Into Back
</button>
<button ref='overback' onclick=${function () { self.trigger('stepOverBack') }} disabled=${this.overBackDisabled} >
<button id='overback' onclick=${function () { self.trigger('stepOverBack') }} disabled=${this.overBackDisabled} >
Step Over Back
</button>
<button ref='overforward' onclick=${function () { self.trigger('stepOverForward') }} disabled=${this.overForwardDisabled} >
<button id='overforward' onclick=${function () { self.trigger('stepOverForward') }} disabled=${this.overForwardDisabled} >
Step Over Forward
</button>
<button ref='intoforward' onclick=${function () { self.trigger('stepIntoForward') }} disabled=${this.intoForwardDisabled} >
<button id='intoforward' onclick=${function () { self.trigger('stepIntoForward') }} disabled=${this.intoForwardDisabled} >
Step Into Forward
</button>
<button ref='nextcall' onclick=${function () { self.trigger('jumpNextCall') }} disabled=${this.nextCallDisabled} >
<button id='nextcall' onclick=${function () { self.trigger('jumpNextCall') }} disabled=${this.nextCallDisabled} >
Jump Next Call
</button>
</div>`
@ -65,9 +65,26 @@ ButtonNavigator.prototype.stepChanged = function (step) {
self.overForwardDisabled = step >= length - 1
self.nextCallDisabled = step >= length - 1
}
self.updateAll()
})
}
yo.update(this.view, this.render())
this.updateAll()
}
ButtonNavigator.prototype.updateAll = function () {
this.updateDisabled('intoback', this.intoBackDisabled)
this.updateDisabled('overback', this.overBackDisabled)
this.updateDisabled('overforward', this.overForwardDisabled)
this.updateDisabled('intoforward', this.intoForwardDisabled)
this.updateDisabled('nextcall', this.nextCallDisabled)
}
ButtonNavigator.prototype.updateDisabled = function (id, disabled) {
if (disabled) {
document.getElementById(id).setAttribute('disabled', true)
} else {
document.getElementById(id).removeAttribute('disabled')
}
}
module.exports = ButtonNavigator

@ -5,14 +5,12 @@ var yo = require('yo-yo')
function CalldataPanel (_parent, _traceManager) {
this.parent = _parent
this.traceManager = _traceManager
this.basicPanel = new BasicPanel('Call Data')
this.basicPanel = new BasicPanel('Call Data', 'calldatapanel')
this.init()
}
CalldataPanel.prototype.render = function () {
return (
yo`${this.basicPanel.render()}`
)
return yo`<div>${this.basicPanel.render()}</div>`
}
CalldataPanel.prototype.init = function () {

@ -5,14 +5,12 @@ var yo = require('yo-yo')
function CallstackPanel (_parent, _traceManager) {
this.parent = _parent
this.traceManager = _traceManager
this.basicPanel = new BasicPanel('Call Stack')
this.basicPanel = new BasicPanel('Call Stack', 'callstackpanel')
this.init()
}
CallstackPanel.prototype.render = function () {
return (
yo`${this.basicPanel.render()}`
)
return yo`<div>${this.basicPanel.render()}</div>`
}
CallstackPanel.prototype.init = function () {

@ -6,14 +6,12 @@ var yo = require('yo-yo')
function MemoryPanel (_parent, _traceManager) {
this.parent = _parent
this.traceManager = _traceManager
this.basicPanel = new BasicPanel('Memory')
this.basicPanel = new BasicPanel('Memory', 'memorypanel')
this.init()
}
MemoryPanel.prototype.render = function () {
return (
yo`${this.basicPanel.render()}`
)
return yo`<div>${this.basicPanel.render()}</div>`
}
MemoryPanel.prototype.init = function () {

@ -17,14 +17,14 @@ Slider.prototype.render = function () {
var self = this
var view = yo`<div>
<input
ref='rule'
id='slider'
style=${ui.formatCss(style.rule)}
type='range'
min=0
max=${this.max}
value=0
onmouseup=${function () { self.onMouseUp() }}
onkeyup=${function () { self.onChange() }}
onmouseup=${function () { self.onChange() }}
disabled=${this.disabled} />
</div>`
if (!this.view) {
@ -34,13 +34,15 @@ Slider.prototype.render = function () {
}
Slider.prototype.init = function (length) {
var slider = document.getElementById('slider')
slider.setAttribute('max', length)
this.max = length
this.updateDisabled(length === 0)
this.disabled = length === 0
yo.update(this.view, this.render())
this.setValue(0)
}
Slider.prototype.onMouseUp = function (event) {
Slider.prototype.onChange = function (event) {
var value = document.getElementById('slider').value
this.trigger('moved', [parseInt(value)])
}
@ -55,4 +57,12 @@ Slider.prototype.setValue = function (value) {
}
}
Slider.prototype.updateDisabled = function (disabled) {
if (disabled) {
document.getElementById('slider').setAttribute('disabled', true)
} else {
document.getElementById('slider').removeAttribute('disabled')
}
}
module.exports = Slider

@ -6,14 +6,12 @@ var yo = require('yo-yo')
function StackPanel (_parent, _traceManager) {
this.parent = _parent
this.traceManager = _traceManager
this.basicPanel = new BasicPanel('Stack')
this.basicPanel = new BasicPanel('Stack', 'stackpanel')
this.init()
}
StackPanel.prototype.render = function () {
return (
yo`${this.basicPanel.render()}`
)
return yo`<div>${this.basicPanel.render()}</div>`
}
StackPanel.prototype.init = function () {

@ -25,7 +25,7 @@ Sticker.prototype.render = function () {
<td>
VMtracestep:
</td>
<td>
<td id='vmtracestepinfo' >
${this.vmTraceStep}
</td>
</tr>
@ -33,7 +33,7 @@ Sticker.prototype.render = function () {
<td>
Step:
</td>
<td>
<td id='stepinfo'>
${this.step}
</td>
</tr>
@ -41,7 +41,7 @@ Sticker.prototype.render = function () {
<td>
Add memory:
</td>
<td>
<td id='addmemoryinfo'>
${this.addmemory}
</td>
</tr>
@ -49,7 +49,7 @@ Sticker.prototype.render = function () {
<td>
Gas:
</td>
<td>
<td id='gasinfo'>
${this.gas}
</td>
</tr>
@ -57,15 +57,15 @@ Sticker.prototype.render = function () {
<td>
Remaining gas:
</td>
<td>
<td id='remaininggasinfo'>
${this.remainingGas}
</td>
</tr>
<tr key='remaininggas'>
<tr key='loadedaddress'>
<td>
Loaded address:
</td>
<td>
<td id='loadedaddressinfo'>
${this.loadedAddress}
</td>
</tr>

@ -5,14 +5,12 @@ var yo = require('yo-yo')
function StoragePanel (_parent, _traceManager) {
this.parent = _parent
this.traceManager = _traceManager
this.basicPanel = new BasicPanel('Storage Changes')
this.basicPanel = new BasicPanel('Storage Changes', 'storagepanel')
this.init()
}
StoragePanel.prototype.render = function () {
return (
yo`${this.basicPanel.render()}`
)
return yo`<div>${this.basicPanel.render()}</div>`
}
StoragePanel.prototype.init = function () {

@ -33,7 +33,7 @@ TxBrowser.prototype.setDefaultValues = function () {
this.blockNumber = null
this.txNumber = '0xcda2b2835add61af54cf83bd076664d98d7908c6cd98d86423b3b48d8b8e51ff'
this.connectInfo = ''
this.checkWeb3()
this.updateWeb3Url(this.web3.currentProvider.host)
}
TxBrowser.prototype.submit = function () {
@ -70,19 +70,27 @@ TxBrowser.prototype.submit = function () {
yo.update(this.view, this.render())
}
TxBrowser.prototype.updateWeb3Url = function (ev) {
init.setProvider(this.web3, ev.target.value)
this.checkWeb3()
yo.update(this.view, this.render())
TxBrowser.prototype.updateWeb3Url = function (newhost) {
init.setProvider(this.web3, newhost)
var self = this
this.checkWeb3(function (error, block) {
if (!error) {
self.connectInfo = 'Connected to ' + self.web3.currentProvider.host + '. Current block number: ' + block
} else {
self.connectInfo = 'Unable to connect to ' + self.web3.currentProvider.host + '. ' + error.message
}
yo.update(self.view, self.render())
})
}
TxBrowser.prototype.checkWeb3 = function () {
TxBrowser.prototype.checkWeb3 = function (callback) {
try {
console.log('block ' + this.web3.eth.blockNumber)
this.connectInfo = 'Connected to ' + this.web3.currentProvider.host
this.web3.eth.getBlockNumber(function (error, block) {
callback(error, block)
})
} catch (e) {
console.log(e)
this.connectInfo = e.message
callback(e.message, null)
}
}
@ -102,16 +110,16 @@ TxBrowser.prototype.init = function (ev) {
TxBrowser.prototype.render = function () {
var self = this
var view = yo`<div style=${ui.formatCss(style.container)}>
<span>Node URL: </span><input onkeyup=${function () { self.updateWeb3Url(arguments[0]) }} value=${this.web3.currentProvider ? this.web3.currentProvider.host : ' - none - '} type='text' />
<span>Node URL: </span><input onkeyup=${function () { self.updateWeb3Url(arguments[0].target.value) }} value=${this.web3.currentProvider ? this.web3.currentProvider.host : ' - none - '} type='text' />
<span>${this.connectInfo}</span>
<br />
<br />
<input onkeyup=${function () { self.updateBlockN(arguments[0]) }} type='text' placeholder=${'Block number (default 1000110)' + this.blockNumber} />
<input onkeyup=${function () { self.updateTxN(arguments[0]) }} type='text' value=${this.txNumber} placeholder=${'Transaction Number or hash (default 2) ' + this.txNumber} />
<button onclick=${function () { self.submit() }}>
Get
<input id='txinput' onkeyup=${function () { self.updateTxN(arguments[0]) }} type='text' value=${this.txNumber} placeholder=${'Transaction Number or hash (default 2) ' + this.txNumber} />
<button id='load' onclick=${function () { self.submit() }}>
Load
</button>
<button onclick=${function () { self.trigger('unloadRequested') }}>Unload</button>
<button id='unload' onclick=${function () { self.trigger('unloadRequested') }}>Unload</button>
<div style=${ui.formatCss(style.transactionInfo)}>
<table>
<tbody>
@ -119,7 +127,7 @@ TxBrowser.prototype.render = function () {
<td>
Hash:
</td>
<td>
<td id='txhash' >
${this.hash}
</td>
</tr>
@ -127,7 +135,7 @@ TxBrowser.prototype.render = function () {
<td>
From:
</td>
<td>
<td id='txfrom'>
${this.from}
</td>
</tr>
@ -135,7 +143,7 @@ TxBrowser.prototype.render = function () {
<td>
To:
</td>
<td>
<td id='txto' >
${this.to}
</td>
</tr>

@ -15,7 +15,7 @@ function VmDebugger (_parent, _traceManager, _web3) {
this.storagePanel = new StoragePanel(_parent, _traceManager)
this.memoryPanel = new MemoryPanel(_parent, _traceManager)
this.calldataPanel = new CalldataPanel(_parent, _traceManager)
this.CallstackPanel = new CallstackPanel(_parent, _traceManager)
this.callstackPanel = new CallstackPanel(_parent, _traceManager)
this.view
var self = this
_parent.register('newTraceLoaded', this, function () {
@ -27,34 +27,23 @@ function VmDebugger (_parent, _traceManager, _web3) {
}
VmDebugger.prototype.render = function () {
var view = yo`<div style='display:none'>
var view = yo`<div id='vmdebugger' style='display:none'>
<div style=${ui.formatCss(style.container)}>
<table>
<tbody>
<tr>
<td>
<td>
${this.asmCode.render()}
</td>
<td>
${this.stackPanel.render()}
</td>
${this.stackPanel.render()}
</tr>
<tr>
<td>
${this.storagePanel.render()}
</td>
<td>
${this.memoryPanel.render()}
</td>
${this.storagePanel.render()}
${this.memoryPanel.render()}
</tr>
<tr>
<td>
${this.calldataPanel.render()}
</td>
<td>
${this.CallstackPanel.render()}
</td>
${this.calldataPanel.render()}
${this.callstackPanel.render()}
</tr>
</tbody>
</table>

@ -18,7 +18,9 @@ module.exports = {
i += jumpNum
}
code.push(this.pad(pc, this.roundLog(raw.length, 10)) + ' ' + curOpCode + ' ' + pushData.toString('hex'))
var data = pushData.toString('hex') !== '' ? ' ' + pushData.toString('hex') : ''
code.push(this.pad(pc, this.roundLog(raw.length, 10)) + ' ' + curOpCode + data)
pushData = ''
}
return [ code, codeMap ]

@ -1,9 +1,8 @@
'use strict'
var Debugger = require('./Ethdebugger')
function init () {
var ethdebugger = new Debugger()
document.getElementById('app').appendChild(ethdebugger.render())
var container = document.getElementById('app')
container.vmdebugger = new Debugger()
container.appendChild(container.vmdebugger.render())
}
init()

@ -1,7 +0,0 @@
module.exports = {
'Page Load': function (browser) {
browser
.url('http://127.0.0.1:8080')
.end()
}
}

@ -0,0 +1,195 @@
var init = require('../test/init')
module.exports = function (browser, callback) {
extendBrowser(browser)
browser
.url('http://127.0.0.1:8080')
.waitForElementPresent('#app div', 1000)
injectScript('./test/resources/testWeb3.json', browser, function () {
callback()
})
}
function injectScript (file, browser, callback) {
init.readFile(file, function (error, result) {
if (!error) {
browser.execute(function (data) {
var vmdebugger = document.getElementById('app').vmdebugger
data = JSON.parse(data)
vmdebugger.web3.eth.getCode = function (address, callback) {
if (callback) {
callback(null, data.testCodes[address])
} else {
return data.testCodes[address]
}
}
vmdebugger.web3.debug.traceTransaction = function (txHash, options, callback) {
callback(null, data.testTraces[txHash])
}
vmdebugger.web3.debug.storageAt = function (blockNumber, txIndex, address, callback) {
callback(null, {})
}
vmdebugger.web3.eth.getTransaction = function (txHash, callback) {
if (callback) {
callback(null, data.testTxs[txHash])
} else {
return data.testTxs[txHash]
}
}
vmdebugger.web3.eth.getTransactionFromBlock = function (blockNumber, txIndex, callback) {
if (callback) {
callback(null, data.testTxsByBlock[blockNumber + '-' + txIndex])
} else {
return data.testTxsByBlock[blockNumber + '-' + txIndex]
}
}
vmdebugger.web3.eth.getBlockNumber = function (callback) { callback(null, 'web3 modified for testing purposes :)') }
vmdebugger.web3.currentProvider = {host: 'web3 modified for testing purposes :)'}
}, [result], function () {
callback()
})
}
})
}
function extendBrowser (browser) {
browser.assertCurrentSelectedItem = function (expected) {
browser.getValue('#asmitems', function (result) {
browser.expect.element('#asmitems option[value="' + result.value + '"]').text.to.equal(expected)
})
return browser
}
browser.assertSticker = function (vmtracestepinfo, stepinfo, addmemoryinfo, gasinfo, remaininggasinfo, loadedaddressinfo) {
browser.expect.element('#vmtracestepinfo').text.to.equal(vmtracestepinfo)
browser.expect.element('#stepinfo').text.to.equal(stepinfo)
browser.expect.element('#addmemoryinfo').text.to.equal(addmemoryinfo)
browser.expect.element('#gasinfo').text.to.equal(gasinfo)
browser.expect.element('#remaininggasinfo').text.to.equal(remaininggasinfo)
browser.expect.element('#loadedaddressinfo').text.to.equal(loadedaddressinfo)
return browser
}
browser.assertStack = function (value) {
return assertPanel('#stackpanel', browser, value)
}
browser.assertStorageChanges = function (value) {
return assertPanel('#storagepanel', browser, value)
}
browser.assertMemory = function (value) {
return assertPanel('#memorypanel', browser, value)
}
browser.assertCallData = function (value) {
return assertPanel('#calldatapanel', browser, value)
}
browser.assertCallStack = function (value) {
return assertPanel('#callstackpanel', browser, value)
}
browser.assertStackValue = function (index, value) {
return assertPanelValue('#stackpanel', browser, index, value)
}
browser.assertStorageChangesValue = function (index, value) {
return assertPanelValue('#storagepanel', browser, index, value)
}
browser.assertMemoryValue = function (index, value) {
return assertPanelValue('#memorypanel', browser, index, value)
}
browser.assertCallStackValue = function (index, value) {
return assertPanelValue('#callstackpanel', browser, index, value)
}
browser.debugerKeyCode = {
'Enter': 13,
'Up': 38,
'Down': 40,
'Right': '39',
'Left': 37,
'Esc': 27,
'SpaceBar': 32,
'Ctrl': 17,
'Alt': 18,
'Shift': 16
}
/* browser.sendKeys is not working for safari */
/* still not working properly
browser.fireEvent = function (el, key, times, callback) {
var data = {
'id': el.substring(1),
'key': key,
'times': times
}
browser.execute(function (data) {
data = JSON.parse(data)
var el = document.getElementById(data.id)
var eventObj
console.log(el)
console.log(data)
var k = 0
if (document.createEventObject) {
eventObj = document.createEventObject()
eventObj.keyCode = data.key
while (k < data.times) {
console.log('firing brfore createEventObject')
el.fireEvent('onkeypress', eventObj)
console.log('firing')
k++
}
} else if (typeof (KeyboardEvent) === 'function') {
eventObj = new KeyboardEvent('keyup')
eventObj.key = data.key
eventObj.which = data.key
while (k < data.times) {
console.log('firing brfore createEvent')
el.dispatchEvent(eventObj)
console.log('firing')
k++
}
}
}, [JSON.stringify(data)], function () {
callback()
})
}
*/
}
function assertPanel (id, browser, value) {
browser.expect.element(id + 'basicpanel').text.to.equal(value)
return browser
}
function assertPanelValue (id, browser, index, value) {
getInnerText(id + 'basicpanel', browser, function (result) {
var values
if (result.value.indexOf('\r\n') !== -1) {
values = result.value.split('\r\n')
} else if (result.value.indexOf('\n') !== -1) {
values = result.value.split('\n')
} else if (result.value.indexOf('\r') !== -1) {
values = result.value.split('\r')
}
browser.assert.equal(values[index], value)
})
return browser
}
function getInnerText (id, browser, callback) {
browser.execute(function (data) {
return document.getElementById(data).innerText
}, [id.substring(1)], function (result) {
callback(result)
})
}

@ -0,0 +1,61 @@
const https = require('https')
module.exports = function sauce (callback) {
const currentTest = this.client.currentTest
const username = this.client.options.username
const sessionId = this.client.capabilities['webdriver.remote.sessionid']
const accessKey = this.client.options.accessKey
if (!this.client.launch_url.match(/saucelabs/)) {
console.log('Not saucelabs ...')
return callback()
}
if (!username || !accessKey || !sessionId) {
console.log(this.client)
console.log('No username, accessKey or sessionId')
return callback()
}
const passed = currentTest.results.passed === currentTest.results.tests
const data = JSON.stringify({passed})
const requestPath = `/rest/v1/${username}/jobs/${sessionId}`
function responseCallback (res) {
res.setEncoding('utf8')
console.log('Response: ', res.statusCode, JSON.stringify(res.headers))
res.on('data', function onData (chunk) {
console.log('BODY: ' + chunk)
})
res.on('end', function onEnd () {
console.info('Finished updating saucelabs')
callback()
})
}
try {
console.log('Updating saucelabs', requestPath)
const req = https.request({
hostname: 'saucelabs.com',
path: requestPath,
method: 'PUT',
auth: `${username}:${accessKey}`,
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
}, responseCallback)
req.on('error', function onError (e) {
console.log('problem with request: ' + e.message)
})
req.write(data)
req.end()
} catch (error) {
console.log('Error', error)
callback()
}
}

@ -0,0 +1,173 @@
'use strict'
var init = require('./init')
var sauce = require('./sauce')
module.exports = {
beforeEach: function (browser, done) {
try {
init(browser, done)
} catch (e) {
var mes = 'error ' + e.message
console.log(mes)
done(mes)
}
},
'vmdebugger': function (browser) {
loadTrace(browser)
browser.click('#unload')
loadTraceNotFound(browser)
browser.click('#unload')
panels(browser)
browser.click('#unload')
slider(browser)
browser.click('#unload')
stepping(browser)
browser.click('#unload')
sticker(browser)
browser.end()
},
tearDown: sauce
}
function loadTrace (browser) {
browser
.clearValue('#txinput')
.setValue('#txinput', '0x20ef65b8b186ca942zcccd634f37074dde49b541c27994fc7596740ef44cfd51')
.click('#load')
.assert.containsText('#txhash', '<not found>')
return browser
}
function loadTraceNotFound (browser) {
browser
.clearValue('#txinput')
.setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
.click('#load')
.waitForElementVisible('#vmdebugger', 1000)
.expect.element('#txhash').text.to.equal('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
browser.expect.element('#txfrom').text.to.equal('0x00101c5bfa3fc8bad02c9f5fd65b069306251915')
browser.expect.element('#txto').text.to.equal('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
browser.expect.element('#txto').text.to.equal('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
browser.click('#unload')
.waitForElementNotVisible('#vmdebugger', 1000)
return browser
}
function panels (browser) {
browser
.clearValue('#txinput')
.setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
.click('#load')
.waitForElementVisible('#vmdebugger', 1000)
.click('#nextcall')
.assertStack('0x\n0x60\n0x65\n0x38\n0x55\n0x60fe47b1')
.assertStorageChanges('0x00 0x38')
.assertCallData('0x60fe47b10000000000000000000000000000000000000000000000000000000000000038')
.assertCallStack('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
.assertStackValue(1, '0x60')
.assertMemoryValue(6, '0xc0 60 60 60 40 52 60 40 51 60 20 80 60 45 83 39 81 ????R??Q????E?9?')
.assertMemoryValue(7, '0xe0 01 60 40 52 80 80 51 90 60 20 01 90 91 90 50 50 ???R??Q???????PP')
.assertMemoryValue(8, '0x100 5b 80 60 01 01 60 00 60 00 50 81 90 55 50 5b 50 ?????????P??UP?P')
.click('#intoforward') // CREATE
.assertStack('')
.assertStorageChanges('')
.assertMemory('')
.assertCallData('0x0000000000000000000000000000000000000000000000000000000000000000000000000000006060606040526040516020806045833981016040528080519060200190919050505b806001016000600050819055')
.assertCallStack('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5\n(Contract Creation - Step 63)')
return browser
}
function slider (browser) {
browser
.clearValue('#txinput')
.setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
.click('#load')
.waitForElementVisible('#vmdebugger', 1000)
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
/*
.sendKeys('#slider', browser.Keys.RIGHT_ARROW)
.sendKeys('#slider', browser.Keys.RIGHT_ARROW)
.sendKeys('#slider', browser.Keys.RIGHT_ARROW)
.sendKeys('#slider', browser.Keys.RIGHT_ARROW)
.sendKeys('#slider', browser.Keys.RIGHT_ARROW)
.sendKeys('#slider', browser.Keys.RIGHT_ARROW)
.sendKeys('#slider', browser.Keys.RIGHT_ARROW)
.sendKeys('#slider', browser.Keys.RIGHT_ARROW)
.sendKeys('#slider', browser.Keys.LEFT_ARROW)
*/
.assertCurrentSelectedItem('041 PUSH 60fe47b1')
return browser
}
function stepping (browser) {
browser
.clearValue('#txinput')
.setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
.click('#load')
.waitForElementVisible('#vmdebugger', 1000)
.click('#intoforward')
.click('#intoforward')
.assertCurrentSelectedItem('004 MSTORE')
.click('#intoforward')
.click('#intoback')
.click('#intoback')
.assertCurrentSelectedItem('002 PUSH 40')
.click('#nextcall')
.assertCurrentSelectedItem('181 CREATE')
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
.click('#overforward')
.assertCurrentSelectedItem('058 RETURN')
.click('#intoforward')
.click('#overback')
.assertCurrentSelectedItem('181 CREATE')
return browser
}
function sticker (browser) {
browser
.clearValue('#txinput')
.setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
.click('#load')
.waitForElementVisible('#vmdebugger', 1000)
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
.click('#intoforward')
/*
.fireEvent('#slider', browser.debugerKeyCode.Right, 4, function () {
browser.assertSticker('6', '6', '', '3', '84476', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
.click('#nextcall')
.assertSticker('63', '63', '', '32000', '79283', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
.click('#intoforward')
.click('#overforward')
.assertSticker('108', '44', '', '0', '27145', '(Contract Creation - Step 63)')
.click('#intoforward')
.assertSticker('109', '64', '', '3', '25145', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
.end()
})
*/
.assertSticker('6', '6', '', '3', '84476', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
.click('#nextcall')
.assertSticker('63', '63', '', '32000', '79283', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
.click('#intoforward')
.click('#overforward')
.assertSticker('108', '44', '', '0', '27145', '(Contract Creation - Step 63)')
.click('#intoforward')
.assertSticker('109', '64', '', '3', '25145', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
return browser
}

@ -1,38 +0,0 @@
'use strict'
var traceHelper = require('../src/helpers/traceHelper')
var traceInvokation = require('./resources/contractInvokationTrace')
function TestTraceRetriever () {
this.storages = {} // contains all intial storage (by addresses)
}
TestTraceRetriever.prototype.getTrace = function (txHash, callback) {
console.log(traceInvokation)
callback(null, traceInvokation)
}
/* not used */
TestTraceRetriever.prototype.getStorage = function (tx, address, callback) {
if (traceHelper.isContractCreation(address)) {
callback(null, {})
} else if (this.storages[address]) {
callback(null, this.storages[address])
} else {
/*
return storage
*/
/*
var self = this
this.web3.debug.storageAt(tx.blockNumber.toString(), tx.transactionIndex, address, function (error, result) {
self.storages[address] = result
callback(error, result)
})
*/
}
}
TestTraceRetriever.prototype.debugStorageAtAvailable = function () {
return false // test scenario does not require web3.
}
module.exports = TestTraceRetriever

@ -1,20 +1,21 @@
'use strict'
var tape = require('tape')
var init = require('../src/helpers/init')
var txInvokation = require('./resources/contractInvokationTx')
var TestTraceRetriever = require('./TestTraceRetriever')
var contractCode = require('./resources/contractInvokationCode')
var TraceManager = require('../src/trace/traceManager')
var CodeManager = require('../src/code/codeManager')
var web3Test = require('./resources/testWeb3')
var initWeb3 = require('./init')
tape('CodeManager', function (t) {
var codeManager
var web3 = init.loadWeb3()
initWeb3.overrideWeb3(web3, web3Test)
var traceManager = new TraceManager(web3)
traceManager.traceRetriever = new TestTraceRetriever()
codeManager = new CodeManager(web3, traceManager)
var contractCode = web3.eth.getCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
codeManager.codeResolver.cacheExecutingCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', contractCode) // so a call to web3 is not necessary
traceManager.resolveTrace(txInvokation, function (error, result) {
var tx = web3.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
traceManager.resolveTrace(tx, function (error, result) {
if (error) {
t.fail(' - traceManager.resolveTrace - failed ' + result)
} else {
@ -53,8 +54,9 @@ function continueTesting (t, codeManager) {
}
}
})
codeManager.resolveStep(0, txInvokation)
codeManager.resolveStep(70, txInvokation)
var tx = codeManager.web3.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
codeManager.resolveStep(0, tx)
codeManager.resolveStep(70, tx)
})
t.test('CodeManager.getInstructionIndex', function (st) {

@ -0,0 +1,31 @@
var init = {
overrideWeb3: function (web3, web3Override) {
web3.eth.getCode = web3Override.getCode
web3.debug.traceTransaction = web3Override.traceTransaction
web3.debug.storageAt = web3Override.storageAt
web3.eth.getTransaction = web3Override.getTransaction
web3.eth.getTransactionFromBlock = web3Override.getTransactionFromBlock
web3.eth.getBlockNumber = web3Override.getBlockNumber
},
readFile: function (filename, callback) {
var fs = require('fs')
try {
console.log('reading ' + filename)
if (callback) {
fs.readFile(filename, 'utf8', callback)
} else {
return fs.readFileSync(filename, 'utf8')
}
} catch (e) {
console.log(e)
if (callback) {
callback(e)
} else {
return e
}
}
}
}
module.exports = init

@ -1 +0,0 @@
module.exports = '0x60606040526000357c01000000000000000000000000000000000000000000000000000000009004806360fe47b11460415780636d4ce63c14605757603f565b005b605560048080359060200190919050506089565b005b606260048050506078565b6040518082815260200191505060405180910390f35b600060006000505490506086565b90565b80600060005081905550602d6040516045806100f083390180828152602001915050604051809103906000f0600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550602281016000600050819055505b505660606040526040516020806045833981016040528080519060200190919050505b806001016000600050819055505b50600a80603b6000396000f360606040526008565b00'

File diff suppressed because one or more lines are too long

@ -1,2 +0,0 @@
module.exports =
{'blockHash':'0xd1d34932f8733e0485b7d9bf8500c4046d650f20ed7792508c304304fa7bbfac','blockNumber':89,'from':'0x00101c5bfa3fc8bad02c9f5fd65b069306251915','gas':105967,'gasPrice':'20000000000','hash':'0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51','input':'0x60fe47b10000000000000000000000000000000000000000000000000000000000000038','nonce':3,'to':'0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5','transactionIndex':0,'value':'0'}

@ -0,0 +1,43 @@
'use strict'
var init = require('../init')
var web3Override = {}
var data = init.readFile(require('path').resolve(__dirname, 'testWeb3.json'))
var data = JSON.parse(data)
web3Override.getCode = function (address, callback) {
if (callback) {
callback(null, data.testCodes[address])
} else {
return data.testCodes[address]
}
}
web3Override.traceTransaction = function (txHash, options, callback) {
callback(null, data.testTraces[txHash])
}
web3Override.storageAt = function (blockNumber, txIndex, address, callback) {
callback(null, {})
}
web3Override.getTransaction = function (txHash, callback) {
if (callback) {
callback(null, data.testTxs[txHash])
} else {
return data.testTxs[txHash]
}
}
web3Override.getTransactionFromBlock = function (blockNumber, txIndex, callback) {
if (callback) {
callback(null, data.testTxsByBlock[blockNumber + '-' + txIndex])
} else {
return data.testTxsByBlock[blockNumber + '-' + txIndex]
}
}
web3Override.getBlockNumber = function (callback) { callback('web3 modified testing purposes :)') }
if (typeof (module) !== 'undefined' && typeof (module.exports) !== 'undefined') {
module.exports = web3Override
}

File diff suppressed because one or more lines are too long

@ -2,21 +2,22 @@
var TraceManager = require('../src/trace/traceManager')
var tape = require('tape')
var init = require('../src/helpers/init')
var TestTraceRetriever = require('./TestTraceRetriever')
var txInvokation = require('./resources/contractInvokationTx')
var web3Test = require('./resources/testWeb3')
var initWeb3 = require('./init')
tape('TraceManager', function (t) {
var traceManager
t.test('TraceManager.init', function (st) {
var web3 = init.loadWeb3()
initWeb3.overrideWeb3(web3, web3Test)
traceManager = new TraceManager(web3)
traceManager.traceRetriever = new TestTraceRetriever()
st.end()
})
t.test('TraceManager.resolveTrace', function (st) {
traceManager.resolveTrace(txInvokation, function (error, result) {
var tx = traceManager.web3.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
traceManager.resolveTrace(tx, function (error, result) {
if (error) {
st.fail(' - traceManager.resolveTrace - failed ' + result)
} else {
@ -44,7 +45,8 @@ tape('TraceManager', function (t) {
})
t.test('TraceManager.getStorageAt', function (st) {
traceManager.getStorageAt(110, txInvokation, function (error, result) {
var tx = traceManager.web3.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
traceManager.getStorageAt(110, tx, function (error, result) {
if (error) {
st.fail(error)
} else {

Loading…
Cancel
Save