Connection UI cleanups & tests for prior PR (#4020)
* Cleanups & tests for #3945 * Externalise icons as per PR comments
This commit is contained in:
parent
7cdfaf1a43
commit
cc8e200ed5
@ -18,7 +18,10 @@ import AddIcon from 'material-ui/svg-icons/content/add';
|
||||
import CancelIcon from 'material-ui/svg-icons/content/clear';
|
||||
import CheckIcon from 'material-ui/svg-icons/navigation/check';
|
||||
import CloseIcon from 'material-ui/svg-icons/navigation/close';
|
||||
import CompareIcon from 'material-ui/svg-icons/action/compare-arrows';
|
||||
import ComputerIcon from 'material-ui/svg-icons/hardware/desktop-mac';
|
||||
import ContractIcon from 'material-ui/svg-icons/action/code';
|
||||
import DashboardIcon from 'material-ui/svg-icons/action/dashboard';
|
||||
import DoneIcon from 'material-ui/svg-icons/action/done-all';
|
||||
import LockedIcon from 'material-ui/svg-icons/action/lock-outline';
|
||||
import NextIcon from 'material-ui/svg-icons/navigation/arrow-forward';
|
||||
@ -27,13 +30,17 @@ import SaveIcon from 'material-ui/svg-icons/content/save';
|
||||
import SendIcon from 'material-ui/svg-icons/content/send';
|
||||
import SnoozeIcon from 'material-ui/svg-icons/av/snooze';
|
||||
import VisibleIcon from 'material-ui/svg-icons/image/remove-red-eye';
|
||||
import VpnIcon from 'material-ui/svg-icons/notification/vpn-lock';
|
||||
|
||||
export {
|
||||
AddIcon,
|
||||
CancelIcon,
|
||||
CheckIcon,
|
||||
CloseIcon,
|
||||
CompareIcon,
|
||||
ComputerIcon,
|
||||
ContractIcon,
|
||||
DashboardIcon,
|
||||
DoneIcon,
|
||||
LockedIcon,
|
||||
NextIcon,
|
||||
@ -41,5 +48,6 @@ export {
|
||||
SaveIcon,
|
||||
SendIcon,
|
||||
SnoozeIcon,
|
||||
VisibleIcon
|
||||
VisibleIcon,
|
||||
VpnIcon
|
||||
};
|
||||
|
@ -17,13 +17,9 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import ActionCompareArrows from 'material-ui/svg-icons/action/compare-arrows';
|
||||
import ActionDashboard from 'material-ui/svg-icons/action/dashboard';
|
||||
import HardwareDesktopMac from 'material-ui/svg-icons/hardware/desktop-mac';
|
||||
import NotificationVpnLock from 'material-ui/svg-icons/notification/vpn-lock';
|
||||
|
||||
import { Input } from '~/ui';
|
||||
import { CompareIcon, ComputerIcon, DashboardIcon, VpnIcon } from '~/ui/Icons';
|
||||
|
||||
import styles from './connection.css';
|
||||
|
||||
@ -51,13 +47,6 @@ class Connection extends Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
const typeIcon = needsToken
|
||||
? <NotificationVpnLock className={ styles.svg } />
|
||||
: <ActionDashboard className={ styles.svg } />;
|
||||
const description = needsToken
|
||||
? this.renderSigner()
|
||||
: this.renderPing();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={ styles.overlay } />
|
||||
@ -65,16 +54,24 @@ class Connection extends Component {
|
||||
<div className={ styles.body }>
|
||||
<div className={ styles.icons }>
|
||||
<div className={ styles.icon }>
|
||||
<HardwareDesktopMac className={ styles.svg } />
|
||||
<ComputerIcon className={ styles.svg } />
|
||||
</div>
|
||||
<div className={ styles.iconSmall }>
|
||||
<ActionCompareArrows className={ `${styles.svg} ${styles.pulse}` } />
|
||||
<CompareIcon className={ `${styles.svg} ${styles.pulse}` } />
|
||||
</div>
|
||||
<div className={ styles.icon }>
|
||||
{ typeIcon }
|
||||
{
|
||||
needsToken
|
||||
? <VpnIcon className={ styles.svg } />
|
||||
: <DashboardIcon className={ styles.svg } />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{ description }
|
||||
{
|
||||
needsToken
|
||||
? this.renderSigner()
|
||||
: this.renderPing()
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -144,10 +141,19 @@ class Connection extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
onChangeToken = (event, _token) => {
|
||||
validateToken = (_token) => {
|
||||
const token = _token.trim();
|
||||
const validToken = /^[a-zA-Z0-9]{4}(-)?[a-zA-Z0-9]{4}(-)?[a-zA-Z0-9]{4}(-)?[a-zA-Z0-9]{4}$/.test(token);
|
||||
|
||||
return {
|
||||
token,
|
||||
validToken
|
||||
};
|
||||
}
|
||||
|
||||
onChangeToken = (event, _token) => {
|
||||
const { token, validToken } = this.validateToken(_token || event.target.value);
|
||||
|
||||
this.setState({ token, validToken }, () => {
|
||||
validToken && this.setToken();
|
||||
});
|
||||
@ -159,7 +165,7 @@ class Connection extends Component {
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
api
|
||||
return api
|
||||
.updateToken(token, 0)
|
||||
.then((isValid) => {
|
||||
this.setState({
|
||||
@ -173,14 +179,14 @@ class Connection extends Component {
|
||||
function mapStateToProps (state) {
|
||||
const { isConnected, isConnecting, needsToken } = state.nodeStatus;
|
||||
|
||||
return { isConnected, isConnecting, needsToken };
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({}, dispatch);
|
||||
return {
|
||||
isConnected,
|
||||
isConnecting,
|
||||
needsToken
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
null
|
||||
)(Connection);
|
||||
|
156
js/src/views/Connection/connection.spec.js
Normal file
156
js/src/views/Connection/connection.spec.js
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright 2015, 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import Connection from './';
|
||||
|
||||
let api;
|
||||
let component;
|
||||
let instance;
|
||||
|
||||
function createApi () {
|
||||
return {
|
||||
updateToken: sinon.stub().resolves()
|
||||
};
|
||||
}
|
||||
|
||||
function createRedux (isConnected = true, isConnecting = false, needsToken = false) {
|
||||
return {
|
||||
dispatch: sinon.stub(),
|
||||
subscribe: sinon.stub(),
|
||||
getState: () => {
|
||||
return {
|
||||
nodeStatus: {
|
||||
isConnected,
|
||||
isConnecting,
|
||||
needsToken
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function render (store) {
|
||||
api = createApi();
|
||||
component = shallow(
|
||||
<Connection />,
|
||||
{ context: { store: store || createRedux() } }
|
||||
).find('Connection').shallow({ context: { api } });
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('views/Connection', () => {
|
||||
it('renders defaults', () => {
|
||||
expect(render()).to.be.ok;
|
||||
});
|
||||
|
||||
it('does not render when connected', () => {
|
||||
expect(render(createRedux(true)).find('div')).to.have.length(0);
|
||||
});
|
||||
|
||||
describe('renderPing', () => {
|
||||
it('renders the connecting to node message', () => {
|
||||
render();
|
||||
const ping = shallow(instance.renderPing());
|
||||
|
||||
expect(ping.find('FormattedMessage').props().id).to.equal('connection.connectingNode');
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderSigner', () => {
|
||||
it('renders the connecting to api message when isConnecting === true', () => {
|
||||
render(createRedux(false, true));
|
||||
const signer = shallow(instance.renderSigner());
|
||||
|
||||
expect(signer.find('FormattedMessage').props().id).to.equal('connection.connectingAPI');
|
||||
});
|
||||
|
||||
it('renders token input when needsToken == true & isConnecting === false', () => {
|
||||
render(createRedux(false, false, true));
|
||||
const signer = shallow(instance.renderSigner());
|
||||
|
||||
expect(signer.find('FormattedMessage').first().props().id).to.equal('connection.noConnection');
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateToken', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
it('trims whitespace from passed tokens', () => {
|
||||
expect(instance.validateToken(' \t test ing\t ').token).to.equal('test ing');
|
||||
});
|
||||
|
||||
it('validates 4-4-4-4 format', () => {
|
||||
expect(instance.validateToken('1234-5678-90ab-cdef').validToken).to.be.true;
|
||||
});
|
||||
|
||||
it('validates 4-4-4-4 format (with trimmable whitespace)', () => {
|
||||
expect(instance.validateToken(' \t 1234-5678-90ab-cdef \t ').validToken).to.be.true;
|
||||
});
|
||||
|
||||
it('validates 4444 format', () => {
|
||||
expect(instance.validateToken('1234567890abcdef').validToken).to.be.true;
|
||||
});
|
||||
|
||||
it('validates 4444 format (with trimmable whitespace)', () => {
|
||||
expect(instance.validateToken(' \t 1234567890abcdef \t ').validToken).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('onChangeToken', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
sinon.spy(instance, 'setToken');
|
||||
sinon.spy(instance, 'validateToken');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
instance.setToken.restore();
|
||||
instance.validateToken.restore();
|
||||
});
|
||||
|
||||
it('validates tokens passed', () => {
|
||||
instance.onChangeToken({ target: { value: 'testing' } });
|
||||
expect(instance.validateToken).to.have.been.calledWith('testing');
|
||||
});
|
||||
|
||||
it('sets the token on the api when valid', () => {
|
||||
instance.onChangeToken({ target: { value: '1234-5678-90ab-cdef' } });
|
||||
expect(instance.setToken).to.have.been.called;
|
||||
});
|
||||
});
|
||||
|
||||
describe('setToken', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
it('calls the api.updateToken', () => {
|
||||
component.setState({ token: 'testing' });
|
||||
|
||||
return instance.setToken().then(() => {
|
||||
expect(api.updateToken).to.have.been.calledWith('testing');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user