1- import { render , screen , waitFor } from '@testing-library/react' ;
1+ import { fireEvent , render , screen , waitFor } from '@testing-library/react' ;
22import userEvent from '@testing-library/user-event' ;
33import { Provider } from 'react-redux' ;
44import { configureStore } from '@reduxjs/toolkit' ;
5- import { MemoryRouter , Route } from 'react-router-dom' ;
5+ import { MemoryRouter , Route , useHistory } from 'react-router-dom' ;
66import RefundRequests from '../RefundRequests'
7+ import routes from '../../../routes' ;
78
89jest . mock ( 'react-i18next' , ( ) => ( {
910 useTranslation : ( ) => ( {
1011 t : ( key : string ) => key ,
1112 } ) ,
1213} ) ) ;
1314
15+ const mockSetAlert = jest . fn ( ) ;
16+ jest . mock ( '../../../hooks/useAlert' , ( ) => ( {
17+ __esModule : true ,
18+ useAlert : ( ) => ( { setAlert : mockSetAlert } ) ,
19+ } ) ) ;
20+
21+ jest . mock ( 'react-router-dom' , ( ) => ( {
22+ ...jest . requireActual ( 'react-router-dom' ) ,
23+ useHistory : jest . fn ( ) ,
24+ } ) ) ;
25+
1426jest . mock ( '@pagopa/selfcare-common-frontend' , ( ) => ( {
1527 TitleBox : ( { title, subTitle } : any ) => (
1628 < div >
@@ -199,6 +211,30 @@ describe('RefundRequests', () => {
199211 expect ( screen . getByTestId ( 'data-table' ) ) . toBeInTheDocument ( ) ;
200212 } ) ;
201213
214+ it ( 'should call history.push' , async ( ) => {
215+ const pushMock = jest . fn ( ) ;
216+
217+ ( useHistory as jest . Mock ) . mockReturnValue ( {
218+ push : pushMock ,
219+ } ) ;
220+
221+ const params = {
222+ row : {
223+ id : '1' ,
224+ name : '001-20251125 223' ,
225+ } ,
226+ } ;
227+ renderWithStore ( < RefundRequests /> ) ;
228+
229+ await waitFor ( ( ) => {
230+ expect ( mockGetRewardBatches ) . toHaveBeenCalled ( ) ;
231+ } ) ;
232+
233+ fireEvent . click ( screen . getByTestId ( '1' ) ) ;
234+
235+ expect ( pushMock ) . toHaveBeenCalled ( ) ;
236+ } ) ;
237+
202238 it ( 'should fetch reward batches on mount' , async ( ) => {
203239 renderWithStore ( < RefundRequests /> ) ;
204240
@@ -521,25 +557,79 @@ describe('RefundRequests', () => {
521557 } ) ;
522558
523559 it ( 'should handle missing initiativeId when sending batch' , async ( ) => {
560+ const user = userEvent . setup ( ) ;
524561 const consoleErrorSpy = jest . spyOn ( console , 'error' ) . mockImplementation ( ) ;
525- const storeWithoutInitiatives = createMockStore ( [ ] ) ;
526562
527- renderWithStore ( < RefundRequests /> , storeWithoutInitiatives ) ;
563+ const currentYear = new Date ( ) . getFullYear ( ) ;
564+ const monthAlwaysSelectable = `${ currentYear } -00` ;
565+ const data = [
566+ {
567+ id : 31 ,
568+ name : 'batch-missing-initiative-id' ,
569+ posType : 'PHYSICAL' ,
570+ initialAmountCents : 10000 ,
571+ status : 'CREATED' ,
572+ month : monthAlwaysSelectable ,
573+ numberOfTransactions : 1 ,
574+ } ,
575+ ] ;
576+
577+ mockGetRewardBatches . mockResolvedValueOnce ( { content : data } ) ;
578+
579+ const storeWithUndefinedInitiativeId = createMockStore ( [ { initiativeId : undefined } as any ] ) ;
580+ renderWithStore ( < RefundRequests /> , storeWithUndefinedInitiativeId ) ;
581+
582+ await waitFor ( ( ) => expect ( screen . getByTestId ( 'checkbox-31' ) ) . toBeInTheDocument ( ) ) ;
583+
584+ await user . click ( screen . getByTestId ( 'checkbox-31' ) ) ;
585+
586+ const sendButton = await screen . findByRole ( 'button' , { name : / p a g e s .r e f u n d R e q u e s t s .s e n d R e q u e s t s / i } ) ;
587+ await user . click ( sendButton ) ;
588+
589+ const confirmButton = await screen . findByText ( 'Invia' ) ;
590+ await user . click ( confirmButton ) ;
528591
529592 await waitFor ( ( ) => {
530- expect ( screen . getByText ( 'pages.refundRequests.title' ) ) . toBeInTheDocument ( ) ;
593+ expect ( consoleErrorSpy ) . toHaveBeenCalledWith ( 'Missing initiativeId or batchId' ) ;
531594 } ) ;
532595
533596 consoleErrorSpy . mockRestore ( ) ;
534597 } ) ;
535598
536599 it ( 'should handle missing batchId when sending batch' , async ( ) => {
600+ const user = userEvent . setup ( ) ;
537601 const consoleErrorSpy = jest . spyOn ( console , 'error' ) . mockImplementation ( ) ;
538602
603+ const currentYear = new Date ( ) . getFullYear ( ) ;
604+ const monthAlwaysSelectable = `${ currentYear } -00` ;
605+ const data = [
606+ {
607+ id : 0 ,
608+ name : 'batch-missing-id' ,
609+ posType : 'PHYSICAL' ,
610+ initialAmountCents : 10000 ,
611+ status : 'CREATED' ,
612+ month : monthAlwaysSelectable ,
613+ numberOfTransactions : 1 ,
614+ } ,
615+ ] ;
616+
617+ mockGetRewardBatches . mockResolvedValueOnce ( { content : data } ) ;
618+
539619 renderWithStore ( < RefundRequests /> ) ;
540620
621+ await waitFor ( ( ) => expect ( screen . getByTestId ( 'checkbox-0' ) ) . toBeInTheDocument ( ) ) ;
622+
623+ await user . click ( screen . getByTestId ( 'checkbox-0' ) ) ;
624+
625+ const sendButton = await screen . findByRole ( 'button' , { name : / p a g e s .r e f u n d R e q u e s t s .s e n d R e q u e s t s / i } ) ;
626+ await user . click ( sendButton ) ;
627+
628+ const confirmButton = await screen . findByText ( 'Invia' ) ;
629+ await user . click ( confirmButton ) ;
630+
541631 await waitFor ( ( ) => {
542- expect ( screen . getByTestId ( 'data-table' ) ) . toBeInTheDocument ( ) ;
632+ expect ( consoleErrorSpy ) . toHaveBeenCalledWith ( 'Missing initiativeId or batchId' ) ;
543633 } ) ;
544634
545635 consoleErrorSpy . mockRestore ( ) ;
@@ -577,4 +667,179 @@ describe('RefundRequests', () => {
577667 expect ( headers [ 0 ] ) . toHaveTextContent ( '' ) ;
578668 } ) ;
579669 } ) ;
580- } ) ;
670+
671+ it ( 'should apply isRowSelectable rules for month/year, numberOfTransactions, and missing month' , async ( ) => {
672+ const currentYear = new Date ( ) . getFullYear ( ) ;
673+ const selectableSameYearPastMonth = `${ currentYear } -00` ;
674+ const selectablePreviousYear = `${ currentYear - 1 } -12` ;
675+ const notSelectableSameYearFutureMonth = `${ currentYear } -13` ;
676+ const customData = [
677+ {
678+ id : 11 ,
679+ name : 'same-year-past' ,
680+ posType : 'PHYSICAL' ,
681+ initialAmountCents : 10000 ,
682+ status : 'CREATED' ,
683+ month : selectableSameYearPastMonth ,
684+ numberOfTransactions : 1 ,
685+ } ,
686+ {
687+ id : 12 ,
688+ name : 'same-year-future' ,
689+ posType : 'PHYSICAL' ,
690+ initialAmountCents : 10000 ,
691+ status : 'CREATED' ,
692+ month : notSelectableSameYearFutureMonth ,
693+ numberOfTransactions : 1 ,
694+ } ,
695+ {
696+ id : 13 ,
697+ name : 'missing-month' ,
698+ posType : 'PHYSICAL' ,
699+ initialAmountCents : 10000 ,
700+ status : 'CREATED' ,
701+ numberOfTransactions : 1 ,
702+ } ,
703+ {
704+ id : 14 ,
705+ name : 'previous-year' ,
706+ posType : 'PHYSICAL' ,
707+ initialAmountCents : 10000 ,
708+ status : 'CREATED' ,
709+ month : selectablePreviousYear ,
710+ numberOfTransactions : 1 ,
711+ } ,
712+ {
713+ id : 15 ,
714+ name : 'zero-transactions' ,
715+ posType : 'PHYSICAL' ,
716+ initialAmountCents : 10000 ,
717+ status : 'CREATED' ,
718+ month : selectableSameYearPastMonth ,
719+ numberOfTransactions : 0 ,
720+ } ,
721+ {
722+ id : 16 ,
723+ name : 'not-created' ,
724+ posType : 'PHYSICAL' ,
725+ initialAmountCents : 10000 ,
726+ status : 'SENT' ,
727+ month : selectableSameYearPastMonth ,
728+ numberOfTransactions : 1 ,
729+ } ,
730+ ] ;
731+
732+ mockGetRewardBatches . mockResolvedValueOnce ( { content : customData } ) ;
733+
734+ renderWithStore ( < RefundRequests /> ) ;
735+
736+ await waitFor ( ( ) => expect ( screen . getByTestId ( 'data-table' ) ) . toBeInTheDocument ( ) ) ;
737+
738+ expect ( screen . getByTestId ( 'checkbox-11' ) ) . toBeInTheDocument ( ) ;
739+ expect ( screen . getByTestId ( 'checkbox-14' ) ) . toBeInTheDocument ( ) ;
740+
741+ expect ( screen . queryByTestId ( 'checkbox-12' ) ) . not . toBeInTheDocument ( ) ;
742+ expect ( screen . queryByTestId ( 'checkbox-13' ) ) . not . toBeInTheDocument ( ) ;
743+ expect ( screen . queryByTestId ( 'checkbox-15' ) ) . not . toBeInTheDocument ( ) ;
744+ expect ( screen . queryByTestId ( 'checkbox-16' ) ) . not . toBeInTheDocument ( ) ;
745+ } ) ;
746+
747+ it ( 'should show specific error alert when backend says previous month batch was not sent (REWARD_BATCH_PREVIOUS_NOT_SENT)' , async ( ) => {
748+ const user = userEvent . setup ( ) ;
749+
750+ const currentYear = new Date ( ) . getFullYear ( ) ;
751+ const monthAlwaysSelectable = `${ currentYear } -00` ;
752+ const data = [
753+ {
754+ id : 41 ,
755+ name : 'batch-prev-not-sent' ,
756+ posType : 'PHYSICAL' ,
757+ initialAmountCents : 10000 ,
758+ status : 'CREATED' ,
759+ month : monthAlwaysSelectable ,
760+ numberOfTransactions : 1 ,
761+ } ,
762+ ] ;
763+
764+ mockGetRewardBatches . mockResolvedValueOnce ( { content : data } ) ;
765+ mockSendRewardBatch . mockResolvedValueOnce ( { code : 'REWARD_BATCH_PREVIOUS_NOT_SENT' } ) ;
766+
767+ renderWithStore ( < RefundRequests /> ) ;
768+
769+ await waitFor ( ( ) => expect ( screen . getByTestId ( 'checkbox-41' ) ) . toBeInTheDocument ( ) ) ;
770+
771+ await user . click ( screen . getByTestId ( 'checkbox-41' ) ) ;
772+
773+ const sendButton = await screen . findByRole ( 'button' , { name : / p a g e s .r e f u n d R e q u e s t s .s e n d R e q u e s t s / i } ) ;
774+ await user . click ( sendButton ) ;
775+
776+ const confirmButton = await screen . findByText ( 'Invia' ) ;
777+ await user . click ( confirmButton ) ;
778+
779+ await waitFor ( ( ) => {
780+ expect ( mockSendRewardBatch ) . toHaveBeenCalledWith ( 'test-initiative-id' , '41' ) ;
781+ } ) ;
782+
783+ expect ( mockSetAlert ) . toHaveBeenCalledWith (
784+ expect . objectContaining ( {
785+ title : 'errors.genericTitle' ,
786+ text : 'errors.sendTheBatchForPreviousMonth' ,
787+ isOpen : true ,
788+ severity : 'error' ,
789+ } )
790+ ) ;
791+
792+ expect ( mockGetRewardBatches ) . toHaveBeenCalledTimes ( 1 ) ;
793+
794+ await waitFor ( ( ) => {
795+ expect ( screen . queryByTestId ( 'refund-modal' ) ) . not . toBeInTheDocument ( ) ;
796+ } ) ;
797+ } ) ;
798+ it ( 'should map approved/suspended amounts only for APPROVED batches (others become undefined)' , async ( ) => {
799+ const currentYear = new Date ( ) . getFullYear ( ) ;
800+ const monthAlwaysSelectable = `${ currentYear } -00` ;
801+
802+ const dataWithApprovedAmounts = [
803+ {
804+ id : 21 ,
805+ name : 'approved-batch' ,
806+ posType : 'ONLINE' ,
807+ initialAmountCents : 5000 ,
808+ approvedAmountCents : 12345 ,
809+ suspendedAmountCents : 200 ,
810+ status : 'APPROVED' ,
811+ month : monthAlwaysSelectable ,
812+ numberOfTransactions : 1 ,
813+ } ,
814+ {
815+ id : 22 ,
816+ name : 'created-but-has-amounts-in-response' ,
817+ posType : 'ONLINE' ,
818+ initialAmountCents : 7000 ,
819+ approvedAmountCents : 9999 ,
820+ suspendedAmountCents : 8888 ,
821+ status : 'CREATED' ,
822+ month : monthAlwaysSelectable ,
823+ numberOfTransactions : 1 ,
824+ } ,
825+ ] ;
826+
827+ mockGetRewardBatches . mockResolvedValueOnce ( { content : dataWithApprovedAmounts } ) ;
828+
829+ renderWithStore ( < RefundRequests /> ) ;
830+
831+ await waitFor ( ( ) => expect ( screen . getByTestId ( 'data-table' ) ) . toBeInTheDocument ( ) ) ;
832+
833+ expect ( screen . getByText ( 'Rimborso approvato' ) ) . toBeInTheDocument ( ) ;
834+ expect ( screen . getByText ( 'Rimborso sospeso' ) ) . toBeInTheDocument ( ) ;
835+
836+ expect ( screen . getByText ( '123.45 €' ) ) . toBeInTheDocument ( ) ;
837+ expect ( screen . getByText ( '2.00 €' ) ) . toBeInTheDocument ( ) ;
838+
839+ expect ( screen . queryByText ( '99.99 €' ) ) . not . toBeInTheDocument ( ) ;
840+ expect ( screen . queryByText ( '88.88 €' ) ) . not . toBeInTheDocument ( ) ;
841+
842+ const nanValues = screen . getAllByText ( 'NaN €' ) ;
843+ expect ( nanValues . length ) . toBeGreaterThanOrEqual ( 2 ) ;
844+ } ) ;
845+ } ) ;
0 commit comments