Skip to content

Instantly share code, notes, and snippets.

@ahlusar1989
Created June 6, 2017 21:01
Show Gist options
  • Save ahlusar1989/38f1ea6dc8c379c7424b3dc995acf730 to your computer and use it in GitHub Desktop.
Save ahlusar1989/38f1ea6dc8c379c7424b3dc995acf730 to your computer and use it in GitHub Desktop.
import { graphql } from 'react-apollo';
import { connect } from 'react-redux';
import { compose } from 'redux';
import {
change,
getFormSubmitErrors,
getFormValues,
reduxForm,
stopAsyncValidation,
SubmissionError,
} from 'redux-form';
import validator from 'validate.js';
// components
import Form from './Form';
// mutations
import createTokenMutation from '../../../mutations/createToken';
import createUserMutation from '../../../mutations/createUser';
// form
const formName = 'users/new';
// rules
const emailRules = {
presence: {
message: 'Please enter an email address',
},
email: {
message: 'Please enter a valid email address',
},
};
const passwordRules = {
presence: {
message: 'Please enter a password',
},
length: {
message: 'Passwords must be at least 8 characters',
minimum: 8,
},
};
async function createUser(variables, props) {
try {
await props.createUser({ variables });
} catch (err) {
const error = err.graphQLErrors[0];
if (error && error.code === 422) {
throw new SubmissionError({ email: error.details.email[0].message });
} else {
throw new SubmissionError(
{
_error: `
Yikes! There was a problem creating your account. Please try again.
`,
},
);
}
}
}
async function createToken(variables, props) {
try {
await props.createToken({ variables });
} catch (err) {
throw new SubmissionError(
{
_error: `
The good news is that your account has been created! The bad
news is that we ran into an unexpected error logging in to your
account.
`,
},
);
}
}
function handleChange(e = {}, dispatch) {
const target = e.target || {};
const { id, value } = target;
dispatch(change(formName, id, value));
dispatch(stopAsyncValidation(formName, {}));
}
async function handleSubmit(values, dispatch, props) {
const { email, password } = values;
const variables = { email, password };
validate(values);
await createUser(variables, props);
await createToken(variables, props);
}
function validate(values) {
const { email, password, passwordStrength } = values;
const messages = {};
Object.assign(messages, validateEmail(email));
Object.assign(messages, validatePassword(password, passwordStrength));
if (Object.keys(messages).length > 0) {
throw new SubmissionError(messages);
}
}
function validateEmail(email) {
const results = validator.single(email, emailRules);
let message;
if (results) message = { email: results[0] };
return message;
}
function validatePassword(password, passwordStrength) {
const results = validator.single(password, passwordRules);
let message;
if (results) {
message = { password: results[0] };
} else if (passwordStrength && passwordStrength.score === 0) {
const { feedback } = passwordStrength;
const { suggestions, warning } = feedback;
message = { password: `${warning}. ${suggestions}` };
}
return message;
}
function mapStateToProps(state) {
return {
formErrors: getFormSubmitErrors(formName)(state),
formValues: getFormValues(formName)(state),
};
}
function mapDispatchToProps(dispatch) {
return {
onChange(e) {
handleChange(e, dispatch);
},
};
}
export default compose(
connect(mapStateToProps, mapDispatchToProps),
graphql(createTokenMutation, { name: 'createToken' }),
graphql(createUserMutation, { name: 'createUser' }),
reduxForm({ form: formName, onSubmit: handleSubmit, pure: false }),
)(Form);
/* eslint-disable no-unused-expressions */
import { expect } from 'chai';
import { mount } from 'enzyme';
import faker from 'faker';
import React from 'react';
import { addTypenameToDocument } from 'react-apollo';
import { MockedProvider } from 'react-apollo/lib/test-utils';
import { change, submit } from 'redux-form';
import createStore from '../../../lib/redux/createStore';
import createTokenMutation from '../../../mutations/createToken';
import createUserMutation from '../../../mutations/createUser';
import FormContainer from './FormContainer';
describe('users', () => {
describe('New', () => {
describe('<FormContainer />', () => {
let dispatch;
let mocks;
let mountedContainer;
let props;
let store;
const container = () => {
if (!mountedContainer) {
mountedContainer = mount(
<MockedProvider mocks={ mocks } store={ store }>
<FormContainer { ...props } />
</MockedProvider>,
);
}
return mountedContainer;
};
const submitErrors = () => {
const state = store.getState() || {};
const form = state.form['users/new'] || {};
return form.submitErrors || {};
};
beforeEach(() => {
mocks = [];
mountedContainer = undefined;
props = {};
store = createStore();
dispatch = store.dispatch;
});
it('renders without exploding', () => {
expect(container().length).to.be.above(0);
});
describe('when form is valid', () => {
beforeEach(() => {
const variables = {
email: faker.internet.email(),
password: faker.internet.password(),
};
const query1 = addTypenameToDocument(createUserMutation);
const result1 = { data: { createUser: { email: variables.email, __typename: 'User' } } };
const query2 = addTypenameToDocument(createTokenMutation);
const result2 = { data: { createToken: { access: faker.lorem.text, __typename: 'Token' } } };
mocks = [
{ request: { variables, query: query1 }, result: result1 },
{ request: { variables, query: query2 }, result: result2 },
];
container();
dispatch(change('users/new', 'email', variables.email));
dispatch(change('users/new', 'password', variables.password));
dispatch(submit('users/new'));
});
it('finishes successfully', (done) => {
setTimeout(() => {
const state = store.getState();
const form = state.form['users/new'];
expect(form.submitSucceeded).to.eql(true);
done();
}, 16);
});
});
describe('when `email` is not present', () => {
beforeEach(() => {
container();
dispatch(change('users/new', 'email', ''));
dispatch(submit('users/new'));
});
it('generates an error message', (done) => {
process.nextTick(() => {
expect(submitErrors().email).to.not.be.null;
done();
});
});
});
describe('when `email` is in an improper format', () => {
beforeEach(() => {
container();
dispatch(change('users/new', 'email', faker.lorem.word()));
dispatch(submit('users/new'));
});
it('generates an error message', (done) => {
process.nextTick(() => {
expect(submitErrors().email).to.not.be.null;
done();
});
});
});
describe('when `password` is not present', () => {
beforeEach(() => {
container();
dispatch(change('users/new', 'password', ''));
dispatch(submit('users/new'));
});
it('generates an error message', (done) => {
process.nextTick(() => {
expect(submitErrors().password).to.not.be.null;
done();
});
});
});
describe('when `password` is less than 8 characters', () => {
beforeEach(() => {
container();
dispatch(change('users/new', 'password', faker.internet.password(7)));
dispatch(submit('users/new'));
});
it('generates an error message', (done) => {
process.nextTick(() => {
expect(submitErrors().password).to.not.be.null;
done();
});
});
});
describe('when `password` is too common', () => {
beforeEach(() => {
container();
dispatch(change('users/new', 'password', 'password'));
dispatch(submit('users/new'));
});
it('generates an error message', (done) => {
process.nextTick(() => {
expect(submitErrors().password).to.not.be.null;
done();
});
});
});
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment