Skip to content
This repository has been archived by the owner on Apr 15, 2019. It is now read-only.

Stabilise RelativeLink e2e tests - Closes #783 #784

Merged
merged 6 commits into from
Sep 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion features/login.feature
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ Feature: Login page
Then I should be logged in
And I should see text "Testnet" in "peer network" element

@ignore
Scenario: should allow to create a new account
Given I'm on login page
When I click "new account button"
Expand Down
1 change: 1 addition & 0 deletions src/components/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const App = () => (
<Route path={`${match.url}/forging/:dialog?`} component={Forging} />
</main>
)} />
<Route exact path="/register" component={Login} />
<Route exact path="/" component={Login} />
</main>
<Dialog />
Expand Down
82 changes: 51 additions & 31 deletions src/components/dialog/dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,70 +10,90 @@ class DialogElement extends Component {
constructor() {
super();
this.state = {};
this.path = {
name: '/',
this.routesReg = [
{
regex: /\/main\/transactions(?:\/[^/]*)?$/,
path: '/main/transactions/',
params: 'dialog',
name: 'transactions',
}, {
regex: /\/main\/voting(?:\/[^/]*)?$/,
path: '/main/voting/',
params: 'dialog',
name: 'voting',
}, {
regex: /\/main\/forging(?:\/[^/]*)?$/,
path: '/main/forging/',
params: 'dialog',
name: 'forging',
}, {
regex: /\/(\w+)?$/,
path: '/',
params: 'dialog',
name: 'login',
},
];
this.current = {
pathname: '/',
reg: this.routesReg[3],
list: [],
dialog: '',
};
}

componentDidMount() {
this.checkForDialog(this.props.history.location);
this.checkForDialog();
}

componentDidUpdate() {
if (this.path.name !== this.props.history.location.pathname) {
this.path.name = this.props.history.location.pathname;
this.checkForDialog(this.props.history.location);
this.checkForDialog();
}

checkForDialog() {
if (this.current.pathname !== this.props.history.location.pathname) {
this.current.reg = this.routesReg.find(item =>
item.regex.test(this.props.history.location.pathname));
this.current.pathname = this.props.history.location.pathname;
const dialogName = this.props.history.location.pathname.replace(this.current.reg.path, '');
if (dialogs[dialogName] !== undefined) {
this.open(this.current.reg, dialogs[dialogName]);
} else {
this.close();
}
}
}

checkForDialog(location) {
const parseParams = search => search.replace(/^\?/, '').split('&&').reduce((acc, param) => {
// eslint-disable-next-line class-methods-use-this
parseParams(search) {
return search.replace(/^\?/, '').split('&&').reduce((acc, param) => {
const keyValue = param.split('=');
if (keyValue[0] !== '' && keyValue[1] !== 'undefined') {
acc[keyValue[0]] = keyValue[1];
}
return acc;
}, {});

this.path.list = location.pathname.replace(/\/$/, '').split('/');
this.dialog = this.path.list[3];

if (this.path.list.length === 5) {
this.props.history.push(`/${this.path.list[1]}/${this.path.list[2]}/${this.path.list[4]}`);
} else if (this.path.list.length === 4 && Object.keys(dialogs).includes(this.dialog)) {
this.routeWithDialog(dialogs[this.dialog], parseParams(location.search));
} else {
this.routeWithOutDialog();
}
}

routeWithDialog(dialog, childComponentProps) {
open(config, dialog) {
clearTimeout(this.timeout);
this.setState({ hidden: false });
this.props.dialogDisplayed({
title: dialog.title,
childComponent: dialog.component,
childComponentProps,
childComponentProps: this.parseParams(this.props.history.location.search),
});
}

routeWithOutDialog() {
close() {
this.timeout = setTimeout(() => {
this.props.dialogHidden();
this.setState({ hidden: false });
}, 500);
this.setState({ hidden: true });
}

closeDialog() {
if (this.props.dialog.childComponentProps.noRouter) {
this.routeWithOutDialog();
} else {
const upperRoute = this.path.name.replace(/\/$/, '').replace(this.dialog, '');
this.props.history.push(upperRoute);
}
goBack() {
this.props.history.push(this.current.reg.path);
}

render() {
Expand All @@ -85,14 +105,14 @@ class DialogElement extends Component {
<AppBar title={this.props.dialog.title} flat={true}
className={styles[this.props.dialog.type]}>
<Navigation type='horizontal'>
<IconButton className={`${styles['x-button']} x-button`} onClick={this.closeDialog.bind(this)} icon='close'/>
<IconButton className={`${styles['x-button']} x-button`} onClick={this.goBack.bind(this)} icon='close'/>
</Navigation>
</AppBar>
<div className='modal-dialog-body'>
{this.props.dialog.childComponent ?
<this.props.dialog.childComponent
{...(this.props.dialog.childComponentProps || {})}
closeDialog={this.closeDialog.bind(this)}
closeDialog={this.goBack.bind(this)}
/> :
null
}
Expand Down
5 changes: 5 additions & 0 deletions src/components/dialog/dialogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import SecondPassphrase from '../secondPassphrase';
import VoteDialog from '../voteDialog';
import ReceiveDialog from '../receiveDialog';
import SaveAccount from '../saveAccount';
import Register from '../register';

export default {
send: {
Expand Down Expand Up @@ -36,6 +37,10 @@ export default {
title: 'Receive LSK',
component: ReceiveDialog,
},
register: {
title: 'New Account',
component: Register,
},
'save-account': {
title: 'Remember this account',
component: SaveAccount,
Expand Down
20 changes: 4 additions & 16 deletions src/components/login/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import networksRaw from './networks';
import PassphraseInput from '../passphraseInput';
import styles from './login.css';
import env from '../../constants/env';
import Passphrase from '../passphrase';
import RelativeLink from '../relativeLink';

/**
* The container component containing login
Expand Down Expand Up @@ -188,25 +188,13 @@ class Login extends React.Component {
onChange={this.changeHandler.bind(this, 'passphrase')} />
<footer className={ `${grid.row} ${grid['center-xs']}` }>
<div className={grid['col-xs-12']}>
<Button label={this.props.t('New Account')} flat primary
className={`${styles.newAccount} new-account-button`}
onClick={() => this.props.setActiveDialog({
title: this.props.t('New Account'),
childComponent: Passphrase,
childComponentProps: {
onPassGenerated: this.onLoginSubmission.bind(this),
keepModal: false,
noRouter: true,
confirmButton: 'Login',
useCaseNote: 'your passphrase will be required for logging in to your account.',
securityNote: 'This passphrase is not recoverable and if you lose it, you will lose access to your account forever.',
},
})} />
<RelativeLink to='register' flat primary
className={`${styles.newAccount} new-account-button`}>{this.props.t('New Account')}</RelativeLink>
<Button label='LOGIN' primary raised
onClick={this.onLoginSubmission.bind(this, this.state.passphrase)}
className='login-button'
disabled={(this.state.network === 2 && this.state.addressValidity !== '') ||
this.state.passphraseValidity !== ''} />
this.state.passphraseValidity !== ''} />
</div>
</footer>
</form>
Expand Down
15 changes: 15 additions & 0 deletions src/components/register/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import { dialogDisplayed } from '../../actions/dialog';
import { activePeerSet } from '../../actions/peers';
import Register from './register';

const mapDispatchToProps = dispatch => ({
setActiveDialog: data => dispatch(dialogDisplayed(data)),
activePeerSet: data => dispatch(activePeerSet(data)),
});

export default connect(
null,
mapDispatchToProps,
)(translate()(Register));
31 changes: 31 additions & 0 deletions src/components/register/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import { I18nextProvider } from 'react-i18next';
import configureMockStore from 'redux-mock-store';
import i18n from '../../i18n';
import Register from './index';


describe('RegisterHOC', () => {
let wrapper;
const peers = {};
const account = {};
const store = configureMockStore([])({
peers,
account,
});

beforeEach(() => {
wrapper = mount(<Provider store={store}>
<I18nextProvider i18n={ i18n }>
<Register />
</I18nextProvider>
</Provider>);
});

it('should render Register', () => {
expect(wrapper.find('Register')).to.have.lengthOf(1);
});
});
3 changes: 3 additions & 0 deletions src/components/register/register.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.hidden {
display: none;
}
60 changes: 60 additions & 0 deletions src/components/register/register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import Passphrase from '../passphrase';
import networksRaw from '../login/networks';

const Register = ({
activePeerSet, closeDialog, t,
}) => {
const validateUrl = (value) => {
const addHttp = (url) => {
const reg = /^(?:f|ht)tps?:\/\//i;
return reg.test(url) ? url : `http://${url}`;
};

const errorMessage = 'URL is invalid';

const isValidLocalhost = url => url.hostname === 'localhost' && url.port.length > 1;
const isValidRemote = url => /(([a-z\d]([a-z\d-]*[a-z\d])*)\.)+[a-z]{2,}|((\d{1,3}\.){3}\d{1,3})/.test(url.hostname);

let addressValidity = '';
try {
const url = new URL(addHttp(value));
addressValidity = url && (isValidRemote(url) || isValidLocalhost(url)) ? '' : errorMessage;
} catch (e) {
addressValidity = errorMessage;
}

return addressValidity === '';
};

const onLoginSubmission = (passphrase) => {
let NetworkIndex = parseInt(localStorage.getItem('network'), 10) || 0;
const address = localStorage.getItem('address') || '';
if (!NetworkIndex || (NetworkIndex === 2 && !validateUrl(address))) {
NetworkIndex = 0;
}

const network = Object.assign({}, networksRaw[NetworkIndex]);
if (NetworkIndex === 2) {
network.address = address;
}

// set active peer
activePeerSet({
passphrase,
network,
});
};

return (
<Passphrase
onPassGenerated={onLoginSubmission}
keepModal={false}
closeDialog={closeDialog}
confirmButton={'Login'}
useCaseNote={t('your passphrase will be required for logging in to your account.')}
securityNote={t('This passphrase is not recoverable and if you lose it, you will lose access to your account forever.')}/>
);
};

export default Register;
56 changes: 56 additions & 0 deletions src/components/register/register.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import { spy } from 'sinon';
import PropTypes from 'prop-types';
import configureMockStore from 'redux-mock-store';
import Register from './register';

describe('Register', () => {
let wrapper;
const peers = { data: {} };
const account = {};
const store = configureMockStore([])({
peers,
account,
activePeerSet: () => {},
});
const options = {
context: { store },
childContextTypes: {
store: PropTypes.object.isRequired,
},
};
const prop = {
account,
peers,
activePeerSet: spy(),
t: key => key,
};

beforeEach(() => {
wrapper = mount(<Register {...prop} />, options);
});

it('renders Passphrase component', () => {
expect(wrapper.find('Passphrase')).to.have.length(1);
});

it('should mount Register with appropriate properties', () => {
const props = wrapper.find('Passphrase').props();
expect(props.useCaseNote).to.be.equal('your passphrase will be required for logging in to your account.');
expect(props.securityNote).to.be.equal('This passphrase is not recoverable and if you lose it, you will lose access to your account forever.');
expect(props.confirmButton).to.be.equal('Login');
expect(props.keepModal).to.be.equal(false);
expect(typeof props.onPassGenerated).to.be.equal('function');
});

it('should call activePeerSet if props.onPassGenerated is called', () => {
const props = wrapper.find('Passphrase').props();
props.onPassGenerated('sample passphrase');
expect(prop.activePeerSet).to.have.been.calledWith({
network: { name: 'Mainnet', port: 443, ssl: true },
passphrase: 'sample passphrase',
});
});
});