Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import MarketInsightsTrendSourcesBottomSheet from '../../components/MarketInsigh
import { MarketInsightsSelectorsIDs } from '../../MarketInsights.testIds';
import { isSafeUrl } from '../../utils/marketInsightsFormatting';
import { useAnalytics } from '../../../../hooks/useAnalytics/useAnalytics';
import PerpsDirectionButton from '../../../Perps/components/PerpsDirectionButton';
import type {
MarketInsightsTweet,
MarketInsightsTrend,
Expand Down Expand Up @@ -886,24 +887,24 @@ const MarketInsightsView: React.FC = () => {
twClassName={`border-t border-muted bg-default px-4 pt-4 pb-[${insets.bottom + 8}px]`}
>
<Box flexDirection={BoxFlexDirection.Row} gap={3}>
<Button
variant={ButtonVariant.Primary}
<PerpsDirectionButton
direction="long"
size={ButtonSize.Lg}
twClassName="flex-1"
onPress={() => handlePerpsDirectionPress('long')}
testID={MarketInsightsSelectorsIDs.LONG_BUTTON}
>
{strings('perps.market.long')}
</Button>
<Button
variant={ButtonVariant.Primary}
</PerpsDirectionButton>
<PerpsDirectionButton
direction="short"
size={ButtonSize.Lg}
twClassName="flex-1"
onPress={() => handlePerpsDirectionPress('short')}
testID={MarketInsightsSelectorsIDs.SHORT_BUTTON}
>
{strings('perps.market.short')}
</Button>
</PerpsDirectionButton>
</Box>
<Box twClassName="pt-3" alignItems={BoxAlignItems.Center}>
<Text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,7 @@ import {
} from '../../selectors/perpsController';
import { useComplianceGate } from '../../../Compliance';
import { selectSelectedInternalAccountAddress } from '../../../../../selectors/accountsController';
import { useABTest } from '../../../../../hooks/useABTest';
import {
BUTTON_COLOR_VARIANTS,
PERPS_BUTTON_COLOR_AB_TEST_KEY,
} from '../../abTestConfig';
import PerpsDirectionButton from '../../components/PerpsDirectionButton';
import { getMarketHoursStatus, isEquityAsset } from '../../utils/marketHours';
import { toPerpsEntryAttribution } from '../../utils/perpsAnalyticsAttribution';
import { refreshLightweightChartViewport } from '../../utils/refreshLightweightChartViewport';
Expand Down Expand Up @@ -629,15 +625,6 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = ({
return 0;
}, [focusedPrice, livePrices, market?.symbol]);

// A/B Testing: Button color test (TAT-1937)
const {
variantName: buttonColorVariant,
isActive: isButtonColorTestEnabled,
} = useABTest(PERPS_BUTTON_COLOR_AB_TEST_KEY, BUTTON_COLOR_VARIANTS, {
experimentName: 'Long/Short Button Color Test',
variationNames: { control: 'White/White', colors: 'Green/Red' },
});

usePerpsConnection();

// Check if market is at open interest cap
Expand Down Expand Up @@ -937,10 +924,6 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = ({
isPerpsInsightsEnabled && Boolean(perpsInsightsReport),
[PERPS_EVENT_PROPERTY.OUTAGE_BANNER_SHOWN]:
isServiceInterruptionBannerEnabled,
// A/B Test context (TAT-1937) - for baseline exposure tracking
...(isButtonColorTestEnabled && {
[PERPS_EVENT_PROPERTY.AB_TEST_BUTTON_COLOR]: buttonColorVariant,
}),
}),
[
market?.symbol,
Expand All @@ -953,8 +936,6 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = ({
isPerpsInsightsEnabled,
perpsInsightsReport,
isServiceInterruptionBannerEnabled,
isButtonColorTestEnabled,
buttonColorVariant,
],
);

Expand Down Expand Up @@ -1206,19 +1187,6 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = ({
return;
}

// Track AB test on button press (TAT-1937)
if (isButtonColorTestEnabled) {
track(MetaMetricsEvents.PERPS_UI_INTERACTION, {
[PERPS_EVENT_PROPERTY.INTERACTION_TYPE]:
PERPS_EVENT_VALUE.INTERACTION_TYPE.TAP,
[PERPS_EVENT_PROPERTY.ASSET]: market.symbol,
[PERPS_EVENT_PROPERTY.DIRECTION]:
direction === 'long'
? PERPS_EVENT_VALUE.DIRECTION.LONG
: PERPS_EVENT_VALUE.DIRECTION.SHORT,
});
}

navigateToOrder({
direction,
asset: market.symbol,
Expand Down Expand Up @@ -1246,7 +1214,6 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = ({
market?.symbol,
market?.providerId,
marketData,
isButtonColorTestEnabled,
chartLibrary,
useBottomSheet,
],
Expand Down Expand Up @@ -2231,53 +2198,27 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = ({
{/* Show Long/Short buttons when no position exists and user can trade */}
{shouldShowLongShortButtonsOnly && (
<View style={styles.actionsContainer} accessible={false}>
{buttonColorVariant === 'colors' ? (
<ButtonSemantic
severity={ButtonSemanticSeverity.Success}
onPress={handleLongPress}
size={ButtonSizeRNDesignSystem.Lg}
isDisabled={isAtOICap}
style={styles.actionButtonWrapper}
testID={PerpsMarketDetailsViewSelectorsIDs.LONG_BUTTON}
>
{strings('perps.market.long')}
</ButtonSemantic>
) : (
<DSButton
variant={ButtonVariant.Primary}
size={ButtonSizeRNDesignSystem.Lg}
onPress={handleLongPress}
isDisabled={isAtOICap}
style={styles.actionButtonWrapper}
testID={PerpsMarketDetailsViewSelectorsIDs.LONG_BUTTON}
>
{strings('perps.market.long')}
</DSButton>
)}
<PerpsDirectionButton
direction="long"
onPress={handleLongPress}
size={ButtonSizeRNDesignSystem.Lg}
isDisabled={isAtOICap}
style={styles.actionButtonWrapper}
testID={PerpsMarketDetailsViewSelectorsIDs.LONG_BUTTON}
>
{strings('perps.market.long')}
</PerpsDirectionButton>

{buttonColorVariant === 'colors' ? (
<ButtonSemantic
severity={ButtonSemanticSeverity.Danger}
onPress={handleShortPress}
size={ButtonSizeRNDesignSystem.Lg}
isDisabled={isAtOICap}
style={styles.actionButtonWrapper}
testID={PerpsMarketDetailsViewSelectorsIDs.SHORT_BUTTON}
>
{strings('perps.market.short')}
</ButtonSemantic>
) : (
<DSButton
variant={ButtonVariant.Primary}
size={ButtonSizeRNDesignSystem.Lg}
onPress={handleShortPress}
isDisabled={isAtOICap}
style={styles.actionButtonWrapper}
testID={PerpsMarketDetailsViewSelectorsIDs.SHORT_BUTTON}
>
{strings('perps.market.short')}
</DSButton>
)}
<PerpsDirectionButton
direction="short"
onPress={handleShortPress}
size={ButtonSizeRNDesignSystem.Lg}
isDisabled={isAtOICap}
style={styles.actionButtonWrapper}
testID={PerpsMarketDetailsViewSelectorsIDs.SHORT_BUTTON}
>
{strings('perps.market.short')}
</PerpsDirectionButton>
</View>
)}
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,16 +234,6 @@ jest.mock('../../hooks/usePerpsEventTracking', () => ({
}),
}));

// Mock useABTest to return default control variant (controllable per-test)
const mockUseABTest = jest.fn(() => ({
variantName: 'control',
variant: { long: 'white', short: 'white' },
isActive: false,
}));
jest.mock('../../../../../hooks/useABTest', () => ({
useABTest: () => mockUseABTest(),
}));

// Mock components
jest.mock('../../components/PerpsOrderBookTable', () => {
const { View } = jest.requireActual('react-native');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import {
ButtonBaseSize,
ButtonIcon,
ButtonIconSize,
ButtonSemantic,
ButtonSemanticSeverity,
FilterButton,
HeaderSubpage,
IconColor,
Expand Down Expand Up @@ -79,11 +77,7 @@ import {
} from '../../selectors/perpsController';
import { useComplianceGate } from '../../../Compliance';
import { selectSelectedInternalAccountAddress } from '../../../../../selectors/accountsController';
import { useABTest } from '../../../../../hooks/useABTest';
import {
BUTTON_COLOR_VARIANTS,
PERPS_BUTTON_COLOR_AB_TEST_KEY,
} from '../../abTestConfig';
import PerpsDirectionButton from '../../components/PerpsDirectionButton';
import {
formatPerpsFiat,
PRICE_RANGES_UNIVERSAL,
Expand Down Expand Up @@ -119,16 +113,6 @@ const PerpsOrderBookView: React.FC<PerpsOrderBookViewProps> = ({
const insets = useSafeAreaInsets();
const activeProvider = useSelector(selectPerpsProvider);

// A/B Testing: Button color test (TAT-1937)
const { variantName: buttonColorVariant } = useABTest(
PERPS_BUTTON_COLOR_AB_TEST_KEY,
BUTTON_COLOR_VARIANTS,
{
experimentName: 'Long/Short Button Color Test',
variationNames: { control: 'White/White', colors: 'Green/Red' },
},
);

// Geo-restriction eligibility check
const isEligible = useSelector(selectPerpsEligibility);
const [isEligibilityModalVisible, setIsEligibilityModalVisible] =
Expand Down Expand Up @@ -776,49 +760,25 @@ const PerpsOrderBookView: React.FC<PerpsOrderBookViewProps> = ({
</View>
) : (
<View style={styles.actionsContainer} accessible={false}>
{buttonColorVariant === 'colors' ? (
<ButtonSemantic
severity={ButtonSemanticSeverity.Success}
onPress={handleLongPress}
size={ButtonSize.Lg}
style={styles.actionButtonWrapper}
testID={PerpsOrderBookViewSelectorsIDs.LONG_BUTTON}
>
{strings('perps.market.long')}
</ButtonSemantic>
) : (
<Button
variant={ButtonVariant.Primary}
size={ButtonSize.Lg}
onPress={handleLongPress}
style={styles.actionButtonWrapper}
testID={PerpsOrderBookViewSelectorsIDs.LONG_BUTTON}
>
{strings('perps.market.long')}
</Button>
)}

{buttonColorVariant === 'colors' ? (
<ButtonSemantic
severity={ButtonSemanticSeverity.Danger}
onPress={handleShortPress}
size={ButtonSize.Lg}
style={styles.actionButtonWrapper}
testID={PerpsOrderBookViewSelectorsIDs.SHORT_BUTTON}
>
{strings('perps.market.short')}
</ButtonSemantic>
) : (
<Button
variant={ButtonVariant.Primary}
size={ButtonSize.Lg}
onPress={handleShortPress}
style={styles.actionButtonWrapper}
testID={PerpsOrderBookViewSelectorsIDs.SHORT_BUTTON}
>
{strings('perps.market.short')}
</Button>
)}
<PerpsDirectionButton
direction="long"
onPress={handleLongPress}
size={ButtonSize.Lg}
style={styles.actionButtonWrapper}
testID={PerpsOrderBookViewSelectorsIDs.LONG_BUTTON}
>
{strings('perps.market.long')}
</PerpsDirectionButton>

<PerpsDirectionButton
direction="short"
onPress={handleShortPress}
size={ButtonSize.Lg}
style={styles.actionButtonWrapper}
testID={PerpsOrderBookViewSelectorsIDs.SHORT_BUTTON}
>
{strings('perps.market.short')}
</PerpsDirectionButton>
</View>
)}
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -724,16 +724,6 @@ jest.mock('../../../../../core/Engine', () => ({
},
}));

// Mock useABTest hook (controllable per-test)
const mockUseABTest = jest.fn(() => ({
variantName: 'control',
variant: { long: 'white', short: 'white' },
isActive: false,
}));
jest.mock('../../../../../hooks/useABTest', () => ({
useABTest: () => mockUseABTest(),
}));

// Mock useTooltipModal hook
jest.mock('../../../../hooks/useTooltipModal', () => ({
__esModule: true,
Expand Down Expand Up @@ -1278,11 +1268,6 @@ function applyDefaultHookMocks() {
setPayToken: jest.fn(),
isNative: undefined,
});
mockUseABTest.mockReturnValue({
variantName: 'control',
variant: { long: 'white', short: 'white' },
isActive: false,
});
mockCreateEventBuilder.mockImplementation(() => ({
addProperties: jest.fn().mockReturnThis(),
build: jest.fn().mockReturnValue({}),
Expand Down Expand Up @@ -4312,13 +4297,8 @@ describe('PerpsOrderView', () => {
expect(placeOrderButton.props.accessibilityState?.disabled).toBeTruthy();
});

it('disables control (white) button variant when TP/SL is invalid', async () => {
// Arrange: control (default/white) A/B test variant + invalid TP
mockUseABTest.mockReturnValue({
variantName: 'control',
variant: { long: 'white', short: 'white' },
isActive: true,
});
it('disables the trade button when TP/SL is invalid', async () => {
// Arrange
(usePerpsOrderContext as jest.Mock).mockReturnValue(
orderContextWithTPSL({ direction: 'long', takeProfitPrice: '2000' }),
);
Expand All @@ -4337,16 +4317,15 @@ describe('PerpsOrderView', () => {
// Act
render(<PerpsOrderView />, { wrapper: TestWrapper });

// Assert: warning visible (proves hasInvalidTPSL is true in control/white path)
// Assert
await waitFor(() => {
expect(screen.getByText(/Take profit must be above/)).toBeDefined();
expect(screen.getByText(/Take profit must be above/)).toBeOnTheScreen();
});

// Assert: control (white) button rendered and receives isDisabled prop
const placeOrderButton = await screen.findByTestId(
PerpsOrderViewSelectorsIDs.PLACE_ORDER_BUTTON,
);
expect(placeOrderButton).toBeDefined();
expect(placeOrderButton).toBeOnTheScreen();
});

describe('limit order TP/SL validates against entry price, not market price', () => {
Expand Down
Loading
Loading