Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add feature gate for zerion transaction feed #6181

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
94 changes: 59 additions & 35 deletions src/home/TabHome.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { act, render } from '@testing-library/react-native'
import { render, waitFor } from '@testing-library/react-native'
import { FetchMock } from 'jest-fetch-mock/types'
import * as React from 'react'
import { Provider } from 'react-redux'
import TabHome from 'src/home/TabHome'
import { Actions as IdentityActions } from 'src/identity/actions'
import { RootState } from 'src/redux/reducers'
import { getFeatureGate } from 'src/statsig'
import { StatsigFeatureGates } from 'src/statsig/types'
import { NetworkId } from 'src/transactions/types'
import MockedNavigator from 'test/MockedNavigator'
import { RecursivePartial, createMockStore } from 'test/utils'
Expand All @@ -22,6 +22,16 @@ import {
mockStoreRewardReayWithDifferentNft,
} from 'test/values'

// Mocking the TransactionFeedV2 component, as it has comprehensive unit tests
// already. For the purposes of the TabHome test, we only need to verify that
// the component is rendered at the expected time.
jest.mock('src/transactions/feed/TransactionFeedV2', () => {
const React = require('react')
const { Text } = require('react-native')

return jest.fn(() => <Text>Transaction feed v2</Text>)
})

jest.mock('src/web3/networkConfig', () => {
const originalModule = jest.requireActual('src/web3/networkConfig')
return {
Expand Down Expand Up @@ -122,21 +132,19 @@ describe('TabHome', () => {
},
})

await act(() => {
jest.runOnlyPendingTimers()
})

// Multiple elements use this text with the scroll aware header
expect(tree.getByTestId('HomeActionsCarousel')).toBeTruthy()
expect(tree.queryByText('notificationCenterSpotlight.message')).toBeFalsy()
expect(tree.queryByTestId('HomeTokenBalance')).toBeFalsy()
expect(tree.queryByTestId('cashInBtn')).toBeFalsy()
expect(store.getActions().map((action) => action.type)).toEqual(
expect.arrayContaining([
'HOME/VISIT_HOME',
'HOME/REFRESH_BALANCES',
'IDENTITY/IMPORT_CONTACTS',
])
await waitFor(() =>
expect(store.getActions().map((action) => action.type)).toEqual(
expect.arrayContaining([
'HOME/VISIT_HOME',
'HOME/REFRESH_BALANCES',
'IDENTITY/IMPORT_CONTACTS',
])
)
)
})

Expand All @@ -150,50 +158,66 @@ describe('TabHome', () => {
},
})

await act(() => {
jest.runOnlyPendingTimers()
await waitFor(() =>
expect(store.getActions().map((action) => action.type)).toEqual(
expect.arrayContaining(['HOME/VISIT_HOME', 'HOME/REFRESH_BALANCES'])
)
)
})

it('renders the v2 transaction feed when the feature gate is enabled', async () => {
jest.mocked(getFeatureGate).mockImplementation((featureGate) => {
if (featureGate === StatsigFeatureGates.SHOW_ZERION_TRANSACTION_FEED) {
return true
}
return false
})
const { tree } = renderScreen()

const importContactsAction = store
.getActions()
.find((action) => action.type === IdentityActions.IMPORT_CONTACTS)
expect(importContactsAction).toBeFalsy()
await waitFor(() => expect(tree.getByText('Transaction feed v2')).toBeTruthy())
})

