Merged remixd into remix-project

pull/5370/head
ioedeveloper 4 years ago
commit d5be3b5084
  1. 282
      .circleci/config.yml
  2. 13
      .editorconfig
  3. 3
      .env
  4. 40
      .eslintrc
  5. 54
      .gitignore
  6. 4
      .prettierignore
  7. 3
      .prettierrc
  8. 17
      CONTRIBUTING.md
  9. 6
      Dockerfile
  10. 28
      Dockerfile.dev
  11. 21
      LICENSE
  12. 188
      README.md
  13. 14
      apps/remix-ide-e2e/.eslintrc
  14. 83
      apps/remix-ide-e2e/nightwatch.ts
  15. 11
      apps/remix-ide-e2e/seleniumConfig.js
  16. 36
      apps/remix-ide-e2e/src/commands/addAtAddressInstance.ts
  17. 39
      apps/remix-ide-e2e/src/commands/addFile.ts
  18. 31
      apps/remix-ide-e2e/src/commands/checkElementStyle.ts
  19. 36
      apps/remix-ide-e2e/src/commands/checkTerminalFilter.ts
  20. 42
      apps/remix-ide-e2e/src/commands/checkVariableDebug.ts
  21. 31
      apps/remix-ide-e2e/src/commands/clearEditableContent.ts
  22. 27
      apps/remix-ide-e2e/src/commands/clickElementAtPosition.ts
  23. 26
      apps/remix-ide-e2e/src/commands/clickFunction.ts
  24. 14
      apps/remix-ide-e2e/src/commands/clickInstance.ts
  25. 14
      apps/remix-ide-e2e/src/commands/clickLaunchIcon.ts
  26. 32
      apps/remix-ide-e2e/src/commands/createContract.ts
  27. 26
      apps/remix-ide-e2e/src/commands/debugTransaction.ts
  28. 22
      apps/remix-ide-e2e/src/commands/editorScroll.ts
  29. 19
      apps/remix-ide-e2e/src/commands/executeScript.ts
  30. 31
      apps/remix-ide-e2e/src/commands/getAddressAtPosition.ts
  31. 23
      apps/remix-ide-e2e/src/commands/getEditorValue.ts
  32. 28
      apps/remix-ide-e2e/src/commands/getInstalledPlugins.ts
  33. 19
      apps/remix-ide-e2e/src/commands/getModalBody.ts
  34. 37
      apps/remix-ide-e2e/src/commands/goToVMTraceStep.ts
  35. 31
      apps/remix-ide-e2e/src/commands/journalChildIncludes.ts
  36. 15
      apps/remix-ide-e2e/src/commands/journalLastChild.ts
  37. 21
      apps/remix-ide-e2e/src/commands/journalLastChildIncludes.ts
  38. 20
      apps/remix-ide-e2e/src/commands/modalFooterCancelClick.ts
  39. 20
      apps/remix-ide-e2e/src/commands/modalFooterOKClick.ts
  40. 29
      apps/remix-ide-e2e/src/commands/noWorkerErrorFor.ts
  41. 19
      apps/remix-ide-e2e/src/commands/notContainsText.ts
  42. 27
      apps/remix-ide-e2e/src/commands/openFile.ts
  43. 49
      apps/remix-ide-e2e/src/commands/removeFile.ts
  44. 56
      apps/remix-ide-e2e/src/commands/renameFile.ts
  45. 37
      apps/remix-ide-e2e/src/commands/rightClick.ts
  46. 16
      apps/remix-ide-e2e/src/commands/scrollAndClick.ts
  47. 24
      apps/remix-ide-e2e/src/commands/scrollInto.ts
  48. 17
      apps/remix-ide-e2e/src/commands/selectAccount.ts
  49. 23
      apps/remix-ide-e2e/src/commands/selectContract.ts
  50. 21
      apps/remix-ide-e2e/src/commands/sendLowLevelTx.ts
  51. 23
      apps/remix-ide-e2e/src/commands/setEditorValue.ts
  52. 16
      apps/remix-ide-e2e/src/commands/setSolidityCompilerVersion.ts
  53. 40
      apps/remix-ide-e2e/src/commands/setupMetamask.ts
  54. 44
      apps/remix-ide-e2e/src/commands/signMessage.ts
  55. 21
      apps/remix-ide-e2e/src/commands/switchBrowserTab.ts
  56. 28
      apps/remix-ide-e2e/src/commands/switchBrowserWindow.ts
  57. 37
      apps/remix-ide-e2e/src/commands/testConstantFunction.ts
  58. 27
      apps/remix-ide-e2e/src/commands/testContracts.ts
  59. 14
      apps/remix-ide-e2e/src/commands/testEditorValue.ts
  60. 62
      apps/remix-ide-e2e/src/commands/testFunction.ts
  61. 34
      apps/remix-ide-e2e/src/commands/verifyCallReturnValue.ts
  62. 71
      apps/remix-ide-e2e/src/commands/verifyContracts.ts
  63. 25
      apps/remix-ide-e2e/src/commands/waitForElementContainsText.ts
  64. 249
      apps/remix-ide-e2e/src/examples/example-contracts.ts
  65. BIN
      apps/remix-ide-e2e/src/extensions/chrome/metamask.crx
  66. 37
      apps/remix-ide-e2e/src/helpers/init.ts
  67. 318
      apps/remix-ide-e2e/src/tests/ballot.test.ts
  68. 103
      apps/remix-ide-e2e/src/tests/compiler_api.test.ts
  69. 116
      apps/remix-ide-e2e/src/tests/debugger.test.ts
  70. 80
      apps/remix-ide-e2e/src/tests/defaultLayout.test.ts
  71. 199
      apps/remix-ide-e2e/src/tests/editor.test.ts
  72. 111
      apps/remix-ide-e2e/src/tests/fileExplorer.test.ts
  73. 191
      apps/remix-ide-e2e/src/tests/fileManager_api.test.ts
  74. 185
      apps/remix-ide-e2e/src/tests/generalSettings.test.ts
  75. 98
      apps/remix-ide-e2e/src/tests/gist.test.ts
  76. 127
      apps/remix-ide-e2e/src/tests/libraryDeployment.test.ts
  77. 155
      apps/remix-ide-e2e/src/tests/pluginManager.test.ts
  78. 66
      apps/remix-ide-e2e/src/tests/publishContract.test.ts
  79. 300
      apps/remix-ide-e2e/src/tests/recorder.test.ts
  80. 141
      apps/remix-ide-e2e/src/tests/remixd.test.ts
  81. 208
      apps/remix-ide-e2e/src/tests/runAndDeploy.ts
  82. 60
      apps/remix-ide-e2e/src/tests/sauce.ts
  83. 97
      apps/remix-ide-e2e/src/tests/signingMessage.test.ts
  84. 89
      apps/remix-ide-e2e/src/tests/solidityImport.test.ts
  85. 382
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  86. 284
      apps/remix-ide-e2e/src/tests/specialFunctions.test.ts
  87. 70
      apps/remix-ide-e2e/src/tests/staticAnalysis.test.ts
  88. 104
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  89. 211
      apps/remix-ide-e2e/src/tests/transactionExecution.test.ts
  90. 53
      apps/remix-ide-e2e/src/tests/txListener.test.ts
  91. 52
      apps/remix-ide-e2e/src/tests/usingWebWorker.test.ts
  92. 20
      apps/remix-ide-e2e/src/tests/workspace.test.ts
  93. 84
      apps/remix-ide-e2e/src/types/index.d.ts
  94. 9
      apps/remix-ide-e2e/tsconfig.e2e.json
  95. 9
      apps/remix-ide-e2e/tsconfig.json
  96. 3
      apps/remix-ide/.babelrc
  97. 1
      apps/remix-ide/.dockerignore
  98. 3
      apps/remix-ide/.env
  99. 29
      apps/remix-ide/.eslintrc
  100. 15
      apps/remix-ide/.gitignore
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,282 @@
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2
jobs:
remix-libs:
docker:
# specify the version you desire here
- image: circleci/node:10.18.0-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
resource_class: xlarge
# - image: circleci/mongo:3.4.4
environment:
- COMMIT_AUTHOR_EMAIL: "yann@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
working_directory: ~/remix-project
steps:
- checkout
- run: npm install
- run: npm run lint:libs
- run: npm run build:libs
- run: npm run test:libs
remix-ide-chrome:
docker:
# specify the version you desire here
- image: circleci/node:10.18.0-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
resource_class: xlarge
# - image: circleci/mongo:3.4.4
environment:
- COMMIT_AUTHOR_EMAIL: "yann@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
- FILES_TO_PACKAGE: "dist/apps/remix-ide/assets dist/apps/remix-ide/index.html dist/apps/remix-ide/main.js dist/apps/remix-ide/polyfills.js dist/apps/remix-ide/runtime.js dist/apps/remix-ide/vendor.js dist/apps/remix-ide/favicon.ico"
working_directory: ~/remix-project
parallelism: 20
steps:
- checkout
- run: npm install
- run: npm run lint
- run: npm run build:libs
- run: npm run downloadsolc_root
- run: npm run build
- run:
name: Download Compatible JAVA Version for Selenium
command: |
java -version
sudo apt-get purge --auto-remove java-common
sudo apt-get update
sudo apt-get install openjdk-8-jdk
java -version
- run:
name: Download Selenium
command: ./node_modules/.bin/selenium-standalone install --config=../remix-project/apps/remix-ide-e2e/seleniumConfig.js
- run:
name: Start Selenium
command: ./node_modules/.bin/selenium-standalone start --config=../remix-project/apps/remix-ide-e2e/seleniumConfig.js
background: true
- run: ./apps/remix-ide/ci/browser_tests_chrome.sh
- store_test_results:
path: ./reports/tests
- store_artifacts:
path: ./reports/screenshots
remix-ide-firefox:
docker:
# specify the version you desire here
- image: circleci/node:10.18.0-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
resource_class: xlarge
# - image: circleci/mongo:3.4.4
environment:
- COMMIT_AUTHOR_EMAIL: "yann@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
- FILES_TO_PACKAGE: "dist/apps/remix-ide/assets dist/apps/remix-ide/index.html dist/apps/remix-ide/main.js dist/apps/remix-ide/polyfills.js dist/apps/remix-ide/runtime.js dist/apps/remix-ide/vendor.js dist/apps/remix-ide/favicon.ico"
working_directory: ~/remix-project
parallelism: 20
steps:
- checkout
- run: npm install
- run: npm run lint
- run: npm run build:libs
- run: npm run downloadsolc_root
- run: npm run build
- run:
name: Download Selenium
command: ./node_modules/.bin/selenium-standalone install --config=../remix-project/apps/remix-ide-e2e/seleniumConfig.js
- run:
name: Start Selenium
command: ./node_modules/.bin/selenium-standalone start --config=../remix-project/apps/remix-ide-e2e/seleniumConfig.js
background: true
- run:
name: Download Latest Firefox
command: sudo apt-get purge -y firefox && wget https://sourceforge.net/projects/ubuntuzilla/files/mozilla/apt/pool/main/f/firefox-mozilla-build/firefox-mozilla-build_73.0.1-0ubuntu1_amd64.deb
- run:
name: Install Firefox
command: sudo dpkg -i firefox-mozilla-build_73.0.1-0ubuntu1_amd64.deb
- run: ./apps/remix-ide/ci/browser_tests_firefox.sh
- store_test_results:
path: ./reports/tests
- store_artifacts:
path: ./reports/screenshots
remix-ide-run-deploy:
docker:
# specify the version you desire here
- image: circleci/node:10.18.0-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
resource_class: xlarge
# - image: circleci/mongo:3.4.4
environment:
- COMMIT_AUTHOR_EMAIL: "yann@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
- FILES_TO_PACKAGE: "dist/apps/remix-ide/assets dist/apps/remix-ide/index.html dist/apps/remix-ide/main.js dist/apps/remix-ide/polyfills.js dist/apps/remix-ide/runtime.js dist/apps/remix-ide/vendor.js dist/apps/remix-ide/favicon.ico"
working_directory: ~/remix-project
steps:
- checkout
- run: npm install
- run: npm run lint
- run: npm run build:libs
- run: npm run downloadsolc_root
- run: npm run build
- run:
name: Download Compatible JAVA Version for Selenium
command: |
java -version
sudo apt-get purge --auto-remove java-common
sudo apt-get update
sudo apt-get install openjdk-8-jdk
java -version
- run:
name: Download Selenium
command: ./node_modules/.bin/selenium-standalone install --config=../remix-project/apps/remix-ide-e2e/seleniumConfig.js
- run:
name: Start Selenium
command: ./node_modules/.bin/selenium-standalone start --config=../remix-project/apps/remix-ide-e2e/seleniumConfig.js
background: true
- run: ./apps/remix-ide/ci/browser_tests_run_deploy.sh
- store_test_results:
path: ./reports/tests
- store_artifacts:
path: ./reports/screenshots
deploy-remix-live:
docker:
# specify the version you desire here
- image: circleci/node:10.18.0-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
resource_class: xlarge
# - image: circleci/mongo:3.4.4
environment:
- COMMIT_AUTHOR_EMAIL: "yann@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
- FILES_TO_PACKAGE: "dist/apps/remix-ide/assets dist/apps/remix-ide/index.html dist/apps/remix-ide/main.js dist/apps/remix-ide/polyfills.js dist/apps/remix-ide/runtime.js dist/apps/remix-ide/vendor.js dist/apps/remix-ide/favicon.ico"
working_directory: ~/remix-project
steps:
- checkout
- run: npm install
- run: npm run lint
- run: npm run build:libs
- run: npm run downloadsolc_root
- run: npm run build
- run:
name: Deploy
command: |
if [ "${CIRCLE_BRANCH}" == "remix_live" ]; then
./apps/remix-ide/ci/deploy_from_travis_remix-live.sh;
fi
publish:
docker:
# specify the version you desire here
- image: circleci/node:10.19.0-buster
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
resource_class: xlarge
# - image: circleci/mongo:3.4.4
environment:
- COMMIT_AUTHOR_EMAIL: "yann@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
- FILES_TO_PACKAGE: "dist/apps/remix-ide/assets dist/apps/remix-ide/index.html dist/apps/remix-ide/main.js dist/apps/remix-ide/polyfills.js dist/apps/remix-ide/runtime.js dist/apps/remix-ide/vendor.js dist/apps/remix-ide/favicon.ico"
working_directory: ~/remix-project
steps:
- checkout
- setup_remote_docker
- run: npm install
- run: npm run build:libs
- run: npm run downloadsolc_root
- run: npm run build
- run: ./apps/remix-ide/ci/build_and_publish_docker_images.sh
- run: ./apps/remix-ide/ci/publishIpfs
deploy-remix-alpha:
docker:
# specify the version you desire here
- image: circleci/node:10.18.0-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
resource_class: xlarge
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/mongo:3.4.4
environment:
- COMMIT_AUTHOR_EMAIL: "yann@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
- FILES_TO_PACKAGE: "dist/apps/remix-ide/assets dist/apps/remix-ide/index.html dist/apps/remix-ide/main.js dist/apps/remix-ide/polyfills.js dist/apps/remix-ide/runtime.js dist/apps/remix-ide/vendor.js dist/apps/remix-ide/favicon.ico"
working_directory: ~/remix-project
steps:
- checkout
- run: npm install
- run: npm run lint
- run: npm run build:libs
- run: npm run downloadsolc_root
- run: npm run build
- run:
name: Deploy
command: |
if [ "${CIRCLE_BRANCH}" == "master" ]; then
./apps/remix-ide/ci/deploy_from_travis_remix-alpha.sh;
fi
workflows:
version: 2
build_all:
jobs:
- remix-libs
- remix-ide-chrome:
requires:
- remix-libs
- remix-ide-firefox:
requires:
- remix-libs
- remix-ide-run-deploy:
requires:
- remix-libs
- publish:
requires:
- remix-ide-chrome
- remix-ide-firefox
- remix-ide-run-deploy
- deploy-remix-live:
requires:
- remix-ide-chrome
- remix-ide-firefox
- remix-ide-run-deploy
filters:
branches:
only: remix_live
- deploy-remix-alpha:
requires:
- remix-ide-chrome
- remix-ide-firefox
- remix-ide-run-deploy
filters:
branches:
only: master

@ -0,0 +1,13 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

@ -0,0 +1,3 @@
gist_token=<token>
account_passphrase=<passphrase>
account_password=<password>

@ -0,0 +1,40 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint", "@nrwl/nx"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
"prettier/@typescript-eslint"
],
"rules": {
"@typescript-eslint/explicit-member-accessibility": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-parameter-properties": "off",
"@nrwl/nx/enforce-module-boundaries": [
"error",
{
"enforceBuildableLibDependency": true,
"allow": [],
"depConstraints": [
{ "sourceTag": "*", "onlyDependOnLibsWithTags": ["*"] }
]
}
]
},
"overrides": [
{
"files": ["*.tsx"],
"rules": {
"@typescript-eslint/no-unused-vars": "off"
}
}
]
}

54
.gitignore vendored

@ -0,0 +1,54 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
build
dist
node_modules
lint.xml
reports/*
babelify-src
docs/_build
.DS_Store
.tern-port
TODO
soljson.js
*~
# compiled output
/dist
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
lerna-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db

@ -0,0 +1,4 @@
# Add files here to ignore them from prettier formatting
/dist
/coverage

@ -0,0 +1,3 @@
{
"singleQuote": true
}

@ -0,0 +1,17 @@
# Contributing
Everyone is very welcome to contribute on the codebase of Remix. Please reach us in [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
## Development
Remix libraries work closely with [Remix IDE](https://remix.ethereum.org). Each library has a readme to explain its application.
When you add a code in any library, please ensure you add related tests. You can visit [here](https://github.com/ethereum/remix-ide#installation) to test your changes by linking the remix libraries with Remix IDE.
## Coding style
Please conform to [standard](https://standardjs.com/) for code styles.
## Submitting Pull Request
Please follow Github's standard model of making changes & submitting pull request which is very well explained [here](https://guides.github.com/activities/forking/). Make sure your code works fine locally before submitting a pull request.

@ -0,0 +1,6 @@
FROM nginx:alpine
WORKDIR /
COPY ./temp_publish_docker/ /usr/share/nginx/html/
EXPOSE 80

@ -0,0 +1,28 @@
# This dockerfile is to build each branch seperately (for dev purposes)
FROM node:10
# Create Remix user, don't use root!
# RUN yes | adduser --disabled-password remix && mkdir /app
# USER remix
# #Now do remix stuff
# USER remix
WORKDIR /home/remix
COPY ./ ./
RUN npm ci
RUN npm run build
FROM nginx:alpine
WORKDIR /
COPY --from=0 /home/remix/build/ /usr/share/nginx/html/build/
COPY --from=0 /home/remix/index.html /usr/share/nginx/html/index.html
COPY --from=0 /home/remix/nginx.conf /etc/nginx/nginx.conf
COPY --from=0 /home/remix/assets/ /usr/share/nginx/html/assets/
COPY --from=0 /home/remix/icon.png /usr/share/nginx/html/icon.png
COPY --from=0 /home/remix/background.js /usr/share/nginx/html/background.js
COPY --from=0 /home/remix/soljson.js /usr/share/nginx/html/soljson.js
COPY --from=0 /home/remix/package.json /usr/share/nginx/html/package.json
EXPOSE 80

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016-2018 Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,188 @@
[![Join the chat at https://gitter.im/ethereum/remix](https://badges.gitter.im/ethereum/remix.svg)](https://gitter.im/ethereum/remix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![CircleCI](https://circleci.com/gh/ethereum/remix-project.svg?style=svg)](https://circleci.com/gh/ethereum/remix-project)
[![Documentation Status](https://readthedocs.org/projects/docs/badge/?version=latest)](https://remix-ide.readthedocs.io/en/latest/index.html)
# Remix
Remix is a browser-based compiler and IDE that enables users to build **Ethereum contracts with Solidity language** and to debug transactions.
To try it out, visit [https://remix.ethereum.org](https://remix.ethereum.org).
https://github.com/ethereum/remix-project/releases also gives others ways to use Remix locally. Please check it out.
Remix consists of many modules and in this repository you will find Remix related apps, libs and plugins. (remix-ide, remix-analyzer, etc.).
![Remix screenshot](https://github.com/ethereum/remix-project/raw/master/apps/remix-ide/remix_screenshot.png)
## Offline Usage
The `gh-pages` branch has always the latest stable build of Remix. It also contains a ZIP file with the entire build. Download it to use offline.
Note: It contains the latest release of Solidity available at the time of the packaging. No other compiler versions are supported.
## INSTALLATION:
Install **npm** and **node.js** (see https://docs.npmjs.com/getting-started/installing-node), then
install [Nx CLI](https://nx.dev/react/cli/overview) globally to enable running **nx executable commands**.
```bash
npm install -g @nrwl/cli
```
clone the github repository (`wget` need to be installed first) :
```bash
git clone https://github.com/ethereum/remix-project.git
cd remix-project
npm install
nx build remix-ide --with-deps
nx serve
```
## Docker:
Prerequisites:
* Docker (https://docs.docker.com/desktop/)
* Docker-compose (https://docs.docker.com/compose/install/)
### Run with docker
If you want to run latest changes that are merged into master branch then run:
```
docker pull remixproject/remix-ide:latest
docker run -p 8080:80 remixproject/remix-ide:latest
```
If you want to run latest remix-live release run.
```
docker pull remixproject/remix-ide:remix_live
docker run -p 8080:80 remixproject/remix-ide:remix_live
```
### Run with docker-compose:
To run locally without building you only need docker-compose.yaml file and you can run:
```
docker-compose pull
docker-compose up -d
```
Then go to http://localhost:8080 and you can use you Remix instance.
To fetch docker-compose file without cloning this repo run:
```
curl https://raw.githubusercontent.com/ethereum/remix-ide/master/docker-compose.yaml > docker-compose.yaml
```
## DEVELOPING:
Run `nx serve` and open `http://127.0.0.1:8080` in your browser.
Then open your `text editor` and start developing.
The browser will automatically refresh when files are saved.
### Troubleshooting building
Some things to consider if you have trouble building the package:
- Make sure that you have the correct version of `node`, `npm` and `nvm`. Also ensure you have [Nx CLI](https://nx.dev/react/cli/overview) installed globally. You can find the version that is tested on Travis CI by looking at the log in the [build results](https://travis-ci.org/ethereum/remix-ide).
Run:
```bash
node --version
npm --version
nvm --version
```
- In Debian based OS such as Ubuntu 14.04LTS you may need to run `apt-get install build-essential`. After installing `build-essential` run `npm rebuild`.
## Unit Testing
Run the unit tests via: `nx test <project-name>`
```bash
nx test remix-analyzer
```
Running unit tests via `nx test` requires at least node v10.0.0
## Browser Testing
To run the Selenium tests via Nightwatch:
- Build Remix IDE and serve it: `nx build remix-ide --with-deps && nx serve` # starts web server at localhost:8080
- Make sure Selenium is installed `npm run selenium-install` # don't need to repeat
- Run a selenium server `npm run selenium`
- Run all the tests `npm run nightwatch_local_firefox` or `npm run nightwatch_local_chrome`
- Or run a specific test case:
- npm run nightwatch_local_ballot
- npm run nightwatch_local_usingWorker
- npm run nightwatch_local_libraryDeployment
- npm run nightwatch_local_solidityImport
- npm run nightwatch_local_recorder
- npm run nightwatch_local_transactionExecution
- npm run nightwatch_local_staticAnalysis
- npm run nightwatch_local_signingMessage
- npm run nightwatch_local_specialFunctions
- npm run nightwatch_local_solidityUnitTests
- npm run nightwatch_local_remixd # remixd needs to be run
- npm run nightwatch_local_terminal
- npm run nightwatch_local_gist
- npm run nightwatch_local_workspace
- npm run nightwatch_local_defaultLayout
- npm run nightwatch_local_pluginManager
- npm run nightwatch_local_publishContract
- npm run nightwatch_local_generalSettings
- npm run nightwatch_local_fileExplorer
- npm run nightwatch_local_debugger
- npm run nightwatch_local_editor
- npm run nightwatch_local_compiler
- npm run nightwatch_local_txListener
- npm run nightwatch_local_fileManager
- npm run nightwatch_local_runAndDeploy
**NOTE:**
- **the `ballot` tests suite** requires to run `ganache-cli` locally.
- **the `remixd` tests suite** requires to run `remixd` locally.
- **the `gist` tests suite** requires specifying a github access token in **.env file**.
```
gist_token = <token>
```
**note that this token should have permission to create a gist.**
## Documentation
To see details about how to use Remix for developing and/or debugging Solidity contracts, please see [our documentation page](https://remix-ide.readthedocs.io/en/latest/)

@ -0,0 +1,14 @@
{
"rules": {},
"overrides": [
{
"files": ["**/*.ts"],
"rules": {
"no-undef": "off",
"@typescript-eslint/no-var-requires": 0
}
}
],
"extends": ["../../.eslintrc"],
"ignorePatterns": ["!**/*"]
}

