commit
aa88a3edbc
@ -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" |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
} |
@ -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 } |
||||||
|
} |
Binary file not shown.
@ -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 @@ |
|||||||
|
node_modules |
@ -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…
Reference in new issue