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

introduce coinmarket under feature flag #16574

Merged
merged 6 commits into from
Jan 24, 2025
Merged
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
5 changes: 4 additions & 1 deletion jest.config.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@ module.exports = {
'\\.(js|jsx|ts|tsx)$': ['babel-jest', babelConfig],
},
transformIgnorePatterns: [
'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg)',
'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg|@shopify/react-native-skia)',
],
setupFiles: [
'<rootDir>/../../node_modules/@shopify/react-native-skia/jestSetup.js',
'<rootDir>/../../node_modules/react-native-gesture-handler/jestSetup.js',
'<rootDir>/../../suite-native/test-utils/src/atomsMock.js',
'<rootDir>/../../suite-native/test-utils/src/expoMock.js',
'<rootDir>/../../suite-native/firmware/src/jestSetup.js',
'<rootDir>/../../suite-native/connection-status/src/jestSetup.js',
'<rootDir>/../../suite-native/react-native-graph/src/jestSetup.js',
],
};
1 change: 1 addition & 0 deletions suite-common/icons/generateIconFont.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const usedIcons = [
'arrowLineUpRight',
'arrowRight',
'arrowsCounterClockwise',
'arrowsLeftRight',
'arrowSquareOut',
'arrowUp',
'arrowUpRight',
Expand Down
21 changes: 11 additions & 10 deletions suite-common/icons/iconFontsMobile/TrezorSuiteIcons.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,15 @@
"bugBeetle": 61784,
"bookmarkSimple": 61785,
"backspace": 61786,
"arrowsCounterClockwise": 61787,
"arrowUpRight": 61788,
"arrowUp": 61789,
"arrowURightDown": 61790,
"arrowSquareOut": 61791,
"arrowRight": 61792,
"arrowLineUpRight": 61793,
"arrowLineUp": 61794,
"arrowLineDown": 61795,
"arrowDown": 61796
"arrowsLeftRight": 61787,
"arrowsCounterClockwise": 61788,
"arrowUpRight": 61789,
"arrowUp": 61790,
"arrowURightDown": 61791,
"arrowSquareOut": 61792,
"arrowRight": 61793,
"arrowLineUpRight": 61794,
"arrowLineUp": 61795,
"arrowLineDown": 61796,
"arrowDown": 61797
}
Binary file modified suite-common/icons/iconFontsMobile/TrezorSuiteIcons.ttf
Binary file not shown.
1 change: 1 addition & 0 deletions suite-native/accounts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dependencies": {
"@mobily/ts-belt": "^3.13.1",
"@react-navigation/native": "6.1.18",
"@reduxjs/toolkit": "1.9.5",
"@suite-common/formatters": "workspace:*",
"@suite-common/redux-utils": "workspace:*",
"@suite-common/token-definitions": "workspace:*",
Expand Down
7 changes: 7 additions & 0 deletions suite-native/accounts/redux.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { AsyncThunkAction } from '@reduxjs/toolkit';

declare module 'redux' {
export interface Dispatch {
<TThunk extends AsyncThunkAction<any, any, any>>(thunk: TThunk): ReturnType<TThunk>;
}
}
1 change: 1 addition & 0 deletions suite-native/app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ android/
npm-debug.*
yarn-debug.*
yarn-error.*
/reports/junit-report.xml

# macOS
.DS_Store
Expand Down
1 change: 1 addition & 0 deletions suite-native/app/e2e/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ module.exports = {
verbose: true,
maxWorkers: 1,
setupFilesAfterEnv: ['<rootDir>/e2e/jest.setup.ts'],
testMatch: ['<rootDir>/e2e/tests/*.test.ts'],
};
1 change: 1 addition & 0 deletions suite-native/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"@suite-native/module-send": "workspace:*",
"@suite-native/module-settings": "workspace:*",
"@suite-native/module-staking-management": "workspace:*",
"@suite-native/module-trading": "workspace:*",
"@suite-native/navigation": "workspace:*",
"@suite-native/notifications": "workspace:*",
"@suite-native/receive": "workspace:*",
Expand Down
11 changes: 11 additions & 0 deletions suite-native/app/src/navigation/AppTabNavigator.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { useSelector } from 'react-redux';