@ -0,0 +1,83 @@
import * as fs from 'fs'
const crxFile = fs.readFileSync('apps/remix-ide-e2e/src/extensions/chrome/metamask.crx')
const metamaskExtension = Buffer.from(crxFile).toString('base64')
module.exports = {
'src_folders': ['dist/apps/remix-ide-e2e/src/tests'],
'output_folder': './reports/tests',
'custom_commands_path': ['dist/apps/remix-ide-e2e/src/commands'],
'custom_assertions_path': '',
'page_objects_path': '',
'globals_path': '',
'test_settings': {
'default': {
'selenium_port': 4444,
'selenium_host': 'localhost',
'globals': {
'waitForConditionTimeout': 10000,
'asyncHookTimeout': 100000
},
'screenshots': {
'enabled': true,
'path': './reports/screenshots',
'on_failure': true,
'on_error': true
},
'desiredCapabilities': {
'browserName': 'firefox',
'javascriptEnabled': true,
'acceptSslCerts': true
},
'exclude': ['dist/apps/remix-ide-e2e/src/tests/runAndDeploy.js']
},
'chrome': {
'desiredCapabilities': {
'browserName': 'chrome',
'javascriptEnabled': true,
'acceptSslCerts': true,
'goog:chromeOptions': {
'args': ['window-size=2560,1440', 'start-fullscreen']
}
}
},
'chrome-runAndDeploy': {
'desiredCapabilities': {
'browserName': 'chrome',
'javascriptEnabled': true,
'acceptSslCerts': true,
'goog:chromeOptions': {
'args': ['window-size=2560,1440', 'start-fullscreen'],
'extensions': [metamaskExtension]
}
}
},
'safari': {
'desiredCapabilities': {
'browserName': 'safari',
'javascriptEnabled': true,
'acceptSslCerts': true
}
},
'ie': {
'desiredCapabilities': {
'browserName': 'internet explorer',
'javascriptEnabled': true,
'acceptSslCerts': true
}
},
'firefox': {
'desiredCapabilities': {
'browserName': 'firefox',
'javascriptEnabled': true,
'acceptSslCerts': true
}
}
}
}

@ -0,0 +1,11 @@
module.exports = {
version: '3.8.1',
baseURL: 'https://selenium-release.storage.googleapis.com',
drivers: {
chrome: {
version: '2.39',
arch: process.arch,
baseURL: 'https://chromedriver.storage.googleapis.com'
}
}
}

@ -0,0 +1,36 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
class addAtAddressInstance extends EventEmitter {
command (this: NightwatchBrowser, address: string, isValidFormat: boolean, isValidChecksum: boolean): NightwatchBrowser {
this.api.perform((done: VoidFunction) => {
addInstance(this.api, address, isValidFormat, isValidChecksum, () => {
done()
this.emit('complete')
})
})
return this
}
}
function addInstance (browser: NightwatchBrowser, address: string, isValidFormat: boolean, isValidChecksum: boolean, callback: VoidFunction) {
browser.clickLaunchIcon('udapp').clearValue('.ataddressinput').setValue('.ataddressinput', address, function () {
browser.click('button[id^="runAndDeployAtAdressButton"]')
.execute(function () {
const ret = document.querySelector('div[class^="modal-body"] div').innerHTML
const modal = document.querySelector('#modal-footer-ok') as HTMLElement
modal.click()
return ret
}, [], function (result) {
if (!isValidFormat) {
browser.assert.equal(result.value, 'Invalid address.')
} else if (!isValidChecksum) {
browser.assert.equal(result.value, 'Invalid checksum address.')
}
callback()
})
})
}
module.exports = addAtAddressInstance

@ -0,0 +1,39 @@
import { NightwatchBrowser, NightwatchContractContent } from 'nightwatch'
import EventEmitter from "events"
class AddFile extends EventEmitter {
command (this: NightwatchBrowser, name: string, content: NightwatchContractContent): NightwatchBrowser {
this.api.perform((done) => {
addFile(this.api, name, content, () => {
done()
this.emit('complete')
})
})
return this
}
}
function addFile (browser: NightwatchBrowser, name: string, content: NightwatchContractContent, done: VoidFunction) {
browser.clickLaunchIcon('udapp').clickLaunchIcon('fileExplorers').click('.newFile')
.waitForElementVisible('#modal-dialog')
.perform((client, done) => {
browser.execute(function (fileName) {
if (fileName !== 'Untitled.sol') {
document.querySelector('#modal-dialog #prompt_text').setAttribute('value', fileName)
}
const elem = document.querySelector('#modal-footer-ok') as HTMLElement
elem.click()
}, [name], function (result) {
console.log(result)
done()
})
})
.setEditorValue(content.content)
.pause(1000)
.perform(function () {
done()
})
}
module.exports = AddFile

@ -0,0 +1,31 @@
import EventEmitter from 'events'
import { NightwatchBrowser } from 'nightwatch'
class checkElementStyle extends EventEmitter {
command (this: NightwatchBrowser, cssSelector: string, styleProperty: string, expectedResult: string): NightwatchBrowser {
this.api.perform((done) => {
checkStyle(this.api, cssSelector, styleProperty, expectedResult, () => {
done()
this.emit('complete')
})
})
return this
}
}
function checkStyle (browser: NightwatchBrowser, cssSelector: string, styleProperty: string, expectedResult: string, callback: VoidFunction) {
browser.execute(function (cssSelector, styleProperty) {
return window.getComputedStyle(document.querySelector(cssSelector)).getPropertyValue(styleProperty)
}, [cssSelector, styleProperty], function (result) {
const value = result.value
if (typeof value === 'string') {
browser.assert.equal(value.trim().toLowerCase(), expectedResult.toLowerCase())
} else {
browser.assert.fail('Failed with error info :', result.value.toString())
}
callback()
})
}
module.exports = checkElementStyle

@ -0,0 +1,36 @@
import EventEmitter from 'events'
import { NightwatchBrowser } from 'nightwatch'
class CheckTerminalFilter extends EventEmitter {
command (this: NightwatchBrowser, filter: string, test: string): NightwatchBrowser {
this.api.perform((done) => {
checkFilter(this.api, filter, test, () => {
done()
this.emit('complete')
})
})
return this
}
}
function checkFilter (browser: NightwatchBrowser, filter: string, test: string, done: VoidFunction) {
if (browser.options.desiredCapabilities.browserName === 'chrome') { // nightwatch deos not handle well that part.... works locally
done()
return
}
const filterClass = '[data-id="terminalInputSearch"]'
browser.setValue(filterClass, filter, function () {
browser.execute(function () {
return document.querySelector('[data-id="terminalJournal"]').innerHTML === test
}, [], function (result) {
browser.clearValue(filterClass).setValue(filterClass, '', function () {
if (!result.value) {
browser.assert.fail('useFilter on ' + filter + ' ' + test, 'info about error', '')
}
done()
})
})
})
}
module.exports = CheckTerminalFilter

@ -0,0 +1,42 @@
import { NightwatchBrowser, NightwatchCheckVariableDebugValue } from 'nightwatch'
import EventEmitter from "events"
const deepequal = require('deep-equal')
class CheckVariableDebug extends EventEmitter {
command (this: NightwatchBrowser, id: string, debugValue: NightwatchCheckVariableDebugValue): NightwatchBrowser {
this.api.perform((done) => {
checkDebug(this.api, id, debugValue, () => {
done()
this.emit('complete')
})
})
return this
}
}
function checkDebug (browser: NightwatchBrowser, id: string, debugValue: NightwatchCheckVariableDebugValue, done: VoidFunction) {
// id is soliditylocals or soliditystate
browser.execute(function (id: string) {
const elem = document.querySelector('#' + id + ' .dropdownrawcontent') as HTMLElement
return elem.innerText
}, [id], function (result) {
console.log(id + ' ' + result.value)
let value
try {
value = JSON.parse(<string>result.value)
} catch (e) {
browser.assert.fail('cant parse solidity state', e.message, '')
done()
return
}
const equal = deepequal(debugValue, value)
if (!equal) {
browser.assert.fail('checkDebug on ' + id, 'info about error\n ' + JSON.stringify(debugValue) + '\n ' + JSON.stringify(value), '')
}
done()
})
}
module.exports = CheckVariableDebug

@ -0,0 +1,31 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class clearEditablecontent extends EventEmitter {
command (this: NightwatchBrowser, cssSelector: string): NightwatchBrowser {
this.api.perform((done) => {
clearContent(this.api, cssSelector, () => {
done()
this.emit('complete')
})
})
return this
}
}
function clearContent (browser: NightwatchBrowser, cssSelector: string, callback: VoidFunction) {
browser.execute(function (cssSelector) {
const selection = window.getSelection()
const range = document.createRange()
range.selectNodeContents(document.querySelector(cssSelector))
selection.removeAllRanges()
selection.addRange(range)
}, [cssSelector], function () {
browser.sendKeys(cssSelector, browser.Keys.BACK_SPACE)
.pause(5000)
callback()
})
}
module.exports = clearEditablecontent

@ -0,0 +1,27 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class ClickElement extends EventEmitter {
command (this: NightwatchBrowser, cssSelector: string, index = 0): NightwatchBrowser {
this.api.perform((done) => {
_clickElement(this.api, cssSelector, index, () => {
done()
this.emit('complete')
})
})
return this
}
}
function _clickElement (browser: NightwatchBrowser, cssSelector: string, index: number, cb: VoidFunction) {
browser.waitForElementPresent(cssSelector)
.execute(function (cssSelector: string, index: number) {
const elem = document.querySelectorAll(cssSelector)[index] as HTMLElement
elem.click()
}, [cssSelector, index], function () {
cb()
})
}
module.exports = ClickElement

@ -0,0 +1,26 @@
import { NightwatchBrowser, NightwatchClickFunctionExpectedInput } from 'nightwatch'
import EventEmitter from "events"
class ClickFunction extends EventEmitter {
command (this: NightwatchBrowser, fnFullName: string, expectedInput?: NightwatchClickFunctionExpectedInput): NightwatchBrowser {
this.api.waitForElementPresent('.instance button[title="' + fnFullName + '"]')
.perform(function (client, done) {
client.execute(function () {
document.querySelector('#runTabView').scrollTop = document.querySelector('#runTabView').scrollHeight
}, [], function () {
if (expectedInput) {
client.setValue('#runTabView input[title="' + expectedInput.types + '"]', expectedInput.values, _ => _)
}
done()
})
})
.click('.instance button[title="' + fnFullName + '"]')
.pause(2000)
.perform(() => {
this.emit('complete')
})
return this
}
}
module.exports = ClickFunction

@ -0,0 +1,14 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
class ClickInstance extends EventEmitter {
command (this: NightwatchBrowser, index: number): NightwatchBrowser {
index = index + 2
const selector = '.instance:nth-of-type(' + index + ') > div > button'
this.api.waitForElementPresent(selector).scrollAndClick(selector).perform(() => { this.emit('complete') })
return this
}
}
module.exports = ClickInstance

@ -0,0 +1,14 @@
import { NightwatchBrowser } from "nightwatch"
import EventEmitter from "events"
class ClickLaunchIcon extends EventEmitter {
command (this: NightwatchBrowser, icon: string): NightwatchBrowser {
this.api.waitForElementVisible('#icon-panel div[plugin="' + icon + '"]').click('#icon-panel div[plugin="' + icon + '"]').perform((done) => {
done()
this.emit('complete')
})
return this
}
}
module.exports = ClickLaunchIcon

@ -0,0 +1,32 @@
import { NightwatchBrowser } from "nightwatch"
import EventEmitter from "events"
class CreateContract extends EventEmitter {
command (this: NightwatchBrowser, inputParams: string): NightwatchBrowser {
this.api.perform((done) => {
createContract(this.api, inputParams, () => {
done()
this.emit('complete')
})
})
return this
}
}
function createContract (browser: NightwatchBrowser, inputParams: string, callback: VoidFunction) {
if (inputParams) {
browser.clickLaunchIcon('settings').clickLaunchIcon('udapp')
.setValue('div[class^="contractActionsContainerSingle"] input', inputParams, function () {
browser.click('#runTabView button[class^="instanceButton"]').pause(500).perform(function () { callback() })
})
} else {
browser
.clickLaunchIcon('settings')
.clickLaunchIcon('udapp')
.click('#runTabView button[class^="instanceButton"]')
.pause(500)
.perform(function () { callback() })
}
}
module.exports = CreateContract

@ -0,0 +1,26 @@
import { NightwatchBrowser } from "nightwatch"
import EventEmitter from "events"
class debugTransaction extends EventEmitter {
command (this: NightwatchBrowser, index = 0): NightwatchBrowser {
this.api.perform((done) => {
checkStyle(this.api, index, () => {
done()
this.emit('complete')
})
})
return this
}
}
function checkStyle (browser: NightwatchBrowser, index: number, callback: VoidFunction) {
browser.pause(2000).execute(function (index: number) {
const debugBtn = document.querySelectorAll('*[data-shared="txLoggerDebugButton"]')[index] as HTMLInputElement
debugBtn.click()
}, [index], function () {
callback()
})
}
module.exports = debugTransaction

@ -0,0 +1,22 @@
import { NightwatchBrowser } from "nightwatch"
import EventEmitter from "events"
// fix for editor scroll
class ScrollEditor extends EventEmitter {
command (this: NightwatchBrowser, direction: 'up' | 'down', numberOfTimes: number): NightwatchBrowser {
const browser = this.api
browser.waitForElementPresent('.ace_text-input')
for (let i = 0; i < numberOfTimes; i++) {
if (direction.toLowerCase() === 'up') browser.sendKeys('.ace_text-input', browser.Keys.ARROW_UP)
if (direction.toLowerCase() === 'down') browser.sendKeys('.ace_text-input', browser.Keys.ARROW_DOWN)
}
browser.perform((done) => {
done()
this.emit('complete')
})
return this
}
}
module.exports = ScrollEditor

@ -0,0 +1,19 @@
import { NightwatchBrowser } from "nightwatch"
import EventEmitter from "events"
class ExecuteScript extends EventEmitter {
command (this: NightwatchBrowser, script: string): NightwatchBrowser {
this.api
.clearEditableContent('*[data-id="terminalCliInput"]')
.click('*[data-id="terminalCli"]')
.sendKeys('*[data-id="terminalCliInput"]', script)
.sendKeys('*[data-id="terminalCliInput"]', this.api.Keys.ENTER)
.sendKeys('*[data-id="terminalCliInput"]', this.api.Keys.ENTER)
.perform(() => {
this.emit('complete')
})
return this
}
}
module.exports = ExecuteScript

@ -0,0 +1,31 @@
import { NightwatchBrowser } from "nightwatch"
import EventEmitter from "events"
class GetAddressAtPosition extends EventEmitter {
command (this: NightwatchBrowser, index: number, cb: (pos: string) => void): NightwatchBrowser {
this.api.perform((done) => {
getAddressAtPosition(this.api, index, (pos) => {
done()
cb(pos)
this.emit('complete')
})
})
return this
}
}
function getAddressAtPosition (browser: NightwatchBrowser, index: number, callback: (pos: string) => void) {
browser.waitForElementPresent('*[data-shared="universalDappUiInstance"]')
.execute(function (index) {
const deployedContracts = document.querySelectorAll('*[data-shared="universalDappUiInstance"]')
const id = deployedContracts[index].getAttribute('id')
return id.replace('instance', '')
}, [index], function (result) {
const pos = typeof result.value === 'string' ? result.value : null
callback(pos)
})
}
module.exports = GetAddressAtPosition

@ -0,0 +1,23 @@
import { NightwatchBrowser } from "nightwatch"
import EventEmitter from "events"
class GetEditorValue extends EventEmitter {
command (this: NightwatchBrowser, callback: (content: string) => void): NightwatchBrowser {
this.api.perform((client, done) => {
this.api.execute(function () {
const elem: any = document.getElementById('input')
return elem.editor.getValue()
}, [], (result) => {
done()
const value = typeof result.value === 'string' ? result.value : null
callback(value)
this.emit('complete')
})
})
return this
}
}
module.exports = GetEditorValue

@ -0,0 +1,28 @@
import { NightwatchBrowser } from "nightwatch"
import EventEmitter from "events"
class GetInstalledPlugins extends EventEmitter {
command (this: NightwatchBrowser, cb: (plugins: string[]) => void): NightwatchBrowser {
const browser = this.api
browser.waitForElementPresent('[plugin]:not([plugin=""]')
.perform((done) => {
browser.execute(function() {
const pluginNames = []
const plugins = document.querySelectorAll('[plugin]:not([plugin=""]')
plugins.forEach(plugin => {
pluginNames.push(plugin.getAttribute('plugin'))
})
return pluginNames
}, [], (result) => {
done()
Array.isArray(result.value) && cb(result.value)
this.emit('complete')
})
})
return this
}
}
module.exports = GetInstalledPlugins

@ -0,0 +1,19 @@
import { NightwatchBrowser } from "nightwatch"
import EventEmitter from "events"
class GetModalBody extends EventEmitter {
command (this: NightwatchBrowser, callback: (value: string, cb: VoidFunction) => void) {
this.api.waitForElementVisible('.modal-body')
.getText('.modal-body', (result) => {
console.log(result)
const value = typeof result.value === 'string' ? result.value : null
callback(value, () => {
this.emit('complete')
})
})
return this
}
}
module.exports = GetModalBody

@ -0,0 +1,37 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
class GoToVmTraceStep extends EventEmitter {
command (this: NightwatchBrowser, step: number, incr?: number): NightwatchBrowser {
this.api.perform((done) => {
goToVMtraceStep(this.api, step, incr, () => {
done()
this.emit('complete')
})
})
return this
}
}
function goToVMtraceStep (browser: NightwatchBrowser, step: number, incr: number, done: VoidFunction) {
if (!incr) incr = 0
browser.execute(function () {
return document.querySelector('#stepdetail').innerHTML
}, [], function (result) {
if (typeof result.value === 'string' && ( result.value.indexOf('vm trace step:') !== -1 && result.value.indexOf(step.toString()) !== -1)) {
done()
} else if (incr > 1000) {
browser.assert.fail('goToVMtraceStep fails', 'info about error', '')
done()
} else {
incr++
browser.click('#intoforward')
.perform(() => {
setTimeout(() => {
goToVMtraceStep(browser, step, incr, done)
}, 200)
})
}
})
}
module.exports = GoToVmTraceStep

@ -0,0 +1,31 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
/*
Checks if any child elements of journal (console) contains a matching value.
*/
class JournalChildIncludes extends EventEmitter {
command (this: NightwatchBrowser, val: string): NightwatchBrowser {
let isTextFound = false
const browser = this.api
this.api.elements('css selector', '*[data-id="terminalJournal"]', (res) => {
Array.isArray(res.value) && res.value.forEach(function (jsonWebElement) {
const jsonWebElementId = jsonWebElement.ELEMENT || jsonWebElement[Object.keys(jsonWebElement)[0]]
browser.elementIdText(jsonWebElementId, (jsonElement) => {
const text = jsonElement.value
if (typeof text === 'string' && text.indexOf(val) !== -1) isTextFound = true
})
})
})
browser.perform(() => {
browser.assert.ok(isTextFound, isTextFound ? `<*[data-id="terminalJournal"]> contains ${val}.` : `${val} not found in <*[data-id="terminalJournal"]> div:last-child>`)
this.emit('complete')
})
return this
}
}
module.exports = JournalChildIncludes

@ -0,0 +1,15 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
class JournalLastChild extends EventEmitter {
command (this: NightwatchBrowser, val: string): NightwatchBrowser {
this.api
.waitForElementVisible('*[data-id="terminalJournal"] > div:last-child', 10000)
.assert.containsText('*[data-id="terminalJournal"] > div:last-child', val).perform(() => {
this.emit('complete')
})
return this
}
}
module.exports = JournalLastChild

@ -0,0 +1,21 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
/*
Check if the last log in the console contains a specific text
*/
class JournalLastChildIncludes extends EventEmitter {
command (this: NightwatchBrowser, val: string): NightwatchBrowser {
this.api
.waitForElementVisible('*[data-id="terminalJournal"] > div:last-child', 10000)
.getText('*[data-id="terminalJournal"] > div:last-child', (result) => {
console.log('JournalLastChildIncludes', result.value)
if (typeof result.value === 'string' && result.value.indexOf(val) === -1) return this.api.assert.fail(`wait for ${val} in ${result.value}`)
else this.api.assert.ok(true, `<*[data-id="terminalJournal"] > div:last-child> contains ${val}.`)
this.emit('complete')
})
return this
}
}
module.exports = JournalLastChildIncludes

@ -0,0 +1,20 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
class ModalFooterOKClick extends EventEmitter {
command (this: NightwatchBrowser): NightwatchBrowser {
this.api.waitForElementVisible('#modal-footer-cancel').perform((client, done) => {
this.api.execute(function () {
const elem = document.querySelector('#modal-footer-cancel') as HTMLElement
elem.click()
}, [], () => {
done()
this.emit('complete')
})
})
return this
}
}
module.exports = ModalFooterOKClick

@ -0,0 +1,20 @@
import { NightwatchBrowser } from "nightwatch"
import EventEmitter from "events"
class ModalFooterOKClick extends EventEmitter {
command (this: NightwatchBrowser): NightwatchBrowser {
this.api.waitForElementVisible('#modal-footer-ok').perform((client, done) => {
this.api.execute(function () {
const elem = document.querySelector('#modal-footer-ok') as HTMLElement
elem.click()
}, [], () => {
done()
this.emit('complete')
})
})
return this
}
}
module.exports = ModalFooterOKClick

@ -0,0 +1,29 @@
import { NightwatchBrowser } from "nightwatch"
import EventEmitter from "events"
class NoWorkerErrorFor extends EventEmitter {
command (this: NightwatchBrowser, version: string): NightwatchBrowser {
this.api.perform((done: VoidFunction) => {
noWorkerErrorFor(this.api, version, () => {
done()
this.emit('complete')
})
})
return this
}
}
function noWorkerErrorFor (browser: NightwatchBrowser, version: string, callback: VoidFunction) {
browser
.setSolidityCompilerVersion(version)
.click('*[data-id="compilerContainerCompileBtn"]')
.waitForElementPresent('*[data-id="compilationFinishedWith_' + version + '"]', 10000)
.notContainsText('*[data-id="compiledErrors"]', 'worker error:undefined')
.notContainsText('*[data-id="compiledErrors"]', 'Uncaught RangeError: Maximum call stack size exceeded')
.notContainsText('*[data-id="compiledErrors"]', 'RangeError: Maximum call stack size exceeded')
.perform(() => {
callback()
})
}
module.exports = NoWorkerErrorFor

@ -0,0 +1,19 @@
import { NightwatchBrowser } from "nightwatch"
import EventEmitter from "events"
class NotContainsText extends EventEmitter {
command (this: NightwatchBrowser, cssSelector: string, text: string): NightwatchBrowser {
const browser = this.api
browser.getText(cssSelector, (result) => {
if (typeof result.value === 'string' && result.value.includes(text)) return this.api.assert.fail(`${cssSelector} contains ${text}.`)
else this.api.assert.ok(true, `${cssSelector} does not contains ${text}.`)
})
.perform(() => {
this.emit('complete')
})
return this
}
}
module.exports = NotContainsText

@ -0,0 +1,27 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class OpenFile extends EventEmitter {
command (this: NightwatchBrowser, name: string) {
this.api.perform((done) => {
openFile(this.api, name, () => {
done()
this.emit('complete')
})
})
return this
}
}
// click on fileExplorer can toggle it. We go through settings to be sure FE is open
function openFile (browser: NightwatchBrowser, name: string, done: VoidFunction) {
browser.clickLaunchIcon('settings').clickLaunchIcon('fileExplorers')
.waitForElementVisible('li[key="' + name + '"]')
.click('li[key="' + name + '"]')
.pause(2000)
.perform(() => {
done()
})
}
module.exports = OpenFile

