+
Confirm Password
{
const tree = createTestInstance().toJSON();
@@ -34,22 +33,6 @@ test('attaches the submit handler', () => {
expect(onSubmit).toBe(instance.handleSubmit);
});
-test('executes validators on submit', async () => {
- const { root } = createTestInstance();
-
- 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(
diff --git a/packages/venia-concept/src/components/CreateAccount/asyncValidators.js b/packages/venia-concept/src/components/CreateAccount/asyncValidators.js
new file mode 100644
index 0000000000..fb843b474f
--- /dev/null
+++ b/packages/venia-concept/src/components/CreateAccount/asyncValidators.js
@@ -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.';
+ }
+};
diff --git a/packages/venia-concept/src/components/CreateAccount/createAccount.js b/packages/venia-concept/src/components/CreateAccount/createAccount.js
index 12ee0f4c65..36033eb262 100644
--- a/packages/venia-concept/src/components/CreateAccount/createAccount.js
+++ b/packages/venia-concept/src/components/CreateAccount/createAccount.js
@@ -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 {
@@ -81,7 +90,7 @@ class CreateAccount extends Component {
@@ -89,7 +98,7 @@ class CreateAccount extends Component {
@@ -97,24 +106,31 @@ class CreateAccount extends Component {
-
+
-
+
diff --git a/packages/venia-concept/src/components/CreateAccount/validators.js b/packages/venia-concept/src/components/CreateAccount/validators.js
deleted file mode 100644
index dc86656853..0000000000
--- a/packages/venia-concept/src/components/CreateAccount/validators.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import { RestApi } from '@magento/peregrine';
-
-const { request } = RestApi.Magento2;
-
-const isPasswordComplexEnough = (str = '') => {
- const count = {
- lower: 0,
- upper: 0,
- digit: 0,
- special: 0
- };
-
- for (const char of str) {
- 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++;
- }
-
- return Object.values(count).filter(Boolean).length >= 3;
-};
-
-export const validators = new Map()
- .set('confirm', (value, values) => {
- return value !== values.password ? 'Passwords must match.' : undefined;
- })
- .set('email', value => {
- const trimmed = (value || '').trim();
-
- if (!trimmed) return 'An email address is required.';
- if (!trimmed.includes('@')) return 'A valid email address is required.';
-
- return undefined;
- })
- .set('firstName', value => {
- return !(value || '').trim() ? 'A first name is required.' : undefined;
- })
- .set('lastName', value => {
- return !(value || '').trim() ? 'A last name is required.' : undefined;
- })
- .set('password', value => {
- if (!value || value.length < 8) {
- return 'A password must contain at least 8 characters.';
- }
- if (!isPasswordComplexEnough(value)) {
- return 'A password must contain at least 3 of the following: lowercase, uppercase, digits, special characters.';
- }
-
- return undefined;
- });
-
-export const asyncValidators = new Map().set('email', 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.';
- }
-});
diff --git a/packages/venia-concept/src/util/__mocks__/formValidators.js b/packages/venia-concept/src/util/__mocks__/formValidators.js
new file mode 100644
index 0000000000..c7137ed965
--- /dev/null
+++ b/packages/venia-concept/src/util/__mocks__/formValidators.js
@@ -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();
diff --git a/packages/venia-concept/src/util/__tests__/formValidators.spec.js b/packages/venia-concept/src/util/__tests__/formValidators.spec.js
index ff11f1805e..020e2f9e02 100644
--- a/packages/venia-concept/src/util/__tests__/formValidators.spec.js
+++ b/packages/venia-concept/src/util/__tests__/formValidators.spec.js
@@ -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');
+ });
+});
diff --git a/packages/venia-concept/src/util/formValidators.js b/packages/venia-concept/src/util/formValidators.js
index c88e0e3f06..560d5e2008 100644
--- a/packages/venia-concept/src/util/formValidators.js
+++ b/packages/venia-concept/src/util/formValidators.js
@@ -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.';
+};