Moved more custom commands to typescript

nightwatch-ts
ioedeveloper 4 years ago
parent a199d4a89e
commit 6446a6414e
  1. 38
      apps/remix-ide-e2e/src/commands/addFile.ts
  2. 25
      apps/remix-ide-e2e/src/commands/clickFunction.ts
  3. 8
      apps/remix-ide-e2e/src/commands/clickLaunchIcon.ts
  4. 37
      apps/remix-ide-e2e/src/commands/goToVMTraceStep.ts
  5. 15
      apps/remix-ide-e2e/src/commands/scrollAndClick.ts
  6. 23
      apps/remix-ide-e2e/src/commands/scrollInto.ts
  7. 16
      apps/remix-ide-e2e/src/commands/selectAccount.ts
  8. 22
      apps/remix-ide-e2e/src/commands/setEditorValue.ts
  9. 8
      apps/remix-ide-e2e/src/commands/switchBrowserTab.ts
  10. 26
      apps/remix-ide-e2e/src/commands/testContracts.ts
  11. 60
      apps/remix-ide-e2e/src/commands/testFunction.ts
  12. 70
      apps/remix-ide-e2e/src/commands/verifyContracts.ts
  13. 249
      apps/remix-ide-e2e/src/examples/example-contracts.ts
  14. 19
      apps/remix-ide-e2e/src/helpers/init.ts
  15. 3
      apps/remix-ide-e2e/src/tests/ballot.test.ts
  16. 37
      apps/remix-ide-e2e/src/types/index.d.ts

@ -0,0 +1,38 @@
import { NightwatchBrowser, NightwatchContractContent } from 'nightwatch'
const EventEmitter = require('events')
export class AddFile extends EventEmitter {
command (this: NightwatchBrowser, name: string, content: NightwatchContractContent): NightwatchBrowser {
this.api.perform((done: VoidFunction) => {
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()
})
}

@ -0,0 +1,25 @@
import { NightwatchBrowser, NightwatchClickFunctionExpectedInput } from 'nightwatch'
const EventEmitter = require('events')
export 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
}
}

@ -2,14 +2,12 @@ import { NightwatchBrowser } from "nightwatch"
const EventEmitter = require('events')
class ClickLaunchIcon extends EventEmitter {
command (this: NightwatchBrowser, icon: string) {
this.api.waitForElementVisible('#icon-panel div[plugin="' + icon + '"]').click('#icon-panel div[plugin="' + icon + '"]').perform((done: CallableFunction) => {
export 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: VoidFunction) => {
done()
this.emit('complete')
})
return this
}
}
module.exports = ClickLaunchIcon

@ -0,0 +1,37 @@
import { NightwatchBrowser } from 'nightwatch'
const EventEmitter = require('events')
export class GoToVmTraceStep extends EventEmitter {
command (this: NightwatchBrowser, step: number, incr?: number): NightwatchBrowser {
this.api.perform((done: VoidFunction) => {
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)
})
}
})
}

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

@ -0,0 +1,23 @@
import { NightwatchBrowser } from 'nightwatch'
const EventEmitter = require('events')
export 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()
})
}

@ -0,0 +1,16 @@
import { NightwatchBrowser } from 'nightwatch'
const EventEmitter = require('events')
export 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
}
}

@ -0,0 +1,22 @@
import { NightwatchBrowser } from 'nightwatch'
const EventEmitter = require('events')
export 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.querySelector('#modal-footer-ok')
elem.editor.session.setValue(value)
}, [value], () => {
done()
if (callback) {
callback.call(this.api)
}
this.emit('complete')
})
})
return this
}
}

