Merge pull request #3743 from ethcore/jg-test-ui

Basic UI rendering tests
This commit is contained in:
Gav Wood 2016-12-09 23:16:11 +01:00 committed by GitHub
commit 341777dbb6
36 changed files with 745 additions and 141 deletions

View File

@ -17,6 +17,15 @@
}, },
"development": { "development": {
"plugins": ["react-hot-loader/babel"] "plugins": ["react-hot-loader/babel"]
},
"test": {
"plugins": [
[
"babel-plugin-webpack-alias", {
"config": "webpack/test.js"
}
]
]
} }
} }
} }

View File

@ -40,9 +40,9 @@
"coveralls": "npm run testCoverage && coveralls < coverage/lcov.info", "coveralls": "npm run testCoverage && coveralls < coverage/lcov.info",
"lint": "eslint --ignore-path .gitignore ./src/", "lint": "eslint --ignore-path .gitignore ./src/",
"lint:cached": "eslint --cache --ignore-path .gitignore ./src/", "lint:cached": "eslint --cache --ignore-path .gitignore ./src/",
"test": "mocha 'src/**/*.spec.js'", "test": "NODE_ENV=test mocha 'src/**/*.spec.js'",
"test:coverage": "istanbul cover _mocha -- 'src/**/*.spec.js'", "test:coverage": "NODE_ENV=test istanbul cover _mocha -- 'src/**/*.spec.js'",
"test:e2e": "mocha 'src/**/*.e2e.js'", "test:e2e": "NODE_ENV=test mocha 'src/**/*.e2e.js'",
"test:npm": "(cd .npmjs && npm i) && node test/npmLibrary && (rm -rf .npmjs/node_modules)", "test:npm": "(cd .npmjs && npm i) && node test/npmLibrary && (rm -rf .npmjs/node_modules)",
"prepush": "npm run lint:cached" "prepush": "npm run lint:cached"
}, },
@ -57,6 +57,7 @@
"babel-plugin-transform-object-rest-spread": "6.20.2", "babel-plugin-transform-object-rest-spread": "6.20.2",
"babel-plugin-transform-react-remove-prop-types": "0.2.11", "babel-plugin-transform-react-remove-prop-types": "0.2.11",
"babel-plugin-transform-runtime": "6.15.0", "babel-plugin-transform-runtime": "6.15.0",
"babel-plugin-webpack-alias": "2.1.2",
"babel-polyfill": "6.20.0", "babel-polyfill": "6.20.0",
"babel-preset-es2015": "6.18.0", "babel-preset-es2015": "6.18.0",
"babel-preset-es2016": "6.16.0", "babel-preset-es2016": "6.16.0",
@ -66,6 +67,7 @@
"babel-register": "6.18.0", "babel-register": "6.18.0",
"babel-runtime": "6.20.0", "babel-runtime": "6.20.0",
"chai": "3.5.0", "chai": "3.5.0",
"chai-as-promised": "6.0.0",
"chai-enzyme": "0.6.1", "chai-enzyme": "0.6.1",
"circular-dependency-plugin": "2.0.0", "circular-dependency-plugin": "2.0.0",
"copy-webpack-plugin": "4.0.1", "copy-webpack-plugin": "4.0.1",

View File

@ -21,7 +21,7 @@ import '../../../environment/tests';
import Application from './application'; import Application from './application';
describe('localtx/Application', () => { describe('dapps/localtx/Application', () => {
describe('rendering', () => { describe('rendering', () => {
it('renders without crashing', () => { it('renders without crashing', () => {
const rendered = shallow(<Application />); const rendered = shallow(<Application />);

View File

@ -29,7 +29,7 @@ Api.api = {
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { Transaction, LocalTransaction } from './transaction'; import { Transaction, LocalTransaction } from './transaction';
describe('localtx/Transaction', () => { describe('dapps/localtx/Transaction', () => {
describe('rendering', () => { describe('rendering', () => {
it('renders without crashing', () => { it('renders without crashing', () => {
const transaction = { const transaction = {
@ -51,7 +51,7 @@ describe('localtx/Transaction', () => {
}); });
}); });
describe('localtx/LocalTransaction', () => { describe('dapps/localtx/LocalTransaction', () => {
describe('rendering', () => { describe('rendering', () => {
it('renders without crashing', () => { it('renders without crashing', () => {
const rendered = shallow( const rendered = shallow(

View 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');
});
});
});

View 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');
});
});
});

View File

@ -17,16 +17,15 @@
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FlatButton } from 'material-ui'; import { FlatButton } from 'material-ui';
import { nodeOrStringProptype } from '~/util/proptypes';
export default class Button extends Component { export default class Button extends Component {
static propTypes = { static propTypes = {
backgroundColor: PropTypes.string, backgroundColor: PropTypes.string,
className: PropTypes.string, className: PropTypes.string,
disabled: PropTypes.bool, disabled: PropTypes.bool,
icon: PropTypes.node, icon: PropTypes.node,
label: PropTypes.oneOfType([ label: nodeOrStringProptype(),
React.PropTypes.string,
React.PropTypes.object
]),
onClick: PropTypes.func, onClick: PropTypes.func,
primary: PropTypes.bool primary: PropTypes.bool
} }

View 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');
});
});
});

View File

@ -27,10 +27,6 @@ export default class Title extends Component {
byline: nodeOrStringProptype() byline: nodeOrStringProptype()
} }
state = {
name: 'Unnamed'
}
render () { render () {
const { className, title, byline } = this.props; const { className, title, byline } = this.props;

View 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');
});
});
});

View 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');
});
});
});

View 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');
});
});
});
});

