From 49bf6a5c81c465cdd00f4f3fb279b06a11a03fdd Mon Sep 17 00:00:00 2001
From: RJ <27843274+rjchow@users.noreply.github.com>
Date: Thu, 20 Jan 2022 16:40:20 +0800
Subject: [PATCH] Updated frontend email validation to use @wordpress/url
(https://github.com/woocommerce/woocommerce-admin/pull/8197)
* Updated frontend email validation to use @wordpress/url
- added testing for StoreDetails
- changed basic email validation to use @wordpress/url isEmail
---
...fix-8155-improve-frontend-email-validation | 4 +
.../steps/store-details/index.js | 9 +-
.../test/__snapshots__/index.js.snap | 426 ++++++++++++++++++
.../steps/store-details/test/index.js | 83 ++++
plugins/woocommerce-admin/package-lock.json | 96 ++--
5 files changed, 565 insertions(+), 53 deletions(-)
create mode 100644 plugins/woocommerce-admin/changelogs/fix-8155-improve-frontend-email-validation
create mode 100644 plugins/woocommerce-admin/client/profile-wizard/steps/store-details/test/__snapshots__/index.js.snap
create mode 100644 plugins/woocommerce-admin/client/profile-wizard/steps/store-details/test/index.js
diff --git a/plugins/woocommerce-admin/changelogs/fix-8155-improve-frontend-email-validation b/plugins/woocommerce-admin/changelogs/fix-8155-improve-frontend-email-validation
new file mode 100644
index 00000000000..e7b2f2d7e68
--- /dev/null
+++ b/plugins/woocommerce-admin/changelogs/fix-8155-improve-frontend-email-validation
@@ -0,0 +1,4 @@
+Significance: patch
+Type: Fix
+
+changed email validation in Store Details onboarding task to more closely match PHP backend validation. #8197
diff --git a/plugins/woocommerce-admin/client/profile-wizard/steps/store-details/index.js b/plugins/woocommerce-admin/client/profile-wizard/steps/store-details/index.js
index 1ddeae95f6f..3d11a7d5be2 100644
--- a/plugins/woocommerce-admin/client/profile-wizard/steps/store-details/index.js
+++ b/plugins/woocommerce-admin/client/profile-wizard/steps/store-details/index.js
@@ -25,6 +25,7 @@ import {
import { recordEvent } from '@woocommerce/tracks';
import { Text } from '@woocommerce/experimental';
import { Icon, info } from '@wordpress/icons';
+import { isEmail } from '@wordpress/url';
/**
* Internal dependencies
@@ -56,7 +57,7 @@ const LoadingPlaceholder = () => (
);
-class StoreDetails extends Component {
+export class StoreDetails extends Component {
constructor( props ) {
super( props );
@@ -205,11 +206,7 @@ class StoreDetails extends Component {
const validateAddress = getStoreAddressValidator( locale );
const errors = validateAddress( values );
- if (
- values.storeEmail &&
- values.storeEmail.trim().length &&
- values.storeEmail.indexOf( '@' ) === -1
- ) {
+ if ( ! isEmail( values.storeEmail ) ) {
errors.storeEmail = __(
'Invalid email address',
'woocommerce-admin'
diff --git a/plugins/woocommerce-admin/client/profile-wizard/steps/store-details/test/__snapshots__/index.js.snap b/plugins/woocommerce-admin/client/profile-wizard/steps/store-details/test/__snapshots__/index.js.snap
new file mode 100644
index 00000000000..f65b44cf25c
--- /dev/null
+++ b/plugins/woocommerce-admin/client/profile-wizard/steps/store-details/test/__snapshots__/index.js.snap
@@ -0,0 +1,426 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`StoreDetails Snapshot test should match saved snapshot 1`] = `
+Object {
+ "asFragment": [Function],
+ "baseElement":
+
+ Notifications
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ "container":
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
,
+ "debug": [Function],
+ "findAllByAltText": [Function],
+ "findAllByDisplayValue": [Function],
+ "findAllByLabelText": [Function],
+ "findAllByPlaceholderText": [Function],
+ "findAllByRole": [Function],
+ "findAllByTestId": [Function],
+ "findAllByText": [Function],
+ "findAllByTitle": [Function],
+ "findByAltText": [Function],
+ "findByDisplayValue": [Function],
+ "findByLabelText": [Function],
+ "findByPlaceholderText": [Function],
+ "findByRole": [Function],
+ "findByTestId": [Function],
+ "findByText": [Function],
+ "findByTitle": [Function],
+ "getAllByAltText": [Function],
+ "getAllByDisplayValue": [Function],
+ "getAllByLabelText": [Function],
+ "getAllByPlaceholderText": [Function],
+ "getAllByRole": [Function],
+ "getAllByTestId": [Function],
+ "getAllByText": [Function],
+ "getAllByTitle": [Function],
+ "getByAltText": [Function],
+ "getByDisplayValue": [Function],
+ "getByLabelText": [Function],
+ "getByPlaceholderText": [Function],
+ "getByRole": [Function],
+ "getByTestId": [Function],
+ "getByText": [Function],
+ "getByTitle": [Function],
+ "queryAllByAltText": [Function],
+ "queryAllByDisplayValue": [Function],
+ "queryAllByLabelText": [Function],
+ "queryAllByPlaceholderText": [Function],
+ "queryAllByRole": [Function],
+ "queryAllByTestId": [Function],
+ "queryAllByText": [Function],
+ "queryAllByTitle": [Function],
+ "queryByAltText": [Function],
+ "queryByDisplayValue": [Function],
+ "queryByLabelText": [Function],
+ "queryByPlaceholderText": [Function],
+ "queryByRole": [Function],
+ "queryByTestId": [Function],
+ "queryByText": [Function],
+ "queryByTitle": [Function],
+ "rerender": [Function],
+ "unmount": [Function],
+}
+`;
diff --git a/plugins/woocommerce-admin/client/profile-wizard/steps/store-details/test/index.js b/plugins/woocommerce-admin/client/profile-wizard/steps/store-details/test/index.js
new file mode 100644
index 00000000000..50950976ba6
--- /dev/null
+++ b/plugins/woocommerce-admin/client/profile-wizard/steps/store-details/test/index.js
@@ -0,0 +1,83 @@
+/**
+ * External dependencies
+ */
+import { render } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
+/**
+ * Internal dependencies
+ */
+import { StoreDetails } from '../';
+
+const testProps = {
+ query: {
+ page: 'wc-admin',
+ path: '/setup-wizard',
+ },
+ step: {
+ key: 'store-details',
+ label: 'Store Details',
+ isComplete: false,
+ },
+ initialValues: {
+ addressLine1: '',
+ addressLine2: '',
+ city: '',
+ countryState: '',
+ postCode: '',
+ isAgreeMarketing: true,
+ storeEmail: 'wordpress@example.com',
+ },
+ getLocale: jest.fn(),
+ isLoading: false,
+};
+
+describe( 'StoreDetails', () => {
+ describe( 'Snapshot test', () => {
+ test( 'should match saved snapshot', () => {
+ const container = render( );
+ expect( container ).toMatchSnapshot();
+ } );
+ } );
+ describe( 'Email validation test cases', () => {
+ // test cases taken from wordpress php is_email test cases
+ // https://github.com/WordPress/wordpress-develop/blob/2648a5f984b8abf06872151898e3a61d3458a628/tests/phpunit/tests/formatting/isEmail.php
+ test.each( [
+ 'khaaaaaaaaaaaaaaan!',
+ 'http://bob.example.com/',
+ "sif i'd give u it, spamer!1",
+ 'com.exampleNOSPAMbob',
+ 'bob@your mom',
+ 'a@b.c',
+ ] )( 'should fail email validation when given %s', async ( email ) => {
+ const container = render( );
+ const emailInput = container.getByLabelText( 'Email address' );
+ await userEvent.clear( emailInput );
+ await userEvent.type( emailInput, email );
+ // validation is triggered onChange but error message only renders on blur
+ // react testing lib doesn't have a "blur" event that can be triggered so this does the job of triggering the error message rendering
+ userEvent.tab();
+ expect(
+ container.queryByText( 'Invalid email address' )
+ ).toBeInTheDocument();
+ } );
+
+ test.each( [
+ 'bob@example.com',
+ 'phil@example.info',
+ // 'ace@204.32.222.14', this testcase passes for the backend validation but fails here, following up in a PR to fix this in @wordpress/url
+ 'kevin@many.subdomains.make.a.happy.man.edu',
+ 'a@b.co',
+ 'bill+ted@example.com',
+ ] )( 'should pass email validation when given %s', async ( email ) => {
+ const container = render( );
+ const emailInput = container.getByLabelText( 'Email address' );
+ await userEvent.clear( emailInput );
+ await userEvent.type( emailInput, email );
+ userEvent.tab();
+ expect(
+ container.queryByText( 'Invalid email address' )
+ ).toBeNull();
+ } );
+ } );
+} );
diff --git a/plugins/woocommerce-admin/package-lock.json b/plugins/woocommerce-admin/package-lock.json
index cc7f945dc25..59a6f7769c7 100644
--- a/plugins/woocommerce-admin/package-lock.json
+++ b/plugins/woocommerce-admin/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@woocommerce/admin-library",
- "version": "3.1.0-dev",
+ "version": "3.2.0-dev",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -9420,6 +9420,16 @@
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz",
"integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
},
+ "@types/yauzl": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz",
+ "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
"@typescript-eslint/eslint-plugin": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.1.tgz",
@@ -15642,18 +15652,6 @@
"debug": "^4.1.1",
"get-stream": "^5.1.0",
"yauzl": "^2.10.0"
- },
- "dependencies": {
- "@types/yauzl": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz",
- "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==",
- "dev": true,
- "optional": true,
- "requires": {
- "@types/node": "*"
- }
- }
}
},
"file-entry-cache": {
@@ -16825,36 +16823,6 @@
"tar-fs": "^2.0.0",
"unbzip2-stream": "^1.3.3",
"ws": "^7.2.3"
- },
- "dependencies": {
- "chownr": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
- "dev": true
- },
- "tar-fs": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
- "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
- "dev": true,
- "requires": {
- "chownr": "^1.1.1",
- "mkdirp-classic": "^0.5.2",
- "pump": "^3.0.0",
- "tar-stream": "^2.1.4"
- }
- },
- "unbzip2-stream": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
- "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
- "dev": true,
- "requires": {
- "buffer": "^5.2.1",
- "through": "^2.3.8"
- }
- }
}
},
"read-pkg": {
@@ -17795,7 +17763,9 @@
"dev": true
},
"ansi-regex": {
- "version": "5.0.1"
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles": {
"version": "3.2.1",
@@ -32985,7 +32955,9 @@
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
},
"json-schema": {
- "version": "0.4.0"
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
},
"json-schema-traverse": {
"version": "0.4.1",
@@ -33039,6 +33011,8 @@
},
"jsprim": {
"version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+ "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
@@ -36032,6 +36006,8 @@
},
"nth-check": {
"version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+ "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
"requires": {
"boolbase": "^1.0.0"
}
@@ -38132,6 +38108,8 @@
},
"prismjs": {
"version": "1.25.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz",
+ "integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==",
"dev": true
},
"process": {
@@ -39536,11 +39514,13 @@
},
"refractor": {
"version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.5.0.tgz",
+ "integrity": "sha512-QwPJd3ferTZ4cSPPjdP5bsYHMytwWYnAN5EEnLtGvkqp/FCCnGsBgxrm9EuIDnjUC3Uc/kETtvVi7fSIVC74Dg==",
"dev": true,
"requires": {
"hastscript": "^6.0.0",
"parse-entities": "^2.0.0",
- "prismjs": "~1.24.0"
+ "prismjs": "~1.25.0"
}
},
"regenerate": {
@@ -42447,6 +42427,18 @@
}
}
},
+ "tar-fs": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
+ "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
+ "dev": true,
+ "requires": {
+ "chownr": "^1.1.1",
+ "mkdirp-classic": "^0.5.2",
+ "pump": "^3.0.0",
+ "tar-stream": "^2.1.4"
+ }
+ },
"tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
@@ -43357,6 +43349,16 @@
"which-boxed-primitive": "^1.0.2"
}
},
+ "unbzip2-stream": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
+ "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
+ "dev": true,
+ "requires": {
+ "buffer": "^5.2.1",
+ "through": "^2.3.8"
+ }
+ },
"unc-path-regex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
@@ -45132,4 +45134,4 @@
"dev": true
}
}
-}
\ No newline at end of file
+}