@ -6,9 +6,9 @@ const EventEmitter = require('events')
Switches between browser tabs
*/
class SwitchBrowserTab extends EventEmitter {
command (this: NightwatchBrowser, index: number) {
this.api.perform((browser: NightwatchBrowser, done: VoidFunction) => {
export class SwitchBrowserTab extends EventEmitter {
command (this: NightwatchBrowser, index: number): NightwatchBrowser {
this.api.perform((browser: NightwatchBrowser, done) => {
browser.windowHandles((result) => {
browser.switchWindow(result.value[index])
done()
@ -18,5 +18,3 @@ class SwitchBrowserTab extends EventEmitter {
return this
}
}
module.exports = SwitchBrowserTab

@ -0,0 +1,26 @@
import { NightwatchBrowser, NightwatchContractContent } from 'nightwatch'
const EventEmitter = require('events')
export class TestContracts extends EventEmitter {
command (this: NightwatchBrowser,fileName: string, contractCode: NightwatchContractContent, compiledContractNames: string[]): NightwatchBrowser {
this.api.perform((done: VoidFunction) => {
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()
})
}

@ -0,0 +1,60 @@
import { NightwatchBrowser, NightwatchTestFunctionExpectedInput } from 'nightwatch'
import deepequal from 'deep-equal'
const EventEmitter = require('events')
export 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
}
}

@ -0,0 +1,70 @@
import { NightwatchBrowser, NightwatchVerifyContractOpts, NightwatchCallbackResult } from 'nightwatch'
const EventEmitter = require('events')
export class VerifyContracts extends EventEmitter {
command (this: NightwatchBrowser,compiledContractNames: string[], opts = { wait: 1000, version: null }): NightwatchBrowser {
this.api.perform((done: VoidFunction) => {
verifyContracts(this.api, compiledContractNames, opts, () => {
done()
this.emit('complete')
})
})
return this
}
}
function getCompiledContracts (browser: NightwatchBrowser, opts: NightwatchVerifyContractOpts, callback: CallableFunction) {
browser
.clickLaunchIcon('solidity')
.pause(opts.wait)
.waitForElementPresent('*[data-id="compiledContracts"] option')
.perform((done: VoidFunction) => {
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: NightwatchVerifyContractOpts, 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()
})
}

@ -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 }
}

@ -7,17 +7,14 @@ export default function (browser: NightwatchBrowser, callback: VoidFunction, url
.url(url || 'http://127.0.0.1:8080')
.pause(5000)
.switchBrowserTab(0)
.injectScript('test-browser/helpers/applytestmode.js', function () {
browser.fullscreenWindow(() => {
if (preloadPlugins) {
console.log('preloadPlugins: ', preloadPlugins)
initModules(browser, () => {
browser.clickLaunchIcon('solidity')
.waitForElementPresent('[for="autoCompile"]')
.click('[for="autoCompile"]')
})
}
})
.fullscreenWindow(() => {
if (preloadPlugins) {
initModules(browser, () => {
browser.clickLaunchIcon('solidity')
.waitForElementPresent('[for="autoCompile"]')
.click('[for="autoCompile"]')
})
}
})
.perform(() => {
callback()

@ -3,8 +3,7 @@
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
import sauce from './sauce'
const examples = require('../../../../../apps/remix-ide/src/app/editor/example-contracts') // reference example-contracts from inside dist directory
import examples from '../examples/example-contracts'
const sources = [
{'browser/Untitled.sol': { content: examples.ballot.content }}

@ -1,17 +1,44 @@
// Merge custom command types with nightwatch types
import { NightwatchBrowser, NightwatchAPI, NightwatchBrowser, NightwatchBrowser } from "nightwatch";
import { NightwatchBrowser, NightwatchBrowser, NightwatchBrowser } from "nightwatch";
declare module "nightwatch" {
export interface NightwatchCustomCommands {
clickLaunchIcon(this: NightwatchBrowser, icon: string): NightwatchBrowser,
switchBrowserTab(this: NightwatchBrowser, index: number): NightwatchBrowser
switchBrowserTab(this: NightwatchBrowser, index: number): NightwatchBrowser,
scrollAndClick(this: NightwatchBrowser, target: string): NightwatchBrowser,
scrollInto(this: NightwatchBrowser, target: string): NightwatchBrowser,
testContracts(this: NightwatchBrowser, fileName: string, contractCode: ContractContent, compiledContractNames: string[]): NightwatchBrowser,
setEditorValue(this: NightwatchBrowser, value: string, callback?: () => void): NightwatchBrowser,
addFile(this: NightwatchBrowser, name: string, content: NightwatchContractContent): NightwatchBrowser,
verifyContracts(this: NightwatchBrowser, compiledContractNames: string[]): NightwatchBrowser,
selectAccount(this: NightwatchBrowser, account?: string): NightwatchBrowser,
clickFunction(this: NightwatchBrowser, fnFullName: string, expectedInput?: NightwatchClickFunctionExpectedInput): NightwatchBrowser,
testFunction(this: NightwatchBrowser, txHash: string, expectedInput: NightwatchTestFunctionExpectedInput): NightwatchBrowser,
goToVMTraceStep(this: NightwatchBrowser, step: number, incr?: number): NightwatchBrowser
}
export interface NightwatchBrowser {
api: NightwatchAPI,
api: this,
emit: (status: string) => void,
fullscreenWindow: (result?: any) => this,
injectScript: (scriptUrl: string, callback?: VoidFunction) => this
fullscreenWindow: (result?: any) => this
}
export interface NightwatchContractContent {
content: string;
}
export interface NightwatchVerifyContractOpts {
wait: number,
version?: string
}
export interface NightwatchClickFunctionExpectedInput {
types: string,
values: string
}
export interface NightwatchTestFunctionExpectedInput {
[key: string]: any
}
}
Loading…
Cancel
Save