@ -0,0 +1,49 @@
import { NightwatchBrowser } from "nightwatch"
const EventEmitter = require('events')
class RemoveFile extends EventEmitter {
command (this: NightwatchBrowser, path: string): NightwatchBrowser {
this.api.perform((done) => {
removeFile(this.api, path, () => {
done()
this.emit('complete')
})
})
return this
}
}
function removeFile (browser: NightwatchBrowser, path: string, done: VoidFunction) {
browser.execute(function (path) {
function contextMenuClick (element) {
const evt = element.ownerDocument.createEvent('MouseEvents')
const RIGHT_CLICK_BUTTON_CODE = 2 // the same for FF and IE
evt.initMouseEvent('contextmenu', true, true,
element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
false, false, false, RIGHT_CLICK_BUTTON_CODE, null)
if (Object.prototype.hasOwnProperty.call(document, 'createEventObject')) {
// dispatch for IE
return element.fireEvent('onclick', evt)
} else {
// dispatch for firefox + others
return !element.dispatchEvent(evt)
}
}
contextMenuClick(document.querySelector('[data-path="' + path + '"]'))
}, [path], function () {
browser
.waitForElementVisible('#menuitemdelete', 2000)
.click('#menuitemdelete')
.pause(500)
.waitForElementVisible('#modal-footer-ok', 2000)
.click('#modal-footer-ok')
.waitForElementNotPresent('[data-path="' + path + '"]')
.perform(() => {
done()
})
})
}
module.exports = RemoveFile

@ -0,0 +1,56 @@
import EventEmitter from 'events'
import { NightwatchBrowser } from 'nightwatch'
class RenameFile extends EventEmitter {
command (this: NightwatchBrowser, path: string, newFileName: string, renamedPath: string) {
this.api.perform((done) => {
renameFile(this.api, path, newFileName, renamedPath, () => {
done()
this.emit('complete')
})
})
return this
}
}
function renameFile (browser: NightwatchBrowser, path: string, newFileName: string, renamedPath: string, done: VoidFunction) {
browser.execute(function (path: string) {
function contextMenuClick (element) {
const evt = element.ownerDocument.createEvent('MouseEvents')
const RIGHT_CLICK_BUTTON_CODE = 2 // the same for FF and IE
evt.initMouseEvent('contextmenu', true, true,
element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
false, false, false, RIGHT_CLICK_BUTTON_CODE, null)
if (Object.prototype.hasOwnProperty.call(document, 'createEventObject')) {
// dispatch for IE
return element.fireEvent('onclick', evt)
} else {
// dispatch for firefox + others
return !element.dispatchEvent(evt)
}
}
contextMenuClick(document.querySelector('[data-path="' + path + '"]'))
}, [path], function () {
browser
.click('#menuitemrename')
.perform((client, doneSetValue) => {
browser.execute(function (path, addvalue) {
document.querySelector('[data-path="' + path + '"]').innerHTML = addvalue
}, [path, newFileName], () => {
doneSetValue()
})
})
.click('body') // blur
.waitForElementVisible('#modal-footer-ok', 100000)
.pause(2000)
.click('#modal-footer-ok')
.waitForElementNotPresent('[data-path="' + path + '"]')
.waitForElementPresent('[data-path="' + renamedPath + '"]')
.perform(() => {
done()
})
})
}
module.exports = RenameFile

@ -0,0 +1,37 @@
import EventEmitter from 'events'
import { NightwatchBrowser } from 'nightwatch'
class RightClick extends EventEmitter {
command (this: NightwatchBrowser, cssSelector: string) {
this.api.perform((done) => {
rightClick(this.api, cssSelector, () => {
done()
this.emit('complete')
})
})
return this
}
}
function rightClick (browser: NightwatchBrowser, cssSelector: string, callback: VoidFunction) {
browser.execute(function (cssSelector: string) {
const element: any = document.querySelector(cssSelector)
const evt = element.ownerDocument.createEvent('MouseEvents')
const RIGHT_CLICK_BUTTON_CODE = 2
evt.initMouseEvent('contextmenu', true, true,
element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
false, false, false, RIGHT_CLICK_BUTTON_CODE, null)
if (Object.prototype.hasOwnProperty.call(document, 'createEventObject')) {
// dispatch for IE
return element.fireEvent('onclick', evt)
} else {
// dispatch for firefox + others
return !element.dispatchEvent(evt)
}
}, [cssSelector], function () {
callback()
})
}
module.exports = RightClick

@ -0,0 +1,16 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
class scrollAndClick extends EventEmitter {
command (this: NightwatchBrowser, target: string): NightwatchBrowser {
this.api
.scrollInto(target)
.click(target)
.perform(() => {
this.emit('complete')
})
return this
}
}
module.exports = scrollAndClick

@ -0,0 +1,24 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
class ScrollInto extends EventEmitter {
command (this: NightwatchBrowser, target: string): NightwatchBrowser {
this.api.perform((client, done) => {
_scrollInto(this.api, target, () => {
done()
this.emit('complete')
})
})
return this
}
}
function _scrollInto (browser: NightwatchBrowser, target: string, cb: VoidFunction): void {
browser.execute(function (target) {
document.querySelector(target).scrollIntoView(({block: 'center'}))
}, [target], function () {
cb()
})
}
module.exports = ScrollInto

@ -0,0 +1,17 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
class SelectAccount extends EventEmitter {
command (this: NightwatchBrowser, account?: string): NightwatchBrowser {
if (account) {
this.api
.click(`select[data-id="runTabSelectAccount"] [value="${account}"]`)
.perform(() => {
this.emit('complete')
})
} else this.emit('complete')
return this
}
}
module.exports = SelectAccount

@ -0,0 +1,23 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
class SelectContract extends EventEmitter {
command (this: NightwatchBrowser, contractName: string): NightwatchBrowser {
this.api.perform((done) => {
selectContract(this.api, contractName, () => {
done()
this.emit('complete')
})
})
return this
}
}
function selectContract (browser: NightwatchBrowser, contractName: string, callback: VoidFunction) {
browser.clickLaunchIcon('settings').clickLaunchIcon('udapp')
.setValue('#runTabView select[class^="contractNames"]', contractName).perform(() => {
callback()
})
}
module.exports = SelectContract

@ -0,0 +1,21 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
class sendLowLevelTx extends EventEmitter {
command (this: NightwatchBrowser, address: string, value: string, callData: string): NightwatchBrowser {
console.log('low level transact to ', address, value, callData)
this.api.waitForElementVisible(`#instance${address} #deployAndRunLLTxSendTransaction`, 1000)
.clearValue(`#instance${address} #deployAndRunLLTxCalldata`)
.setValue(`#instance${address} #deployAndRunLLTxCalldata`, callData)
.waitForElementVisible('#value')
.clearValue('#value')
.setValue('#value', value)
.scrollAndClick(`#instance${address} #deployAndRunLLTxSendTransaction`)
.perform(() => {
this.emit('complete')
})
return this
}
}
module.exports = sendLowLevelTx

@ -0,0 +1,23 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class SetEditorValue extends EventEmitter {
command (this: NightwatchBrowser, value: string, callback?: VoidFunction): NightwatchBrowser {
this.api.perform((client, done) => {
this.api.execute(function (value) {
const elem: any = document.getElementById('input')
elem.editor.session.setValue(value)
}, [value], () => {
done()
if (callback) {
callback.call(this.api)
}
this.emit('complete')
})
})
return this
}
}
module.exports = SetEditorValue

@ -0,0 +1,16 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class SetSolidityCompilerVersion extends EventEmitter {
command (this: NightwatchBrowser, version: string): NightwatchBrowser {
this.api
.click(`#compileTabView #versionSelector [value="${version}"]`)
.pause(5000)
.perform(() => {
this.emit('complete')
})
return this
}
}
module.exports = SetSolidityCompilerVersion

@ -0,0 +1,40 @@
import { NightwatchBrowser } from "nightwatch"
const EventEmitter = require('events')
class MetaMask extends EventEmitter {
command (this: NightwatchBrowser, passphrase: string, password: string): NightwatchBrowser {
this.api.perform((done) => {
setupMetaMask(this.api, passphrase, password, () => {
done()
this.emit('complete')
})
})
return this
}
}
function setupMetaMask (browser: NightwatchBrowser, passphrase: string, password: string, done: VoidFunction) {
browser
.switchBrowserWindow('chrome-extension://poemojpkcjbpmcccohjnomjffeinlafe/home.html#initialize/welcome', 'MetaMask', (browser) => {
browser.waitForElementPresent('.first-time-flow__button')
.click('.first-time-flow__button')
.waitForElementPresent('.select-action__select-button:nth-of-type(1) > .first-time-flow__button')
.click('.select-action__select-button:nth-of-type(1) > .first-time-flow__button')
.waitForElementPresent('.page-container__footer-button:nth-of-type(2)')
.click('.page-container__footer-button:nth-of-type(2)')
.waitForElementPresent('.first-time-flow__textarea')
.setValue('.first-time-flow__textarea', passphrase)
.setValue('*[autocomplete="new-password"]', password)
.setValue('*[autocomplete="confirm-password"]', password)
.click('.first-time-flow__checkbox')
.click('.first-time-flow__button')
.pause(5000)
.click('.first-time-flow__button')
.perform(() => {
done()
})
})
}
module.exports = MetaMask

@ -0,0 +1,44 @@
import { NightwatchBrowser } from "nightwatch"
const EventEmitter = require('events')
class SelectContract extends EventEmitter {
command (this: NightwatchBrowser, msg: string, callback: (hash: { value: string }, signature: { value: string }) => void): NightwatchBrowser {
this.api.perform((done) => {
signMsg(this.api, msg, (hash, signature) => {
callback(hash, signature)
done()
this.emit('complete')
})
})
return this
}
}
function signMsg (browser: NightwatchBrowser, msg: string, cb: (hash: { value: string }, signature: { value: string }) => void) {
let hash, signature
browser
.waitForElementPresent('i[id="remixRunSignMsg"]')
.click('i[id="remixRunSignMsg"]')
.waitForElementVisible('textarea[id="prompt_text"]')
.setValue('textarea[id="prompt_text"]', msg, () => {
browser.modalFooterOKClick().perform(
(client, done) => {
browser.waitForElementVisible('span[id="remixRunSignMsgHash"]').getText('span[id="remixRunSignMsgHash"]', (v) => { hash = v; done() })
}
)
.perform(
(client, done) => {
browser.waitForElementVisible('span[id="remixRunSignMsgSignature"]').getText('span[id="remixRunSignMsgSignature"]', (v) => { signature = v; done() })
}
)
.modalFooterOKClick()
.perform(
() => {
cb(hash, signature)
}
)
})
}
module.exports = SelectContract

@ -0,0 +1,21 @@
import { NightwatchBrowser, NightwatchAPI } from "nightwatch"
import EventEmitter from "events"
/*
Switches between browser tabs
*/
class SwitchBrowserTab extends EventEmitter {
command (this: NightwatchBrowser, index: number): NightwatchBrowser {
this.api.perform((browser: NightwatchAPI, done) => {
browser.windowHandles((result) => {
browser.switchWindow(result.value[index])
done()
})
this.emit('complete')
})
return this
}
}
module.exports = SwitchBrowserTab

@ -0,0 +1,28 @@
import { NightwatchBrowser, NightwatchCallbackResult } from "nightwatch"
const EventEmitter = require('events')
class SwitchBrowserWindow extends EventEmitter {
command (this: NightwatchBrowser, url: string, windowName: string, cb: (browser: NightwatchBrowser, window?: NightwatchCallbackResult<Window>) => void): NightwatchBrowser {
this.api.perform((done) => {
switchWindow(this.api, url, windowName, cb)
done()
this.emit('complete')
})
return this
}
}
function switchWindow (browser: NightwatchBrowser, url: string, windowName: string, cb: (browser: NightwatchBrowser, window: NightwatchCallbackResult<Window>) => void) {
browser.execute(function (windowName) {
return window.open('', windowName, 'width=2560, height=1440')
}, [windowName], (newWindow) => {
browser.switchWindow(windowName)
.url(url)
.pause(5000)
.assert.urlContains(url)
if (cb) cb(browser, newWindow)
})
}
module.exports = SwitchBrowserWindow

@ -0,0 +1,37 @@
import { NightwatchBrowser, NightwatchTestConstantFunctionExpectedInput } from "nightwatch"
import EventEmitter from "events"
class TestConstantFunction extends EventEmitter {
command (this: NightwatchBrowser, address: string, fnFullName: string, expectedInput: NightwatchTestConstantFunctionExpectedInput | null, expectedOutput: string): NightwatchBrowser {
console.log('TestConstantFunction ' + address + ' fnFullName')
this.api.perform((done) => {
testConstantFunction(this.api, address, fnFullName, expectedInput, expectedOutput, () => {
done()
this.emit('complete')
})
})
return this
}
}
function testConstantFunction (browser: NightwatchBrowser, address: string, fnFullName: string, expectedInput: NightwatchTestConstantFunctionExpectedInput, expectedOutput: string, cb: VoidFunction) {
browser.waitForElementPresent('.instance button[title="' + fnFullName + '"]').perform(function (client, done) {
client.execute(function () {
document.querySelector('#runTabView').scrollTop = document.querySelector('#runTabView').scrollHeight
}, [], function () {
if (expectedInput) {
client.setValue('#runTabView input[title="' + expectedInput.types + '"]', expectedInput.values)
}
done()
})
})
.click('.instance button[title="' + fnFullName + '"]')
.pause(1000)
.waitForElementPresent('#instance' + address + ' div[class^="contractActionsContainer"] div[class^="value"]')
.scrollInto('#instance' + address + ' div[class^="contractActionsContainer"] div[class^="value"]')
.assert.containsText('#instance' + address + ' div[class^="contractActionsContainer"] div[class^="value"]', expectedOutput).perform(() => {
cb()
})
}
module.exports = TestConstantFunction

@ -0,0 +1,27 @@
import { NightwatchBrowser, NightwatchContractContent } from 'nightwatch'
import EventEmitter from "events"
class TestContracts extends EventEmitter {
command (this: NightwatchBrowser,fileName: string, contractCode: NightwatchContractContent, compiledContractNames: string[]): NightwatchBrowser {
this.api.perform((done) => {
testContracts(this.api, fileName, contractCode, compiledContractNames, () => {
done()
this.emit('complete')
})
})
return this
}
}
function testContracts (browser: NightwatchBrowser, fileName: string, contractCode: NightwatchContractContent, compiledContractNames: string[], callback: VoidFunction) {
browser
.clickLaunchIcon('solidity')
.addFile(fileName, contractCode)
.pause(1000)
.verifyContracts(compiledContractNames)
.perform(() => {
callback()
})
}
module.exports = TestContracts

@ -0,0 +1,14 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
class TestEditorValue extends EventEmitter {
command (this: NightwatchBrowser, testvalue: string): NightwatchBrowser {
this.api.getEditorValue((value) => {
this.api.assert.equal(testvalue, value)
this.emit('complete')
})
return this
}
}
module.exports = TestEditorValue

@ -0,0 +1,62 @@
import { NightwatchBrowser, NightwatchTestFunctionExpectedInput } from 'nightwatch'
import EventEmitter from "events"
const deepequal = require('deep-equal')
class TestFunction extends EventEmitter {
command (this: NightwatchBrowser, txHash: string, expectedValue: NightwatchTestFunctionExpectedInput): NightwatchBrowser {
const browser = this.api
const logs = {}
const setLog = (index: number, value: string) => { logs[Object.keys(logs)[index]] = typeof value === 'string' ? value.trim() : value }
browser
.waitForElementVisible(`[data-id="block_tx${txHash}"]`)
.click(`[data-id="block_tx${txHash}"]`)
.waitForElementVisible(`*[data-id="txLoggerTable${txHash}"]`)
// fetch and format transaction logs as key => pair object
.elements('css selector', `*[data-shared="key_${txHash}"]`, (res) => {
Array.isArray(res.value) && res.value.forEach(function (jsonWebElement) {
const jsonWebElementId: string = jsonWebElement.ELEMENT || jsonWebElement[Object.keys(jsonWebElement)[0]]
browser.elementIdText(jsonWebElementId, (jsonElement) => {
const key = typeof jsonElement.value === 'string' ? jsonElement.value.trim() : null
logs[key] = null
})
})
})
.elements('css selector', `*[data-shared="pair_${txHash}"]`, (res) => {
Array.isArray(res.value) && res.value.forEach(function (jsonWebElement, index) {
const jsonWebElementId = jsonWebElement.ELEMENT || jsonWebElement[Object.keys(jsonWebElement)[0]]
browser.elementIdText(jsonWebElementId, (jsonElement) => {
let value = jsonElement.value
try {
value = JSON.parse(<string>jsonElement.value)
setLog(index, <string>value)
} catch (e) {
setLog(index, <string>value)
}
})
})
})
browser.perform(() => {
Object.keys(expectedValue).forEach(key => {
const equal: boolean = deepequal(logs[key], expectedValue[key])
if (!equal) {
browser.assert.fail(`Expected ${expectedValue[key]} but got ${logs[key]}`)
} else {
browser.assert.ok(true, `Expected value matched returned value ${expectedValue[key]}`)
}
})
this.emit('complete')
})
return this
}
}
module.exports = TestFunction

@ -0,0 +1,34 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from "events"
class VerifyCallReturnValue extends EventEmitter {
command (this: NightwatchBrowser, address: string, checks: string[]): NightwatchBrowser {
this.api.perform((done) => {
verifyCallReturnValue(this.api, address, checks, () => {
done()
this.emit('complete')
})
})
return this
}
}
function verifyCallReturnValue (browser: NightwatchBrowser, address: string, checks: string[], done: VoidFunction) {
browser.execute(function (address: string) {
const nodes = document.querySelectorAll('#instance' + address + ' div[class^="contractActionsContainer"] div[class^="value"]') as NodeListOf<HTMLElement>
const ret = []
for (let k = 0; k < nodes.length; k++) {
const text = nodes[k].innerText ? nodes[k].innerText : nodes[k].textContent
ret.push(text.replace('\n', ''))
}
return ret
}, [address], function (result) {
console.log('verifyCallReturnValue', result)
for (const k in checks) {
browser.assert.equal(result.value[k].trim(), checks[k].trim())
}
done()
})
}
module.exports = VerifyCallReturnValue

@ -0,0 +1,71 @@
import { NightwatchBrowser, NightwatchCallbackResult } from 'nightwatch'
import EventEmitter from "events"
class VerifyContracts extends EventEmitter {
command (this: NightwatchBrowser,compiledContractNames: string[], opts = { wait: 1000, version: null }): NightwatchBrowser {
this.api.perform((done) => {
verifyContracts(this.api, compiledContractNames, opts, () => {
done()
this.emit('complete')
})
})
return this
}
}
function getCompiledContracts (browser: NightwatchBrowser, opts: { wait: number, version?: string }, callback: (result: NightwatchCallbackResult<any>) => void) {
browser
.clickLaunchIcon('solidity')
.pause(opts.wait)
.waitForElementPresent('*[data-id="compiledContracts"] option')
.perform((done) => {
if (opts.version) {
browser
.click('*[data-id="compilation-details"]')
.waitForElementVisible('*[data-id="treeViewDivcompiler"]')
.pause(2000)
.click('*[data-id="treeViewDivcompiler"]')
.waitForElementVisible('*[data-id="treeViewLicompiler/version"]')
.assert.containsText('*[data-id="treeViewLicompiler/version"]', `version:\n ${opts.version}`)
.perform(done)
} else done()
})
.execute(function () {
const contracts = document.querySelectorAll('*[data-id="compiledContracts"] option') as NodeListOf<HTMLInputElement>
if (!contracts) {
return null
} else {
const ret = []
for (let c = 0; c < contracts.length; c++) {
ret.push(contracts[c].value)
}
return ret
}
}, [], function (result) {
callback(result)
})
}
function verifyContracts (browser: NightwatchBrowser, compiledContractNames: string[], opts: { wait: number, version?: string }, callback: VoidFunction) {
getCompiledContracts(browser, opts, (result: NightwatchCallbackResult<any>) => {
if (result.value) {
for (const contract in compiledContractNames) {
console.log(' - ' + compiledContractNames[contract], result.value)
if (result.value.indexOf(compiledContractNames[contract]) === -1) {
browser.assert.fail('compiled contract ' + compiledContractNames + ' not found', 'info about error', '')
browser.end()
return
}
}
} else {
browser.assert.fail('compiled contract ' + compiledContractNames + ' not found - none found', 'info about error', '')
browser.end()
}
console.log('contracts all found ' + compiledContractNames)
callback()
})
}
module.exports = VerifyContracts

@ -0,0 +1,25 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class WaitForElementContainsText extends EventEmitter {
command (this: NightwatchBrowser, id: string, value: string): NightwatchBrowser {
let incr = 0
const runid = setInterval(() => {
this.api.getText(id, (result) => {
if (typeof result.value === 'string' && value.indexOf(result.value || '') !== -1) {
clearInterval(runid)
this.api.assert.ok(true, `WaitForElementContainsText ${id} contains ${value}`)
this.emit('complete')
} else incr++
if (incr > 50) {
clearInterval(runid)
this.api.assert.fail(`WaitForElementContainsText - expected ${value} but got ${result.value}`)
// throw new Error(`WaitForElementContainsText ${id} ${value}`)
}
})
}, 200)
return this
}
}
module.exports = WaitForElementContainsText

@ -0,0 +1,249 @@
'use strict'
const storage = `pragma solidity >=0.4.22 <0.7.0;
/**
* @title Storage
* @dev Store & retreive value in a variable
*/
contract Storage {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retreive() public view returns (uint256){
return number;
}
}`
const owner = `pragma solidity >=0.4.22 <0.7.0;
/**
* @title Owner
* @dev Set & change owner
*/
contract Owner {
address private owner;
// event for EVM logging
event OwnerSet(address indexed oldOwner, address indexed newOwner);
// modifier to check if caller is owner
modifier isOwner() {
// If the first argument of 'require' evaluates to 'false', execution terminates and all
// changes to the state and to Ether balances are reverted.
// This used to consume all gas in old EVM versions, but not anymore.
// It is often a good idea to use 'require' to check if functions are called correctly.
// As a second argument, you can also provide an explanation about what went wrong.
require(msg.sender == owner, "Caller is not owner");
_;
}
/**
* @dev Set contract deployer as owner
*/
constructor() public {
owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor
emit OwnerSet(address(0), owner);
}
/**
* @dev Change owner
* @param newOwner address of new owner
*/
function changeOwner(address newOwner) public isOwner {
emit OwnerSet(owner, newOwner);
owner = newOwner;
}
/**
* @dev Return owner address
* @return address of owner
*/
function getOwner() external view returns (address) {
return owner;
}
}`
const ballot = `pragma solidity >=0.4.22 <0.7.0;
/**
* @title Ballot
* @dev Implements voting process along with vote delegation
*/
contract Ballot {
struct Voter {
uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
struct Proposal {
// If you can limit the length to a certain number of bytes,
// always use one of bytes1 to bytes32 because they are much cheaper
bytes32 name; // short name (up to 32 bytes)
uint voteCount; // number of accumulated votes
}
address public chairperson;
mapping(address => Voter) public voters;
Proposal[] public proposals;
/**
* @dev Create a new ballot to choose one of 'proposalNames'.
* @param proposalNames names of proposals
*/
constructor(bytes32[] memory proposalNames) public {
chairperson = msg.sender;
voters[chairperson].weight = 1;
for (uint i = 0; i < proposalNames.length; i++) {
// 'Proposal({...})' creates a temporary
// Proposal object and 'proposals.push(...)'
// appends it to the end of 'proposals'.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
/**
* @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'.
* @param voter address of voter
*/
function giveRightToVote(address voter) public {
require(
msg.sender == chairperson,
"Only chairperson can give right to vote."
);
require(
!voters[voter].voted,
"The voter already voted."
);
require(voters[voter].weight == 0);
voters[voter].weight = 1;
}
/**
* @dev Delegate your vote to the voter 'to'.
* @param to address to which vote is delegated
*/
function delegate(address to) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted, "You already voted.");
require(to != msg.sender, "Self-delegation is disallowed.");
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
// We found a loop in the delegation, not allowed.
require(to != msg.sender, "Found loop in delegation.");
}
sender.voted = true;
sender.delegate = to;
Voter storage delegate_ = voters[to];
if (delegate_.voted) {
// If the delegate already voted,
// directly add to the number of votes
proposals[delegate_.vote].voteCount += sender.weight;
} else {
// If the delegate did not vote yet,
// add to her weight.
delegate_.weight += sender.weight;
}
}
/**
* @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'.
* @param proposal index of proposal in the proposals array
*/
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
require(sender.weight != 0, "Has no right to vote");
require(!sender.voted, "Already voted.");
sender.voted = true;
sender.vote = proposal;
// If 'proposal' is out of the range of the array,
// this will throw automatically and revert all
// changes.
proposals[proposal].voteCount += sender.weight;
}
/**
* @dev Computes the winning proposal taking all previous votes into account.
* @return winningProposal_ index of winning proposal in the proposals array
*/
function winningProposal() public view
returns (uint winningProposal_)
{
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal_ = p;
}
}
}
/**
* @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then
* @return winnerName_ the name of the winner
*/
function winnerName() public view
returns (bytes32 winnerName_)
{
winnerName_ = proposals[winningProposal()].name;
}
}
`
const ballotTest = `pragma solidity >=0.4.22 <0.7.0;
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "../3_Ballot.sol";
contract BallotTest {
bytes32[] proposalNames;
Ballot ballotToTest;
function beforeAll () public {
proposalNames.push(bytes32("candidate1"));
ballotToTest = new Ballot(proposalNames);
}
function checkWinningProposal () public {
ballotToTest.vote(0);
Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal");
Assert.equal(ballotToTest.winnerName(), bytes32("candidate1"), "candidate1 should be the winner name");
}
function checkWinninProposalWithReturnValue () public view returns (bool) {
return ballotToTest.winningProposal() == 0;
}
}
`
export default {
storage: { name: '1_Storage.sol', content: storage },
owner: { name: '2_Owner.sol', content: owner },
ballot: { name: '3_Ballot.sol', content: ballot },
ballot_test: { name: 'tests/4_Ballot_test.sol', content: ballotTest }
}

