Merge pull request #3743 from ethcore/jg-test-ui
Basic UI rendering tests
This commit is contained in:
commit
341777dbb6
@ -17,6 +17,15 @@
|
||||
},
|
||||
"development": {
|
||||
"plugins": ["react-hot-loader/babel"]
|
||||
},
|
||||
"test": {
|
||||
"plugins": [
|
||||
[
|
||||
"babel-plugin-webpack-alias", {
|
||||
"config": "webpack/test.js"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,9 +40,9 @@
|
||||
"coveralls": "npm run testCoverage && coveralls < coverage/lcov.info",
|
||||
"lint": "eslint --ignore-path .gitignore ./src/",
|
||||
"lint:cached": "eslint --cache --ignore-path .gitignore ./src/",
|
||||
"test": "mocha 'src/**/*.spec.js'",
|
||||
"test:coverage": "istanbul cover _mocha -- 'src/**/*.spec.js'",
|
||||
"test:e2e": "mocha 'src/**/*.e2e.js'",
|
||||
"test": "NODE_ENV=test mocha 'src/**/*.spec.js'",
|
||||
"test:coverage": "NODE_ENV=test istanbul cover _mocha -- 'src/**/*.spec.js'",
|
||||
"test:e2e": "NODE_ENV=test mocha 'src/**/*.e2e.js'",
|
||||
"test:npm": "(cd .npmjs && npm i) && node test/npmLibrary && (rm -rf .npmjs/node_modules)",
|
||||
"prepush": "npm run lint:cached"
|
||||
},
|
||||
@ -57,6 +57,7 @@
|
||||
"babel-plugin-transform-object-rest-spread": "6.20.2",
|
||||
"babel-plugin-transform-react-remove-prop-types": "0.2.11",
|
||||
"babel-plugin-transform-runtime": "6.15.0",
|
||||
"babel-plugin-webpack-alias": "2.1.2",
|
||||
"babel-polyfill": "6.20.0",
|
||||
"babel-preset-es2015": "6.18.0",
|
||||
"babel-preset-es2016": "6.16.0",
|
||||
@ -66,6 +67,7 @@
|
||||
"babel-register": "6.18.0",
|
||||
"babel-runtime": "6.20.0",
|
||||
"chai": "3.5.0",
|
||||
"chai-as-promised": "6.0.0",
|
||||
"chai-enzyme": "0.6.1",
|
||||
"circular-dependency-plugin": "2.0.0",
|
||||
"copy-webpack-plugin": "4.0.1",
|
||||
|
@ -21,7 +21,7 @@ import '../../../environment/tests';
|
||||
|
||||
import Application from './application';
|
||||
|
||||
describe('localtx/Application', () => {
|
||||
describe('dapps/localtx/Application', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders without crashing', () => {
|
||||
const rendered = shallow(<Application />);
|
||||
|
@ -29,7 +29,7 @@ Api.api = {
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { Transaction, LocalTransaction } from './transaction';
|
||||
|
||||
describe('localtx/Transaction', () => {
|
||||
describe('dapps/localtx/Transaction', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders without crashing', () => {
|
||||
const transaction = {
|
||||
@ -51,7 +51,7 @@ describe('localtx/Transaction', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('localtx/LocalTransaction', () => {
|
||||
describe('dapps/localtx/LocalTransaction', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders without crashing', () => {
|
||||
const rendered = shallow(
|
||||
|
38
js/src/ui/Actionbar/actionbar.spec.js
Normal file
38
js/src/ui/Actionbar/actionbar.spec.js
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import Actionbar from './actionbar';
|
||||
|
||||
function renderShallow (props) {
|
||||
return shallow(
|
||||
<Actionbar { ...props } />
|
||||
);
|
||||
}
|
||||
|
||||
describe('ui/Actionbar', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders defaults', () => {
|
||||
expect(renderShallow()).to.be.ok;
|
||||
});
|
||||
|
||||
it('renders with the specified className', () => {
|
||||
expect(renderShallow({ className: 'testClass' })).to.have.className('testClass');
|
||||
});
|
||||
});
|
||||
});
|
38
js/src/ui/Badge/badge.spec.js
Normal file
38
js/src/ui/Badge/badge.spec.js
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import Badge from './badge';
|
||||
|
||||
function renderShallow (props) {
|
||||
return shallow(
|
||||
<Badge { ...props } />
|
||||
);
|
||||
}
|
||||
|
||||
describe('ui/Badge', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders defaults', () => {
|
||||
expect(renderShallow()).to.be.ok;
|
||||
});
|
||||
|
||||
it('renders with the specified className', () => {
|
||||
expect(renderShallow({ className: 'testClass' })).to.have.className('testClass');
|
||||
});
|
||||
});
|
||||
});
|
@ -17,16 +17,15 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FlatButton } from 'material-ui';
|
||||
|
||||
import { nodeOrStringProptype } from '~/util/proptypes';
|
||||
|
||||
export default class Button extends Component {
|
||||
static propTypes = {
|
||||
backgroundColor: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
icon: PropTypes.node,
|
||||
label: PropTypes.oneOfType([
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.object
|
||||
]),
|
||||
label: nodeOrStringProptype(),
|
||||
onClick: PropTypes.func,
|
||||
primary: PropTypes.bool
|
||||
}
|
||||
|
38
js/src/ui/Button/button.spec.js
Normal file
38
js/src/ui/Button/button.spec.js
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import Button from './button';
|
||||
|
||||
function renderShallow (props) {
|
||||
return shallow(
|
||||
<Button { ...props } />
|
||||
);
|
||||
}
|
||||
|
||||
describe('ui/Button', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders defaults', () => {
|
||||
expect(renderShallow()).to.be.ok;
|
||||
});
|
||||
|
||||
it('renders with the specified className', () => {
|
||||
expect(renderShallow({ className: 'testClass' })).to.have.className('testClass');
|
||||
});
|
||||
});
|
||||
});
|
@ -27,10 +27,6 @@ export default class Title extends Component {
|
||||
byline: nodeOrStringProptype()
|
||||
}
|
||||
|
||||
state = {
|
||||
name: 'Unnamed'
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, title, byline } = this.props;
|
||||
|
||||
|
52
js/src/ui/Container/Title/title.spec.js
Normal file
52
js/src/ui/Container/Title/title.spec.js
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 React from 'react';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
|
||||
import Title from './title';
|
||||
|
||||
function renderShallow (props) {
|
||||
return shallow(
|
||||
<Title { ...props } />
|
||||
);
|
||||
}
|
||||
|
||||
function renderMount (props) {
|
||||
return mount(
|
||||
<Title { ...props } />
|
||||
);
|
||||
}
|
||||
|
||||
describe('ui/Container/Title', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders defaults', () => {
|
||||
expect(renderShallow()).to.be.ok;
|
||||
});
|
||||
|
||||
it('renders with the specified className', () => {
|
||||
expect(renderShallow({ className: 'testClass' })).to.have.className('testClass');
|
||||
});
|
||||
|
||||
it('renders the specified title', () => {
|
||||
expect(renderMount({ title: 'titleText' })).to.contain.text('titleText');
|
||||
});
|
||||
|
||||
it('renders the specified byline', () => {
|
||||
expect(renderMount({ byline: 'bylineText' })).to.contain.text('bylineText');
|
||||
});
|
||||
});
|
||||
});
|
38
js/src/ui/Container/container.spec.js
Normal file
38
js/src/ui/Container/container.spec.js
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import Container from './container';
|
||||
|
||||
function renderShallow (props) {
|
||||
return shallow(
|
||||
<Container { ...props } />
|
||||
);
|
||||
}
|
||||
|
||||
describe('ui/Container', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders defaults', () => {
|
||||
expect(renderShallow()).to.be.ok;
|
||||
});
|
||||
|
||||
it('renders with the specified className', () => {
|
||||
expect(renderShallow({ className: 'testClass' })).to.have.className('testClass');
|
||||
});
|
||||
});
|
||||
});
|
76
js/src/ui/IdentityName/identityName.spec.js
Normal file
76
js/src/ui/IdentityName/identityName.spec.js
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import IdentityName from './identityName';
|
||||
|
||||
const ADDR_A = '0x123456789abcdef0123456789A';
|
||||
const ADDR_B = '0x123456789abcdef0123456789B';
|
||||
const ADDR_C = '0x123456789abcdef0123456789C';
|
||||
const STORE = {
|
||||
dispatch: sinon.stub(),
|
||||
subscribe: sinon.stub(),
|
||||
getState: () => {
|
||||
return {
|
||||
balances: {
|
||||
tokens: {}
|
||||
},
|
||||
personal: {
|
||||
accountsInfo: {
|
||||
[ADDR_A]: { name: 'testing' },
|
||||
[ADDR_B]: {}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function render (props) {
|
||||
return mount(
|
||||
<IdentityName
|
||||
store={ STORE }
|
||||
{ ...props } />
|
||||
);
|
||||
}
|
||||
|
||||
describe('ui/IdentityName', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders defaults', () => {
|
||||
expect(render()).to.be.ok;
|
||||
});
|
||||
|
||||
describe('account not found', () => {
|
||||
it('renders null with empty', () => {
|
||||
expect(render({ address: ADDR_C, empty: true }).html()).to.be.null;
|
||||
});
|
||||
|
||||
it('renders address without empty', () => {
|
||||
expect(render({ address: ADDR_C }).text()).to.equal(ADDR_C);
|
||||
});
|
||||
|
||||
it('renders short address with shorten', () => {
|
||||
expect(render({ address: ADDR_C, shorten: true }).text()).to.equal('123456…56789c');
|
||||
});
|
||||
|
||||
it('renders unknown with flag', () => {
|
||||
expect(render({ address: ADDR_C, unknown: true }).text()).to.equal('UNNAMED');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
55
js/src/ui/Theme/theme.spec.js
Normal file
55
js/src/ui/Theme/theme.spec.js
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 getMuiTheme from 'material-ui/styles/getMuiTheme';
|
||||
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
|
||||
|
||||
const muiTheme = getMuiTheme(lightBaseTheme);
|
||||
|
||||
import theme from './theme';
|
||||
|
||||
describe('ui/Theme', () => {
|
||||
it('is MUI-based', () => {
|
||||
expect(Object.keys(theme)).to.deep.equal(Object.keys(muiTheme).concat('parity'));
|
||||
});
|
||||
|
||||
it('allows setting of Parity backgrounds', () => {
|
||||
expect(typeof theme.parity.setBackgroundSeed === 'function').to.be.true;
|
||||
expect(typeof theme.parity.getBackgroundStyle === 'function').to.be.true;
|
||||
});
|
||||
|
||||
describe('parity', () => {
|
||||
describe('setBackgroundSeed', () => {
|
||||
const SEED = 'testseed';
|
||||
|
||||
beforeEach(() => {
|
||||
theme.parity.setBackgroundSeed(SEED);
|
||||
});
|
||||
|
||||
it('sets the correct theme values', () => {
|
||||
expect(theme.parity.backgroundSeed).to.equal(SEED);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBackgroundStyle', () => {
|
||||
it('generates a style containing background', () => {
|
||||
const style = theme.parity.getBackgroundStyle();
|
||||
|
||||
expect(style).to.have.property('background');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
17
js/src/ui/TxList/TxRow/index.js
Normal file
17
js/src/ui/TxList/TxRow/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
export default from './txRow';
|
133
js/src/ui/TxList/TxRow/txRow.js
Normal file
133
js/src/ui/TxList/TxRow/txRow.js
Normal file
@ -0,0 +1,133 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 moment from 'moment';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { txLink, addressLink } from '~/3rdparty/etherscan/links';
|
||||
|
||||
import IdentityIcon from '../../IdentityIcon';
|
||||
import IdentityName from '../../IdentityName';
|
||||
import MethodDecoding from '../../MethodDecoding';
|
||||
|
||||
import styles from '../txList.css';
|
||||
|
||||
export default class TxRow extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
tx: PropTypes.object.isRequired,
|
||||
address: PropTypes.string.isRequired,
|
||||
isTest: PropTypes.bool.isRequired,
|
||||
|
||||
block: PropTypes.object,
|
||||
historic: PropTypes.bool,
|
||||
className: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
historic: true
|
||||
};
|
||||
|
||||
render () {
|
||||
const { tx, address, isTest, historic, className } = this.props;
|
||||
|
||||
return (
|
||||
<tr className={ className || '' }>
|
||||
{ this.renderBlockNumber(tx.blockNumber) }
|
||||
{ this.renderAddress(tx.from) }
|
||||
<td className={ styles.transaction }>
|
||||
{ this.renderEtherValue(tx.value) }
|
||||
<div>⇒</div>
|
||||
<div>
|
||||
<a
|
||||
className={ styles.link }
|
||||
href={ txLink(tx.hash, isTest) }
|
||||
target='_blank'>
|
||||
{ `${tx.hash.substr(2, 6)}...${tx.hash.slice(-6)}` }
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
{ this.renderAddress(tx.to) }
|
||||
<td className={ styles.method }>
|
||||
<MethodDecoding
|
||||
historic={ historic }
|
||||
address={ address }
|
||||
transaction={ tx } />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
renderAddress (address) {
|
||||
const { isTest } = this.props;
|
||||
|
||||
let esLink = null;
|
||||
if (address) {
|
||||
esLink = (
|
||||
<a
|
||||
href={ addressLink(address, isTest) }
|
||||
target='_blank'
|
||||
className={ styles.link }>
|
||||
<IdentityName address={ address } shorten />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<td className={ styles.address }>
|
||||
<div className={ styles.center }>
|
||||
<IdentityIcon
|
||||
center
|
||||
className={ styles.icon }
|
||||
address={ address } />
|
||||
</div>
|
||||
<div className={ styles.center }>
|
||||
{ esLink || 'DEPLOY' }
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
renderEtherValue (_value) {
|
||||
const { api } = this.context;
|
||||
const value = api.util.fromWei(_value);
|
||||
|
||||
if (value.eq(0)) {
|
||||
return <div className={ styles.value }>{ ' ' }</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.value }>
|
||||
{ value.toFormat(5) }<small>ETH</small>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderBlockNumber (_blockNumber) {
|
||||
const { block } = this.props;
|
||||
const blockNumber = _blockNumber.toNumber();
|
||||
|
||||
return (
|
||||
<td className={ styles.timestamp }>
|
||||
<div>{ blockNumber && block ? moment(block.timestamp).fromNow() : null }</div>
|
||||
<div>{ blockNumber ? _blockNumber.toFormat() : 'Pending' }</div>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
}
|
51
js/src/ui/TxList/TxRow/txRow.spec.js
Normal file
51
js/src/ui/TxList/TxRow/txRow.spec.js
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 BigNumber from 'bignumber.js';
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import Api from '~/api';
|
||||
|
||||
import TxRow from './txRow';
|
||||
|
||||
const api = new Api({ execute: sinon.stub() });
|
||||
|
||||
function renderShallow (props) {
|
||||
return shallow(
|
||||
<TxRow
|
||||
{ ...props } />,
|
||||
{ context: { api } }
|
||||
);
|
||||
}
|
||||
|
||||
describe('ui/TxRow', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders defaults', () => {
|
||||
const block = {
|
||||
timestamp: new Date()
|
||||
};
|
||||
const tx = {
|
||||
blockNumber: new BigNumber(123),
|
||||
hash: '0x123456789abcdef0123456789abcdef0123456789abcdef',
|
||||
value: new BigNumber(1)
|
||||
};
|
||||
|
||||
expect(renderShallow({ block, tx })).to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
@ -45,6 +45,8 @@ export default class Store {
|
||||
|
||||
if (bnB.eq(0)) {
|
||||
return bnB.eq(bnA) ? 0 : 1;
|
||||
} else if (bnA.eq(0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return bnB.comparedTo(bnA);
|
||||
|
90
js/src/ui/TxList/store.spec.js
Normal file
90
js/src/ui/TxList/store.spec.js
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 BigNumber from 'bignumber.js';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import Store from './store';
|
||||
|
||||
const SUBID = 123;
|
||||
const BLOCKS = {
|
||||
1: { blockhash: '0x1' },
|
||||
2: { blockhash: '0x2' }
|
||||
};
|
||||
const TRANSACTIONS = {
|
||||
'0x123': { blockNumber: new BigNumber(1) },
|
||||
'0x234': { blockNumber: new BigNumber(0) },
|
||||
'0x345': { blockNumber: new BigNumber(2) },
|
||||
'0x456': { blockNumber: new BigNumber(0) }
|
||||
};
|
||||
|
||||
describe('ui/TxList/store', () => {
|
||||
let api;
|
||||
let store;
|
||||
|
||||
beforeEach(() => {
|
||||
api = {
|
||||
subscribe: sinon.stub().resolves(SUBID),
|
||||
eth: {
|
||||
getBlockByNumber: (blockNumber) => {
|
||||
return Promise.resolve(BLOCKS[blockNumber]);
|
||||
}
|
||||
}
|
||||
};
|
||||
store = new Store(api);
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('has empty storage', () => {
|
||||
expect(store.blocks).to.deep.equal({});
|
||||
expect(store.sortedHashes.peek()).to.deep.equal([]);
|
||||
expect(store.transactions).to.deep.equal({});
|
||||
});
|
||||
|
||||
it('subscribes to eth_blockNumber', () => {
|
||||
expect(api.subscribe).to.have.been.calledWith('eth_blockNumber');
|
||||
expect(store._subscriptionId).to.equal(SUBID);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addBlocks', () => {
|
||||
beforeEach(() => {
|
||||
store.addBlocks(BLOCKS);
|
||||
});
|
||||
|
||||
it('adds the blocks to the list', () => {
|
||||
expect(store.blocks).to.deep.equal(BLOCKS);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addTransactions', () => {
|
||||
beforeEach(() => {
|
||||
store.addTransactions(TRANSACTIONS);
|
||||
});
|
||||
|
||||
it('adds all transactions to the list', () => {
|
||||
expect(store.transactions).to.deep.equal(TRANSACTIONS);
|
||||
});
|
||||
|
||||
it('sorts transactions based on blockNumber', () => {
|
||||
expect(store.sortedHashes.peek()).to.deep.equal(['0x234', '0x456', '0x345', '0x123']);
|
||||
});
|
||||
|
||||
it('adds pending transactions to the pending queue', () => {
|
||||
expect(store._pendingHashes).to.deep.equal(['0x234', '0x456']);
|
||||
});
|
||||
});
|
||||
});
|
@ -14,128 +14,16 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import moment from 'moment';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import { txLink, addressLink } from '~/3rdparty/etherscan/links';
|
||||
|
||||
import IdentityIcon from '../IdentityIcon';
|
||||
import IdentityName from '../IdentityName';
|
||||
import MethodDecoding from '../MethodDecoding';
|
||||
import Store from './store';
|
||||
import TxRow from './TxRow';
|
||||
|
||||
import styles from './txList.css';
|
||||
|
||||
export class TxRow extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
tx: PropTypes.object.isRequired,
|
||||
address: PropTypes.string.isRequired,
|
||||
isTest: PropTypes.bool.isRequired,
|
||||
|
||||
block: PropTypes.object,
|
||||
historic: PropTypes.bool,
|
||||
className: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
historic: true
|
||||
};
|
||||
|
||||
render () {
|
||||
const { tx, address, isTest, historic, className } = this.props;
|
||||
|
||||
return (
|
||||
<tr className={ className || '' }>
|
||||
{ this.renderBlockNumber(tx.blockNumber) }
|
||||
{ this.renderAddress(tx.from) }
|
||||
<td className={ styles.transaction }>
|
||||
{ this.renderEtherValue(tx.value) }
|
||||
<div>⇒</div>
|
||||
<div>
|
||||
<a
|
||||
className={ styles.link }
|
||||
href={ txLink(tx.hash, isTest) }
|
||||
target='_blank'>
|
||||
{ `${tx.hash.substr(2, 6)}...${tx.hash.slice(-6)}` }
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
{ this.renderAddress(tx.to) }
|
||||
<td className={ styles.method }>
|
||||
<MethodDecoding
|
||||
historic={ historic }
|
||||
address={ address }
|
||||
transaction={ tx } />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
renderAddress (address) {
|
||||
const { isTest } = this.props;
|
||||
|
||||
let esLink = null;
|
||||
if (address) {
|
||||
esLink = (
|
||||
<a
|
||||
href={ addressLink(address, isTest) }
|
||||
target='_blank'
|
||||
className={ styles.link }>
|
||||
<IdentityName address={ address } shorten />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<td className={ styles.address }>
|
||||
<div className={ styles.center }>
|
||||
<IdentityIcon
|
||||
center
|
||||
className={ styles.icon }
|
||||
address={ address } />
|
||||
</div>
|
||||
<div className={ styles.center }>
|
||||
{ esLink || 'DEPLOY' }
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
renderEtherValue (_value) {
|
||||
const { api } = this.context;
|
||||
const value = api.util.fromWei(_value);
|
||||
|
||||
if (value.eq(0)) {
|
||||
return <div className={ styles.value }>{ ' ' }</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.value }>
|
||||
{ value.toFormat(5) }<small>ETH</small>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderBlockNumber (_blockNumber) {
|
||||
const { block } = this.props;
|
||||
const blockNumber = _blockNumber.toNumber();
|
||||
|
||||
return (
|
||||
<td className={ styles.timestamp }>
|
||||
<div>{ blockNumber && block ? moment(block.timestamp).fromNow() : null }</div>
|
||||
<div>{ blockNumber ? _blockNumber.toFormat() : 'Pending' }</div>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class TxList extends Component {
|
||||
static contextTypes = {
|
||||
|
54
js/src/ui/TxList/txList.spec.js
Normal file
54
js/src/ui/TxList/txList.spec.js
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import Api from '~/api';
|
||||
|
||||
import TxList from './txList';
|
||||
|
||||
const api = new Api({ execute: sinon.stub() });
|
||||
|
||||
const STORE = {
|
||||
dispatch: sinon.stub(),
|
||||
subscribe: sinon.stub(),
|
||||
getState: () => {
|
||||
return {
|
||||
nodeStatus: {
|
||||
isTest: true
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function renderShallow (props) {
|
||||
return shallow(
|
||||
<TxList
|
||||
store={ STORE }
|
||||
{ ...props } />,
|
||||
{ context: { api } }
|
||||
);
|
||||
}
|
||||
|
||||
describe('ui/TxList', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders defaults', () => {
|
||||
expect(renderShallow()).to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
@ -17,7 +17,7 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { getShortData, getFee, getTotalValue } from './transaction';
|
||||
|
||||
describe('util/transaction', () => {
|
||||
describe('views/Signer/components/util/transaction', () => {
|
||||
describe('getEstimatedMiningTime', () => {
|
||||
it('should return estimated mining time', () => {
|
||||
});
|
||||
|
@ -21,7 +21,7 @@ import getMuiTheme from 'material-ui/styles/getMuiTheme';
|
||||
|
||||
import WrappedAutoComplete from './AutoComplete';
|
||||
|
||||
describe('components/AutoComplete', () => {
|
||||
describe('views/Status/components/AutoComplete', () => {
|
||||
describe('rendering', () => {
|
||||
let rendered;
|
||||
|
||||
|
@ -19,7 +19,7 @@ import { shallow } from 'enzyme';
|
||||
|
||||
import Box from './Box';
|
||||
|
||||
describe('components/Box', () => {
|
||||
describe('views/Status/components/Box', () => {
|
||||
describe('rendering', () => {
|
||||
const title = 'test title';
|
||||
let rendered;
|
||||
|
@ -22,7 +22,7 @@ import '../../../../environment/tests';
|
||||
|
||||
import Call from './Call';
|
||||
|
||||
describe('components/Call', () => {
|
||||
describe('views/Status/components/Call', () => {
|
||||
const call = { callIdx: 123, callNo: 456, name: 'eth_call', params: [{ name: '123' }], response: '' };
|
||||
const element = 'dummyElement';
|
||||
|
||||
|
@ -21,7 +21,7 @@ import '../../../../environment/tests';
|
||||
|
||||
import Calls from './Calls';
|
||||
|
||||
describe('components/Calls', () => {
|
||||
describe('views/Status/components/Calls', () => {
|
||||
describe('rendering (no calls)', () => {
|
||||
let rendered;
|
||||
|
||||
|
@ -22,7 +22,7 @@ import '../../../../environment/tests';
|
||||
|
||||
import CallsToolbar from './CallsToolbar';
|
||||
|
||||
describe('components/CallsToolbar', () => {
|
||||
describe('views/Status/components/CallsToolbar', () => {
|
||||
const callEl = { offsetTop: 0 };
|
||||
const containerEl = { scrollTop: 0, clientHeight: 0, scrollHeight: 999 };
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import { decodeExtraData } from './decodeExtraData';
|
||||
|
||||
describe('MINING SETTINGS', () => {
|
||||
describe('views/Status/components/MiningSettings/decodeExtraData', () => {
|
||||
describe('EXTRA DATA', () => {
|
||||
const str = 'parity/1.0.0/1.0.0-beta2';
|
||||
const encoded = '0xd783010000867061726974798b312e302e302d6265746132';
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import { numberFromString } from './numberFromString';
|
||||
|
||||
describe('NUMBER FROM STRING', () => {
|
||||
describe('views/Status/components/MiningSettings/numberFromString', () => {
|
||||
it('should convert string to number', () => {
|
||||
expect(numberFromString('12345'), 12345);
|
||||
});
|
||||
|
@ -21,7 +21,7 @@ import '../../../../environment/tests';
|
||||
|
||||
import Response from './Response';
|
||||
|
||||
describe('components/Response', () => {
|
||||
describe('views/Status/components/Response', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders non-arrays/non-objects exactly as received', () => {
|
||||
const TEST = '1234567890';
|
||||
|
@ -21,7 +21,7 @@ import { syncRpcStateFromLocalStorage } from '../actions/localstorage';
|
||||
import rpcData from '../data/rpc.json';
|
||||
import LocalStorageMiddleware from './localstorage';
|
||||
|
||||
describe('MIDDLEWARE: LOCAL STORAGE', () => {
|
||||
describe('views/Status/middleware/localstorage', () => {
|
||||
let cut, state;
|
||||
|
||||
beforeEach('mock cut', () => {
|
||||
|
@ -17,7 +17,7 @@
|
||||
import sinon from 'sinon';
|
||||
import * as ErrorUtil from './error';
|
||||
|
||||
describe('util/error', () => {
|
||||
describe('views/Status/util/error', () => {
|
||||
beforeEach('spy on isError', () => {
|
||||
sinon.spy(ErrorUtil, 'isError');
|
||||
});
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import { toPromise, identity } from './';
|
||||
|
||||
describe('util', () => {
|
||||
describe('views/Status/util', () => {
|
||||
describe('toPromise', () => {
|
||||
it('rejects on error result', () => {
|
||||
const ERROR = new Error();
|
||||
|
@ -23,7 +23,7 @@ import { bindActionCreators } from 'redux';
|
||||
import { confirmOperation, revokeOperation } from '~/redux/providers/walletActions';
|
||||
import { bytesToHex } from '~/api/util/format';
|
||||
import { Container, InputAddress, Button, IdentityIcon } from '~/ui';
|
||||
import { TxRow } from '~/ui/TxList/txList';
|
||||
import TxRow from '~/ui/TxList/TxRow';
|
||||
|
||||
import styles from '../wallet.css';
|
||||
import txListStyles from '~/ui/TxList/txList.css';
|
||||
|
@ -18,7 +18,7 @@ import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { bytesToHex } from '~/api/util/format';
|
||||
import { Container } from '~/ui';
|
||||
import { TxRow } from '~/ui/TxList/txList';
|
||||
import TxRow from '~/ui/TxList/TxRow';
|
||||
|
||||
import txListStyles from '~/ui/TxList/txList.css';
|
||||
|
||||
|
@ -22,11 +22,13 @@ es6Promise.polyfill();
|
||||
import 'mock-local-storage';
|
||||
|
||||
import chai from 'chai';
|
||||
import chaiAsPromised from 'chai-as-promised';
|
||||
import chaiEnzyme from 'chai-enzyme';
|
||||
import sinonChai from 'sinon-chai';
|
||||
import { WebSocket } from 'mock-socket';
|
||||
import jsdom from 'jsdom';
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
chai.use(chaiEnzyme());
|
||||
chai.use(sinonChai);
|
||||
|
||||
|
26
js/webpack/test.js
Normal file
26
js/webpack/test.js
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
context: path.join(__dirname, '../src'),
|
||||
resolve: {
|
||||
alias: {
|
||||
'~': path.resolve(__dirname, '../src')
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user