11import React from 'react' ;
2+ import { Provider } from 'react-redux' ;
23
34import { injectIntl , IntlProvider } from '@edx/frontend-platform/i18n' ;
45import { mount } from 'enzyme' ;
56import { act } from 'react-dom/test-utils' ;
7+ import { MemoryRouter } from 'react-router-dom' ;
8+ import configureStore from 'redux-mock-store' ;
69
10+ import { fetchRealtimeValidations } from '../../register/data/actions' ;
711import FormGroup from '../FormGroup' ;
812import PasswordField from '../PasswordField' ;
913
@@ -26,10 +30,27 @@ describe('FormGroup', () => {
2630} ) ;
2731
2832describe ( 'PasswordField' , ( ) => {
33+ const mockStore = configureStore ( ) ;
2934 const IntlPasswordField = injectIntl ( PasswordField ) ;
3035 let props = { } ;
36+ let store = { } ;
37+
38+ const reduxWrapper = children => (
39+ < IntlProvider locale = "en" >
40+ < MemoryRouter >
41+ < Provider store = { store } > { children } </ Provider >
42+ </ MemoryRouter >
43+ </ IntlProvider >
44+ ) ;
45+
46+ const initialState = {
47+ register : {
48+ validationApiRateLimited : false ,
49+ } ,
50+ } ;
3151
3252 beforeEach ( ( ) => {
53+ store = mockStore ( initialState ) ;
3354 props = {
3455 floatingLabel : 'Password' ,
3556 name : 'password' ,
@@ -39,7 +60,7 @@ describe('PasswordField', () => {
3960 } ) ;
4061
4162 it ( 'should show/hide password on icon click' , ( ) => {
42- const passwordField = mount ( < IntlProvider locale = "en" > < IntlPasswordField { ...props } /> </ IntlProvider > ) ;
63+ const passwordField = mount ( reduxWrapper ( < IntlPasswordField { ...props } /> ) ) ;
4364
4465 passwordField . find ( 'button[aria-label="Show password"]' ) . simulate ( 'click' ) ;
4566 expect ( passwordField . find ( 'input' ) . prop ( 'type' ) ) . toEqual ( 'text' ) ;
@@ -49,7 +70,7 @@ describe('PasswordField', () => {
4970 } ) ;
5071
5172 it ( 'should show password requirement tooltip on focus' , async ( ) => {
52- const passwordField = mount ( < IntlProvider locale = "en" > < IntlPasswordField { ...props } /> </ IntlProvider > ) ;
73+ const passwordField = mount ( reduxWrapper ( < IntlPasswordField { ...props } /> ) ) ;
5374 jest . useFakeTimers ( ) ;
5475 await act ( async ( ) => {
5576 passwordField . find ( 'input' ) . simulate ( 'focus' ) ;
@@ -67,7 +88,7 @@ describe('PasswordField', () => {
6788 } ;
6889
6990 jest . useFakeTimers ( ) ;
70- const passwordField = mount ( < IntlProvider locale = "en" > < IntlPasswordField { ...props } /> </ IntlProvider > ) ;
91+ const passwordField = mount ( reduxWrapper ( < IntlPasswordField { ...props } /> ) ) ;
7192 await act ( async ( ) => {
7293 passwordField . find ( 'input' ) . simulate ( 'focus' ) ;
7394 jest . runAllTimers ( ) ;
@@ -80,7 +101,7 @@ describe('PasswordField', () => {
80101 } ) ;
81102
82103 it ( 'should update password requirement checks' , async ( ) => {
83- const passwordField = mount ( < IntlProvider locale = "en" > < IntlPasswordField { ...props } /> </ IntlProvider > ) ;
104+ const passwordField = mount ( reduxWrapper ( < IntlPasswordField { ...props } /> ) ) ;
84105 jest . useFakeTimers ( ) ;
85106 await act ( async ( ) => {
86107 passwordField . find ( 'input' ) . simulate ( 'focus' ) ;
@@ -92,4 +113,116 @@ describe('PasswordField', () => {
92113 expect ( passwordField . find ( '#number-check span' ) . prop ( 'className' ) ) . toEqual ( 'pgn__icon text-success mr-1' ) ;
93114 expect ( passwordField . find ( '#characters-check span' ) . prop ( 'className' ) ) . toEqual ( 'pgn__icon text-success mr-1' ) ;
94115 } ) ;
116+
117+ it ( 'should not run validations when blur is fired on password icon click' , ( ) => {
118+ const passwordField = mount ( reduxWrapper ( < IntlPasswordField { ...props } /> ) ) ;
119+
120+ passwordField . find ( 'button[aria-label="Show password"]' ) . simulate ( 'blur' , {
121+ target : {
122+ name : 'password' ,
123+ value : 'invalid' ,
124+ } ,
125+ relatedTarget : {
126+ name : 'passwordIcon' ,
127+ } ,
128+ } ) ;
129+
130+ expect ( passwordField . find ( 'div[feedback-for="password"]' ) . exists ( ) ) . toBeFalsy ( ) ;
131+ } ) ;
132+
133+ it ( 'should call props handle blur if available' , ( ) => {
134+ props = {
135+ ...props ,
136+ handleBlur : jest . fn ( ) ,
137+ } ;
138+ const passwordField = mount ( reduxWrapper ( < IntlPasswordField { ...props } /> ) ) ;
139+
140+ passwordField . find ( 'input#password' ) . simulate ( 'blur' , {
141+ target : {
142+ name : 'password' ,
143+ value : '' ,
144+ } ,
145+ } ) ;
146+
147+ expect ( props . handleBlur ) . toHaveBeenCalledTimes ( 1 ) ;
148+ } ) ;
149+
150+ it ( 'should run validations when blur event when rendered from register page' , ( ) => {
151+ props = {
152+ ...props ,
153+ handleErrorChange : jest . fn ( ) ,
154+ } ;
155+ const passwordField = mount ( reduxWrapper ( < IntlPasswordField { ...props } /> ) ) ;
156+
157+ passwordField . find ( 'input#password' ) . simulate ( 'blur' , {
158+ target : {
159+ name : 'password' ,
160+ value : '' ,
161+ } ,
162+ } ) ;
163+
164+ expect ( props . handleErrorChange ) . toHaveBeenCalledTimes ( 1 ) ;
165+ expect ( props . handleErrorChange ) . toHaveBeenCalledWith (
166+ 'password' ,
167+ 'Password criteria has not been met' ,
168+ ) ;
169+ } ) ;
170+
171+ it ( 'should not clear error when focus is fired on password icon click when rendered from register page' , ( ) => {
172+ props = {
173+ ...props ,
174+ handleErrorChange : jest . fn ( ) ,
175+ } ;
176+
177+ const passwordField = mount ( reduxWrapper ( < IntlPasswordField { ...props } /> ) ) ;
178+
179+ passwordField . find ( 'button[aria-label="Show password"]' ) . simulate ( 'focus' , {
180+ target : {
181+ name : 'passwordIcon' ,
182+ value : '' ,
183+ } ,
184+ } ) ;
185+
186+ expect ( props . handleErrorChange ) . toHaveBeenCalledTimes ( 0 ) ;
187+ } ) ;
188+
189+ it ( 'should clear error when focus is fired on password icon click when rendered from register page' , ( ) => {
190+ props = {
191+ ...props ,
192+ handleErrorChange : jest . fn ( ) ,
193+ } ;
194+
195+ const passwordField = mount ( reduxWrapper ( < IntlPasswordField { ...props } /> ) ) ;
196+
197+ passwordField . find ( 'button[aria-label="Show password"]' ) . simulate ( 'focus' , {
198+ target : {
199+ name : 'password' ,
200+ value : 'invalid' ,
201+ } ,
202+ } ) ;
203+
204+ expect ( props . handleErrorChange ) . toHaveBeenCalledTimes ( 1 ) ;
205+ expect ( props . handleErrorChange ) . toHaveBeenCalledWith (
206+ 'password' ,
207+ '' ,
208+ ) ;
209+ } ) ;
210+
211+ it ( 'should run backend validations when frontend validations pass on blur when rendered from register page' , ( ) => {
212+ store . dispatch = jest . fn ( store . dispatch ) ;
213+ props = {
214+ ...props ,
215+ handleErrorChange : jest . fn ( ) ,
216+ } ;
217+ const passwordField = mount ( reduxWrapper ( < IntlPasswordField { ...props } /> ) ) ;
218+
219+ passwordField . find ( 'button[aria-label="Show password"]' ) . simulate ( 'blur' , {
220+ target : {
221+ name : 'password' ,
222+ value : 'password123' ,
223+ } ,
224+ } ) ;
225+
226+ expect ( store . dispatch ) . toHaveBeenCalledWith ( fetchRealtimeValidations ( { password : 'password123' } ) ) ;
227+ } ) ;
95228} ) ;
0 commit comments