@ -0,0 +1,37 @@
import { NightwatchBrowser } from "nightwatch"
require('dotenv').config()
export default function (browser: NightwatchBrowser, callback: VoidFunction, url?: string, preloadPlugins = true): void {
browser
.url(url || 'http://127.0.0.1:8080')
.pause(5000)
.switchBrowserTab(0)
.fullscreenWindow(() => {
if (preloadPlugins) {
initModules(browser, () => {
browser.clickLaunchIcon('solidity')
.waitForElementVisible('[for="autoCompile"]')
.click('[for="autoCompile"]')
.verify.elementPresent('[data-id="compilerContainerAutoCompile"]:checked')
})
}
})
.perform(() => {
callback()
})
}
function initModules (browser: NightwatchBrowser, callback: VoidFunction) {
browser.pause(5000)
.click('[data-id="verticalIconsKindpluginManager"]')
.scrollAndClick('[data-id="pluginManagerComponentActivateButtonsolidityStaticAnalysis"]')
.scrollAndClick('[data-id="pluginManagerComponentActivateButtondebugger"]')
.scrollAndClick('[data-id="verticalIconsKindfileExplorers"]')
.clickLaunchIcon('settings')
.click('*[data-id="settingsTabGenerateContractMetadataLabel"]')
.setValue('[data-id="settingsTabGistAccessToken"]', process.env.gist_token)
.click('[data-id="settingsTabSaveGistToken"]')
.click('[data-id="settingsTabThemeLabelFlatly"]') // e2e tests were initially developed with Flatly. Some tests are failing with the default one (Dark), because the dark theme put uppercase everywhere.
.perform(() => { callback() })
}

@ -0,0 +1,318 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
import sauce from './sauce'
import examples from '../examples/example-contracts'
const sources = [
{'browser/Untitled.sol': { content: examples.ballot.content }}
]
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Deploy Ballot': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('solidity')
.testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['Ballot'])
.clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c')
.setValue('input[placeholder="bytes32[] proposalNames"]', '["0x48656c6c6f20576f726c64210000000000000000000000000000000000000000"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.waitForElementPresent('*[data-id="universalDappUiContractActionWrapper"]')
.click('*[data-id="universalDappUiTitleExpander"]')
.clickFunction('delegate - transact (not payable)', {types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'})
.testFunction('0x41fab8ea5b1d9fba5e0a6545ca1a2d62fff518578802c033c2b9a031a01c31b3',
{
status: '0x1 Transaction mined and execution succeed',
'transaction hash': '0x41fab8ea5b1d9fba5e0a6545ca1a2d62fff518578802c033c2b9a031a01c31b3',
'decoded input': { 'address to': '0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB' }
})
},
'Debug Ballot / delegate': function (browser: NightwatchBrowser) {
browser.pause(500)
.click('*[data-id="txLoggerDebugButton0x41fab8ea5b1d9fba5e0a6545ca1a2d62fff518578802c033c2b9a031a01c31b3"]')
.pause(2000)
// .clickLaunchIcon('debugger')
.click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.pause(2000)
.goToVMTraceStep(79)
.pause(1000)
.checkVariableDebug('soliditystate', stateCheck)
.checkVariableDebug('soliditylocals', localsCheck)
},
'Access Ballot via at address': function (browser: NightwatchBrowser) {
browser.clickLaunchIcon('udapp')
.click('*[data-id="universalDappUiUdappClose"]')
.addFile('ballot.abi', { content: ballotABI })
.addAtAddressInstance('0x692a70D2e424a56D2C6C27aA97D1a86395877b3B', true, false)
.clickLaunchIcon('fileExplorers')
.addAtAddressInstance('0x692a70D2e424a56D2C6C27aA97D1a86395877b3A', true, true)
.pause(500)
.waitForElementPresent('*[data-id="universalDappUiContractActionWrapper"]')
.click('*[data-id="universalDappUiTitleExpander"]')
.clickFunction('delegate - transact (not payable)', {types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'})
.testFunction('0xca58080c8099429caeeffe43b8104df919c2c543dceb9edf9242fa55f045c803',
{
status: '0x0 Transaction mined but execution failed',
'transaction hash': '0xca58080c8099429caeeffe43b8104df919c2c543dceb9edf9242fa55f045c803',
'decoded input': { 'address to': '0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB' }
})
},
'Deploy and use Ballot using external web3': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="settingsWeb3Mode"]')
.modalFooterOKClick()
.clickLaunchIcon('solidity')
.testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['Ballot'])
.clickLaunchIcon('udapp')
.setValue('input[placeholder="bytes32[] proposalNames"]', '["0x48656c6c6f20576f726c64210000000000000000000000000000000000000000"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.clickInstance(0)
.click('*[data-id="terminalClearConsole"]')
.clickFunction('delegate - transact (not payable)', {types: 'address to', values: '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'})
.journalLastChildIncludes('Ballot.delegate(address)')
.journalLastChildIncludes('data: 0x5c1...a733c')
.end()
},
tearDown: sauce
}
const localsCheck = {
'to': {
'value': '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB',
'type': 'address'
}
}
const stateCheck = {
'chairperson': {
'value': '0xCA35B7D915458EF540ADE6068DFE2F44E8FA733C',
'type': 'address',
'constant': false
},
'voters': {
'value': {
'000000000000000000000000ca35b7d915458ef540ade6068dfe2f44e8fa733c': {
'value': {
'weight': {
'value': '1',
'type': 'uint256'
},
'voted': {
'value': false,
'type': 'bool'
},
'delegate': {
'value': '0x0000000000000000000000000000000000000000',
'type': 'address'
},
'vote': {
'value': '0',
'type': 'uint256'
}
},
'type': 'struct Ballot.Voter'
}
},
'type': 'mapping(address => struct Ballot.Voter)',
'constant': false
},
'proposals': {
'value': [
{
'value': {
'name': {
'value': '0x48656C6C6F20576F726C64210000000000000000000000000000000000000000',
'type': 'bytes32'
},
'voteCount': {
'value': '0',
'type': 'uint256'
}
},
'type': 'struct Ballot.Proposal'
}
],
'length': '0x1',
'type': 'struct Ballot.Proposal[]',
'constant': false
}
}
const ballotABI = `[
{
"inputs": [
{
"internalType": "bytes32[]",
"name": "proposalNames",
"type": "bytes32[]"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"constant": true,
"inputs": [],
"name": "chairperson",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
}
],
"name": "delegate",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "voter",
"type": "address"
}
],
"name": "giveRightToVote",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "proposals",
"outputs": [
{
"internalType": "bytes32",
"name": "name",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "voteCount",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "uint256",
"name": "proposal",
"type": "uint256"
}
],
"name": "vote",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "voters",
"outputs": [
{
"internalType": "uint256",
"name": "weight",
"type": "uint256"
},
{
"internalType": "bool",
"name": "voted",
"type": "bool"
},
{
"internalType": "address",
"name": "delegate",
"type": "address"
},
{
"internalType": "uint256",
"name": "vote",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "winnerName",
"outputs": [
{
"internalType": "bytes32",
"name": "winnerName_",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "winningProposal",
"outputs": [
{
"internalType": "uint256",
"name": "winningProposal_",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]`

@ -0,0 +1,103 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
import sauce from './sauce'
import examples from '../examples/example-contracts'
const sources = [
{'browser/Untitled.sol': { content: examples.ballot.content }}
]
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Should compile using "compileWithParamaters" API': function (browser: NightwatchBrowser) {
browser
.addFile('test_jsCompile.js', { content: jsCompile })
.executeScript('remix.exeCurrent()')
.pause(5000)
.journalChildIncludes(`"languageversion": "0.6.8+commit.0bbfe453"`)
},
'Should update the compiler configuration with "setCompilerConfig" API': function (browser: NightwatchBrowser) {
browser
.addFile('test_updateConfiguration.js', { content: updateConfiguration })
.executeScript('remix.exeCurrent()')
.pause(5000)
.addFile('test_updateConfiguration.sol', { content: simpleContract })
.verifyContracts(['StorageTestUpdateConfiguration'], { wait: 5000, version: '0.6.8+commit.0bbfe453' })
.end()
},
tearDown: sauce
}
const simpleContract = `pragma solidity >=0.4.22 <0.7.0;
/**
* @title Storage
* @dev Store & retreive value in a variable
*/
contract StorageTestUpdateConfiguration {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retreive() public view returns (uint256){
return number;
}
}
`
const jsCompile = `(async () => {
try {
const contract = {
"storage.sol": {content : \`${simpleContract}\` }
}
console.log('compile')
const params = {
optimize: false,
evmVersion: null,
language: 'Solidity',
version: '0.6.8+commit.0bbfe453'
}
const result = await remix.call('solidity', 'compileWithParameters', contract, params)
console.log('result ', result)
} catch (e) {
console.log(e.message)
}
})()`
const updateConfiguration = `(async () => {
try {
const params = {
optimize: false,
evmVersion: null,
language: 'Solidity',
version: '0.6.8+commit.0bbfe453'
}
await remix.call('solidity', 'setCompilerConfig', params)
} catch (e) {
console.log(e.message)
}
})()`

@ -0,0 +1,116 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Should launch debugger': function (browser: NightwatchBrowser) {
browser.addFile('blah.sol', sources[0]['browser/blah.sol'])
.clickLaunchIcon('udapp')
.waitForElementPresent('*[title="Deploy - transact (not payable)"]')
.click('*[title="Deploy - transact (not payable)"]')
.debugTransaction(0)
.assert.containsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER')
},
'Should debug failing transaction': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindudapp"]')
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="universalDappUiTitleExpander"]')
.click('*[data-id="universalDappUiTitleExpander"]')
.scrollAndClick('*[title="string name, uint256 goal"]')
.setValue('*[title="string name, uint256 goal"]', '"toast", 999')
.click('*[data-id="createProject - transact (not payable)"]')
.debugTransaction(1)
.pause(2000)
.scrollAndClick('*[data-id="solidityLocals"]')
.assert.containsText('*[data-id="solidityLocals"]', 'toast')
.assert.containsText('*[data-id="solidityLocals"]', '999')
},
'Should debug transaction using slider': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindudapp"]')
.waitForElementVisible('*[data-id="slider"]')
.click('*[data-id="slider"]')
.setValue('*[data-id="slider"]', '50')
.pause(2000)
.assert.containsText('*[data-id="solidityLocals"]', 'no locals')
.assert.containsText('*[data-id="stepdetail"]', 'vm trace step:\n92')
},
'Should step back and forward transaction': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindudapp"]')
.waitForElementPresent('*[data-id="buttonNavigatorIntoBack"]')
.scrollAndClick('*[data-id="buttonNavigatorIntoBack"]')
.pause(2000)
.assert.containsText('*[data-id="stepdetail"]', 'vm trace step:\n91')
.assert.containsText('*[data-id="stepdetail"]', 'execution step:\n91')
.click('*[data-id="buttonNavigatorIntoForward"]')
.pause(2000)
.assert.containsText('*[data-id="stepdetail"]', 'vm trace step:\n92')
.assert.containsText('*[data-id="stepdetail"]', 'execution step:\n92')
},
'Should jump through breakpoints': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="editorInput"]')
.click('.ace_gutter-cell:nth-of-type(10)')
.click('.ace_gutter-cell:nth-of-type(20)')
.waitForElementVisible('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.pause(2000)
.assert.containsText('*[data-id="stepdetail"]', 'vm trace step:\n0')
.assert.containsText('*[data-id="stepdetail"]', 'execution step:\n0')
.click('*[data-id="buttonNavigatorJumpNextBreakpoint"]')
.pause(2000)
.assert.containsText('*[data-id="stepdetail"]', 'vm trace step:\n184')
.assert.containsText('*[data-id="stepdetail"]', 'execution step:\n184')
.end()
},
tearDown: sauce
}
const sources = [
{
'browser/blah.sol': {
content: `
pragma solidity >=0.4.22 <0.6.0;
contract Kickstarter {
enum State { Started, Completed }
struct Project {
address owner;
string name;
uint goal;
State state;
}
Project[] public projects;
constructor() public {
}
function createProject(string memory name, uint goal) public {
Project storage project = projects[projects.length];
project.name = name;
project.owner = msg.sender;
project.state = State.Started;
project.goal = goal;
}
}
`
}
}
]

@ -0,0 +1,80 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', false)
},
'Loads Icon\'s Panel': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="remixIdeIconPanel"]', 10000)
.waitForElementVisible('div[data-id="verticalIconsHomeIcon"]')
.waitForElementVisible('div[plugin="fileExplorers"]')
.waitForElementVisible('div[plugin="pluginManager"]')
.waitForElementVisible('div[plugin="settings"]')
},
'Loads Side Panel': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="remixIdeSidePanel"]')
.assert.containsText('h6[data-id="sidePanelSwapitTitle"]', 'FILE EXPLORERS')
.waitForElementVisible('div[data-id="filePanelFileExplorerTree"]')
.waitForElementVisible('li[key="browser/3_Ballot.sol"]')
},
'Loads Main View': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="mainPanelPluginsContainer"]')
.waitForElementVisible('div[data-id="landingPageHomeContainer"]')
.waitForElementVisible('div[data-id="landingPageHpSections"]')
.waitForElementVisible('div[data-id="terminalContainer"]')
},
'Loads terminal': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('div[data-id="terminalCli"]', 10000)
.journalLastChildIncludes('Welcome to Remix')
},
'Toggles Side Panel': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="remixIdeSidePanel"]')
.assert.containsText('h6[data-id="sidePanelSwapitTitle"]', 'FILE EXPLORERS')
.clickLaunchIcon('fileExplorers')
.assert.hidden('div[data-id="remixIdeSidePanel"]')
.clickLaunchIcon('fileExplorers')
.assert.visible('div[data-id="remixIdeSidePanel"]')
.assert.containsText('h6[data-id="sidePanelSwapitTitle"]', 'FILE EXPLORERS')
},
'Toggles Terminal': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="terminalContainer"]')
.assert.visible('div[data-id="terminalContainerDisplay"]')
.click('i[data-id="terminalToggleIcon"]')
.checkElementStyle('div[data-id="terminalToggleMenu"]', 'height', '35px')
.click('i[data-id="terminalToggleIcon"]')
.assert.visible('div[data-id="terminalContainerDisplay"]')
},
'Toggles File Explorer Browser': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('div[data-id="filePanelFileExplorerTree"]')
.assert.visible('ul[key="browser"]')
.click('div[data-id="treeViewTogglebrowser"]')
.assert.hidden('ul[key="browser"]')
.click('div[data-id="treeViewTogglebrowser"]')
.assert.visible('ul[key="browser"]')
},
'Switch Tabs using tabs icon': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('div[data-id="filePanelFileExplorerTree"]')
.openFile('browser/3_Ballot.sol')
.assert.containsText('div[title="browser/3_Ballot.sol"]', '3_Ballot.sol')
.click('span[class^=dropdownCaret]')
.click('#homeItem')
.assert.containsText('div[title="home"]', 'Home')
.end()
},
tearDown: sauce
}

@ -0,0 +1,199 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'Should zoom in editor': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="mainPanelPluginsContainer"]')
.openFile('browser/1_Storage.sol')
.waitForElementVisible('*[data-id="editorInput"]')
.checkElementStyle('*[data-id="editorInput"]', 'font-size', '12px')
.click('*[data-id="tabProxyZoomIn"]')
.click('*[data-id="tabProxyZoomIn"]')
.checkElementStyle('*[data-id="editorInput"]', 'font-size', '14px')
},
'Should zoom out editor': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="editorInput"]')
.checkElementStyle('*[data-id="editorInput"]', 'font-size', '14px')
.click('*[data-id="tabProxyZoomOut"]')
.click('*[data-id="tabProxyZoomOut"]')
.checkElementStyle('*[data-id="editorInput"]', 'font-size', '12px')
},
'Should display compile error in editor': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="editorInput"]')
.waitForElementVisible('*[class="ace_content"]')
.click('*[class="ace_content"]')
.sendKeys('*[class="ace_text-input"]', 'error')
.pause(2000)
.waitForElementVisible('.ace_error')
},
'Should minimize and maximize codeblock in editor': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="editorInput"]')
.waitForElementVisible('.ace_open')
.click('.ace_start:nth-of-type(1)')
.waitForElementVisible('.ace_closed')
.click('.ace_start:nth-of-type(1)')
.waitForElementVisible('.ace_open')
},
'Should add breakpoint to editor': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="editorInput"]')
.waitForElementNotPresent('.ace_breakpoint')
.click('.ace_gutter-cell:nth-of-type(1)')
.waitForElementVisible('.ace_breakpoint')
},
'Should load syntax highlighter for ace light theme': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="editorInput"]')
.checkElementStyle('.ace_keyword', 'color', aceThemes.light.keyword)
.checkElementStyle('.ace_comment.ace_doc', 'color', aceThemes.light.comment)
.checkElementStyle('.ace_function', 'color', aceThemes.light.function)
.checkElementStyle('.ace_variable', 'color', aceThemes.light.variable)
},
'Should load syntax highlighter for ace dark theme': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]')
.click('*[data-id="verticalIconsKindsettings"]')
.waitForElementVisible('*[data-id="settingsTabThemeLabelDark"]')
.click('*[data-id="settingsTabThemeLabelDark"]')
.pause(2000)
.waitForElementVisible('*[data-id="editorInput"]')
/* @todo(#2863) ch for class and not colors
.checkElementStyle('.ace_keyword', 'color', aceThemes.dark.keyword)
.checkElementStyle('.ace_comment.ace_doc', 'color', aceThemes.dark.comment)
.checkElementStyle('.ace_function', 'color', aceThemes.dark.function)
.checkElementStyle('.ace_variable', 'color', aceThemes.dark.variable)
*/
},
'Should highlight source code': function (browser: NightwatchBrowser) {
browser.addFile('sourcehighlight.js', sourcehighlightScript)
.openFile('browser/sourcehighlight.js')
.executeScript('remix.exeCurrent()')
.editorScroll('down', 60)
.waitForElementPresent('.highlightLine32')
.checkElementStyle('.highlightLine32', 'background-color', 'rgb(8, 108, 181)')
.waitForElementPresent('.highlightLine40')
.checkElementStyle('.highlightLine40', 'background-color', 'rgb(8, 108, 181)')
.waitForElementPresent('.highlightLine50')
.checkElementStyle('.highlightLine50', 'background-color', 'rgb(8, 108, 181)')
},
'Should remove 1 highlight from source code': function (browser: NightwatchBrowser) {
browser.addFile('removeSourcehighlightScript.js', removeSourcehighlightScript)
.openFile('browser/removeSourcehighlightScript.js')
.executeScript('remix.exeCurrent()')
.openFile('browser/3_Ballot.sol')
.waitForElementNotPresent('.highlightLine32')
.checkElementStyle('.highlightLine40', 'background-color', 'rgb(8, 108, 181)')
.checkElementStyle('.highlightLine50', 'background-color', 'rgb(8, 108, 181)')
},
'Should remove all highlights from source code': function (browser: NightwatchBrowser) {
browser.addFile('removeAllSourcehighlightScript.js', removeAllSourcehighlightScript)
.openFile('browser/removeAllSourcehighlightScript.js')
.executeScript('remix.exeCurrent()')
.openFile('browser/3_Ballot.sol')
.waitForElementNotPresent('.highlightLine32')
.waitForElementNotPresent('.highlightLine40')
.waitForElementNotPresent('.highlightLine50')
.end()
},
tearDown: sauce
}
const aceThemes = {
light: {
keyword: 'rgb(147, 15, 128)',
comment: 'rgb(35, 110, 36)',
function: 'rgb(0, 0, 162)',
variable: 'rgb(253, 151, 31)'
},
dark: {
keyword: 'rgb(0, 105, 143)',
comment: 'rgb(85, 85, 85)',
function: 'rgb(0, 174, 239)',
variable: 'rgb(153, 119, 68)'
}
}
const sourcehighlightScript = {
content: `
(async () => {
try {
const pos = {
start: {
line: 32,
column: 3
},
end: {
line: 32,
column: 20
}
}
await remix.call('editor', 'highlight', pos, 'browser/3_Ballot.sol')
const pos2 = {
start: {
line: 40,
column: 3
},
end: {
line: 40,
column: 20
}
}
await remix.call('editor', 'highlight', pos2, 'browser/3_Ballot.sol')
const pos3 = {
start: {
line: 50,
column: 3
},
end: {
line: 50,
column: 20
}
}
await remix.call('editor', 'highlight', pos3, 'browser/3_Ballot.sol')
} catch (e) {
console.log(e.message)
}
})()
`
}
const removeSourcehighlightScript = {
content: `
(async () => {
try {
await remix.call('editor', 'discardHighlightAt', 32, 'browser/3_Ballot.sol')
} catch (e) {
console.log(e.message)
}
})()
`
}
const removeAllSourcehighlightScript = {
content: `
(async () => {
try {
await remix.call('editor', 'discardHighlight')
} catch (e) {
console.log(e.message)
}
})()
`
}

@ -0,0 +1,111 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
import * as path from 'path'
const testData = {
testFile1: path.resolve(__dirname + '/editor.test.js'), // eslint-disable-line
testFile2: path.resolve(__dirname + '/fileExplorer.test.js'), // eslint-disable-line
testFile3: path.resolve(__dirname + '/generalSettings.test.js') // eslint-disable-line
}
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'Should create a new file `5_New_contract.sol` in file explorer': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="remixIdeSidePanel"]')
.clickLaunchIcon('fileExplorers')
.assert.containsText('h6[data-id="sidePanelSwapitTitle"]', 'FILE EXPLORERS')
.click('*[data-id="fileExplorerNewFilecreateNewFile"]')
.waitForElementVisible('*[data-id="modalDialogContainer"]')
.setValue('*[data-id="modalDialogCustomPromptText"]', '5_New_contract.sol')
.modalFooterOKClick()
.waitForElementVisible('*[data-id="treeViewLibrowser/5_New_contract.sol"]', 7000)
},
'Should rename `5_New_contract.sol` to 5_Renamed_Contract.sol': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="treeViewLibrowser/5_New_contract.sol"]')
.renameFile('browser/5_New_contract.sol', '5_Renamed_Contract.sol', 'browser/5_Renamed_Contract.sol')
.waitForElementVisible('*[data-id="treeViewLibrowser/5_Renamed_Contract.sol"]')
},
'Should delete file `5_Renamed_Contract.sol` from file explorer': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="treeViewLibrowser/5_Renamed_Contract.sol"]')
.rightClick('[data-path="browser/5_Renamed_Contract.sol"]')
.click('*[id="menuitemdelete"]')
.waitForElementVisible('*[data-id="modalDialogContainer"]')
.modalFooterOKClick()
.waitForElementNotPresent('*[data-id="treeViewLibrowser/5_Renamed_Contract.sol"')
},
'Should create a new folder': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="treeViewLibrowser/1_Storage.sol"]')
.rightClick('[data-path="browser/1_Storage.sol"]')
.click('*[id="menuitemcreate folder"]')
.waitForElementVisible('*[data-id="modalDialogContainer"]')
.setValue('*[data-id="modalDialogCustomPromptText"]', 'Browser_Tests')
.modalFooterOKClick()
.waitForElementVisible('*[data-id="treeViewLibrowser/Browser_Tests"]')
},
'Should rename Browser_Tests folder to Browser_E2E_Tests': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="treeViewLibrowser/Browser_Tests"]')
.rightClick('[data-path="browser/Browser_Tests"]')
.click('*[id="menuitemrename"]')
.sendKeys('[data-path="browser/Browser_Tests"]', 'Browser_E2E_Tests')
.sendKeys('[data-path="browser/Browser_Tests"]', browser.Keys.ENTER)
.waitForElementVisible('*[data-id="treeViewLibrowser/Browser_E2E_Tests"]')
},
'Should delete Browser_E2E_Tests folder': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="treeViewLibrowser/Browser_E2E_Tests"]')
.rightClick('[data-path="browser/Browser_E2E_Tests"]')
.click('*[id="menuitemdelete"]')
.waitForElementVisible('*[data-id="modalDialogContainer"]')
.modalFooterOKClick()
.waitForElementNotPresent('*[data-id="treeViewLibrowser/Browser_E2E_Tests"]')
},
'Should publish all explorer files to github gist': function (browser: NightwatchBrowser) {
const runtimeBrowser = browser.options.desiredCapabilities.browserName
browser
.waitForElementVisible('*[data-id="fileExplorerNewFilepublishToGist"]')
.click('*[data-id="fileExplorerNewFilepublishToGist"]')
.waitForElementVisible('*[data-id="modalDialogContainer"]')
.modalFooterOKClick()
.waitForElementVisible('*[data-id="modalDialogContainer"]', 7000)
.modalFooterOKClick()
.pause(2000)
.perform((done) => {
if (runtimeBrowser === 'chrome') {
browser.switchBrowserTab(1)
.assert.urlContains('https://gist.github.com')
.switchBrowserTab(0)
}
done()
})
},
'Should open local filesystem explorer': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="filePanelFileExplorerTree"]')
.setValue('*[data-id="fileExplorerFileUpload"]', testData.testFile1)
.setValue('*[data-id="fileExplorerFileUpload"]', testData.testFile2)
.setValue('*[data-id="fileExplorerFileUpload"]', testData.testFile3)
.waitForElementVisible('*[key="browser/editor.test.js"]')
.waitForElementVisible('*[key="browser/fileExplorer.test.js"]')
.waitForElementVisible('*[key="browser/generalSettings.test.js"]')
.end()
},
tearDown: sauce
}