import { BottomTabBarProps, createBottomTabNavigator } from '@react-navigation/bottom-tabs';

import { HomeStackNavigator } from '@suite-native/module-home';
import { AccountsStackNavigator } from '@suite-native/module-accounts-management';
import { SettingsScreen } from '@suite-native/module-settings';
import { AppTabsParamList, AppTabsRoutes, TabBar } from '@suite-native/navigation';
import { useHandleDeviceRequestsPassphrase } from '@suite-native/device-authorization';
import { createSelectIsFeatureFlagEnabled, FeatureFlag } from '@suite-native/feature-flags';
import { TradingStackNavigator } from '@suite-native/module-trading';

import { rootTabsOptions } from './routes';

Expand All @@ -13,6 +17,10 @@ const Tab = createBottomTabNavigator<AppTabsParamList>();
export const AppTabNavigator = () => {
useHandleDeviceRequestsPassphrase();

const isTradingEnabled = useSelector(
createSelectIsFeatureFlagEnabled(FeatureFlag.IsTradingEnabled),
);

return (
<Tab.Navigator
initialRouteName={AppTabsRoutes.HomeStack}
Expand All @@ -26,6 +34,9 @@ export const AppTabNavigator = () => {
>
<Tab.Screen name={AppTabsRoutes.HomeStack} component={HomeStackNavigator} />
<Tab.Screen name={AppTabsRoutes.AccountsStack} component={AccountsStackNavigator} />
{isTradingEnabled && (
<Tab.Screen name={AppTabsRoutes.TradeStack} component={TradingStackNavigator} />
)}
<Tab.Screen name={AppTabsRoutes.Settings} component={SettingsScreen} />
</Tab.Navigator>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { renderWithStore, waitFor, PreloadedState, fireEvent } from '@suite-native/test-utils';
import { FeatureFlag, featureFlagsInitialState } from '@suite-native/feature-flags';

import { AppTabNavigator } from '../AppTabNavigator';

describe('AppTabNavigator', () => {
const renderTabs = async (preloadedState?: PreloadedState) => {
const result = renderWithStore(<AppTabNavigator />, { preloadedState });
await waitFor(() => expect(result.getByText('Home')).toBeDefined());

return result;
};

it('should render 3 buttons', async () => {
const { getByText } = await renderTabs();

expect(getByText('Home')).toBeDefined();
expect(getByText('My assets')).toBeDefined();
expect(getByText('Settings')).toBeDefined();
});

it('should not render Trade tab when IsTradingEnabled is false', async () => {
const { queryByText } = await renderTabs({
featureFlags: {
...featureFlagsInitialState,
[FeatureFlag.IsTradingEnabled]: false,
},
});

expect(queryByText('Trade')).toBe(null);
});

it('should render Trade tab when IsTradingEnabled is true', async () => {
const { getByText } = await renderTabs({
featureFlags: {
...featureFlagsInitialState,
[FeatureFlag.IsTradingEnabled]: true,
},
});

const tradeTab = getByText('Trade');
fireEvent.press(tradeTab);

expect(getByText('Trading placeholder')).toBeDefined();
});
});
8 changes: 8 additions & 0 deletions suite-native/app/src/navigation/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ const accountsStack = enhanceTabOption({
},
});

const tradeStack = enhanceTabOption({
routeName: AppTabsRoutes.TradeStack,
iconName: 'arrowsLeftRight',
focusedIconName: 'arrowsLeftRight',
label: 'Trade',
});

const settings = enhanceTabOption({
routeName: AppTabsRoutes.Settings,
iconName: 'gear',
Expand All @@ -29,5 +36,6 @@ const settings = enhanceTabOption({
export const rootTabsOptions = {
...homeStack,
...accountsStack,
...tradeStack,
...settings,
};
1 change: 1 addition & 0 deletions suite-native/app/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
{
"path": "../module-staking-management"
},
{ "path": "../module-trading" },
{ "path": "../navigation" },
{ "path": "../notifications" },
{ "path": "../receive" },
Expand Down
1 change: 1 addition & 0 deletions suite-native/atoms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dependencies": {
"@gorhom/bottom-sheet": "5.0.5",
"@mobily/ts-belt": "^3.13.1",
"@reduxjs/toolkit": "1.9.5",
"@shopify/flash-list": "^1.7.2",
"@shopify/react-native-skia": "^1.5.10",
"@suite-common/wallet-config": "workspace:*",
Expand Down
7 changes: 7 additions & 0 deletions suite-native/atoms/redux.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { AsyncThunkAction } from '@reduxjs/toolkit';

declare module 'redux' {
export interface Dispatch {
<TThunk extends AsyncThunkAction<any, any, any>>(thunk: TThunk): ReturnType<TThunk>;
}
}
3 changes: 3 additions & 0 deletions suite-native/connection-status/src/jestSetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import mockRNCNetInfo from '@react-native-community/netinfo/jest/netinfo-mock.js';

jest.mock('@react-native-community/netinfo', () => mockRNCNetInfo);
3 changes: 2 additions & 1 deletion suite-native/feature-flags/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"main": "src/index",
"scripts": {
"depcheck": "yarn g:depcheck",
"type-check": "yarn g:tsc --build"
"type-check": "yarn g:tsc --build",
"test:unit": "yarn g:jest -c ../../jest.config.native.js"
},
"dependencies": {
"@reduxjs/toolkit": "1.9.5",
Expand Down
123 changes: 123 additions & 0 deletions suite-native/feature-flags/src/__tests__/featureFlagsSlice.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
describe('featureFlagsSlice', () => {
afterEach(() => {
jest.resetModules();
jest.resetAllMocks();
});

describe('initial state', () => {
it('should have correct initial state for debug environment on android', () => {
jest.mock('@suite-native/config', () => ({
...jest.requireActual('@suite-native/config'),
isDebugEnv: () => true,
isDevelopOrDebugEnv: () => true,
}));
jest.mock('@trezor/env-utils', () => ({
...jest.requireActual('@trezor/env-utils'),
isAndroid: () => true,
}));

const { featureFlagsReducer } = require('../featureFlagsSlice');

const initialState = featureFlagsReducer(undefined, { type: 'undefined_action' });

expect(initialState).toEqual({
isDeviceConnectEnabled: true,
isCardanoSendEnabled: true,
isRegtestEnabled: true,
isConnectPopupEnabled: true,
areEthL2sEnabled: true,
isTradingEnabled: true,
isDeviceOnboardingEnabled: true,
});
});

it('should have correct initial state for production environment on android', () => {
jest.mock('@suite-native/config', () => ({
...jest.requireActual('@suite-native/config'),
isDebugEnv: () => false,
isDevelopOrDebugEnv: () => false,
}));
jest.mock('@trezor/env-utils', () => ({
...jest.requireActual('@trezor/env-utils'),
isAndroid: () => true,
}));

const { featureFlagsReducer } = require('../featureFlagsSlice');

const initialState = featureFlagsReducer(undefined, { type: 'undefined_action' });

expect(initialState).toEqual({
isDeviceConnectEnabled: true,
isCardanoSendEnabled: false,
isRegtestEnabled: false,
isConnectPopupEnabled: false,
areEthL2sEnabled: false,
isTradingEnabled: false,
isDeviceOnboardingEnabled: false,
});
});

it('should have correct initial state for production environment on iOS', () => {
jest.mock('@suite-native/config', () => ({
...jest.requireActual('@suite-native/config'),
isDebugEnv: () => false,
isDevelopOrDebugEnv: () => false,
}));
jest.mock('@trezor/env-utils', () => ({
...jest.requireActual('@trezor/env-utils'),
isAndroid: () => false,
}));

const { featureFlagsReducer } = require('../featureFlagsSlice');

const initialState = featureFlagsReducer(undefined, { type: 'undefined_action' });

expect(initialState).toEqual({
isDeviceConnectEnabled: false,
isCardanoSendEnabled: false,
isRegtestEnabled: false,
isConnectPopupEnabled: false,
areEthL2sEnabled: false,
isTradingEnabled: false,
isDeviceOnboardingEnabled: false,
});
});
});

describe('toggleFeatureFlag', () => {
it('should toggle feature flag', () => {
const { featureFlagsReducer, toggleFeatureFlag } = require('../featureFlagsSlice');

const state = featureFlagsReducer(
undefined,
toggleFeatureFlag({ featureFlag: 'isDeviceConnectEnable' }),
);
expect(state.isDeviceConnectEnabled).toEqual(false);

const state2 = featureFlagsReducer(
state,
toggleFeatureFlag({ featureFlag: 'isDeviceConnectEnabled' }),
);
expect(state2.isDeviceConnectEnabled).toEqual(true);
});
});

describe('selectIsFeatureFlagEnabled', () => {
it('should return correct value', () => {
const {
featureFlagsReducer,
toggleFeatureFlag,
selectIsFeatureFlagEnabled,
} = require('../featureFlagsSlice');

const state = featureFlagsReducer(
undefined,
toggleFeatureFlag({ featureFlag: 'isDeviceConnectEnabled' }),
);

expect(
selectIsFeatureFlagEnabled({ featureFlags: state }, 'isDeviceConnectEnabled'),
).toEqual(true);
});
});
});
4 changes: 4 additions & 0 deletions suite-native/feature-flags/src/featureFlagsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ export const FeatureFlag = {
IsConnectPopupEnabled: 'isConnectPopupEnabled',
AreEthL2sEnabled: 'areEthL2sEnabled',
IsDeviceOnboardingEnabled: 'isDeviceOnboardingEnabled',
IsTradingEnabled: 'isTradingEnabled',
} as const;

export type FeatureFlag = (typeof FeatureFlag)[keyof typeof FeatureFlag];

export type FeatureFlagsState = Record<FeatureFlag, boolean>;
Expand All @@ -26,6 +28,7 @@ export const featureFlagsInitialState: FeatureFlagsState = {
[FeatureFlag.IsConnectPopupEnabled]: isDevelopOrDebugEnv(),
[FeatureFlag.AreEthL2sEnabled]: isDebugEnv(),
[FeatureFlag.IsDeviceOnboardingEnabled]: isDebugEnv() && !isDetoxTestBuild(),
[FeatureFlag.IsTradingEnabled]: isDebugEnv(),
};

export const featureFlagsPersistedKeys: Array<keyof FeatureFlagsState> = [
Expand All @@ -35,6 +38,7 @@ export const featureFlagsPersistedKeys: Array<keyof FeatureFlagsState> = [
FeatureFlag.IsConnectPopupEnabled,
FeatureFlag.AreEthL2sEnabled,
FeatureFlag.IsDeviceOnboardingEnabled,
FeatureFlag.IsTradingEnabled,
];

export const featureFlagsSlice = createSlice({
Expand Down
6 changes: 6 additions & 0 deletions suite-native/firmware/src/jestSetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
jest.mock('@suite-native/firmware', () => ({
...jest.requireActual('./nativeFirmwareSlice'),
...jest.requireActual('./hooks/useIsFirmwareUpdateFeatureEnabled'),
UpdateProgressIndicatorDemo: () => null,
FirmwareUpdateInProgressScreen: () => null,
}));
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const featureFlagsTitleMap = {
[FeatureFlagEnum.IsConnectPopupEnabled]: 'Connect Popup',
[FeatureFlagEnum.AreEthL2sEnabled]: 'Eth L2s',
[FeatureFlagEnum.IsDeviceOnboardingEnabled]: 'Device onboarding',
[FeatureFlagEnum.IsTradingEnabled]: 'Trading',
} as const satisfies Record<FeatureFlagEnum, string>;

const FeatureFlag = ({ featureFlag }: { featureFlag: FeatureFlagEnum }) => {
Expand Down
Loading
Loading