From f19535b31213a25640dda2ec523f0c544b7b9821 Mon Sep 17 00:00:00 2001 From: Vit Stanislav Date: Thu, 11 May 2017 10:03:18 +0200 Subject: [PATCH 1/6] Resolve FIXME comments in e2e tests #203 --- .../delegateRegistration.js | 2 +- src/app/components/delegates/delegates.pug | 4 +- src/app/components/delegates/vote.pug | 9 ++- src/app/components/forging/forging.js | 2 +- src/app/services/api/delegateApi.js | 12 ++-- src/spec/spec.js | 70 ++++++++----------- 6 files changed, 42 insertions(+), 57 deletions(-) diff --git a/src/app/components/delegate-registration/delegateRegistration.js b/src/app/components/delegate-registration/delegateRegistration.js index b4d377de1..d38725928 100644 --- a/src/app/components/delegate-registration/delegateRegistration.js +++ b/src/app/components/delegate-registration/delegateRegistration.js @@ -15,7 +15,7 @@ app.directive('delegateRegistration', ($mdDialog, delegateService, Account, dial ) .then(() => { dialog.successAlert({ - title: 'Congratulations!', + title: 'Success', text: 'Account was successfully registered as delegate.', }) .then(() => { diff --git a/src/app/components/delegates/delegates.pug b/src/app/components/delegates/delegates.pug index 475d9e27a..cec51219b 100644 --- a/src/app/components/delegates/delegates.pug +++ b/src/app/components/delegates/delegates.pug @@ -38,7 +38,7 @@ div.offline-hide md-button.md-icon-button.lsk-vote-remove-button(ng-click='$ctrl.unselect(username)') i.material-icons close span.pull-right.right-action-buttons - md-button(ng-click='$ctrl.openVoteDialog()') + md-button.vote-button(ng-click='$ctrl.openVoteDialog()') i.material-icons done span Vote span(ng-if='$ctrl.voteList.length || $ctrl.unvoteList.length') @@ -59,7 +59,7 @@ div.offline-hide th(md-column) Uptime th(md-column) Approval tbody(md-body, infinite-scroll='$ctrl.showMore()', infinite-scroll-distance='1') - tr(md-row, ng-hide='$ctrl.filteredDelegates.length || $ctrl.loading') + tr(md-row, ng-if='!$ctrl.filteredDelegates.length && !$ctrl.loading') td(md-cell, colspan='6') No delegates found tr(md-row, ng-repeat="delegate in ($ctrl.filteredDelegates = ($ctrl.delegates | filter : {username: search} )) | limitTo : $ctrl.delegatesDisplayedCount", ng-class='{"downvote": delegate.status.voted && !delegate.status.selected, "upvote": !delegate.status.voted && delegate.status.selected, "pending": delegate.status.pending}') td(md-cell) diff --git a/src/app/components/delegates/vote.pug b/src/app/components/delegates/vote.pug index 2152f2210..257804018 100644 --- a/src/app/components/delegates/vote.pug +++ b/src/app/components/delegates/vote.pug @@ -18,6 +18,9 @@ div.dialog-vote(aria-label='Vote for delegates') md-chip-template {{$chip.username}} md-autocomplete(flex, required, md-input-minlength='2', md-no-cache='false', md-selected-item='$ctrl.selectedUnvoteDelegate', md-search-text='$ctrl.unvoteSearchText', md-items='delegate in $ctrl.delegateService.unvoteAutocomplete($ctrl.unvoteSearchText, $ctrl.votedList)', md-item-text='delegate.username', md-require-match, placeholder='Search by username') span(md-highlight-text='$ctrl.unvoteSearchText') {{delegate.username}} + md-input-container.md-block(ng-if='$ctrl.account.get().secondSignature') + label Second Passphrase + input(type='password', ng-model='$ctrl.secondPassphrase') p.pull-right Fee: 1 LSK md-divider div(layout='row') @@ -28,11 +31,7 @@ div.dialog-vote(aria-label='Vote for delegates') br span You can vote for up to 101 delegates in total. md-divider - md-input-container.md-block(ng-if='$ctrl.account.get().secondSignature') - label Second Passphrase - input(type='password', ng-model='$ctrl.secondPassphrase') - md-divider md-dialog-actions(layout='row') md-button(ng-click="$ctrl.$mdDialog.cancel()") Cancel span(flex) - md-button(ng-disabled='!$ctrl.canVote()', ng-click="$ctrl.vote()") {{$ctrl.votingInProgress ? 'Voting...' : 'Confirm vote'}} + md-button.md-primary(ng-disabled='!$ctrl.canVote()', ng-click="$ctrl.vote()") {{$ctrl.votingInProgress ? 'Voting...' : 'Confirm vote'}} diff --git a/src/app/components/forging/forging.js b/src/app/components/forging/forging.js index ed4b5a5cd..ebbf2a489 100644 --- a/src/app/components/forging/forging.js +++ b/src/app/components/forging/forging.js @@ -54,7 +54,7 @@ app.component('forging', { this.blocks = data.blocks; } else if (offset) { Array.prototype.push.apply(this.blocks, data.blocks); - } else if (this.blocks[0].id !== data.blocks[0].id) { + } else if (this.blocks[0] && data.blocks[0] && this.blocks[0].id !== data.blocks[0].id) { Array.prototype.unshift.apply(this.blocks, data.blocks.filter(block => block.timestamp > this.blocks[0].timestamp)); } diff --git a/src/app/services/api/delegateApi.js b/src/app/services/api/delegateApi.js index 44bdf4843..a58bd5bed 100644 --- a/src/app/services/api/delegateApi.js +++ b/src/app/services/api/delegateApi.js @@ -11,14 +11,14 @@ app.factory('delegateService', Peers => ({ return Peers.sendRequestPromise('delegates/get', options); }, - vote(options) { + vote({secret, publicKey, voteList, unvoteList, secondSecret = null}) { return Peers.sendRequestPromise('accounts/delegates', { - secret: options.secret, - publicKey: options.publicKey, - secondSecret: options.secondSecret, - delegates: options.voteList.map(delegate => `+${delegate.publicKey}`).concat( - options.unvoteList.map(delegate => `-${delegate.publicKey}`), + secret: secret, + publicKey: publicKey, + delegates: voteList.map(delegate => `+${delegate.publicKey}`).concat( + unvoteList.map(delegate => `-${delegate.publicKey}`), ), + secondSecret: secondSecret, }); }, diff --git a/src/spec/spec.js b/src/spec/spec.js index af687105f..3ba46b55b 100644 --- a/src/spec/spec.js +++ b/src/spec/spec.js @@ -43,7 +43,7 @@ function waitForElemAndClickIt(selector) { } function checkErrorMessage(message) { - waitForElemAndCheckItsText('send .md-input-message-animation', message); + waitForElemAndCheckItsText('transfer .md-input-message-animation', message); } function launchApp() { @@ -65,10 +65,10 @@ function logout() { logoutButton.click(); } -function send(fromAccount, toAddress, amount) { +function transfer(fromAccount, toAddress, amount) { login(fromAccount); - const sendElem = element(by.css('send')); - const sendModalButton = element(by.css('md-content.header button.send')); + const sendElem = element(by.css('transfer')); + const sendModalButton = element(by.css('md-content.header button.transfer')); browser.wait(EC.presenceOf(sendModalButton), waitTime); sendModalButton.click(); @@ -76,10 +76,10 @@ function send(fromAccount, toAddress, amount) { // wait for modal animation to finish browser.sleep(1000); - element(by.css('send input[name="recipient"]')).sendKeys(toAddress); - element(by.css('send input[name="amount"]')).sendKeys(`${amount}`); - element(by.css('send input[name="recipient"]')).click(); - const sendButton = element.all(by.css('send button.md-primary')).get(0); + element(by.css('transfer input[name="recipient"]')).sendKeys(toAddress); + element(by.css('transfer input[name="amount"]')).sendKeys(`${amount}`); + element(by.css('transfer input[name="recipient"]')).click(); + const sendButton = element.all(by.css('transfer button.md-primary')).get(0); // browser.wait(EC.presenceOf(sendButton), waitTime); sendButton.click(); } @@ -187,18 +187,18 @@ function testShowBalance() { function testSend() { const amount = 1.1; - send(masterAccount, delegateAccount.address, amount); + transfer(masterAccount, delegateAccount.address, amount); browser.sleep(1000); - checkAlertDialog('Success', `${amount} sent to ${delegateAccount.address}`); + checkAlertDialog('Success', `${amount} LSK was successfully transferred to ${delegateAccount.address}`); } function testSendWithNotEnoughFunds() { - send(emptyAccount, delegateAccount.address, 10000); + transfer(emptyAccount, delegateAccount.address, 10000); checkErrorMessage('Insufficient funds'); } function testSendWithInvalidAddress() { - send(masterAccount, emptyAccount.address.substr(0, 10), 1); + transfer(masterAccount, emptyAccount.address.substr(0, 10), 1); checkErrorMessage('Invalid'); } @@ -267,8 +267,7 @@ function testDelegateRegistration() { waitForElemAndClickIt('md-dialog button.md-primary'); browser.sleep(500); - // FIXME: the title should really be "Success", not "Congratulations!" to be consistent - checkAlertDialog('Congratulations!', 'Account was successfully registered as delegate.'); + checkAlertDialog('Success', 'Account was successfully registered as delegate.'); } function testForgingCenter() { @@ -276,6 +275,7 @@ function testForgingCenter() { waitForElemAndClickIt('main md-tab-item:nth-child(3)'); // FIXME: there is some bug in forging center that makes it really slow to load + // should be fixed by @alihaghighatkhah in #174 browser.sleep(5000); waitForElemAndCheckItsText('forging md-card .title', delegateAccount.username); @@ -288,9 +288,7 @@ function testViewDelegates() { waitForElemAndCheckItsText('delegates table thead tr th:nth-child(1)', 'Vote'); waitForElemAndCheckItsText('delegates table tbody tr td:nth-child(2)', '1'); - // FIXME: there are 20 delegates displayed, so this should be toEqual(20) - // but we have to use ng-if instead of ng-hide for tr with "No delegates found" message - expect(element.all(by.css('delegates table tbody tr')).count()).toEqual(21); + expect(element.all(by.css('delegates table tbody tr')).count()).toEqual(20); } function testSearchDelegates() { @@ -301,9 +299,7 @@ function testSearchDelegates() { browser.sleep(500); waitForElemAndCheckItsText('delegates table tbody tr td:nth-child(3)', delegateAccount.username); - // FIXME: there should be 1 delegate displayed, so this should be toEqual(1) - // but we have to use ng-if instead of ng-hide for tr with "No delegates found" message - expect(element.all(by.css('delegates table tbody tr')).count()).toEqual(2); + expect(element.all(by.css('delegates table tbody tr')).count()).toEqual(1); } function testViewVotes() { @@ -321,10 +317,8 @@ function testVoteFromTable() { waitForElemAndClickIt('delegates tr:nth-child(3) md-checkbox'); waitForElemAndClickIt('delegates tr:nth-child(5) md-checkbox'); waitForElemAndClickIt('delegates tr:nth-child(8) md-checkbox'); - // FIXME: add 'vote-button' class the "Vote" button and use it here - element.all(by.css('delegates md-card-title button')).last().click(); - // FIXME: add 'md-primary' class the "Confirm vote" button and use it here - waitForElemAndClickIt('vote md-dialog-actions button[ng-disabled]'); + element.all(by.css('delegates md-card-title button.vote-button')).last().click(); + waitForElemAndClickIt('vote md-dialog-actions button.md-primary'); waitForElemAndCheckItsText('md-toast', 'Voting successful'); } @@ -333,14 +327,12 @@ function testVoteFromDialog() { waitForElemAndClickIt('main md-tab-item:nth-child(2)'); waitForElemAndClickIt('delegates tr:nth-child(3) md-checkbox'); waitForElemAndClickIt('delegates tr:nth-child(3) md-checkbox'); - // FIXME: add 'vote-button' class the "Vote" button and use it here - element.all(by.css('delegates md-card-title button')).last().click(); + element.all(by.css('delegates md-card-title button.vote-button')).last().click(); element(by.css('md-autocomplete-wrap input')).sendKeys('genesis_7'); waitForElemAndClickIt('md-autocomplete-parent-scope'); element(by.css('md-autocomplete-wrap input')).sendKeys('genesis_7'); waitForElemAndClickIt('md-autocomplete-parent-scope'); - // FIXME: add 'md-primary' class the "Confirm vote" button and use it here - waitForElemAndClickIt('vote md-dialog-actions button[ng-disabled]'); + waitForElemAndClickIt('vote md-dialog-actions button.md-primary'); waitForElemAndCheckItsText('md-toast', 'Voting successful'); } @@ -350,10 +342,8 @@ function testUnvote() { waitForElemAndClickIt('delegates tr:nth-child(3) md-checkbox'); waitForElemAndClickIt('delegates tr:nth-child(5) md-checkbox'); waitForElemAndClickIt('delegates tr:nth-child(8) md-checkbox'); - // FIXME: add 'vote-button' class the "Vote" button and use it here - element.all(by.css('delegates md-card-title button')).last().click(); - // FIXME: add 'md-primary' class the "Confirm vote" button and use it here - waitForElemAndClickIt('vote md-dialog-actions button[ng-disabled]'); + element.all(by.css('delegates md-card-title button.vote-button')).last().click(); + waitForElemAndClickIt('vote md-dialog-actions button.md-primary'); waitForElemAndCheckItsText('md-toast', 'Voting successful'); } @@ -411,10 +401,9 @@ describe('Lisk Nano', () => { }); describe('Send dialog', () => { - it('should allow to send transaction when enough funds and correct address form', testSend); - // FIXME: there is currently a bug - #194 Maximum amount validation doesn't work - xit('should not allow to send transaction when not enough funds', testSendWithNotEnoughFunds); - it('should not allow to send transaction when invalid address', testSendWithInvalidAddress); + it('should allow to do a transfer when enough funds and correct address form', testSend); + it('should not allow to do a transfer when not enough funds', testSendWithNotEnoughFunds); + it('should not allow to do a transfer when invalid address', testSendWithInvalidAddress); }); describe('Transactions tab', () => { @@ -429,11 +418,8 @@ describe('Lisk Nano', () => { it('should allow to view delegates', testViewDelegates); it('should allow to search delegates', testSearchDelegates); it('should allow to view my votes', testViewVotes); - // FIXME: voting is broken, because it sends secondPassphrase = undefined - xit('should allow to select delegates in the "Voting" tab and vote for them', testVoteFromTable); - // FIXME: voting is broken, because it sends secondPassphrase = undefined - xit('should allow to select delegates in the "Vote" dialog and vote for them', testVoteFromDialog); - // FIXME: voting is broken, because it sends secondPassphrase = undefined - xit('should allow to remove votes form delegates', testUnvote); + it('should allow to select delegates in the "Voting" tab and vote for them', testVoteFromTable); + it('should allow to select delegates in the "Vote" dialog and vote for them', testVoteFromDialog); + it('should allow to remove votes form delegates', testUnvote); }); }); From 8a80d2463500d95adc6801ef84f0ac42f0814fb1 Mon Sep 17 00:00:00 2001 From: Vit Stanislav Date: Thu, 11 May 2017 11:28:44 +0200 Subject: [PATCH 2/6] Fix all warnings in e2e tests --- src/spec/spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/spec/spec.js b/src/spec/spec.js index 3ba46b55b..92100d2f6 100644 --- a/src/spec/spec.js +++ b/src/spec/spec.js @@ -31,13 +31,13 @@ const EC = protractor.ExpectedConditions; const waitTime = 5000; function waitForElemAndCheckItsText(selector, text) { - const elem = element(by.css(selector)); + const elem = element.all(by.css(selector)).get(0); browser.wait(EC.presenceOf(elem), waitTime, `waiting for element '${selector}'`); expect(elem.getText()).toEqual(text, `inside element "${selector}"`); } function waitForElemAndClickIt(selector) { - const elem = element(by.css(selector)); + const elem = element.all(by.css(selector)).get(0); browser.wait(EC.presenceOf(elem), waitTime, `waiting for element '${selector}'`); elem.click(); } @@ -328,9 +328,9 @@ function testVoteFromDialog() { waitForElemAndClickIt('delegates tr:nth-child(3) md-checkbox'); waitForElemAndClickIt('delegates tr:nth-child(3) md-checkbox'); element.all(by.css('delegates md-card-title button.vote-button')).last().click(); - element(by.css('md-autocomplete-wrap input')).sendKeys('genesis_7'); + element.all(by.css('md-autocomplete-wrap input')).get(0).sendKeys('genesis_7'); waitForElemAndClickIt('md-autocomplete-parent-scope'); - element(by.css('md-autocomplete-wrap input')).sendKeys('genesis_7'); + element.all(by.css('md-autocomplete-wrap input')).get(0).sendKeys('genesis_7'); waitForElemAndClickIt('md-autocomplete-parent-scope'); waitForElemAndClickIt('vote md-dialog-actions button.md-primary'); waitForElemAndCheckItsText('md-toast', 'Voting successful'); From 416c3628b18911d0f992e51c259d6aa74c0e6882 Mon Sep 17 00:00:00 2001 From: Vit Stanislav Date: Thu, 11 May 2017 11:58:30 +0200 Subject: [PATCH 3/6] Fix eslint violations --- src/app/services/api/delegateApi.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/services/api/delegateApi.js b/src/app/services/api/delegateApi.js index a58bd5bed..1aafa547d 100644 --- a/src/app/services/api/delegateApi.js +++ b/src/app/services/api/delegateApi.js @@ -11,14 +11,14 @@ app.factory('delegateService', Peers => ({ return Peers.sendRequestPromise('delegates/get', options); }, - vote({secret, publicKey, voteList, unvoteList, secondSecret = null}) { + vote({ secret, publicKey, voteList, unvoteList, secondSecret = null }) { return Peers.sendRequestPromise('accounts/delegates', { - secret: secret, - publicKey: publicKey, + secret, + publicKey, delegates: voteList.map(delegate => `+${delegate.publicKey}`).concat( unvoteList.map(delegate => `-${delegate.publicKey}`), ), - secondSecret: secondSecret, + secondSecret, }); }, From 5f3c1da5b6154dc33897dd8bca56d4f46e3bb70b Mon Sep 17 00:00:00 2001 From: isabello Date: Thu, 11 May 2017 12:26:12 +0200 Subject: [PATCH 4/6] Enable e2e tests in Jenkins --- Jenkinsfile | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4786858a8..dd3c90361 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -49,24 +49,30 @@ pipeline { # Add coveralls config file cp ~/.coveralls.yml-nano .coveralls.yml - # Prepare lisk core for testing - bash ~/tx.sh - # Run test npm run test - # Commented until e2e is ready - # export CHROME_BIN=chromium-browser - # export DISPLAY=:0.0 - # Xvfb :0 -ac -screen 0 1280x1024x24 & - # ./node_modules/protractor/bin/webdriver-manager update - # npm run e2e-test + # Run Dev build and Build + npm run dev &> .lisk-nano.log & + sleep 30 - # Commented until e2e is ready - # cat .protractor.log - # cat .lisk-nano.log + # End to End test configuration + export CHROME_BIN=chromium-browser + export DISPLAY=:99 + Xvfb :99 -ac -screen 0 1024x768x24 & + ./node_modules/protractor/bin/webdriver-manager update + ./node_modules/protractor/bin/webdriver-manager start & + + # Prepare lisk core for testing + bash ~/tx.sh - pkill -f app.js -9 || true + # Run End to End Tests + npm run e2e-test + + pkill -f selenium -9 || true + pkill -f Xvfb -9 || true + rm -rf /tmp/.X0-lock || true + pkill -f app.js || true pkill -f webpack-dev-server -9 || true ''' milestone 1 From a8c1a29257a2b18e1bcfe2142b8b63f59edc1430 Mon Sep 17 00:00:00 2001 From: Vit Stanislav Date: Thu, 11 May 2017 12:32:46 +0200 Subject: [PATCH 5/6] Ensure that protractor waits for the first element to interact with --- src/spec/spec.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/spec/spec.js b/src/spec/spec.js index 92100d2f6..7be1d1c2a 100644 --- a/src/spec/spec.js +++ b/src/spec/spec.js @@ -42,6 +42,12 @@ function waitForElemAndClickIt(selector) { elem.click(); } +function waitForElemAndSendKeys(selector, keys) { + const elem = element.all(by.css(selector)).get(0); + browser.wait(EC.presenceOf(elem), waitTime, `waiting for element '${selector}'`); + elem.sendKeys(keys); +} + function checkErrorMessage(message) { waitForElemAndCheckItsText('transfer .md-input-message-animation', message); } @@ -55,7 +61,7 @@ function launchApp() { function login(account) { launchApp(); - element(by.css('input[type="password"]')).sendKeys(account.passphrase); + waitForElemAndSendKeys('input[type="password"]', account.passphrase); element(by.css('.md-button.md-primary.md-raised')).click(); } @@ -147,7 +153,7 @@ function doPassphraseGenerationProcedure(callback) { function testNewAccount() { launchApp(); - element.all(by.css('.md-button.md-primary')).get(0).click(); + waitForElemAndClickIt('.md-button.md-primary'); doPassphraseGenerationProcedure(checkIsLoggedIn); } @@ -159,16 +165,14 @@ function testAddress() { function testPeer() { launchApp(); login(masterAccount); - expect(element.all(by.css('.peer .md-title')).get(0).getText()).toEqual('Peer'); - expect(element.all(by.css('.peer .value')).get(0).getText()).toEqual('localhost:4000'); + waitForElemAndCheckItsText('.peer .md-title', 'Peer'); + waitForElemAndCheckItsText('.peer .value', 'localhost:4000'); } function testChangeNetwork() { launchApp(); - const peerElem = element(by.css('form md-select')); - browser.wait(EC.presenceOf(peerElem), waitTime); - peerElem.click(); + waitForElemAndClickIt('form md-select'); const optionElem = element.all(by.css('md-select-menu md-option')).get(1); browser.wait(EC.presenceOf(optionElem), waitTime); From cec6d3688e73453f25bada103b3daca4ced4aa55 Mon Sep 17 00:00:00 2001 From: isabello Date: Fri, 12 May 2017 12:35:24 +0200 Subject: [PATCH 6/6] Disable NODE_ENV variable before npm run dev --- Jenkinsfile | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index dd3c90361..7ab5f50e1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,6 +8,7 @@ pipeline { steps { node('master-nano-01'){ lock(resource: "master-nano-01", inversePrecedence: true) { + deleteDir() checkout scm sh '''#!/bin/bash env @@ -27,7 +28,7 @@ pipeline { dropdb lisk_test || true createdb lisk_test psql -d lisk_test -c "alter user "$USER" with password 'password';" - cp /var/lib/jenkins/workspace/lisk-node-Linux-x86_64.tßar.gz . + cp /var/lib/jenkins/workspace/lisk-node-Linux-x86_64.tar.gz . tar -zxvf lisk-node-Linux-x86_64.tar.gz npm install git submodule init @@ -49,15 +50,18 @@ pipeline { # Add coveralls config file cp ~/.coveralls.yml-nano .coveralls.yml + # Run Build + npm run build + # Run test npm run test # Run Dev build and Build + export NODE_ENV= npm run dev &> .lisk-nano.log & - sleep 30 + sleep 20 # End to End test configuration - export CHROME_BIN=chromium-browser export DISPLAY=:99 Xvfb :99 -ac -screen 0 1024x768x24 & ./node_modules/protractor/bin/webdriver-manager update