@ -0,0 +1,191 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'Should execute `file` api from file manager external api': function (browser: NightwatchBrowser) {
browser
.addFile('file.js', { content: executeFile })
.executeScript(`remix.exeCurrent()`)
.pause(2000)
.journalLastChildIncludes('browser/file.js')
},
'Should execute `exists` api from file manager external api': function (browser: NightwatchBrowser) {
browser
.addFile('exists.js', { content: executeExists })
.executeScript(`remix.exeCurrent()`)
.pause(2000)
.journalChildIncludes('browser/exists.js true')
.journalChildIncludes('browser/non-exists.js false')
},
'Should execute `open` api from file manager external api': function (browser: NightwatchBrowser) {
browser
.addFile('open.js', { content: executeOpen })
.executeScript(`remix.exeCurrent()`)
.pause(2000)
.journalLastChildIncludes('browser/3_Ballot.sol')
},
'Should execute `writeFile` api from file manager external api': function (browser: NightwatchBrowser) {
browser
.addFile('writeFile.js', { content: executeWriteFile })
.executeScript(`remix.exeCurrent()`)
.pause(2000)
.openFile('browser/new_contract.sol')
.assert.containsText('[data-id="editorInput"]', 'pragma solidity ^0.6.0')
},
'Should execute `readFile` api from file manager external api': function (browser: NightwatchBrowser) {
browser
.addFile('readFile.js', { content: executeReadFile })
.executeScript(`remix.exeCurrent()`)
.pause(2000)
.journalLastChildIncludes('pragma solidity ^0.6.0')
},
'Should execute `copyFile` api from file manager external api': function (browser: NightwatchBrowser) {
browser
.addFile('copyFile.js', { content: executeCopyFile })
.executeScript(`remix.exeCurrent()`)
.pause(2000)
.journalLastChildIncludes('pragma solidity >=0.4.22 <0.7.0;')
},
'Should execute `rename` api from file manager external api': function (browser: NightwatchBrowser) {
browser
.addFile('renameFile.js', { content: executeRename })
.executeScript(`remix.exeCurrent()`)
.pause(2000)
.waitForElementPresent('[data-id="treeViewLibrowser/old_contract.sol"]')
},
'Should execute `mkdir` api from file manager external api': function (browser: NightwatchBrowser) {
browser
.addFile('mkdirFile.js', { content: executeMkdir })
.executeScript(`remix.exeCurrent()`)
.pause(2000)
.waitForElementPresent('[data-id="treeViewLibrowser/Test_Folder"]')
},
'Should execute `readdir` api from file manager external api': function (browser: NightwatchBrowser) {
browser
.addFile('readdirFile.js', { content: executeReaddir })
.executeScript(`remix.exeCurrent()`)
.pause(2000)
.journalLastChildIncludes('Test_Folder isDirectory true')
},
'Should execute `remove` api from file manager external api': function (browser: NightwatchBrowser) {
browser
.addFile('removeFile.js', { content: executeRemove })
.executeScript(`remix.exeCurrent()`)
.pause(2000)
.waitForElementNotPresent('[data-id="treeViewLibrowser/old_contract.sol"]')
.end()
},
tearDown: sauce
}
const executeFile = `
const run = async () => {
const result = await remix.call('fileManager', 'file')
console.log(result)
}
run()
`
const executeExists = `
const run = async () => {
const result1 = await remix.call('fileManager', 'exists', 'browser/exists.js')
const result2 = await remix.call('fileManager', 'exists', 'browser/non-exists.js')
console.log('browser/exists.js ' + result1)
console.log('browser/non-exists.js ' + result2)
}
run()
`
const executeOpen = `
const run = async () => {
await remix.call('fileManager', 'open', 'browser/3_Ballot.sol')
const result = await remix.call('fileManager', 'file')
console.log(result)
}
run()
`
const executeWriteFile = `
const run = async () => {
await remix.call('fileManager', 'writeFile', 'browser/new_contract.sol', 'pragma solidity ^0.6.0')
}
run()
`
const executeReadFile = `
const run = async () => {
const result = await remix.call('fileManager', 'readFile', 'browser/new_contract.sol')
console.log(result)
}
run()
`
const executeCopyFile = `
const run = async () => {
await remix.call('fileManager', 'copyFile', 'browser/3_Ballot.sol', 'browser/new_contract.sol')
const result = await remix.call('fileManager', 'readFile', 'browser/new_contract.sol')
console.log(result)
}
run()
`
const executeRename = `
const run = async () => {
await remix.call('fileManager', 'rename', 'browser/new_contract.sol', 'browser/old_contract.sol')
}
run()
`
const executeMkdir = `
const run = async () => {
await remix.call('fileManager', 'mkdir', 'browser/Test_Folder/')
}
run()
`
const executeReaddir = `
const run = async () => {
const result = await remix.call('fileManager', 'readdir', 'browser/')
console.log('Test_Folder isDirectory ', result["Test_Folder"].isDirectory)
}
run()
`
const executeRemove = `
const run = async () => {
await remix.call('fileManager', 'remove', 'browser/old_contract.sol')
}
run()
`

@ -0,0 +1,185 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', false)
},
'Should display settings menu': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.click('*[data-id="landingPageStartSolidity"]')
.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.click('*[data-id="verticalIconsKindsettings"]')
.waitForElementContainsText('h6[data-id="sidePanelSwapitTitle"]', 'SETTINGS')
},
'Should activate `generate contract metadata`': function (browser) {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000)
.waitForElementVisible('*[data-id="settingsTabGenerateContractMetadataLabel"]', 5000)
.click('*[data-id="verticalIconsFileExplorerIcons"]')
.openFile('browser/3_Ballot.sol')
.click('*[data-id="verticalIconsKindsolidity"]')
.pause(2000)
.click('*[data-id="compilerContainerCompileBtn"]')
.pause(3000)
.click('*[data-id="verticalIconsKindfileExplorers"]')
.openFile('browser/artifacts/Ballot.json')
.openFile('browser/artifacts/Ballot_metadata.json')
.getEditorValue((content) => {
const metadata = JSON.parse(content)
browser.assert.equal(metadata.language, 'Solidity')
})
},
'Should add new github access token': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.click('*[data-id="verticalIconsKindsettings"]')
.setValue('*[data-id="settingsTabGistAccessToken"]', '**********')
.click('*[data-id="settingsTabSaveGistToken"]')
.waitForElementVisible('*[data-shared="tooltipPopup"]:nth-last-of-type(1)', 5000)
.assert.containsText('*[data-shared="tooltipPopup"]:nth-last-of-type(1)', 'Access token saved')
},
'Should copy github access token to clipboard': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.click('*[data-id="copyToClipboardCopyIcon"]')
.waitForElementVisible('*[data-shared="tooltipPopup"]:nth-last-of-type(1)', 5000)
.assert.containsText('*[data-shared="tooltipPopup"]:nth-last-of-type(1)', 'Copied value to clipboard.')
},
'Should remove github access token': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.click('*[data-id="settingsTabRemoveGistToken"]')
.waitForElementVisible('*[data-shared="tooltipPopup"]:nth-last-of-type(1)', 5000)
.assert.containsText('*[data-shared="tooltipPopup"]:nth-last-of-type(1)', 'Access token removed')
.assert.containsText('*[data-id="settingsTabGistAccessToken"]', '')
},
'Should load dark theme': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.click('*[data-id="settingsTabThemeLabelDark"]')
.pause(2000)
.checkElementStyle(':root', '--primary', remixIdeThemes.dark.primary)
.checkElementStyle(':root', '--secondary', remixIdeThemes.dark.secondary)
.checkElementStyle(':root', '--success', remixIdeThemes.dark.success)
.checkElementStyle(':root', '--info', remixIdeThemes.dark.info)
.checkElementStyle(':root', '--warning', remixIdeThemes.dark.warning)
.checkElementStyle(':root', '--danger', remixIdeThemes.dark.danger)
},
'Should load light theme': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.click('*[data-id="settingsTabThemeLabelLight"]')
.pause(2000)
.checkElementStyle(':root', '--primary', remixIdeThemes.light.primary)
.checkElementStyle(':root', '--secondary', remixIdeThemes.light.secondary)
.checkElementStyle(':root', '--success', remixIdeThemes.light.success)
.checkElementStyle(':root', '--info', remixIdeThemes.light.info)
.checkElementStyle(':root', '--warning', remixIdeThemes.light.warning)
.checkElementStyle(':root', '--danger', remixIdeThemes.light.danger)
},
'Should load Cerulean theme': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.click('*[data-id="settingsTabThemeLabelCerulean"]')
.pause(5000)
.checkElementStyle(':root', '--primary', remixIdeThemes.curelean.primary)
.checkElementStyle(':root', '--secondary', remixIdeThemes.curelean.secondary)
.checkElementStyle(':root', '--success', remixIdeThemes.curelean.success)
.checkElementStyle(':root', '--info', remixIdeThemes.curelean.info)
.checkElementStyle(':root', '--warning', remixIdeThemes.curelean.warning)
.checkElementStyle(':root', '--danger', remixIdeThemes.curelean.danger)
},
'Should load Flatly theme': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.click('*[data-id="settingsTabThemeLabelFlatly"]')
.pause(2000)
.checkElementStyle(':root', '--primary', remixIdeThemes.flatly.primary)
.checkElementStyle(':root', '--secondary', remixIdeThemes.flatly.secondary)
.checkElementStyle(':root', '--success', remixIdeThemes.flatly.success)
.checkElementStyle(':root', '--info', remixIdeThemes.flatly.info)
.checkElementStyle(':root', '--warning', remixIdeThemes.flatly.warning)
.checkElementStyle(':root', '--danger', remixIdeThemes.flatly.danger)
},
'Should load Spacelab theme': function (browser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.click('*[data-id="settingsTabThemeLabelSpacelab"]')
.pause(2000)
.checkElementStyle(':root', '--primary', remixIdeThemes.spacelab.primary)
.checkElementStyle(':root', '--secondary', remixIdeThemes.spacelab.secondary)
.checkElementStyle(':root', '--success', remixIdeThemes.spacelab.success)
.checkElementStyle(':root', '--info', remixIdeThemes.spacelab.info)
.checkElementStyle(':root', '--warning', remixIdeThemes.spacelab.warning)
.checkElementStyle(':root', '--danger', remixIdeThemes.spacelab.danger)
},
'Should load Cyborg theme': function (browser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.click('*[data-id="settingsTabThemeLabelCyborg"]')
.pause(2000)
.checkElementStyle(':root', '--primary', remixIdeThemes.cyborg.primary)
.checkElementStyle(':root', '--secondary', remixIdeThemes.cyborg.secondary)
.checkElementStyle(':root', '--success', remixIdeThemes.cyborg.success)
.checkElementStyle(':root', '--info', remixIdeThemes.cyborg.info)
.checkElementStyle(':root', '--warning', remixIdeThemes.cyborg.warning)
.checkElementStyle(':root', '--danger', remixIdeThemes.cyborg.danger)
},
tearDown: sauce
}
const remixIdeThemes = {
dark: {
primary: '#007aa6',
secondary: '#595c76',
success: '#32ba89',
info: '#086CB5',
warning: '#c97539',
danger: '#b84040'
},
light: {
primary: '#007aa6',
secondary: '#a8b3bc',
success: '#32ba89',
info: '#007aa6',
warning: '#c97539',
danger: '#b84040'
},
curelean: {
primary: '#2FA4E7',
secondary: '#e9ecef',
success: '#73A839',
info: '#033C73',
warning: '#DD5600',
danger: '#C71C22'
},
flatly: {
primary: '#2C3E50',
secondary: '#95a5a6',
success: '#18BC9C',
info: '#3498DB',
warning: '#F39C12',
danger: '#E74C3C'
},
spacelab: {
primary: '#446E9B',
secondary: '#999',
success: '#3CB521',
info: '#3399F3',
warning: '#D47500',
danger: '#CD0200'
},
cyborg: {
primary: '#2A9FD6',
secondary: '#555',
success: '#77B300',
info: '#9933CC',
warning: '#FF8800',
danger: '#CC0000'
}
}

@ -0,0 +1,98 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
const testData = {
validGistId: '1859c97c6e1efc91047d725d5225888e',
invalidGistId: '6368b389f9302v32902msk2402'
}
// 99266d6da54cc12f37f11586e8171546c7700d67
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'UploadToGists': function (browser: NightwatchBrowser) {
/*
- set the access token
- publish to gist
- retrieve the gist
- switch to a file in the new gist
*/
console.log('token', process.env.gist_token)
const runtimeBrowser = browser.options.desiredCapabilities.browserName
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('fileExplorers')
.rightClick('[data-path="browser/1_Storage.sol"]')
.click('*[id="menuitemcreate folder"]')
.waitForElementVisible('*[data-id="modalDialogContainer"]')
.setValue('*[data-id="modalDialogCustomPromptText"]', 'Browser_Tests')
.modalFooterOKClick()
.waitForElementVisible('*[data-id="treeViewLibrowser/Browser_Tests"]')
.addFile('File.sol', { content: '' })
.click('*[data-id="fileExplorerNewFilepublishToGist"]')
.modalFooterOKClick()
.getModalBody((value, done) => {
const reg = /gist.github.com\/([^.]+)/
const id = value.match(reg)
console.log('gist regex', id)
if (!id) {
browser.assert.fail('cannot get the gist id', '', '')
} else {
const gistid = id[1]
browser
.modalFooterCancelClick()
.executeScript(`remix.loadgist('${gistid}')`)
.perform((done) => { if (runtimeBrowser === 'chrome') { browser.openFile('browser/gists') } done() })
.openFile(`browser/gists/${gistid}/1_Storage.sol`)
.perform(done)
}
})
},
'Load Gist Modal': function (browser: NightwatchBrowser) {
browser.clickLaunchIcon('home')
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('fileExplorers')
.scrollAndClick('*[data-id="landingPageImportFromGistButton"]')
.waitForElementVisible('*[data-id="modalDialogModalTitle"]')
.assert.containsText('*[data-id="modalDialogModalTitle"]', 'Load a Gist')
.waitForElementVisible('*[data-id="modalDialogModalBody"]')
.assert.containsText('*[data-id="modalDialogModalBody"]', 'Enter the ID of the Gist or URL you would like to load.')
.waitForElementVisible('*[data-id="modalDialogCustomPromptText"]')
.modalFooterCancelClick()
},
'Display Error Message For Invalid Gist ID': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('fileExplorers')
.scrollAndClick('*[data-id="landingPageImportFromGistButton"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptText"]')
.setValue('*[data-id="modalDialogCustomPromptText"]', testData.invalidGistId)
.modalFooterOKClick()
.waitForElementVisible('*[data-id="modalDialogModalBody"]')
.assert.containsText('*[data-id="modalDialogModalBody"]', 'Gist load error: Not Found')
.modalFooterOKClick()
},
'Import From Gist For Valid Gist ID': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('fileExplorers')
.scrollAndClick('*[data-id="landingPageImportFromGistButton"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptText"]')
.setValue('*[data-id="modalDialogCustomPromptText"]', testData.validGistId)
.modalFooterOKClick()
.openFile(`browser/gists/${testData.validGistId}/ApplicationRegistry`)
.waitForElementVisible(`div[title='browser/gists/${testData.validGistId}/ApplicationRegistry']`)
.assert.containsText(`div[title='browser/gists/${testData.validGistId}/ApplicationRegistry'] > span`, 'ApplicationRegistry')
.end()
},
tearDown: sauce
}

@ -0,0 +1,127 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Add Lib Test File': function (browser: NightwatchBrowser) {
browser.addFile('Untitled5.sol', sources[0]['browser/Untitled5.sol'])
.clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
},
'Test Auto Deploy Lib': function (browser: NightwatchBrowser) {
let addressRef: string
browser.verifyContracts(['test'])
.selectContract('test')
.createContract('')
.getAddressAtPosition(0, (address) => {
console.log('testAutoDeployLib ' + address)
addressRef = address
})
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2) > div > button')
.perform((done) => {
browser.testConstantFunction(addressRef, 'get - call', null, '0:\nuint256: 45').perform(() => {
done()
})
})
},
'Test Manual Deploy Lib': function (browser: NightwatchBrowser) {
console.log('testManualDeployLib')
browser.click('*[data-id="deployAndRunClearInstances"]')
.pause(5000)
.clickLaunchIcon('settings')
.click('*[data-id="settingsTabGenerateContractMetadataLabel"]')
.clickLaunchIcon('solidity')
.click('#compileTabView button[title="Compile"]') // that should generate the JSON artefact
.verifyContracts(['test'])
.selectContract('lib') // deploy lib
.createContract('')
.perform((done) => {
browser.getAddressAtPosition(0, (address) => {
console.log(address)
checkDeployShouldFail(browser, () => {
checkDeployShouldSucceed(browser, address, () => {
done()
})
})
})
})
.end()
},
tearDown: sauce
}
function checkDeployShouldFail (browser: NightwatchBrowser, callback: VoidFunction) {
let config
browser.openFile('browser/artifacts').openFile('browser/artifacts/test.json')
.getEditorValue((content) => {
config = JSON.parse(content)
config.deploy['VM:-'].autoDeployLib = false
})
.perform(() => {
browser.setEditorValue(JSON.stringify(config))
})
.openFile('browser/Untitled5.sol')
.selectContract('test') // deploy lib
.createContract('')
.getText('div[class^="terminal"]', (value) => {
console.log('value: ', value)
})
.assert.containsText('div[class^="terminal"]', '<address> is not a valid address')
.perform(() => { callback() })
}
function checkDeployShouldSucceed (browser: NightwatchBrowser, address: string, callback: VoidFunction) {
let addressRef: string
let config
browser.openFile('browser/artifacts').openFile('browser/artifacts/test.json')
.getEditorValue((content) => {
config = JSON.parse(content)
config.deploy['VM:-'].autoDeployLib = false
config.deploy['VM:-']['linkReferences']['browser/Untitled5.sol'].lib = address
})
.perform(() => {
browser.setEditorValue(JSON.stringify(config))
})
.openFile('browser/Untitled5.sol')
.selectContract('test') // deploy lib
.createContract('')
.getAddressAtPosition(1, (address) => {
addressRef = address
})
.waitForElementPresent('.instance:nth-of-type(3)')
.click('.instance:nth-of-type(3) > div > button')
.perform(() => {
browser
.testConstantFunction(addressRef, 'get - call', null, '0:\nuint256: 45')
.perform(() => { callback() })
})
}
const sources = [
{
'browser/Untitled5.sol': {content: `library lib {
function getInt () public view returns (uint) {
return 45;
}
}
contract test {
function get () public view returns (uint) {
return lib.getInt();
}
}`}
}
]

@ -0,0 +1,155 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
const testData = {
pluginName: 'remixIde',
pluginDisplayName: 'Remix IDE',
pluginUrl: 'https://remix-project.org/'
}
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', false)
},
'Should Load Plugin Manager': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.pause(3000)
.click('*[plugin="pluginManager"]')
.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.assert.containsText('*[data-id="sidePanelSwapitTitle"]', 'PLUGIN MANAGER')
},
'Should Search for plugins': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.click('*[data-id="pluginManagerComponentSearchInput"]')
.keys('debugger')
.waitForElementVisible('*[data-id="pluginManagerComponentActivateButtondebugger"]')
.clearValue('*[data-id="pluginManagerComponentSearchInput"]')
.click('*[data-id="pluginManagerComponentSearchInput"]')
.keys('Vyper')
.waitForElementVisible('*[data-id="pluginManagerComponentActivateButtonvyper"]')
.clearValue('*[data-id="pluginManagerComponentSearchInput"]')
.click('*[data-id="pluginManagerComponentSearchInput"]')
.keys('ZoKrates')
.waitForElementVisible('*[data-id="pluginManagerComponentActivateButtonZoKrates"]')
.clearValue('*[data-id="pluginManagerComponentSearchInput"]')
.click('*[data-id="pluginManagerComponentSearchInput"]')
.keys(browser.Keys.ENTER)
},
'Should activate plugins': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.click('*[data-id="pluginManagerComponentPluginManager"]')
.scrollAndClick('*[data-id="pluginManagerComponentActivateButtondebugger"]')
.waitForElementVisible('*[data-id="pluginManagerComponentDeactivateButtondebugger"]')
.scrollAndClick('*[data-id="pluginManagerComponentActivateButtonvyper"]')
.waitForElementVisible('*[data-id="pluginManagerComponentDeactivateButtonvyper"]')
.scrollAndClick('*[data-id="pluginManagerComponentActivateButtonZoKrates"]')
.waitForElementVisible('*[data-id="pluginManagerComponentDeactivateButtonZoKrates"]')
},
'Should deactivate plugins': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.click('*[data-id="pluginManagerComponentPluginManager"]')
.scrollAndClick('*[data-id="pluginManagerComponentDeactivateButtondebugger"]')
.waitForElementVisible('*[data-id="pluginManagerComponentActivateButtondebugger"]')
.scrollAndClick('*[data-id="pluginManagerComponentDeactivateButtonvyper"]')
.waitForElementVisible('*[data-id="pluginManagerComponentActivateButtonvyper"]')
},
/*
'Should grant plugin permission (ZOKRATES)': function (browser) {
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.click('*[data-id="pluginManagerPermissionsButton"]')
.waitForElementVisible('*[data-id="pluginManagerSettingsPermissionForm"]')
.assert.containsText('*[data-id="pluginManagerSettingsPermissionForm"]', 'No Permission requested yet')
.modalFooterOKClick()
.click('*[data-id="verticalIconsFileExplorerIcons"]')
.openFile('browser/3_Ballot.sol')
.click('*[plugin="ZoKrates"]')
.pause(5000)
.frame(0)
.useXpath().click("//span[text()='Compile']")
.pause(2000)
.frameParent()
.useCss().waitForElementVisible('*[data-id="modalDialogContainer"]')
.assert.containsText('*[data-id="permissionHandlerMessage"]', 'ZOKRATES" WOULD LIKE TO ACCESS "FILE MANAGER" :')
.pause(2000)
.click('*[data-id="permissionHandlerRememberChoice"]')
.pause(2000)
.modalFooterOKClick()
},
'Should revert plugin permission (ZOKRATES)': function (browser) {
browser.waitForElementVisible('*[data-id="verticalIconsSettingsIcons"]')
.click('*[data-id="verticalIconsSettingsIcons"]')
.waitForElementVisible('*[data-id="pluginManagerPermissionsButton"]')
.click('*[data-id="pluginManagerPermissionsButton"]')
.waitForElementVisible('*[data-id="modalDialogContainer"]')
.click('*[data-id="pluginManagerSettingsPermissionForm"]')
.pause(2000)
.click('*[data-id="pluginManagerSettingsClearAllPermission"]')
.pause(2000)
.assert.containsText('*[data-id="pluginManagerSettingsPermissionForm"]', 'No Permission requested yet')
.modalFooterOKClick()
},
*/
'Should connect a local plugin': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.click('*[data-id="pluginManagerComponentPluginSearchButton"]')
.waitForElementVisible('*[data-id="modalDialogContainer"]')
.click('*[data-id="modalDialogModalBody"]')
.waitForElementVisible('*[data-id="localPluginName"]')
.setValue('*[data-id="localPluginName"]', testData.pluginName)
.setValue('*[data-id="localPluginDisplayName"]', testData.pluginDisplayName)
.setValue('*[data-id="localPluginUrl"]', testData.pluginUrl)
.click('*[data-id="localPluginRadioButtoniframe"]')
.click('*[data-id="localPluginRadioButtonsidePanel"]')
.click('*[data-id="modalDialogModalFooter"]')
.modalFooterOKClick()
.waitForElementVisible('*[data-id="pluginManagerComponentDeactivateButtonremixIde"]')
},
'Should display error message for creating already existing plugin': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.click('*[data-id="pluginManagerComponentPluginSearchButton"]')
.waitForElementVisible('*[data-id="modalDialogContainer"]')
.click('*[data-id="modalDialogModalBody"]')
.waitForElementVisible('*[data-id="localPluginName"]')
.clearValue('*[data-id="localPluginName"]').setValue('*[data-id="localPluginName"]', testData.pluginName)
.clearValue('*[data-id="localPluginDisplayName"]').setValue('*[data-id="localPluginDisplayName"]', testData.pluginDisplayName)
.clearValue('*[data-id="localPluginUrl"]').setValue('*[data-id="localPluginUrl"]', testData.pluginUrl)
.click('*[data-id="localPluginRadioButtoniframe"]')
.click('*[data-id="localPluginRadioButtonsidePanel"]')
.click('*[data-id="modalDialogModalFooter"]')
.modalFooterOKClick()
.pause(5000)
.waitForElementVisible('*[data-shared="tooltipPopup"]:nth-last-of-type(1)')
.pause(2000)
.assert.containsText('*[data-shared="tooltipPopup"]:nth-last-of-type(1)', 'Cannot create Plugin : This name has already been used')
},
'Should load back installed plugins after reload': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.getInstalledPlugins((plugins) => {
browser.refresh()
.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.pause(3000)
.perform((done) => {
plugins.forEach(plugin => {
if (plugin !== testData.pluginName) {
browser.waitForElementVisible(`[plugin="${plugin}"`)
}
})
done()
})
})
.end()
},
tearDown: sauce
}

