289 lines
8.6 KiB
JavaScript
289 lines
8.6 KiB
JavaScript
import { Provider } from 'react-redux';
|
|
|
|
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
|
import { fireEvent, render } from '@testing-library/react';
|
|
import { act } from 'react-dom/test-utils';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
import configureStore from 'redux-mock-store';
|
|
|
|
import { fetchRealtimeValidations } from '../../register/data/actions';
|
|
import FormGroup from '../FormGroup';
|
|
import PasswordField from '../PasswordField';
|
|
|
|
describe('FormGroup', () => {
|
|
const props = {
|
|
floatingLabel: 'Email',
|
|
helpText: ['Email field help text'],
|
|
name: 'email',
|
|
value: '',
|
|
handleFocus: jest.fn(),
|
|
};
|
|
|
|
it('should show help text on field focus', () => {
|
|
const { queryByText, getByLabelText } = render(<FormGroup {...props} />);
|
|
const emailInput = getByLabelText('Email');
|
|
|
|
expect(queryByText('Email field help text')).toBeNull();
|
|
|
|
fireEvent.focus(emailInput);
|
|
|
|
const helpText = queryByText('Email field help text');
|
|
|
|
expect(helpText).toBeTruthy();
|
|
expect(helpText.textContent).toEqual('Email field help text');
|
|
});
|
|
});
|
|
|
|
describe('PasswordField', () => {
|
|
const mockStore = configureStore();
|
|
let props = {};
|
|
let store = {};
|
|
|
|
const reduxWrapper = children => (
|
|
<IntlProvider locale="en">
|
|
<MemoryRouter>
|
|
<Provider store={store}>{children}</Provider>
|
|
</MemoryRouter>
|
|
</IntlProvider>
|
|
);
|
|
|
|
const initialState = {
|
|
register: {
|
|
validationApiRateLimited: false,
|
|
},
|
|
};
|
|
|
|
beforeEach(() => {
|
|
store = mockStore(initialState);
|
|
props = {
|
|
floatingLabel: 'Password',
|
|
name: 'password',
|
|
value: 'password123',
|
|
handleFocus: jest.fn(),
|
|
};
|
|
});
|
|
|
|
it('should show/hide password on icon click', () => {
|
|
const { getByLabelText } = render(reduxWrapper(<PasswordField {...props} />));
|
|
const passwordInput = getByLabelText('Password');
|
|
|
|
const showPasswordButton = getByLabelText('Show password');
|
|
fireEvent.click(showPasswordButton);
|
|
expect(passwordInput.type).toBe('text');
|
|
|
|
const hidePasswordButton = getByLabelText('Hide password');
|
|
fireEvent.click(hidePasswordButton);
|
|
expect(passwordInput.type).toBe('password');
|
|
});
|
|
|
|
it('should show password requirement tooltip on focus', async () => {
|
|
const { getByLabelText } = render(reduxWrapper(<PasswordField {...props} />));
|
|
const passwordInput = getByLabelText('Password');
|
|
jest.useFakeTimers();
|
|
await act(async () => {
|
|
fireEvent.focus(passwordInput);
|
|
jest.runAllTimers();
|
|
});
|
|
const passwordRequirementTooltip = document.querySelector('#password-requirement-left');
|
|
|
|
expect(passwordRequirementTooltip).toBeTruthy();
|
|
});
|
|
|
|
it('should show all password requirement checks as failed', async () => {
|
|
props = {
|
|
...props,
|
|
value: '',
|
|
};
|
|
const { getByLabelText } = render(reduxWrapper(<PasswordField {...props} />));
|
|
const passwordInput = getByLabelText('Password');
|
|
jest.useFakeTimers();
|
|
await act(async () => {
|
|
fireEvent.focus(passwordInput);
|
|
jest.runAllTimers();
|
|
});
|
|
|
|
const letterCheckIcon = document.querySelector('#letter-check span');
|
|
const numberCheckIcon = document.querySelector('#number-check span');
|
|
const charactersCheckIcon = document.querySelector('#characters-check span');
|
|
|
|
expect(letterCheckIcon).toBeTruthy();
|
|
expect(letterCheckIcon.className).toContain('pgn__icon mr-1 text-light-700');
|
|
|
|
expect(numberCheckIcon).toBeTruthy();
|
|
expect(numberCheckIcon.className).toContain('pgn__icon mr-1 text-light-700');
|
|
|
|
expect(charactersCheckIcon).toBeTruthy();
|
|
expect(charactersCheckIcon.className).toContain('pgn__icon mr-1 text-light-700');
|
|
});
|
|
|
|
it('should update password requirement checks', async () => {
|
|
const { getByLabelText } = render(reduxWrapper(<PasswordField {...props} />));
|
|
const passwordInput = getByLabelText('Password');
|
|
jest.useFakeTimers();
|
|
await act(async () => {
|
|
fireEvent.focus(passwordInput);
|
|
jest.runAllTimers();
|
|
});
|
|
|
|
const letterCheckIcon = document.querySelector('#letter-check span');
|
|
const numberCheckIcon = document.querySelector('#number-check span');
|
|
const charactersCheckIcon = document.querySelector('#characters-check span');
|
|
|
|
expect(letterCheckIcon).toBeTruthy();
|
|
expect(letterCheckIcon.className).toContain('pgn__icon text-success mr-1');
|
|
|
|
expect(numberCheckIcon).toBeTruthy();
|
|
expect(numberCheckIcon.className).toContain('pgn__icon text-success mr-1');
|
|
|
|
expect(charactersCheckIcon).toBeTruthy();
|
|
expect(charactersCheckIcon.className).toContain('pgn__icon text-success mr-1');
|
|
});
|
|
|
|
it('should not run validations when blur is fired on password icon click', () => {
|
|
const { container, getByLabelText } = render(reduxWrapper(<PasswordField {...props} />));
|
|
const passwordInput = container.querySelector('input[name="password"]');
|
|
|
|
const passwordIcon = getByLabelText('Show password');
|
|
|
|
fireEvent.blur(passwordInput, {
|
|
target: {
|
|
name: 'password',
|
|
value: 'invalid',
|
|
},
|
|
relatedTarget: passwordIcon,
|
|
});
|
|
|
|
expect(container.querySelector('div[feedback-for="password"]')).toBeNull();
|
|
});
|
|
|
|
it('should call props handle blur if available', () => {
|
|
props = {
|
|
...props,
|
|
handleBlur: jest.fn(),
|
|
};
|
|
const { container } = render(reduxWrapper(<PasswordField {...props} />));
|
|
const passwordInput = container.querySelector('input[name="password"]');
|
|
|
|
fireEvent.blur(passwordInput, {
|
|
target: {
|
|
name: 'password',
|
|
value: '',
|
|
},
|
|
});
|
|
|
|
expect(props.handleBlur).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should run validations on blur event when rendered from register page', () => {
|
|
props = {
|
|
...props,
|
|
handleErrorChange: jest.fn(),
|
|
};
|
|
const { container } = render(reduxWrapper(<PasswordField {...props} />));
|
|
const passwordInput = container.querySelector('input[name="password"]');
|
|
|
|
fireEvent.blur(passwordInput, {
|
|
target: {
|
|
name: 'password',
|
|
value: '',
|
|
},
|
|
});
|
|
|
|
expect(props.handleErrorChange).toHaveBeenCalledTimes(1);
|
|
expect(props.handleErrorChange).toHaveBeenCalledWith(
|
|
'password',
|
|
'Password criteria has not been met',
|
|
);
|
|
});
|
|
|
|
it('should not clear error when focus is fired on password icon click when rendered from register page', () => {
|
|
props = {
|
|
...props,
|
|
handleErrorChange: jest.fn(),
|
|
};
|
|
|
|
const { getByLabelText } = render(reduxWrapper(<PasswordField {...props} />));
|
|
|
|
const passwordIcon = getByLabelText('Show password');
|
|
|
|
fireEvent.focus(passwordIcon, {
|
|
target: {
|
|
name: 'passwordIcon',
|
|
value: '',
|
|
},
|
|
});
|
|
|
|
expect(props.handleErrorChange).toHaveBeenCalledTimes(0);
|
|
});
|
|
|
|
it('should clear error when focus is fired on password icon click when rendered from register page', () => {
|
|
props = {
|
|
...props,
|
|
handleErrorChange: jest.fn(),
|
|
};
|
|
|
|
const { getByLabelText } = render(reduxWrapper(<PasswordField {...props} />));
|
|
|
|
const passwordIcon = getByLabelText('Show password');
|
|
|
|
fireEvent.focus(passwordIcon, {
|
|
target: {
|
|
name: 'password',
|
|
value: 'invalid',
|
|
},
|
|
});
|
|
|
|
expect(props.handleErrorChange).toHaveBeenCalledTimes(1);
|
|
expect(props.handleErrorChange).toHaveBeenCalledWith(
|
|
'password',
|
|
'',
|
|
);
|
|
});
|
|
|
|
it('should run backend validations when frontend validations pass on blur when rendered from register page', () => {
|
|
store.dispatch = jest.fn(store.dispatch);
|
|
props = {
|
|
...props,
|
|
handleErrorChange: jest.fn(),
|
|
};
|
|
const { getByLabelText } = render(reduxWrapper(<PasswordField {...props} />));
|
|
const passwordField = getByLabelText('Password');
|
|
fireEvent.blur(passwordField, {
|
|
target: {
|
|
name: 'password',
|
|
value: 'password123',
|
|
},
|
|
});
|
|
|
|
expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations({ password: 'password123' }));
|
|
});
|
|
|
|
it('should use password value from prop when password icon is focused out (blur due to icon)', () => {
|
|
store.dispatch = jest.fn(store.dispatch);
|
|
props = {
|
|
...props,
|
|
value: 'testPassword',
|
|
handleErrorChange: jest.fn(),
|
|
handleBlur: jest.fn(),
|
|
};
|
|
const { getByLabelText } = render(reduxWrapper(<PasswordField {...props} />));
|
|
|
|
const passwordIcon = getByLabelText('Show password');
|
|
|
|
fireEvent.blur(passwordIcon, {
|
|
target: {
|
|
name: 'passwordIcon',
|
|
value: undefined,
|
|
},
|
|
});
|
|
|
|
expect(props.handleBlur).toHaveBeenCalledTimes(1);
|
|
expect(props.handleBlur).toHaveBeenCalledWith({
|
|
target: {
|
|
name: 'password',
|
|
value: 'testPassword',
|
|
},
|
|
});
|
|
});
|
|
});
|