diff --git a/Jenkinsfile b/Jenkinsfile index 4786858a8..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,24 +50,33 @@ pipeline { # Add coveralls config file cp ~/.coveralls.yml-nano .coveralls.yml - # Prepare lisk core for testing - bash ~/tx.sh + # Run Build + npm run build # 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 + export NODE_ENV= + npm run dev &> .lisk-nano.log & + sleep 20 + + # End to End test configuration + 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 - # Commented until e2e is ready - # cat .protractor.log - # cat .lisk-nano.log + # Run End to End Tests + npm run e2e-test - pkill -f app.js -9 || true + 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 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..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(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, + publicKey, + delegates: voteList.map(delegate => `+${delegate.publicKey}`).concat( + unvoteList.map(delegate => `-${delegate.publicKey}`), ), + secondSecret, }); }, diff --git a/src/spec/spec.js b/src/spec/spec.js index af687105f..7be1d1c2a 100644 --- a/src/spec/spec.js +++ b/src/spec/spec.js @@ -31,19 +31,25 @@ 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(); } +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('send .md-input-message-animation', message); + waitForElemAndCheckItsText('transfer .md-input-message-animation', message); } function launchApp() { @@ -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(); } @@ -65,10 +71,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 +82,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(); } @@ -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); @@ -187,18 +191,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 +271,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 +279,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 +292,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 +303,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 +321,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 +331,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(by.css('md-autocomplete-wrap input')).sendKeys('genesis_7'); + element.all(by.css('delegates md-card-title button.vote-button')).last().click(); + 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'); - // 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 +346,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 +405,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 +422,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); }); });