-
Notifications
You must be signed in to change notification settings - Fork 60
Register as delegate - Closes #354 #543
Changes from 8 commits
625399b
6124963
1b0101c
8e4f2b4
fac6282
64b6462
9074cf3
8a2328e
8e2a2d7
7024822
38ce2a9
0d54eaa
55ac449
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,3 +20,6 @@ | |
.menu { | ||
right: -16px !important; | ||
} | ||
.hidden { | ||
display: none; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,9 +18,11 @@ describe('HeaderElement', () => { | |
beforeEach(() => { | ||
const mockInputProps = { | ||
setActiveDialog: () => { }, | ||
account: {}, | ||
}; | ||
propsMock = sinon.mock(mockInputProps); | ||
wrapper = shallow(<HeaderElement setActiveDialog={mockInputProps.setActiveDialog} />); | ||
wrapper = shallow(<HeaderElement account={mockInputProps.account} | ||
setActiveDialog={mockInputProps.setActiveDialog} />); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can be simplified to |
||
}); | ||
|
||
afterEach(() => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -98,8 +98,10 @@ class LoginFormComponent extends React.Component { | |
getAccount(this.props.peers.data, accountInfo.address).then((result) => { | ||
onAccountUpdated(result); | ||
getDelegate(this.props.peers.data, accountInfo.publicKey).then((data) => { | ||
console.log('success'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this |
||
onAccountUpdated({ delegate: data.delegate, isDelegate: true }); | ||
}).catch(() => { | ||
console.log('error'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this |
||
onAccountUpdated({ delegate: {}, isDelegate: false }); | ||
}); | ||
// redirect to main/transactions | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { connect } from 'react-redux'; | ||
import RegisterDelegate from './registerDelegate'; | ||
import { accountUpdated } from '../../actions/account'; | ||
import { successAlertDialogDisplayed, errorAlertDialogDisplayed } from '../../actions/dialog'; | ||
|
||
const mapStateToProps = state => ({ | ||
account: state.account, | ||
peers: state.peers, | ||
}); | ||
|
||
const mapDispatchToProps = dispatch => ({ | ||
onAccountUpdated: data => dispatch(accountUpdated(data)), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I cannot find where is this used. |
||
showSuccessAlert: data => dispatch(successAlertDialogDisplayed(data)), | ||
showErrorAlert: data => dispatch(errorAlertDialogDisplayed(data)), | ||
}); | ||
|
||
const RegisterDelegateConnected = connect( | ||
mapStateToProps, | ||
mapDispatchToProps, | ||
)(RegisterDelegate); | ||
|
||
export default RegisterDelegateConnected; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { expect } from 'chai'; | ||
import { mount } from 'enzyme'; | ||
import { BrowserRouter as Router } from 'react-router-dom'; | ||
import RegisterDelegate from './registerDelegate'; | ||
import RegisterDelegateConnected from './index'; | ||
import { accountUpdated } from '../../actions/account'; | ||
|
||
describe('RegisterDelegateConnected', () => { | ||
let mountedAccount; | ||
// Mocking store | ||
const peers = { | ||
status: { | ||
online: false, | ||
}, | ||
data: { | ||
currentPeer: 'localhost', | ||
port: 4000, | ||
options: { | ||
name: 'Custom Node', | ||
}, | ||
}, | ||
}; | ||
|
||
const account = { | ||
isDelegate: false, | ||
address: '16313739661670634666L', | ||
username: 'lisk-nano', | ||
}; | ||
|
||
const store = { | ||
dispatch: () => {}, | ||
subscribe: () => {}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use |
||
getState: () => ({ | ||
peers, | ||
account, | ||
}), | ||
onAccountUpdated: () => (data) => { | ||
store.account = data; | ||
return accountUpdated(data); | ||
}, | ||
showSuccessAlert: () => {}, | ||
showErrorAlert: () => {}, | ||
}; | ||
const options = { | ||
context: { store }, | ||
childContextTypes: { store: PropTypes.object.isRequired }, | ||
}; | ||
|
||
beforeEach(() => { | ||
mountedAccount = mount(<Router><RegisterDelegateConnected/></Router>, options); | ||
}); | ||
|
||
it('should mount registerDelegate with appropriate properties', () => { | ||
const props = mountedAccount.find(RegisterDelegate).props(); | ||
expect(props.peers).to.be.equal(peers); | ||
expect(props.account).to.be.equal(account); | ||
expect(typeof props.onAccountUpdated).to.be.equal('function'); | ||
expect(typeof props.showSuccessAlert).to.be.equal('function'); | ||
expect(typeof props.showErrorAlert).to.be.equal('function'); | ||
}); | ||
|
||
describe('onAccountUpdated', () => { | ||
it('should return a dispatch object', () => { | ||
const props = mountedAccount.find(RegisterDelegate).props(); | ||
const data = props.onAccountUpdated(account); | ||
expect(data).to.be.equal(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks wrong. First It would be better to |
||
}); | ||
}); | ||
|
||
describe('showSuccessAlert', () => { | ||
it('should return a dispatch object', () => { | ||
const props = mountedAccount.find(RegisterDelegate).props(); | ||
const data = props.showSuccessAlert('sample text'); | ||
expect(data).to.be.equal(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
}); | ||
}); | ||
|
||
describe('showErrorAlert', () => { | ||
it('should return a dispatch object', () => { | ||
const props = mountedAccount.find(RegisterDelegate).props(); | ||
const data = props.showErrorAlert('sample text'); | ||
expect(data).to.be.equal(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import React from 'react'; | ||
import grid from 'flexboxgrid/dist/flexboxgrid.css'; | ||
import Input from 'react-toolbox/lib/input'; | ||
import Button from 'react-toolbox/lib/button'; | ||
import InfoParagraph from '../infoParagraph'; | ||
import { registerDelegate } from '../../utils/api/delegate'; | ||
|
||
class RegisterDelegate extends React.Component { | ||
constructor() { | ||
super(); | ||
|
||
this.state = { | ||
name: '', | ||
nameError: '', | ||
}; | ||
} | ||
|
||
changeHandler(name, value) { | ||
this.setState({ [name]: value }); | ||
} | ||
|
||
register(username, secondSecret) { | ||
registerDelegate(this.props.peers.data, username, | ||
this.props.account.passphrase, secondSecret) | ||
.then(() => { | ||
this.props.showSuccessAlert({ | ||
text: `Delegate registration was successfully submitted with username: "${this.state.name}". It can take several seconds before it is processed.`, | ||
}); | ||
}) | ||
.catch((error) => { | ||
if (error && error.message === 'Username already exists') { | ||
this.setState({ nameError: error.message }); | ||
} else { | ||
this.props.showErrorAlert({ | ||
text: error && error.message ? `${error.message}.` : 'An error occurred while registering as delegate.', | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
render() { | ||
// notify use about insufficient balance | ||
return ( | ||
<div> | ||
<Input label='Delegate name' required={true} | ||
autoFocus={true} | ||
className='username' | ||
onChange={this.changeHandler.bind(this, 'name')} | ||
error={this.state.nameError} | ||
value={this.state.name} /> | ||
{ | ||
this.props.account.secondSecret && | ||
<Input label='Second secret' required={true} | ||
className='second-secret' | ||
value={this.state.secondSecret} /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
} | ||
<hr/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
<InfoParagraph> | ||
Becoming a delegate requires registration. You may choose your own | ||
delegate name, which can be used to promote your delegate. Only the | ||
top 101 delegates are eligible to forge. All fees are shared equally | ||
between the top 101 delegates. | ||
</InfoParagraph> | ||
<section className={`${grid.row} ${grid['between-xs']}`}> | ||
<Button label='Cancel' className='cancel-button' onClick={this.props.closeDialog} /> | ||
<Button label='Register' | ||
primary={true} raised={true} | ||
disabled={!this.state.name} | ||
className='submit-button' | ||
onClick={this.register.bind(this, this.state.name, this.state.secondSecret)}/> | ||
</section> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default RegisterDelegate; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import React from 'react'; | ||
import chai, { expect } from 'chai'; | ||
import { mount } from 'enzyme'; | ||
import chaiEnzyme from 'chai-enzyme'; | ||
import sinon from 'sinon'; | ||
import Lisk from 'lisk-js'; | ||
import RegisterDelegate from './registerDelegate'; | ||
import * as delegateApi from '../../utils/api/delegate'; | ||
|
||
chai.use(chaiEnzyme()); | ||
|
||
const normalAccount = { | ||
isDelegate: false, | ||
address: '16313739661670634666L', | ||
balance: 1000e8, | ||
}; | ||
|
||
const delegateAccount = { | ||
isDelegate: true, | ||
address: '16313739661670634666L', | ||
balance: 1000e8, | ||
delegate: { | ||
username: 'lisk-nano', | ||
}, | ||
}; | ||
|
||
const withSecondSecretAccount = { | ||
isDelegate: true, | ||
address: '16313739661670634666L', | ||
balance: 1000e8, | ||
delegate: { | ||
username: 'lisk-nano', | ||
}, | ||
secondSecret: 'sample phrase', | ||
}; | ||
|
||
const props = { | ||
peers: { | ||
data: Lisk.api({ | ||
name: 'Custom Node', | ||
custom: true, | ||
address: 'http://localhost:4000', | ||
testnet: true, | ||
nethash: '198f2b61a8eb95fbeed58b8216780b68f697f26b849acf00c8c93bb9b24f783d', | ||
}), | ||
}, | ||
closeDialog: () => {}, | ||
onAccountUpdated: () => {}, | ||
showSuccessAlert: () => {}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can make this Those tests ending with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed that I'm not doing this either (in |
||
showErrorAlert: () => {}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
}; | ||
|
||
const delegateProps = { ...props, account: delegateAccount }; | ||
const normalProps = { ...props, account: normalAccount }; | ||
const withSecondSecretProps = { ...props, account: withSecondSecretAccount }; | ||
|
||
describe('RegisterDelegate', () => { | ||
let wrapper; | ||
let delegateApiMock; | ||
|
||
beforeEach(() => { | ||
delegateApiMock = sinon.mock(delegateApi); | ||
}); | ||
|
||
afterEach(() => { | ||
delegateApiMock.verify(); | ||
delegateApiMock.restore(); | ||
}); | ||
|
||
describe('Ordinary account', () => { | ||
beforeEach(() => { | ||
wrapper = mount(<RegisterDelegate {...normalProps} />); | ||
}); | ||
|
||
it('renders an InfoParagraph components', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo "components" -> "component" |
||
expect(wrapper.find('InfoParagraph')).to.have.length(1); | ||
}); | ||
|
||
it('renders one Input component for a normal account', () => { | ||
expect(wrapper.find('Input')).to.have.length(1); | ||
}); | ||
|
||
it('allows register as delegate for a non delegate account', () => { | ||
delegateApiMock.expects('registerDelegate').resolves({ success: true }); | ||
|
||
wrapper.find('.username input').simulate('change', { target: { value: 'sample_username' } }); | ||
wrapper.find('.submit-button').simulate('click'); | ||
}); | ||
|
||
it('does not allow registering an existing username', () => { | ||
delegateApiMock.expects('registerDelegate').resolves({ success: false }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be |
||
|
||
wrapper.find('.username input').simulate('change', { target: { value: 'sample_username' } }); | ||
wrapper.find('.submit-button').simulate('click'); | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test case for 'Username already exists' missing |
||
}); | ||
|
||
describe('Ordinary account with second secret', () => { | ||
beforeEach(() => { | ||
wrapper = mount(<RegisterDelegate {...withSecondSecretProps} />); | ||
}); | ||
|
||
it('renders two Input component for a an account with second secret', () => { | ||
expect(wrapper.find('Input')).to.have.length(2); | ||
}); | ||
|
||
it('allows register as delegate for a non delegate account with second secret', () => { | ||
delegateApiMock.expects('registerDelegate').resolves({ success: true }); | ||
|
||
wrapper.find('.username input').simulate('change', { target: { value: 'sample_username' } }); | ||
wrapper.find('.second-secret input').simulate('change', { target: { value: 'sample phrase' } }); | ||
wrapper.find('.submit-button').simulate('click'); | ||
}); | ||
}); | ||
|
||
describe('Delegate account', () => { | ||
beforeEach(() => { | ||
wrapper = mount(<RegisterDelegate {...delegateProps} />); | ||
}); | ||
|
||
it('does not allow register as delegate for a delegate account', () => { | ||
delegateApiMock.expects('registerDelegate').resolves({ success: false }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be |
||
|
||
wrapper.find('.username input').simulate('change', { target: { value: 'sample_username' } }); | ||
wrapper.find('.submit-button').simulate('click'); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.hidden
is not used.