@@ -2,7 +2,7 @@ import { act, fireEvent, render, waitFor } from '@testing-library/react-native';
22import {
33 PERPS_EVENT_PROPERTY ,
44 PERPS_EVENT_VALUE ,
5- ORDER_SLIPPAGE_CONFIG
5+ ORDER_SLIPPAGE_CONFIG ,
66} from '@metamask/perps-controller' ;
77import { MetaMetricsEvents } from '../../../../../core/Analytics' ;
88import React from 'react' ;
@@ -1998,7 +1998,6 @@ describe('PerpsClosePositionView', () => {
19981998 // HyperLiquid's marginUsed already includes PnL
19991999 // receivedAmount = marginUsed - fees = 1450 - 45 = 1405
20002000 // realizedPnl = unrealizedPnl = 150 (from defaultPerpsPositionMock)
2001- < < << << < HEAD
20022001 expect ( handleClosePosition ) . toHaveBeenCalledWith (
20032002 expect . objectContaining ( {
20042003 position : defaultPerpsPositionMock ,
@@ -2022,167 +2021,11 @@ describe('PerpsClosePositionView', () => {
20222021 slippage : {
20232022 usdAmount : undefined , // undefined for full close to bypass $10 minimum validation
20242023 priceAtCalculation : 3000 , // effectivePrice: currentPrice for market orders
2025- maxSlippageBps : 300 , // maxSlippageBps: 3% slippage tolerance (300 basis points) - conservative default
2024+ maxSlippageBps : ORDER_SLIPPAGE_CONFIG . DefaultMarketSlippageBps ,
20262025 } ,
20272026 } ) ,
20282027 ) ;
20292028 } ) ;
2030-
2031- it ( 'validates limit order requires price before confirmation' , async ( ) => {
2032- // Test the early return in handleConfirm when limit has no price
2033- const handleClosePosition = jest . fn ( ) ;
2034- usePerpsClosePositionMock . mockReturnValue ( {
2035- handleClosePosition,
2036- isClosing : false ,
2037- } ) ;
2038-
2039- // Create a test component that simulates limit order without price
2040- const TestComponent = ( ) => {
2041- const [ orderType ] = React . useState < 'market' | 'limit' > ( 'limit' ) ;
2042- const [ limitPrice ] = React . useState ( '' ) ; // No price set
2043-
2044- return (
2045- < TouchableOpacity
2046- testID = "test-confirm-no-price"
2047- onPress = { async ( ) => {
2048- // This simulates the handleConfirm logic that returns early
2049- if ( orderType === 'limit' && ! limitPrice ) {
2050- return ; // Early return - should not call handleClosePosition
2051- }
2052- await handleClosePosition ( ) ;
2053- } }
2054- >
2055- < Text > Confirm</ Text >
2056- </ TouchableOpacity >
2057- ) ;
2058- } ;
2059-
2060- const { getByTestId } = render ( < TestComponent /> ) ;
2061-
2062- // Act - Try to confirm without limit price
2063- fireEvent . press ( getByTestId ( 'test-confirm-no-price' ) ) ;
2064-
2065- // Assert - Should NOT call handleClosePosition due to early return
2066- await waitFor ( ( ) => {
2067- expect ( handleClosePosition ) . not . toHaveBeenCalled ( ) ;
2068- } ) ;
2069- } ) ;
2070-
2071- it ( 'handles limit order confirmation with price validation' , async ( ) => {
2072- // Arrange
2073- const handleClosePosition = jest . fn ( ) . mockResolvedValue ( undefined ) ;
2074- usePerpsClosePositionMock . mockReturnValue ( {
2075- handleClosePosition,
2076- isClosing : false ,
2077- } ) ;
2078-
2079- // Mock a component state with limit order and price
2080- const TestComponent = ( ) => {
2081- const [ orderType ] = React . useState < 'market' | 'limit' > ( 'limit' ) ;
2082- const [ limitPrice ] = React . useState ( '50000' ) ;
2083-
2084- return (
2085- < View >
2086- < TouchableOpacity
2087- testID = "test-confirm"
2088- onPress = { async ( ) => {
2089- // Simulate the handleConfirm logic for limit orders
2090- if ( orderType === 'limit' && ! limitPrice ) {
2091- return ; // Should not proceed without price
2092- }
2093- await handleClosePosition ( {
2094- position : defaultPerpsPositionMock ,
2095- size : '' ,
2096- orderType,
2097- limitPrice : orderType === 'limit' ? limitPrice : undefined ,
2098- trackingData : {
2099- totalFee : 45 ,
2100- marketPrice : 3000 ,
2101- receivedAmount : 1405 ,
2102- realizedPnl : 150 ,
2103- metamaskFeeRate : 0 ,
2104- feeDiscountPercentage : undefined ,
2105- metamaskFee : 0 ,
2106- estimatedPoints : undefined ,
2107- inputMethod : 'default' ,
2108- } ,
2109- marketPrice : '3000.00' ,
2110- slippage : {
2111- usdAmount : '4500' ,
2112- priceAtCalculation : 3000 ,
2113- maxSlippageBps : 100 ,
2114- } ,
2115- } ) ;
2116- } }
2117- >
2118- < Text > Confirm</ Text >
2119- </ TouchableOpacity >
2120- </ View >
2121- ) ;
2122- } ;
2123-
2124- const { getByTestId } = render ( < TestComponent /> ) ;
2125-
2126- // Act - Press confirm for limit order
2127- fireEvent . press ( getByTestId ( 'test-confirm' ) ) ;
2128-
2129- // Assert - Should call with limit price and specific calculated values
2130- await waitFor ( ( ) => {
2131- expect ( handleClosePosition ) . toHaveBeenCalledWith (
2132- expect . objectContaining ( {
2133- position : defaultPerpsPositionMock ,
2134- size : '' ,
2135- orderType : 'limit' ,
2136- limitPrice : '50000' ,
2137- trackingData : expect . objectContaining ( {
2138- totalFee : 45 ,
2139- marketPrice : 3000 ,
2140- receivedAmount : 1405 ,
2141- realizedPnl : 150 ,
2142- metamaskFeeRate : 0 ,
2143- metamaskFee : 0 ,
2144- feeDiscountPercentage : undefined ,
2145- estimatedPoints : undefined ,
2146- inputMethod : 'default' ,
2147- } ) ,
2148- marketPrice : '3000.00' ,
2149- slippage : {
2150- usdAmount : '4500' ,
2151- priceAtCalculation : 3000 ,
2152- maxSlippageBps : 100 ,
2153- } ,
2154- } ) ,
2155- ) ;
2156- } ) ;
2157- } ) ;
2158- = === ===
2159- expect ( handleClosePosition ) . toHaveBeenCalledWith ( {
2160- position : defaultPerpsPositionMock ,
2161- size : '' ,
2162- orderType : 'market' ,
2163- limitPrice : undefined ,
2164- trackingData : {
2165- totalFee : 45 ,
2166- marketPrice : 3000 ,
2167- receivedAmount : 1405 ,
2168- realizedPnl : 150 ,
2169- metamaskFeeRate : 0 ,
2170- metamaskFee : 0 ,
2171- feeDiscountPercentage : undefined ,
2172- estimatedPoints : undefined ,
2173- inputMethod : 'default' ,
2174- } ,
2175- marketPrice : '3000.00' ,
2176- // Slippage parameters added in USD-as-source-of-truth refactor
2177- // For full closes (100%), usdAmount is undefined to bypass $10 minimum validation
2178- slippage : {
2179- usdAmount : undefined , // undefined for full close to bypass $10 minimum validation
2180- priceAtCalculation : 3000 , // effectivePrice: currentPrice for market orders
2181- maxSlippageBps : ORDER_SLIPPAGE_CONFIG . DefaultMarketSlippageBps ,
2182- } ,
2183- } ) ;
2184- } ) ;
2185- > >>> >>> main
21862029 } ) ;
21872030
21882031 describe ( 'Keypad Interaction - Real Component' , ( ) => {
@@ -3214,7 +3057,6 @@ describe('PerpsClosePositionView', () => {
32143057 } ) ;
32153058 } ) ;
32163059
3217- < < << << < HEAD
32183060 describe ( 'abandon order tracking' , ( ) => {
32193061 const abandonInteraction = [
32203062 MetaMetricsEvents . PERPS_UI_INTERACTION ,
@@ -3290,7 +3132,88 @@ describe('PerpsClosePositionView', () => {
32903132 const fire = setupAbandonNav ( [ { key : 'close' } ] ) ;
32913133 const { getByTestId } = renderWithProvider ( < PerpsClosePositionView /> ) ;
32923134
3293- = === ===
3135+ fireEvent . press (
3136+ getByTestId (
3137+ PerpsClosePositionViewSelectorsIDs . CLOSE_POSITION_CONFIRM_BUTTON ,
3138+ ) ,
3139+ ) ;
3140+ act ( ( ) => fire ( 'beforeRemove' ) ) ;
3141+
3142+ expect ( defaultPerpsEventTrackingMock . track ) . not . toHaveBeenCalledWith (
3143+ ...abandonInteraction ,
3144+ ) ;
3145+ } ) ;
3146+ } ) ;
3147+
3148+ describe ( 'position_close entry action (button_clicked)' , ( ) => {
3149+ const expectScreenViewed = ( expected : Record < string , unknown > ) =>
3150+ expect ( defaultPerpsEventTrackingMock . track ) . toHaveBeenCalledWith (
3151+ MetaMetricsEvents . PERPS_SCREEN_VIEWED ,
3152+ expect . objectContaining ( {
3153+ [ PERPS_EVENT_PROPERTY . SCREEN_TYPE ] :
3154+ PERPS_EVENT_VALUE . SCREEN_TYPE . POSITION_CLOSE ,
3155+ ...expected ,
3156+ } ) ,
3157+ ) ;
3158+
3159+ it ( 'reports button_clicked=reduce_exposure and source=position_screen when opened via the reduce-exposure entry' , ( ) => {
3160+ useRouteMock . mockReturnValue ( {
3161+ params : {
3162+ position : defaultPerpsPositionMock ,
3163+ source : PERPS_EVENT_VALUE . SOURCE . POSITION_SCREEN ,
3164+ buttonClicked : PERPS_EVENT_VALUE . BUTTON_CLICKED . REDUCE_EXPOSURE ,
3165+ buttonLocation : PERPS_EVENT_VALUE . BUTTON_LOCATION . SCREEN ,
3166+ } ,
3167+ } ) ;
3168+
3169+ renderWithProvider ( < PerpsClosePositionView /> ) ;
3170+
3171+ expectScreenViewed ( {
3172+ [ PERPS_EVENT_PROPERTY . SOURCE ] : PERPS_EVENT_VALUE . SOURCE . POSITION_SCREEN ,
3173+ [ PERPS_EVENT_PROPERTY . BUTTON_CLICKED ] :
3174+ PERPS_EVENT_VALUE . BUTTON_CLICKED . REDUCE_EXPOSURE ,
3175+ [ PERPS_EVENT_PROPERTY . BUTTON_LOCATION ] :
3176+ PERPS_EVENT_VALUE . BUTTON_LOCATION . SCREEN ,
3177+ } ) ;
3178+ } ) ;
3179+
3180+ it ( 'reports button_clicked=close and source=order_book when opened via the order-book close entry' , ( ) => {
3181+ useRouteMock . mockReturnValue ( {
3182+ params : {
3183+ position : defaultPerpsPositionMock ,
3184+ source : PERPS_EVENT_VALUE . SOURCE . ORDER_BOOK ,
3185+ buttonClicked : PERPS_EVENT_VALUE . BUTTON_CLICKED . CLOSE ,
3186+ buttonLocation : PERPS_EVENT_VALUE . BUTTON_LOCATION . ORDER_BOOK ,
3187+ } ,
3188+ } ) ;
3189+
3190+ renderWithProvider ( < PerpsClosePositionView /> ) ;
3191+
3192+ expectScreenViewed ( {
3193+ [ PERPS_EVENT_PROPERTY . SOURCE ] : PERPS_EVENT_VALUE . SOURCE . ORDER_BOOK ,
3194+ [ PERPS_EVENT_PROPERTY . BUTTON_CLICKED ] :
3195+ PERPS_EVENT_VALUE . BUTTON_CLICKED . CLOSE ,
3196+ [ PERPS_EVENT_PROPERTY . BUTTON_LOCATION ] :
3197+ PERPS_EVENT_VALUE . BUTTON_LOCATION . ORDER_BOOK ,
3198+ } ) ;
3199+ } ) ;
3200+
3201+ it ( 'defaults button_clicked=close and source=perp_asset_screen when no entry action is provided' , ( ) => {
3202+ useRouteMock . mockReturnValue ( {
3203+ params : { position : defaultPerpsPositionMock } ,
3204+ } ) ;
3205+
3206+ renderWithProvider ( < PerpsClosePositionView /> ) ;
3207+
3208+ expectScreenViewed ( {
3209+ [ PERPS_EVENT_PROPERTY . SOURCE ] :
3210+ PERPS_EVENT_VALUE . SOURCE . PERP_ASSET_SCREEN ,
3211+ [ PERPS_EVENT_PROPERTY . BUTTON_CLICKED ] :
3212+ PERPS_EVENT_VALUE . BUTTON_CLICKED . CLOSE ,
3213+ } ) ;
3214+ } ) ;
3215+ } ) ;
3216+
32943217 describe ( 'Order type selector (feature flag)' , ( ) => {
32953218 const selectPerpsClosePositionLimitOrderEnabledFlagMock = jest . mocked (
32963219 jest . requireMock ( '../../selectors/featureFlags' )
@@ -3421,88 +3344,11 @@ describe('PerpsClosePositionView', () => {
34213344 ) . toBeNull ( ) ;
34223345
34233346 // Act - confirm the close
3424- > >>> >>> main
34253347 fireEvent . press (
34263348 getByTestId (
34273349 PerpsClosePositionViewSelectorsIDs . CLOSE_POSITION_CONFIRM_BUTTON ,
34283350 ) ,
34293351 ) ;
3430- < < << << < HEAD
3431- act ( ( ) => fire ( 'beforeRemove' ) ) ;
3432-
3433- expect ( defaultPerpsEventTrackingMock . track ) . not . toHaveBeenCalledWith (
3434- ...abandonInteraction ,
3435- ) ;
3436- } ) ;
3437- } ) ;
3438-
3439- describe ( 'position_close entry action (button_clicked)' , ( ) => {
3440- const expectScreenViewed = ( expected : Record < string , unknown > ) =>
3441- expect ( defaultPerpsEventTrackingMock . track ) . toHaveBeenCalledWith (
3442- MetaMetricsEvents . PERPS_SCREEN_VIEWED ,
3443- expect . objectContaining ( {
3444- [ PERPS_EVENT_PROPERTY . SCREEN_TYPE ] :
3445- PERPS_EVENT_VALUE . SCREEN_TYPE . POSITION_CLOSE ,
3446- ...expected ,
3447- } ) ,
3448- ) ;
3449-
3450- it ( 'reports button_clicked=reduce_exposure and source=position_screen when opened via the reduce-exposure entry' , ( ) => {
3451- useRouteMock . mockReturnValue ( {
3452- params : {
3453- position : defaultPerpsPositionMock ,
3454- source : PERPS_EVENT_VALUE . SOURCE . POSITION_SCREEN ,
3455- buttonClicked : PERPS_EVENT_VALUE . BUTTON_CLICKED . REDUCE_EXPOSURE ,
3456- buttonLocation : PERPS_EVENT_VALUE . BUTTON_LOCATION . SCREEN ,
3457- } ,
3458- } ) ;
3459-
3460- renderWithProvider ( < PerpsClosePositionView /> ) ;
3461-
3462- expectScreenViewed ( {
3463- [ PERPS_EVENT_PROPERTY . SOURCE ] : PERPS_EVENT_VALUE . SOURCE . POSITION_SCREEN ,
3464- [ PERPS_EVENT_PROPERTY . BUTTON_CLICKED ] :
3465- PERPS_EVENT_VALUE . BUTTON_CLICKED . REDUCE_EXPOSURE ,
3466- [ PERPS_EVENT_PROPERTY . BUTTON_LOCATION ] :
3467- PERPS_EVENT_VALUE . BUTTON_LOCATION . SCREEN ,
3468- } ) ;
3469- } ) ;
3470-
3471- it ( 'reports button_clicked=close and source=order_book when opened via the order-book close entry' , ( ) => {
3472- useRouteMock . mockReturnValue ( {
3473- params : {
3474- position : defaultPerpsPositionMock ,
3475- source : PERPS_EVENT_VALUE . SOURCE . ORDER_BOOK ,
3476- buttonClicked : PERPS_EVENT_VALUE . BUTTON_CLICKED . CLOSE ,
3477- buttonLocation : PERPS_EVENT_VALUE . BUTTON_LOCATION . ORDER_BOOK ,
3478- } ,
3479- } ) ;
3480-
3481- renderWithProvider ( < PerpsClosePositionView /> ) ;
3482-
3483- expectScreenViewed ( {
3484- [ PERPS_EVENT_PROPERTY . SOURCE ] : PERPS_EVENT_VALUE . SOURCE . ORDER_BOOK ,
3485- [ PERPS_EVENT_PROPERTY . BUTTON_CLICKED ] :
3486- PERPS_EVENT_VALUE . BUTTON_CLICKED . CLOSE ,
3487- [ PERPS_EVENT_PROPERTY . BUTTON_LOCATION ] :
3488- PERPS_EVENT_VALUE . BUTTON_LOCATION . ORDER_BOOK ,
3489- } ) ;
3490- } ) ;
3491-
3492- it ( 'defaults button_clicked=close and source=perp_asset_screen when no entry action is provided' , ( ) => {
3493- useRouteMock . mockReturnValue ( {
3494- params : { position : defaultPerpsPositionMock } ,
3495- } ) ;
3496-
3497- renderWithProvider ( < PerpsClosePositionView /> ) ;
3498-
3499- expectScreenViewed ( {
3500- [ PERPS_EVENT_PROPERTY . SOURCE ] :
3501- PERPS_EVENT_VALUE . SOURCE . PERP_ASSET_SCREEN ,
3502- [ PERPS_EVENT_PROPERTY . BUTTON_CLICKED ] :
3503- PERPS_EVENT_VALUE . BUTTON_CLICKED . CLOSE ,
3504- } ) ;
3505- = === ===
35063352
35073353 // Assert - a market close is submitted, never a limit order behind the flag
35083354 await waitFor ( ( ) => {
@@ -3551,7 +3397,6 @@ describe('PerpsClosePositionView', () => {
35513397 expect (
35523398 queryByTestId ( PerpsLimitPriceBottomSheetSelectorsIDs . CONFIRM_BUTTON ) ,
35533399 ) . toBeNull ( ) ;
3554- > >>> >>> main
35553400 } ) ;
35563401 } ) ;
35573402} ) ;
0 commit comments