Skip to content
This repository has been archived by the owner on Apr 15, 2019. It is now read-only.

Commit

Permalink
Merge pull request #966 from LiskHQ/573-multi-account-management
Browse files Browse the repository at this point in the history
Allow to save and manage multiple accounts in local storage - Closes #573
  • Loading branch information
slaweet committed Nov 13, 2017
2 parents be3285f + 1f7128b commit bb863f4
Show file tree
Hide file tree
Showing 35 changed files with 726 additions and 437 deletions.
49 changes: 0 additions & 49 deletions features/accountManagement.feature

This file was deleted.

82 changes: 82 additions & 0 deletions features/savedAccounts.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
Feature: Saved Accounts
Scenario: should allow to save account locally, after page reload it should require passphrase to do the first transaction, and remember the passphrase for next transactions
Given I'm logged in as "genesis"
When I click "saved accounts" in main menu
And I click "add active account button"
And I click "x button"
And I wait 1 seconds
And I should see text "Account saved" in "toast" element
And I refresh the page
And I wait 2 seconds
Then I should be logged in
And I click "send button"
And I should see empty "passphrase" field
And I fill in "1" to "amount" field
And I fill in "537318935439898807L" to "recipient" field
And I fill in passphrase of "genesis" to "passphrase" field
And I click "submit button"
And I click "ok button"
And I wait 1 seconds
And I click "send button"
And I fill in "2" to "amount" field
And I fill in "537318935439898807L" to "recipient" field
And I click "submit button"
And I should see alert dialog with title "Success" and text "Your transaction of 2 LSK to 537318935439898807L was accepted and will be processed in a few seconds."

Scenario: should allow to save second account
Given I'm logged in as "genesis"
When I click "saved accounts" in main menu
And I click "add active account button"
And I click "x button"
And I wait 1 seconds
And I click "logout button"
And I'm logged in as "empty account"
And I click "saved accounts" in main menu
And I click "add active account button"
Then I should see "saved accounts table" table with 2 lines
And I refresh the page
And I wait 2 seconds
And I should be logged in as "empty account" account

Scenario: should allow to forget second account
Given I'm logged in as "genesis"
When I click "saved accounts" in main menu
And I click "add active account button"
And I click "x button"
And I wait 1 seconds
And I click "logout button"
And I'm logged in as "delegate"
And I click "saved accounts" in main menu
And I click "add active account button"
And I should see "saved accounts table" table with 2 lines
And I click "forget button"
Then I should see "saved accounts table" table with 1 lines

@pending
Scenario: should allow to switch account
Given I'm logged in as "genesis"
When I click "saved accounts" in main menu
And I click "add active account button"
And I click "x button"
And I wait 1 seconds
And I click "logout button"
And I'm logged in as "delegate"
And I click "saved accounts" in main menu
And I click "add active account button"
And I click "switch button"
And I wait 1 seconds
And I should be logged in as "genesis" account

Scenario: should login to last active saved account
Given I'm logged in as "genesis"
When I click "saved accounts" in main menu
And I click "add active account button"
And I click "x button"
And I wait 1 seconds
And I click "logout button"
And I'm logged in as "empty account"
And I click "saved accounts" in main menu
And I click "add active account button"
And I click "x button"
And I refresh the page
And I should be logged in as "empty account" account
16 changes: 15 additions & 1 deletion features/step_definitions/generic.step.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ defineSupportCode(({ Given, When, Then, setDefaultTimeout }) => {
.and.notify(callback);
});

Then('I should see "{elementName}" table with {lineCount} lines', (elementName, lineCount, callback) => {
browser.sleep(500);
expect(element.all(by.css(`table.${elementName.replace(/ /g, '-')} tbody tr`)).count()).to.eventually.equal(parseInt(lineCount, 10))
.and.notify(callback);
});


