diff --git a/src/login/tests/AccountActivationMessage.test.jsx b/src/login/tests/AccountActivationMessage.test.jsx index 740f270bd9..df2a0c0859 100644 --- a/src/login/tests/AccountActivationMessage.test.jsx +++ b/src/login/tests/AccountActivationMessage.test.jsx @@ -2,7 +2,9 @@ import React from 'react'; import { mergeConfig } from '@edx/frontend-platform'; import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { + render, screen, +} from '@testing-library/react'; import AccountActivationMessage from '../AccountActivationMessage'; import { ACCOUNT_ACTIVATION_MESSAGE } from '../data/constants'; @@ -17,18 +19,22 @@ describe('AccountActivationMessage', () => { }); it('should match account already activated message', () => { - const accountActivationMessage = mount( + render( , ); const expectedMessage = 'This account has already been activated.'; - expect(accountActivationMessage.find('#account-activation-message').find('div').first().text()).toEqual(expectedMessage); + + expect(screen.getByText( + '', + { selector: '#account-activation-message' }, + ).textContent).toBe(expectedMessage); }); it('should match account activated success message', () => { - const accountActivationMessage = mount( + render( , @@ -37,11 +43,15 @@ describe('AccountActivationMessage', () => { const expectedMessage = 'Success! You have activated your account.' + 'You will now receive email updates and alerts from us related to ' + 'the courses you are enrolled in. Sign in to continue.'; - expect(accountActivationMessage.find('#account-activation-message').first().text()).toEqual(expectedMessage); + + expect(screen.getByText( + '', + { selector: '#account-activation-message' }, + ).textContent).toBe(expectedMessage); }); it('should match account activation error message', () => { - const accountActivationMessage = mount( + render( , @@ -49,17 +59,22 @@ describe('AccountActivationMessage', () => { const expectedMessage = 'Your account could not be activated' + 'Something went wrong, please contact support to resolve this issue.'; - expect(accountActivationMessage.find('#account-activation-message').first().text()).toEqual(expectedMessage); + + expect(screen.getByText( + '', + { selector: '#account-activation-message' }, + ).textContent).toBe(expectedMessage); }); it('should not display anything for invalid message type', () => { - const accountActivationMessage = mount( + const { container } = render( , ); - expect(accountActivationMessage).toEqual({}); + const accountActivationMessage = container.querySelectorAll('#account-activation-message'); + expect(accountActivationMessage[0]).toBe(undefined); }); }); @@ -71,36 +86,45 @@ describe('EmailConfirmationMessage', () => { }); it('should match email already confirmed message', () => { - const accountVerificationMessage = mount( + render( , ); const expectedMessage = 'This email has already been confirmed.'; - expect(accountVerificationMessage.find('#account-activation-message').find('div').first().text()).toEqual(expectedMessage); + + expect(screen.getByText( + '', + { selector: '#account-activation-message' }, + ).textContent).toBe(expectedMessage); }); it('should match email confirmation success message', () => { - const accountVerificationMessage = mount( + render( , ); - const expectedMessage = 'Success! You have confirmed your email.Sign in to continue.'; - expect(accountVerificationMessage.find('#account-activation-message').first().text()).toEqual(expectedMessage); + + expect(screen.getByText( + '', + { selector: '#account-activation-message' }, + ).textContent).toBe(expectedMessage); }); it('should match email confirmation error message', () => { - const accountVerificationMessage = mount( + render( , ); - const expectedMessage = 'Your email could not be confirmed' + 'Something went wrong, please contact support to resolve this issue.'; - expect(accountVerificationMessage.find('#account-activation-message').first().text()).toEqual(expectedMessage); + expect(screen.getByText( + '', + { selector: '#account-activation-message' }, + ).textContent).toBe(expectedMessage); }); }); diff --git a/src/login/tests/ChangePasswordPrompt.test.jsx b/src/login/tests/ChangePasswordPrompt.test.jsx index 0c5c2776e2..f544a55c6c 100644 --- a/src/login/tests/ChangePasswordPrompt.test.jsx +++ b/src/login/tests/ChangePasswordPrompt.test.jsx @@ -2,7 +2,9 @@ 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, screen, +} from '@testing-library/react'; import { act } from 'react-dom/test-utils'; import { MemoryRouter } from 'react-router-dom'; @@ -39,7 +41,7 @@ describe('ChangePasswordPromptTests', () => { delete window.location; window.location = { href: getConfig().BASE_URL }; - const changePasswordPrompt = mount( + render( @@ -47,7 +49,7 @@ describe('ChangePasswordPromptTests', () => { , ); - changePasswordPrompt.find('button#password-security-close').simulate('click'); + fireEvent.click(screen.getByText('Close')); expect(window.location.href).toBe(dashboardUrl); }); @@ -56,7 +58,7 @@ describe('ChangePasswordPromptTests', () => { variant: 'block', }; - const changePasswordPrompt = mount( + render( @@ -65,10 +67,12 @@ describe('ChangePasswordPromptTests', () => { ); await act(async () => { - await changePasswordPrompt.find('div.pgn__modal-backdrop').first().simulate('click'); + await fireEvent.click(screen.getByText( + '', + { selector: '.pgn__modal-backdrop' }, + )); }); - changePasswordPrompt.update(); expect(mockedNavigator).toHaveBeenCalledWith(RESET_PAGE); }); }); diff --git a/src/login/tests/LoginFailure.test.jsx b/src/login/tests/LoginFailure.test.jsx index 2357b61129..dc97fccb12 100644 --- a/src/login/tests/LoginFailure.test.jsx +++ b/src/login/tests/LoginFailure.test.jsx @@ -1,7 +1,9 @@ import React from 'react'; import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { + render, screen, +} from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { @@ -44,7 +46,7 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( , @@ -55,7 +57,10 @@ describe('LoginFailureMessage', () => { + 'password-reset message to the email address associated with this account. ' + 'Thank you for helping us keep your data safe.'; - expect(loginFailureMessage.find('#login-failure-alert').first().text()).toEqual(expectedMessage); + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toBe(expectedMessage); }); it('should match inactive user error message', () => { @@ -70,7 +75,7 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( , @@ -80,8 +85,12 @@ describe('LoginFailureMessage', () => { + 'We just sent an activation link to text@example.com. If you do not receive an email, ' + 'check your spam folders or contact openedX support.'; - expect(loginFailureMessage.find('#login-failure-alert').first().text()).toEqual(expectedMessage); - expect(loginFailureMessage.find('#login-failure-alert').find('a').props().href).toEqual('http://support.openedx.test'); + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toBe(expectedMessage); + + expect(screen.getByRole('link', { name: 'contact openedX support' }).getAttribute('href')).toBe('http://support.openedx.test'); }); it('test match failed login attempt error', () => { @@ -97,15 +106,19 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( , ); + const expectedMessage = 'We couldn\'t sign you in.The username, email or password you entered is incorrect. ' + 'You have 3 more sign in attempts before your account is temporarily locked.If you\'ve forgotten your password, click here to reset it.'; - expect(loginFailureMessage.find('#login-failure-alert').first().text()).toEqual(expectedMessage); + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toBe(expectedMessage); }); it('test match failed login error first attempt', () => { @@ -120,14 +133,18 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( , ); + const expectedMessage = 'We couldn\'t sign you in.The username, email, or password you entered is incorrect. Please try again.'; - expect(loginFailureMessage.find('#login-failure-alert').first().text()).toEqual(expectedMessage); + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toBe(expectedMessage); }); it('test match failed login error second attempt', () => { @@ -142,14 +159,18 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( , ); + const expectedMessage = 'We couldn\'t sign you in.The username, email, or password you entered is incorrect. Please try again or reset your password.'; - expect(loginFailureMessage.find('#login-failure-alert').first().text()).toEqual(expectedMessage); + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toBe(expectedMessage); }); it('should match rate limit error message', () => { @@ -159,14 +180,18 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( , ); const expectedMessage = 'We couldn\'t sign you in.Too many failed login attempts. Try again later.'; - expect(loginFailureMessage.find('#login-failure-alert').first().text()).toEqual(expectedMessage); + + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toBe(expectedMessage); }); it('should match internal server error message', () => { @@ -176,14 +201,18 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( , ); const expectedMessage = 'We couldn\'t sign you in.An error has occurred. Try refreshing the page, or check your internet connection.'; - expect(loginFailureMessage.find('#login-failure-alert').first().text()).toEqual(expectedMessage); + + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toBe(expectedMessage); }); it('should match invalid form error message', () => { @@ -193,14 +222,17 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( , ); const expectedMessage = 'We couldn\'t sign you in.Please fill in the fields below.'; - expect(loginFailureMessage.find('#login-failure-alert').first().text()).toEqual(expectedMessage); + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toBe(expectedMessage); }); it('should match internal server of error message', () => { @@ -210,14 +242,17 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( , ); const expectedMessage = 'We couldn\'t sign you in.An error has occurred. Try refreshing the page, or check your internet connection.'; - expect(loginFailureMessage.find('#login-failure-alert').first().text()).toEqual(expectedMessage); + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toBe(expectedMessage); }); it('should match tpa authentication failed error message', () => { @@ -230,7 +265,7 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( , @@ -238,8 +273,15 @@ describe('LoginFailureMessage', () => { const expectedMessageSubstring = 'We are sorry, you are not authorized to access'; - expect(loginFailureMessage.find('#login-failure-alert').first().text()).toContain(expectedMessageSubstring); - expect(loginFailureMessage.find('#login-failure-alert').first().text()).toContain('An error occurred'); + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toContain(expectedMessageSubstring); + + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toContain('An error occurred'); }); it('should show modal that nudges users to change password', () => { @@ -249,7 +291,7 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( @@ -257,11 +299,16 @@ describe('LoginFailureMessage', () => { , ); - expect(loginFailureMessage.find('.pgn__modal-title').text()).toEqual('Password security'); - expect(loginFailureMessage.find('.pgn__modal-body').text()).toEqual( - 'Our system detected that your password is vulnerable. ' - + 'We recommend you change it so that your account stays secure.', - ); + const message = 'Our system detected that your password is vulnerable. ' + + 'We recommend you change it so that your account stays secure.'; + expect(screen.getByText( + 'Password security', + { selector: '.pgn__modal-title' }, + ).textContent).toEqual('Password security'); + expect(screen.getByText( + '', + { selector: '.pgn__modal-body' }, + ).textContent).toEqual(message); }); it('should show modal that requires users to change password', () => { @@ -271,7 +318,7 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( @@ -279,8 +326,14 @@ describe('LoginFailureMessage', () => { , ); - expect(loginFailureMessage.find('.pgn__modal-title').text()).toEqual('Password change required'); - expect(loginFailureMessage.find('.pgn__modal-body').text()).toEqual( + expect(screen.getByText( + 'Password change required', + { selector: '.pgn__modal-title' }, + ).textContent).toEqual('Password change required'); + expect(screen.getByText( + '', + { selector: '.pgn__modal-body' }, + ).textContent).toEqual( 'Our system detected that your password is vulnerable. ' + 'Change your password so that your account stays secure.', ); @@ -299,7 +352,7 @@ describe('LoginFailureMessage', () => { }, }; - const loginFailureMessage = mount( + render( , @@ -308,7 +361,11 @@ describe('LoginFailureMessage', () => { const errorMessage = "We couldn't sign you in.As test.com user, You must login with your test.com Google account."; const url = 'http://localhost:18000/dashboard/?tpa_hint=google-auth2'; - expect(loginFailureMessage.find('#login-failure-alert').first().text()).toEqual(errorMessage); - expect(loginFailureMessage.find('#login-failure-alert').find('a').props().href).toEqual(url); + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toContain(errorMessage); + + expect(screen.getByRole('link', { name: 'Google account' }).getAttribute('href')).toBe(url); }); }); diff --git a/src/login/tests/LoginPage.test.jsx b/src/login/tests/LoginPage.test.jsx index 1a5c7e9e14..cffbea42fa 100644 --- a/src/login/tests/LoginPage.test.jsx +++ b/src/login/tests/LoginPage.test.jsx @@ -4,9 +4,11 @@ import { Provider } from 'react-redux'; import { getConfig, mergeConfig } from '@edx/frontend-platform'; import { sendPageEvent } from '@edx/frontend-platform/analytics'; import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n'; -import { mount } from 'enzyme'; +import { + fireEvent, render, screen, waitFor, +} from '@testing-library/react'; +import { act } from 'react-dom/test-utils'; import { MemoryRouter } from 'react-router-dom'; -import renderer from 'react-test-renderer'; import configureStore from 'redux-mock-store'; import { COMPLETE_STATE, LOGIN_PAGE, PENDING_STATE } from '../../data/constants'; @@ -14,7 +16,6 @@ import { loginRemovePasswordResetBanner, loginRequest, loginRequestFailure, loginRequestReset, setLoginFormData, } from '../data/actions'; import { INTERNAL_SERVER_ERROR } from '../data/constants'; -import LoginFailureMessage from '../LoginFailure'; import LoginPage from '../LoginPage'; jest.mock('@edx/frontend-platform/analytics', () => ({ @@ -25,7 +26,6 @@ jest.mock('@edx/frontend-platform/auth', () => ({ getAuthService: jest.fn(), })); -const IntlLoginFailureMessage = injectIntl(LoginFailureMessage); const IntlLoginPage = injectIntl(LoginPage); const mockStore = configureStore(); @@ -97,67 +97,108 @@ describe('LoginPage', () => { it('should submit form for valid input', () => { store.dispatch = jest.fn(store.dispatch); - const loginPage = mount(reduxWrapper()); - loginPage.find('input#emailOrUsername').simulate('change', { target: { value: 'test@example.com' } }); - loginPage.find('input#password').simulate('change', { target: { value: 'password' } }); - loginPage.find('button.btn-brand').simulate('click'); + render(reduxWrapper()); + + fireEvent.change(screen.getByText( + '', + { selector: '#emailOrUsername' }, + ), { target: { value: 'test@example.com' } }); + fireEvent.change(screen.getByText( + '', + { selector: '#password' }, + ), { target: { value: 'password' } }); + + fireEvent.click(screen.getByText( + '', + { selector: '.btn-brand' }, + )); expect(store.dispatch).toHaveBeenCalledWith(loginRequest({ email_or_username: 'test@example.com', password: 'password' })); }); it('should not dispatch loginRequest on empty form submission', () => { store.dispatch = jest.fn(store.dispatch); - const loginPage = mount(reduxWrapper()); + render(reduxWrapper()); - loginPage.find('button.btn-brand').simulate('click'); + fireEvent.click(screen.getByText( + '', + { selector: '.btn-brand' }, + )); expect(store.dispatch).not.toHaveBeenCalledWith(loginRequest({})); }); // ******** test login form validations ******** it('should match state on empty form submission', () => { - const errorState = { emailOrUsername: 'Enter your username or email', password: 'Enter your password' }; store.dispatch = jest.fn(store.dispatch); - const loginPage = (mount(reduxWrapper())).find('LoginPage'); - loginPage.find('button.btn-brand').simulate('click'); + render(reduxWrapper()); + fireEvent.click(screen.getByText( + '', + { selector: '.btn-brand' }, + )); - // Check that loginRequestFailure was dispatched and state is updated - expect(loginPage.state('errors')).toEqual(errorState); + expect(screen.getByText('Enter your username or email')).toBeDefined(); expect(store.dispatch).toHaveBeenCalledWith(loginRequestFailure({ errorCode: 'invalid-form' })); }); it('should match state for invalid email (less than 3 characters), on form submission', () => { - const errorState = { emailOrUsername: 'Username or email must have at least 3 characters.', password: '' }; store.dispatch = jest.fn(store.dispatch); - const loginPage = (mount(reduxWrapper())).find('LoginPage'); + render(reduxWrapper()); + + fireEvent.change(screen.getByText( + '', + { selector: '#password' }, + ), { target: { value: 'test' } }); + fireEvent.change(screen.getByText( + '', + { selector: '#emailOrUsername' }, + ), { target: { value: 'te' } }); - loginPage.find('input#password').simulate('change', { target: { value: 'test', name: 'password' } }); - loginPage.find('input#emailOrUsername').simulate('change', { target: { value: 'te', name: 'email' } }); - loginPage.find('button.btn-brand').simulate('click'); + fireEvent.click(screen.getByText( + '', + { selector: '.btn-brand' }, + )); - expect(loginPage.state('errors')).toEqual(errorState); + expect(screen.getByText('Username or email must have at least 3 characters.')).toBeDefined(); }); - it('should reset field related error messages on onFocus event', () => { - const errorState = { emailOrUsername: '', password: '' }; + it('should reset field related error messages on onFocus event', async () => { store.dispatch = jest.fn(store.dispatch); - const loginPage = (mount(reduxWrapper())).find('LoginPage'); - loginPage.find('button.btn-brand').simulate('click'); + render(reduxWrapper()); + + await act(async () => { + // clicking submit button with empty fields to make the errors appear + fireEvent.click(screen.getByText( + '', + { selector: '.btn-brand' }, + )); + + // focusing the fields to verify that the errors are cleared + fireEvent.focus(screen.getByText( + '', + { selector: '#password' }, + )); + fireEvent.focus(screen.getByText( + '', + { selector: '#emailOrUsername' }, + )); + }); - loginPage.find('input#emailOrUsername').simulate('focus'); - loginPage.find('input#password').simulate('focus'); - expect(loginPage.state('errors')).toEqual(errorState); + // verifying that the errors are cleared + await waitFor(() => { + expect(screen.queryByText('Enter your username or email')).toBeNull(); + }); }); // ******** test form buttons and links ******** it('should match default button state', () => { - const loginPage = mount(reduxWrapper()); - expect(loginPage.find('button[type="submit"] span').first().text()).toEqual('Sign in'); + render(reduxWrapper()); + expect(screen.getByText('Sign in')).toBeDefined(); }); it('should match pending button state', () => { @@ -169,15 +210,20 @@ describe('LoginPage', () => { }, }); - const loginPage = mount(reduxWrapper()); - const button = loginPage.find('button[type="submit"] span').first(); + render(reduxWrapper()); - expect(button.find('.sr-only').text()).toEqual('pending'); + expect(screen.getByText( + 'pending', + ).textContent).toEqual('pending'); }); it('should show forgot password link', () => { - const loginPage = mount(reduxWrapper()); - expect(loginPage.find('a#forgot-password').text()).toEqual('Forgot password'); + render(reduxWrapper()); + + expect(screen.getByText( + 'Forgot password', + { selector: '#forgot-password' }, + ).textContent).toEqual('Forgot password'); }); it('should show single sign on provider button', () => { @@ -198,8 +244,11 @@ describe('LoginPage', () => { }, }); - const loginPage = mount(reduxWrapper()); - expect(loginPage.find(`button#${ssoProvider.id}`).length).toEqual(1); + render(reduxWrapper()); + expect(screen.getByText( + '', + { selector: `#${ssoProvider.id}` }, + )).toBeDefined(); mergeConfig({ DISABLE_ENTERPRISE_LOGIN: '', @@ -207,8 +256,8 @@ describe('LoginPage', () => { }); it('should not display institution login option when no secondary providers are present', () => { - const root = mount(reduxWrapper()); - expect(root.text().includes('Use my university info')).toBe(false); + const { queryByText } = render(reduxWrapper()); + expect(queryByText('Use my university info')).toBeNull(); }); it('should not show sign-in header and enterprise login once user authenticated through SSO', () => { @@ -230,9 +279,9 @@ describe('LoginPage', () => { }, }); - const loginPage = mount(reduxWrapper()); - expect(loginPage.text().includes('Company or school credentials')).toBe(false); - expect(loginPage.text().includes('Or sign in with:')).toBe(false); + const { queryByText } = render(reduxWrapper()); + expect(queryByText('Company or school credentials')).toBeNull(); + expect(queryByText('Or sign in with:')).toBeNull(); }); it('should show sign-in header providers (ENABLE ENTERPRISE LOGIN)', () => { @@ -253,10 +302,10 @@ describe('LoginPage', () => { }, }); - const loginPage = mount(reduxWrapper()); - expect(loginPage.text().includes('Or sign in with:')).toBe(true); - expect(loginPage.text().includes('Company or school credentials')).toBe(true); - expect(loginPage.text().includes('Institution/campus credentials')).toBe(false); + const { queryByText } = render(reduxWrapper()); + expect(queryByText('Or sign in with:')).toBeDefined(); + expect(queryByText('Company or school credentials')).toBeDefined(); + expect(queryByText('Institution/campus credentials')).toBeDefined(); }); it('should show sign-in header with providers (DISABLE ENTERPRISE LOGIN)', () => { @@ -277,10 +326,10 @@ describe('LoginPage', () => { }, }); - const loginPage = mount(reduxWrapper()); - expect(loginPage.text().includes('Or sign in with:')).toBe(true); - expect(loginPage.text().includes('Company or school credentials')).toBe(false); - expect(loginPage.text().includes('Institution/campus credentials')).toBe(false); + const { queryByText } = render(reduxWrapper()); + expect(queryByText('Or sign in with:')).toBeDefined(); + expect(queryByText('Company or school credentials')).toBeNull(); + expect(queryByText('Institution/campus credentials')).toBeNull(); mergeConfig({ DISABLE_ENTERPRISE_LOGIN: '', @@ -302,10 +351,10 @@ describe('LoginPage', () => { }, }); - const loginPage = mount(reduxWrapper()); - expect(loginPage.text().includes('Or sign in with:')).toBe(false); - expect(loginPage.text().includes('Company or school credentials')).toBe(false); - expect(loginPage.text().includes('Institution/campus credentials')).toBe(false); + const { queryByText } = render(reduxWrapper()); + expect(queryByText('Or sign in with:')).toBeNull(); + expect(queryByText('Company or school credentials')).toBeNull(); + expect(queryByText('Institution/campus credentials')).toBeNull(); }); it('should not show sign-in header without Providers and secondary Providers (DISABLE ENTERPRISE LOGIN)', () => { @@ -323,10 +372,10 @@ describe('LoginPage', () => { }, }); - const loginPage = mount(reduxWrapper()); - expect(loginPage.text().includes('Or sign in with:')).toBe(false); - expect(loginPage.text().includes('Company or school credentials')).toBe(false); - expect(loginPage.text().includes('Institution/campus credentials')).toBe(false); + const { queryByText } = render(reduxWrapper()); + expect(queryByText('Or sign in with:')).toBeNull(); + expect(queryByText('Company or school credentials')).toBeNull(); + expect(queryByText('Institution/campus credentials')).toBeNull(); mergeConfig({ DISABLE_ENTERPRISE_LOGIN: '', @@ -351,9 +400,9 @@ describe('LoginPage', () => { }, }); - const loginPage = mount(reduxWrapper()); - expect(loginPage.text().includes('Or sign in with:')).toBe(true); - expect(loginPage.text().includes('Institution/campus credentials')).toBe(true); + const { queryByText } = render(reduxWrapper()); + expect(queryByText('Or sign in with:')).toBeDefined(); + expect(queryByText('Institution/campus credentials')).toBeDefined(); mergeConfig({ DISABLE_ENTERPRISE_LOGIN: '', @@ -381,10 +430,10 @@ describe('LoginPage', () => { }, }); - const loginPage = mount(reduxWrapper()); - expect(loginPage.text().includes('Or sign in with:')).toBe(true); - expect(loginPage.text().includes('Company or school credentials')).toBe(false); - expect(loginPage.text().includes('Institution/campus credentials')).toBe(true); + const { queryByText } = render(reduxWrapper()); + expect(queryByText('Or sign in with:')).toBeDefined(); + expect(queryByText('Company or school credentials')).toBeNull(); + expect(queryByText('Institution/campus credentials')).toBeDefined(); mergeConfig({ DISABLE_ENTERPRISE_LOGIN: '', @@ -403,8 +452,11 @@ describe('LoginPage', () => { }, }); - const loginPage = mount(reduxWrapper()); - expect(loginPage.find('#login-failure-alert').first().text()).toEqual(`We couldn't sign you in.${errorMessage}`); + render(reduxWrapper()); + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toEqual(`We couldn't sign you in.${errorMessage}`); }); it('should match account activation message', () => { @@ -415,8 +467,11 @@ describe('LoginPage', () => { delete window.location; window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE), search: '?account_activation_status=success' }; - const loginPage = mount(reduxWrapper()); - expect(loginPage.find('div#account-activation-message').text()).toEqual(activationMessage); + render(reduxWrapper()); + expect(screen.getByText( + '', + { selector: '#account-activation-message' }, + ).textContent).toEqual(activationMessage); }); it('should match third party auth alert', () => { @@ -436,8 +491,11 @@ describe('LoginPage', () => { + 'linked '}${ getConfig().SITE_NAME } account. To link your accounts, sign in now using your ${ getConfig().SITE_NAME } password.`; - const loginPage = mount(reduxWrapper()); - expect(loginPage.find('#tpa-alert').find('p').text()).toEqual(expectedMessage); + render(reduxWrapper()); + expect(screen.getByText( + '', + { selector: '#tpa-alert' }, + ).textContent).toEqual(expectedMessage); }); it('should show tpa authentication fails error message', () => { @@ -453,8 +511,11 @@ describe('LoginPage', () => { }, }); - const loginPage = mount(reduxWrapper()); - expect(loginPage.find('#login-failure-alert').find('p').text()).toContain('An error occurred'); + render(reduxWrapper()); + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toContain('An error occurred'); }); it('should match invalid login form error message', () => { @@ -467,8 +528,11 @@ describe('LoginPage', () => { }, }); - const loginPage = mount(reduxWrapper()); - expect(loginPage.find('#login-failure-alert p').first().text()).toEqual(errorMessage); + render(reduxWrapper()); + expect(screen.getByText( + '', + { selector: '#login-failure-alert' }, + ).textContent).toContain(errorMessage); }); // ******** test redirection ******** @@ -488,7 +552,7 @@ describe('LoginPage', () => { delete window.location; window.location = { href: getConfig().BASE_URL }; - renderer.create(reduxWrapper()); + render(reduxWrapper()); expect(window.location.href).toBe(dashboardUrl); }); @@ -515,7 +579,7 @@ describe('LoginPage', () => { delete window.location; window.location = { href: getConfig().BASE_URL }; - renderer.create(reduxWrapper()); + render(reduxWrapper()); expect(window.location.href).toBe(getConfig().LMS_BASE_URL + authCompleteUrl); }); @@ -542,9 +606,12 @@ describe('LoginPage', () => { delete window.location; window.location = { href: getConfig().BASE_URL }; - const loginPage = mount(reduxWrapper()); + render(reduxWrapper()); - loginPage.find('button#oa2-apple-id').simulate('click'); + fireEvent.click(screen.getByText( + '', + { selector: '#oa2-apple-id' }, + )); expect(window.location.href).toBe(getConfig().LMS_BASE_URL + loginUrl); mergeConfig({ @@ -570,9 +637,15 @@ describe('LoginPage', () => { delete window.location; window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE), search: `?next=/dashboard&tpa_hint=${ssoProvider.id}` }; - const loginPage = mount(reduxWrapper()); - expect(loginPage.find(`button#${ssoProvider.id}`).find('span').text()).toEqual(ssoProvider.name); - expect(loginPage.find(`button#${ssoProvider.id}`).hasClass(`btn-tpa btn-${ssoProvider.id}`)).toEqual(true); + render(reduxWrapper()); + expect(screen.getByText( + '', + { selector: `#${ssoProvider.id}` }, + ).textContent).toEqual(ssoProvider.name); + expect(screen.getByText( + '', + { selector: `.btn-${ssoProvider.id}` }, + )).toBeTruthy(); }); it('should render tpa button for tpa_hint id matching one of the secondary providers', () => { @@ -593,7 +666,7 @@ describe('LoginPage', () => { window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE), search: `?next=/dashboard&tpa_hint=${secondaryProviders.id}` }; secondaryProviders.iconImage = null; - mount(reduxWrapper()); + render(reduxWrapper()); expect(window.location.href).toEqual(getConfig().LMS_BASE_URL + secondaryProviders.loginUrl); }); @@ -617,8 +690,8 @@ describe('LoginPage', () => { delete window.location; window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE), search: '?next=/dashboard&tpa_hint=invalid' }; - const loginPage = mount(reduxWrapper()); - expect(loginPage.find(`button#${ssoProvider.id}`).find('span#provider-name').text()).toEqual(`${ssoProvider.name}`); + const { container } = render(reduxWrapper()); + expect(container.querySelector(`#${ssoProvider.id}`).querySelector('#provider-name').textContent).toEqual(`${ssoProvider.name}`); mergeConfig({ DISABLE_ENTERPRISE_LOGIN: '', @@ -641,8 +714,10 @@ describe('LoginPage', () => { delete window.location; window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE), search: `?tpa_hint=${ssoProvider.id}` }; - const loginPage = mount(reduxWrapper()); - expect(loginPage.find('button#other-ways-to-sign-in').text()).toEqual('Show me other ways to sign in or register'); + render(reduxWrapper()); + expect(screen.getByText( + 'Show me other ways to sign in or register', + ).textContent).toBeDefined(); }); it('should render other ways to sign in button when public account creation disabled', () => { @@ -664,35 +739,48 @@ describe('LoginPage', () => { delete window.location; window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE), search: `?tpa_hint=${ssoProvider.id}` }; - const loginPage = mount(reduxWrapper()); - expect(loginPage.find('button#other-ways-to-sign-in').text()).toEqual('Show me other ways to sign in'); + render(reduxWrapper()); + expect(screen.getByText( + 'Show me other ways to sign in', + ).textContent).toBeDefined(); }); // ******** miscellaneous tests ******** it('should send page event when login page is rendered', () => { - mount(reduxWrapper()); + render(reduxWrapper()); expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'login'); }); - it('tests that form is only scrollable on form submission', () => { - const loginPage = mount(reduxWrapper()); + it('tests that form is in invalid state when it is submission', () => { + store = mockStore({ + ...initialState, + login: { + ...initialState.login, + loginError: { errorCode: 'invalid-form' }, + }, + }); - loginPage.find('input#password').simulate('change', { target: { value: 'test', name: 'password' } }); - loginPage.find('button.btn-brand').simulate('click'); + render(reduxWrapper()); - expect(loginPage.find()).toBeTruthy(); - expect(loginPage.find('LoginPage').state('isSubmitted')).toEqual(true); + fireEvent.change(screen.getByText( + '', + { selector: '#password' }, + ), { target: { value: 'password', name: 'password' } }); + fireEvent.click(screen.getByText( + '', + { selector: '.btn-brand' }, + )); + + expect(screen.getByText('Please fill in the fields below.')).toBeTruthy(); }); it('should reset login form errors', () => { - const errorState = { emailOrUsername: '', password: '' }; store.dispatch = jest.fn(store.dispatch); - const loginPage = (mount(reduxWrapper())).find('LoginPage'); + render(reduxWrapper()); expect(store.dispatch).toHaveBeenCalledWith(loginRequestReset()); - expect(loginPage.state('errors')).toEqual(errorState); }); // persists form data tests @@ -705,11 +793,20 @@ describe('LoginPage', () => { }, }; store.dispatch = jest.fn(store.dispatch); - const loginPage = mount(reduxWrapper()); - - loginPage.find('input#emailOrUsername').simulate('change', { target: { value: '' } }); - loginPage.find('input#password').simulate('change', { target: { value: '' } }); - loginPage.find('button.btn-brand').simulate('click'); + render(reduxWrapper()); + + fireEvent.change(screen.getByText( + '', + { selector: '#emailOrUsername' }, + ), { target: { value: '' } }); + fireEvent.change(screen.getByText( + '', + { selector: '#password' }, + ), { target: { value: '' } }); + fireEvent.click(screen.getByText( + '', + { selector: '.btn-brand' }, + )); expect(store.dispatch).toHaveBeenCalledWith(setLoginFormData(formData)); }); @@ -717,17 +814,22 @@ describe('LoginPage', () => { it('should set form data in redux store on onBlur', () => { store.dispatch = jest.fn(store.dispatch); - const loginPage = mount(reduxWrapper()); - loginPage.find('input#emailOrUsername').simulate('blur'); - + render(reduxWrapper()); + fireEvent.blur(screen.getByText( + '', + { selector: '#emailOrUsername' }, + )); expect(store.dispatch).toHaveBeenCalledWith(setLoginFormData({ emailOrUsername: '' })); }); it('should clear form field errors in redux store on onFocus', () => { store.dispatch = jest.fn(store.dispatch); - const loginPage = mount(reduxWrapper()); - loginPage.find('input#emailOrUsername').simulate('focus'); + render(reduxWrapper()); + fireEvent.focus(screen.getByText( + '', + { selector: '#emailOrUsername' }, + )); expect(store.dispatch).toHaveBeenCalledWith(setLoginFormData({ errors: { @@ -736,19 +838,28 @@ describe('LoginPage', () => { })); }); - it('should update form fields state if updated in redux store', () => { - const nextProps = { - loginFormData: { - emailOrUsername: 'john_doe', - password: 'password1', + it('should update form fields state if updated in redux store', async () => { + const { rerender } = render(reduxWrapper()); + + store = mockStore({ + ...initialState, + login: { + ...initialState.login, + loginFormData: { + emailOrUsername: 'john_doe', + password: 'password1', + }, }, - }; + }); - const loginPage = mount(reduxWrapper()); - loginPage.find('LoginPage').instance().shouldComponentUpdate(nextProps); + rerender(( + + + + + )); - expect(loginPage.find('LoginPage').state('emailOrUsername')).toEqual('john_doe'); - expect(loginPage.find('LoginPage').state('password')).toEqual('password1'); + expect(screen.getByDisplayValue('password1')).toBeDefined(); }); it('should update reset password value when unmount called', () => { @@ -761,8 +872,8 @@ describe('LoginPage', () => { }); store.dispatch = jest.fn(store.dispatch); - const loginPage = mount(reduxWrapper()); - loginPage.unmount(); + const { unmount } = render(reduxWrapper()); + unmount(); expect(store.dispatch).toHaveBeenCalledWith(loginRemovePasswordResetBanner()); });