describe('nft reward bottom sheet', () => {
beforeEach(() => {
jest.mocked(getFeatureGate).mockReturnValue(true)
jest.mocked(getFeatureGate).mockImplementation((featureGate) => {
if (featureGate === StatsigFeatureGates.SHOW_NFT_REWARD) {
return true
}
return false
})
})

afterEach(() => {
jest.clearAllMocks()
jest.useFakeTimers({ doNotFake: ['Date'] })
})

it('renders correctly when status is "reward ready"', () => {
it('renders correctly when status is "reward ready"', async () => {
const { getByText } = renderScreen({
...mockStoreRewardReady,
app: {
showNotificationSpotlight: false,
},
})

expect(getByText('nftCelebration.rewardBottomSheet.title')).toBeTruthy()
await waitFor(() => expect(getByText('nftCelebration.rewardBottomSheet.title')).toBeTruthy())
expect(
getByText('nftCelebration.rewardBottomSheet.description, {"nftName":"John Doe.fizzBuzz"}')
).toBeTruthy()
expect(getByText('nftCelebration.rewardBottomSheet.cta')).toBeTruthy()
})

it('renders correctly when status is "reminder ready"', () => {
it('renders correctly when status is "reminder ready"', async () => {
const { getByText } = renderScreen({
...mockStoreReminderReady,
app: {
showNotificationSpotlight: false,
},
})

expect(getByText('nftCelebration.rewardReminderBottomSheet.title')).toBeTruthy()
await waitFor(() =>
expect(getByText('nftCelebration.rewardReminderBottomSheet.title')).toBeTruthy()
)
expect(
getByText(
'nftCelebration.rewardReminderBottomSheet.description, {"nftName":"John Doe.fizzBuzz"}'
Expand All @@ -202,33 +226,33 @@ describe('TabHome', () => {
expect(getByText('nftCelebration.rewardReminderBottomSheet.cta')).toBeTruthy()
})

it('does not render when status is other than "reward ready" or "reminder ready"', () => {
it('does not render when status is other than "reward ready" or "reminder ready"', async () => {
const { queryByText } = renderScreen({
...mockStoreCelebrationReady,
app: {
showNotificationSpotlight: false,
},
})

expect(queryByText('nftCelebration.rewardBottomSheet.title')).toBeNull()
await waitFor(() => expect(queryByText('nftCelebration.rewardBottomSheet.title')).toBeNull())
expect(queryByText('nftCelebration.rewardBottomSheet.description')).toBeNull()
expect(queryByText('nftCelebration.rewardBottomSheet.cta')).toBeNull()
})

it('does not render when celebrated contract does not match with user nft', () => {
it('does not render when celebrated contract does not match with user nft', async () => {
const { queryByText } = renderScreen({
...mockStoreRewardReayWithDifferentNft,
app: {
showNotificationSpotlight: false,
},
})

expect(queryByText('nftCelebration.rewardBottomSheet.title')).toBeNull()
await waitFor(() => expect(queryByText('nftCelebration.rewardBottomSheet.title')).toBeNull())
expect(queryByText('nftCelebration.rewardBottomSheet.description')).toBeNull()
expect(queryByText('nftCelebration.rewardBottomSheet.cta')).toBeNull()
})

it('does not render when feature gate is closed', () => {
it('does not render when feature gate is closed', async () => {
jest.mocked(getFeatureGate).mockReturnValue(false)

const { queryByText } = renderScreen({
Expand All @@ -238,38 +262,38 @@ describe('TabHome', () => {
},
})

expect(queryByText('nftCelebration.rewardBottomSheet.title')).toBeNull()
await waitFor(() => expect(queryByText('nftCelebration.rewardBottomSheet.title')).toBeNull())
expect(queryByText('nftCelebration.rewardBottomSheet.description')).toBeNull()
expect(queryByText('nftCelebration.rewardBottomSheet.cta')).toBeNull()
})

it('does not render if reward is already displayed', () => {
it('does not render if reward is already displayed', async () => {
const { queryByText } = renderScreen({
...mockStoreRewardDisplayed,
app: {
showNotificationSpotlight: false,
},
})

expect(queryByText('nftCelebration.rewardBottomSheet.title')).toBeNull()
await waitFor(() => expect(queryByText('nftCelebration.rewardBottomSheet.title')).toBeNull())
expect(queryByText('nftCelebration.rewardBottomSheet.description')).toBeNull()
expect(queryByText('nftCelebration.rewardBottomSheet.cta')).toBeNull()
})

it('does not render if reminder is already displayed', () => {
it('does not render if reminder is already displayed', async () => {
const { queryByText } = renderScreen({
...mockStoreReminderDisplayed,
app: {
showNotificationSpotlight: false,
},
})

expect(queryByText('nftCelebration.rewardBottomSheet.title')).toBeNull()
await waitFor(() => expect(queryByText('nftCelebration.rewardBottomSheet.title')).toBeNull())
expect(queryByText('nftCelebration.rewardBottomSheet.description')).toBeNull()
expect(queryByText('nftCelebration.rewardBottomSheet.cta')).toBeNull()
})

it('does not render if expired', () => {
it('does not render if expired', async () => {
jest.useFakeTimers({ now: new Date('3001-01-01T00:00:00.000Z') })

const { queryByText } = renderScreen({
Expand All @@ -279,7 +303,7 @@ describe('TabHome', () => {
},
})

expect(queryByText('nftCelebration.rewardBottomSheet.title')).toBeNull()
await waitFor(() => expect(queryByText('nftCelebration.rewardBottomSheet.title')).toBeNull())
expect(queryByText('nftCelebration.rewardBottomSheet.description')).toBeNull()
expect(queryByText('nftCelebration.rewardBottomSheet.cta')).toBeNull()
})
Expand Down
10 changes: 9 additions & 1 deletion src/home/TabHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ import { StackParamList } from 'src/navigator/types'
import { phoneRecipientCacheSelector } from 'src/recipients/reducer'
import { useDispatch, useSelector } from 'src/redux/hooks'
import { initializeSentryUserContext } from 'src/sentry/actions'
import { getFeatureGate } from 'src/statsig'
import { StatsigFeatureGates } from 'src/statsig/types'
import colors from 'src/styles/colors'
import TransactionFeed from 'src/transactions/feed/TransactionFeed'
import TransactionFeedV2 from 'src/transactions/feed/TransactionFeedV2'
import { hasGrantedContactsPermission } from 'src/utils/contacts'

const AnimatedFlatList = Animated.createAnimatedComponent(FlatList)
Expand All @@ -53,6 +56,8 @@ function TabHome(_props: Props) {
const canShowNftReward = useSelector(showNftRewardSelector)
const showNftReward = canShowNftReward && isFocused && !showNotificationSpotlight

const showZerionTransactionFeed = getFeatureGate(StatsigFeatureGates.SHOW_ZERION_TRANSACTION_FEED)

useEffect(() => {
dispatch(visitHome())
}, [])
Expand Down Expand Up @@ -120,7 +125,10 @@ function TabHome(_props: Props) {
key: 'NotificationBox',
component: <NotificationBox showOnlyHomeScreenNotifications={true} />,
},
{ key: 'TransactionFeed', component: <TransactionFeed /> },
{
key: 'TransactionFeed',
component: showZerionTransactionFeed ? <TransactionFeedV2 /> : <TransactionFeed />,
},
]

const renderItem = ({ item }: { item: any }) => item.component
Expand Down
1 change: 1 addition & 0 deletions src/statsig/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export enum StatsigFeatureGates {
SHOW_SWAP_AND_DEPOSIT = 'show_swap_and_deposit',
SHOW_UK_COMPLIANT_VARIANT = 'show_uk_compliant_variant',
ALLOW_EARN_PARTIAL_WITHDRAWAL = 'allow_earn_partial_withdrawal',
SHOW_ZERION_TRANSACTION_FEED = 'show_zerion_transaction_feed',
}

export enum StatsigExperiments {
Expand Down
Loading