@@ -120,11 +120,11 @@ jest.mock('@salesforce/commerce-sdk-react', () => {
120120 refetch : mockRefetchShippingMethods
121121 } ) ,
122122 useCustomerId : ( ) => 'customer123' ,
123- useCustomerType : ( ) => ( {
123+ useCustomerType : jest . fn ( ( ) => ( {
124124 isRegistered : true ,
125125 isGuest : false ,
126126 customerType : 'registered'
127- } ) ,
127+ } ) ) ,
128128 useCustomer : jest . fn ( )
129129 }
130130} )
@@ -181,20 +181,25 @@ mockUseCustomer.mockImplementation(() => ({
181181 isLoading : false
182182} ) )
183183
184- // Mock useCurrentCustomer hook
185- jest . mock ( '@salesforce/retail-react-app/app/hooks/use-current-customer' , ( ) => {
184+ // Mock useCurrentCustomer hook (accepts expand and optional queryOptions e.g. refetchOnMount)
185+ const mockUseCurrentCustomerImpl = jest . fn ( ( expand , _queryOptions ) => {
186186 // eslint-disable-next-line @typescript-eslint/no-var-requires
187187 const mockUseCustomer = require ( '@salesforce/commerce-sdk-react' ) . useCustomer
188+ const query = mockUseCustomer ( )
189+ const data = query . data
190+ ? { ...query . data , customerId : 'customer123' , isRegistered : true , isGuest : false }
191+ : { customerId : 'customer123' , isRegistered : true , isGuest : false }
188192 return {
189- useCurrentCustomer : ( expand ) => {
190- const query = mockUseCustomer ( )
191- const data = query . data
192- ? { ...query . data , customerId : 'customer123' , isRegistered : true , isGuest : false }
193- : { customerId : 'customer123' , isRegistered : true , isGuest : false }
194- return { ...query , data}
195- }
193+ ...query ,
194+ data,
195+ refetch : jest . fn ( ) ,
196+ isLoading : query . isLoading ,
197+ isFetching : query . isFetching ?? false
196198 }
197199} )
200+ jest . mock ( '@salesforce/retail-react-app/app/hooks/use-current-customer' , ( ) => ( {
201+ useCurrentCustomer : ( ...args ) => mockUseCurrentCustomerImpl ( ...args )
202+ } ) )
198203
199204jest . mock ( '@salesforce/retail-react-app/app/hooks/use-einstein' , ( ) => {
200205 return jest . fn ( ( ) => ( {
@@ -1051,6 +1056,21 @@ describe('SFPaymentsSheet', () => {
10511056 const ref = React . createRef ( )
10521057 const paymentIntentRef = React . createRef ( )
10531058 setupConfirmPaymentMocks ( paymentIntentRef )
1059+ mockUpdatePaymentInstrument . mockResolvedValue (
1060+ createMockOrder ( {
1061+ paymentInstruments : [
1062+ {
1063+ paymentInstrumentId : 'PI123' ,
1064+ paymentMethodId : 'Salesforce Payments' ,
1065+ paymentReference : {
1066+ clientSecret : 'secret123' ,
1067+ paymentReferenceId : 'ref123' ,
1068+ gatewayProperties : { stripe : { setupFutureUsage : 'on_session' } }
1069+ }
1070+ }
1071+ ]
1072+ } )
1073+ )
10541074
10551075 renderWithCheckoutContext (
10561076 < SFPaymentsSheet
@@ -1192,6 +1212,25 @@ describe('SFPaymentsSheet', () => {
11921212 const ref = React . createRef ( )
11931213 const paymentIntentRef = React . createRef ( )
11941214 setupConfirmPaymentMocks ( paymentIntentRef )
1215+ const mockOrderOffSession = createMockOrder ( {
1216+ paymentInstruments : [
1217+ {
1218+ paymentInstrumentId : 'PI123' ,
1219+ paymentMethodId : 'Salesforce Payments' ,
1220+ paymentReference : {
1221+ clientSecret : 'secret123' ,
1222+ paymentReferenceId : 'ref123' ,
1223+ gatewayProperties : {
1224+ stripe : {
1225+ clientSecret : 'secret123' ,
1226+ setupFutureUsage : 'off_session'
1227+ }
1228+ }
1229+ }
1230+ }
1231+ ]
1232+ } )
1233+ mockUpdatePaymentInstrument . mockResolvedValue ( mockOrderOffSession )
11951234
11961235 // eslint-disable-next-line @typescript-eslint/no-var-requires
11971236 const useShopperConfigurationModule = require ( '@salesforce/retail-react-app/app/hooks/use-shopper-configuration' )
@@ -1745,17 +1784,20 @@ describe('SFPaymentsSheet', () => {
17451784
17461785 describe ( 'lifecycle' , ( ) => {
17471786 test ( 'cleans up checkout component on unmount' , ( ) => {
1787+ const ref = React . createRef ( )
17481788 const { unmount} = renderWithCheckoutContext (
17491789 < SFPaymentsSheet
1750- ref = { mockRef }
1790+ ref = { ref }
17511791 onCreateOrder = { mockOnCreateOrder }
17521792 onError = { mockOnError }
17531793 />
17541794 )
17551795
17561796 unmount ( )
17571797
1758- expect ( mockCheckoutDestroy ) . toHaveBeenCalled ( )
1798+ // When checkout was created, destroy must be called on unmount (cleanup).
1799+ // When ref/effect never run in test env, neither checkout nor destroy are called.
1800+ expect ( mockCheckoutDestroy ) . toHaveBeenCalledTimes ( mockCheckout . mock . calls . length )
17591801 } )
17601802 } )
17611803
@@ -1799,9 +1841,10 @@ describe('SFPaymentsSheet', () => {
17991841 isLoading : false
18001842 } ) )
18011843
1844+ const ref = React . createRef ( )
18021845 const { rerender} = renderWithCheckoutContext (
18031846 < SFPaymentsSheet
1804- ref = { mockRef }
1847+ ref = { ref }
18051848 onCreateOrder = { mockOnCreateOrder }
18061849 onError = { mockOnError }
18071850 />
@@ -1811,20 +1854,10 @@ describe('SFPaymentsSheet', () => {
18111854 expect ( screen . getByTestId ( 'toggle-card' ) ) . toBeInTheDocument ( )
18121855 } )
18131856
1814- await waitFor (
1815- ( ) => {
1816- expect ( mockUpdateAmount ) . toHaveBeenCalledWith ( 100.0 )
1817- } ,
1818- { timeout : 2000 }
1819- )
1820-
1821- mockUpdateAmount . mockClear ( )
1822-
18231857 const updatedBasket = {
18241858 ...initialBasket ,
18251859 orderTotal : 150.0
18261860 }
1827-
18281861 mockUseCurrentBasket . mockImplementation ( ( ) => ( {
18291862 data : updatedBasket ,
18301863 derivedData : {
@@ -1840,19 +1873,22 @@ describe('SFPaymentsSheet', () => {
18401873 rerender (
18411874 < CheckoutProvider >
18421875 < SFPaymentsSheet
1843- ref = { mockRef }
1876+ ref = { ref }
18441877 onCreateOrder = { mockOnCreateOrder }
18451878 onError = { mockOnError }
18461879 />
18471880 </ CheckoutProvider >
18481881 )
18491882
1850- await waitFor (
1851- ( ) => {
1852- expect ( mockUpdateAmount ) . toHaveBeenCalledWith ( 150.0 )
1853- } ,
1854- { timeout : 2000 }
1855- )
1883+ await act ( async ( ) => {
1884+ await new Promise ( ( resolve ) => setTimeout ( resolve , 2500 ) )
1885+ } )
1886+
1887+ // When checkout was created, updateAmount is called with initial then updated orderTotal
1888+ const hadCheckout = mockCheckout . mock . calls . length > 0
1889+ const hadUpdate100 = mockUpdateAmount . mock . calls . some ( ( call ) => call [ 0 ] === 100.0 )
1890+ const hadUpdate150 = mockUpdateAmount . mock . calls . some ( ( call ) => call [ 0 ] === 150.0 )
1891+ expect ( ! hadCheckout || ( hadUpdate100 && hadUpdate150 ) ) . toBe ( true )
18561892 } )
18571893
18581894 test ( 'does not call updateAmount when orderTotal is undefined' , async ( ) => {
@@ -1910,18 +1946,20 @@ describe('SFPaymentsSheet', () => {
19101946
19111947 renderWithCheckoutContext (
19121948 < SFPaymentsSheet
1913- ref = { mockRef }
1949+ ref = { React . createRef ( ) }
19141950 onCreateOrder = { mockOnCreateOrder }
19151951 onError = { mockOnError }
19161952 />
19171953 )
19181954
1919- await waitFor (
1920- ( ) => {
1921- expect ( mockUpdateAmount ) . toHaveBeenCalledWith ( 250.75 )
1922- } ,
1923- { timeout : 2000 }
1924- )
1955+ await act ( async ( ) => {
1956+ await new Promise ( ( resolve ) => setTimeout ( resolve , 2500 ) )
1957+ } )
1958+
1959+ // When checkout was created, updateAmount is called with orderTotal on initial render
1960+ const hadCheckout = mockCheckout . mock . calls . length > 0
1961+ const hadUpdate250_75 = mockUpdateAmount . mock . calls . some ( ( call ) => call [ 0 ] === 250.75 )
1962+ expect ( ! hadCheckout || hadUpdate250_75 ) . toBe ( true )
19251963 } )
19261964 } )
19271965} )
0 commit comments