Then('I should see no "{elementName}"', (elementName, callback) => {
const selector = `.${elementName.replace(/ /g, '-')}`;
waitForElemRemoved(selector).then(() => {
Expand Down Expand Up @@ -139,7 +146,6 @@ defineSupportCode(({ Given, When, Then, setDefaultTimeout }) => {
});

Given('I\'m logged in as "{accountName}"', { timeout: 2 * defaultTimeout }, (accountName, callback) => {
browser.get(browser.params.baseURL);
const passphrase = browser.params.useTestnetPassphrase
? browser.params.testnetPassphrase
: accounts[accountName].passphrase;
Expand Down Expand Up @@ -200,6 +206,14 @@ defineSupportCode(({ Given, When, Then, setDefaultTimeout }) => {
browser.executeScript('window.scrollBy(0, 10000);');
});

Then('I should be logged in', (callback) => {
waitForElemAndCheckItsText('.logout-button', 'LOGOUT', callback);
});

Then('I should be logged in as "{accountName}" account', (accountName, callback) => {
waitForElemAndCheckItsText('.full.address', accounts[accountName].address, callback);
});

When('I click "{itemSelector}" in main menu', (itemSelector, callback) => {
waitForElemAndClickIt('.main-menu-icon-button');
browser.sleep(1000);
Expand Down
1 change: 1 addition & 0 deletions features/step_definitions/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ defineSupportCode(({ Before, After }) => {
localStorage.clear();
localStorage.setItem('address', browser.params.liskCoreURL);
localStorage.setItem('network', networks[browser.params.network].code);
browser.get(browser.params.baseURL);
callback();
});

Expand Down
7 changes: 1 addition & 6 deletions features/step_definitions/login.step.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
/* eslint-disable import/no-extraneous-dependencies */
const { defineSupportCode } = require('cucumber');
const { waitForElemAndCheckItsText } = require('../support/util.js');

defineSupportCode(({ Given, Then }) => {
defineSupportCode(({ Given }) => {
Given('I\'m on login page', (callback) => {
browser.get(browser.params.baseURL).then(callback);
});

Then('I should be logged in', (callback) => {
waitForElemAndCheckItsText('.logout-button', 'LOGOUT', callback);
});
});

9 changes: 6 additions & 3 deletions i18n/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"About": "About",
"Account saved": "Account saved",
"Account was successfully forgotten.": "Account was successfully forgotten.",
"Add active account": "Add active account",
"Add vote to": "Add vote to",
"Address": "Address",
"Address copied to clipboard": "Address copied to clipboard",
Expand All @@ -19,6 +20,7 @@
"Blockchain Application Registration": "Blockchain Application Registration",
"Cancel": "Cancel",
"Click to send all funds": "Click to send all funds",
"Close": "Close",
"Confirm": "Confirm",
"Connection re-established": "Connection re-established",
"Copy": "Copy",
Expand All @@ -42,7 +44,7 @@
"Fee": "Fee",
"Fee: {{amount}} LSK": "Fee: {{amount}} LSK",
"Fee: {{fee}} LSK": "Fee: {{fee}} LSK",
"Forget this account": "Forget this account",
"Forget": "Forget",
"Forging": "Forging",
"From / To": "From / To",
"Help": "Help",
Expand Down Expand Up @@ -73,6 +75,7 @@
"Move your mouse to generate random bytes": "Move your mouse to generate random bytes",
"Multisignature Creation": "Multisignature Creation",
"Name": "Name",
"Network": "Network",
"New Account": "New Account",
"Next": "Next",
"No delegates found": "No delegates found",
Expand Down Expand Up @@ -112,16 +115,15 @@
"Register as delegate": "Register as delegate",
"Register second passphrase": "Register second passphrase",
"Reload": "Reload",
"Remember this account": "Remember this account",
"Remove vote from": "Remove vote from",
"Repeat the transaction": "Repeat the transaction",
"Report Issue...": "Report Issue...",
"Required": "Required",
"Result": "Result",
"Result copied to clipboard": "Result copied to clipboard",
"Reward": "Reward",
"Save account": "Save account",
"Save your passphrase in a safe place": "Save your passphrase in a safe place",
"Saved accounts": "Saved accounts",
"Search": "Search",
"Search by username": "Search by username",
"Second Passphrase": "Second Passphrase",
Expand All @@ -143,6 +145,7 @@
"Signing a message with this tool indicates ownership of a privateKey (secret) and provides a level of proof that you are the owner of the key. Its important to bear in mind that this is not a 100% proof as computer systems can be compromised, but is still an effective tool for proving ownership of a particular publicKey/address pair.": "Signing a message with this tool indicates ownership of a privateKey (secret) and provides a level of proof that you are the owner of the key. Its important to bear in mind that this is not a 100% proof as computer systems can be compromised, but is still an effective tool for proving ownership of a particular publicKey/address pair.",
"Submit": "Submit",
"Success": "Success",
"Switch": "Switch",
"Testnet": "Testnet",
"The URL was invalid": "The URL was invalid",
"There are no transactions, yet.": "There are no transactions, yet.",
Expand Down
47 changes: 32 additions & 15 deletions src/actions/savedAccounts.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,58 @@
import actionTypes from '../constants/actions';
import { getSavedAccount, setSavedAccount, removeSavedAccount } from '../utils/saveAccount';
import {
getSavedAccounts,
setSavedAccount,
removeSavedAccount,
setLastActiveAccount,
getLastActiveAccount,
} from '../utils/savedAccounts';

/**
* An action to dispatch accountSaved
*
*/
export const accountSaved = (data) => {
setSavedAccount(data);
export const accountSaved = (account) => {
setSavedAccount(account);
setLastActiveAccount(account);
return {
data,
data: account,
type: actionTypes.accountSaved,
};
};

/**
* An action to dispatch accountRemoved
*/
export const accountRemoved = (publicKey) => {
removeSavedAccount(publicKey);
export const accountRemoved = (account) => {
removeSavedAccount(account);
return {
data: publicKey,
data: account,
type: actionTypes.accountRemoved,
};
};

/**
* An action to dispatch accountSwitched
*/
export const accountSwitched = (account) => {
setLastActiveAccount(account);
return {
data: account,
type: actionTypes.accountSwitched,
};
};

/**
* The action to initiate savedAccounts store with the retrieved accounts
*
* @todo since the utility returns only one item, this action puts it in the array
* eventually it should receive an array and return that to reducer
*
*/
export const accountsRetrieved = () => {
const accounts = getSavedAccount();
const data = accounts !== undefined ? accounts : [];
return {
data,
type: actionTypes.accountsRetrieved,
};
};
export const accountsRetrieved = () => ({
data: {
accounts: getSavedAccounts(),
lastActive: getLastActiveAccount(),
},
type: actionTypes.accountsRetrieved,
});
24 changes: 21 additions & 3 deletions src/actions/savedAccounts.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { expect } from 'chai';
import sinon from 'sinon';
import actionTypes from '../constants/actions';
import * as saveAccountUtils from '../utils/saveAccount';
import * as savedAccountsUtils from '../utils/savedAccounts';
import {
accountSaved,
accountSwitched,
accountRemoved,
accountsRetrieved,
} from './savedAccounts';
Expand All @@ -18,12 +19,19 @@ describe('actions: savedAccount', () => {

describe('accountsRetrieved', () => {
it('should create an action to retrieved the saved accounts list', () => {
sinon.stub(saveAccountUtils, 'getSavedAccount').returns([data]);
const getSavedAccountsStub = sinon.stub(savedAccountsUtils, 'getSavedAccounts').returns([data]);
const getLastActiveAccountStub = sinon.stub(savedAccountsUtils, 'getLastActiveAccount').returns(data);
const expectedAction = {
data: [data],
data: {
accounts: [data],
lastActive: data,
},
type: actionTypes.accountsRetrieved,
};
expect(accountsRetrieved()).to.be.deep.equal(expectedAction);

getSavedAccountsStub.restore();
getLastActiveAccountStub.restore();
});
});

Expand All @@ -37,6 +45,16 @@ describe('actions: savedAccount', () => {
});
});

describe('accountSwitched', () => {
it('should create an action to save account', () => {
const expectedAction = {
data,
type: actionTypes.accountSwitched,
};
expect(accountSwitched(data)).to.be.deep.equal(expectedAction);
});
});

describe('accountRemoved', () => {
it('should create an action to remove account', () => {
const expectedAction = {
Expand Down
8 changes: 4 additions & 4 deletions src/components/dialog/dialogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import i18next from 'i18next';
import ReceiveDialog from '../receiveDialog';
import Register from '../register';
import RegisterDelegate from '../registerDelegate';
import SaveAccount from '../saveAccount';
import SavedAccounts from '../savedAccounts';
import SecondPassphrase from '../secondPassphrase';
import Send from '../send';
import Settings from '../settings';
Expand Down Expand Up @@ -45,9 +45,9 @@ export default () => ({
title: i18next.t('New Account'),
component: Register,
},
'save-account': {
title: i18next.t('Remember this account'),
component: SaveAccount,
'saved-accounts': {
title: i18next.t('Saved accounts'),
component: SavedAccounts,
},
settings: {
title: i18next.t('Settings'),
Expand Down

0 comments on commit bb863f4

Please sign in to comment.