From 1940809dd4b10fe469feead56c3274445de2ce70 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 27 Jan 2017 14:42:06 +0100 Subject: [PATCH] DeleteAccount & LoadContract modal updates (#4320) * i18n & tests for DeleteAccount * i18n & tests for LoadContract --- js/src/modals/DeleteAccount/deleteAccount.js | 48 +++-- .../DeleteAccount/deleteAccount.spec.js | 139 +++++++++++++ js/src/modals/LoadContract/loadContract.js | 191 ++++++++++++------ .../modals/LoadContract/loadContract.spec.js | 159 +++++++++++++++ 4 files changed, 459 insertions(+), 78 deletions(-) create mode 100644 js/src/modals/DeleteAccount/deleteAccount.spec.js create mode 100644 js/src/modals/LoadContract/loadContract.spec.js diff --git a/js/src/modals/DeleteAccount/deleteAccount.js b/js/src/modals/DeleteAccount/deleteAccount.js index 0fa9d797a..d6ea3f2de 100644 --- a/js/src/modals/DeleteAccount/deleteAccount.js +++ b/js/src/modals/DeleteAccount/deleteAccount.js @@ -15,6 +15,7 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; +import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; @@ -46,22 +47,33 @@ class DeleteAccount extends Component { return ( + } + visible >
- Are you sure you want to permanently delete the following account? +
- +
{ account.address } @@ -73,11 +85,21 @@ class DeleteAccount extends Component {
+ } + label={ + + } + onChange={ this.onChangePassword } type='password' value={ password } - onChange={ this.onChangePassword } />
@@ -93,7 +115,7 @@ class DeleteAccount extends Component { const { account, newError } = this.props; const { password } = this.state; - api.parity + return api.parity .killAccount(account.address, password) .then((result) => { if (result === true) { @@ -114,15 +136,11 @@ class DeleteAccount extends Component { } } -function mapStateToProps (state) { - return {}; -} - function mapDispatchToProps (dispatch) { return bindActionCreators({ newError }, dispatch); } export default connect( - mapStateToProps, + null, mapDispatchToProps )(DeleteAccount); diff --git a/js/src/modals/DeleteAccount/deleteAccount.spec.js b/js/src/modals/DeleteAccount/deleteAccount.spec.js new file mode 100644 index 000000000..871743453 --- /dev/null +++ b/js/src/modals/DeleteAccount/deleteAccount.spec.js @@ -0,0 +1,139 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { shallow } from 'enzyme'; +import React from 'react'; +import sinon from 'sinon'; + +import DeleteAccount from './'; + +let api; +let component; +let instance; +let onClose; +let router; +let store; + +const TEST_ADDRESS = '0x123456789012345678901234567890'; +const TEST_PASSWORD = 'testPassword'; + +function createApi () { + api = { + parity: { + killAccount: sinon.stub().resolves(true) + } + }; + + return api; +} + +function createRouter () { + router = { + push: sinon.stub() + }; + + return router; +} + +function createStore () { + store = { + dispatch: sinon.stub(), + subscribe: sinon.stub(), + getState: () => { + return {}; + } + }; + + return store; +} + +function render () { + onClose = sinon.stub(); + component = shallow( + , + { + context: { + store: createStore() + } + } + ).find('DeleteAccount').shallow({ + context: { + api: createApi(), + router: createRouter() + } + }); + instance = component.instance(); + + return component; +} + +describe('modals/DeleteAccount', () => { + beforeEach(() => { + render(); + }); + + it('renders defaults', () => { + expect(component).to.be.ok; + }); + + describe('event handlers', () => { + describe('onChangePassword', () => { + it('sets the state with the new password', () => { + instance.onChangePassword(null, TEST_PASSWORD); + expect(instance.state.password).to.equal(TEST_PASSWORD); + }); + }); + + describe('closeDeleteDialog', () => { + it('calls onClose', () => { + instance.closeDeleteDialog(); + expect(onClose).to.have.been.called; + }); + }); + + describe('onDeleteConfirmed', () => { + beforeEach(() => { + sinon.spy(instance, 'closeDeleteDialog'); + instance.onChangePassword(null, TEST_PASSWORD); + return instance.onDeleteConfirmed(); + }); + + afterEach(() => { + instance.closeDeleteDialog.restore(); + }); + + it('calls parity_killAccount', () => { + expect(api.parity.killAccount).to.have.been.calledWith(TEST_ADDRESS, TEST_PASSWORD); + }); + + it('changes the route to /accounts', () => { + expect(router.push).to.have.been.calledWith('/accounts'); + }); + + it('closes the dialog', () => { + expect(instance.closeDeleteDialog).to.have.been.called; + }); + }); + }); +}); diff --git a/js/src/modals/LoadContract/loadContract.js b/js/src/modals/LoadContract/loadContract.js index 2678aef34..1bc49a2e3 100644 --- a/js/src/modals/LoadContract/loadContract.js +++ b/js/src/modals/LoadContract/loadContract.js @@ -14,22 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import React, { Component, PropTypes } from 'react'; - -import ContentClear from 'material-ui/svg-icons/content/clear'; -import CheckIcon from 'material-ui/svg-icons/navigation/check'; -import DeleteIcon from 'material-ui/svg-icons/action/delete'; - -import { List, ListItem, makeSelectable } from 'material-ui/List'; import { Subheader, IconButton, Tabs, Tab } from 'material-ui'; +import { List, ListItem, makeSelectable } from 'material-ui/List'; import moment from 'moment'; +import React, { Component, PropTypes } from 'react'; +import { FormattedMessage } from 'react-intl'; import { Button, Modal, Editor } from '~/ui'; +import { CancelIcon, CheckIcon, DeleteIcon } from '~/ui/Icons'; import styles from './loadContract.css'; const SelectableList = makeSelectable(List); +const REMOVAL_STYLE = { + backgroundColor: 'none', + cursor: 'default' +}; const SELECTED_STYLE = { backgroundColor: 'rgba(255, 255, 255, 0.1)' }; @@ -52,14 +53,24 @@ export default class LoadContract extends Component { render () { const { deleteRequest } = this.state; - const title = deleteRequest - ? 'confirm removal' - : 'view contracts'; - return ( + ) + : ( + + ) + } visible > { this.renderBody() } @@ -73,17 +84,25 @@ export default class LoadContract extends Component { } const { contracts, snippets } = this.props; - const contractsTab = Object.keys(contracts).length === 0 ? null : ( - + + } + > { this.renderEditor() } - - - Saved Contracts + + + + { this.renderContracts(contracts) } @@ -93,14 +112,22 @@ export default class LoadContract extends Component {
{ contractsTab } - - + + } + > { this.renderEditor() } - - - Contract Snippets + + + + { this.renderContracts(snippets, false) } @@ -116,13 +143,23 @@ export default class LoadContract extends Component { return (

- Are you sure you want to remove the following - contract from your saved contracts? +

+ } + style={ REMOVAL_STYLE } />
@@ -152,9 +189,9 @@ export default class LoadContract extends Component {

{ name }

); @@ -169,23 +206,36 @@ export default class LoadContract extends Component { const { id, name, timestamp, description } = contract; const onDelete = () => this.onDeleteRequest(id); - const secondaryText = description || `Saved ${moment(timestamp).fromNow()}`; - const remove = removable - ? ( - - - - ) - : null; - return ( + + + ) + : null + } + secondaryText={ + description || ( + + ) + } + style={ + selected === id + ? SELECTED_STYLE + : null + } + value={ id } /> ); }); @@ -197,46 +247,61 @@ export default class LoadContract extends Component { if (deleteRequest) { return [