From cefd89e315d9ed1c5b51bc422a2bc87aba09adc5 Mon Sep 17 00:00:00 2001 From: Artem_Blazhko Date: Thu, 7 Sep 2023 16:19:24 +0300 Subject: [PATCH] Cover RequesterInformation by jest/RTL tests --- CHANGELOG.md | 1 + .../InstanceInformation.test.js | 3 +- .../RequesterInformation.js | 7 +- .../RequesterInformation.test.js | 661 ++++++++++++++++++ test/jest/__mock__/stripesCore.mock.js | 32 +- 5 files changed, 688 insertions(+), 16 deletions(-) create mode 100644 src/components/RequesterInformation/RequesterInformation.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dce240ac..ad03cea81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ * Correctly escape CQL special characters in tag API queries. Refs UIREQ-918. * Add new pickup service point error. Refs UIREQ-981. * Move request popup show available Request Types. Refs UIREQ-1005. +* Cover RequesterInformation by jest/RTL tests. Refs UIREQ-952. ## [8.0.2](https://github.com/folio-org/ui-requests/tree/v8.0.2) (2023-03-29) [Full Changelog](https://github.com/folio-org/ui-requests/compare/v8.0.1...v8.0.2) diff --git a/src/components/InstanceInformation/InstanceInformation.test.js b/src/components/InstanceInformation/InstanceInformation.test.js index ee0eb5c6b..84378034b 100644 --- a/src/components/InstanceInformation/InstanceInformation.test.js +++ b/src/components/InstanceInformation/InstanceInformation.test.js @@ -235,7 +235,8 @@ describe('InstanceInformation', () => { it('should trigger "findInstance" with correct argument', () => { const hrid = 'hrid'; - const searchButton = screen.getByText('Search Instance'); + const searchButtonLabel = 'Search'; + const searchButton = screen.getByText(searchButtonLabel); fireEvent.click(searchButton); diff --git a/src/components/RequesterInformation/RequesterInformation.js b/src/components/RequesterInformation/RequesterInformation.js index a70bb8412..8e7e27795 100644 --- a/src/components/RequesterInformation/RequesterInformation.js +++ b/src/components/RequesterInformation/RequesterInformation.js @@ -30,8 +30,8 @@ import { import css from '../../requests.css'; -const VISIBLE_COLUMNS = ['active', 'name', 'patronGroup', 'username', 'barcode']; -const COLUMN_MAPPING = { +export const VISIBLE_COLUMNS = ['active', 'name', 'patronGroup', 'username', 'barcode']; +export const COLUMN_MAPPING = { name: , patronGroup: , username: , @@ -42,7 +42,6 @@ class RequesterInformation extends Component { static propTypes = { form: PropTypes.object.isRequired, values: PropTypes.object.isRequired, - selectedUser: PropTypes, onSetSelectedUser: PropTypes.func.isRequired, submitting: PropTypes.bool.isRequired, onSetIsPatronBlocksOverridden: PropTypes.func.isRequired, @@ -52,6 +51,7 @@ class RequesterInformation extends Component { findUser: PropTypes.func.isRequired, triggerUserBarcodeValidation: PropTypes.func.isRequired, stripes: stripesShape.isRequired, + selectedUser: PropTypes.object, isLoading: PropTypes.bool, request: PropTypes.object, optionLists: PropTypes.object, @@ -229,6 +229,7 @@ class RequesterInformation extends Component { return ( ({ + isFormEditing: jest.fn(() => false), + memoizeValidation: (fn) => () => fn, +})); +jest.mock('../../UserForm', () => jest.fn(() =>
User form
)); + +const basicProps = { + triggerUserBarcodeValidation: jest.fn(), + findUser: jest.fn(() => null), + onSetSelectedUser: jest.fn(), + onSetIsPatronBlocksOverridden: jest.fn(), + onSetBlocked: jest.fn(), + onSelectProxy: jest.fn(), + handleCloseProxy: jest.fn(), + form: { + change: jest.fn(), + }, + values: { + keyOfUserBarcodeField: 1, + requester: { + barcode: 'requesterBarcode', + }, + }, + request: { + id: 'requestId', + requester: {}, + }, + selectedUser: {}, + stripes: {}, + optionLists: {}, + selectedProxy: {}, + patronGroup: { + group: 'group', + }, + isLoading: false, + submitting: false, +}; +const labelIds = { + selectUserError: 'ui-requests.errors.selectUser', + userBarcodeDoesNotExistError: 'ui-requests.errors.userBarcodeDoesNotExist', + scanOrEnterBarcodePlaceholder: 'ui-requests.requester.scanOrEnterBarcode', + requesterBarcodeLabel: 'ui-requests.requester.barcode', + findUserPluginLabel: 'ui-requests.requester.findUserPluginLabel', + enterButton: 'ui-requests.enter', +}; +const testIds = { + requesterBarcodeField: 'requesterBarcodeField', + searchUser: 'searchUser', + errorMessage: 'errorMessage', +}; +const renderRequesterInfoWithBarcode = (onBlur) => { + Field.mockImplementation(jest.fn(({ + children, + 'data-testid': testId, + validate, + }) => { + return children({ + meta: {}, + input: { + validate, + 'data-testid': testId, + value: 'requesterBarcode', + onBlur, + }, + }); + })); + + render( + + ); +}; + +describe('RequesterInformation', () => { + afterEach(() => { + Field.mockClear(); + basicProps.onSetSelectedUser.mockReset(); + cleanup(); + }); + + describe('when "isFormEditing" returns false', () => { + beforeEach(() => { + render( + + ); + }); + + it('should render "scanOrEnterBarcode" placeholder', () => { + const scanOrEnterBarcodePlaceholder = screen.getByPlaceholderText(labelIds.scanOrEnterBarcodePlaceholder); + + expect(scanOrEnterBarcodePlaceholder).toBeVisible(); + }); + + it('should render requester barcode label', () => { + const requesterBarcodeLabel = screen.getByText(labelIds.requesterBarcodeLabel); + + expect(requesterBarcodeLabel).toBeVisible(); + }); + + it('should trigger requester barcode Field with correct props', () => { + const expectedProps = { + name: REQUEST_FORM_FIELD_NAMES.REQUESTER_BARCODE, + validate: expect.any(Function), + validateFields: [], + }; + + expect(Field).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); + }); + + it('should trigger "findUser" when Enter key is pressed', () => { + const requesterBarcodeField = screen.getByTestId(testIds.requesterBarcodeField); + + fireEvent.keyDown(requesterBarcodeField, { key: ENTER_EVENT_KEY }); + + expect(basicProps.findUser).toHaveBeenCalledWith(RESOURCE_KEYS.barcode, basicProps.values.requester.barcode); + }); + + it('should not trigger "findUser" when Control key is pressed', () => { + const requesterBarcodeField = screen.getByTestId(testIds.requesterBarcodeField); + + fireEvent.keyDown(requesterBarcodeField, { key: 'Control' }); + + expect(basicProps.findUser).not.toHaveBeenCalledWith(); + }); + + it('should trigger "form.change" with correct arguments', () => { + const requesterBarcodeField = screen.getByTestId(testIds.requesterBarcodeField); + const event = { + target: { + value: 'requesterBarcode', + }, + }; + + fireEvent.change(requesterBarcodeField, event); + + expect(basicProps.form.change).toHaveBeenCalledWith(REQUEST_FORM_FIELD_NAMES.REQUESTER_BARCODE, event.target.value); + }); + + it('should trigger "TextField" with correct props', () => { + const expectedProps = { + required: true, + error: null, + onChange: expect.any(Function), + onBlur: expect.any(Function), + onKeyDown: expect.any(Function), + }; + + expect(TextField).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); + }); + + it('should render "TextField" with validation error', () => { + const requesterBarcodeField = screen.getByTestId(testIds.requesterBarcodeField); + const enterButton = screen.getByText(labelIds.enterButton); + const error = 'error'; + const event = { + target: { + value: 'barcode', + }, + }; + + Field.mockImplementationOnce(jest.fn(({ + children, + 'data-testid': testId, + validate, + }) => { + return children({ + meta: { + touched: true, + error, + }, + input: { + validate, + 'data-testid': testId, + }, + }); + })); + + fireEvent.click(enterButton); + fireEvent.change(requesterBarcodeField, event); + + expect(TextField).toHaveBeenCalledWith(expect.objectContaining({ error }), {}); + }); + + it('should render "Enter" button', () => { + const enterButton = screen.getByText(labelIds.enterButton); + + expect(enterButton).toBeVisible(); + }); + + it('should trigger "onSetSelectedUser" with correct argument', () => { + const enterButton = screen.getByText(labelIds.enterButton); + + fireEvent.click(enterButton); + + expect(basicProps.onSetSelectedUser).toHaveBeenCalledWith(null); + }); + + it('should trigger "findUser" with correct arguments', () => { + const enterButton = screen.getByText(labelIds.enterButton); + + fireEvent.click(enterButton); + + expect(basicProps.findUser).toHaveBeenCalledWith(RESOURCE_KEYS.barcode, basicProps.values.requester.barcode); + }); + }); + + describe('when "isFormEditing" returns true', () => { + beforeEach(() => { + isFormEditing.mockReturnValueOnce(true); + }); + + it('should trigger "UserForm" with correct props', () => { + const props = { + ...basicProps, + selectedUser: { + id: 'selectedUserId', + }, + }; + const expectedProps = { + user: basicProps.request.requester, + stripes: basicProps.stripes, + request: basicProps.request, + patronGroup: basicProps.patronGroup.group, + proxy: basicProps.selectedProxy, + onSelectProxy: basicProps.onSelectProxy, + onCloseProxy: basicProps.handleCloseProxy, + }; + + render( + + ); + + expect(UserForm).toHaveBeenCalledWith(expectedProps, {}); + }); + + it('should trigger "UserForm" with "selectedUser"', () => { + const props = { + ...basicProps, + request: undefined, + selectedUser: { + id: 'selectedUserId', + }, + }; + const expectedProps = { + user: props.selectedUser, + }; + + render( + + ); + + expect(UserForm).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); + }); + }); + + describe('Validation', () => { + afterEach(() => { + TextField.mockClear(); + basicProps.findUser.mockClear(); + }); + + beforeEach(() => { + TextField.mockImplementation(({ + onChange, + validate, + ...rest + }) => { + const [error, setError] = useState(''); + const handleChange = async (e) => { + setError(await validate(e.target.value)); + onChange(e); + }; + + return ( +
+ + {error} +
+ ); + }); + }); + + describe('when "barcode" is not presented', () => { + const event = { + target: { + value: '', + }, + }; + + it('should not render error message', async () => { + const props = { + ...basicProps, + selectedUser: { + id: 'selectedUserId', + }, + }; + + render( + + ); + + const requesterBarcodeField = screen.getByTestId(testIds.requesterBarcodeField); + + fireEvent.change(requesterBarcodeField, event); + + await waitFor(() => { + const errorMessage = screen.getByTestId(testIds.errorMessage); + + expect(errorMessage).toBeEmpty(); + }); + }); + + it('should render "selectUser" error message', async () => { + render( + + ); + + const requesterBarcodeField = screen.getByTestId(testIds.requesterBarcodeField); + + fireEvent.change(requesterBarcodeField, event); + + await waitFor(() => { + const errorMessage = screen.queryByText(labelIds.selectUserError); + + expect(errorMessage).toBeVisible(); + }); + }); + }); + + describe('when "barcode" is presented', () => { + const event = { + target: { + value: 'barcode', + }, + }; + + beforeEach(() => { + render( + + ); + }); + + it('should not render error message', async () => { + const requesterBarcodeField = screen.getByTestId(testIds.requesterBarcodeField); + + fireEvent.change(requesterBarcodeField, event); + + await waitFor(() => { + const errorMessage = screen.getByTestId(testIds.errorMessage); + + expect(errorMessage).toBeEmpty(); + }); + }); + + it('should render "userBarcodeDoesNotExist" error message', async () => { + const requesterBarcodeField = screen.getByTestId(testIds.requesterBarcodeField); + + fireEvent.keyDown(requesterBarcodeField, { key: ENTER_EVENT_KEY }); + fireEvent.change(requesterBarcodeField, event); + + await waitFor(() => { + const errorMessage = screen.queryByText(labelIds.userBarcodeDoesNotExistError); + + expect(errorMessage).toBeVisible(); + }); + }); + + it('should not render error message if requester found', async () => { + basicProps.findUser.mockReturnValue({}); + + const requesterBarcodeField = screen.getByTestId(testIds.requesterBarcodeField); + + fireEvent.keyDown(requesterBarcodeField, { key: ENTER_EVENT_KEY }); + fireEvent.change(requesterBarcodeField, event); + + await waitFor(() => { + const errorMessage = screen.getByTestId(testIds.errorMessage); + + expect(errorMessage).toBeEmpty(); + }); + }); + }); + }); + + describe('handleBlur', () => { + const onBlur = jest.fn(); + + afterEach(() => { + onBlur.mockClear(); + }); + + it('should trigger "input.onBlur" if requester barcode is presented', () => { + renderRequesterInfoWithBarcode(onBlur); + + const requesterBarcodeField = screen.getByTestId(testIds.requesterBarcodeField); + + fireEvent.click(requesterBarcodeField); + fireEvent.blur(requesterBarcodeField); + + expect(onBlur).toHaveBeenCalled(); + }); + + it('should trigger "input.onBlur" if there is no requester barcode', () => { + Field.mockImplementationOnce(({ + children, + 'data-testid': testId, + validate, + }) => { + return children({ + meta: {}, + input: { + validate, + 'data-testid': testId, + value: '', + onBlur, + }, + }); + }); + + render( + + ); + + const requesterBarcodeField = screen.getByTestId(testIds.requesterBarcodeField); + + fireEvent.click(requesterBarcodeField); + fireEvent.blur(requesterBarcodeField); + + expect(onBlur).toHaveBeenCalled(); + }); + + it('should not trigger "input.onBlur" if requester barcode was validated previously', () => { + renderRequesterInfoWithBarcode(onBlur); + + const requesterBarcodeField = screen.getByTestId(testIds.requesterBarcodeField); + + // first input focus + fireEvent.click(requesterBarcodeField); + fireEvent.blur(requesterBarcodeField); + onBlur.mockClear(); + + // second input focus after validation of initial value + fireEvent.click(requesterBarcodeField); + fireEvent.blur(requesterBarcodeField); + + expect(onBlur).not.toHaveBeenCalled(); + }); + }); + + describe('Pluggable', () => { + describe('when user is not presented', () => { + beforeEach(() => { + render( + + ); + }); + + it('should render user lookup plugin label', () => { + const findUserPluginLabel = screen.getByText(labelIds.findUserPluginLabel); + + expect(findUserPluginLabel).toBeVisible(); + }); + + it('should trigger "Pluggable" with correct props', () => { + const expectedProps = { + 'aria-haspopup': 'true', + searchButtonStyle: 'link', + type: 'find-user', + dataKey: 'users', + selectUser: expect.any(Function), + visibleColumns: VISIBLE_COLUMNS, + columnMapping: COLUMN_MAPPING, + disableRecordCreation: true, + marginTop0: true, + }; + + expect(Pluggable).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); + }); + + it('should not trigger "onSetSelectedUser"', () => { + const searchButtonLabel = 'Search'; + const searchButton = screen.getByText(searchButtonLabel); + + fireEvent.click(searchButton); + + expect(basicProps.onSetSelectedUser).not.toHaveBeenCalled(); + }); + }); + + describe('when user has a barcode', () => { + const user = { + barcode: 'userBarcode', + }; + + beforeEach(() => { + Pluggable.mockImplementationOnce(({ + selectUser, + }) => ( + + )); + + render( + + ); + + const searchButton = screen.getByTestId(testIds.searchUser); + + fireEvent.click(searchButton); + }); + + it('should trigger "onSetSelectedUser" with correct argument', () => { + expect(basicProps.onSetSelectedUser).toHaveBeenCalledWith(null); + }); + + it('should trigger "findUser" with correct arguments', () => { + const expectedArgs = [RESOURCE_KEYS.barcode, user.barcode]; + + expect(basicProps.findUser).toHaveBeenCalledWith(...expectedArgs); + }); + }); + + describe('when user does not have a barcode', () => { + const user = { + id: 'userId', + }; + + beforeEach(() => { + Pluggable.mockImplementationOnce(({ + selectUser, + }) => ( + + )); + + render( + + ); + + const searchButton = screen.getByTestId(testIds.searchUser); + + fireEvent.click(searchButton); + }); + + it('should trigger "onSetSelectedUser" with correct argument', () => { + expect(basicProps.onSetSelectedUser).toHaveBeenCalledWith(null); + }); + + it('should trigger "findUser" with correct arguments', () => { + const expectedArgs = [RESOURCE_KEYS.id, user.id]; + + expect(basicProps.findUser).toHaveBeenCalledWith(...expectedArgs); + }); + }); + }); + + describe('Spinner', () => { + afterEach(() => { + Icon.mockClear(); + }); + + describe('when data is loading', () => { + const props = { + ...basicProps, + isLoading: true, + }; + + beforeEach(() => { + render( + + ); + }); + + it('should render loading "Icon" with correct props', () => { + expect(Icon).toHaveBeenCalledWith(BASE_SPINNER_PROPS, {}); + }); + }); + + describe('when data is not loading', () => { + beforeEach(() => { + render( + + ); + }); + + it('should not render loading "Icon"', () => { + expect(Icon).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/test/jest/__mock__/stripesCore.mock.js b/test/jest/__mock__/stripesCore.mock.js index 63ef598ba..a7f2092e1 100644 --- a/test/jest/__mock__/stripesCore.mock.js +++ b/test/jest/__mock__/stripesCore.mock.js @@ -25,18 +25,26 @@ jest.mock('@folio/stripes/core', () => ({ stripesConnect: Component => props => , Pluggable: jest.fn(({ searchLabel, - selectInstance, - }) => ( - <> -
{searchLabel}
- - - )), + selectInstance = () => {}, + selectUser = () => {}, + }) => { + const handleClick = () => { + selectInstance({ hrid: 'hrid' }); + selectUser(); + }; + + return ( + <> +
{searchLabel}
+ + + ); + }), IfPermission: jest.fn(({ children }) =>
{children}
), TitleManager: jest.fn(jest.fn(() => null)), AppIcon: jest.fn(({ children }) =>
{children}
),