Fix newError noops when not bound to dispacher (#4013)
* AddContract properly binds newError * EditMeta properly binds newError * PasswordManager properly binds newError * pass null instead of empty mapStateToProps * Add openSnackbar test & binded prop
This commit is contained in:
parent
9db3f383e3
commit
04ed53e0f2
@ -17,6 +17,8 @@
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import { newError } from '~/redux/actions';
|
||||
import { Button, Modal, Form, Input, InputAddress, RadioButtons } from '~/ui';
|
||||
@ -25,13 +27,14 @@ import { AddIcon, CancelIcon, NextIcon, PrevIcon } from '~/ui/Icons';
|
||||
import Store from './store';
|
||||
|
||||
@observer
|
||||
export default class AddContract extends Component {
|
||||
class AddContract extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
contracts: PropTypes.object.isRequired,
|
||||
newError: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func
|
||||
};
|
||||
|
||||
@ -244,7 +247,7 @@ export default class AddContract extends Component {
|
||||
this.onClose();
|
||||
})
|
||||
.catch((error) => {
|
||||
newError(error);
|
||||
this.props.newError(error);
|
||||
});
|
||||
}
|
||||
|
||||
@ -252,3 +255,14 @@ export default class AddContract extends Component {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({
|
||||
newError
|
||||
}, dispatch);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mapDispatchToProps
|
||||
)(AddContract);
|
||||
|
@ -20,24 +20,27 @@ import sinon from 'sinon';
|
||||
|
||||
import AddContract from './';
|
||||
|
||||
import { CONTRACTS, createApi } from './addContract.test.js';
|
||||
import { CONTRACTS, createApi, createRedux } from './addContract.test.js';
|
||||
|
||||
let api;
|
||||
let component;
|
||||
let instance;
|
||||
let onClose;
|
||||
let reduxStore;
|
||||
|
||||
function renderShallow (props) {
|
||||
function render (props = {}) {
|
||||
api = createApi();
|
||||
onClose = sinon.stub();
|
||||
reduxStore = createRedux();
|
||||
|
||||
component = shallow(
|
||||
<AddContract
|
||||
{ ...props }
|
||||
contracts={ CONTRACTS }
|
||||
onClose={ onClose } />,
|
||||
{
|
||||
context: {
|
||||
api: createApi()
|
||||
}
|
||||
}
|
||||
);
|
||||
{ context: { store: reduxStore } }
|
||||
).find('AddContract').shallow({ context: { api } });
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
@ -45,11 +48,37 @@ function renderShallow (props) {
|
||||
describe('modals/AddContract', () => {
|
||||
describe('rendering', () => {
|
||||
beforeEach(() => {
|
||||
renderShallow();
|
||||
render();
|
||||
});
|
||||
|
||||
it('renders the defauls', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
describe('onAdd', () => {
|
||||
it('calls store addContract', () => {
|
||||
sinon.stub(instance.store, 'addContract').resolves(true);
|
||||
return instance.onAdd().then(() => {
|
||||
expect(instance.store.addContract).to.have.been.called;
|
||||
instance.store.addContract.restore();
|
||||
});
|
||||
});
|
||||
|
||||
it('calls closes dialog on success', () => {
|
||||
sinon.stub(instance.store, 'addContract').resolves(true);
|
||||
return instance.onAdd().then(() => {
|
||||
expect(onClose).to.have.been.called;
|
||||
instance.store.addContract.restore();
|
||||
});
|
||||
});
|
||||
|
||||
it('adds newError on failure', () => {
|
||||
sinon.stub(instance.store, 'addContract').rejects('test');
|
||||
return instance.onAdd().then(() => {
|
||||
expect(reduxStore.dispatch).to.have.been.calledWith({ error: new Error('test'), type: 'newError' });
|
||||
instance.store.addContract.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -31,8 +31,19 @@ function createApi () {
|
||||
};
|
||||
}
|
||||
|
||||
function createRedux () {
|
||||
return {
|
||||
dispatch: sinon.stub(),
|
||||
subscribe: sinon.stub(),
|
||||
getState: () => {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
ABI,
|
||||
CONTRACTS,
|
||||
createApi
|
||||
createApi,
|
||||
createRedux
|
||||
};
|
||||
|
@ -17,20 +17,24 @@
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import { newError } from '~/redux/actions';
|
||||
import { Button, Form, Input, InputChip, Modal } from '~/ui';
|
||||
import { CancelIcon, SaveIcon } from '~/ui/Icons';
|
||||
|
||||
import Store from './store';
|
||||
|
||||
@observer
|
||||
export default class EditMeta extends Component {
|
||||
class EditMeta extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
account: PropTypes.object.isRequired,
|
||||
newError: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
@ -138,6 +142,20 @@ export default class EditMeta extends Component {
|
||||
|
||||
return this.store
|
||||
.save()
|
||||
.then(() => this.props.onClose());
|
||||
.then(() => this.props.onClose())
|
||||
.catch((error) => {
|
||||
this.props.newError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({
|
||||
newError
|
||||
}, dispatch);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mapDispatchToProps
|
||||
)(EditMeta);
|
||||
|
@ -20,25 +20,27 @@ import sinon from 'sinon';
|
||||
|
||||
import EditMeta from './';
|
||||
|
||||
import { ACCOUNT, createApi } from './editMeta.test.js';
|
||||
import { ACCOUNT, createApi, createRedux } from './editMeta.test.js';
|
||||
|
||||
let api;
|
||||
let component;
|
||||
let instance;
|
||||
let onClose;
|
||||
let reduxStore;
|
||||
|
||||
function render (props) {
|
||||
api = createApi();
|
||||
onClose = sinon.stub();
|
||||
reduxStore = createRedux();
|
||||
|
||||
component = shallow(
|
||||
<EditMeta
|
||||
{ ...props }
|
||||
account={ ACCOUNT }
|
||||
onClose={ onClose } />,
|
||||
{
|
||||
context: {
|
||||
api: createApi()
|
||||
}
|
||||
}
|
||||
);
|
||||
{ context: { store: reduxStore } }
|
||||
).find('EditMeta').shallow({ context: { api } });
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
@ -56,15 +58,29 @@ describe('modals/EditMeta', () => {
|
||||
});
|
||||
|
||||
describe('onSave', () => {
|
||||
it('calls store.save() & props.onClose', () => {
|
||||
const instance = component.instance();
|
||||
it('calls store.save', () => {
|
||||
sinon.spy(instance.store, 'save');
|
||||
|
||||
instance.onSave().then(() => {
|
||||
return instance.onSave().then(() => {
|
||||
expect(instance.store.save).to.have.been.called;
|
||||
instance.store.save.restore();
|
||||
});
|
||||
});
|
||||
|
||||
it('closes the dialog on success', () => {
|
||||
return instance.onSave().then(() => {
|
||||
expect(onClose).to.have.been.called;
|
||||
});
|
||||
});
|
||||
|
||||
it('adds newError on failure', () => {
|
||||
sinon.stub(instance.store, 'save').rejects('test');
|
||||
|
||||
return instance.onSave().then(() => {
|
||||
expect(reduxStore.dispatch).to.have.been.calledWith({ error: new Error('test'), type: 'newError' });
|
||||
instance.store.save.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -50,8 +50,19 @@ function createApi () {
|
||||
};
|
||||
}
|
||||
|
||||
function createRedux () {
|
||||
return {
|
||||
dispatch: sinon.stub(),
|
||||
subscribe: sinon.stub(),
|
||||
getState: () => {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
ACCOUNT,
|
||||
ADDRESS,
|
||||
createApi
|
||||
createApi,
|
||||
createRedux
|
||||
};
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
import { action, computed, observable, transaction } from 'mobx';
|
||||
|
||||
import { newError } from '~/redux/actions';
|
||||
import { validateName } from '~/util/validation';
|
||||
|
||||
export default class Store {
|
||||
@ -92,8 +91,7 @@ export default class Store {
|
||||
])
|
||||
.catch((error) => {
|
||||
console.error('onSave', error);
|
||||
|
||||
newError(error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,10 @@ import { Tabs, Tab } from 'material-ui/Tabs';
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import { newError, showSnackbar } from '~/redux/actions';
|
||||
import { newError, openSnackbar } from '~/redux/actions';
|
||||
import { Button, Modal, IdentityName, IdentityIcon } from '~/ui';
|
||||
import Form, { Input } from '~/ui/Form';
|
||||
import { CancelIcon, CheckIcon, SendIcon } from '~/ui/Icons';
|
||||
@ -42,13 +44,15 @@ const TABS_ITEM_STYLE = {
|
||||
};
|
||||
|
||||
@observer
|
||||
export default class PasswordManager extends Component {
|
||||
class PasswordManager extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
account: PropTypes.object.isRequired,
|
||||
openSnackbar: PropTypes.func.isRequired,
|
||||
newError: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func
|
||||
}
|
||||
|
||||
@ -336,7 +340,7 @@ export default class PasswordManager extends Component {
|
||||
.changePassword()
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
showSnackbar(
|
||||
this.props.openSnackbar(
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='passwordChange.success'
|
||||
@ -347,7 +351,7 @@ export default class PasswordManager extends Component {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
newError(error);
|
||||
this.props.newError(error);
|
||||
});
|
||||
}
|
||||
|
||||
@ -355,7 +359,19 @@ export default class PasswordManager extends Component {
|
||||
return this.store
|
||||
.testPassword()
|
||||
.catch((error) => {
|
||||
newError(error);
|
||||
this.props.newError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({
|
||||
openSnackbar,
|
||||
newError
|
||||
}, dispatch);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mapDispatchToProps
|
||||
)(PasswordManager);
|
||||
|
@ -20,25 +20,25 @@ import sinon from 'sinon';
|
||||
|
||||
import PasswordManager from './';
|
||||
|
||||
import { ACCOUNT, createApi } from './passwordManager.test.js';
|
||||
import { ACCOUNT, createApi, createRedux } from './passwordManager.test.js';
|
||||
|
||||
let component;
|
||||
let instance;
|
||||
let onClose;
|
||||
let reduxStore;
|
||||
|
||||
function render (props) {
|
||||
onClose = sinon.stub();
|
||||
reduxStore = createRedux();
|
||||
|
||||
component = shallow(
|
||||
<PasswordManager
|
||||
{ ...props }
|
||||
account={ ACCOUNT }
|
||||
onClose={ onClose } />,
|
||||
{
|
||||
context: {
|
||||
api: createApi()
|
||||
}
|
||||
}
|
||||
);
|
||||
{ context: { store: reduxStore } }
|
||||
).find('PasswordManager').shallow({ context: { api: createApi() } });
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
@ -56,24 +56,53 @@ describe('modals/PasswordManager', () => {
|
||||
});
|
||||
|
||||
describe('changePassword', () => {
|
||||
it('calls store.changePassword & props.onClose', () => {
|
||||
const instance = component.instance();
|
||||
it('calls store.changePassword', () => {
|
||||
sinon.spy(instance.store, 'changePassword');
|
||||
|
||||
instance.changePassword().then(() => {
|
||||
return instance.changePassword().then(() => {
|
||||
expect(instance.store.changePassword).to.have.been.called;
|
||||
instance.store.changePassword.restore();
|
||||
});
|
||||
});
|
||||
|
||||
it('closes the dialog on success', () => {
|
||||
return instance.changePassword().then(() => {
|
||||
expect(onClose).to.have.been.called;
|
||||
});
|
||||
});
|
||||
|
||||
it('shows snackbar on success', () => {
|
||||
return instance.changePassword().then(() => {
|
||||
expect(reduxStore.dispatch).to.have.been.calledWithMatch({ type: 'openSnackbar' });
|
||||
});
|
||||
});
|
||||
|
||||
it('adds newError on failure', () => {
|
||||
sinon.stub(instance.store, 'changePassword').rejects('test');
|
||||
|
||||
return instance.changePassword().then(() => {
|
||||
expect(reduxStore.dispatch).to.have.been.calledWith({ error: new Error('test'), type: 'newError' });
|
||||
instance.store.changePassword.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('testPassword', () => {
|
||||
it('calls store.testPassword', () => {
|
||||
const instance = component.instance();
|
||||
sinon.spy(instance.store, 'testPassword');
|
||||
|
||||
instance.testPassword().then(() => {
|
||||
return instance.testPassword().then(() => {
|
||||
expect(instance.store.testPassword).to.have.been.called;
|
||||
instance.store.testPassword.restore();
|
||||
});
|
||||
});
|
||||
|
||||
it('adds newError on failure', () => {
|
||||
sinon.stub(instance.store, 'testPassword').rejects('test');
|
||||
|
||||
return instance.testPassword().then(() => {
|
||||
expect(reduxStore.dispatch).to.have.been.calledWith({ error: new Error('test'), type: 'newError' });
|
||||
instance.store.testPassword.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -37,7 +37,18 @@ function createApi (result = true) {
|
||||
};
|
||||
}
|
||||
|
||||
function createRedux () {
|
||||
return {
|
||||
dispatch: sinon.stub(),
|
||||
subscribe: sinon.stub(),
|
||||
getState: () => {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
ACCOUNT,
|
||||
createApi
|
||||
createApi,
|
||||
createRedux
|
||||
};
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import { newError } from '~/ui/Errors/actions';
|
||||
import { setAddressImage } from './providers/imagesActions';
|
||||
import { showSnackbar } from './providers/snackbarActions';
|
||||
import { openSnackbar, showSnackbar } from './providers/snackbarActions';
|
||||
import { clearStatusLogs, toggleStatusLogs, toggleStatusRefresh } from './providers/statusActions';
|
||||
import { toggleView } from '~/views/Settings/actions';
|
||||
|
||||
@ -24,6 +24,7 @@ export {
|
||||
newError,
|
||||
clearStatusLogs,
|
||||
setAddressImage,
|
||||
openSnackbar,
|
||||
showSnackbar,
|
||||
toggleStatusLogs,
|
||||
toggleStatusRefresh,
|
||||
|
@ -20,7 +20,7 @@ export function showSnackbar (message, cooldown) {
|
||||
};
|
||||
}
|
||||
|
||||
function openSnackbar (message, cooldown) {
|
||||
export function openSnackbar (message, cooldown) {
|
||||
return {
|
||||
type: 'openSnackbar',
|
||||
message, cooldown
|
||||
|
Loading…
Reference in New Issue
Block a user