@@ -29,6 +29,7 @@ import {
2929 useTransactionPayRequiredTokens ,
3030 useIsTransactionPayLoading ,
3131 useTransactionPayQuotes ,
32+ useTransactionPayQuotesLastUpdated ,
3233} from '../../../hooks/pay/useTransactionPayData' ;
3334import { useTransactionPayHasSourceAmount } from '../../../hooks/pay/useTransactionPayHasSourceAmount' ;
3435import { strings } from '../../../../../../../locales/i18n' ;
@@ -276,6 +277,9 @@ describe('CustomAmountInfo', () => {
276277 ) ;
277278
278279 const useTransactionPayQuotesMock = jest . mocked ( useTransactionPayQuotes ) ;
280+ const useTransactionPayQuotesLastUpdatedMock = jest . mocked (
281+ useTransactionPayQuotesLastUpdated ,
282+ ) ;
279283
280284 const useTransactionPayHasSourceAmountMock = jest . mocked (
281285 useTransactionPayHasSourceAmount ,
@@ -403,6 +407,7 @@ describe('CustomAmountInfo', () => {
403407 } ) ;
404408 useIsTransactionPayLoadingMock . mockReturnValue ( false ) ;
405409 useTransactionPayQuotesMock . mockReturnValue ( [ ] ) ;
410+ useTransactionPayQuotesLastUpdatedMock . mockReturnValue ( undefined ) ;
406411 useTransactionPayHasSourceAmountMock . mockReturnValue ( false ) ;
407412 useTokenFiatRatesMock . mockReturnValue ( [ 1 , 1 ] ) ;
408413 useTransactionMetadataRequestMock . mockReturnValue ( {
@@ -740,19 +745,30 @@ describe('CustomAmountInfo', () => {
740745 ) . toBeUndefined ( ) ;
741746 } ) ;
742747
743- it ( 'clears local preparation when the amount update resolves ' , async ( ) => {
748+ it ( 'keeps the loading review through the handoff to controller loading ' , async ( ) => {
744749 const { deferred } = arrangePendingPreparation ( ) ;
745750 const view = render ( {
746751 transactionType : TransactionType . moneyAccountDeposit ,
747752 } ) ;
748753 fireEvent . press ( view . getByTestId ( 'deposit-keyboard-done-button' ) ) ;
749754
750- useIsTransactionPayLoadingMock . mockReturnValue ( true ) ;
751755 await act ( async ( ) => {
752756 deferred . resolve ( ) ;
753757 await deferred . promise ;
754758 } ) ;
755759
760+ expect ( view . getByTestId ( 'bridge-fee-row-skeleton' ) ) . toBeOnTheScreen ( ) ;
761+ expect (
762+ view . getByTestId ( ConfirmationFooterSelectorIDs . CONFIRM_BUTTON ) ,
763+ ) . toBeDisabled ( ) ;
764+
765+ useIsTransactionPayLoadingMock . mockReturnValue ( true ) ;
766+ view . rerender (
767+ createCustomAmountInfo ( {
768+ transactionType : TransactionType . moneyAccountDeposit ,
769+ } ) ,
770+ ) ;
771+
756772 expect ( view . getByTestId ( 'bridge-fee-row-skeleton' ) ) . toBeOnTheScreen ( ) ;
757773
758774 useIsTransactionPayLoadingMock . mockReturnValue ( false ) ;
@@ -768,6 +784,67 @@ describe('CustomAmountInfo', () => {
768784 ) . not . toBeDisabled ( ) ;
769785 } ) ;
770786
787+ it ( 'releases the loading review when a fast quote completes between renders' , async ( ) => {
788+ const { deferred } = arrangePendingPreparation ( ) ;
789+ const view = render ( {
790+ transactionType : TransactionType . moneyAccountDeposit ,
791+ } ) ;
792+ fireEvent . press ( view . getByTestId ( 'deposit-keyboard-done-button' ) ) ;
793+
794+ await act ( async ( ) => {
795+ deferred . resolve ( ) ;
796+ await deferred . promise ;
797+ } ) ;
798+
799+ expect ( view . getByTestId ( 'bridge-fee-row-skeleton' ) ) . toBeOnTheScreen ( ) ;
800+
801+ useTransactionPayQuotesLastUpdatedMock . mockReturnValue ( 123 ) ;
802+ view . rerender (
803+ createCustomAmountInfo ( {
804+ transactionType : TransactionType . moneyAccountDeposit ,
805+ } ) ,
806+ ) ;
807+
808+ expect ( view . getByTestId ( 'bridge-fee-row' ) ) . toBeOnTheScreen ( ) ;
809+ expect (
810+ view . getByTestId ( ConfirmationFooterSelectorIDs . CONFIRM_BUTTON ) ,
811+ ) . not . toBeDisabled ( ) ;
812+ } ) ;
813+
814+ it ( 'ignores stale quote updates while the amount is still committing' , async ( ) => {
815+ useTransactionPayQuotesLastUpdatedMock . mockReturnValue ( 1 ) ;
816+ const { deferred } = arrangePendingPreparation ( ) ;
817+ const view = render ( {
818+ transactionType : TransactionType . moneyAccountDeposit ,
819+ } ) ;
820+ fireEvent . press ( view . getByTestId ( 'deposit-keyboard-done-button' ) ) ;
821+
822+ useTransactionPayQuotesLastUpdatedMock . mockReturnValue ( 2 ) ;
823+ view . rerender (
824+ createCustomAmountInfo ( {
825+ transactionType : TransactionType . moneyAccountDeposit ,
826+ } ) ,
827+ ) ;
828+
829+ expect ( view . getByTestId ( 'bridge-fee-row-skeleton' ) ) . toBeOnTheScreen ( ) ;
830+
831+ await act ( async ( ) => {
832+ deferred . resolve ( ) ;
833+ await deferred . promise ;
834+ } ) ;
835+
836+ expect ( view . getByTestId ( 'bridge-fee-row-skeleton' ) ) . toBeOnTheScreen ( ) ;
837+
838+ useTransactionPayQuotesLastUpdatedMock . mockReturnValue ( 3 ) ;
839+ view . rerender (
840+ createCustomAmountInfo ( {
841+ transactionType : TransactionType . moneyAccountDeposit ,
842+ } ) ,
843+ ) ;
844+
845+ expect ( view . getByTestId ( 'bridge-fee-row' ) ) . toBeOnTheScreen ( ) ;
846+ } ) ;
847+
771848 it ( 'keeps preparation active while the amount update is pending' , async ( ) => {
772849 const { deferred } = arrangePendingPreparation ( ) ;
773850 const view = render ( {
@@ -910,14 +987,21 @@ describe('CustomAmountInfo', () => {
910987 onReject : jest . fn ( ) ,
911988 } ) ;
912989
913- const { getByText } = render ( ) ;
990+ const view = render ( ) ;
914991
915992 await act ( async ( ) => {
916- fireEvent . press ( getByText ( strings ( 'confirm.edit_amount_done' ) ) ) ;
993+ fireEvent . press ( view . getByText ( strings ( 'confirm.edit_amount_done' ) ) ) ;
917994 } ) ;
918995
996+ useIsTransactionPayLoadingMock . mockReturnValue ( true ) ;
997+ view . rerender ( createCustomAmountInfo ( ) ) ;
998+ useIsTransactionPayLoadingMock . mockReturnValue ( false ) ;
999+ view . rerender ( createCustomAmountInfo ( ) ) ;
1000+
9191001 await act ( async ( ) => {
920- fireEvent . press ( getByText ( strings ( 'confirm.deposit_edit_amount_done' ) ) ) ;
1002+ fireEvent . press (
1003+ view . getByText ( strings ( 'confirm.deposit_edit_amount_done' ) ) ,
1004+ ) ;
9211005 } ) ;
9221006
9231007 expect ( setIsConfirmationSubmittingMock ) . toHaveBeenCalledWith ( true ) ;
@@ -1184,22 +1268,25 @@ describe('CustomAmountInfo', () => {
11841268 } ) ;
11851269
11861270 describe ( 'showPaymentDetails' , ( ) => {
1187- async function pressDone (
1188- getByText : ReturnType < typeof render > [ 'getByText' ] ,
1189- ) {
1271+ async function pressDone ( view : ReturnType < typeof render > ) {
11901272 await act ( async ( ) => {
1191- fireEvent . press ( getByText ( strings ( 'confirm.edit_amount_done' ) ) ) ;
1273+ fireEvent . press ( view . getByText ( strings ( 'confirm.edit_amount_done' ) ) ) ;
11921274 } ) ;
1275+
1276+ useIsTransactionPayLoadingMock . mockReturnValue ( true ) ;
1277+ view . rerender ( createCustomAmountInfo ( ) ) ;
1278+ useIsTransactionPayLoadingMock . mockReturnValue ( false ) ;
1279+ view . rerender ( createCustomAmountInfo ( ) ) ;
11931280 }
11941281
11951282 it ( 'shows fee rows for same-chain payment without quotes' , async ( ) => {
11961283 useTransactionPayHasSourceAmountMock . mockReturnValue ( false ) ;
11971284 useTransactionPayQuotesMock . mockReturnValue ( [ ] ) ;
11981285
1199- const { getByText , getByTestId } = render ( ) ;
1200- await pressDone ( getByText ) ;
1286+ const view = render ( ) ;
1287+ await pressDone ( view ) ;
12011288
1202- expect ( getByTestId ( 'bridge-fee-row' ) ) . toBeOnTheScreen ( ) ;
1289+ expect ( view . getByTestId ( 'bridge-fee-row' ) ) . toBeOnTheScreen ( ) ;
12031290 } ) ;
12041291
12051292 it ( 'hides fee rows when no-quotes alert is present' , async ( ) => {
@@ -1217,20 +1304,20 @@ describe('CustomAmountInfo', () => {
12171304 fieldAlerts : [ ] as Alert [ ] ,
12181305 } as AlertsContextParams ) ;
12191306
1220- const { getByText , queryByTestId } = render ( ) ;
1221- await pressDone ( getByText ) ;
1307+ const view = render ( ) ;
1308+ await pressDone ( view ) ;
12221309
1223- expect ( queryByTestId ( 'bridge-fee-row' ) ) . toBeNull ( ) ;
1310+ expect ( view . queryByTestId ( 'bridge-fee-row' ) ) . toBeNull ( ) ;
12241311 } ) ;
12251312
12261313 it ( 'shows fee rows when quotes exist regardless of source amount' , async ( ) => {
12271314 useTransactionPayHasSourceAmountMock . mockReturnValue ( true ) ;
12281315 useTransactionPayQuotesMock . mockReturnValue ( [ { } as never ] ) ;
12291316
1230- const { getByText , getByTestId } = render ( ) ;
1231- await pressDone ( getByText ) ;
1317+ const view = render ( ) ;
1318+ await pressDone ( view ) ;
12321319
1233- expect ( getByTestId ( 'bridge-fee-row' ) ) . toBeOnTheScreen ( ) ;
1320+ expect ( view . getByTestId ( 'bridge-fee-row' ) ) . toBeOnTheScreen ( ) ;
12341321 } ) ;
12351322 } ) ;
12361323
0 commit comments