Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
24add32
feat: adds loading state to ramps controller
georgeweiler Jan 29, 2026
dbdbdfc
feat: adds loading and error state to ramps controller
georgeweiler Jan 29, 2026
3a54173
Merge branch 'main' of github.com:MetaMask/core into ramps-loading-state
georgeweiler Jan 29, 2026
f9e4f08
chore: changelog update
georgeweiler Jan 29, 2026
1dfb044
chore: lint and 100 test coverage
georgeweiler Jan 29, 2026
b00fa9b
feat: update ramp controller state to use nested resource objects
georgeweiler Jan 29, 2026
1f4cfbe
chore: controller test update
georgeweiler Jan 29, 2026
a0d8a41
chore: controller test update
georgeweiler Jan 29, 2026
9b4cf08
chore: selectors test update
georgeweiler Jan 29, 2026
03388d2
Merge branch 'main' of github.com:MetaMask/core into ramps-loading-state
georgeweiler Feb 2, 2026
6e7c9d2
chore: formatting
georgeweiler Feb 2, 2026
ee50872
chore: changelog update
georgeweiler Feb 2, 2026
6229c0e
chore: lint and refactor
georgeweiler Feb 2, 2026
5740835
chore: changelog
georgeweiler Feb 2, 2026
7aa95e9
feat: race condition fixes from code review
georgeweiler Feb 2, 2026
6bac097
feat: race condition fixes from code review
georgeweiler Feb 2, 2026
596b802
chore: bugbot
georgeweiler Feb 2, 2026
9239cb2
chore: bugbot
georgeweiler Feb 2, 2026
3f1beec
chore: bugbot
georgeweiler Feb 2, 2026
8a28108
chore: lint and fix test
georgeweiler Feb 3, 2026
00ec4cb
feat: removes loading error state from user regino
georgeweiler Feb 3, 2026
2aeb120
chore: formatting
georgeweiler Feb 3, 2026
982b3a3
refactor: clean up request logic and state clearning mechanism
georgeweiler Feb 3, 2026
f5f1dd5
chore: ts fi
georgeweiler Feb 3, 2026
72db5b9
chore: bugbot
georgeweiler Feb 3, 2026
72bf63f
chore: fixes a test
georgeweiler Feb 3, 2026
ec00bd9
chore: formatting
georgeweiler Feb 3, 2026
d5a5052
feat: fixes payment method cache key
georgeweiler Feb 3, 2026
59e3398
Merge branch 'main' of github.com:MetaMask/core into ramps-loading-st…
georgeweiler Feb 3, 2026
6ff0e3d
chore: changelog
georgeweiler Feb 3, 2026
46d5a44
chore: removes unused code
georgeweiler Feb 3, 2026
f3a7bb4
chore: changelog formatting
georgeweiler Feb 3, 2026
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
1 change: 1 addition & 0 deletions packages/ramps-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Refactor: Consolidate reset logic with a shared resetResource helper and fix abort handling for dependent resources ([#7818](https://github.com/MetaMask/core/pull/7818))
- **BREAKING:** Restructure `RampsControllerState` to use nested `ResourceState` objects for each resource with `data`, `selected`, `isLoading`, and `error` ([#7779](https://github.com/MetaMask/core/pull/7779))

## [5.1.0]
Expand Down
162 changes: 160 additions & 2 deletions packages/ramps-controller/src/RampsController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,61 @@ describe('RampsController', () => {
);
});
});

it('returns providers for region when state has providers (fetches and returns result)', async () => {
await withController(
{
options: {
state: {
userRegion: createMockUserRegion('us-ca'),
providers: createResourceState(mockProviders, null),
},
},
},
async ({ controller, rootMessenger }) => {
let serviceCalled = false;
rootMessenger.registerActionHandler(
'RampsService:getProviders',
async () => {
serviceCalled = true;
return { providers: mockProviders };
},
);

const result = await controller.getProviders('us-ca');

expect(serviceCalled).toBe(true);
expect(result.providers).toStrictEqual(mockProviders);
},
);
});

it('calls service when getProviders is called with filter options even if state has providers', async () => {
await withController(
{
options: {
state: {
userRegion: createMockUserRegion('us-ca'),
providers: createResourceState(mockProviders, null),
},
},
},
async ({ controller, rootMessenger }) => {
let serviceCalled = false;
rootMessenger.registerActionHandler(
'RampsService:getProviders',
async () => {
serviceCalled = true;
return { providers: mockProviders };
},
);

await controller.getProviders('us-ca', { provider: 'moonpay' });

expect(serviceCalled).toBe(true);
},
);
});
});