@ -0,0 +1,66 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return []
},
'Publish on IPFS': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('#icon-panel', 10000)
.clickLaunchIcon('fileExplorers')
.openFile('browser/3_Ballot.sol')
.verifyContracts(['Ballot'])
.click('#publishOnIpfs')
.getModalBody((value, done) => {
if (value.indexOf('Metadata of "ballot" was published successfully.') === -1) browser.assert.fail('ipfs deploy failed', '', '')
if (value.indexOf('dweb:/ipfs') === -1) browser.assert.fail('ipfs deploy failed', '', '')
done()
})
.modalFooterOKClick()
},
'Publish on Swarm': '' + function (browser: NightwatchBrowser) {
browser
.click('#publishOnSwarm')
.getModalBody((value, done) => {
if (value.indexOf('Metadata of "ballot" was successfully.') === -1) browser.assert.fail('swarm deploy failed', '', '')
if (value.indexOf('bzz') === -1) browser.assert.fail('swarm deploy failed', '', '')
done()
})
.modalFooterOKClick()
},
'Should publish contract metadata to ipfs on deploy': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('#icon-panel')
.clickLaunchIcon('fileExplorers')
.openFile('browser/1_Storage.sol')
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="contractDropdownIpfsCheckbox"]')
.click('*[data-id="contractDropdownIpfsCheckbox"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.pause(5000)
.assert.containsText('*[data-id="modalDialogModalBody"]', 'Metadata of "storage" was published successfully.')
.modalFooterOKClick()
},
'Should remember choice after page refresh': function (browser: NightwatchBrowser) {
browser
.refresh()
.openFile('browser/1_Storage.sol')
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="contractDropdownIpfsCheckbox"]')
.verify.elementPresent('*[data-id="contractDropdownIpfsCheckbox"]:checked')
.end()
},
tearDown: sauce
}

@ -0,0 +1,300 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Test Recorder': function (browser: NightwatchBrowser) {
let addressRef
browser.addFile('scenario.json', {content: records})
.pause(5000)
.clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.click('div[class^="cardContainer"] i[class^="arrow"]')
.click('#runTabView .runtransaction')
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2) > div > button')
.waitForElementPresent('.instance:nth-of-type(3)')
.click('.instance:nth-of-type(3) > div > button')
.clickFunction('getInt - call')
.clickFunction('getAddress - call')
.clickFunction('getFromLib - call')
.waitForElementPresent('div[class^="contractActionsContainer"] div[class^="value"] ul')
.getAddressAtPosition(1, (address) => {
console.log('Test Recorder ' + address)
addressRef = address
})
.perform((done) => {
browser.verifyCallReturnValue(addressRef, ['0:uint256: 1', '0:uint256: 3456', '0:address: 0xbBF289D846208c16EDc8474705C748aff07732dB'])
.perform(() => done())
})
.click('*[data-id="deployAndRunClearInstances"]')
.testContracts('testRecorder.sol', sources[0]['browser/testRecorder.sol'], ['testRecorder'])
.createContract('12')
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2) > div > button')
.clickFunction('set - transact (not payable)', {types: 'uint256 _p', values: '34'})
.click('i.savetransaction')
.modalFooterOKClick()
.getEditorValue(function (result) {
const parsed = JSON.parse(result)
browser.assert.equal(JSON.stringify(parsed.transactions[0].record.parameters), JSON.stringify(scenario.transactions[0].record.parameters))
browser.assert.equal(JSON.stringify(parsed.transactions[0].record.name), JSON.stringify(scenario.transactions[0].record.name))
browser.assert.equal(JSON.stringify(parsed.transactions[0].record.type), JSON.stringify(scenario.transactions[0].record.type))
browser.assert.equal(JSON.stringify(parsed.transactions[0].record.from), JSON.stringify(scenario.transactions[0].record.from))
browser.assert.equal(JSON.stringify(parsed.transactions[0].record.contractName), JSON.stringify(scenario.transactions[0].record.contractName))
browser.assert.equal(JSON.stringify(parsed.transactions[1].record.parameters), JSON.stringify(scenario.transactions[1].record.parameters))
browser.assert.equal(JSON.stringify(parsed.transactions[1].record.name), JSON.stringify(scenario.transactions[1].record.name))
browser.assert.equal(JSON.stringify(parsed.transactions[1].record.type), JSON.stringify(scenario.transactions[1].record.type))
browser.assert.equal(JSON.stringify(parsed.transactions[1].record.from), JSON.stringify(scenario.transactions[1].record.from))
})
.end()
},
tearDown: sauce
}
const sources = [{'browser/testRecorder.sol': {content: `contract testRecorder {
constructor(uint p) public {
}
function set (uint _p) public {
}
}`}}]
const records = `{
"accounts": {
"account{10}": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"
},
"linkReferences": {
"testLib": "created{1512830014773}"
},
"transactions": [
{
"timestamp": 1512830014773,
"record": {
"value": "0",
"parameters": [],
"abi": "0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a",
"contractName": "testLib",
"bytecode": "60606040523415600e57600080fd5b60968061001c6000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636d4ce63c146044575b600080fd5b604a6060565b6040518082815260200191505060405180910390f35b6000610d809050905600a165627a7a7230582022d123b15248b8176151f8d45c2dc132063bcc9bb8d5cd652aea7efae362c8050029",
"linkReferences": {},
"inputs": "()",
"type": "constructor",
"from": "account{10}"
}
},
{
"timestamp": 1512830015080,
"record": {
"value": "100",
"parameters": [
11
],
"abi": "0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec",
"contractName": "test",
"bytecode": "60606040526040516020806102b183398101604052808051906020019091905050806000819055505061027a806100376000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632f30c6f61461006757806338cc48311461009e57806362738998146100f357806387cc10e11461011c575b600080fd5b61009c600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610145565b005b34156100a957600080fd5b6100b1610191565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100fe57600080fd5b6101066101bb565b6040518082815260200191505060405180910390f35b341561012757600080fd5b61012f6101c4565b6040518082815260200191505060405180910390f35b8160008190555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054905090565b600073__browser/ballot.sol:testLib____________636d4ce63c6000604051602001526040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b151561022e57600080fd5b6102c65a03f4151561023f57600080fd5b505050604051805190509050905600a165627a7a72305820e0b2510bb2890a0334bfe5613d96db3e72442e63b514cdeaee8fc2c6bbd19d3a0029",
"linkReferences": {
"browser/ballot.sol": {
"testLib": [
{
"length": 20,
"start": 511
}
]
}
},
"name": "",
"type": "constructor",
"inputs": "(uint256)",
"from": "account{10}"
}
},
{
"timestamp": 1512830034180,
"record": {
"value": "1000000000000000000",
"parameters": [
1,
"created{1512830015080}"
],
"to": "created{1512830015080}",
"abi": "0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec",
"name": "set",
"inputs": "(uint256,address)",
"type": "function",
"from": "account{10}"
}
}
],
"abis": {
"0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a": [
{
"constant": true,
"inputs": [],
"name": "get",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
],
"0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec": [
{
"constant": true,
"inputs": [],
"name": "getInt",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getFromLib",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getAddress",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_t",
"type": "uint256"
},
{
"name": "_add",
"type": "address"
}
],
"name": "set",
"outputs": [],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"name": "_r",
"type": "uint256"
}
],
"payable": true,
"stateMutability": "payable",
"type": "constructor"
}
]
}
}`
const scenario = {
'accounts': {
'account{10}': '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'
},
'linkReferences': {},
'transactions': [
{
'timestamp': 1512912691086,
'record': {
'value': '0',
'parameters': [
"12" // eslint-disable-line
],
'abi': '0x54a8c0ab653c15bfb48b47fd011ba2b9617af01cb45cab344acd57c924d56798',
'contractName': 'testRecorder',
'bytecode': '6060604052341561000f57600080fd5b6040516020806100cd833981016040528080519060200190919050505060938061003a6000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b1146044575b600080fd5b3415604e57600080fd5b606260048080359060200190919050506064565b005b505600a165627a7a723058204839660366b94f5f3c8c6da233a2c5fe95ad5635b5c8a2bb630a8b845d68ecdd0029',
'linkReferences': {},
'name': '',
'type': 'constructor',
'inputs': '(uint256)',
'from': 'account{10}'
}
},
{
'timestamp': 1512912696128,
'record': {
'value': '0',
'parameters': [
"34" // eslint-disable-line
],
'to': 'created{1512912691086}',
'abi': '0x54a8c0ab653c15bfb48b47fd011ba2b9617af01cb45cab344acd57c924d56798',
'name': 'set',
'inputs': '(uint256)',
'type': 'function',
'from': 'account{10}'
}
}
],
'abis': {
'0x54a8c0ab653c15bfb48b47fd011ba2b9617af01cb45cab344acd57c924d56798': [
{
'constant': false,
'inputs': [
{
'name': '_p',
'type': 'uint256'
}
],
'name': 'set',
'outputs': [],
'payable': false,
'stateMutability': 'nonpayable',
'type': 'function'
},
{
'inputs': [
{
'name': 'p',
'type': 'uint256'
}
],
'payable': false,
'stateMutability': 'nonpayable',
'type': 'constructor'
}
]
}
}

@ -0,0 +1,141 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
const assetsTestContract = `import "./contract.sol";
contract Assets {
uint[] proposals;
function add(uint8 _numProposals) public {
proposals.length = _numProposals;
}
}
`
const gmbhTestContract = `contract gmbh {
uint[] proposals;
function register(uint8 _numProposals) public {
proposals.length = _numProposals;
}
}
`
const sources = [
{
'localhost/folder1/contract2.sol': {content: 'contract test2 { function get () public returns (uint) { return 11; }}'}
},
{
'localhost/src/gmbh/company.sol': {content: assetsTestContract}
},
{
'localhost/src/gmbh/company.sol': {content: assetsTestContract},
'localhost/src/gmbh/contract.sol': {content: gmbhTestContract}
},
{
'browser/test_import_node_modules.sol': {content: 'import "openzeppelin-solidity/contracts/math/SafeMath.sol";'}
},
{
'browser/test_import_node_modules_with_github_import.sol': {content: 'import "openzeppelin-solidity/contracts/sample.sol";'}
}
]
module.exports = {
before: function (browser, done) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Remixd': function (browser) {
runTests(browser)
},
'Import from node_modules ': function (browser) {
/*
when a relative import is used (i.e import "openzeppelin-solidity/contracts/math/SafeMath.sol")
remix (as well as truffle) try to resolve it against the node_modules and installed_contracts folder.
*/
browser.waitForElementVisible('#icon-panel', 2000)
.clickLaunchIcon('fileExplorers')
.addFile('test_import_node_modules.sol', sources[3]['browser/test_import_node_modules.sol'])
.clickLaunchIcon('solidity')
.testContracts('test_import_node_modules.sol', sources[3]['browser/test_import_node_modules.sol'], ['SafeMath'])
},
'Import from node_modules and reference a github import': function (browser) {
browser.waitForElementVisible('#icon-panel', 2000)
.clickLaunchIcon('fileExplorers')
.addFile('test_import_node_modules_with_github_import.sol', sources[4]['browser/test_import_node_modules_with_github_import.sol'])
.clickLaunchIcon('solidity')
.setSolidityCompilerVersion('soljson-v0.6.2+commit.bacdbe57.js') // open-zeppelin moved to pragma ^0.6.0
.testContracts('test_import_node_modules_with_github_import.sol', sources[4]['browser/test_import_node_modules_with_github_import.sol'], ['ERC20', 'test11'])
.clickLaunchIcon('pluginManager')
.scrollAndClick('#pluginManager article[id="remixPluginManagerListItem_remixd"] button')
.end()
},
tearDown: sauce
}
function runTests (browser: NightwatchBrowser) {
const browserName = browser.options.desiredCapabilities.browserName
if (browserName === 'safari' || browserName === 'internet explorer') {
console.log('do not run remixd test for ' + browserName + ': sauce labs doesn\'t seems to handle websocket')
browser.end()
return
}
browser
.waitForElementVisible('#icon-panel', 2000)
.clickLaunchIcon('fileExplorers')
.clickLaunchIcon('pluginManager')
.scrollAndClick('#pluginManager article[id="remixPluginManagerListItem_remixd"] button')
.waitForElementVisible('#modal-footer-ok', 2000)
.pause(2000)
.click('#modal-footer-ok')
.clickLaunchIcon('fileExplorers')
.waitForElementVisible('[data-path="localhost/folder1"]')
.click('[data-path="localhost/folder1"]')
.waitForElementVisible('[data-path="localhost/contract1.sol"]')
.assert.containsText('[data-path="localhost/contract1.sol"]', 'contract1.sol')
.assert.containsText('[data-path="localhost/contract2.sol"]', 'contract2.sol')
.waitForElementVisible('[data-path="localhost/folder1/contract1.sol"]')
.assert.containsText('[data-path="localhost/folder1/contract1.sol"]', 'contract1.sol')
.assert.containsText('[data-path="localhost/folder1/contract2.sol"]', 'contract2.sol') // load and test sub folder
.click('[data-path="localhost/folder1/contract2.sol"]')
.click('[data-path="localhost/folder1/contract1.sol"]') // open localhost/folder1/contract1.sol
.pause(1000)
.testEditorValue('contract test1 { function get () returns (uint) { return 10; }}') // check the content and replace by another
.setEditorValue('contract test1Changed { function get () returns (uint) { return 10; }}')
.testEditorValue('contract test1Changed { function get () returns (uint) { return 10; }}')
.setEditorValue('contract test1 { function get () returns (uint) { return 10; }}')
.click('[data-path="localhost/folder1/contract_' + browserName + '.sol"]') // rename a file and check
.pause(1000)
.renameFile('localhost/folder1/contract_' + browserName + '.sol', 'renamed_contract_' + browserName + '.sol', 'localhost/folder1/renamed_contract_' + browserName + '.sol')
.pause(1000)
.removeFile('localhost/folder1/contract_' + browserName + '_toremove.sol')
.perform(function (done) {
testImportFromRemixd(browser, () => { done() })
})
.clickLaunchIcon('fileExplorers')
.waitForElementVisible('[data-path="localhost/folder1"]')
.click('[data-path="localhost/folder1"]')
.click('[data-path="localhost/folder1"]') // click twice because remixd does not return nested folder details after update
.waitForElementVisible('[data-path="localhost/folder1/contract1.sol"]')
.waitForElementVisible('[data-path="localhost/folder1/renamed_contract_' + browserName + '.sol"]') // check if renamed file is preset
.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"]')
}
function testImportFromRemixd (browser: NightwatchBrowser, callback: VoidFunction) {
browser
.waitForElementVisible('[data-path="localhost/src"]', 100000)
.click('[data-path="localhost/src"]')
.waitForElementVisible('[data-path="localhost/src/gmbh"]', 100000)
.click('[data-path="localhost/src/gmbh"]')
.waitForElementVisible('[data-path="localhost/src/gmbh/company.sol"]', 100000)
.click('[data-path="localhost/src/gmbh/company.sol"]')
.pause(1000)
.verifyContracts(['Assets', 'gmbh'])
.perform(() => { callback() })
}

@ -0,0 +1,208 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
const passphrase = process.env.account_passphrase
const password = process.env.account_password
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Should load run and deploy tab': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="sidePanelSwapitTitle"]')
.assert.containsText('*[data-id="sidePanelSwapitTitle"]', 'DEPLOY & RUN TRANSACTIONS')
},
'Should sign message using account key': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="settingsRemixRunSignMsg"]')
.click('*[data-id="settingsRemixRunSignMsg"]')
.pause(2000)
.waitForElementPresent('*[data-id="modalDialogCustomPromptText"]')
.setValue('*[data-id="modalDialogCustomPromptText"]', 'Remix is cool!')
.assert.elementNotPresent('*[data-id="settingsRemixRunSignMsgHash"]')
.assert.elementNotPresent('*[data-id="settingsRemixRunSignMsgSignature"]')
.modalFooterOKClick()
.waitForElementPresent('*[data-id="modalDialogContainer"]', 12000)
.assert.elementPresent('*[data-id="settingsRemixRunSignMsgHash"]')
.assert.elementPresent('*[data-id="settingsRemixRunSignMsgSignature"]')
.modalFooterOKClick()
},
'Should deploy contract on JavascriptVM': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.clickLaunchIcon('fileExplorers')
.addFile('Greet.sol', sources[0]['browser/Greet.sol'])
.clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c')
.waitForElementPresent('*[data-id="Deploy - transact (not payable)"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.pause(5000)
.testFunction('0xc39ee005c1e1368c84f02e458de4b41dbb966631a8714d15ef8362dada249ede', {
status: '0x1 Transaction mined and execution succeed',
'transaction hash': '0xc39ee005c1e1368c84f02e458de4b41dbb966631a8714d15ef8362dada249ede'
})
},
'Should run low level interaction (fallback function)': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.waitForElementPresent('*[data-id="universalDappUiTitleExpander"]')
.click('*[data-id="universalDappUiTitleExpander"]')
.waitForElementPresent('*[data-id="pluginManagerSettingsDeployAndRunLLTxSendTransaction"]')
.click('*[data-id="pluginManagerSettingsDeployAndRunLLTxSendTransaction"]')
.pause(5000)
.testFunction('0xfe718871ee0b4d03cdcac0e12e5b164efaf7e23ba952c07db76e62692867019b', {
status: '0x1 Transaction mined and execution succeed',
'transaction hash': '0xfe718871ee0b4d03cdcac0e12e5b164efaf7e23ba952c07db76e62692867019b'
})
},
'Should connect to Goerli Test Network using MetaMask': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.setupMetamask(passphrase, password)
.click('.network-indicator__down-arrow')
.useXpath().click("//span[text()='Goerli Test Network']")
.useCss().switchBrowserTab(0)
.refresh()
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.click('*[data-id="landingPageStartSolidity"]')
.pause(5000)
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="settingsSelectEnvOptions"]')
.click('*[data-id="settingsSelectEnvOptions"] option[id="injected-mode"]')
.waitForElementPresent('*[data-id="settingsNetworkEnv"]')
.assert.containsText('*[data-id="settingsNetworkEnv"]', 'Goerli (5) network')
.switchBrowserTab(2)
.waitForElementPresent('.page-container__footer-button:nth-of-type(2)')
.click('.page-container__footer-button:nth-of-type(2)')
.switchBrowserTab(0)
},
'Should deploy contract on Goerli Test Network using MetaMask': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="runTabSelectAccount"] option')
.clickLaunchIcon('fileExplorers')
.openFile('browser/Greet.sol')
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="Deploy - transact (not payable)"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.switchBrowserTab(2)
.waitForElementPresent('.transaction-status--unapproved')
.click('.transaction-status--unapproved')
.waitForElementPresent('.page-container__footer-button:nth-of-type(2)')
.click('.page-container__footer-button:nth-of-type(2)')
.waitForElementPresent('.transaction-status--submitted')
.pause(25000)
.switchBrowserTab(0)
},
'Should run low level interaction (fallback function) on Goerli Test Network using MetaMask': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.waitForElementPresent('*[data-id="universalDappUiTitleExpander"]')
.click('*[data-id="universalDappUiTitleExpander"]')
.waitForElementPresent('*[data-id="pluginManagerSettingsDeployAndRunLLTxSendTransaction"]')
.click('*[data-id="pluginManagerSettingsDeployAndRunLLTxSendTransaction"]')
.switchBrowserTab(2)
.waitForElementPresent('.transaction-status--unapproved')
.click('.transaction-status--unapproved')
.waitForElementPresent('.page-container__footer-button:nth-of-type(2)')
.click('.page-container__footer-button:nth-of-type(2)')
.waitForElementPresent('.transaction-status--submitted')
.pause(25000)
.switchBrowserTab(0)
},
'Should connect to Ethereum Main Network using MetaMask': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.switchBrowserTab(2)
.waitForElementPresent('.network-indicator__down-arrow')
.click('.network-indicator__down-arrow')
.useXpath().click("//span[text()='Main Ethereum Network']")
.useCss().switchBrowserTab(0)
.refresh()
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.click('*[data-id="landingPageStartSolidity"]')
.pause(5000)
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="settingsSelectEnvOptions"]')
.click('*[data-id="settingsSelectEnvOptions"] option[id="injected-mode"]')
.waitForElementPresent('*[data-id="settingsNetworkEnv"]')
.assert.containsText('*[data-id="settingsNetworkEnv"]', 'Main (1) network')
},
'Should deploy contract on Ethereum Main Network using MetaMask': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="runTabSelectAccount"] option')
.clickLaunchIcon('fileExplorers')
.openFile('browser/Greet.sol')
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="Deploy - transact (not payable)"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.waitForElementPresent('*[data-id="modalDialogContainer"]', 15000)
.pause(10000)
.assert.containsText('*[data-id="modalDialogModalBody"]', 'You are creating a transaction on the main network. Click confirm if you are sure to continue.')
.modalFooterCancelClick()
},
/*
* This test is using 3 differents services:
* - Metamask for getting the transaction
* - Source Verifier service for fetching the contract code
* - Ropsten node for retrieving the trace and storage
*
*/
'Should debug Ropsten transaction with source highlighting using the source verifier service and MetaMask': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.switchBrowserTab(2)
.waitForElementPresent('.network-indicator__down-arrow')
.click('.network-indicator__down-arrow')
.useXpath().click("//span[text()='Ropsten Test Network']") // switch to Ropsten
.useCss().switchBrowserTab(0)
.refresh()
.clickLaunchIcon('pluginManager') // load debugger and source verification
// .scrollAndClick('#pluginManager article[id="remixPluginManagerListItem_source-verification"] button')
// debugger already activated .scrollAndClick('#pluginManager article[id="remixPluginManagerListItem_debugger"] button')
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="settingsSelectEnvOptions"]')
.click('*[data-id="settingsSelectEnvOptions"] option[id="injected-mode"]') // switch to Ropsten in udapp
.waitForElementPresent('*[data-id="settingsNetworkEnv"]')
.assert.containsText('*[data-id="settingsNetworkEnv"]', 'Ropsten (3) network')
.clickLaunchIcon('debugger')
.setValue('*[data-id="debuggerTransactionInput"]', '0x959371506b8f6223d71c709ac2eb2d0158104dca2d76ca949f1662712cf0e6db') // debug tx
.click('*[data-id="debuggerTransactionStartButton"]')
.waitForElementVisible('*[data-id="treeViewDivto"]', 30000)
.assert.containsText('*[data-id="stepdetail"]', 'loaded address:\n0x3c943Fb816694d7D1f4C738e3e7823818a88DD6C')
.assert.containsText('*[data-id="solidityLocals"]', 'to: 0x6C3CCC7FBA111707D5A1AAF2758E9D4F4AC5E7B1')
.end()
},
tearDown: sauce
}
const sources = [
{
'browser/Greet.sol': {
content:
`
pragma solidity ^0.6.0;
contract helloWorld {
string public message;
fallback () external {
message = 'Hello World!';
}
function greet(string memory _message) public {
message = _message;
}
}`
}
}
]

