diff --git a/js/src/api/util/identity.js b/js/src/api/util/identity.js
index e4a95891f..48a683989 100644
--- a/js/src/api/util/identity.js
+++ b/js/src/api/util/identity.js
@@ -21,7 +21,7 @@ const TEST_ENV = process.env.NODE_ENV === 'test';
export function createIdentityImg (address, scale = 8) {
return TEST_ENV
- ? ''
+ ? 'test-createIdentityImg'
: blockies({
seed: (address || '').toLowerCase(),
size: 8,
diff --git a/js/src/ui/BlockStatus/blockStatus.js b/js/src/ui/BlockStatus/blockStatus.js
index f50c7a685..47ee1a1c8 100644
--- a/js/src/ui/BlockStatus/blockStatus.js
+++ b/js/src/ui/BlockStatus/blockStatus.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';
@@ -39,7 +40,12 @@ class BlockStatus extends Component {
if (!syncing) {
return (
- { blockNumber.toFormat() } best block
+
);
}
@@ -47,26 +53,45 @@ class BlockStatus extends Component {
if (syncing.warpChunksAmount && syncing.warpChunksProcessed && !syncing.warpChunksAmount.eq(syncing.warpChunksProcessed)) {
return (
- { syncing.warpChunksProcessed.mul(100).div(syncing.warpChunksAmount).toFormat(2) }% warp restore
+
);
}
+ let syncStatus = null;
let warpStatus = null;
+ if (syncing.currentBlock && syncing.highestBlock) {
+ syncStatus = (
+
+
+
+ );
+ }
+
if (syncing.blockGap) {
const [first, last] = syncing.blockGap;
warpStatus = (
- , { first.mul(100).div(last).toFormat(2) }% historic
- );
- }
-
- let syncStatus = null;
-
- if (syncing && syncing.currentBlock && syncing.highestBlock) {
- syncStatus = (
- { syncing.currentBlock.toFormat() }/{ syncing.highestBlock.toFormat() } syncing
+
+
+
);
}
diff --git a/js/src/ui/BlockStatus/blockStatus.spec.js b/js/src/ui/BlockStatus/blockStatus.spec.js
new file mode 100644
index 000000000..73358dc90
--- /dev/null
+++ b/js/src/ui/BlockStatus/blockStatus.spec.js
@@ -0,0 +1,94 @@
+// 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 .
+
+import BigNumber from 'bignumber.js';
+import { shallow } from 'enzyme';
+import React from 'react';
+import sinon from 'sinon';
+
+import BlockStatus from './';
+
+let component;
+
+function createRedux (syncing = false, blockNumber = new BigNumber(123)) {
+ return {
+ dispatch: sinon.stub(),
+ subscribe: sinon.stub(),
+ getState: () => {
+ return {
+ nodeStatus: {
+ blockNumber,
+ syncing
+ }
+ };
+ }
+ };
+}
+
+function render (reduxStore = createRedux(), props) {
+ component = shallow(
+ ,
+ { context: { store: reduxStore } }
+ ).find('BlockStatus').shallow();
+
+ return component;
+}
+
+describe('ui/BlockStatus', () => {
+ it('renders defaults', () => {
+ expect(render()).to.be.ok;
+ });
+
+ it('renders null with no blockNumber', () => {
+ expect(render(createRedux(false, null)).find('div')).to.have.length(0);
+ });
+
+ it('renders only the best block when syncing === false', () => {
+ const messages = render().find('FormattedMessage');
+
+ expect(messages).to.have.length(1);
+ expect(messages).to.have.id('ui.blockStatus.bestBlock');
+ });
+
+ it('renders only the warp restore status when restoring', () => {
+ const messages = render(createRedux({
+ warpChunksAmount: new BigNumber(100),
+ warpChunksProcessed: new BigNumber(5)
+ })).find('FormattedMessage');
+
+ expect(messages).to.have.length(1);
+ expect(messages).to.have.id('ui.blockStatus.warpRestore');
+ });
+
+ it('renders the current/highest when syncing', () => {
+ const messages = render(createRedux({
+ currentBlock: new BigNumber(123),
+ highestBlock: new BigNumber(456)
+ })).find('FormattedMessage');
+
+ expect(messages).to.have.length(1);
+ expect(messages).to.have.id('ui.blockStatus.syncStatus');
+ });
+
+ it('renders warp blockGap when catching up', () => {
+ const messages = render(createRedux({
+ blockGap: [new BigNumber(123), new BigNumber(456)]
+ })).find('FormattedMessage');
+
+ expect(messages).to.have.length(1);
+ expect(messages).to.have.id('ui.blockStatus.warpStatus');
+ });
+});
diff --git a/js/src/ui/Button/button.spec.js b/js/src/ui/Button/button.spec.js
index 101bb19ac..ce0a8bd38 100644
--- a/js/src/ui/Button/button.spec.js
+++ b/js/src/ui/Button/button.spec.js
@@ -19,7 +19,11 @@ import { shallow } from 'enzyme';
import Button from './button';
-function renderShallow (props) {
+function render (props = {}) {
+ if (props && props.label === undefined) {
+ props.label = 'test';
+ }
+
return shallow(
);
@@ -28,11 +32,11 @@ function renderShallow (props) {
describe('ui/Button', () => {
describe('rendering', () => {
it('renders defaults', () => {
- expect(renderShallow()).to.be.ok;
+ expect(render()).to.be.ok;
});
it('renders with the specified className', () => {
- expect(renderShallow({ className: 'testClass' })).to.have.className('testClass');
+ expect(render({ className: 'testClass' })).to.have.className('testClass');
});
});
});
diff --git a/js/src/ui/ConfirmDialog/confirmDialog.js b/js/src/ui/ConfirmDialog/confirmDialog.js
index 103c1562d..5035ced03 100644
--- a/js/src/ui/ConfirmDialog/confirmDialog.js
+++ b/js/src/ui/ConfirmDialog/confirmDialog.js
@@ -15,16 +15,27 @@
// along with Parity. If not, see .
import React, { Component, PropTypes } from 'react';
-import ActionDone from 'material-ui/svg-icons/action/done';
-import ContentClear from 'material-ui/svg-icons/content/clear';
+import { FormattedMessage } from 'react-intl';
import { nodeOrStringProptype } from '~/util/proptypes';
import Button from '../Button';
import Modal from '../Modal';
+import { CancelIcon, CheckIcon } from '../Icons';
import styles from './confirmDialog.css';
+const DEFAULT_NO = (
+
+);
+const DEFAULT_YES = (
+
+);
+
export default class ConfirmDialog extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
@@ -33,10 +44,10 @@ export default class ConfirmDialog extends Component {
iconDeny: PropTypes.node,
labelConfirm: PropTypes.string,
labelDeny: PropTypes.string,
- title: nodeOrStringProptype().isRequired,
- visible: PropTypes.bool.isRequired,
onConfirm: PropTypes.func.isRequired,
- onDeny: PropTypes.func.isRequired
+ onDeny: PropTypes.func.isRequired,
+ title: nodeOrStringProptype().isRequired,
+ visible: PropTypes.bool.isRequired
}
render () {
@@ -60,12 +71,12 @@ export default class ConfirmDialog extends Component {
return [
}
+ icon={ iconDeny || }
+ label={ labelDeny || DEFAULT_NO }
onClick={ onDeny } />,
}
+ icon={ iconConfirm || }
+ label={ labelConfirm || DEFAULT_YES }
onClick={ onConfirm } />
];
}
diff --git a/js/src/ui/ConfirmDialog/confirmDialog.spec.js b/js/src/ui/ConfirmDialog/confirmDialog.spec.js
new file mode 100644
index 000000000..6affa4cbf
--- /dev/null
+++ b/js/src/ui/ConfirmDialog/confirmDialog.spec.js
@@ -0,0 +1,157 @@
+// 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 .
+
+import { shallow } from 'enzyme';
+import React, { PropTypes } from 'react';
+import sinon from 'sinon';
+
+import muiTheme from '../Theme';
+
+import ConfirmDialog from './';
+
+let component;
+let instance;
+let onConfirm;
+let onDeny;
+
+function createRedux () {
+ return {
+ dispatch: sinon.stub(),
+ subscribe: sinon.stub(),
+ getState: () => {
+ return {
+ settings: {
+ backgroundSeed: 'xyz'
+ }
+ };
+ }
+ };
+}
+
+function render (props = {}) {
+ onConfirm = sinon.stub();
+ onDeny = sinon.stub();
+
+ if (props.visible === undefined) {
+ props.visible = true;
+ }
+
+ const baseComponent = shallow(
+
+
+ some test content
+
+
+ );
+
+ instance = baseComponent.instance();
+ component = baseComponent.find('Connect(Modal)').shallow({
+ childContextTypes: {
+ muiTheme: PropTypes.object,
+ store: PropTypes.object
+ },
+ context: {
+ muiTheme,
+ store: createRedux()
+ }
+ });
+
+ return component;
+}
+
+describe('ui/ConfirmDialog', () => {
+ it('renders defaults', () => {
+ expect(render()).to.be.ok;
+ });
+
+ it('renders the body as provided', () => {
+ expect(render().find('div[id="testContent"]').text()).to.equal('some test content');
+ });
+
+ describe('properties', () => {
+ let props;
+
+ beforeEach(() => {
+ props = render().props();
+ });
+
+ it('passes the actions', () => {
+ expect(props.actions).to.deep.equal(instance.renderActions());
+ });
+
+ it('passes title', () => {
+ expect(props.title).to.equal('test title');
+ });
+
+ it('passes visiblity flag', () => {
+ expect(props.visible).to.be.true;
+ });
+ });
+
+ describe('renderActions', () => {
+ describe('defaults', () => {
+ let buttons;
+
+ beforeEach(() => {
+ render();
+ buttons = instance.renderActions();
+ });
+
+ it('renders with supplied onConfim/onDeny callbacks', () => {
+ expect(buttons[0].props.onClick).to.deep.equal(onDeny);
+ expect(buttons[1].props.onClick).to.deep.equal(onConfirm);
+ });
+
+ it('renders default labels', () => {
+ expect(buttons[0].props.label.props.id).to.equal('ui.confirmDialog.no');
+ expect(buttons[1].props.label.props.id).to.equal('ui.confirmDialog.yes');
+ });
+
+ it('renders default icons', () => {
+ expect(buttons[0].props.icon.type.displayName).to.equal('ContentClear');
+ expect(buttons[1].props.icon.type.displayName).to.equal('NavigationCheck');
+ });
+ });
+
+ describe('overrides', () => {
+ let buttons;
+
+ beforeEach(() => {
+ render({
+ labelConfirm: 'labelConfirm',
+ labelDeny: 'labelDeny',
+ iconConfirm: 'iconConfirm',
+ iconDeny: 'iconDeny'
+ });
+ buttons = instance.renderActions();
+ });
+
+ it('renders supplied labels', () => {
+ expect(buttons[0].props.label).to.equal('labelDeny');
+ expect(buttons[1].props.label).to.equal('labelConfirm');
+ });
+
+ it('renders supplied icons', () => {
+ expect(buttons[0].props.icon).to.equal('iconDeny');
+ expect(buttons[1].props.icon).to.equal('iconConfirm');
+ });
+ });
+ });
+});
diff --git a/js/src/ui/Container/container.spec.js b/js/src/ui/Container/container.spec.js
index 5e627999f..105cea164 100644
--- a/js/src/ui/Container/container.spec.js
+++ b/js/src/ui/Container/container.spec.js
@@ -19,7 +19,7 @@ import { shallow } from 'enzyme';
import Container from './container';
-function renderShallow (props) {
+function render (props) {
return shallow(
);
@@ -28,11 +28,24 @@ function renderShallow (props) {
describe('ui/Container', () => {
describe('rendering', () => {
it('renders defaults', () => {
- expect(renderShallow()).to.be.ok;
+ expect(render()).to.be.ok;
});
it('renders with the specified className', () => {
- expect(renderShallow({ className: 'testClass' })).to.have.className('testClass');
+ expect(render({ className: 'testClass' })).to.have.className('testClass');
+ });
+ });
+
+ describe('sections', () => {
+ it('renders the Card', () => {
+ expect(render().find('Card')).to.have.length(1);
+ });
+
+ it('renders the Title', () => {
+ const title = render({ title: 'title' }).find('Title');
+
+ expect(title).to.have.length(1);
+ expect(title.props().title).to.equal('title');
});
});
});
diff --git a/js/src/ui/GasPriceEditor/gasPriceEditor.spec.js b/js/src/ui/GasPriceEditor/gasPriceEditor.spec.js
index 336ec5c7e..3a414f90e 100644
--- a/js/src/ui/GasPriceEditor/gasPriceEditor.spec.js
+++ b/js/src/ui/GasPriceEditor/gasPriceEditor.spec.js
@@ -29,6 +29,7 @@ const api = {
const store = {
estimated: '123',
+ histogram: {},
priceDefault: '456',
totalValue: '789',
setGas: sinon.stub(),
diff --git a/js/src/ui/IdentityIcon/identityIcon.js b/js/src/ui/IdentityIcon/identityIcon.js
index 5ab1651ec..d4d65241a 100644
--- a/js/src/ui/IdentityIcon/identityIcon.js
+++ b/js/src/ui/IdentityIcon/identityIcon.js
@@ -34,8 +34,8 @@ class IdentityIcon extends Component {
button: PropTypes.bool,
center: PropTypes.bool,
className: PropTypes.string,
- inline: PropTypes.bool,
images: PropTypes.object.isRequired,
+ inline: PropTypes.bool,
padded: PropTypes.bool,
tiny: PropTypes.bool
}
diff --git a/js/src/ui/IdentityIcon/identityIcon.spec.js b/js/src/ui/IdentityIcon/identityIcon.spec.js
new file mode 100644
index 000000000..759907deb
--- /dev/null
+++ b/js/src/ui/IdentityIcon/identityIcon.spec.js
@@ -0,0 +1,120 @@
+// 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 .
+
+import { mount } from 'enzyme';
+import React, { PropTypes } from 'react';
+import sinon from 'sinon';
+
+import muiTheme from '../Theme';
+
+import IdentityIcon from './';
+
+const ADDRESS0 = '0x0000000000000000000000000000000000000000';
+const ADDRESS1 = '0x0123456789012345678901234567890123456789';
+const ADDRESS2 = '0x9876543210987654321098765432109876543210';
+
+let component;
+
+function createApi () {
+ return {
+ dappsUrl: 'dappsUrl/'
+ };
+}
+
+function createRedux () {
+ return {
+ dispatch: sinon.stub(),
+ subscribe: sinon.stub(),
+ getState: () => {
+ return {
+ images: {
+ [ADDRESS2]: 'reduxImage'
+ }
+ };
+ }
+ };
+}
+
+function render (props = {}) {
+ if (props && props.address === undefined) {
+ props.address = ADDRESS1;
+ }
+
+ component = mount(
+ ,
+ {
+ childContextTypes: {
+ api: PropTypes.object,
+ muiTheme: PropTypes.object
+ },
+ context: {
+ api: createApi(),
+ muiTheme,
+ store: createRedux()
+ }
+ }
+ );
+
+ return component;
+}
+
+describe('ui/IdentityIcon', () => {
+ it('renders defaults', () => {
+ expect(render()).to.be.ok;
+ });
+
+ describe('images', () => {
+ it('renders an with address specified', () => {
+ const img = render().find('img');
+
+ expect(img).to.have.length(1);
+ expect(img.props().src).to.equal('test-createIdentityImg');
+ });
+
+ it('renders an with redux source when available', () => {
+ const img = render({ address: ADDRESS2 }).find('img');
+
+ expect(img).to.have.length(1);
+ expect(img.props().src).to.equal('dappsUrl/reduxImage');
+ });
+
+ it('renders an with no address specified', () => {
+ expect(render({ address: null }).find('ActionCode')).to.have.length(1);
+ });
+
+ it('renders an with 0x00..00 address specified', () => {
+ expect(render({ address: ADDRESS0 }).find('ContentClear')).to.have.length(1);
+ });
+ });
+
+ describe('sizes', () => {
+ it('renders 56px by default', () => {
+ expect(render().find('img').props().width).to.equal('56px');
+ });
+
+ it('renders 16px for tiny', () => {
+ expect(render({ tiny: true }).find('img').props().width).to.equal('16px');
+ });
+
+ it('renders 24px for button', () => {
+ expect(render({ button: true }).find('img').props().width).to.equal('24px');
+ });
+
+ it('renders 32px for inline', () => {
+ expect(render({ inline: true }).find('img').props().width).to.equal('32px');
+ });
+ });
+});
diff --git a/js/src/ui/IdentityName/identityName.js b/js/src/ui/IdentityName/identityName.js
index 45e864a75..980f42638 100644
--- a/js/src/ui/IdentityName/identityName.js
+++ b/js/src/ui/IdentityName/identityName.js
@@ -15,13 +15,23 @@
// 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';
import { isNullAddress } from '~/util/validation';
import ShortenedHash from '../ShortenedHash';
-const defaultName = 'UNNAMED';
+const defaultName = (
+
+);
+const defaultNameNull = (
+
+);
class IdentityName extends Component {
static propTypes = {
@@ -43,7 +53,7 @@ class IdentityName extends Component {
return null;
}
- const nullName = isNullAddress(address) ? 'null' : null;
+ const nullName = isNullAddress(address) ? defaultNameNull : null;
const addressFallback = nullName || (shorten ? () : address);
const fallback = unknown ? defaultName : addressFallback;
const isUuid = account && account.name === account.uuid;
diff --git a/js/src/ui/IdentityName/identityName.spec.js b/js/src/ui/IdentityName/identityName.spec.js
index bb0b55e46..12bd07363 100644
--- a/js/src/ui/IdentityName/identityName.spec.js
+++ b/js/src/ui/IdentityName/identityName.spec.js
@@ -14,8 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-import React from 'react';
import { mount } from 'enzyme';
+import React from 'react';
+import { IntlProvider } from 'react-intl';
+
import sinon from 'sinon';
import IdentityName from './identityName';
@@ -44,9 +46,11 @@ const STORE = {
function render (props) {
return mount(
-
+
+
+
);
}
@@ -74,7 +78,7 @@ describe('ui/IdentityName', () => {
});
it('renders 0x000...000 as null', () => {
- expect(render({ address: ADDR_NULL }).text()).to.equal('null');
+ expect(render({ address: ADDR_NULL }).text()).to.equal('NULL');
});
});
});
diff --git a/js/src/ui/TxHash/txHash.js b/js/src/ui/TxHash/txHash.js
index 09905d594..df5e9342e 100644
--- a/js/src/ui/TxHash/txHash.js
+++ b/js/src/ui/TxHash/txHash.js
@@ -16,6 +16,7 @@
import BigNumber from 'bignumber.js';
import React, { Component, PropTypes } from 'react';
+import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { LinearProgress } from 'material-ui';
@@ -33,8 +34,8 @@ class TxHash extends Component {
static propTypes = {
hash: PropTypes.string.isRequired,
isTest: PropTypes.bool,
- summary: PropTypes.bool,
- maxConfirmations: PropTypes.number
+ maxConfirmations: PropTypes.number,
+ summary: PropTypes.bool
}
static defaultProps = {
@@ -43,14 +44,14 @@ class TxHash extends Component {
state = {
blockNumber: new BigNumber(0),
- transaction: null,
- subscriptionId: null
+ subscriptionId: null,
+ transaction: null
}
componentDidMount () {
const { api } = this.context;
- api.subscribe('eth_blockNumber', this.onBlockNumber).then((subscriptionId) => {
+ return api.subscribe('eth_blockNumber', this.onBlockNumber).then((subscriptionId) => {
this.setState({ subscriptionId });
});
}
@@ -59,28 +60,28 @@ class TxHash extends Component {
const { api } = this.context;
const { subscriptionId } = this.state;
- api.unsubscribe(subscriptionId);
+ return api.unsubscribe(subscriptionId);
}
render () {
const { hash, isTest, summary } = this.props;
- const link = (
+ const hashLink = (
);
- let header = (
- The transaction has been posted to the network, with a hash of { link }.
- );
- if (summary) {
- header = ({ link }
);
- }
-
return (
- { header }
+
{
+ summary
+ ? hashLink
+ :
+ }
{ this.renderConfirmations() }
);
@@ -98,20 +99,22 @@ class TxHash extends Component {
color='white'
mode='indeterminate'
/>
- waiting for confirmations
+
+
+
);
}
const confirmations = blockNumber.minus(transaction.blockNumber).plus(1);
const value = Math.min(confirmations.toNumber(), maxConfirmations);
- let count;
- if (confirmations.gt(maxConfirmations)) {
- count = confirmations.toFormat(0);
- } else {
- count = confirmations.toFormat(0) + `/${maxConfirmations}`;
+
+ let count = confirmations.toFormat(0);
+ if (confirmations.lte(maxConfirmations)) {
+ count = `${count}/${maxConfirmations}`;
}
- const unit = value === 1 ? 'confirmation' : 'confirmations';
return (
@@ -121,10 +124,17 @@ class TxHash extends Component {
max={ maxConfirmations }
value={ value }
color='white'
- mode='determinate'
- />
+ mode='determinate' />
-
{ count } { unit }
+
+
+
);
@@ -138,15 +148,17 @@ class TxHash extends Component {
return;
}
- this.setState({ blockNumber });
-
- api.eth
+ return api.eth
.getTransactionReceipt(hash)
.then((transaction) => {
- this.setState({ transaction });
+ this.setState({
+ blockNumber,
+ transaction
+ });
})
.catch((error) => {
console.warn('onBlockNumber', error);
+ this.setState({ blockNumber });
});
}
}
diff --git a/js/src/ui/TxHash/txHash.spec.js b/js/src/ui/TxHash/txHash.spec.js
new file mode 100644
index 000000000..7f47363ee
--- /dev/null
+++ b/js/src/ui/TxHash/txHash.spec.js
@@ -0,0 +1,132 @@
+// 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 .
+
+import BigNumber from 'bignumber.js';
+import { shallow } from 'enzyme';
+import React from 'react';
+import sinon from 'sinon';
+
+import TxHash from './';
+
+const TXHASH = '0xabcdef123454321abcdef';
+
+let api;
+let blockNumber;
+let callback;
+let component;
+let instance;
+
+function createApi () {
+ blockNumber = new BigNumber(100);
+ api = {
+ eth: {
+ getTransactionReceipt: (hash) => {
+ return Promise.resolve({
+ blockNumber: new BigNumber(100),
+ hash
+ });
+ }
+ },
+ nextBlock: (increment = 1) => {
+ blockNumber = blockNumber.plus(increment);
+ return callback(null, blockNumber);
+ },
+ subscribe: (type, _callback) => {
+ callback = _callback;
+ return callback(null, blockNumber).then(() => {
+ return Promise.resolve(1);
+ });
+ },
+ unsubscribe: sinon.stub().resolves(true)
+ };
+
+ return api;
+}
+
+function createRedux () {
+ return {
+ dispatch: sinon.stub(),
+ subscribe: sinon.stub(),
+ getState: () => {
+ return {
+ nodeStatus: { isTest: true }
+ };
+ }
+ };
+}
+
+function render (props) {
+ const baseComponent = shallow(
+ ,
+ { context: { store: createRedux() } }
+ );
+ component = baseComponent.find('TxHash').shallow({ context: { api: createApi() } });
+ instance = component.instance();
+
+ return component;
+}
+
+describe('ui/TxHash', () => {
+ beforeEach(() => {
+ render();
+ });
+
+ it('renders defaults', () => {
+ expect(component).to.be.ok;
+ });
+
+ it('renders the summary', () => {
+ expect(component.find('p').find('FormattedMessage').props().id).to.equal('ui.txHash.posted');
+ });
+
+ describe('renderConfirmations', () => {
+ describe('with no transaction retrieved', () => {
+ let child;
+
+ beforeEach(() => {
+ child = shallow(instance.renderConfirmations());
+ });
+
+ it('renders indeterminate progressbar', () => {
+ expect(child.find('LinearProgress[mode="indeterminate"]')).to.have.length(1);
+ });
+
+ it('renders waiting text', () => {
+ expect(child.find('FormattedMessage').props().id).to.equal('ui.txHash.waiting');
+ });
+ });
+
+ describe('with transaction retrieved', () => {
+ let child;
+
+ beforeEach(() => {
+ return instance.componentDidMount().then(() => {
+ child = shallow(instance.renderConfirmations());
+ });
+ });
+
+ it('renders determinate progressbar', () => {
+ expect(child.find('LinearProgress[mode="determinate"]')).to.have.length(1);
+ });
+
+ it('renders confirmation text', () => {
+ expect(child.find('FormattedMessage').props().id).to.equal('ui.txHash.confirmations');
+ });
+ });
+ });
+});
diff --git a/js/src/ui/TxList/TxRow/txRow.spec.js b/js/src/ui/TxList/TxRow/txRow.spec.js
index ddee9024c..030ff4432 100644
--- a/js/src/ui/TxList/TxRow/txRow.spec.js
+++ b/js/src/ui/TxList/TxRow/txRow.spec.js
@@ -25,7 +25,7 @@ import TxRow from './txRow';
const api = new Api({ execute: sinon.stub() });
-function renderShallow (props) {
+function render (props) {
return shallow(
,
@@ -33,7 +33,7 @@ function renderShallow (props) {
);
}
-describe('ui/TxRow', () => {
+describe('ui/TxList/TxRow', () => {
describe('rendering', () => {
it('renders defaults', () => {
const block = {
@@ -45,7 +45,7 @@ describe('ui/TxRow', () => {
value: new BigNumber(1)
};
- expect(renderShallow({ block, tx })).to.be.ok;
+ expect(render({ address: '0x123', block, isTest: true, tx })).to.be.ok;
});
});
});
diff --git a/js/src/ui/TxList/txList.spec.js b/js/src/ui/TxList/txList.spec.js
index 88012888c..11367fbce 100644
--- a/js/src/ui/TxList/txList.spec.js
+++ b/js/src/ui/TxList/txList.spec.js
@@ -36,7 +36,7 @@ const STORE = {
}
};
-function renderShallow (props) {
+function render (props) {
return shallow(
{
describe('rendering', () => {
it('renders defaults', () => {
- expect(renderShallow()).to.be.ok;
+ expect(render({ address: '0x123', hashes: [] })).to.be.ok;
});
});
});