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
3 changes: 3 additions & 0 deletions app/_locales/en/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ exports[`getRampsControllerApi exposes ramps background methods 1`] = `
"setRampsSelectedProvider",
"setRampsSelectedToken",
"setRampsUserRegion",
"syncRampsOrdersWithUserStorage",
]
`;
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ exports[`RampsControllerInit exposes ramps background API methods 1`] = `
"setRampsUserRegion",
"startRampsLifecycle",
"stopRampsLifecycle",
"syncRampsOrdersWithUserStorage",
]
`;
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,18 @@ export function getRampsControllerMessenger(

messenger.delegate({
messenger: controllerMessenger,
actions: [...RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS],
actions: [
...RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS,
// The controller reads the `moneyHeadlessAllProviders` feature flag
// itself for quote widening.
'RemoteFeatureFlagController:getState',
'UserStorageController:getState',
'UserStorageController:performGetStorage',
'UserStorageController:performGetStorageAllFeatureEntries',
'UserStorageController:performSetStorage',
'UserStorageController:performBatchSetStorage',
'AuthenticationController:isSignedIn',
],
events: [],
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ describe('getRampsControllerApi', () => {
removeOrder: jest.fn(),
getOrder: jest.fn(),
getOrderFromCallback: jest.fn(),
syncOrdersWithUserStorage: jest.fn(),
};

const api = getRampsControllerApi(rampsController as never);
Expand Down
2 changes: 2 additions & 0 deletions app/scripts/messenger-client-init/ramps-controller-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ export function getRampsControllerApi(rampsController: RampsController) {
refreshRampsOrder: rampsController.getOrder.bind(rampsController),
getRampsOrderFromCallback:
rampsController.getOrderFromCallback.bind(rampsController),
syncRampsOrdersWithUserStorage:
rampsController.syncOrdersWithUserStorage.bind(rampsController),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const mockRampsController = {
removeOrder: jest.fn(),
getOrder: jest.fn(),
getOrderFromCallback: jest.fn(),
syncOrdersWithUserStorage: jest.fn(),
};

let mockInit: jest.Mock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const initialStore = () => ({
isBackupAndSyncEnabled: true,
isAccountSyncingEnabled: false,
isContactSyncingEnabled: false,
isRampsSyncingEnabled: false,
completedMetaMetricsOnboarding: true,
optedIn: false,
isBackupAndSyncUpdateLoading: false,
Expand Down Expand Up @@ -186,11 +187,52 @@ describe('BackupAndSyncFeaturesToggles', () => {
);
});

it('enables ramps syncing', () => {
const store = initialStore();
store.metamask.isRampsSyncingEnabled = false;

const { setIsBackupAndSyncFeatureEnabledMock } = arrangeMocks();

const { getByTestId } = render(
<Redux.Provider store={mockStore(store)}>
<BackupAndSyncFeaturesToggles />
</Redux.Provider>,
);
fireEvent.click(
getByTestId(backupAndSyncFeaturesTogglesTestIds.rampsSyncingToggleButton),
);
expect(setIsBackupAndSyncFeatureEnabledMock).toHaveBeenCalledWith(
BACKUPANDSYNC_FEATURES.rampsSyncing,
true,
);
});

it('disables ramps syncing', () => {
const store = initialStore();
store.metamask.isRampsSyncingEnabled = true;

const { setIsBackupAndSyncFeatureEnabledMock } = arrangeMocks();

const { getByTestId } = render(
<Redux.Provider store={mockStore(store)}>
<BackupAndSyncFeaturesToggles />
</Redux.Provider>,
);
fireEvent.click(
getByTestId(backupAndSyncFeaturesTogglesTestIds.rampsSyncingToggleButton),
);
expect(setIsBackupAndSyncFeatureEnabledMock).toHaveBeenCalledWith(
BACKUPANDSYNC_FEATURES.rampsSyncing,
false,
);
});

it('disables main backup and sync when all sub-features are manually turned off', async () => {
const store = initialStore();
store.metamask.isBackupAndSyncEnabled = true;
store.metamask.isAccountSyncingEnabled = false; // Already off
store.metamask.isContactSyncingEnabled = false; // Already off
store.metamask.isAccountSyncingEnabled = false;
store.metamask.isContactSyncingEnabled = false;
store.metamask.isRampsSyncingEnabled = false;

const { setIsBackupAndSyncFeatureEnabledMock } = arrangeMocks();

Expand All @@ -212,8 +254,9 @@ describe('BackupAndSyncFeaturesToggles', () => {
it('does not disable main backup and sync when at least one sub-feature is enabled', async () => {
const store = initialStore();
store.metamask.isBackupAndSyncEnabled = true;
store.metamask.isAccountSyncingEnabled = true; // One is ON
store.metamask.isContactSyncingEnabled = false; // One is OFF
store.metamask.isAccountSyncingEnabled = true;
store.metamask.isContactSyncingEnabled = false;
store.metamask.isRampsSyncingEnabled = false;

const { setIsBackupAndSyncFeatureEnabledMock } = arrangeMocks();

Expand All @@ -233,6 +276,29 @@ describe('BackupAndSyncFeaturesToggles', () => {
);
});

it('does not disable main backup and sync when only ramps syncing is enabled', async () => {
const store = initialStore();
store.metamask.isBackupAndSyncEnabled = true;
store.metamask.isAccountSyncingEnabled = false;
store.metamask.isContactSyncingEnabled = false;
store.metamask.isRampsSyncingEnabled = true;

const { setIsBackupAndSyncFeatureEnabledMock } = arrangeMocks();

render(
<Redux.Provider store={mockStore(store)}>
<BackupAndSyncFeaturesToggles />
</Redux.Provider>,
);

await new Promise((resolve) => setTimeout(resolve, 100));

expect(setIsBackupAndSyncFeatureEnabledMock).not.toHaveBeenCalledWith(
BACKUPANDSYNC_FEATURES.main,
false,
);
});

function arrangeMocks() {
const setIsBackupAndSyncFeatureEnabledMock = jest.fn(() =>
Promise.resolve(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useI18nContext } from '../../../../hooks/useI18nContext';
import {
selectIsAccountSyncingEnabled,
selectIsContactSyncingEnabled,
selectIsRampsSyncingEnabled,
selectIsBackupAndSyncEnabled,
selectIsBackupAndSyncUpdateLoading,
} from '../../../../selectors/identity/backup-and-sync';
Expand All @@ -35,6 +36,8 @@ export const backupAndSyncFeaturesTogglesTestIds = {
accountSyncingToggleButton: 'account-syncing-toggle-button',
contactSyncingToggleContainer: 'contact-syncing-toggle-container',
contactSyncingToggleButton: 'contact-syncing-toggle-button',
rampsSyncingToggleContainer: 'ramps-syncing-toggle-container',
rampsSyncingToggleButton: 'ramps-syncing-toggle-button',
};

export const backupAndSyncFeaturesTogglesSections = [
Expand All @@ -60,6 +63,17 @@ export const backupAndSyncFeaturesTogglesSections = [
toggleButtonTestId:
backupAndSyncFeaturesTogglesTestIds.contactSyncingToggleButton,
},
{
id: 'ramps',
titleI18NKey: 'backupAndSyncFeatureRamps',
iconName: IconName.Card,
backupAndSyncfeatureKey: BACKUPANDSYNC_FEATURES.rampsSyncing,
featureReduxSelector: selectIsRampsSyncingEnabled,
toggleContainerTestId:
backupAndSyncFeaturesTogglesTestIds.rampsSyncingToggleContainer,
toggleButtonTestId:
backupAndSyncFeaturesTogglesTestIds.rampsSyncingToggleButton,
},
];

const FeatureToggle = ({
Expand Down Expand Up @@ -154,14 +168,17 @@ export const BackupAndSyncFeaturesToggles = () => {
);
const isAccountSyncingEnabled = useSelector(selectIsAccountSyncingEnabled);
const isContactSyncingEnabled = useSelector(selectIsContactSyncingEnabled);
const isRampsSyncingEnabled = useSelector(selectIsRampsSyncingEnabled);

const { setIsBackupAndSyncFeatureEnabled } = useBackupAndSync();

// Reverse cascading: if all sub-features are manually turned off, turn off main toggle
// Guard against race conditions by not running while updates are in progress
useEffect(() => {
const allSubFeaturesDisabled =
!isAccountSyncingEnabled && !isContactSyncingEnabled;
!isAccountSyncingEnabled &&
!isContactSyncingEnabled &&
!isRampsSyncingEnabled;

if (
isBackupAndSyncEnabled &&
Expand All @@ -183,6 +200,7 @@ export const BackupAndSyncFeaturesToggles = () => {
isBackupAndSyncEnabled,
isAccountSyncingEnabled,
isContactSyncingEnabled,
isRampsSyncingEnabled,
isBackupAndSyncUpdateLoading,
setIsBackupAndSyncFeatureEnabled,
]);
Expand Down
40 changes: 40 additions & 0 deletions ui/contexts/identity/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import '@testing-library/jest-dom';
import * as redux from 'react-redux';
import { useAccountSyncing } from '../../hooks/identity/useAccountSyncing';
import { useContactSyncing } from '../../hooks/identity/useContactSyncing';
import { useRampsOrderSyncing } from '../../hooks/identity/useRampsOrderSyncing/useRampsOrderSyncing';
import {
useAutoSignIn,
useAutoSignOut,
Expand All @@ -17,13 +18,15 @@ jest.mock('react-redux', () => ({
jest.mock('../../hooks/identity/useBackupAndSync');
jest.mock('../../hooks/identity/useAccountSyncing');
jest.mock('../../hooks/identity/useContactSyncing');
jest.mock('../../hooks/identity/useRampsOrderSyncing/useRampsOrderSyncing');
jest.mock('../../hooks/identity/useAuthentication');

const mockUseSelector = jest.mocked(redux.useSelector);

describe('MetamaskIdentityProvider', () => {
const mockUseAccountSyncing = jest.mocked(useAccountSyncing);
const mockUseContactSyncing = jest.mocked(useContactSyncing);
const mockUseRampsOrderSyncing = jest.mocked(useRampsOrderSyncing);
const mockUseAutoSignIn = jest.mocked(useAutoSignIn);
const mockUseAutoSignOut = jest.mocked(useAutoSignOut);

Expand All @@ -38,6 +41,11 @@ describe('MetamaskIdentityProvider', () => {
shouldDispatchContactSyncing: false,
});

mockUseRampsOrderSyncing.mockReturnValue({
dispatchRampsOrderSyncing: jest.fn(),
shouldDispatchRampsOrderSyncing: false,
});

mockUseAutoSignIn.mockReturnValue({
autoSignIn: jest.fn(),
shouldAutoSignIn: false,
Expand Down Expand Up @@ -125,6 +133,38 @@ describe('MetamaskIdentityProvider', () => {
expect(dispatchContactSyncing).not.toHaveBeenCalled();
});

it('calls dispatchRampsOrderSyncing if shouldDispatchRampsOrderSyncing is true', () => {
const dispatchRampsOrderSyncing = jest.fn();
mockUseRampsOrderSyncing.mockReturnValue({
dispatchRampsOrderSyncing,
shouldDispatchRampsOrderSyncing: true,
});

render(
<MetamaskIdentityProvider>
<div>Child Component</div>
</MetamaskIdentityProvider>,
);

expect(dispatchRampsOrderSyncing).toHaveBeenCalled();
});

it('does not call dispatchRampsOrderSyncing if shouldDispatchRampsOrderSyncing is false', () => {
const dispatchRampsOrderSyncing = jest.fn();
mockUseRampsOrderSyncing.mockReturnValue({
dispatchRampsOrderSyncing,
shouldDispatchRampsOrderSyncing: false,
});

render(
<MetamaskIdentityProvider>
<div>Child Component</div>
</MetamaskIdentityProvider>,
);

expect(dispatchRampsOrderSyncing).not.toHaveBeenCalled();
});

it('calls autoSignIn if shouldAutoSignIn returns true', () => {
const autoSignIn = jest.fn();
const shouldAutoSignIn = true;
Expand Down
9 changes: 9 additions & 0 deletions ui/contexts/identity/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect } from 'react';
import { useAccountSyncing } from '../../hooks/identity/useAccountSyncing';
import { useContactSyncing } from '../../hooks/identity/useContactSyncing';
import { useRampsOrderSyncing } from '../../hooks/identity/useRampsOrderSyncing/useRampsOrderSyncing';
import {
useAutoSignIn,
useAutoSignOut,
Expand All @@ -13,6 +14,8 @@ export const MetamaskIdentityProvider = ({
useAccountSyncing();
const { dispatchContactSyncing, shouldDispatchContactSyncing } =
useContactSyncing();
const { dispatchRampsOrderSyncing, shouldDispatchRampsOrderSyncing } =
useRampsOrderSyncing();
const { autoSignIn, shouldAutoSignIn } = useAutoSignIn();
const { autoSignOut, shouldAutoSignOut } = useAutoSignOut();

Expand All @@ -31,6 +34,12 @@ export const MetamaskIdentityProvider = ({
}
}, [shouldDispatchContactSyncing, dispatchContactSyncing]);

useEffect(() => {
if (shouldDispatchRampsOrderSyncing) {
dispatchRampsOrderSyncing();
}
}, [shouldDispatchRampsOrderSyncing, dispatchRampsOrderSyncing]);

/**
* Authentication effects
*
Expand Down
Loading
Loading