Merge remote-tracking branch 'upstream/master'

pull/1/head
Sergii Bomko 5 years ago
commit 19cd3b6176
  1. 11
      .circleci/config.yml
  2. 4
      .travis.yml
  3. 5
      README.md
  4. 26
      ci/browser_tests.sh
  5. 105
      meetings.md
  6. 32
      nightwatch.js
  7. 5
      package.json
  8. 48
      release-process.md
  9. 13
      src/app.js
  10. 4
      src/app/components/plugin-manager-component.js
  11. 19
      src/app/components/side-panel.js
  12. 53
      src/app/components/vertical-icons.js
  13. 9
      src/app/debugger/debuggerUI/TxBrowser.js
  14. 4
      src/app/files/file-explorer.js
  15. 2
      src/app/panels/tab-proxy.js
  16. 26
      src/app/tabs/compile-tab.js
  17. 44
      src/app/tabs/compileTab/compileTab.js
  18. 2
      src/app/tabs/run-tab.js
  19. 2
      src/app/tabs/settings-tab.js
  20. 2
      src/app/tabs/styles/test-tab-styles.js
  21. 73
      src/app/tabs/test-tab.js
  22. 2
      src/app/tabs/testTab/testTab.js
  23. 11
      src/app/ui/landing-page/landing-page.js
  24. 2
      src/app/ui/modaldialog.js
  25. 4
      src/app/ui/tooltip.js
  26. 2
      src/framingService.js
  27. 6
      src/lib/offsetToLineColumnConverter.js
  28. 4
      src/universal-dapp-styles.js
  29. 6
      src/universal-dapp-ui.js
  30. 4
      test-browser/helpers/contracts.js
  31. 8
      test-browser/helpers/init.js
  32. 50
      test-browser/tests/generalTests.js
  33. 10
      test-browser/tests/sauce.js
  34. 18
      test-browser/tests/sharedFolderExplorer.js

@ -7,7 +7,7 @@ jobs:
remix-ide:
docker:
# specify the version you desire here
- image: circleci/node:9.11.2
- image: circleci/node:9.11.2-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
@ -32,7 +32,14 @@ jobs:
key: dep-bundle-29-{{ checksum "package.json" }}
paths:
- ~/repo/node_modules
- run: npm run lint && npm run test && npm run make-mock-compiler && npm run build
- run: npm run lint && npm run test && npm run make-mock-compiler
- run:
name: Download Selenium
command: wget http://selenium-release.storage.googleapis.com/3.5/selenium-server-standalone-3.5.3.jar
- run:
name: Start Selenium
command: java -jar selenium-server-standalone-3.5.3.jar
background: true
- run: ./ci/browser_tests.sh
workflows:

@ -3,7 +3,7 @@ node_js:
- "9"
branches:
only:
- remix_alpha
- master
- remix_live
script:
- npm run lint && npm run test && npm run make-mock-compiler && npm run build
@ -13,7 +13,7 @@ deploy:
script: ci/deploy_from_travis_remix-alpha.sh
skip_cleanup: true
on:
branch: remix_alpha
branch: master
- provider: script
script: ci/deploy_from_travis_remix-live.sh
skip_cleanup: true