@ -0,0 +1,60 @@
// const https = require('https')
export default function sauce (callback: VoidFunction): void {
if (typeof callback === 'function') 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 (!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,97 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Test Signature': function (browser: NightwatchBrowser) {
let hash, signature
browser
.clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.signMessage('test message', (h, s) => {
hash = h
signature = s
console.log('hash', hash)
console.log('signature', signature)
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')
})
.addFile('signMassage.sol', sources[0]['browser/signMassage.sol'])
.openFile('browser/signMassage.sol')
.pause(5000)
.selectContract('ECVerify')
.createContract('')
.clickInstance(0)
.perform((done) => {
browser.getAddressAtPosition(0, (address) => {
// skip 'instance' part of e.g. 'instance0x692a70d2e424a56d2c6c27aa97d1a86395877b3a'
console.log('Test Signature address', address)
const inputs = `"${hash.value}","${signature.value}"`
console.log('Test Signature Input', inputs)
browser.clickFunction('ecrecovery - call', { types: 'bytes32 hash, bytes sig', values: inputs })
.pause(5000)
.verifyCallReturnValue(
address,
['0:address: 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'])
.perform(() => {
done()
})
})
})
.end()
},
tearDown: sauce
}
const sources = [
{
'browser/signMassage.sol': {content: `
pragma solidity >=0.4.22 <0.7.0;
contract SignMassageTest {
function testRecovery(bytes32 h, uint8 v, bytes32 r, bytes32 s) public pure returns (address) {
return ecrecover(h, v, r, s);
}
}
library ECVerify {
function ecrecovery(bytes32 hash, bytes memory sig) public pure returns (address) {
bytes32 r;
bytes32 s;
uint8 v;
if (sig.length != 65) {
return address(0);
}
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := and(mload(add(sig, 65)), 255)
}
if (v < 27) {
v += 27;
}
if (v != 27 && v != 28) {
return address(0);
}
return ecrecover(hash, v, r, s);
}
function ecverify(bytes32 hash, bytes memory sig, address signer) public pure returns (bool) {
return signer == ecrecovery(hash, sig);
}
}`}
}
]

@ -0,0 +1,89 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Test Simple Contract': function (browser: NightwatchBrowser) {
browser.testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['test1', 'test2'])
},
'Test Success Import': function (browser: NightwatchBrowser) {
browser.addFile('Untitled1.sol', sources[1]['browser/Untitled1.sol'])
.addFile('Untitled2.sol', sources[1]['browser/Untitled2.sol'])
.openFile('browser/Untitled1.sol')
.verifyContracts(['test6', 'test4', 'test5'])
},
'Test Failed Import': function (browser: NightwatchBrowser) {
browser.addFile('Untitled3.sol', sources[2]['browser/Untitled3.sol'])
.clickLaunchIcon('solidity')
.assert.containsText('#compileTabView .error pre', 'not found browser/Untitled11.sol')
},
'Test Github Import - from master branch': function (browser: NightwatchBrowser) {
browser
.setSolidityCompilerVersion('soljson-v0.6.2+commit.bacdbe57.js') // open-zeppelin moved to pragma ^0.6.0 (master branch)
.addFile('Untitled4.sol', sources[3]['browser/Untitled4.sol'])
.clickLaunchIcon('fileExplorers')
.verifyContracts(['test7', 'ERC20', 'SafeMath'], {wait: 10000})
},
'Test Github Import - from other branch': function (browser: NightwatchBrowser) {
browser
.setSolidityCompilerVersion('soljson-v0.5.0+commit.1d4f565a.js') // switch back to 0.5.0 : release-v2.3.0 branch is not solidity 0.6 compliant
.addFile('Untitled5.sol', sources[4]['browser/Untitled5.sol'])
.clickLaunchIcon('fileExplorers')
.verifyContracts(['test8', 'ERC20', 'SafeMath'], {wait: 10000})
},
'Test Github Import - no branch specified': function (browser: NightwatchBrowser) {
browser
.setSolidityCompilerVersion('soljson-v0.6.2+commit.bacdbe57.js') // open-zeppelin moved to pragma ^0.6.0 (master branch)
.addFile('Untitled6.sol', sources[5]['browser/Untitled6.sol'])
.clickLaunchIcon('fileExplorers')
.verifyContracts(['test10', 'ERC20', 'SafeMath'], {wait: 10000})
},
'Test Github Import - raw URL': function (browser: NightwatchBrowser) {
browser
.addFile('Untitled7.sol', sources[6]['browser/Untitled7.sol'])
.clickLaunchIcon('fileExplorers')
.verifyContracts(['test11', 'ERC20', 'SafeMath'], {wait: 10000})
.end()
},
tearDown: sauce
}
const sources = [
{
'browser/Untitled.sol': {content: 'contract test1 {} contract test2 {}'}
},
{
'browser/Untitled1.sol': {content: 'import "./Untitled2.sol"; contract test6 {}'},
'browser/Untitled2.sol': {content: 'contract test4 {} contract test5 {}'}
},
{
'browser/Untitled3.sol': {content: 'import "./Untitled11.sol"; contract test6 {}'}
},
{
'browser/Untitled4.sol': {content: 'import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol"; contract test7 {}'}
},
{
'browser/Untitled5.sol': {content: 'import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v2.3.0/contracts/token/ERC20/ERC20.sol"; contract test8 {}'}
},
{
'browser/Untitled6.sol': {content: 'import "https://github.com/OpenZeppelin/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; contract test10 {}'}
},
{
'browser/Untitled7.sol': {content: 'import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol"; contract test11 {}'}
}
]

@ -0,0 +1,382 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Should launch solidity unit test plugin': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="verticalIconsKindfileExplorers"]')
.clickLaunchIcon('fileExplorers')
.addFile('simple_storage.sol', sources[0]['browser/simple_storage.sol'])
.addFile('ks2a.sol', sources[0]['browser/ks2a.sol'])
.clickLaunchIcon('pluginManager')
.scrollAndClick('*[data-id="pluginManagerComponentActivateButtonsolidityUnitTesting"]')
.click('*[data-id="verticalIconsKindsolidityUnitTesting"]')
.waitForElementPresent('*[data-id="sidePanelSwapitTitle"]')
.assert.containsText('*[data-id="sidePanelSwapitTitle"]', 'SOLIDITY UNIT TESTING')
},
'Should generate test file': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="verticalIconsKindfileExplorers"]')
.clickLaunchIcon('fileExplorers')
.openFile('browser/simple_storage.sol')
.click('*[data-id="verticalIconsKindsolidityUnitTesting"]')
.waitForElementPresent('*[data-id="testTabGenerateTestFile"]')
.click('*[data-id="testTabGenerateTestFile"]')
.waitForElementPresent('*[title="browser/tests/simple_storage_test.sol"]')
.clickLaunchIcon('fileExplorers')
.pause(10000)
.openFile('browser/tests/simple_storage_test.sol')
.removeFile('browser/tests/simple_storage_test.sol')
},
'Should run simple unit test `simple_storage_test.sol` ': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="verticalIconsKindfileExplorers"]')
.addFile('tests/simple_storage_test.sol', sources[0]['browser/tests/simple_storage_test.sol'])
.click('*[data-id="verticalIconsKindsolidityUnitTesting"]')
.waitForElementPresent('*[data-id="testTabCheckAllTests"]')
.click('*[data-id="testTabCheckAllTests"]')
.clickElementAtPosition('.singleTestLabel', 1)
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 80000)
.pause(5000)
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'MyTest (browser/tests/simple_storage_test.sol)')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✓ Initial value should be100')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✓ Value is set200')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Should fail for wrong value200')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'Passing: 2')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'Failing: 1')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'FAIL MyTest (browser/tests/simple_storage_test.sol)')
},
'Should run advance unit test using natspec and experimental ABIEncoderV2 `ks2b_test.sol` ': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="verticalIconsKindfileExplorers"]')
.clickLaunchIcon('fileExplorers')
.addFile('tests/ks2b_test.sol', sources[0]['browser/tests/ks2b_test.sol'])
.click('*[data-id="verticalIconsKindsolidityUnitTesting"]')
.waitForElementPresent('*[data-id="testTabCheckAllTests"]')
.click('*[data-id="testTabCheckAllTests"]')
.clickElementAtPosition('.singleTestLabel', 2)
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 40000)
.pause(5000)
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'browser/tests/ks2b_test.sol')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✓ Check project exists')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Check wrong project owner')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Check wrong sender')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✘ Check wrong value')
.pause(5000)
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', '✓ Check project is fundable')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'owner is incorrect')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'wrong sender')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'wrong value')
},
'Should stop unit tests during test execution` ': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="verticalIconsKindfileExplorers"]')
.waitForElementPresent('*[data-id="testTabRunTestsTabRunAction"]')
.clickElementAtPosition('.singleTestLabel', 0)
.clickElementAtPosition('.singleTestLabel', 1)
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.pause(5000)
.click('*[data-id="testTabRunTestsTabStopAction"]')
.pause(1000)
.assert.containsText('*[data-id="testTabRunTestsTabStopAction"]', 'Stopping')
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 40000)
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'browser/tests/ks2b_test.sol')
.notContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'browser/tests/4_Ballot_test.sol')
.notContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'browser/tests/simple_storage_test.sol')
.pause(7000)
.assert.containsText('*[data-id="testTabTestsExecutionStopped"]', 'The test execution has been stopped')
},
'Should fail on compilation': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="verticalIconsKindfileExplorers"]')
.addFile('tests/compilationError_test.sol', sources[0]['browser/compilationError_test.sol'])
.clickLaunchIcon('fileExplorers')
.openFile('browser/tests/compilationError_test.sol')
.clickLaunchIcon('solidityUnitTesting')
.click('*[data-id="testTabCheckAllTests"]')
.clickElementAtPosition('.singleTestLabel', 3)
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 40000)
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutput"]')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'SyntaxError: No visibility specified')
.assert.containsText('*[data-id="testTabTestsExecutionStoppedError"]', 'The test execution has been stopped because of error(s) in your test file')
},
'Should fail on deploy': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="verticalIconsKindfileExplorers"]')
.addFile('tests/deployError_test.sol', sources[0]['browser/tests/deployError_test.sol'])
.clickLaunchIcon('fileExplorers')
.openFile('browser/tests/deployError_test.sol')
.clickLaunchIcon('solidityUnitTesting')
.click('*[data-id="testTabCheckAllTests"]')
.clickElementAtPosition('.singleTestLabel', 4)
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 40000)
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutput"]')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'contract deployment failed after trying twice')
},
'Should fail when parameters are to method in test contract': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="verticalIconsKindfileExplorers"]')
.addFile('tests/methodFailure_test.sol', sources[0]['browser/tests/methodFailure_test.sol'])
.clickLaunchIcon('fileExplorers')
.openFile('browser/tests/methodFailure_test.sol')
.clickLaunchIcon('solidityUnitTesting')
.click('*[data-id="testTabCheckAllTests"]')
.clickElementAtPosition('.singleTestLabel', 5)
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 40000)
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutput"]')
.assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', `Method 'add' can not have parameters inside a test contract`)
},
'Changing current path': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="verticalIconsKindfileExplorers"]')
.addFile('myTests/simple_storage_test.sol', sources[0]['browser/tests/simple_storage_test.sol'])
.clickLaunchIcon('solidityUnitTesting')
.setValue('*[data-id="uiPathInput"]', 'browser/myTests')
.clickElementAtPosition('.singleTestLabel', 0)
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 40000)
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutput"]')
.clearValue('*[data-id="uiPathInput"]')
.setValue('*[data-id="uiPathInput"]', 'browser/tests')
},
'Solidity Unittests': function (browser: NightwatchBrowser) {
runTests(browser)
},
tearDown: sauce
}
function runTests (browser: NightwatchBrowser) {
browser
.waitForElementPresent('*[data-id="verticalIconsKindfileExplorers"]')
.clickLaunchIcon('fileExplorers')
.openFile('browser/3_Ballot.sol')
.clickLaunchIcon('solidityUnitTesting')
.pause(500)
.scrollAndClick('#runTestsTabRunAction')
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 40000)
.waitForElementPresent('#solidityUnittestsOutput div[class^="testPass"]', 7000)
.assert.containsText('#solidityUnittestsOutput', 'browser/tests/4_Ballot_test.sol')
.assert.containsText('#solidityUnittestsOutput', '✓ Check winning proposal')
.assert.containsText('#solidityUnittestsOutput', '✓ Check winnin proposal with return value')
.end()
}
const sources = [
{
'browser/simple_storage.sol': {
content: `
pragma solidity >=0.4.22 <0.7.0;
contract SimpleStorage {
uint public storedData;
constructor() public {
storedData = 100;
}
function set(uint x) public {
storedData = x;
}
function get() public view returns (uint retVal) {
return storedData;
}
}
`
},
'browser/tests/simple_storage_test.sol': {
content: `
pragma solidity >=0.4.22 <0.7.0;
import "remix_tests.sol";
import "../simple_storage.sol";
contract MyTest {
SimpleStorage foo;
function beforeEach() public {
foo = new SimpleStorage();
}
function initialValueShouldBe100() public returns (bool) {
return Assert.equal(foo.get(), 100, "initial value is not correct");
}
function valueIsSet200() public returns (bool) {
foo.set(200);
return Assert.equal(foo.get(), 200, "value is not 200");
}
function shouldFailForWrongValue200() public returns (bool) {
foo.set(300);
return Assert.equal(foo.get(), 200, "value is not 200");
}
}
`
},
'browser/ks2a.sol': {
content: `
pragma solidity >=0.4.22 <0.6.0;
contract Kickstarter {
enum State { Started, Completed }
struct Project {
address owner;
string name;
uint goal;
uint fundsAvailable; // added
uint amountContributed; // added
State state;
mapping(address => uint) funders; // added
}
Project[] public projects;
constructor() public {
}
function createProject(string memory name, uint goal) public {
projects.length++; // new line
Project storage project = projects[projects.length - 1];
project.name = name;
project.goal = goal;
project.owner = msg.sender;
project.state = State.Started;
}
function fundProject(uint projectId) payable public {
Project storage project = projects[projectId];
// require project exists
// PLEASE CHECK / or erase
// not this: require(projects[projectId].exists, "the project must exist to be funded");
// require for... underflow/overflow protection
project.funders[msg.sender] += msg.value;
project.amountContributed += msg.value;
project.fundsAvailable += msg.value;
if (project.amountContributed >= project.goal) {
project.state = State.Completed;
}
}
// this function is here because we can't use web3 when using the VM
function getContractBalance() public view returns(uint balance) {
return address(this).balance;
}
}
`
},
'browser/tests/ks2b_test.sol': {
content: `
pragma solidity >=0.4.22 <0.6.0;
pragma experimental ABIEncoderV2;
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "remix_accounts.sol";
import "../ks2a.sol";
contract kickstarterTest {
enum State { Started, Completed }
Kickstarter kickstarter;
function beforeAll () public {
kickstarter = new Kickstarter();
kickstarter.createProject("ProjectA", 123000);
kickstarter.createProject("ProjectB", 100);
}
/// #sender: account-1
/// #value: 10000000
function checkProjectExists () public payable {
(address owner, string memory name, uint goal, uint fundsAvailable, uint amountContributed, Kickstarter.State state) = kickstarter.projects(0);
Assert.equal(name, "ProjectA", "project name is incorrect");
Assert.equal(goal, 123000, "funding goal is incorrect");
Assert.equal(owner, address(this), "owner is incorrect");
Assert.equal(msg.sender, TestsAccounts.getAccount(1), "wrong sender");
Assert.equal(msg.value, 10000000, "wrong value");
}
/// #sender: account-1
/// #value: 10000000
function checkWrongProjectOwner () public payable {
(address owner,,,,,) = kickstarter.projects(0);
Assert.equal(owner, TestsAccounts.getAccount(0), "owner is incorrect"); //failing case
}
/// #sender: account-1
/// #value: 10000000
function checkWrongSender () public payable {
Assert.equal(msg.sender, TestsAccounts.getAccount(0), "wrong sender"); //failing case
}
/// #sender: account-1
/// #value: 10000000
function checkWrongValue () public payable {
Assert.equal(msg.value, 5000000, "wrong value"); //failing case
}
function checkProjectIsFundable () public {
kickstarter.fundProject.value(120000)(0);
(address owner, string memory name, uint goal, uint fundsAvailable, uint amountContributed, Kickstarter.State state) = kickstarter.projects(0);
Assert.equal(amountContributed, 120000, "contributed amount is incorrect");
}
}
`
},
'browser/compilationError_test.sol': {
content: `
pragma solidity ^0.6.1;
contract failOnCompilation {
fallback() {
}
}
`
},
'browser/tests/deployError_test.sol': {
content: `
pragma solidity ^0.6.0;
contract failingDeploy {
constructor() public {
revert('Deploy Failed');
}
}
`
},
'browser/tests/methodFailure_test.sol': {
content: `
pragma solidity ^0.6.0;
contract methodfailure {
function add(uint a, uint b) public {
uint c = a+b;
Assert.equal(a+b, c, "wrong value");
}
}
`
}
}
]

@ -0,0 +1,284 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
// this test suite also contribute testing https://github.com/ethereum/remix/pull/1497 and https://github.com/ethereum/remix-ide/pull/2898
// quick explanation:
// the goal of https://github.com/ethereum/remix-ide/pull/2898 is to keep track of all the compiled contracts an not only the last one.
// this introduce an issue: if 2 compiled contracts have the same name, the second one override the first which is not wanted.
// fix's delivered by https://github.com/ethereum/remix/pull/1497: instead of getting contract by name,
// which result in name clashing we process the whole contract object (which contain bytecode, deployedbytecode, ...)
init(browser, done)
},
'@sources': function () {
return sources
},
'Use special functions receive/fallback - both are declared, sending data': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('receiveAndFallback.sol', sources[0]['browser/receiveAndFallback.sol'], ['CheckSpecials']) // compile
.clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.selectContract('CheckSpecials')
.createContract('') // deploy
.clickInstance(0)
.perform((done) => {
browser.getAddressAtPosition(0, (address) => {
browser.sendLowLevelTx(address, '0', '0xaa')
.pause(1000)
.journalLastChildIncludes('to: CheckSpecials.(fallback)')
.journalLastChildIncludes('value: 0 wei')
.journalLastChildIncludes('data: 0xaa')
.perform(done)
})
})
},
'Use special functions receive/fallback - both are declared, failing sending data < 1 byte': function (browser: NightwatchBrowser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(0, (address) => {
browser.sendLowLevelTx(address, '0', '0xa')
.pause(1000)
.waitForElementVisible(`#instance${address} label[id="deployAndRunLLTxError"]`)
.assert.containsText(`#instance${address} label[id="deployAndRunLLTxError"]`, `The calldata should be a valid hexadecimal value with size of at least one byte.`)
.perform(done)
})
})
},
'Use special functions receive/fallback - both are declared, failing sending data with odd number of digits': function (browser: NightwatchBrowser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(0, (address) => {
browser.sendLowLevelTx(address, '0', '0x1aa')
.pause(1000)
.waitForElementVisible(`#instance${address} label[id="deployAndRunLLTxError"]`)
.assert.containsText(`#instance${address} label[id="deployAndRunLLTxError"]`, `The calldata should be a valid hexadecimal value.`)
.perform(done)
})
})
},
'Use special functions receive/fallback - both are declared - receive called, sending wei': function (browser: NightwatchBrowser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(0, (address) => {
browser.sendLowLevelTx(address, '1', '')
.pause(1000)
.journalLastChildIncludes('to: CheckSpecials.(receive)')
.journalLastChildIncludes('value: 1 wei')
.journalLastChildIncludes('data: 0x')
.perform(done)
})
})
},
'Use special functions receive/fallback - both are declared - fallback should fail cause not payable, sending data and wei': function (browser: NightwatchBrowser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(0, (address) => {
browser.sendLowLevelTx(address, '10', '0xaa')
.pause(1000)
.journalLastChildIncludes('to CheckSpecials.(fallback) errored:')
.journalLastChildIncludes('The called function should be payable if you send value')
.perform(done)
})
})
},
'Use special functions receive/fallback - only receive is declared, sending wei': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('receiveOnly.sol', sources[1]['browser/receiveOnly.sol'], ['CheckSpecials'])
.clickLaunchIcon('udapp')
.selectContract('CheckSpecials')
.createContract('')
.clickInstance(1)
.perform((done) => {
browser.getAddressAtPosition(1, (address) => {
browser.sendLowLevelTx(address, '1', '')
.pause(1000)
.journalLastChildIncludes('to: CheckSpecials.(receive)')
.journalLastChildIncludes('value: 1 wei')
.journalLastChildIncludes('data: 0x')
.perform(done)
})
})
},
'Use special functions receive/fallback - only receive is declared, failing, fallback is not declared, sending data': function (browser: NightwatchBrowser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(1, (address) => {
browser.sendLowLevelTx(address, '0', '0xaa')
.pause(1000)
.waitForElementVisible(`#instance${address} label[id="deployAndRunLLTxError"]`)
.assert.containsText(`#instance${address} label[id="deployAndRunLLTxError"]`, `'Fallback' function is not defined`)
.perform(done)
})
})
},
'Use special functions receive/fallback - only fallback declared and is payable, sending wei': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('fallbackOnlyPayable.sol', sources[2]['browser/fallbackOnlyPayable.sol'], ['CheckSpecials'])
.clickLaunchIcon('udapp')
.selectContract('CheckSpecials')
.createContract('')
.clickInstance(2)
.perform((done) => {
browser.getAddressAtPosition(2, (address) => {
browser.sendLowLevelTx(address, '1', '')
.pause(1000)
.journalLastChildIncludes('to: CheckSpecials.(fallback)')
.journalLastChildIncludes('value: 1 wei')
.journalLastChildIncludes('data: 0x')
.perform(done)
})
})
},
'Use special functions receive/fallback - only fallback is diclared and is payable, sending data and wei': function (browser: NightwatchBrowser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(2, (address) => {
browser.sendLowLevelTx(address, '1', '0xaa')
.pause(1000)
.journalLastChildIncludes('to: CheckSpecials.(fallback)')
.journalLastChildIncludes('value: 1 wei')
.journalLastChildIncludes('data: 0xaa')
.perform(done)
})
})
},
'Use special functions receive/fallback - only fallback is declared, fallback should fail cause not payable, sending wei': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('fallbackOnlyNotPayable.sol', sources[3]['browser/fallbackOnlyNotPayable.sol'], ['CheckSpecials'])
.clickLaunchIcon('udapp')
.selectContract('CheckSpecials')
.createContract('')
.clickInstance(3)
.perform((done) => {
browser.getAddressAtPosition(3, (address) => {
browser.sendLowLevelTx(address, '1', '')
.pause(1000)
.waitForElementVisible(`#instance${address} label[id="deployAndRunLLTxError"]`)
.assert.containsText(`#instance${address} label[id="deployAndRunLLTxError"]`, `should have either 'receive' or payable 'fallback'`)
.perform(done)
})
})
},
'Use special functions receive/fallback - receive and fallback are declared, sending data and wei': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('receiveAndFallbackBothPayable.sol', sources[4]['browser/receiveAndFallbackBothPayable.sol'], ['CheckSpecials'])
.clickLaunchIcon('udapp')
.selectContract('CheckSpecials')
.waitForElementVisible('#value')
.clearValue('#value')
.setValue('#value', '0')
.createContract('')
.clickInstance(4)
.pause(1000)
.perform((done) => {
browser.getAddressAtPosition(4, (address) => {
browser.sendLowLevelTx(address, '1', '0xaa')
.pause(1000)
.journalLastChildIncludes('to: CheckSpecials.(fallback)')
.journalLastChildIncludes('value: 1 wei')
.journalLastChildIncludes('data: 0xaa')
.perform(done)
})
})
},
'Use special functions receive/fallback - receive and fallback are declared and payable, sending wei': function (browser: NightwatchBrowser) {
browser.perform((done) => {
browser.getAddressAtPosition(4, (address) => {
browser.sendLowLevelTx(address, '1', '')
.pause(1000)
.journalLastChildIncludes('to: CheckSpecials.(receive)')
.journalLastChildIncludes('value: 1 wei')
.journalLastChildIncludes('data: 0x')
.perform(done)
})
})
},
'Use special functions receive/fallback - receive and fallback are not declared, sending nothing': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('notSpecial.sol', sources[5]['browser/notSpecial.sol'], ['CheckSpecials'])
.clickLaunchIcon('udapp')
.selectContract('CheckSpecials')
.waitForElementVisible('#value')
.clearValue('#value')
.setValue('#value', '0')
.createContract('')
.clickInstance(5)
.pause(1000)
.perform((done) => {
browser.getAddressAtPosition(5, (address) => {
browser.sendLowLevelTx(address, '0', '')
.pause(1000)
.waitForElementVisible(`#instance${address} label[id="deployAndRunLLTxError"]`)
.assert.containsText(`#instance${address} label[id="deployAndRunLLTxError"]`, `Both 'receive' and 'fallback' functions are not defined`)
.perform(done)
})
})
.end()
},
tearDown: sauce
}
const sources = [
{
'browser/receiveAndFallback.sol': {
content: `
contract CheckSpecials {
receive() payable external{}
fallback() external {}
}
`
}
},
{
'browser/receiveOnly.sol': {
content: `
contract CheckSpecials {
receive() payable external {}
}
`
}
},
{
'browser/fallbackOnlyPayable.sol': {
content: `
contract CheckSpecials {
fallback() payable external {}
}
`
}
},
{
'browser/fallbackOnlyNotPayable.sol': {
content: `
contract CheckSpecials {
fallback() external {}
}
`
}
},
{
'browser/receiveAndFallbackBothPayable.sol': {
content: `
contract CheckSpecials {
receive() payable external {}
fallback() payable external {}
}
`
}
},
{
'browser/notSpecial.sol': {
content: `
contract CheckSpecials {
function otherFallback() payable external {}
}
`
}
}
]

