@@ -2091,6 +2091,87 @@ describe('Transaction', () => {
20912091 expect ( Object . keys ( reportActions ?? { } ) . length ) . toBe ( 0 ) ;
20922092 } ) ;
20932093
2094+ it ( 'should preserve an existing hold when optimistic dismissal is built from a stale transaction snapshot' , async ( ) => {
2095+ const transactionID = 'dismissTxnHold' ;
2096+ const threadReportID = 'threadDismissHold' ;
2097+ const transactionKey = `${ ONYXKEYS . COLLECTION . TRANSACTION } ${ transactionID } ` as const ;
2098+ const testerEmail = 'tester@example.com' ;
2099+ const mockViolations : TransactionViolation [ ] = [ { name : CONST . VIOLATIONS . DUPLICATED_TRANSACTION , type : 'warning' } ] ;
2100+
2101+ mockFetch . pause ( ) ;
2102+
2103+ await Onyx . merge ( `${ ONYXKEYS . COLLECTION . TRANSACTION_VIOLATIONS } ${ transactionID } ` , mockViolations ) ;
2104+
2105+ const transactionInOnyx = generateTransaction ( {
2106+ transactionID,
2107+ reportID : FAKE_OLD_REPORT_ID ,
2108+ comment : {
2109+ hold : 'holdReportActionID' ,
2110+ dismissedViolations : {
2111+ [ CONST . VIOLATIONS . SMARTSCAN_FAILED ] : {
2112+ owner : 123 ,
2113+ } ,
2114+ } ,
2115+ } ,
2116+ } ) ;
2117+ await Onyx . merge ( transactionKey , transactionInOnyx ) ;
2118+
2119+ const staleTransaction = {
2120+ ...transactionInOnyx ,
2121+ comment : {
2122+ dismissedViolations : {
2123+ [ CONST . VIOLATIONS . SMARTSCAN_FAILED ] : {
2124+ owner : 123 ,
2125+ } ,
2126+ } ,
2127+ hold : undefined ,
2128+ } ,
2129+ } as Transaction ;
2130+
2131+ const iouAction = {
2132+ reportActionID : rand64 ( ) ,
2133+ actionName : CONST . REPORT . ACTIONS . TYPE . IOU ,
2134+ childReportID : threadReportID ,
2135+ actorAccountID : CURRENT_USER_ID ,
2136+ created : DateUtils . getDBTime ( ) ,
2137+ originalMessage : {
2138+ IOUReportID : FAKE_OLD_REPORT_ID ,
2139+ IOUTransactionID : transactionID ,
2140+ amount : transactionInOnyx . amount ,
2141+ currency : transactionInOnyx . currency ,
2142+ type : CONST . IOU . REPORT_ACTION_TYPE . CREATE ,
2143+ } ,
2144+ } ;
2145+ await Onyx . merge ( `${ ONYXKEYS . COLLECTION . REPORT_ACTIONS } ${ FAKE_OLD_REPORT_ID } ` , { [ iouAction . reportActionID ] : iouAction } ) ;
2146+ await Onyx . merge ( `${ ONYXKEYS . COLLECTION . REPORT_ACTIONS } ${ threadReportID } ` , { } ) ;
2147+
2148+ mockFetch . fail ( ) ;
2149+
2150+ dismissDuplicateTransactionViolation ( {
2151+ transactionIDs : [ transactionID ] ,
2152+ dismissedPersonalDetails : { login : testerEmail , accountID : CURRENT_USER_ID } ,
2153+ expenseReport : newReport ,
2154+ policy : undefined ,
2155+ isASAPSubmitBetaEnabled : false ,
2156+ allTransactions : { [ transactionKey ] : staleTransaction } ,
2157+ } ) ;
2158+ await waitForBatchedUpdates ( ) ;
2159+
2160+ const optimisticTransaction = await getOnyxValue ( transactionKey ) ;
2161+ const duplicateDismissals = optimisticTransaction ?. comment ?. dismissedViolations ?. [ CONST . VIOLATIONS . DUPLICATED_TRANSACTION ] ;
2162+ expect ( optimisticTransaction ?. comment ?. hold ) . toBe ( 'holdReportActionID' ) ;
2163+ expect ( optimisticTransaction ?. comment ?. dismissedViolations ?. [ CONST . VIOLATIONS . SMARTSCAN_FAILED ] ) . toEqual ( { owner : 123 } ) ;
2164+ expect ( duplicateDismissals ?. [ testerEmail ] ) . toEqual ( expect . any ( Number ) ) ;
2165+
2166+ await mockFetch . resume ( ) ;
2167+ await waitForBatchedUpdates ( ) ;
2168+
2169+ const revertedTransaction = await getOnyxValue ( transactionKey ) ;
2170+ expect ( revertedTransaction ?. comment ?. hold ) . toBe ( 'holdReportActionID' ) ;
2171+ expect ( revertedTransaction ?. comment ?. dismissedViolations ?. [ CONST . VIOLATIONS . SMARTSCAN_FAILED ] ) . toEqual ( { owner : 123 } ) ;
2172+ expect ( revertedTransaction ?. comment ?. dismissedViolations ?. [ CONST . VIOLATIONS . DUPLICATED_TRANSACTION ] ) . toBeUndefined ( ) ;
2173+ } ) ;
2174+
20942175 it ( 'should not modify Onyx data when tag list does not exist at given index (empty violations array)' , async ( ) => {
20952176 const transactionID = 'dismissTxn3' ;
20962177 const threadReportID = 'threadDismiss3' ;
0 commit comments