@ -37,6 +37,11 @@ Or if you want to clone the github repository (`wget` need to be installed first
```bash
git clone https://github.com/ethereum/remix-ide.git
git clone https://github.com/ethereum/remix.git # only if you plan to link remix and remix-ide repositories and develop on it.
cd remix # only if you plan to link remix and remix-ide repositories and develop on it.
npm install # only if you plan to link remix and remix-ide repositories and develop on it.
npm run bootstrap # only if you plan to link remix and remix-ide repositories and develop on it.
cd remix-ide
npm install
npm run setupremix # only if you plan to link remix and remix-ide repositories and develop on it.

@ -11,42 +11,22 @@ setupRemixd () {
cd ..
}
if test $(uname -s) = "Darwin"
then
OS="osx"
FILEFORMAT="zip"
else
OS="linux"
FILEFORMAT="tar.gz"
fi
SC_VERSION="4.5.1"
SAUCECONNECT_URL="https://saucelabs.com/downloads/sc-$SC_VERSION-$OS.$FILEFORMAT"
SAUCECONNECT_USERNAME="yanneth"
SAUCECONNECT_ACCESSKEY="1f5a4560-b02b-41aa-b52b-f033aad30870"
BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}}
echo "$BUILD_ID"
SAUCECONNECT_JOBIDENTIFIER="browsersolidity_tests_${BUILD_ID}"
SAUCECONNECT_READYFILE="sc.ready"
TEST_EXITCODE=0
npm run serve &
setupRemixd
wget "$SAUCECONNECT_URL"
tar -zxvf sc-"$SC_VERSION"-"$OS"."$FILEFORMAT"
./sc-"$SC_VERSION"-"$OS"/bin/sc -u "$SAUCECONNECT_USERNAME" -k "$SAUCECONNECT_ACCESSKEY" -i "$SAUCECONNECT_JOBIDENTIFIER" --no-ssl-bump-domains all --readyfile "$SAUCECONNECT_READYFILE" &
while [ ! -f "$SAUCECONNECT_READYFILE" ]; do
sleep .5
done
sleep 5
npm run nightwatch_remote_chrome || TEST_EXITCODE=1
npm run nightwatch_remote_firefox || TEST_EXITCODE=1
# npm run nightwatch_remote_firefox || 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
node ci/sauceDisconnect.js "$SAUCECONNECT_USERNAME" "$SAUCECONNECT_ACCESSKEY" "$SAUCECONNECT_JOBIDENTIFIER"
# node ci/sauceDisconnect.js "$SAUCECONNECT_USERNAME" "$SAUCECONNECT_ACCESSKEY" "$SAUCECONNECT_JOBIDENTIFIER"
echo "$TEST_EXITCODE"
if [ "$TEST_EXITCODE" -eq 1 ]

@ -0,0 +1,105 @@
# Team meeting 04/06/2019
## documentation
#### doc update for release
For each remix IDE PR, check whether the documentation needs to be updated.
If so, either
- update the doc with and link to the PR
- create an issue with the label "documentation"
Before releasing (better a few days before), we take time to address documentation issues that *needs* to be done. @rob is committing to checking and organize our work on this.
#### monthly doc update
We setup a monthly call where we read through the documentation and check what should be added / updated / improved
#### move to remix IDE
we move the documentation to the remix-ide repository
## medium post policy
Any post that relates to Ethereum could be put in the remix plublication.
Although that is not mandatory and left up to the writter.
## guided tour
we still need to validate the framework but "shepherd" seems to be the one.
It will work as a native plugin, started by default.
Each other native plugin can request a guided tour with:
`this.call('guidedtour', 'start', 'debugger')`
Other type of plugin may be able to the native plugin guided tour but we won't push this if the integration is not working out of the box.
We rather update the remix-plugin doc saying that `guided tour framework name` is the prefferred one.
## web site
we commit to have a public web site for general info about us.
It won't be a branded web site.
We are asking a designer for improving the Liana's logo.
We don't need to have a framework (hugo, hexo) if that's too much overhead.
@liana is testing out the easiest solution.
## solidity tutorial framework
it will be set of file:
- md
- solidity contract
- test contract
we only support md for now and move to supporting other format if needded.
It requires the "test" native plugin to extend its API.
@rob/@francois are managing that.
## remix workshops repository
the current workshop should either be removed or converted to the md tutorial framework.
@rob/@francois are managing that
## coding best practices
Use html "id" only if needed.
"id" has to be explicit enough so the node can be targeted without using nested css target.
## file manager / file explorer / file provider
They are going to be refactored out to a stand alone ts repo / npm module
## npm module structure
will likely looks like:
RemixProject/component:
/Treeview
/tab
/tabs
/dialog
/fs
RemixProject/libs:
/Solidity
/Tests
/FsProvider
RemixProject/IDE
## browser test
a PR (https://github.com/ethereum/remix-ide/pull/1961) is waiting for merging @yann
It iwll be followed by other PRs, aiming to improve process of writting browser tests.
## desktop version
we will propose an offline version using electron (@yann)
first steps :
- put all the public link to the local package
- basic electron wrapper
## out reach beyond community
We agree it is something interesting to explore,
It is not 100% dev tool nor remix so we should organize call with other people from EF at least

@ -1,7 +1,5 @@
'use strict'
var buildId = process.env.CIRCLE_BUILD_NUM || process.env.TRAVIS_JOB_NUMBER
module.exports = {
'src_folders': ['test-browser/tests'],
'output_folder': 'reports',
@ -12,13 +10,8 @@ module.exports = {
'test_settings': {
'default': {
'launch_url': 'http://ondemand.saucelabs.com:80',
'selenium_host': 'ondemand.saucelabs.com',
'selenium_port': 80,
'silent': true,
'username': 'yanneth',
'access_key': '1f5a4560-b02b-41aa-b52b-f033aad30870',
'use_ssl': false,
'selenium_port': 4444,
'selenium_host': 'localhost',
'globals': {
'waitForConditionTimeout': 10000,
'asyncHookTimeout': 100000
@ -30,9 +23,7 @@ module.exports = {
'desiredCapabilities': {
'browserName': 'firefox',
'javascriptEnabled': true,
'acceptSslCerts': true,
'build': 'build-' + buildId,
'tunnel-identifier': 'browsersolidity_tests_' + buildId
'acceptSslCerts': true
}
},
@ -41,8 +32,6 @@ module.exports = {
'browserName': 'chrome',
'javascriptEnabled': true,
'acceptSslCerts': true,
'build': 'build-' + buildId,
'tunnel-identifier': 'browsersolidity_tests_' + buildId,
'chromeOptions': {
'args': ['window-size=2560,1440', 'start-fullscreen']
}
@ -53,11 +42,7 @@ module.exports = {
'desiredCapabilities': {
'browserName': 'safari',
'javascriptEnabled': true,
'platform': 'macOS 10.13',
'version': '11.0',
'acceptSslCerts': true,
'build': 'build-' + buildId,
'tunnel-identifier': 'browsersolidity_tests_' + buildId
'acceptSslCerts': true
}
},
@ -65,18 +50,11 @@ module.exports = {
'desiredCapabilities': {
'browserName': 'internet explorer',
'javascriptEnabled': true,
'platform': 'Windows 10',
'acceptSslCerts': true,
'version': '11.103',
'build': 'build-' + buildId,
'tunnel-identifier': 'browsersolidity_tests_' + buildId
'acceptSslCerts': true
}
},
'local': {
'launch_url': 'http://localhost:8080',
'selenium_port': 4444,
'selenium_host': 'localhost',
'desiredCapabilities': {
'browserName': 'chrome',
'javascriptEnabled': true,

@ -1,6 +1,6 @@
{
"name": "remix-ide",
"version": "v0.8.0",
"version": "v0.8.1",
"description": "Minimalistic browser-based Solidity IDE",
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.8.1",
@ -68,7 +68,7 @@
},
"dependencies": {
"http-server": "^0.11.1",
"remix-plugin": "0.0.2-alpha.9",
"remix-plugin": "0.0.2-alpha.10",
"remixd": "0.1.8-alpha.6"
},
"repository": {
@ -173,6 +173,7 @@
"minify": "uglifyjs --in-source-map inline --source-map-inline -c warnings=false",
"nightwatch_local": "nightwatch --config nightwatch.js --env local",
"nightwatch_local_general": "nightwatch ./test-browser/tests/generalTests.js --config nightwatch.js --env local ",
"nightwatch_local_sharedFolderExplorer": "nightwatch ./test-browser/tests/sharedFolderExplorer.js --config nightwatch.js --env local ",
"nightwatch_local_debugger": "nightwatch --config nightwatch_debugger.js --env local",
"nightwatch_remote_chrome": "nightwatch --config nightwatch.js --env chrome",
"nightwatch_remote_firefox": "nightwatch --config nightwatch.js --env default",

@ -3,56 +3,18 @@ This document includes:
- how to update remix-alpha.ethereum.org.
- how to release remix IDE.
# remix-* release (npm release, github release)
- For a specifix module (lib/core/debug/ide/solidity/tests)
- In a new branch, bump the version in package.json, push it and create PR.
- Wait for tests completion.
- merge PR
- build the branch ( `npm run build` for remix-ide ).
- execute `npm publish`.
- create new `tag` ( e.g `git tag v0.6.1-alpha.2` ).
- push the tag ( `git push --tag` ).
- execute `gren changelog --generate -t <new tag>..<previous tag> --data-source=prs`.
- in `changelog.md` remove the closed and non merged PR.
- publish a release in github using the changelog.
# remix.ethereum.org update
This is not strictly speaking a release. Updating the remix site is done through the Travis build:
- In remix-ide repository
- Switch to the branch `remix_live`
- Rebase the branch against master
- Force push
- https://travis-ci.org/ethereum/remix-ide
- Click `More options`
- Click `Trigger build`
- Select `remix_live`
- Click `Trigger custom build`
- Once the build is finished (can take a while) and successful, check remix.ethereum.org is updated accordingly
- switch to the remix_live
- git reset --hard <master-commit-hash>
- git push -f origin remix_live
Travis will build automaticaly and update remix.ethereum.org
# remix-alpha.ethereum.org update
This is not strictly speaking a release. Updating the remix-alpha site is done through the Travis build:
- https://travis-ci.org/ethereum/remix-ide
- Click `More options`
- Click `Trigger build`
- Select `Master`
- Click `Trigger custom build`
- Once the build is finished (can take a while) and successful, check remix-alpha.ethereum.org is updated accordingly
# beta testing remix
We publish a new release roughly every month and greatly appreciate support on beta testing.
By giving report, beta testers help to:
- verify viabilty (in term core and UX design) of new features
- track possible regression
- propose new update
- contribute on reviewing / building Pull Request
remix-alpha.ethereum.org is automaticaly updated every time commits are pushed to master
# remix IDE release

@ -263,10 +263,14 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
return 'Are you sure you want to leave?'
}
let appStore = new EntityStore('module', 'name')
const appManager = new RemixAppManager(appStore)
registry.put({api: appManager, name: 'appmanager'})
registry.put({api: msg => self._components.mainview.logHtmlMessage(msg), name: 'logCallback'})
// helper for converting offset to line/column
var offsetToLineColumnConverter = new OffsetToLineColumnConverter()
var offsetToLineColumnConverter = new OffsetToLineColumnConverter(appManager)
registry.put({api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter'})
// json structure for hosting the last compilattion result
@ -318,10 +322,6 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
// TODO: There are still a lot of dep between mainview and filemanager
let appStore = new EntityStore('module', 'name')
const appManager = new RemixAppManager(appStore)
registry.put({api: appManager, name: 'appmanager'})
// ----------------- file manager ----------------------------
self._components.fileManager = new FileManager()
const fileManager = self._components.fileManager
@ -412,7 +412,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
let test = new TestTab(
registry.get('filemanager').api,
registry.get('filepanel').api,
compileTab
compileTab,
appStore
)
let sourceHighlighters = registry.get('editor').api.sourceHighlighters

@ -91,7 +91,7 @@ class PluginManagerComponent extends BaseApi {
</button>`
return yo`
<article class="list-group-item py-1" title="${name}" >
<article id="remixPluginManagerListItem_${name}" class="list-group-item py-1" title="${displayName}" >
<div class="${css.row} justify-content-between align-items-center">
<h6 class="${css.displayName}">${displayName}</h6>
${activationButton}
@ -155,7 +155,7 @@ class PluginManagerComponent extends BaseApi {
? yo`
<nav class="navbar navbar-expand-lg navbar-light bg-light justify-content-between align-items-center">
<span class="navbar-brand">Inactive Modules</span>
<span class="badge badge-pill badge-primary">${inactives.length}</span>
<span class="badge badge-pill badge-primary" style = "cursor: default;">${inactives.length}</span>
</nav>`
: ''

@ -10,6 +10,8 @@ const css = csjs`
.swapitTitle {
text-transform: uppercase;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.swapitTitle i{
padding-left: 6px;
@ -21,6 +23,7 @@ const css = csjs`
display: flex;
justify-content: space-between;
align-items: center;
justify-content: flex-start;
}
.swapitHeader h6 {
margin: 0;
@ -33,6 +36,9 @@ const css = csjs`
height: calc(100% - 35px);
overflow: auto;
}
.titleInfo {
padding-left: 10px;
}
`
const options = {
@ -56,7 +62,7 @@ export class SidePanel extends AbstractPanel {
yo.update(this.header, this.renderHeader())
}
/** The header of the swap panel */
/** The header of the side panel */
renderHeader () {
let name = ' - '
let docLink = ''
@ -64,14 +70,15 @@ export class SidePanel extends AbstractPanel {
const { profile } = this.store.getOne(this.active)
name = profile.displayName ? profile.displayName : profile.name
const docsRoot = 'https://remix.readthedocs.io/en/latest/'
docLink = profile.documentation ? yo`<a href="${docsRoot}${profile.documentation}" title="link to documentation" target="_blank"><i aria-hidden="true" class="fas fa-book"></i></a>` : ''
docLink = profile.documentation ? yo`<a href="${docsRoot}${profile.documentation}" class="${css.titleInfo}" title="link to documentation" target="_blank"><i aria-hidden="true" class="fas fa-book"></i></a>` : ''
}
return yo`
<header class="${css.swapitHeader}">
<h6 class="${css.swapitTitle}">${name}${docLink}</h6>
</div>
</header>`
<header class="${css.swapitHeader}">
<h6 class="${css.swapitTitle}">${name}</h6>
${docLink}
</header>
`
}
render () {

@ -10,7 +10,7 @@ export class VerticalIcons {
constructor (name, appStore, homeProfile) {
this.store = appStore
this.homeProfile = homeProfile
this.homeProfile = homeProfile.profile
this.events = new EventEmitter()
this.icons = {}
this.iconKind = {}
@ -74,12 +74,13 @@ export class VerticalIcons {
* @param {ModuleProfile} profile The profile of the module
*/
addIcon ({kind, name, icon, displayName, tooltip}) {
let title = (displayName || name)// + (tooltip ? tooltip : "")
let title = (tooltip || displayName || name)
this.icons[name] = yo`
<div
class="${css.icon}"
onclick="${() => { this._iconClick(name) }}"
plugin="${name}" title="${title}" >
onclick="${() => { this.toggle(name) }}"
plugin="${name}"
title="${title}">
<img class="image" src="${icon}" alt="${name}" />
</div>`
this.iconKind[kind || 'other'].appendChild(this.icons[name])
@ -195,9 +196,22 @@ export class VerticalIcons {
* @param {string} name Name of profile of the module to activate
*/
select (name) {
this.updateActivations(name)
this.events.emit('showContent', name)
}
/**
* Toggles the side panel for plugin
* @param {string} name Name of profile of the module to activate
*/
toggle (name) {
this.updateActivations(name)
this.events.emit('toggleContent', name)
}
updateActivations (name) {
this.removeActive()
this.addActive(name)
this.events.emit('showContent', name)
}
onThemeChanged (themeType) {
@ -209,10 +223,11 @@ export class VerticalIcons {
}
}
_iconClick (name) {
this.removeActive()
this.addActive(name)
this.events.emit('toggleContent', name)
/**
* Show the home page
*/
showHome () {
globalRegistry.get('appmanager').api.ensureActivated('home')
}
render () {
@ -220,7 +235,7 @@ export class VerticalIcons {
<div
class="${css.homeIcon}"
onclick="${(e) => {
globalRegistry.get('appmanager').api.ensureActivated('home')
this.showHome()
}}"
plugin="${this.homeProfile.name}" title="${this.homeProfile.displayName}"
>
@ -344,15 +359,15 @@ export class VerticalIcons {
this.view = yo`
<div class=${css.icons}>
${home}
${this.iconKind['fileexplorer']}
${this.iconKind['compile']}
${this.iconKind['run']}
${this.iconKind['testing']}
${this.iconKind['analysis']}
${this.iconKind['debugging']}
${this.iconKind['other']}
${this.iconKind['settings']}
${home}
${this.iconKind['fileexplorer']}
${this.iconKind['compile']}
${this.iconKind['run']}
${this.iconKind['testing']}
${this.iconKind['analysis']}
${this.iconKind['debugging']}
${this.iconKind['other']}
${this.iconKind['settings']}
</div>
`
return this.view

@ -62,6 +62,9 @@ TxBrowser.prototype.submit = function () {
TxBrowser.prototype.updateTxN = function (ev) {
this.state.txNumber = ev.target.value
if (this.view) {
yo.update(this.view, this.render())
}
}
TxBrowser.prototype.load = function (txHash, tx) {
@ -81,6 +84,7 @@ TxBrowser.prototype.setState = function (state) {
TxBrowser.prototype.render = function () {
var self = this
let action = yo`<button class='btn btn-primary btn-sm ${css.txbutton}' id='load' title='${this.state.debugging ? 'Stop' : 'Start'} debugging' onclick=${function () { self.submit() }}>${this.state.debugging ? 'Stop' : 'Start'} debugging</button>`
var view = yo`<div class="${css.container}">
<div class="${css.txContainer}">
<div class="${css.txinputs} p-1 input-group">
@ -94,7 +98,7 @@ TxBrowser.prototype.render = function () {
/>
</div>
<div class="${css.txbuttons} btn-group p-1">
<button class='btn btn-primary btn-sm ${css.txbutton}' id='load' title='${this.state.debugging ? 'Stop' : 'Start'} debugging' onclick=${function () { self.submit() }}>${this.state.debugging ? 'Stop' : 'Start'} debugging</button>
${action}
</div>
</div>
<span id='error'></span>
@ -102,6 +106,9 @@ TxBrowser.prototype.render = function () {
if (this.state.debugging) {
view.querySelectorAll('input').forEach(element => { element.setAttribute('disabled', '') })
}
if (!this.state.txNumber) {
action.setAttribute('disabled', 'disabled')
}
if (!this.view) {
this.view = view
}

@ -522,12 +522,12 @@ fileExplorer.prototype.renderMenuItems = function () {
items = this.menuItems.map(({action, title, icon}) => {
if (action === 'uploadFile') {
return yo`
<label class="${icon} ${css.newFile}" title="${title}">
<span class="${icon} ${css.newFile}" title="${title}">
<input type="file" onchange=${(event) => {
event.stopPropagation()
this.uploadFile(event)
}} multiple />
</label>
</span>
`
} else {
return yo`

@ -117,7 +117,7 @@ export class TabProxy {
id: name,
title,
icon,
tooltip: name
tooltip: title
})
this._handlers[name] = { switchTo, close }
}

@ -132,7 +132,7 @@ class CompileTab extends CompilerApi {
// Update contract Selection
let contractMap = {}
if (success) this.compiler.visitContracts((contract) => { contractMap[contract.name] = contract })
let contractSelection = this.contractSelection(Object.keys(contractMap) || [], source.target)
let contractSelection = this.contractSelection(contractMap)
yo.update(this._view.contractSelection, contractSelection)
if (data['error']) {
@ -173,6 +173,11 @@ class CompileTab extends CompilerApi {
return this.compileTabLogic.compiler.lastCompilationResult
}
// This function is used by remix-plugin
compile (fileName) {
return this.compileTabLogic.compileFile(fileName)
}
/*********
* SUB-COMPONENTS
*/
@ -181,13 +186,22 @@ class CompileTab extends CompilerApi {
* Section to select the compiled contract
* @param {string[]} contractList Names of the compiled contracts
*/
contractSelection (contractList = [], sourceFile) {
contractSelection (contractMap) {
// Return the file name of a path: ex "browser/ballot.sol" -> "ballot.sol"
const getFileName = (path) => {
const part = path.split('/')
return part[part.length - 1]
}
const contractList = contractMap ? Object.keys(contractMap).map((key) => ({
name: key,
file: getFileName(contractMap[key].file)
})) : []
let selectEl = yo`
<select
onchange="${e => this.selectContract(e.target.value)}"
id="compiledContracts" class="custom-select"
>
${contractList.map((name) => yo`<option value="${name}">${name}</option>`)}
${contractList.map(({name, file}) => yo`<option value="${name}">${name} (${file})</option>`)}
</select>
`
let result = contractList.length
@ -207,14 +221,14 @@ class CompileTab extends CompilerApi {
Compilation Details
</button>
<!-- Copy to Clipboard -->
<div class="${css.contractHelperButtons} btn-secondary">
<div class="${css.contractHelperButtons}">
<div class="input-group">
<div class="btn-group" role="group" aria-label="Copy to Clipboard">
<button class="btn btn-secondary" title="Copy ABI to clipboard" onclick="${() => { this.copyABI() }}">
<button class="btn" title="Copy ABI to clipboard" onclick="${() => { this.copyABI() }}">
<i class="${css.copyIcon} far fa-clipboard" aria-hidden="true"></i>
<span>ABI</span>
</button>
<button class="btn btn-secondary" title="Copy Bytecode to clipboard" onclick="${() => { this.copyBytecode() }}">
<button class="btn" title="Copy Bytecode to clipboard" onclick="${() => { this.copyBytecode() }}">
<i class="${css.copyIcon} far fa-clipboard" aria-hidden="true"></i>
<span>Bytecode</span>
</button>

@ -33,28 +33,38 @@ class CompileTab {
this.compiler.setOptimize(this.optimize)
}
runCompiler () {
this.fileManager.saveCurrentFile()
this.editor.clearAnnotations()
var currentFile = this.config.get('currentFile')
if (!currentFile) return
if (!/\.sol/.exec(currentFile)) return
/**
* Compile a specific file of the file manager
* @param {string} target the path to the file to compile
*/
compileFile (target) {
if (!target) throw new Error('No target provided for compiliation')
if (!/\.sol/.exec(target)) throw new Error(`${target} is not a solidity file. It cannot be compiled with solidity compiler`)
// only compile *.sol file.
var target = currentFile
var sources = {}
var provider = this.fileManager.fileProviderOf(currentFile)
if (!provider) return console.log('cannot compile ' + currentFile + '. Does not belong to any explorer')
provider.get(target, (error, content) => {
if (error) return console.log(error)
sources[target] = { content }
this.event.emit('startingCompilation')
setTimeout(() => {
const provider = this.fileManager.fileProviderOf(target)
if (!provider) throw new Error(`cannot compile ${target}. Does not belong to any explorer`)
return new Promise((resolve, reject) => {
provider.get(target, (error, content) => {
if (error) return reject(error)
const sources = { [target]: { content } }
this.event.emit('startingCompilation')
// setTimeout fix the animation on chrome... (animation triggered by 'staringCompilation')
this.compiler.compile(sources, target)
}, 100)
setTimeout(() => this.compiler.compile(sources, target), 100)
})
})
}
runCompiler () {
try {
this.fileManager.saveCurrentFile()
this.editor.clearAnnotations()
var currentFile = this.config.get('currentFile')
return this.compileFile(currentFile)
} catch (err) {
console.error(err)
}
}
importExternal (url, cb) {
this.compilerImport.import(url,

@ -18,7 +18,7 @@ import { BaseApi } from 'remix-plugin'
const profile = {
name: 'run',
displayName: 'Deploy and run transactions',
displayName: 'Deploy & run transactions',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzFfY29weSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4Ig0KCSB5PSIwcHgiIHdpZHRoPSI3NDIuNTQ1cHgiIGhlaWdodD0iNjc2Ljg4NnB4IiB2aWV3Qm94PSIwIC0wLjIwNCA3NDIuNTQ1IDY3Ni44ODYiDQoJIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAtMC4yMDQgNzQyLjU0NSA2NzYuODg2IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxwb2x5Z29uIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBwb2ludHM9IjI5NS45MTEsMC43MTEgNDg4LjkxMSwzMDQuMTg2IDQ4OC45MTEsMzk3LjE4MSAyOTMuOTExLDY3Ni41NTYgDQoJCTc0MS43ODYsMzQ5Ljk0MyAJIi8+DQoJPHBvbHlnb24gc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHBvaW50cz0iNDE3LjA4Myw0MDYuNTg5IDIwOS43OTEsNTE5LjQ5NCAxLjg0Niw0MDYuMjM0IDIwOS43OTEsNjc1Ljg2MyAJIi8+DQoJPHBvbHlnb24gc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHBvaW50cz0iNDE3LjA4MywzMTguNzA3IDIwOS43OTEsMC43MTEgMS44NDYsMzE4LjQyOCAyMDkuNzkxLDQzMS42ODkgCSIvPg0KPC9nPg0KPC9zdmc+DQo=',

@ -71,7 +71,7 @@ module.exports = class SettingsTab extends BaseApi {
var gistAddToken = yo`<input class="${css.savegisttoken} btn btn-sm btn-primary" id="savegisttoken" onclick=${() => { this.config.set('settings/gist-access-token', gistAccessToken.value); tooltip('Access token saved') }} value="Save" type="button">`
var gistRemoveToken = yo`<input class="btn btn-sm btn-primary" id="removegisttoken" onclick=${() => { gistAccessToken.value = ''; this.config.set('settings/gist-access-token', ''); tooltip('Access token removed') }} value="Remove" type="button">`
this._view.gistToken = yo`<div class="${css.checkboxText}">${gistAccessToken}${copyToClipboard(() => this.config.get('settings/gist-access-token'))}${gistAddToken}${gistRemoveToken}</div>`
this._view.optionVM = yo`<input onchange=${onchangeOption} checked class="align-middle form-check-input" id="alwaysUseVM" type="checkbox">`
this._view.optionVM = yo`<input onchange=${onchangeOption} class="align-middle form-check-input" id="alwaysUseVM" type="checkbox">`
if (this.config.get('settings/always-use-vm') === undefined) this.config.set('settings/always-use-vm', true)
if (this.config.get('settings/always-use-vm')) this._view.optionVM.setAttribute('checked', '')
this._view.personal = yo`<input onchange=${onchangePersonal} id="personal" type="checkbox" class="align-middle form-check-input">`

@ -45,6 +45,7 @@ var css = csjs`
align-items: center;
}
.runButton {
white-space: nowrap;
}
.generateTestFile {
margin-top: 20px;
@ -58,6 +59,7 @@ var css = csjs`
.label {
display: flex;
align-items: center;
white-space: nowrap;
}
`
module.exports = css

@ -19,16 +19,22 @@ const profile = {
}
module.exports = class TestTab extends BaseApi {
constructor (fileManager, filePanel, compileTab) {
constructor (fileManager, filePanel, compileTab, appStore) {
super(profile)
this.compileTab = compileTab
this._view = { el: null }
this.compileTab = compileTab
this.fileManager = fileManager
this.filePanel = filePanel
this.appStore = appStore
this.testTabLogic = new TestTabLogic(fileManager)
this.data = {}
this.testList = yo`<div class=${css.testList}></div>`
this.appStore.event.on('activate', (name) => {
if (name === 'solidity') this.updateRunAction(fileManager.currentFile())
})
this.appStore.event.on('deactivate', (name) => {
if (name === 'solidity') this.updateRunAction(fileManager.currentFile())
})
}
activate () {
@ -47,15 +53,20 @@ module.exports = class TestTab extends BaseApi {
this.data.selectedTests.push(file)
})
this.fileManager.events.on('noFileSelected', () => {
this.updateGenerateFileAction()
this.updateRunAction()
this.updateTestFileList()
})
this.fileManager.events.on('currentFileChanged', (file, provider) => {
this.updateGenerateFileAction(file)
this.updateRunAction(file)
this.testTabLogic.getTests((error, tests) => {
if (error) return tooltip(error)
this.data.allTests = tests
this.data.selectedTests = [...this.data.allTests]
const testsMessage = (tests.length ? this.listTests() : 'No test file available')
yo.update(this.testList, yo`<div class=${css.testList}>${testsMessage}</div>`)
this.updateTestFileList(tests)
if (!this.testsOutput || !this.testsSummary) return
})
})
@ -148,13 +159,55 @@ module.exports = class TestTab extends BaseApi {
}
runTests () {
this.loading.hidden = false
this.testsOutput.innerHTML = ''
this.testsSummary.innerHTML = ''
var tests = this.data.selectedTests
if (!tests) return
this.loading.hidden = tests.length === 0
async.eachOfSeries(tests, (value, key, callback) => { this.runTest(value, callback) })
}
updateGenerateFileAction (currentFile) {
let el = yo`<button class="${css.generateTestFile} btn btn-primary" onclick="${this.testTabLogic.generateTestFile.bind(this.testTabLogic)}">Generate test file</button>`
if (!currentFile) {
el.setAttribute('disabled', 'disabled')
el.setAttribute('title', 'No file selected')
}
if (!this.generateFileActionElement) {
this.generateFileActionElement = el
} else {
yo.update(this.generateFileActionElement, el)
}
return this.generateFileActionElement
}
updateRunAction (currentFile) {
let el = yo`<button class="${css.runButton} btn btn-primary" onclick="${this.runTests.bind(this)}">Run Tests</button>`
const isSolidityActive = this.appStore.isActive('solidity')
if (!currentFile || !isSolidityActive) {
el.setAttribute('disabled', 'disabled')
if (!currentFile) el.setAttribute('title', 'No file selected')
if (!isSolidityActive) el.setAttribute('title', 'The solidity module should be activated')
}
if (!this.runActionElement) {
this.runActionElement = el
} else {
yo.update(this.runActionElement, el)
}
return this.runActionElement
}
updateTestFileList (tests) {
const testsMessage = (tests && tests.length ? this.listTests() : 'No test file available')
let el = yo`<div class=${css.testList}>${testsMessage}</div>`
if (!this.testFilesListElement) {
this.testFilesListElement = el
} else {
yo.update(this.testFilesListElement, el)
}
return this.testFilesListElement
}
render () {
this.testsOutput = yo`<div class="${css.container} m-3 border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>`
this.testsSummary = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>`
@ -172,11 +225,11 @@ module.exports = class TestTab extends BaseApi {
For more details, see
How to test smart contracts guide in our documentation.
<br/>
<div class="${css.generateTestFile} btn btn-secondary" onclick="${this.testTabLogic.generateTestFile.bind(this.testTabLogic)}">Generate test file</div>
${this.updateGenerateFileAction()}
</div>
<div class="${css.tests}">
<div class="${css.buttons}">
<div class="${css.runButton} btn btn-primary" onclick="${this.runTests.bind(this)}">Run Tests</div>
${this.updateRunAction()}
<label class="${css.label} mx-4 m-2" for="checkAllTests">
<input id="checkAllTests"
type="checkbox"
@ -186,7 +239,7 @@ module.exports = class TestTab extends BaseApi {
Check/Uncheck all
</label>
</div>
${this.testList}
${this.updateTestFileList()}
<hr>
<div class="${css.buttons}" ><h6>Results:${this.loading}</h6></div>
${this.testsOutput}

@ -33,7 +33,7 @@ class TestTabLogic {
for (var file in files) {
if (/.(_test.sol)$/.exec(file)) tests.push(provider.type + '/' + file)
}
cb(null, tests)
cb(null, tests, path)
}
generateTestContractSample () {

@ -96,9 +96,12 @@ export class LandingPage extends BaseApi {
let load = function (service, item, examples, info) {
let compilerImport = new CompilerImport()
let fileProviders = globalRegistry.get('fileproviders').api
const msg = yo`<div class="p-2"><span>Enter the ${item} you would like to load.</span>
<div>${info}</div>
<div>e.g ${examples.map((url) => { return yo`<div class="p-1"><a>${url}</a></div>` })}</div></div>`
const msg = yo`
<div class="p-2">
<span>Enter the ${item} you would like to load.</span>
<div>${info}</div>
<div>e.g ${examples.map((url) => { return yo`<div class="p-1"><a>${url}</a></div>` })}</div>
</div>`
modalDialogCustom.prompt(`Import from ${service}`, msg, null, (target) => {
if (target !== '') {
@ -222,7 +225,7 @@ export class LandingPage extends BaseApi {
<p class="mb-1">Import From:</p>
<div class="btn-group">
<button class="btn btn-sm btn-secondary" onclick=${() => { importFromGist() }}>Gist</button>
<button class="btn btn-sm btn-secondary" onclick=${() => { load('Github', 'github URL', ['https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/crowdsale/Crowdsale.sol', 'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol', 'github:OpenZeppelin/openzeppelin-solidity/contracts/ownership/Ownable.sol#v2.1.2']) }}>Github</button>
<button class="btn btn-sm btn-secondary" onclick=${() => { load('Github', 'github URL', ['https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token-metadata.sol', 'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol', 'github:OpenZeppelin/openzeppelin-solidity/contracts/ownership/Ownable.sol#v2.1.2']) }}>GitHub</button>
<button class="btn btn-sm btn-secondary" onclick=${() => { load('Swarm', 'bzz-raw URL', ['bzz-raw://<swarm-hash>']) }}>Swarm</button>
<button class="btn btn-sm btn-secondary" onclick=${() => { load('Ipfs', 'ipfs URL', ['ipfs://<ipfs-hash>']) }}>Ipfs</button>
<button class="btn btn-sm btn-secondary" onclick=${() => { load('Https', 'http/https raw content', ['https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/master/contracts/crowdsale/validation/IndividuallyCappedCrowdsale.sol']) }}>https</button>

@ -3,7 +3,7 @@ var css = require('./styles/modaldialog-styles')
module.exports = (title, content, ok, cancel, focusSelector, opts) => {
let agreed = true
let footerIsActive = true
let footerIsActive = false
opts = opts || {}
var container = document.querySelector(`.modal`)
if (!container) {

@ -23,7 +23,7 @@ class Toaster {
}, 2000)
animation(this.tooltip, css.animateTop.className)
}
render (tooltipText, action, opts) {
render (tooltipText, actionElement, opts) {
opts = defaultOptions(opts)
let canShorten = true
if (tooltipText instanceof Element) {
@ -53,7 +53,7 @@ class Toaster {
<span class="px-2">
${shortTooltipText}
${button}
${action}
${actionElement}
</span>
<span style="align-self: baseline;">
<button class="fas fa-times btn-info mx-1 p-0" onclick=${() => {

@ -11,7 +11,7 @@ export default {
})
verticalIcon.select('fileExplorers')
mainPanel.showContent('home')
verticalIcon.showHome()
document.addEventListener('keypress', (e) => {
if (e.shiftKey && e.ctrlKey) {

@ -1,10 +1,12 @@
'use strict'
var SourceMappingDecoder = require('remix-lib').SourceMappingDecoder
function offsetToColumnConverter () {
function offsetToColumnConverter (appManager) {
this.lineBreakPositionsByContent = {}
this.sourceMappingDecoder = new SourceMappingDecoder()
// we don't listen anymore on compilation result for clearing the cache
appManager.data.proxy.event.register('sendCompilationResult', () => {
this.clear()
})
}
offsetToColumnConverter.prototype.offsetToLineColumn = function (rawLocation, file, sources, asts) {

@ -8,10 +8,9 @@ var css = csjs`
.title {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 11px;
/* height: 30px; */
/* width: 97%; */
width: 100%;
overflow: hidden;
word-break: break-word;
line-height: initial;
@ -49,6 +48,7 @@ var css = csjs`
padding: 5px 7px;
}
.nameNbuts {
display: contents;
flex-wrap: nowrap;
width: 100%;
}

@ -68,17 +68,17 @@ UniversalDAppUI.prototype.renderInstanceFromABI = function (contractABI, address
var shortAddress = helper.shortenAddress(address)
var title = yo`
<div class="${css.title} alert alert-secondary">
<div class="${css.title} alert alert-secondary p-2">
<button class="btn ${css.titleExpander}" onclick="${(e) => { toggleClass(e) }}"><i class="fas fa-angle-right" aria-hidden="true"></i></button>
<div class="input-group ${css.nameNbuts}">
<div class="${css.titleText} input-group-prepend"><span class="input-group-text ${css.spanTitleText}"> ${contractName} at ${shortAddress} (${context})</span></div>
<div class="btn-group">
<button class="btn p-2 btn-secondary">${copyToClipboard(() => address)}</button>
<button class="btn p-1 btn-secondary">${copyToClipboard(() => address)}</button>
</div>
</div>
</div>`
var close = yo`<button class="${css.udappClose} p-2 btn btn-secondary" onclick=${remove}><i class="${css.closeIcon} fas fa-times" aria-hidden="true"></i></button>`
var close = yo`<button class="${css.udappClose} p-1 btn btn-secondary" onclick=${remove} title="Remove from the list"><i class="${css.closeIcon} fas fa-times" aria-hidden="true"></i></button>`
title.querySelector('.btn-group').appendChild(close)
var contractActionsWrapper = yo`

@ -42,7 +42,7 @@ function getCompiledContracts (browser, compiled, callback) {
} else {
var ret = []
for (var c = 0; c < contracts.length; c++) {
ret.push(contracts[c].innerHTML)
ret.push(contracts[c].value)
}
return ret
}
@ -116,6 +116,7 @@ function clickFunction (fnFullName, expectedInput) {
}
function verifyCallReturnValue (browser, address, checks, done) {
console.log('verifyCallReturnValue address', address)
browser.execute(function (address) {
var nodes = document.querySelectorAll('#instance' + address + ' div[class^="contractActionsContainer"] div[class^="value"]')
var ret = []
@ -125,6 +126,7 @@ function verifyCallReturnValue (browser, address, checks, done) {
}
return ret
}, [address], function (result) {
console.log('verifyCallReturnValue', result)
for (var k in checks) {
browser.assert.equal(result.value[k], checks[k])
}

@ -21,10 +21,10 @@ function initModules (browser, callback) {
.execute(function () {
document.querySelector('div[id="pluginManager"]').scrollTop = document.querySelector('div[id="pluginManager"]').scrollHeight
}, [], function () {
browser.click('#pluginManager article[title="solidity"] button')
.click('#pluginManager article[title="run"] button')
.click('#pluginManager article[title="solidityStaticAnalysis"] button')
.click('#pluginManager article[title="debugger"] button')
browser.click('#pluginManager article[id="remixPluginManagerListItem_solidity"] button')
.click('#pluginManager article[id="remixPluginManagerListItem_run"] button')
.click('#pluginManager article[id="remixPluginManagerListItem_solidityStaticAnalysis"] button')
.click('#pluginManager article[id="remixPluginManagerListItem_debugger"] button')
.click('#icon-panel div[plugin="fileExplorers"]')
.perform(() => { callback() })
})

@ -164,32 +164,30 @@ function checkDeployShouldSucceed (browser, address, callback) {
function testSignature (browser, callback) {
let hash, signature
browser.perform((client, done) => {
contractHelper.signMsg(browser, 'test message', (h, s) => {
hash = h
signature = s
browser.assert.ok(typeof hash.value === 'string', 'type of hash.value must be String')
browser.assert.ok(typeof signature.value === 'string', 'type of signature.value must be String')
contractHelper.addFile(browser, 'signMassage.sol', sources[6]['browser/signMassage.sol'], () => {
contractHelper.switchFile(browser, 'browser/signMassage.sol', () => {
contractHelper.selectContract(browser, 'ECVerify', () => { // deploy lib
contractHelper.createContract(browser, '', () => {
const instanceSelector = '.instance:nth-of-type(4)'
browser.waitForElementPresent(instanceSelector)
.click(instanceSelector + ' > div > button')
.getAttribute(instanceSelector, 'id', (result) => {
// skip 'instance' part of e.g. 'instance0x692a70d2e424a56d2c6c27aa97d1a86395877b3a'
const address = result.value.slice('instance'.length)
browser.clickFunction('ecrecovery - call', {types: 'bytes32 hash, bytes sig', values: `"${hash.value}","${signature.value}"`}).perform(
() => {
contractHelper.verifyCallReturnValue(
browser,
address,
['0: address: 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'],
() => { callback(null, browser) }
)
})
})
contractHelper.signMsg(browser, 'test message', (h, s) => {
hash = h
signature = s
browser.assert.ok(typeof hash.value === 'string', 'type of hash.value must be String')
browser.assert.ok(typeof signature.value === 'string', 'type of signature.value must be String')
contractHelper.addFile(browser, 'signMassage.sol', sources[6]['browser/signMassage.sol'], () => {
contractHelper.switchFile(browser, 'browser/signMassage.sol', () => {
contractHelper.selectContract(browser, 'ECVerify', () => { // deploy lib
contractHelper.createContract(browser, '', () => {
const instanceSelector = '.instance:nth-of-type(4)'
browser.waitForElementPresent(instanceSelector)
.click(instanceSelector + ' > div > button')
.getAttribute(instanceSelector, 'id', (result) => {
// skip 'instance' part of e.g. 'instance0x692a70d2e424a56d2c6c27aa97d1a86395877b3a'
const address = result.value.slice('instance'.length)
browser.clickFunction('ecrecovery - call', {types: 'bytes32 hash, bytes sig', values: `"${hash.value}","${signature.value}"`}).perform(
() => {
contractHelper.verifyCallReturnValue(
browser,
address,
['0: address: 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'],
() => { callback(null, browser) }
)
})
})
})
})

@ -1,17 +1,14 @@
const https = require('https')
// const https = require('https')
module.exports = function sauce (callback) {
return 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')
@ -59,4 +56,5 @@ module.exports = function sauce (callback) {
console.log('Error', error)
callback()
}
*/
}

@ -59,24 +59,19 @@ function runTests (browser, testData) {
browser.end()
return
}
if (browserName === 'chrome') {
console.log('do not run remixd test for ' + browserName + ': TODO to reenable later')
browser.end()
return
}
if (browserName === 'firefox') {
console.log('do not run remixd test for ' + browserName + ': TODO to reenable later')
browser.end()
return
}
browser
.waitForElementVisible('#icon-panel', 10000)
.waitForElementVisible('#icon-panel', 2000)
.clickLaunchIcon('fileExplorers')
.click('.websocketconn')
.waitForElementVisible('#modal-footer-ok', 10000)
.clickLaunchIcon('pluginManager')
.click('#pluginManager article[id="remixPluginManagerListItem_remixd"] button')
.waitForElementVisible('#modal-footer-ok', 2000)
.click('#modal-footer-ok')
.waitForElementVisible('[data-path="localhost"]', 100000)
.click('[data-path="localhost"]')
.clickLaunchIcon('fileExplorers')
.waitForElementVisible('[data-path="localhost/folder1"]')
.click('[data-path="localhost/folder1"]')
.waitForElementVisible('[data-path="localhost/contract1.sol"]')
@ -142,7 +137,8 @@ function runTests (browser, testData) {
.waitForElementNotPresent('[data-path="localhost/folder1/contract_' + browserName + '.sol"]') // check if renamed (old) file is not present
.waitForElementNotPresent('[data-path="localhost/folder1/contract_' + browserName + '_toremove.sol"]') // check if removed (old) file is not present
.click('[data-path="localhost/folder1/renamed_contract_' + browserName + '.sol"]')
.click('.websocketconn')
.clickLaunchIcon('pluginManager')
.click('#pluginManager article[id="remixPluginManagerListItem_remixd"] button')
.end()
})
}

Loading…
Cancel
Save