View 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');
});
});
});
});

View 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';

View 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>
);
}
}

View 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;
});
});
});

View File

@ -45,6 +45,8 @@ export default class Store {
if (bnB.eq(0)) { if (bnB.eq(0)) {
return bnB.eq(bnA) ? 0 : 1; return bnB.eq(bnA) ? 0 : 1;
} else if (bnA.eq(0)) {
return -1;
} }
return bnB.comparedTo(bnA); return bnB.comparedTo(bnA);

View 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']);
});
});
});

View File

@ -14,128 +14,16 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import moment from 'moment';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { observer } from 'mobx-react'; 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 Store from './store';
import TxRow from './TxRow';
import styles from './txList.css'; 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 @observer
class TxList extends Component { class TxList extends Component {
static contextTypes = { static contextTypes = {

View 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;
});
});
});

View File

@ -17,7 +17,7 @@
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { getShortData, getFee, getTotalValue } from './transaction'; import { getShortData, getFee, getTotalValue } from './transaction';
describe('util/transaction', () => { describe('views/Signer/components/util/transaction', () => {
describe('getEstimatedMiningTime', () => { describe('getEstimatedMiningTime', () => {
it('should return estimated mining time', () => { it('should return estimated mining time', () => {
}); });

View File

@ -21,7 +21,7 @@ import getMuiTheme from 'material-ui/styles/getMuiTheme';
import WrappedAutoComplete from './AutoComplete'; import WrappedAutoComplete from './AutoComplete';
describe('components/AutoComplete', () => { describe('views/Status/components/AutoComplete', () => {
describe('rendering', () => { describe('rendering', () => {
let rendered; let rendered;

View File

@ -19,7 +19,7 @@ import { shallow } from 'enzyme';
import Box from './Box'; import Box from './Box';
describe('components/Box', () => { describe('views/Status/components/Box', () => {
describe('rendering', () => { describe('rendering', () => {
const title = 'test title'; const title = 'test title';
let rendered; let rendered;

View File

@ -22,7 +22,7 @@ import '../../../../environment/tests';
import Call from './Call'; 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 call = { callIdx: 123, callNo: 456, name: 'eth_call', params: [{ name: '123' }], response: '' };
const element = 'dummyElement'; const element = 'dummyElement';

View File

@ -21,7 +21,7 @@ import '../../../../environment/tests';
import Calls from './Calls'; import Calls from './Calls';
describe('components/Calls', () => { describe('views/Status/components/Calls', () => {
describe('rendering (no calls)', () => { describe('rendering (no calls)', () => {
let rendered; let rendered;

View File

@ -22,7 +22,7 @@ import '../../../../environment/tests';
import CallsToolbar from './CallsToolbar'; import CallsToolbar from './CallsToolbar';
describe('components/CallsToolbar', () => { describe('views/Status/components/CallsToolbar', () => {
const callEl = { offsetTop: 0 }; const callEl = { offsetTop: 0 };
const containerEl = { scrollTop: 0, clientHeight: 0, scrollHeight: 999 }; const containerEl = { scrollTop: 0, clientHeight: 0, scrollHeight: 999 };

View File

@ -16,7 +16,7 @@
import { decodeExtraData } from './decodeExtraData'; import { decodeExtraData } from './decodeExtraData';
describe('MINING SETTINGS', () => { describe('views/Status/components/MiningSettings/decodeExtraData', () => {
describe('EXTRA DATA', () => { describe('EXTRA DATA', () => {
const str = 'parity/1.0.0/1.0.0-beta2'; const str = 'parity/1.0.0/1.0.0-beta2';
const encoded = '0xd783010000867061726974798b312e302e302d6265746132'; const encoded = '0xd783010000867061726974798b312e302e302d6265746132';

View File

@ -16,7 +16,7 @@
import { numberFromString } from './numberFromString'; import { numberFromString } from './numberFromString';
describe('NUMBER FROM STRING', () => { describe('views/Status/components/MiningSettings/numberFromString', () => {
it('should convert string to number', () => { it('should convert string to number', () => {
expect(numberFromString('12345'), 12345); expect(numberFromString('12345'), 12345);
}); });

View File

@ -21,7 +21,7 @@ import '../../../../environment/tests';
import Response from './Response'; import Response from './Response';
describe('components/Response', () => { describe('views/Status/components/Response', () => {
describe('rendering', () => { describe('rendering', () => {
it('renders non-arrays/non-objects exactly as received', () => { it('renders non-arrays/non-objects exactly as received', () => {
const TEST = '1234567890'; const TEST = '1234567890';

View File

@ -21,7 +21,7 @@ import { syncRpcStateFromLocalStorage } from '../actions/localstorage';
import rpcData from '../data/rpc.json'; import rpcData from '../data/rpc.json';
import LocalStorageMiddleware from './localstorage'; import LocalStorageMiddleware from './localstorage';
describe('MIDDLEWARE: LOCAL STORAGE', () => { describe('views/Status/middleware/localstorage', () => {
let cut, state; let cut, state;
beforeEach('mock cut', () => { beforeEach('mock cut', () => {

View File

@ -17,7 +17,7 @@
import sinon from 'sinon'; import sinon from 'sinon';
import * as ErrorUtil from './error'; import * as ErrorUtil from './error';
describe('util/error', () => { describe('views/Status/util/error', () => {
beforeEach('spy on isError', () => { beforeEach('spy on isError', () => {
sinon.spy(ErrorUtil, 'isError'); sinon.spy(ErrorUtil, 'isError');
}); });

View File

@ -16,7 +16,7 @@
import { toPromise, identity } from './'; import { toPromise, identity } from './';
describe('util', () => { describe('views/Status/util', () => {
describe('toPromise', () => { describe('toPromise', () => {
it('rejects on error result', () => { it('rejects on error result', () => {
const ERROR = new Error(); const ERROR = new Error();

View File

@ -23,7 +23,7 @@ import { bindActionCreators } from 'redux';
import { confirmOperation, revokeOperation } from '~/redux/providers/walletActions'; import { confirmOperation, revokeOperation } from '~/redux/providers/walletActions';
import { bytesToHex } from '~/api/util/format'; import { bytesToHex } from '~/api/util/format';
import { Container, InputAddress, Button, IdentityIcon } from '~/ui'; 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 styles from '../wallet.css';
import txListStyles from '~/ui/TxList/txList.css'; import txListStyles from '~/ui/TxList/txList.css';

View File

@ -18,7 +18,7 @@ import React, { Component, PropTypes } from 'react';
import { bytesToHex } from '~/api/util/format'; import { bytesToHex } from '~/api/util/format';
import { Container } from '~/ui'; import { Container } from '~/ui';
import { TxRow } from '~/ui/TxList/txList'; import TxRow from '~/ui/TxList/TxRow';
import txListStyles from '~/ui/TxList/txList.css'; import txListStyles from '~/ui/TxList/txList.css';

View File

@ -22,11 +22,13 @@ es6Promise.polyfill();
import 'mock-local-storage'; import 'mock-local-storage';
import chai from 'chai'; import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import chaiEnzyme from 'chai-enzyme'; import chaiEnzyme from 'chai-enzyme';
import sinonChai from 'sinon-chai'; import sinonChai from 'sinon-chai';
import { WebSocket } from 'mock-socket'; import { WebSocket } from 'mock-socket';
import jsdom from 'jsdom'; import jsdom from 'jsdom';
chai.use(chaiAsPromised);
chai.use(chaiEnzyme()); chai.use(chaiEnzyme());
chai.use(sinonChai); chai.use(sinonChai);

26
js/webpack/test.js Normal file
View 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')
}
}
};