From b6a98fe0cc5b7940f14de3fb165a46b7dadd8878 Mon Sep 17 00:00:00 2001 From: Yury Saukou Date: Wed, 18 Dec 2024 12:57:23 +0400 Subject: [PATCH 1/4] fix broken test --- .../modals/SendClaimsModal/SendClaimsModal.test.js | 8 ++++---- translations/stripes-acq-components/en.json | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/claiming/components/modals/SendClaimsModal/SendClaimsModal.test.js b/lib/claiming/components/modals/SendClaimsModal/SendClaimsModal.test.js index db75c474..ab712ff7 100644 --- a/lib/claiming/components/modals/SendClaimsModal/SendClaimsModal.test.js +++ b/lib/claiming/components/modals/SendClaimsModal/SendClaimsModal.test.js @@ -32,7 +32,7 @@ describe('SendClaimsModal', () => { it('should render send claim modal', () => { renderSendClaimsModal(); - expect(screen.getByText('ui-receiving.modal.sendClaim.heading')).toBeInTheDocument(); + expect(screen.getByText('stripes-acq-components.claiming.modal.sendClaim.heading')).toBeInTheDocument(); }); it('should validate "Claim expiry date" field', async () => { @@ -45,7 +45,7 @@ describe('SendClaimsModal', () => { await user.type(screen.getByPlaceholderText(FORMAT), today.format(FORMAT)); await user.click(saveBtn); - expect(screen.getByText('ui-receiving.validation.dateAfter')).toBeInTheDocument(); + expect(screen.getByText('stripes-acq-components.validation.dateAfter')).toBeInTheDocument(); }); it('should submit valid form', async () => { @@ -56,8 +56,8 @@ describe('SendClaimsModal', () => { const externalNote = 'External'; await user.type(screen.getByPlaceholderText(FORMAT), date.format(FORMAT)); - await user.type(screen.getByLabelText('ui-receiving.piece.internalNote'), internalNote); - await user.type(screen.getByLabelText('ui-receiving.piece.externalNote'), externalNote); + await user.type(screen.getByLabelText('stripes-acq-components.claiming.modal.sendClaim.field.internalNote'), internalNote); + await user.type(screen.getByLabelText('stripes-acq-components.claiming.modal.sendClaim.field.externalNote'), externalNote); await user.click(screen.getByRole('button', { name: 'stripes-acq-components.FormFooter.save' })); expect(defaultProps.onSubmit).toHaveBeenCalledWith( diff --git a/translations/stripes-acq-components/en.json b/translations/stripes-acq-components/en.json index 9255cab2..b069c9f8 100644 --- a/translations/stripes-acq-components/en.json +++ b/translations/stripes-acq-components/en.json @@ -212,6 +212,7 @@ "location.label": "Name (code)", "validation.cantContainSpaces": "Spaces aren't allowed", + "validation.dateAfter": "Selected date must be later than the current date", "validation.fundDistributionType": "All distributions should be the same type", "validation.fundDistribution.uniqueFunds": "You can not have multiple distributions for the same fund with the same expense class.", "validation.invalidURL": "URL is not valid, must include URL scheme. Eg. ftp://", From 7c23fdb7aeb7bbc14548517ab104c8783c3a6f1f Mon Sep 17 00:00:00 2001 From: Yury Saukou Date: Wed, 18 Dec 2024 13:47:48 +0400 Subject: [PATCH 2/4] add unit tests --- .../FieldClaimingDate.test.js | 7 ++- .../DelayClaimActionMenuItem.test.js | 48 +++++++++++++++ .../MarkUnreceivableActionMenuItem.test.js | 53 +++++++++++++++++ .../SendClaimActionMenuItem.test.js | 48 +++++++++++++++ .../SendClaimsModal/SendClaimsModal.test.js | 7 ++- .../hooks/useClaimsSend/useClaimsSend.test.js | 59 +++++++++++++++++++ .../usePiecesStatusBatchUpdate.test.js | 44 ++++++++++++++ 7 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 lib/claiming/components/menu-items/DelayClaimActionMenuItem/DelayClaimActionMenuItem.test.js create mode 100644 lib/claiming/components/menu-items/MarkUnreceivableActionMenuItem/MarkUnreceivableActionMenuItem.test.js create mode 100644 lib/claiming/components/menu-items/SendClaimActionMenuItem/SendClaimActionMenuItem.test.js create mode 100644 lib/claiming/hooks/useClaimsSend/useClaimsSend.test.js create mode 100644 lib/hooks/usePiecesStatusBatchUpdate/usePiecesStatusBatchUpdate.test.js diff --git a/lib/claiming/components/FieldClaimingDate/FieldClaimingDate.test.js b/lib/claiming/components/FieldClaimingDate/FieldClaimingDate.test.js index f839362c..fe10532e 100644 --- a/lib/claiming/components/FieldClaimingDate/FieldClaimingDate.test.js +++ b/lib/claiming/components/FieldClaimingDate/FieldClaimingDate.test.js @@ -1,6 +1,11 @@ +/* Developed collaboratively using AI (GitHub Copilot) */ + +import { + render, + screen, +} from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; -import { render, screen } from '@testing-library/react'; import stripesFinalForm from '@folio/stripes/final-form'; import { FieldClaimingDate } from './FieldClaimingDate'; diff --git a/lib/claiming/components/menu-items/DelayClaimActionMenuItem/DelayClaimActionMenuItem.test.js b/lib/claiming/components/menu-items/DelayClaimActionMenuItem/DelayClaimActionMenuItem.test.js new file mode 100644 index 00000000..45bdf8bd --- /dev/null +++ b/lib/claiming/components/menu-items/DelayClaimActionMenuItem/DelayClaimActionMenuItem.test.js @@ -0,0 +1,48 @@ +/* Developed collaboratively using AI (GitHub Copilot) */ + +import { + render, + screen, +} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { DelayClaimActionMenuItem } from './DelayClaimActionMenuItem'; + +const defaultProps = { + onClick: jest.fn(), +}; + +const renderComponent = (props = {}) => render( + , +); + +describe('DelayClaimActionMenuItem', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should call onClick when button is clicked', async () => { + renderComponent(); + + await userEvent.click(screen.getByTestId('delay-claim-button')); + + expect(defaultProps.onClick).toHaveBeenCalledTimes(1); + }); + + it('should be disabled when disabled prop is true', () => { + renderComponent({ disabled: true }); + + expect(screen.getByTestId('delay-claim-button')).toBeDisabled(); + }); + + it('should not call onClick when button is disabled and clicked', async () => { + renderComponent({ disabled: true }); + + await userEvent.click(screen.getByTestId('delay-claim-button')); + + expect(defaultProps.onClick).not.toHaveBeenCalled(); + }); +}); diff --git a/lib/claiming/components/menu-items/MarkUnreceivableActionMenuItem/MarkUnreceivableActionMenuItem.test.js b/lib/claiming/components/menu-items/MarkUnreceivableActionMenuItem/MarkUnreceivableActionMenuItem.test.js new file mode 100644 index 00000000..fdfbdea6 --- /dev/null +++ b/lib/claiming/components/menu-items/MarkUnreceivableActionMenuItem/MarkUnreceivableActionMenuItem.test.js @@ -0,0 +1,53 @@ +/* Developed collaboratively using AI (GitHub Copilot) */ + +import { + render, + screen, +} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { MarkUnreceivableActionMenuItem } from './MarkUnreceivableActionMenuItem'; + +const defaultProps = { + onClick: jest.fn(), + disabled: false, +}; + +const renderComponent = (props = {}) => render( + , +); + +describe('MarkUnreceivableActionMenuItem', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should render the button', () => { + renderComponent(); + + expect(screen.getByTestId('unreceivable-button')).toBeInTheDocument(); + }); + + it('should call onClick when button is clicked', async () => { + renderComponent(); + + await userEvent.click(screen.getByTestId('unreceivable-button')); + + expect(defaultProps.onClick).toHaveBeenCalledTimes(1); + }); + + it('should disable the button when disabled prop is true', () => { + renderComponent({ disabled: true }); + + expect(screen.getByTestId('unreceivable-button')).toBeDisabled(); + }); + + it('should enable the button when disabled prop is false', () => { + renderComponent({ disabled: false }); + + expect(screen.getByTestId('unreceivable-button')).toBeEnabled(); + }); +}); diff --git a/lib/claiming/components/menu-items/SendClaimActionMenuItem/SendClaimActionMenuItem.test.js b/lib/claiming/components/menu-items/SendClaimActionMenuItem/SendClaimActionMenuItem.test.js new file mode 100644 index 00000000..d2ed2015 --- /dev/null +++ b/lib/claiming/components/menu-items/SendClaimActionMenuItem/SendClaimActionMenuItem.test.js @@ -0,0 +1,48 @@ +/* Developed collaboratively using AI (GitHub Copilot) */ + +import { + render, + screen, +} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { SendClaimActionMenuItem } from './SendClaimActionMenuItem'; + +const defaultProps = { + onClick: jest.fn(), +}; + +const renderComponent = (props = {}) => render( + , +); + +describe('SendClaimActionMenuItem', () => { + it('should render the button', () => { + renderComponent(); + + expect(screen.getByTestId('send-claim-button')).toBeInTheDocument(); + }); + + it('should call onClick when button is clicked', async () => { + renderComponent(); + + await userEvent.click(screen.getByTestId('send-claim-button')); + + expect(defaultProps.onClick).toHaveBeenCalled(); + }); + + it('should disable the button when disabled prop is true', () => { + renderComponent({ disabled: true }); + + expect(screen.getByTestId('send-claim-button')).toBeDisabled(); + }); + + it('should enable the button when disabled prop is false', () => { + renderComponent({ disabled: false }); + + expect(screen.getByTestId('send-claim-button')).not.toBeDisabled(); + }); +}); diff --git a/lib/claiming/components/modals/SendClaimsModal/SendClaimsModal.test.js b/lib/claiming/components/modals/SendClaimsModal/SendClaimsModal.test.js index ab712ff7..fe6be2ce 100644 --- a/lib/claiming/components/modals/SendClaimsModal/SendClaimsModal.test.js +++ b/lib/claiming/components/modals/SendClaimsModal/SendClaimsModal.test.js @@ -1,4 +1,7 @@ -import { render, screen } from '@testing-library/react'; +import { + render, + screen, +} from '@testing-library/react'; import user from '@testing-library/user-event'; import { MemoryRouter } from 'react-router-dom'; @@ -41,10 +44,12 @@ describe('SendClaimsModal', () => { const saveBtn = screen.getByRole('button', { name: 'stripes-acq-components.FormFooter.save' }); await user.click(saveBtn); + expect(screen.getByText('stripes-acq-components.validation.required')).toBeInTheDocument(); await user.type(screen.getByPlaceholderText(FORMAT), today.format(FORMAT)); await user.click(saveBtn); + expect(screen.getByText('stripes-acq-components.validation.dateAfter')).toBeInTheDocument(); }); diff --git a/lib/claiming/hooks/useClaimsSend/useClaimsSend.test.js b/lib/claiming/hooks/useClaimsSend/useClaimsSend.test.js new file mode 100644 index 00000000..3fe8939b --- /dev/null +++ b/lib/claiming/hooks/useClaimsSend/useClaimsSend.test.js @@ -0,0 +1,59 @@ +/* Developed collaboratively using AI (GitHub Copilot) */ + +import { renderHook } from '@testing-library/react-hooks'; +import { + QueryClient, + QueryClientProvider, +} from 'react-query'; + +import { useOkapiKy } from '@folio/stripes/core'; + +import { SEND_CLAIMS_API } from '../../../constants'; +import { useClaimsSend } from './useClaimsSend'; + +const queryClient = new QueryClient(); +const wrapper = ({ children }) => ( + + {children} + +); + +describe('useClaimsSend', () => { + const kyPostMock = jest.fn(); + + beforeEach(() => { + useOkapiKy.mockReturnValue({ + post: kyPostMock, + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should call ky.post with correct arguments when sendClaims is called', async () => { + const { result } = renderHook(() => useClaimsSend(), { wrapper }); + const data = { claim: 'test' }; + + kyPostMock.mockReturnValue({ + json: jest.fn().mockResolvedValue({}), + }); + + await result.current.sendClaims({ data }); + + expect(kyPostMock).toHaveBeenCalledWith(SEND_CLAIMS_API, { json: data }); + }); + + it('should handle errors when sendClaims is called', async () => { + const { result } = renderHook(() => useClaimsSend(), { wrapper }); + + const data = { claim: 'test' }; + const error = new Error('Failed to send claims'); + + kyPostMock.mockReturnValue({ + json: jest.fn().mockRejectedValue(error), + }); + + await expect(result.current.sendClaims({ data })).rejects.toThrow(error); + }); +}); diff --git a/lib/hooks/usePiecesStatusBatchUpdate/usePiecesStatusBatchUpdate.test.js b/lib/hooks/usePiecesStatusBatchUpdate/usePiecesStatusBatchUpdate.test.js new file mode 100644 index 00000000..9d3378a8 --- /dev/null +++ b/lib/hooks/usePiecesStatusBatchUpdate/usePiecesStatusBatchUpdate.test.js @@ -0,0 +1,44 @@ +/* Developed collaboratively using AI (GitHub Copilot) */ + +import { renderHook } from '@testing-library/react-hooks'; +import { + QueryClient, + QueryClientProvider, +} from 'react-query'; + +import { useOkapiKy } from '@folio/stripes/core'; + +import { PIECES_BATCH_STATUS_API } from '../../constants'; +import { usePiecesStatusBatchUpdate } from './usePiecesStatusBatchUpdate'; + +const queryClient = new QueryClient(); +const wrapper = ({ children }) => ( + + {children} + +); + +describe('usePiecesStatusBatchUpdate', () => { + const kyMock = { + put: jest.fn(() => ({ + json: jest.fn(), + })), + }; + + beforeEach(() => { + useOkapiKy.mockReturnValue(kyMock); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should call ky.put with correct arguments', async () => { + const { result } = renderHook(() => usePiecesStatusBatchUpdate(), { wrapper }); + const data = { status: 'Received' }; + + await result.current.updatePiecesStatus({ data }); + + expect(kyMock.put).toHaveBeenCalledWith(PIECES_BATCH_STATUS_API, { json: data }); + }); +}); From 57e7673bee35c4382beeb89a282436bede7df6d4 Mon Sep 17 00:00:00 2001 From: Yury Saukou Date: Wed, 18 Dec 2024 14:38:42 +0400 Subject: [PATCH 3/4] add tests --- .../DelayClaimsModal/DelayClaimsModal.test.js | 65 +++++++++++++++++++ .../useClaimsDelay/useClaimsDelay.test.js | 40 ++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 lib/claiming/components/modals/DelayClaimsModal/DelayClaimsModal.test.js create mode 100644 lib/claiming/hooks/useClaimsDelay/useClaimsDelay.test.js diff --git a/lib/claiming/components/modals/DelayClaimsModal/DelayClaimsModal.test.js b/lib/claiming/components/modals/DelayClaimsModal/DelayClaimsModal.test.js new file mode 100644 index 00000000..cd9505b2 --- /dev/null +++ b/lib/claiming/components/modals/DelayClaimsModal/DelayClaimsModal.test.js @@ -0,0 +1,65 @@ +/* Developed collaboratively using AI (GitHub Copilot) */ + +import { + render, + screen, +} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { MemoryRouter } from 'react-router-dom'; + +import { dayjs } from '@folio/stripes/components'; + +import DelayClaimsModal from './DelayClaimsModal'; + +const FORMAT = 'MM/DD/YYYY'; +const today = dayjs(); + +const defaultProps = { + claimsCount: 1, + onSubmit: jest.fn(() => console.log('')), + message: 'Test message', + onCancel: jest.fn(), + open: true, +}; + +const renderComponent = (props = {}) => render( + , + { wrapper: MemoryRouter }, +); + +describe('DelayClaimsModal', () => { + it('should call onCancel when cancel button is clicked', async () => { + renderComponent(); + + await userEvent.click(screen.getByText('stripes-acq-components.FormFooter.cancel')); + + expect(defaultProps.onCancel).toHaveBeenCalled(); + }); + + it('should validate "Delay to" field and call onSubmit when save button is clicked', async () => { + renderComponent(); + + const saveBtn = screen.getByRole('button', { name: 'stripes-acq-components.FormFooter.save' }); + + /* Empty input */ + await userEvent.click(saveBtn); + + expect(screen.getByText('stripes-acq-components.validation.required')).toBeInTheDocument(); + + /* Invalid date input */ + await userEvent.type(screen.getByPlaceholderText(FORMAT), today.format(FORMAT)); + await userEvent.click(saveBtn); + + expect(screen.getByText('stripes-acq-components.validation.dateAfter')).toBeInTheDocument(); + + /* Valid date */ + await userEvent.clear(screen.getByPlaceholderText(FORMAT)); + await userEvent.type(screen.getByPlaceholderText(FORMAT), dayjs().add(5, 'days').format(FORMAT)); + await userEvent.click(saveBtn); + + expect(defaultProps.onSubmit).toHaveBeenCalled(); + }); +}); diff --git a/lib/claiming/hooks/useClaimsDelay/useClaimsDelay.test.js b/lib/claiming/hooks/useClaimsDelay/useClaimsDelay.test.js new file mode 100644 index 00000000..450d8d4c --- /dev/null +++ b/lib/claiming/hooks/useClaimsDelay/useClaimsDelay.test.js @@ -0,0 +1,40 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import { PIECE_STATUS } from '../../../constants'; +import { usePiecesStatusBatchUpdate } from '../../../hooks'; +import { useClaimsDelay } from './useClaimsDelay'; + +jest.mock('../../../hooks', () => ({ + usePiecesStatusBatchUpdate: jest.fn(), +})); + +const mockUpdatePiecesStatus = jest.fn(() => Promise.resolve()); + +describe('useClaimsDelay', () => { + beforeEach(() => { + usePiecesStatusBatchUpdate.mockReturnValue({ + isLoading: false, + updatePiecesStatus: mockUpdatePiecesStatus, + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should call updatePiecesStatus with correct parameters when delayClaims is called', async () => { + const { result } = renderHook(() => useClaimsDelay()); + const claimingInterval = 30; + const pieceIds = ['piece1', 'piece2']; + + await result.current.delayClaims({ claimingInterval, pieceIds }); + + expect(mockUpdatePiecesStatus).toHaveBeenCalledWith({ + data: { + claimingInterval, + pieceIds, + receivingStatus: PIECE_STATUS.claimDelayed, + }, + }); + }); +}); From f2578fea0ace90e60494ddb45541afeded2a590d Mon Sep 17 00:00:00 2001 From: Yury Saukou Date: Wed, 18 Dec 2024 14:50:54 +0400 Subject: [PATCH 4/4] move GH related files to .github folder --- .github/CODEOWNERS | 1 + .../PULL_REQUEST_TEMPLATE.md | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 .github/CODEOWNERS rename PULL_REQUEST_TEMPLATE.md => .github/PULL_REQUEST_TEMPLATE.md (89%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..e029a897 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @folio-org/acquisitions-ui diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 89% rename from PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md index 03c9a5d8..5ccfea1e 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -35,6 +35,19 @@ [ ] Use GitHub checklists. When solved, check the box and explain the answer. --> +## Screenshots + +