describe('metadata', () => {
Expand Down Expand Up @@ -1369,6 +1424,50 @@ describe('RampsController', () => {
);
});
});

it('calls getTokens and getProviders when hydrating even if state has data', async () => {
const existingProviders: Provider[] = [
{
id: '/providers/test',
name: 'Test Provider',
environmentType: 'STAGING',
description: 'Test',
hqAddress: '123 Test St',
links: [],
logos: { light: '', dark: '', height: 24, width: 77 },
},
];
await withController(
{
options: {
state: {
userRegion: createMockUserRegion('us-ca'),
providers: createResourceState(existingProviders, null),
},
},
},
async ({ controller, rootMessenger }) => {
let providersCalled = false;
rootMessenger.registerActionHandler(
'RampsService:getTokens',
async () => ({ topTokens: [], allTokens: [] }),
);
rootMessenger.registerActionHandler(
'RampsService:getProviders',
async () => {
providersCalled = true;
return { providers: [] };
},
);

controller.hydrateState();

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

expect(providersCalled).toBe(true);
},
);
});
});

describe('setUserRegion', () => {
Expand Down Expand Up @@ -2069,6 +2168,8 @@ describe('RampsController', () => {
expect(controller.state.providers.selected).toBeNull();
expect(controller.state.paymentMethods.data).toStrictEqual([]);
expect(controller.state.paymentMethods.selected).toBeNull();
expect(controller.state.paymentMethods.isLoading).toBe(false);
expect(controller.state.paymentMethods.error).toBeNull();
},
);
});
Expand Down Expand Up @@ -2263,6 +2364,8 @@ describe('RampsController', () => {
expect(controller.state.tokens.selected).toBeNull();
expect(controller.state.paymentMethods.data).toStrictEqual([]);
expect(controller.state.paymentMethods.selected).toBeNull();
expect(controller.state.paymentMethods.isLoading).toBe(false);
expect(controller.state.paymentMethods.error).toBeNull();
},
);
});
Expand Down Expand Up @@ -2637,6 +2740,61 @@ describe('RampsController', () => {
});
});

it('returns tokens for region when state has tokens (fetches and returns result)', async () => {
await withController(
{
options: {
state: {
userRegion: createMockUserRegion('us-ca'),
tokens: createResourceState(mockTokens, null),
},
},
},
async ({ controller, rootMessenger }) => {
let serviceCalled = false;
rootMessenger.registerActionHandler(
'RampsService:getTokens',
async () => {
serviceCalled = true;
return mockTokens;
},
);

const result = await controller.getTokens('us-ca', 'buy');

expect(serviceCalled).toBe(true);
expect(result).toStrictEqual(mockTokens);
},
);
});

it('calls service when getTokens is called with provider filter even if state has tokens', async () => {
await withController(
{
options: {
state: {
userRegion: createMockUserRegion('us-ca'),
tokens: createResourceState(mockTokens, null),
},
},
},
async ({ controller, rootMessenger }) => {
let serviceCalled = false;
rootMessenger.registerActionHandler(
'RampsService:getTokens',
async () => {
serviceCalled = true;
return mockTokens;
},
);

await controller.getTokens('us-ca', 'buy', { provider: 'moonpay' });

expect(serviceCalled).toBe(true);
},
);
});

it('prefers provided region over userRegion in state', async () => {
await withController(
{
Expand Down Expand Up @@ -3402,10 +3560,10 @@ describe('RampsController', () => {
);

controller.setSelectedToken(tokenB.assetId);
await new Promise((resolve) => setTimeout(resolve, 10));

resolveTokenARequest({ payments: paymentMethodsForTokenA });
await tokenAPaymentMethodsPromise;
await new Promise((resolve) => setTimeout(resolve, 10));

expect(controller.state.tokens.selected).toStrictEqual(tokenB);
expect(controller.state.paymentMethods.data).toStrictEqual(
Expand Down Expand Up @@ -3510,10 +3668,10 @@ describe('RampsController', () => {
);

controller.setSelectedProvider(providerB.id);
await new Promise((resolve) => setTimeout(resolve, 10));

resolveProviderARequest({ payments: paymentMethodsForProviderA });
await providerAPaymentMethodsPromise;
await new Promise((resolve) => setTimeout(resolve, 10));

expect(controller.state.providers.selected).toStrictEqual(providerB);
expect(controller.state.paymentMethods.data).toStrictEqual(
Expand Down
Loading
Loading