@ -0,0 +1,70 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
const sources = [
{
'browser/Untitled.sol': {content: `
pragma solidity >=0.4.22 <0.6.0;
contract test1 { address test = tx.origin; }
contract test2 {}
contract TooMuchGas {
uint x;
function() external {
x++;
uint test;
uint test1;
}
}`}}
]
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Static Analysis': function (browser: NightwatchBrowser) {
runTests(browser)
},
tearDown: sauce
}
function runTests (browser: NightwatchBrowser) {
browser
.waitForElementVisible('#icon-panel', 10000)
.clickLaunchIcon('solidity')
.testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['TooMuchGas', 'test1', 'test2'])
.clickLaunchIcon('solidityStaticAnalysis')
.click('#staticanalysisView button')
.waitForElementPresent('#staticanalysisresult .warning', 2000, true, function () {
listSelectorContains(['Use of tx.origin',
'Fallback function of contract TooMuchGas requires too much gas',
'TooMuchGas.() : Variables have very similar names "test" and "test1".'],
'#staticanalysisresult .warning',
browser, function () {
browser.end()
}
)
})
}
function listSelectorContains (textsToFind: string[], selector: string, browser: NightwatchBrowser, callback: VoidFunction) {
browser.execute(function (selector) {
const items = document.querySelectorAll(selector)
const ret = []
for (let k = 0; k < items.length; k++) {
ret.push(items[k].innerText)
}
return ret
}, [selector], function (result) {
console.log(result.value)
for (const k in textsToFind) {
console.log('testing `' + result.value[k] + '` against `' + textsToFind[k] + '`')
browser.assert.equal(result.value[k].indexOf(textsToFind[k]) !== -1, true)
}
callback()
})
}

@ -0,0 +1,104 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080?plugins=solidity,udapp', false)
},
'Should execution a simple console command': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="terminalCli"]', 10000)
.executeScript('console.log(1 + 1)')
.journalLastChild('2')
},
'Should clear console': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="terminalCli"]')
.journalChildIncludes('Welcome to Remix')
.click('#clearConsole')
.assert.containsText('*[data-id="terminalJournal"]', '')
},
'Should display auto-complete menu': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="terminalCli"]')
.click('*[data-id="terminalCli"]')
.sendKeys('*[data-id="terminalCliInput"]', 'remix.')
.assert.visible('*[data-id="autoCompletePopUpAutoCompleteItem"]')
},
'Should execute remix.help() command': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="terminalCli"]')
.executeScript('remix.help()')
.journalChildIncludes('remix.loadgist(id)')
.journalChildIncludes('remix.loadurl(url)')
.journalChildIncludes('remix.execute(filepath)')
.journalChildIncludes('remix.exeCurrent()')
.journalChildIncludes('remix.help()')
},
'Async/Await Script': function (browser: NightwatchBrowser) {
browser
.addFile('asyncAwait.js', { content: asyncAwait })
.openFile('browser/asyncAwait.js')
.executeScript(`remix.execute('browser/asyncAwait.js')`)
.journalLastChild('Waiting Promise')
.pause(5500)
.journalLastChild('result - Promise Resolved')
},
'Call Remix File Manager from a script': function (browser: NightwatchBrowser) {
browser
.addFile('asyncAwaitWithFileManagerAccess.js', { content: asyncAwaitWithFileManagerAccess })
.openFile('browser/asyncAwaitWithFileManagerAccess.js')
.pause(5000)
.executeScript(`remix.execute('browser/asyncAwaitWithFileManagerAccess.js')`)
.pause(6000)
.journalLastChildIncludes('contract Ballot {')
.end()
},
tearDown: sauce
}
const asyncAwait = `
var p = function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("Promise Resolved")
}, 5000)
})
}
var run = async () => {
console.log('Waiting Promise')
var result = await p()
console.log('result - ', result)
}
run()
`
const asyncAwaitWithFileManagerAccess = `
var p = function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("Promise Resolved")
}, 0)
})
}
var run = async () => {
console.log('Waiting Promise')
var result = await p()
let text = await remix.call('fileManager', 'readFile', 'browser/3_Ballot.sol')
console.log('result - ', text)
}
run()
`

@ -0,0 +1,211 @@
'use strict'
import { NightwatchBrowser } from "nightwatch"
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Execute Simple Contract and Test Terminal': function (browser: NightwatchBrowser) {
browser.testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['TestContract'])
.clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.click('#runTabView button[class^="instanceButton"]')
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2) > div > button')
.click('#runTabView .instance div[class^="title"]')
.click('#runTabView .instance div[class^="title"]')
.clickFunction('f - transact (not payable)')
.testFunction('0x38bb944fa4709ed9e163d6c670259f97284b4defd916d512a2fcc3f35bb53e03',
{
status: '0x1 Transaction mined and execution succeed',
'transaction hash': '0x38bb944fa4709ed9e163d6c670259f97284b4defd916d512a2fcc3f35bb53e03',
'decoded output': { '0': 'uint256: 8' }
})
.pause(500)
.checkTerminalFilter('0x12332162e2e31397dc1e07ed0a1cf08f728e9b4487c6f9ed79d2f39410c92782', '')
.clickFunction('g - transact (not payable)')
.testFunction('0xab4f794ca0b531f27fc6eace623666b440facbf20e77615a057d728c67b500f0',
{
status: '0x1 Transaction mined and execution succeed',
'transaction hash': '0xab4f794ca0b531f27fc6eace623666b440facbf20e77615a057d728c67b500f0',
'decoded output': {
'0': 'uint256: 345',
'1': 'string: comment_comment_',
'2': 'bool: true',
'3': 'uint256: 4'
}
})
.click('*[data-id="deployAndRunClearInstances"]')
},
'Test Complex Return Values': function (browser: NightwatchBrowser) {
browser.testContracts('returnValues.sol', sources[1]['browser/returnValues.sol'], ['testReturnValues'])
.clickLaunchIcon('udapp')
.click('#runTabView button[class^="instanceButton"]')
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2) > div > button')
.clickFunction('retunValues1 - transact (not payable)')
.testFunction('0x09c6716a67f0f8c7a0ca2b3ddf59c25982da856a95aefd640b767f9b9feee39d',
{
status: '0x1 Transaction mined and execution succeed',
'transaction hash': '0x09c6716a67f0f8c7a0ca2b3ddf59c25982da856a95aefd640b767f9b9feee39d',
'decoded output': {
'0': 'bool: _b true',
'1': 'uint256: _u 345',
'2': 'int256: _i -345',
'3': 'address: _a 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'
}
})
.clickFunction('retunValues2 - transact (not payable)')
.testFunction('0xe884953e0695399d60914af3e1ea2dad59fe41f3c0c20665c130fa40dd0fb6bf',
{
status: '0x1 Transaction mined and execution succeed',
'transaction hash': '0xe884953e0695399d60914af3e1ea2dad59fe41f3c0c20665c130fa40dd0fb6bf',
'decoded output': {
'0': 'bytes1: _b 0x12',
'1': 'bytes2: _b2 0x1223',
'2': 'bytes3: _b3 0x000000',
'3': 'bytes: _blit 0x123498',
'4': 'bytes5: _b5 0x0432450000',
'5': 'bytes6: _b6 0x234553253200',
'6': 'string: _str this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string',
'7': 'bytes7: _b7 0x03252353253253',
'8': 'bytes22: _b22 0x32523523532532532523532500000000000000000000',
'9': 'bytes32: _b32 0x0325235325325235325235325235320000000000000000000000000000000000'
}
}).pause(500)
.clickFunction('retunValues3 - transact (not payable)')
.testFunction('0xb4108649d5e65a4a0776d6ac98c2c356540a7e99d641705a82352a845d467eb5',
{
status: '0x1 Transaction mined and execution succeed',
'transaction hash': '0xb4108649d5e65a4a0776d6ac98c2c356540a7e99d641705a82352a845d467eb5',
'decoded output': {
'0': 'uint8: _en 2',
'1': 'int256[5][]: _a1 1,-45,-78,56,60,-1,42,334,-45455,-446,1,10,-5435,45,-7'
}
}).click('*[data-id="deployAndRunClearInstances"]')
},
'Test Complex Input Values': function (browser: NightwatchBrowser) {
browser.testContracts('inputValues.sol', sources[2]['browser/inputValues.sol'], ['test'])
.clickLaunchIcon('udapp')
.click('#runTabView button[class^="instanceButton"]')
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2) > div > button')
.clickFunction('inputValue1 - transact (not payable)', {types: 'uint256 _u, int256 _i, string _str', values: '"2343242", "-4324324", "string _ string _ string _ string _ string _ string _ string _ string _ string _ string _"'})
.testFunction('0xe9678b5486674a0425301a1d7e925c22cfb9f7f7ec6242697d742009f7ef5b97',
{
status: '0x1 Transaction mined and execution succeed',
'transaction hash': '0xe9678b5486674a0425301a1d7e925c22cfb9f7f7ec6242697d742009f7ef5b97',
'decoded output': {
'0': 'uint256: _uret 2343242',
'1': 'int256: _iret -4324324',
'2': 'string: _strret string _ string _ string _ string _ string _ string _ string _ string _ string _ string _'
}
})
.pause(500)
.clickFunction('inputValue2 - transact (not payable)', {types: 'uint256[3] _n, bytes8[4] _b8', values: '[1,2,3], ["0x1234000000000000", "0x1234000000000000","0x1234000000000000","0x1234000000000000"]'})
.testFunction('0x21724b08c3699bda8375803f8dc842194aea370f2aac284e55144b452dca321f', {
status: '0x1 Transaction mined and execution succeed',
'transaction hash': '0x21724b08c3699bda8375803f8dc842194aea370f2aac284e55144b452dca321f',
'decoded output': {
'0': 'uint256[3]: _nret 1,2,3',
'1': 'bytes8[4]: _b8ret 0x1234000000000000,0x1234000000000000,0x1234000000000000,0x1234000000000000'
},
logs: [
{
'from': '0x8c1ed7e19abaa9f23c476da86dc1577f1ef401f5',
'topic': '0xd30981760edbf605bda8689e945f622877f230c9a77cbfbd448aa4b7d8ac6e7f',
'event': 'event1',
'args': {
'0': '-123',
'1': '123',
'2': {
'hash': '0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658',
'type': 'Indexed'
},
'3': '0x12340000',
'4': 'test _ test _ test _ test test _ test test _ test test _ test test _ test test _ test test _ test ',
'_i': '-123',
'_u': '123',
'_str': {
'hash': '0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658',
'type': 'Indexed'
},
'_b': '0x12340000',
'_notIndexed': 'test _ test _ test _ test test _ test test _ test test _ test test _ test test _ test test _ test ',
'length': 5
}
}
]
})
.click('*[data-id="deployAndRunClearInstances"]')
.end()
},
tearDown: sauce
}
// @TODO test: bytes8[3][] type as input
const sources = [
{'browser/Untitled.sol': {content: `
contract TestContract { function f() public returns (uint) { return 8; }
function g() public returns (uint, string memory, bool, uint) {
uint payment = 345;
bool payed = true;
string memory comment = "comment_comment_";
uint month = 4;
return (payment, comment, payed, month); } }`}},
{'browser/returnValues.sol': {content: `
contract testReturnValues {
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
function retunValues1 () public returns (bool _b, uint _u, int _i, address _a) {
_b = true;
_u = 345;
_i = -345;
_a = msg.sender;
}
function retunValues2 () public returns (byte _b, bytes2 _b2, bytes3 _b3, bytes memory _blit, bytes5 _b5, bytes6 _b6, string memory _str, bytes7 _b7, bytes22 _b22, bytes32 _b32) {
_b = 0x12;
_b2 = 0x1223;
_b5 = hex"043245";
_b6 = hex"2345532532";
_b7 = hex"03252353253253";
_b22 = hex"325235235325325325235325";
_b32 = hex"032523532532523532523532523532";
_blit = hex"123498";
_str = "this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string _ this is a long string";
}
function retunValues3 () public returns (ActionChoices _en, int[5][] memory _a1) {
_en = ActionChoices.GoStraight;
int[5][] memory a = new int[5][](3);
a[0] = [int(1),-45,-78,56,60];
a[1] = [int(-1),42,334,-45455,-446];
a[2] = [int(1),10,-5435,45,-7];
_a1 = a;
}
}`}},
{'browser/inputValues.sol': {content: `
contract test {
event event1(int _i, uint indexed _u, string indexed _str, bytes4 _b, string _notIndexed);
function inputValue1 (uint _u, int _i, string memory _str) public returns (uint _uret, int _iret, string memory _strret) {
_uret = _u;
_iret = _i;
_strret = _str;
}
function inputValue2 (uint[3] memory _n, bytes8[4] memory _b8) public returns (uint[3] memory _nret, bytes8[4] memory _b8ret){
_nret = _n;
_b8ret = _b8;
emit event1(-123, 123, "test", hex"1234", "test _ test _ test _ test test _ test test _ test test _ test test _ test test _ test test _ test ");
}
}`}}
]

@ -0,0 +1,53 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
import sauce from './sauce'
import examples from '../examples/example-contracts'
const sources = [
{'browser/Untitled.sol': {content: examples.ballot.content}},
{'browser/Untitled1.sol': {content: `contract test {}`}}
]
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'The sequence: Compiling / Deploying / Compiling another contract / calling the first contract - should display in the log the transaction with all the decoded information': function (browser: NightwatchBrowser) {
// https://github.com/ethereum/remix-ide/issues/2864
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('solidity')
.testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['Ballot'])
.clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c')
.setValue('input[placeholder="bytes32[] proposalNames"]', '["0x48656c6c6f20576f726c64210000000000000000000000000000000000000000"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.waitForElementPresent('*[data-id="universalDappUiContractActionWrapper"]')
.click('*[data-id="universalDappUiTitleExpander"]')
.clickFunction('delegate - transact (not payable)', {types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'})
.testFunction('0x41fab8ea5b1d9fba5e0a6545ca1a2d62fff518578802c033c2b9a031a01c31b3',
{
status: '0x1 Transaction mined and execution succeed',
'transaction hash': '0x41fab8ea5b1d9fba5e0a6545ca1a2d62fff518578802c033c2b9a031a01c31b3',
'decoded input': { 'address to': '0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB' }
})
.clickLaunchIcon('solidity')
.testContracts('Untitled1.sol', sources[1]['browser/Untitled1.sol'], ['test'])
.clickLaunchIcon('udapp')
.clickFunction('delegate - transact (not payable)', {types: 'address to', values: ''})
.pause(5000)
.testFunction('0xca58080c8099429caeeffe43b8104df919c2c543dceb9edf9242fa55f045c803',
{
status: '0x0 Transaction mined but execution failed',
'transaction hash': '0xca58080c8099429caeeffe43b8104df919c2c543dceb9edf9242fa55f045c803',
'decoded input': { 'address to': '0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB' }
})
.end()
},
tearDown: sauce
}

@ -0,0 +1,52 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
import sauce from './sauce'
const sources = [
{'browser/basic.sol': { content:
`pragma solidity >=0.2.0 <0.7.0;
/**
* @title Basic contract
*/
contract Basic {
uint someVar;
constructor() public {}
}`
}}
]
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Using Web Worker': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('fileExplorers')
.addFile('browser/basic.sol', sources[0]['browser/basic.sol'])
.clickLaunchIcon('solidity')
.execute(function() {
const elem = document.getElementById('nightlies') as HTMLInputElement
elem.checked = true
})
.noWorkerErrorFor('soljson-v0.3.4+commit.7dab8902.js')
.noWorkerErrorFor('soljson-v0.6.5+commit.f956cc89.js')
.noWorkerErrorFor('soljson-v0.6.8-nightly.2020.5.14+commit.a6d0067b.js')
.noWorkerErrorFor('soljson-v0.6.0-nightly.2019.12.17+commit.d13438ee.js')
.noWorkerErrorFor('soljson-v0.4.26+commit.4563c3fc.js')
.execute(function() {
const elem = document.getElementById('nightlies') as HTMLInputElement
elem.checked = false
})
.end()
},
tearDown: sauce
}

@ -0,0 +1,20 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
import sauce from './sauce'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080?plugins=solidity,udapp', false)
},
'CheckSolidityActivatedAndUDapp': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('#icon-panel', 10000)
.clickLaunchIcon('solidity')
.clickLaunchIcon('udapp')
.end()
},
tearDown: sauce
}

@ -0,0 +1,84 @@
// Merge custom command types with nightwatch types
import { NightwatchBrowser, NightwatchBrowser, NightwatchBrowser } from "nightwatch";
declare module "nightwatch" {
export interface NightwatchCustomCommands {
clickLaunchIcon(icon: string): NightwatchBrowser,
switchBrowserTab(index: number): NightwatchBrowser,
scrollAndClick(target: string): NightwatchBrowser,
scrollInto(target: string): NightwatchBrowser,
testContracts(fileName: string, contractCode: NightwatchContractContent, compiledContractNames: string[]): NightwatchBrowser,
setEditorValue(value: string, callback?: () => void): NightwatchBrowser,
addFile(name: string, content: NightwatchContractContent): NightwatchBrowser,
verifyContracts(compiledContractNames: string[], opts?: { wait: number, version?: string }): NightwatchBrowser,
selectAccount(account?: string): NightwatchBrowser,
clickFunction(fnFullName: string, expectedInput?: NightwatchClickFunctionExpectedInput): NightwatchBrowser,
testFunction(txHash: string, expectedInput: NightwatchTestFunctionExpectedInput): NightwatchBrowser,
goToVMTraceStep(step: number, incr?: number): NightwatchBrowser,
checkVariableDebug(id: string, debugValue: NightwatchCheckVariableDebugValue): NightwatchBrowser,
addAtAddressInstance(address: string, isValidFormat: boolean, isValidChecksum: boolean): NightwatchBrowser,
modalFooterOKClick(): NightwatchBrowser,
clickInstance(index: number): NightwatchBrowser,
journalLastChildIncludes(val: string): NightwatchBrowser,
executeScript(script: string): NightwatchBrowser,
clearEditableContent(cssSelector: string): NightwatchBrowser,
journalChildIncludes(val: string): NightwatchBrowser,
debugTransaction(index: number): NightwatchBrowser,
checkElementStyle(cssSelector: string, styleProperty: string, expectedResult: string): NightwatchBrowser,
openFile(name: string): NightwatchBrowser,
editorScroll(direction: 'up' | 'down', numberOfTimes: number): NightwatchBrowser,
renameFile(path: string, newFileName: string, renamedPath: string): NightwatchBrowser,
rightClick(cssSelector: string): NightwatchBrowser,
waitForElementContainsText(id: string, value: string): NightwatchBrowser,
getModalBody(callback: (value: string, cb: VoidFunction) => void): NightwatchBrowser,
modalFooterCancelClick(): NightwatchBrowser,
selectContract(contractName: string): NightwatchBrowser,
createContract(inputParams: string): NightwatchBrowser,
getAddressAtPosition(index: number, cb: (pos: string) => void): NightwatchBrowser,
testConstantFunction(address: string, fnFullName: string, expectedInput: NightwatchTestConstantFunctionExpectedInput | null, expectedOutput: string): NightwatchBrowser,
getEditorValue(callback: (content: string) => void): NightwatchBrowser,
getInstalledPlugins(cb: (plugins: string[]) => void): NightwatchBrowser,
verifyCallReturnValue(address: string, checks: string[]): NightwatchBrowser,
testEditorValue(testvalue: string): NightwatchBrowser,
removeFile(path: string): NightwatchBrowser,
switchBrowserWindow(url: string, windowName: string, cb: (browser: NightwatchBrowser, window?: NightwatchCallbackResult<Window>) => void): NightwatchBrowser,
setupMetamask(passphrase: string, password: string): NightwatchBrowser,
signMessage(msg: string, callback: (hash: { value: string }, signature: { value: string }) => void): NightwatchBrowser,
setSolidityCompilerVersion(version: string): NightwatchBrowser,
clickElementAtPosition(cssSelector: string, index: number): NightwatchBrowser,
notContainsText(cssSelector: string, text: string): NightwatchBrowser,
sendLowLevelTx(address: string, value: string, callData: string): NightwatchBrowser,
journalLastChild(val: string): NightwatchBrowser,
checkTerminalFilter(filter: string, test: string): NightwatchBrowser,
noWorkerErrorFor(version: string): NightwatchBrowser
}
export interface NightwatchBrowser {
api: this,
emit: (status: string) => void,
fullscreenWindow: (result?: any) => this,
keys(keysToSend: string, callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void): NightwatchBrowser,
sendKeys: (selector: string, inputValue: string | string[], callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void) => NightwatchBrowser
}
export interface NightwatchContractContent {
content: string;
}
export interface NightwatchClickFunctionExpectedInput {
types: string,
values: string
}
export interface NightwatchTestFunctionExpectedInput {
[key: string]: any
}
export interface NightwatchTestConstantFunctionExpectedInput {
types: string,
values: string
}
export type NightwatchCheckVariableDebugValue = NightwatchTestFunctionExpectedInput
}

@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"outDir": "../../dist",
"allowJs": true
},
"include": ["**/*.ts", "**/*.js"]
}

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["node", "nightwatch"],
"esModuleInterop": true
},
"include": ["**/*.ts", "**/*.js"]
}

@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}

@ -0,0 +1,3 @@
gist_token=<token>
account_passphrase=<passphrase>
account_password=<password>

@ -0,0 +1,29 @@
{
"env": {
"browser": true,
"es6": true
},
"extends": "../../.eslintrc",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"rules": {
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-this-alias": "off",
"no-unused-vars": "off",
"no-redeclare": "off",
"no-empty": "off",
"no-constant-condition": "off",
"no-async-promise-executor": "off",
"no-useless-catch": "off",
"no-extra-semi": "off",
"no-undef": "off"
}
}

@ -0,0 +1,15 @@
.idea
.vscode
build
node_modules
docs/_build
reports
soljson.js
soljson.js.*
npm-debug.log*
remix
.DS_Store
contracts
TODO
.tern-port
temp_publish_docker

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save