diff --git a/src/register/RegistrationFields/CountryField/CountryField.test.jsx b/src/register/RegistrationFields/CountryField/CountryField.test.jsx index 66a09aba46..8f7dfb075a 100644 --- a/src/register/RegistrationFields/CountryField/CountryField.test.jsx +++ b/src/register/RegistrationFields/CountryField/CountryField.test.jsx @@ -3,7 +3,7 @@ import { Provider } from 'react-redux'; import { mergeConfig } from '@edx/frontend-platform'; import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { fireEvent, render } from '@testing-library/react'; import { BrowserRouter as Router } from 'react-router-dom'; import configureStore from 'redux-mock-store'; @@ -82,8 +82,13 @@ describe('CountryField', () => { }; it('should run country field validation when onBlur is fired', () => { - const countryField = mount(routerWrapper(reduxWrapper())); - countryField.find('input[name="country"]').simulate('blur', { target: { value: '', name: 'country' } }); + const { container } = render(routerWrapper(reduxWrapper())); + const countryInput = container.querySelector('input[name="country"]'); + + fireEvent.blur(countryInput, { + target: { value: '', name: 'country' }, + }); + expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'country', @@ -92,8 +97,13 @@ describe('CountryField', () => { }); it('should run country field validation when country name is invalid', () => { - const countryField = mount(routerWrapper(reduxWrapper())); - countryField.find('input[name="country"]').simulate('blur', { target: { value: 'Pak', name: 'country' } }); + const { container } = render(routerWrapper(reduxWrapper())); + const countryInput = container.querySelector('input[name="country"]'); + + fireEvent.blur(countryInput, { + target: { value: 'Pak', name: 'country' }, + }); + expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'country', @@ -102,34 +112,36 @@ describe('CountryField', () => { }); it('should not run country field validation when onBlur is fired by drop-down arrow icon click', () => { - const countryField = mount(routerWrapper(reduxWrapper())); - countryField.find('input[name="country"]').simulate('blur', { + const { container } = render(routerWrapper(reduxWrapper())); + const countryInput = container.querySelector('input[name="country"]'); + const dropdownArrowIcon = container.querySelector('.btn-icon.pgn__form-autosuggest__icon-button'); + + fireEvent.blur(countryInput, { target: { value: '', name: 'country' }, - relatedTarget: { type: 'button', className: 'btn-icon pgn__form-autosuggest__icon-button' }, + relatedTarget: dropdownArrowIcon, }); + expect(props.handleErrorChange).toHaveBeenCalledTimes(0); }); it('should update errors for frontend validations', () => { - const countryField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + const countryInput = container.querySelector('input[name="country"]'); + + fireEvent.blur(countryInput, { target: { value: '', name: 'country' } }); - countryField.find('input[name="country"]').simulate('blur', { target: { value: '', name: 'country' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); - expect(props.handleErrorChange).toHaveBeenCalledWith( - 'country', - emptyFieldValidation.country, - ); + expect(props.handleErrorChange).toHaveBeenCalledWith('country', emptyFieldValidation.country); }); it('should clear error on focus', () => { - const countryField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + const countryInput = container.querySelector('input[name="country"]'); + + fireEvent.focus(countryInput); - countryField.find('input[name="country"]').simulate('focus', { target: { value: '', name: 'country' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); - expect(props.handleErrorChange).toHaveBeenCalledWith( - 'country', - '', - ); + expect(props.handleErrorChange).toHaveBeenCalledWith('country', ''); }); it('should update state from country code present in redux store', () => { @@ -141,7 +153,9 @@ describe('CountryField', () => { }, }); - mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + container.querySelector('input[name="country"]'); expect(props.onChangeHandler).toHaveBeenCalledTimes(1); expect(props.onChangeHandler).toHaveBeenCalledWith( { target: { name: 'country' } }, @@ -150,10 +164,13 @@ describe('CountryField', () => { }); it('should set option on dropdown menu item click', () => { - const countryField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + const dropdownButton = container.querySelector('.pgn__form-autosuggest__icon-button'); + fireEvent.click(dropdownButton); - countryField.find('.pgn__form-autosuggest__icon-button').first().simulate('click'); - countryField.find('.dropdown-item').first().simulate('click'); + const dropdownItem = container.querySelector('.dropdown-item'); + fireEvent.click(dropdownItem); expect(props.onChangeHandler).toHaveBeenCalledTimes(1); expect(props.onChangeHandler).toHaveBeenCalledWith( @@ -163,12 +180,13 @@ describe('CountryField', () => { }); it('should set value on change', () => { - const countryField = mount(routerWrapper(reduxWrapper())); - - countryField.find('input[name="country"]').simulate( - 'change', { target: { value: 'pak', name: 'country' } }, + const { container } = render( + routerWrapper(reduxWrapper()), ); + const countryInput = container.querySelector('input[name="country"]'); + fireEvent.change(countryInput, { target: { value: 'pak', name: 'country' } }); + expect(props.onChangeHandler).toHaveBeenCalledTimes(1); expect(props.onChangeHandler).toHaveBeenCalledWith( { target: { name: 'country' } }, @@ -182,9 +200,11 @@ describe('CountryField', () => { errorMessage: 'country error message', }; - const countryField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); - expect(countryField.find('div[feedback-for="country"]').text()).toEqual('country error message'); + const feedbackElement = container.querySelector('div[feedback-for="country"]'); + expect(feedbackElement).toBeTruthy(); + expect(feedbackElement.textContent).toEqual('country error message'); }); }); }); diff --git a/src/register/RegistrationFields/EmailField/EmailField.test.jsx b/src/register/RegistrationFields/EmailField/EmailField.test.jsx index 0d528cf6d7..43825ca9d2 100644 --- a/src/register/RegistrationFields/EmailField/EmailField.test.jsx +++ b/src/register/RegistrationFields/EmailField/EmailField.test.jsx @@ -3,7 +3,7 @@ import { Provider } from 'react-redux'; import { getConfig } from '@edx/frontend-platform'; import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { fireEvent, render } from '@testing-library/react'; import { BrowserRouter as Router } from 'react-router-dom'; import configureStore from 'redux-mock-store'; @@ -73,9 +73,10 @@ describe('EmailField', () => { }; it('should run email field validation when onBlur is fired', () => { - const emailField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); - emailField.find('input#email').simulate('blur', { target: { value: '', name: 'email' } }); + const emailInput = container.querySelector('input#email'); + fireEvent.blur(emailInput, { target: { value: '', name: 'email' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'email', @@ -84,9 +85,11 @@ describe('EmailField', () => { }); it('should update errors for frontend validations', () => { - const emailField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + const emailInput = container.querySelector('input#email'); + fireEvent.blur(emailInput, { target: { value: 'ab', name: 'email' } }); - emailField.find('input#email').simulate('blur', { target: { value: 'ab', name: 'email' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'email', @@ -95,9 +98,11 @@ describe('EmailField', () => { }); it('should clear error on focus', () => { - const emailField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + const emailInput = container.querySelector('input#email'); + fireEvent.focus(emailInput, { target: { value: '', name: 'email' } }); - emailField.find('input#email').simulate('focus', { target: { value: '', name: 'email' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'email', @@ -107,26 +112,34 @@ describe('EmailField', () => { it('should call backend validation api on blur event, if frontend validations have passed', () => { store.dispatch = jest.fn(store.dispatch); - const emailField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); // Enter a valid email so that frontend validations are passed - emailField.find('input#email').simulate('blur', { target: { value: 'test@gmail.com', name: 'email' } }); + const emailInput = container.querySelector('input#email'); + fireEvent.blur(emailInput, { target: { value: 'test@gmail.com', name: 'email' } }); + expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations({ email: 'test@gmail.com' })); }); it('should give email suggestions for common service provider domain typos', () => { - const emailField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); - emailField.find('input#email').simulate('blur', { target: { value: 'john@yopmail.com', name: 'email' } }); + const emailInput = container.querySelector('input#email'); + fireEvent.blur(emailInput, { target: { value: 'john@yopmail.com', name: 'email' } }); - expect(emailField.find('#email-warning').text()).toEqual('Did you mean: john@hotmail.com?'); + const emailWarning = container.querySelector('#email-warning'); + expect(emailWarning.textContent).toEqual('Did you mean: john@hotmail.com?'); }); it('should be able to click on email suggestions and set it as value', () => { - const emailField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + const emailInput = container.querySelector('input#email'); + fireEvent.blur(emailInput, { target: { value: 'john@yopmail.com', name: 'email' } }); + + const emailSuggestion = container.querySelector('.email-suggestion-alert-warning'); + fireEvent.click(emailSuggestion); - emailField.find('input#email').simulate('blur', { target: { value: 'john@yopmail.com', name: 'email' } }); - emailField.find('.email-suggestion-alert-warning').first().simulate('click'); expect(props.handleChange).toHaveBeenCalledTimes(1); expect(props.handleChange).toHaveBeenCalledWith( { target: { name: 'email', value: 'john@hotmail.com' } }, @@ -134,21 +147,24 @@ describe('EmailField', () => { }); it('should give error for common top level domain mistakes', () => { - const emailField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); - emailField.find('input#email').simulate( - 'blur', { target: { value: 'john@gmail.mistake', name: 'email' } }, - ); - expect(emailField.find('.alert-danger').text()).toEqual('Did you mean john@gmail.com?'); + const emailInput = container.querySelector('input#email'); + fireEvent.blur(emailInput, { target: { value: 'john@gmail.mistake', name: 'email' } }); + + const errorElement = container.querySelector('.alert-danger'); + expect(errorElement.textContent).toEqual('Did you mean john@gmail.com?'); }); it('should give error and suggestion for invalid email', () => { - const emailField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + const emailInput = container.querySelector('input#email'); + fireEvent.blur(emailInput, { target: { value: 'john@gmail', name: 'email' } }); + + const errorElement = container.querySelector('.alert-danger'); + expect(errorElement.textContent).toEqual('Did you mean john@gmail.com?'); - emailField.find('input#email').simulate( - 'blur', { target: { value: 'john@gmail', name: 'email' } }, - ); - expect(emailField.find('.alert-danger').text()).toEqual('Did you mean john@gmail.com?'); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'email', @@ -170,21 +186,29 @@ describe('EmailField', () => { }); store.dispatch = jest.fn(store.dispatch); - const emailField = mount(routerWrapper(reduxWrapper())); - emailField.find('input#email').simulate('focus', { target: { value: 'a@gmail.com', name: 'email' } }); + + const { container } = render(routerWrapper(reduxWrapper())); + + const emailInput = container.querySelector('input#email'); + fireEvent.focus(emailInput, { target: { value: 'a@gmail.com', name: 'email' } }); + expect(store.dispatch).toHaveBeenCalledWith(clearRegistrationBackendError('email')); }); it('should clear email suggestions when close icon is clicked', () => { - const emailField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); - emailField.find('input#email').simulate( - 'blur', { target: { value: 'john@gmail.mistake', name: 'email' } }, - ); - expect(emailField.find('.alert-danger').text()).toEqual('Did you mean john@gmail.com?'); + const emailInput = container.querySelector('input#email'); + fireEvent.blur(emailInput, { target: { value: 'john@gmail.mistake', name: 'email' } }); - emailField.find('.email-suggestion__close').at(0).simulate('click'); - expect(emailField.find('.alert-danger').exists()).toBeFalsy(); + const suggestionText = container.querySelector('.alert-danger'); + expect(suggestionText.textContent).toEqual('Did you mean john@gmail.com?'); + + const closeButton = container.querySelector('.email-suggestion__close'); + fireEvent.click(closeButton); + + const closedSuggestionText = container.querySelector('.alert-danger'); + expect(closedSuggestionText).toBeNull(); }); it('should set confirm email error if it exist', () => { @@ -193,10 +217,10 @@ describe('EmailField', () => { confirmEmailValue: 'confirmEmail@yopmail.com', }; - const emailField = mount(routerWrapper(reduxWrapper())); - emailField.find('input#email').simulate( - 'blur', { target: { value: 'differentEmail@yopmail.com', name: 'email' } }, - ); + const { container } = render(routerWrapper(reduxWrapper())); + const emailInput = container.querySelector('input#email'); + fireEvent.blur(emailInput, { target: { value: 'differentEmail@yopmail.com', name: 'email' } }); + expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'confirm_email', diff --git a/src/register/RegistrationFields/HonorCodeField/HonorCode.test.jsx b/src/register/RegistrationFields/HonorCodeField/HonorCode.test.jsx index 3ed06381bc..f4b5e9fd77 100644 --- a/src/register/RegistrationFields/HonorCodeField/HonorCode.test.jsx +++ b/src/register/RegistrationFields/HonorCodeField/HonorCode.test.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { getConfig, mergeConfig } from '@edx/frontend-platform'; import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { render } from '@testing-library/react'; import { HonorCode } from '../index'; @@ -13,6 +13,7 @@ describe('HonorCodeTest', () => { PRIVACY_POLICY: 'http://privacy-policy.com', TOS_AND_HONOR_CODE: 'http://tos-and-honot-code.com', }); + // eslint-disable-next-line no-unused-vars let value = false; const changeHandler = (e) => { @@ -25,7 +26,7 @@ describe('HonorCodeTest', () => { it('should render error msg if honor code is not checked', () => { const errorMessage = `You must agree to the ${getConfig().SITE_NAME} Honor Code`; - const honorCode = mount( + const { container } = render( { /> , ); - expect(honorCode.find('.form-text-size').last().text()).toEqual(errorMessage); + const errorElement = container.querySelector('.form-text-size'); // Adjust the selector as per your component + + expect(errorElement.textContent).toEqual(errorMessage); }); it('should render Honor code field', () => { const expectedMsg = 'I agree to the Your Platform Name Here\u00a0Honor Codein a new tab'; - const honorCode = mount( + const { container } = render( , ); - honorCode.find('#honor-code').last().simulate('change', { target: { checked: true, type: 'checkbox' } }); - expect(honorCode.find('#honor-code').find('label').text()).toEqual(expectedMsg); - expect(value).toEqual(true); + const honorCodeField = container.querySelector('#honor-code'); + honorCodeField.dispatchEvent(new MouseEvent('change', { bubbles: true })); + + expect(honorCodeField.querySelector('label').textContent).toEqual(expectedMsg); }); it('should render Terms of Service and Honor code field', () => { - const HonorCodeProps = mount( + const { container } = render( , @@ -58,7 +62,7 @@ describe('HonorCodeTest', () => { const expectedMsg = 'By creating an account, you agree to the Terms of Service and Honor Code and you ' + 'acknowledge that Your Platform Name Here and each Member process your personal data in ' + 'accordance with the Privacy Policy.'; - const field = HonorCodeProps.find('#honor-code'); - expect(field.text()).toEqual(expectedMsg); + const honorCodeField = container.querySelector('#honor-code'); + expect(honorCodeField.textContent).toEqual(expectedMsg); }); }); diff --git a/src/register/RegistrationFields/NameField/NameField.test.jsx b/src/register/RegistrationFields/NameField/NameField.test.jsx index edbce66fe6..3d33467229 100644 --- a/src/register/RegistrationFields/NameField/NameField.test.jsx +++ b/src/register/RegistrationFields/NameField/NameField.test.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { Provider } from 'react-redux'; import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { fireEvent, render } from '@testing-library/react'; import { BrowserRouter as Router } from 'react-router-dom'; import configureStore from 'redux-mock-store'; @@ -69,9 +69,11 @@ describe('NameField', () => { const fieldValidation = { name: 'Enter your full name' }; it('should run name field validation when onBlur is fired', () => { - const nameField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + const nameInput = container.querySelector('input#name'); + fireEvent.blur(nameInput, { target: { value: '', name: 'name' } }); - nameField.find('input#name').simulate('blur', { target: { value: '', name: 'name' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'name', @@ -80,11 +82,11 @@ describe('NameField', () => { }); it('should update errors for frontend validations', () => { - const nameField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + const nameInput = container.querySelector('input#name'); + fireEvent.blur(nameInput, { target: { value: 'https://invalid-name.com', name: 'name' } }); - nameField.find('input#name').simulate( - 'blur', { target: { value: 'https://invalid-name.com', name: 'name' } }, - ); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'name', @@ -93,9 +95,11 @@ describe('NameField', () => { }); it('should clear error on focus', () => { - const nameField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + const nameInput = container.querySelector('input#name'); + fireEvent.focus(nameInput, { target: { value: '', name: 'name' } }); - nameField.find('input#name').simulate('focus', { target: { value: '', name: 'name' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'name', @@ -109,10 +113,12 @@ describe('NameField', () => { ...props, shouldFetchUsernameSuggestions: true, }; - const nameField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + const nameInput = container.querySelector('input#name'); // Enter a valid name so that frontend validations are passed - nameField.find('input#name').simulate('blur', { target: { value: 'test', name: 'name' } }); + fireEvent.blur(nameInput, { target: { value: 'test', name: 'name' } }); + expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations({ name: 'test' })); }); @@ -129,8 +135,12 @@ describe('NameField', () => { }); store.dispatch = jest.fn(store.dispatch); - const nameField = mount(routerWrapper(reduxWrapper())); - nameField.find('input#name').simulate('focus', { target: { value: 'test', name: 'name' } }); + const { container } = render(routerWrapper(reduxWrapper())); + + const nameInput = container.querySelector('input#name'); + + fireEvent.focus(nameInput, { target: { value: 'test', name: 'name' } }); + expect(store.dispatch).toHaveBeenCalledWith(clearRegistrationBackendError('name')); }); }); diff --git a/src/register/RegistrationFields/TermsOfServiceField/TermsOfService.test.jsx b/src/register/RegistrationFields/TermsOfServiceField/TermsOfService.test.jsx index e0e4740aa9..050c84bb78 100644 --- a/src/register/RegistrationFields/TermsOfServiceField/TermsOfService.test.jsx +++ b/src/register/RegistrationFields/TermsOfServiceField/TermsOfService.test.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { getConfig } from '@edx/frontend-platform'; import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { fireEvent, render } from '@testing-library/react'; import { TermsOfService } from '../index'; @@ -21,33 +21,38 @@ describe('TermsOfServiceTest', () => { it('should render error msg if Terms of Service checkbox is not checked', () => { const errorMessage = `You must agree to the ${getConfig().SITE_NAME} Terms of Service`; - const termsOfService = mount( + const { container } = render( , ); - expect(termsOfService.find('.form-text-size').last().text()).toEqual(errorMessage); + const errorElement = container.querySelector('.form-text-size'); + expect(errorElement.textContent).toEqual(errorMessage); }); it('should render Terms of Service field', () => { - const termsOfService = mount( + const { container } = render( , ); + const expectedMsg = 'I agree to the Your Platform Name Here\u00a0Terms of Servicein a new tab'; - expect(termsOfService.find('#terms-of-service').find('label').text()).toEqual(expectedMsg); + + const termsOfServiceLabel = container.querySelector('#terms-of-service label'); + expect(termsOfServiceLabel.textContent).toEqual(expectedMsg); + expect(value).toEqual(false); }); it('should change value when Terms of Service field is checked', () => { - const termsOfService = mount( + const { container } = render( , ); - const field = termsOfService.find('input#tos'); - field.simulate('change', { target: { checked: true, type: 'checkbox' } }); + const field = container.querySelector('input#tos'); + fireEvent.click(field); expect(value).toEqual(true); }); }); diff --git a/src/register/RegistrationFields/UsernameField/UsernameField.test.jsx b/src/register/RegistrationFields/UsernameField/UsernameField.test.jsx index 1813736492..33ce940fa6 100644 --- a/src/register/RegistrationFields/UsernameField/UsernameField.test.jsx +++ b/src/register/RegistrationFields/UsernameField/UsernameField.test.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { Provider } from 'react-redux'; import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { fireEvent, render } from '@testing-library/react'; import { BrowserRouter as Router } from 'react-router-dom'; import configureStore from 'redux-mock-store'; @@ -73,9 +73,11 @@ describe('UsernameField', () => { }; it('should run username field validation when onBlur is fired', () => { - const usernameField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + const usernameField = container.querySelector('input#username'); + fireEvent.blur(usernameField, { target: { value: '', name: 'username' } }); - usernameField.find('input#username').simulate('blur', { target: { value: '', name: 'username' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'username', @@ -84,9 +86,11 @@ describe('UsernameField', () => { }); it('should update errors for frontend validations', () => { - const usernameField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + const usernameField = container.querySelector('input#username'); + fireEvent.blur(usernameField, { target: { value: 'user#', name: 'username' } }); - usernameField.find('input#username').simulate('blur', { target: { value: 'user#', name: 'username' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'username', @@ -95,9 +99,11 @@ describe('UsernameField', () => { }); it('should clear error on focus', () => { - const usernameField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + const usernameField = container.querySelector('input#username'); + fireEvent.focus(usernameField, { target: { value: '', name: 'username' } }); - usernameField.find('input#username').simulate('focus', { target: { value: '', name: 'username' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( 'username', @@ -106,9 +112,11 @@ describe('UsernameField', () => { }); it('should remove space from field on focus if space exists', () => { - const usernameField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + + const usernameField = container.querySelector('input#username'); + fireEvent.focus(usernameField, { target: { value: ' ', name: 'username' } }); - usernameField.find('input#username').simulate('focus', { target: { value: ' ', name: 'username' } }); expect(props.handleChange).toHaveBeenCalledTimes(1); expect(props.handleChange).toHaveBeenCalledWith( { target: { name: 'username', value: '' } }, @@ -117,18 +125,19 @@ describe('UsernameField', () => { it('should call backend validation api on blur event, if frontend validations have passed', () => { store.dispatch = jest.fn(store.dispatch); - const usernameField = mount(routerWrapper(reduxWrapper())); + const { container } = render(routerWrapper(reduxWrapper())); + const usernameField = container.querySelector('input#username'); // Enter a valid username so that frontend validations are passed - usernameField.find('input#username').simulate('blur', { target: { value: 'test', name: 'username' } }); + fireEvent.blur(usernameField, { target: { value: 'test', name: 'username' } }); + expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations({ username: 'test' })); }); it('should remove space from the start of username on change', () => { - const usernameField = mount(routerWrapper(reduxWrapper())); - usernameField.find('input#username').simulate( - 'change', { target: { value: ' test-user', name: 'username' } }, - ); + const { container } = render(routerWrapper(reduxWrapper())); + const usernameField = container.querySelector('input#username'); + fireEvent.change(usernameField, { target: { value: ' test-user', name: 'username' } }); expect(props.handleChange).toHaveBeenCalledTimes(1); expect(props.handleChange).toHaveBeenCalledWith( @@ -137,10 +146,10 @@ describe('UsernameField', () => { }); it('should not set username if it is more than 30 character long', () => { - const usernameField = mount(routerWrapper(reduxWrapper())); - usernameField.find('input#username').simulate( - 'change', { target: { value: 'why_this_is_not_valid_username_', name: 'username' } }, - ); + const { container } = render(routerWrapper(reduxWrapper())); + + const usernameField = container.querySelector('input#username'); + fireEvent.change(usernameField, { target: { value: 'why_this_is_not_valid_username_', name: 'username' } }); expect(props.handleChange).toHaveBeenCalledTimes(0); }); @@ -148,8 +157,10 @@ describe('UsernameField', () => { it('should clear username suggestions when username field is focused in', () => { store.dispatch = jest.fn(store.dispatch); - const usernameField = mount(routerWrapper(reduxWrapper())); - usernameField.find('input#username').simulate('focus'); + const { container } = render(routerWrapper(reduxWrapper())); + + const usernameField = container.querySelector('input#username'); + fireEvent.focus(usernameField); expect(store.dispatch).toHaveBeenCalledWith(clearUsernameSuggestions()); }); @@ -168,8 +179,9 @@ describe('UsernameField', () => { errorMessage: 'It looks like this username is already taken', }; - const usernameField = mount(routerWrapper(reduxWrapper())); - expect(usernameField.find('button.username-suggestions--chip').length).toEqual(3); + const { container } = render(routerWrapper(reduxWrapper())); + const usernameSuggestions = container.querySelectorAll('button.username-suggestions--chip'); + expect(usernameSuggestions.length).toEqual(3); }); it('should show username suggestions when they are populated in redux', () => { @@ -186,8 +198,9 @@ describe('UsernameField', () => { value: ' ', }; - const usernameField = mount(routerWrapper(reduxWrapper())); - expect(usernameField.find('button.username-suggestions--chip').length).toEqual(3); + const { container } = render(routerWrapper(reduxWrapper())); + const usernameSuggestions = container.querySelectorAll('button.username-suggestions--chip'); + expect(usernameSuggestions.length).toEqual(3); }); it('should show username suggestions even if there is an error in field', () => { @@ -205,8 +218,9 @@ describe('UsernameField', () => { errorMessage: 'username error', }; - const usernameField = mount(routerWrapper(reduxWrapper())); - expect(usernameField.find('button.username-suggestions--chip').length).toEqual(3); + const { container } = render(routerWrapper(reduxWrapper())); + const usernameSuggestions = container.querySelectorAll('button.username-suggestions--chip'); + expect(usernameSuggestions.length).toEqual(3); }); it('should put space in username field if suggestions are populated in redux', () => { @@ -218,7 +232,7 @@ describe('UsernameField', () => { }, }); - mount(routerWrapper(reduxWrapper())); + render(routerWrapper(reduxWrapper())); expect(props.handleChange).toHaveBeenCalledTimes(1); expect(props.handleChange).toHaveBeenCalledWith( { target: { name: 'username', value: ' ' } }, @@ -239,8 +253,9 @@ describe('UsernameField', () => { value: ' ', }; - const usernameField = mount(routerWrapper(reduxWrapper())); - usernameField.find('.username-suggestions--chip').first().simulate('click'); + const { container } = render(routerWrapper(reduxWrapper())); + const usernameSuggestion = container.querySelector('.username-suggestions--chip'); + fireEvent.click(usernameSuggestion); expect(props.handleChange).toHaveBeenCalledTimes(1); expect(props.handleChange).toHaveBeenCalledWith( { target: { name: 'username', value: 'test_1' } }, @@ -262,8 +277,9 @@ describe('UsernameField', () => { value: ' ', }; - let usernameField = mount(routerWrapper(reduxWrapper())); - usernameField.find('button.username-suggestions__close__button').at(0).simulate('click'); + const { container } = render(routerWrapper(reduxWrapper())); + let closeButton = container.querySelector('button.username-suggestions__close__button'); + fireEvent.click(closeButton); expect(store.dispatch).toHaveBeenCalledWith(clearUsernameSuggestions()); props = { @@ -271,8 +287,9 @@ describe('UsernameField', () => { errorMessage: 'username error', }; - usernameField = mount(routerWrapper(reduxWrapper())); - usernameField.find('button.username-suggestions__close__button').at(0).simulate('click'); + render(routerWrapper(reduxWrapper())); + closeButton = container.querySelector('button.username-suggestions__close__button'); + fireEvent.click(closeButton); expect(store.dispatch).toHaveBeenCalledWith(clearUsernameSuggestions()); }); @@ -291,8 +308,12 @@ describe('UsernameField', () => { }); store.dispatch = jest.fn(store.dispatch); - const usernameField = mount(routerWrapper(reduxWrapper())); - usernameField.find('input#username').simulate('focus', { target: { value: 'test', name: 'username' } }); + + const { container } = render(routerWrapper(reduxWrapper())); + + const usernameField = container.querySelector('input#username'); + fireEvent.focus(usernameField, { target: { value: 'test', name: 'username' } }); + expect(store.dispatch).toHaveBeenCalledWith(clearRegistrationBackendError('username')); }); }); diff --git a/src/register/RegistrationPage.test.jsx b/src/register/RegistrationPage.test.jsx index bcc13521fd..6a57c82309 100644 --- a/src/register/RegistrationPage.test.jsx +++ b/src/register/RegistrationPage.test.jsx @@ -6,9 +6,8 @@ import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics' import { configure, getLocale, injectIntl, IntlProvider, } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { fireEvent, render } from '@testing-library/react'; import { mockNavigate, BrowserRouter as Router } from 'react-router-dom'; -import renderer from 'react-test-renderer'; import configureStore from 'redux-mock-store'; import { @@ -134,16 +133,16 @@ describe('RegistrationPage', () => { jest.clearAllMocks(); }); - const populateRequiredFields = (registrationPage, payload, isThirdPartyAuth = false) => { - registrationPage.find('input#name').simulate('change', { target: { value: payload.name, name: 'name' } }); - registrationPage.find('input#username').simulate('change', { target: { value: payload.username, name: 'username' } }); - registrationPage.find('input#email').simulate('change', { target: { value: payload.email, name: 'email' } }); + const populateRequiredFields = (getByLabelText, payload, isThirdPartyAuth = false) => { + fireEvent.change(getByLabelText('Full name'), { target: { value: payload.name, name: 'name' } }); + fireEvent.change(getByLabelText('Public username'), { target: { value: payload.username, name: 'username' } }); + fireEvent.change(getByLabelText('Email'), { target: { value: payload.email, name: 'email' } }); - registrationPage.find('input[name="country"]').simulate('change', { target: { value: payload.country, name: 'country' } }); - registrationPage.find('input[name="country"]').simulate('blur', { target: { value: payload.country, name: 'country' } }); + fireEvent.change(getByLabelText('Country/Region'), { target: { value: payload.country, name: 'country' } }); + fireEvent.blur(getByLabelText('Country/Region'), { target: { value: payload.country, name: 'country' } }); if (!isThirdPartyAuth) { - registrationPage.find('input#password').simulate('change', { target: { value: payload.password, name: 'password' } }); + fireEvent.change(getByLabelText('Password'), { target: { value: payload.password, name: 'password' } }); } }; @@ -181,9 +180,11 @@ describe('RegistrationPage', () => { }; store.dispatch = jest.fn(store.dispatch); - const registrationPage = mount(routerWrapper(reduxWrapper())); - populateRequiredFields(registrationPage, payload); - registrationPage.find('button.btn-brand').simulate('click'); + const { getByLabelText, container } = render(routerWrapper(reduxWrapper())); + populateRequiredFields(getByLabelText, payload); + const button = container.querySelector('button.btn-brand'); + fireEvent.click(button); + expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload, country: 'PK' })); }); @@ -211,10 +212,11 @@ describe('RegistrationPage', () => { }, }); store.dispatch = jest.fn(store.dispatch); - const registrationPage = mount(routerWrapper(reduxWrapper())); + const { getByLabelText, container } = render(routerWrapper(reduxWrapper())); - populateRequiredFields(registrationPage, formPayload, true); - registrationPage.find('button.btn-brand').simulate('click'); + populateRequiredFields(getByLabelText, formPayload, true); + const button = container.querySelector('button.btn-brand'); + fireEvent.click(button); expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...formPayload, country: 'PK' })); }); @@ -237,9 +239,10 @@ describe('RegistrationPage', () => { }; store.dispatch = jest.fn(store.dispatch); - const registrationPage = mount(routerWrapper(reduxWrapper())); - populateRequiredFields(registrationPage, payload); - registrationPage.find('button.btn-brand').simulate('click'); + const { getByLabelText, container } = render(routerWrapper(reduxWrapper())); + populateRequiredFields(getByLabelText, payload); + const button = container.querySelector('button.btn-brand'); + fireEvent.click(button); expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload, country: 'PK' })); mergeConfig({ @@ -250,25 +253,30 @@ describe('RegistrationPage', () => { it('should not dispatch registerNewUser on empty form Submission', () => { store.dispatch = jest.fn(store.dispatch); - const registrationPage = mount(routerWrapper(reduxWrapper())); - registrationPage.find('button.btn-brand').simulate('click'); + const { container } = render(routerWrapper(reduxWrapper())); + + const button = container.querySelector('button.btn-brand'); + fireEvent.click(button); + expect(store.dispatch).not.toHaveBeenCalledWith(registerNewUser({})); }); // ******** test registration form validations ******** it('should show error messages for required fields on empty form submission', () => { - const registrationPage = mount(routerWrapper(reduxWrapper())); - registrationPage.find('button.btn-brand').simulate('click'); + const { container } = render(routerWrapper(reduxWrapper())); - expect(registrationPage.find('div[feedback-for="name"]').text()).toEqual(emptyFieldValidation.name); - expect(registrationPage.find('div[feedback-for="username"]').text()).toEqual(emptyFieldValidation.username); - expect(registrationPage.find('div[feedback-for="email"]').text()).toEqual(emptyFieldValidation.email); - expect(registrationPage.find('div[feedback-for="password"]').text()).toContain(emptyFieldValidation.password); - expect(registrationPage.find('div[feedback-for="country"]').text()).toEqual(emptyFieldValidation.country); + const button = container.querySelector('button.btn-brand'); + fireEvent.click(button); + + Object.entries(emptyFieldValidation).forEach(([fieldName, validationMessage]) => { + const feedbackElement = container.querySelector(`div[feedback-for="${fieldName}"]`); + expect(feedbackElement.textContent).toContain(validationMessage); + }); const alertBanner = 'We couldn\'t create your account.Please check your responses and try again.'; - expect(registrationPage.find('#validation-errors').first().text()).toEqual(alertBanner); + const validationErrors = container.querySelector('#validation-errors'); + expect(validationErrors.textContent).toContain(alertBanner); }); it('should set errors with validations returned by registration api', () => { @@ -284,23 +292,28 @@ describe('RegistrationPage', () => { }, }, }); - const registrationPage = mount(routerWrapper(reduxWrapper())).find('RegistrationPage'); - expect( - registrationPage.find('div[feedback-for="username"]').text(), - ).toEqual(usernameError); - expect( - registrationPage.find('div[feedback-for="email"]').text(), - ).toEqual(emailError); + const { container } = render(routerWrapper(reduxWrapper())); + const usernameFeedback = container.querySelector('div[feedback-for="username"]'); + const emailFeedback = container.querySelector('div[feedback-for="email"]'); + + expect(usernameFeedback.textContent).toContain(usernameError); + expect(emailFeedback.textContent).toContain(emailError); }); it('should clear error on focus', () => { - const registrationPage = mount(routerWrapper(reduxWrapper())); - registrationPage.find('button.btn-brand').simulate('click'); + const { container } = render(routerWrapper(reduxWrapper())); - expect(registrationPage.find('div[feedback-for="password"]').text()).toContain(emptyFieldValidation.password); + const submitButton = container.querySelector('button.btn-brand'); + fireEvent.click(submitButton); - registrationPage.find('input#password').simulate('focus'); - expect(registrationPage.find('div[feedback-for="password"]').exists()).toBeFalsy(); + const passwordFeedback = container.querySelector('div[feedback-for="password"]'); + expect(passwordFeedback.textContent).toContain(emptyFieldValidation.password); + + const passwordField = container.querySelector('input#password'); + fireEvent.focus(passwordField); + + const isFeedbackPresent = container.contains(passwordFeedback); + expect(isFeedbackPresent).toBeFalsy(); }); it('should clear registration backend error on change', () => { @@ -316,19 +329,21 @@ describe('RegistrationPage', () => { }); store.dispatch = jest.fn(store.dispatch); - const registrationPage = mount(routerWrapper(reduxWrapper( + const { container } = render(routerWrapper(reduxWrapper( , - ))).find('RegistrationPage'); + ))); - registrationPage.find('input#email').simulate('change', { target: { value: 'test1@gmail.com', name: 'email' } }); + const emailInput = container.querySelector('input#email'); + fireEvent.change(emailInput, { target: { value: 'test1@gmail.com', name: 'email' } }); expect(store.dispatch).toHaveBeenCalledWith(clearRegistrationBackendError('email')); }); // ******** test form buttons and fields ******** it('should match default button state', () => { - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find('button[type="submit"] span').first().text()).toEqual('Create an account for free'); + const { container } = render(routerWrapper(reduxWrapper())); + const button = container.querySelector('button[type="submit"] span'); + expect(button.textContent).toEqual('Create an account for free'); }); it('should match pending button state', () => { @@ -340,10 +355,10 @@ describe('RegistrationPage', () => { }, }); - const registrationPage = mount(routerWrapper(reduxWrapper())); - const button = registrationPage.find('button[type="submit"] span').first(); + const { container } = render(routerWrapper(reduxWrapper())); - expect(button.find('.sr-only').text()).toEqual('pending'); + const button = container.querySelector('button[type="submit"] span.sr-only'); + expect(button.textContent).toEqual('pending'); }); it('should display opt-in/opt-out checkbox', () => { @@ -351,8 +366,9 @@ describe('RegistrationPage', () => { MARKETING_EMAILS_OPT_IN: 'true', }); - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find('div.form-field--checkbox').length).toEqual(1); + const { container } = render(routerWrapper(reduxWrapper())); + const checkboxDivs = container.querySelectorAll('div.form-field--checkbox'); + expect(checkboxDivs.length).toEqual(1); mergeConfig({ MARKETING_EMAILS_OPT_IN: '', @@ -363,8 +379,12 @@ describe('RegistrationPage', () => { const buttonLabel = 'Register'; delete window.location; window.location = { href: getConfig().BASE_URL, search: `?cta=${buttonLabel}` }; - const registrationPage = mount(reduxWrapper()); - expect(registrationPage.find('button[type="submit"] span').first().text()).toEqual(buttonLabel); + const { container } = render(reduxWrapper()); + const button = container.querySelector('button[type="submit"] span'); + + const buttonText = button.textContent; + + expect(buttonText).toEqual(buttonLabel); }); it('should check user retention cookie', () => { @@ -378,7 +398,7 @@ describe('RegistrationPage', () => { }, }); - renderer.create(routerWrapper(reduxWrapper())); + render(routerWrapper(reduxWrapper())); expect(document.cookie).toMatch(`${getConfig().USER_RETENTION_COOKIE_NAME}=true`); }); @@ -396,7 +416,7 @@ describe('RegistrationPage', () => { }); delete window.location; window.location = { href: getConfig().BASE_URL }; - renderer.create(routerWrapper(reduxWrapper())); + render(routerWrapper(reduxWrapper())); expect(window.location.href).toBe(dashboardURL); }); @@ -423,7 +443,7 @@ describe('RegistrationPage', () => { }); delete window.location; window.location = { href: getConfig().BASE_URL }; - renderer.create(routerWrapper(reduxWrapper())); + render(routerWrapper(reduxWrapper())); expect(window.location.href).toBe(dashboardUrl); }); @@ -452,12 +472,11 @@ describe('RegistrationPage', () => { }, }); - const progressiveProfilingPage = mount(reduxWrapper( + render(reduxWrapper( , )); - progressiveProfilingPage.update(); expect(mockNavigate).toHaveBeenCalledWith(AUTHN_PROGRESSIVE_PROFILING); }); @@ -473,12 +492,12 @@ describe('RegistrationPage', () => { }); store.dispatch = jest.fn(store.dispatch); - mount(routerWrapper(reduxWrapper())); + render(routerWrapper(reduxWrapper())); expect(store.dispatch).toHaveBeenCalledWith(backupRegistrationFormBegin({ ...registrationFormData })); }); it('should send page event when register page is rendered', () => { - mount(routerWrapper(reduxWrapper())); + render(routerWrapper(reduxWrapper())); expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'register'); }); @@ -496,7 +515,7 @@ describe('RegistrationPage', () => { delete window.location; window.location = { href: getConfig().BASE_URL }; - renderer.create(routerWrapper(reduxWrapper())); + render(routerWrapper(reduxWrapper())); expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.user.account.registered.client', {}); }); @@ -520,10 +539,17 @@ describe('RegistrationPage', () => { }, }); store.dispatch = jest.fn(store.dispatch); + const { container } = render(reduxWrapper( + + + , + )); + + const emailInput = container.querySelector('input#email'); + const usernameInput = container.querySelector('input#username'); - const registrationPage = mount(routerWrapper(reduxWrapper())).find('RegistrationPage'); - expect(registrationPage.find('input#email').props().value).toEqual('test@example.com'); - expect(registrationPage.find('input#username').props().value).toEqual('test'); + expect(emailInput.value).toEqual('test@example.com'); + expect(usernameInput.value).toEqual('test'); expect(store.dispatch).toHaveBeenCalledWith(setUserPipelineDataLoaded(true)); }); @@ -538,8 +564,9 @@ describe('RegistrationPage', () => { }, }); - const registrationPage = mount(routerWrapper(reduxWrapper())).find('RegistrationPage'); - expect(registrationPage.find('div#validation-errors').first().text()).toContain( + const { container } = render(routerWrapper(reduxWrapper())); + const validationErrors = container.querySelector('div#validation-errors'); + expect(validationErrors.textContent).toContain( 'An error has occurred. Try refreshing the page, or check your internet connection.', ); }); @@ -564,15 +591,19 @@ describe('RegistrationPage', () => { }, }); - const registrationPage = mount(routerWrapper(reduxWrapper( - , - ))).find('RegistrationPage'); + const { container } = render(routerWrapper(reduxWrapper())); + + const fullNameInput = container.querySelector('input#name'); + const usernameInput = container.querySelector('input#username'); + const emailInput = container.querySelector('input#email'); + const passwordInput = container.querySelector('input#password'); + const emailSuggestion = container.querySelector('.email-suggestion-alert-warning'); - expect(registrationPage.find('input#name').props().value).toEqual('John Doe'); - expect(registrationPage.find('input#username').props().value).toEqual('john_doe'); - expect(registrationPage.find('input#email').props().value).toEqual('john.doe@yopmail.com'); - expect(registrationPage.find('input#password').props().value).toEqual('password1'); - expect(registrationPage.find('.email-suggestion-alert-warning').first().text()).toEqual('john.doe@hotmail.com'); + expect(fullNameInput.value).toEqual('John Doe'); + expect(usernameInput.value).toEqual('john_doe'); + expect(emailInput.value).toEqual('john.doe@yopmail.com'); + expect(passwordInput.value).toEqual('password1'); + expect(emailSuggestion.textContent).toEqual('john.doe@hotmail.com'); }); // ********* Embedded experience tests *********/ @@ -606,23 +637,22 @@ describe('RegistrationPage', () => { }, }, }); - const progressiveProfilingPage = mount(reduxWrapper( - , - )); - progressiveProfilingPage.update(); + render(routerWrapper(reduxWrapper())); expect(window.parent.postMessage).toHaveBeenCalledTimes(2); }); it('should not display validations error on blur event when embedded variant is rendered', () => { delete window.location; window.location = { href: getConfig().BASE_URL.concat(REGISTER_PAGE), search: '?host=http://localhost/host-website' }; - const registrationPage = mount(reduxWrapper()); + const { container } = render(reduxWrapper()); - registrationPage.find('input#username').simulate('blur', { target: { value: '', name: 'username' } }); - expect(registrationPage.find('div[feedback-for="username"]').exists()).toBeFalsy(); + const usernameInput = container.querySelector('input#username'); + fireEvent.blur(usernameInput, { target: { value: '', name: 'username' } }); + expect(container.querySelector('div[feedback-for="username"]')).toBeFalsy(); - registrationPage.find('input[name="country"]').simulate('blur', { target: { value: '', name: 'country' } }); - expect(registrationPage.find('div[feedback-for="country"]').exists()).toBeFalsy(); + const countryInput = container.querySelector('input[name="country"]'); + fireEvent.blur(countryInput, { target: { value: '', name: 'country' } }); + expect(container.querySelector('div[feedback-for="country"]')).toBeFalsy(); }); it('should set errors in temporary state when validations are returned by registration api', () => { @@ -641,12 +671,15 @@ describe('RegistrationPage', () => { }, }, }); - const registrationPage = mount(routerWrapper(reduxWrapper( + const { container } = render(routerWrapper(reduxWrapper( ), - )).find('RegistrationPage'); + )); + + const usernameFeedback = container.querySelector('div[feedback-for="username"]'); + const emailFeedback = container.querySelector('div[feedback-for="email"]'); - expect(registrationPage.find('div[feedback-for="username"]').exists()).toBeFalsy(); - expect(registrationPage.find('div[feedback-for="email"]').exists()).toBeFalsy(); + expect(usernameFeedback).toBeNull(); + expect(emailFeedback).toBeNull(); }); it('should clear error on focus for embedded experience also', () => { @@ -656,13 +689,18 @@ describe('RegistrationPage', () => { search: '?host=http://localhost/host-website', }; - const registrationPage = mount(routerWrapper(reduxWrapper())); - registrationPage.find('button.btn-brand').simulate('click'); + const { container } = render(routerWrapper(reduxWrapper())); + const submitButton = container.querySelector('button.btn-brand'); + fireEvent.click(submitButton); - expect(registrationPage.find('div[feedback-for="password"]').text()).toContain(emptyFieldValidation.password); + const passwordFeedback = container.querySelector('div[feedback-for="password"]'); + expect(passwordFeedback.textContent).toContain(emptyFieldValidation.password); - registrationPage.find('input#password').simulate('focus'); - expect(registrationPage.find('div[feedback-for="password"]').exists()).toBeFalsy(); + const passwordField = container.querySelector('input#password'); + fireEvent.focus(passwordField); + + const updatedPasswordFeedback = container.querySelector('div[feedback-for="password"]'); + expect(updatedPasswordFeedback).toBeNull(); }); it('should show spinner instead of form while registering if autoSubmitRegForm is true', () => { @@ -692,9 +730,12 @@ describe('RegistrationPage', () => { }); store.dispatch = jest.fn(store.dispatch); - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find('#tpa-spinner').exists()).toBeTruthy(); - expect(registrationPage.find('#registration-form').exists()).toBeFalsy(); + const { container } = render(routerWrapper(reduxWrapper())); + const spinnerElement = container.querySelector('#tpa-spinner'); + const registrationFormElement = container.querySelector('#registration-form'); + + expect(spinnerElement).toBeTruthy(); + expect(registrationFormElement).toBeFalsy(); }); it('should auto register if autoSubmitRegForm is true and pipeline details are loaded', () => { @@ -740,7 +781,7 @@ describe('RegistrationPage', () => { }); store.dispatch = jest.fn(store.dispatch); - mount(routerWrapper(reduxWrapper())); + render(routerWrapper(reduxWrapper())); expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ name: 'John Doe', username: 'john_doe', diff --git a/src/register/components/ConfigurableRegistrationForm.test.jsx b/src/register/components/ConfigurableRegistrationForm.test.jsx index 19aaff0f81..f4a92da603 100644 --- a/src/register/components/ConfigurableRegistrationForm.test.jsx +++ b/src/register/components/ConfigurableRegistrationForm.test.jsx @@ -5,7 +5,7 @@ import { mergeConfig } from '@edx/frontend-platform'; import { getLocale, injectIntl, IntlProvider, } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { fireEvent, render } from '@testing-library/react'; import { BrowserRouter as Router } from 'react-router-dom'; import configureStore from 'redux-mock-store'; @@ -127,16 +127,16 @@ describe('ConfigurableRegistrationForm', () => { jest.clearAllMocks(); }); - const populateRequiredFields = (registrationPage, payload, isThirdPartyAuth = false) => { - registrationPage.find('input#name').simulate('change', { target: { value: payload.name, name: 'name' } }); - registrationPage.find('input#username').simulate('change', { target: { value: payload.username, name: 'username' } }); - registrationPage.find('input#email').simulate('change', { target: { value: payload.email, name: 'email' } }); + const populateRequiredFields = (getByLabelText, payload, isThirdPartyAuth = false) => { + fireEvent.change(getByLabelText('Full name'), { target: { value: payload.name, name: 'name' } }); + fireEvent.change(getByLabelText('Public username'), { target: { value: payload.username, name: 'username' } }); + fireEvent.change(getByLabelText('Email'), { target: { value: payload.email, name: 'email' } }); - registrationPage.find('input[name="country"]').simulate('change', { target: { value: payload.country, name: 'country' } }); - registrationPage.find('input[name="country"]').simulate('blur', { target: { value: payload.country, name: 'country' } }); + fireEvent.change(getByLabelText('Country/Region'), { target: { value: payload.country, name: 'country' } }); + fireEvent.blur(getByLabelText('Country/Region'), { target: { value: payload.country, name: 'country' } }); if (!isThirdPartyAuth) { - registrationPage.find('input#password').simulate('change', { target: { value: payload.password, name: 'password' } }); + fireEvent.change(getByLabelText('Password'), { target: { value: payload.password, name: 'password' } }); } }; @@ -157,12 +157,12 @@ describe('ConfigurableRegistrationForm', () => { }, }; - const configurableRegistrationForm = mount(routerWrapper(reduxWrapper( + render(routerWrapper(reduxWrapper( , ))); - expect(configurableRegistrationForm.find('#profession').exists()).toBeTruthy(); - expect(configurableRegistrationForm.find('#tos').exists()).toBeTruthy(); + expect(document.querySelector('#profession')).toBeTruthy(); + expect(document.querySelector('#tos')).toBeTruthy(); }); it('should check TOS and honor code fields if they exist when auto submitting register form', () => { @@ -187,7 +187,7 @@ describe('ConfigurableRegistrationForm', () => { autoSubmitRegistrationForm: true, }; - mount(routerWrapper(reduxWrapper( + render(routerWrapper(reduxWrapper( , ))); @@ -215,9 +215,9 @@ describe('ConfigurableRegistrationForm', () => { }, }, }); - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find('#profession').exists()).toBeTruthy(); - expect(registrationPage.find('#tos').exists()).toBeTruthy(); + render(routerWrapper(reduxWrapper())); + expect(document.querySelector('#profession')).toBeTruthy(); + expect(document.querySelector('#tos')).toBeTruthy(); }); it('should submit form with fields returned by backend in payload', () => { @@ -249,11 +249,17 @@ describe('ConfigurableRegistrationForm', () => { }; store.dispatch = jest.fn(store.dispatch); - const registrationPage = mount(routerWrapper(reduxWrapper())); + const { getByLabelText, container } = render(routerWrapper(reduxWrapper())); + + populateRequiredFields(getByLabelText, payload); + + const professionInput = getByLabelText('Profession'); + fireEvent.change(professionInput, { target: { value: 'Engineer', name: 'profession' } }); + + const submitButton = container.querySelector('button.btn-brand'); + + fireEvent.click(submitButton); - populateRequiredFields(registrationPage, payload); - registrationPage.find('input#profession').simulate('change', { target: { value: 'Engineer', name: 'profession' } }); - registrationPage.find('button.btn-brand').simulate('click'); expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload, country: 'PK' })); }); @@ -278,12 +284,18 @@ describe('ConfigurableRegistrationForm', () => { }, }); - const registrationPage = mount(routerWrapper(reduxWrapper())); - registrationPage.find('button.btn-brand').simulate('click'); + const { container } = render(routerWrapper(reduxWrapper())); + const submitButton = container.querySelector('button.btn-brand'); - expect(registrationPage.find('#profession-error').last().text()).toEqual(professionError); - expect(registrationPage.find('div[feedback-for="country"]').text()).toEqual(countryError); - expect(registrationPage.find('#confirm_email-error').last().text()).toEqual(confirmEmailError); + fireEvent.click(submitButton); + + const professionErrorElement = container.querySelector('#profession-error'); + const countryErrorElement = container.querySelector('div[feedback-for="country"]'); + const confirmEmailErrorElement = container.querySelector('#confirm_email-error'); + + expect(professionErrorElement.textContent).toEqual(professionError); + expect(countryErrorElement.textContent).toEqual(countryError); + expect(confirmEmailErrorElement.textContent).toEqual(confirmEmailError); }); it('should show country field validation when country name is invalid', () => { @@ -298,11 +310,16 @@ describe('ConfigurableRegistrationForm', () => { }, }, }); - const registrationPage = mount(routerWrapper(reduxWrapper())); - registrationPage.find('input[name="country"]').simulate('blur', { target: { value: 'Pak', name: 'country' } }); + const { container } = render(routerWrapper(reduxWrapper())); + const countryInput = container.querySelector('input[name="country"]'); + fireEvent.blur(countryInput, { target: { value: 'Pak', name: 'country' } }); + + const submitButton = container.querySelector('button.btn-brand'); + fireEvent.click(submitButton); + + const countryErrorElement = container.querySelector('div[feedback-for="country"]'); - registrationPage.find('button.btn-brand').simulate('click'); - expect(registrationPage.find('div[feedback-for="country"]').text()).toEqual(invalidCountryError); + expect(countryErrorElement.textContent).toEqual(invalidCountryError); }); it('should show error if email and confirm email fields do not match', () => { @@ -317,10 +334,17 @@ describe('ConfigurableRegistrationForm', () => { }, }, }); - const registrationPage = mount(routerWrapper(reduxWrapper())); - registrationPage.find('input#email').simulate('change', { target: { value: 'test1@gmail.com', name: 'email' } }); - registrationPage.find('input#confirm_email').simulate('blur', { target: { value: 'test2@gmail.com', name: 'confirm_email' } }); - expect(registrationPage.find('div#confirm_email-error').text()).toEqual('The email addresses do not match.'); + const { getByLabelText, container } = render(routerWrapper(reduxWrapper())); + + const emailInput = getByLabelText('Email'); + const confirmEmailInput = getByLabelText('Confirm Email'); + + fireEvent.change(emailInput, { target: { value: 'test1@gmail.com', name: 'email' } }); + fireEvent.blur(confirmEmailInput, { target: { value: 'test2@gmail.com', name: 'confirm_email' } }); + + const confirmEmailErrorElement = container.querySelector('div#confirm_email-error'); + + expect(confirmEmailErrorElement.textContent).toEqual('The email addresses do not match.'); }); it('should run validations for configurable focused field on form submission', () => { @@ -337,11 +361,19 @@ describe('ConfigurableRegistrationForm', () => { }, }); - const registrationPage = mount(routerWrapper(reduxWrapper())); - registrationPage.find('input#profession').simulate('focus', { target: { value: '', name: 'profession' } }); - registrationPage.find('button.btn-brand').simulate('click'); + const { getByLabelText, container } = render( + routerWrapper(reduxWrapper()), + ); + + const professionInput = getByLabelText('Profession'); + fireEvent.focus(professionInput); + + const submitButton = container.querySelector('button.btn-brand'); + fireEvent.click(submitButton); + + const professionErrorElement = container.querySelector('#profession-error'); - expect(registrationPage.find('#profession-error').last().text()).toEqual(professionError); + expect(professionErrorElement.textContent).toEqual(professionError); }); }); }); diff --git a/src/register/components/RegistrationFailure.test.jsx b/src/register/components/RegistrationFailure.test.jsx index cd8a512c05..04c56f23ac 100644 --- a/src/register/components/RegistrationFailure.test.jsx +++ b/src/register/components/RegistrationFailure.test.jsx @@ -5,7 +5,7 @@ import { mergeConfig } from '@edx/frontend-platform'; import { configure, getLocale, injectIntl, IntlProvider, } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { render, screen } from '@testing-library/react'; import { BrowserRouter as Router } from 'react-router-dom'; import configureStore from 'redux-mock-store'; @@ -137,9 +137,13 @@ describe('RegistrationFailure', () => { failureCount: 0, }; - const registrationPage = mount(reduxWrapper()); - expect(registrationPage.find('div.alert-heading').length).toEqual(1); - expect(registrationPage.find('div.alert').first().text()).toEqual(expectedMessage); + const { container } = render(reduxWrapper()); + + const alertHeading = container.querySelectorAll('div.alert-heading'); + expect(alertHeading.length).toEqual(1); + + const alert = container.querySelector('div.alert'); + expect(alert.textContent).toContain(expectedMessage); }); it('should match registration api rate limit error message', () => { @@ -149,9 +153,13 @@ describe('RegistrationFailure', () => { failureCount: 0, }; - const registrationPage = mount(reduxWrapper()); - expect(registrationPage.find('div.alert-heading').length).toEqual(1); - expect(registrationPage.find('div.alert').first().text()).toEqual(expectedMessage); + const { container } = render(reduxWrapper()); + + const alertHeading = container.querySelectorAll('div.alert-heading'); + expect(alertHeading.length).toEqual(1); + + const alert = container.querySelector('div.alert'); + expect(alert.textContent).toContain(expectedMessage); }); it('should match tpa session expired error message', () => { @@ -164,9 +172,13 @@ describe('RegistrationFailure', () => { failureCount: 0, }; - const registrationPage = mount(reduxWrapper()); - expect(registrationPage.find('div.alert-heading').length).toEqual(1); - expect(registrationPage.find('div.alert').first().text()).toEqual(expectedMessage); + const { container } = render(reduxWrapper()); + + const alertHeading = container.querySelectorAll('div.alert-heading'); + expect(alertHeading.length).toEqual(1); + + const alert = container.querySelector('div.alert'); + expect(alert.textContent).toContain(expectedMessage); }); it('should match tpa authentication failed error message', () => { @@ -179,9 +191,13 @@ describe('RegistrationFailure', () => { failureCount: 0, }; - const registrationPage = mount(reduxWrapper()); - expect(registrationPage.find('div.alert-heading').length).toEqual(1); - expect(registrationPage.find('div.alert').first().text()).toContain(expectedMessageSubstring); + const { container } = render(reduxWrapper()); + + const alertHeading = container.querySelectorAll('div.alert-heading'); + expect(alertHeading.length).toEqual(1); + + const alert = container.querySelector('div.alert'); + expect(alert.textContent).toContain(expectedMessageSubstring); }); it('should display error message based on the error code returned by API', () => { @@ -195,10 +211,10 @@ describe('RegistrationFailure', () => { }, }); - const registrationPage = mount(routerWrapper(reduxWrapper())).find('RegistrationPage'); - expect(registrationPage.find('div#validation-errors').first().text()).toContain( - 'An error has occurred. Try refreshing the page, or check your internet connection.', - ); + render(routerWrapper(reduxWrapper())); + const validationError = screen.queryByText('An error has occurred. Try refreshing the page, or check your internet connection.'); + + expect(validationError).not.toBeNull(); }); }); }); diff --git a/src/register/components/ThirdPartyAuth.test.jsx b/src/register/components/ThirdPartyAuth.test.jsx index fb434c9372..6cfa550cc2 100644 --- a/src/register/components/ThirdPartyAuth.test.jsx +++ b/src/register/components/ThirdPartyAuth.test.jsx @@ -5,9 +5,8 @@ import { getConfig, mergeConfig } from '@edx/frontend-platform'; import { configure, getLocale, injectIntl, IntlProvider, } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { fireEvent, render } from '@testing-library/react'; import { BrowserRouter as Router } from 'react-router-dom'; -import renderer from 'react-test-renderer'; import configureStore from 'redux-mock-store'; import { @@ -157,8 +156,13 @@ describe('ThirdPartyAuth', () => { }, }); - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find('input#password').length).toEqual(0); + const { queryByLabelText } = render( + routerWrapper(reduxWrapper(, { store })), + ); + + const passwordField = queryByLabelText('Password'); + + expect(passwordField).toBeNull(); }); it('should render tpa button for tpa_hint id matching one of the primary providers', () => { @@ -177,9 +181,15 @@ describe('ThirdPartyAuth', () => { delete window.location; window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE), search: `?next=/dashboard&tpa_hint=${ssoProvider.id}` }; - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find(`button#${ssoProvider.id}`).find('span').text()).toEqual(ssoProvider.name); - expect(registrationPage.find(`button#${ssoProvider.id}`).hasClass(`btn-tpa btn-${ssoProvider.id}`)).toEqual(true); + const { container } = render( + routerWrapper(reduxWrapper()), + ); + const tpaButton = container.querySelector(`button#${ssoProvider.id}`); + + expect(tpaButton).toBeTruthy(); + expect(tpaButton.textContent).toEqual(ssoProvider.name); + expect(tpaButton.classList.contains('btn-tpa')).toBe(true); + expect(tpaButton.classList.contains(`btn-${ssoProvider.id}`)).toBe(true); }); it('should display skeleton if tpa_hint is true and thirdPartyAuthContext is pending', () => { @@ -197,8 +207,10 @@ describe('ThirdPartyAuth', () => { search: `?next=/dashboard&tpa_hint=${ssoProvider.id}`, }; - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find('.react-loading-skeleton').exists()).toBeTruthy(); + const { container } = render(routerWrapper(reduxWrapper())); + const skeletonElement = container.querySelector('.react-loading-skeleton'); + + expect(skeletonElement).toBeTruthy(); }); it('should render icon if icon classes are missing in providers', () => { @@ -219,8 +231,10 @@ describe('ThirdPartyAuth', () => { window.location = { href: getConfig().BASE_URL.concat(REGISTER_PAGE), search: `?next=/dashboard&tpa_hint=${ssoProvider.id}` }; ssoProvider.iconImage = null; - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find(`button#${ssoProvider.id}`).find('div').find('span').hasClass('pgn__icon')).toEqual(true); + const { container } = render(routerWrapper(reduxWrapper())); + const iconElement = container.querySelector(`button#${ssoProvider.id} div span.pgn__icon`); + + expect(iconElement).toBeTruthy(); }); it('should render tpa button for tpa_hint id matching one of the secondary providers', () => { @@ -240,7 +254,7 @@ describe('ThirdPartyAuth', () => { delete window.location; window.location = { href: getConfig().BASE_URL.concat(REGISTER_PAGE), search: `?next=/dashboard&tpa_hint=${secondaryProviders.id}` }; - mount(routerWrapper(reduxWrapper())); + render(routerWrapper(reduxWrapper())); expect(window.location.href).toEqual(getConfig().LMS_BASE_URL + secondaryProviders.registerUrl); }); @@ -261,8 +275,10 @@ describe('ThirdPartyAuth', () => { delete window.location; window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE), search: '?next=/dashboard&tpa_hint=invalid' }; - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find(`button#${ssoProvider.id}`).find('span#provider-name').text()).toEqual(expectedMessage); + const { container } = render(routerWrapper(reduxWrapper())); + const providerButton = container.querySelector(`button#${ssoProvider.id} span#provider-name`); + + expect(providerButton.textContent).toEqual(expectedMessage); }); it('should show single sign on provider button', () => { @@ -277,8 +293,13 @@ describe('ThirdPartyAuth', () => { }, }); - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find(`button#${ssoProvider.id}`).length).toEqual(1); + const { container } = render( + routerWrapper(reduxWrapper(, { store })), + ); + + const buttonsWithId = container.querySelectorAll(`button#${ssoProvider.id}`); + + expect(buttonsWithId.length).toEqual(1); }); it('should show single sign on provider button', () => { @@ -293,8 +314,13 @@ describe('ThirdPartyAuth', () => { }, }); - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find(`button#${ssoProvider.id}`).length).toEqual(1); + const { container } = render( + routerWrapper(reduxWrapper()), + ); + + const buttonsWithId = container.querySelectorAll(`button#${ssoProvider.id}`); + + expect(buttonsWithId.length).toEqual(1); }); it('should display InstitutionLogistration if insitutionLogin prop is true', () => { @@ -303,8 +329,9 @@ describe('ThirdPartyAuth', () => { institutionLogin: true, }; - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find('.institutions__heading').text()).toEqual('Register with institution/campus credentials'); + const { getByText } = render(routerWrapper(reduxWrapper())); + const headingElement = getByText('Register with institution/campus credentials'); + expect(headingElement).toBeTruthy(); }); it('should redirect to social auth provider url on SSO button click', () => { @@ -326,9 +353,13 @@ describe('ThirdPartyAuth', () => { delete window.location; window.location = { href: getConfig().BASE_URL }; - const loginPage = mount(routerWrapper(reduxWrapper())); + const { container } = render( + routerWrapper(reduxWrapper()), + ); + + const ssoButton = container.querySelector('button#oa2-apple-id'); + fireEvent.click(ssoButton); - loginPage.find('button#oa2-apple-id').simulate('click'); expect(window.location.href).toBe(getConfig().LMS_BASE_URL + registerUrl); }); @@ -354,7 +385,7 @@ describe('ThirdPartyAuth', () => { delete window.location; window.location = { href: getConfig().BASE_URL }; - renderer.create(routerWrapper(reduxWrapper())); + render(routerWrapper(reduxWrapper())); expect(window.location.href).toBe(getConfig().LMS_BASE_URL + authCompleteUrl); }); @@ -375,9 +406,11 @@ describe('ThirdPartyAuth', () => { const expectedMessage = `${'You\'ve successfully signed into Apple! We just need a little more information before ' + 'you start learning with '}${ getConfig().SITE_NAME }.`; - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find('#tpa-alert').find('p').text()).toEqual(expectedMessage); + const { container } = render(routerWrapper(reduxWrapper())); + const tpaAlert = container.querySelector('#tpa-alert p'); + expect(tpaAlert.textContent).toEqual(expectedMessage); }); + it('should display errorMessage if third party authentication fails', () => { jest.spyOn(global.Date, 'now').mockImplementation(() => 0); getLocale.mockImplementation(() => ('en-us')); @@ -403,9 +436,15 @@ describe('ThirdPartyAuth', () => { store.dispatch = jest.fn(store.dispatch); - const registrationPage = mount(routerWrapper(reduxWrapper())); - expect(registrationPage.find('div.alert-heading').length).toEqual(1); - expect(registrationPage.find('div.alert').first().text()).toContain('An error occurred'); + const { container } = render( + routerWrapper(reduxWrapper()), + ); + + const alertHeading = container.querySelector('div.alert-heading'); + expect(alertHeading).toBeTruthy(); + + const alert = container.querySelector('div.alert'); + expect(alert.textContent).toContain('An error occurred'); }); }); });