Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

833 refactor the create account form #835

Merged
Show file tree
Hide file tree
Changes from 13 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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const validateEmail = jest.fn();

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ exports[`renders the correct tree 1`] = `
</div>
<div>
<span>
<span />
Password
</span>
<span
Expand Down Expand Up @@ -119,6 +120,7 @@ exports[`renders the correct tree 1`] = `
</div>
<div>
<span>
<span />
Confirm Password
</span>
<span
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ import { Form } from 'informed';
import { createTestInstance } from '@magento/peregrine';

import CreateAccount from '../createAccount';
import { validators } from '../validators';

jest.mock('../validators');
jest.mock('src/util/formValidators');

const submitCallback = jest.fn();
export const submitCallback = jest.fn();

test('renders the correct tree', () => {
const tree = createTestInstance(<CreateAccount />).toJSON();
Expand All @@ -34,22 +33,6 @@ test('attaches the submit handler', () => {
expect(onSubmit).toBe(instance.handleSubmit);
});

test('executes validators on submit', async () => {
const { root } = createTestInstance(<CreateAccount />);

const form = root.findByType(Form);
const { formApi } = form.instance;

// touch fields, call validators, call onSubmit
act(() => {
formApi.submitForm();
});

for (const validator of validators.values()) {
expect(validator).toHaveBeenCalledTimes(2);
}
});

test('calls onSubmit if validation passes', async () => {
const { root } = createTestInstance(
<CreateAccount onSubmit={submitCallback} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { RestApi } from '@magento/peregrine';

const { request } = RestApi.Magento2;

export const validateEmail = async value => {
try {
const body = {
customerEmail: value,
website_id: null
};

// response is a boolean
const available = await request('/rest/V1/customers/isEmailAvailable', {
method: 'POST',
body: JSON.stringify(body)
});

return !available ? 'This email address is not available.' : null;
} catch (error) {
throw 'An error occurred while looking up this email address.';
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The next version of informed that we're moving to in #1078 does not support asyncValidators. We have decided to keep this code around in the hopes that support for it gets added back. Then we can just hook up this code to the UI.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@ import Button from 'src/components/Button';
import Checkbox from 'src/components/Checkbox';
import Field from 'src/components/Field';
import TextInput from 'src/components/TextInput';
import { validators } from './validators';

import combine from 'src/util/combineValidators';
import {
validateEmail,
isRequired,
validatePassword,
validateConfirmPassword,
hasLengthAtLeast
} from 'src/util/formValidators';

import defaultClasses from './createAccount.css';

class CreateAccount extends Component {
Expand Down Expand Up @@ -81,40 +90,47 @@ class CreateAccount extends Component {
<TextInput
field="customer.firstname"
autoComplete="given-name"
validate={validators.get('firstName')}
validate={isRequired}
validateOnBlur
/>
</Field>
<Field label="Last Name" required={true}>
<TextInput
field="customer.lastname"
autoComplete="family-name"
validate={validators.get('lastName')}
validate={isRequired}
validateOnBlur
/>
</Field>
<Field label="Email" required={true}>
<TextInput
field="customer.email"
autoComplete="email"
validate={validators.get('email')}
validate={combine([isRequired, validateEmail])}
validateOnBlur
/>
</Field>
<Field label="Password">
<Field label="Password" required={true}>
<TextInput
field="password"
type="password"
autoComplete="new-password"
validate={validators.get('password')}
validate={combine([
isRequired,
[hasLengthAtLeast, 8],
validatePassword
])}
validateOnBlur
/>
</Field>
<Field label="Confirm Password">
<Field label="Confirm Password" required={true}>
<TextInput
field="confirm"
type="password"
validate={validators.get('confirm')}
validate={combine([
isRequired,
validateConfirmPassword
])}
validateOnBlur
/>
</Field>
Expand Down
69 changes: 0 additions & 69 deletions packages/venia-concept/src/components/CreateAccount/validators.js

This file was deleted.

5 changes: 5 additions & 0 deletions packages/venia-concept/src/util/__mocks__/formValidators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const validateEmail = jest.fn();
export const isRequired = jest.fn();
export const validatePassword = jest.fn();
export const validateConfirmPassword = jest.fn();
export const hasLengthAtLeast = jest.fn();
51 changes: 51 additions & 0 deletions packages/venia-concept/src/util/__tests__/formValidators.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,54 @@ describe('validateRegionCode', () => {
expect(typeof result).toBe('string');
});
});

describe('validatePassword', () => {
test('it returns undefined on success', () => {
const result = validators.validatePassword('123qwe_+*');

expect(result).toBeUndefined();
});

test('it returns a string on failure', () => {
const result = validators.validatePassword('1111');

expect(typeof result).toBe('string');
});
});

describe('validateConfirmPassword', () => {
test('it returns undefined on success', () => {
const values = {
password: 'qwerty12345'
};
const password = 'qwerty12345';
const result = validators.validateConfirmPassword(password, values);

expect(result).toBeUndefined();
});

test('it returns undefined on success with a password key', () => {
const values = {
password_key: 'qwerty12345'
};
const password = 'qwerty12345';
const passwordKey = 'password_key';
const result = validators.validateConfirmPassword(
password,
values,
passwordKey
);

expect(result).toBeUndefined();
});

test('it returns a string on failure', () => {
const values = {
password: 'qwertz12345'
};
const password = 'qwerty12345';
const result = validators.validateConfirmPassword(password, values);

expect(typeof result).toBe('string');
});
});
30 changes: 30 additions & 0 deletions packages/venia-concept/src/util/formValidators.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,33 @@ export const validateRegionCode = (value, values, countries) => {

return SUCCESS;
};

export const validatePassword = value => {
const count = {
lower: 0,
upper: 0,
digit: 0,
special: 0
};

for (const char of value) {
if (/[a-z]/.test(char)) count.lower++;
else if (/[A-Z]/.test(char)) count.upper++;
else if (/\d/.test(char)) count.digit++;
else if (/\S/.test(char)) count.special++;
}

if (Object.values(count).filter(Boolean).length < 3) {
return 'A password must contain at least 3 of the following: lowercase, uppercase, digits, special characters.';
}

return SUCCESS;
};

export const validateConfirmPassword = (
value,
values,
passwordKey = 'password'
) => {
return value === values[passwordKey] ? SUCCESS : 'Passwords must match.';
};