Skip to content

Commit 6e92ee4

Browse files
committed
chore: Rebase with develop
2 parents 2c458c5 + bd2248d commit 6e92ee4

24 files changed

+272
-25
lines changed

.storybook/test-data.js

+27
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,32 @@ const state = {
487487
},
488488
},
489489
},
490+
allTokens: {
491+
'0x1': {
492+
'0x64a845a5b02460acf8a3d84503b0d68d028b4bb4': [
493+
{
494+
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
495+
aggregators: [],
496+
decimals: 6,
497+
symbol: 'USDC',
498+
},
499+
{
500+
address: '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e',
501+
aggregators: [],
502+
decimals: 18,
503+
symbol: 'YFI',
504+
},
505+
],
506+
},
507+
},
508+
tokenBalances: {
509+
'0x64a845a5b02460acf8a3d84503b0d68d028b4bb4': {
510+
'0x1': {
511+
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48': '0xbdbd',
512+
'0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e': '0x501b4176a64d6',
513+
},
514+
},
515+
},
490516
tokens: [
491517
{
492518
address: '0xaD6D458402F60fD3Bd25163575031ACDce07538A',
@@ -682,6 +708,7 @@ const state = {
682708
order: 'dsc',
683709
sortCallback: 'stringNumeric',
684710
},
711+
tokenNetworkFilter: {},
685712
},
686713
incomingTransactionsPreferences: {
687714
[CHAIN_IDS.MAINNET]: true,

app/scripts/constants/sentry-state.ts

+1
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ export const SENTRY_BACKGROUND_STATE = {
245245
showFiatInTestnets: true,
246246
showTestNetworks: true,
247247
smartTransactionsOptInStatus: true,
248+
tokenNetworkFilter: {},
248249
showNativeTokenAsMainBalance: true,
249250
petnamesEnabled: true,
250251
showConfirmationAdvancedDetails: true,

app/scripts/metamask-controller.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3209,7 +3209,7 @@ export default class MetamaskController extends EventEmitter {
32093209
const { completedOnboarding } = this.onboardingController.state;
32103210

32113211
let networkVersion = this.deprecatedNetworkVersions[networkClientId];
3212-
if (!networkVersion && completedOnboarding) {
3212+
if (networkVersion === undefined && completedOnboarding) {
32133213
const ethQuery = new EthQuery(networkClient.provider);
32143214
networkVersion = await new Promise((resolve) => {
32153215
ethQuery.sendAsync({ method: 'net_version' }, (error, result) => {

shared/modules/network.utils.test.ts

+49
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
isSafeChainId,
44
isPrefixedFormattedHexString,
55
isTokenDetectionEnabledForNetwork,
6+
convertNetworkId,
67
} from './network.utils';
78

89
describe('network utils', () => {
@@ -83,4 +84,52 @@ describe('network utils', () => {
8384
expect(isTokenDetectionEnabledForNetwork(undefined)).toBe(false);
8485
});
8586
});
87+
88+
describe('convertNetworkId', () => {
89+
it('returns decimal strings for postive integer number values', () => {
90+
expect(convertNetworkId(0)).toStrictEqual('0');
91+
expect(convertNetworkId(123)).toStrictEqual('123');
92+
expect(convertNetworkId(1337)).toStrictEqual('1337');
93+
});
94+
95+
it('returns null for negative numbers', () => {
96+
expect(convertNetworkId(-1)).toStrictEqual(null);
97+
});
98+
99+
it('returns null for non integer numbers', () => {
100+
expect(convertNetworkId(0.1)).toStrictEqual(null);
101+
expect(convertNetworkId(1.1)).toStrictEqual(null);
102+
});
103+
104+
it('returns null for NaN', () => {
105+
expect(convertNetworkId(Number.NaN)).toStrictEqual(null);
106+
});
107+
108+
it('returns decimal strings for strict valid hex values', () => {
109+
expect(convertNetworkId('0x0')).toStrictEqual('0');
110+
expect(convertNetworkId('0x1')).toStrictEqual('1');
111+
expect(convertNetworkId('0x539')).toStrictEqual('1337');
112+
});
113+
114+
it('returns null for invalid hex values', () => {
115+
expect(convertNetworkId('0xG')).toStrictEqual(null);
116+
expect(convertNetworkId('0x@')).toStrictEqual(null);
117+
expect(convertNetworkId('0xx1')).toStrictEqual(null);
118+
});
119+
120+
it('returns the value as is if already a postive decimal string', () => {
121+
expect(convertNetworkId('0')).toStrictEqual('0');
122+
expect(convertNetworkId('1')).toStrictEqual('1');
123+
expect(convertNetworkId('1337')).toStrictEqual('1337');
124+
});
125+
126+
it('returns null for negative number strings', () => {
127+
expect(convertNetworkId('-1')).toStrictEqual(null);
128+
});
129+
130+
it('returns null for non integer number strings', () => {
131+
expect(convertNetworkId('0.1')).toStrictEqual(null);
132+
expect(convertNetworkId('1.1')).toStrictEqual(null);
133+
});
134+
});
86135
});

shared/modules/network.utils.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,16 @@ function isSafeInteger(value: unknown): value is number {
7878
* as either a number, a decimal string, or a 0x-prefixed hex string.
7979
*
8080
* @param value - The network ID to convert, in an unknown format.
81-
* @returns A valid network ID (as a decimal string)
82-
* @throws If the given value cannot be safely parsed.
81+
* @returns A valid network ID (as a decimal string) or null if
82+
* the given value cannot be parsed.
8383
*/
84-
export function convertNetworkId(value: unknown): string {
85-
if (typeof value === 'number' && !Number.isNaN(value)) {
84+
export function convertNetworkId(value: unknown): string | null {
85+
if (typeof value === 'number' && Number.isInteger(value) && value >= 0) {
8686
return `${value}`;
8787
} else if (isStrictHexString(value)) {
8888
return `${convertHexToDecimal(value)}`;
8989
} else if (typeof value === 'string' && /^\d+$/u.test(value)) {
9090
return value;
9191
}
92-
throw new Error(`Cannot parse as a valid network ID: '${value}'`);
92+
return null;
9393
}

test/e2e/fixture-builder.js

+3
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ function onboardingFixture() {
8989
order: 'dsc',
9090
sortCallback: 'stringNumeric',
9191
},
92+
tokenNetworkFilter: {},
9293
shouldShowAggregatedBalancePopover: true,
9394
},
9495
useExternalServices: true,
@@ -126,6 +127,7 @@ function onboardingFixture() {
126127
},
127128
showTestNetworks: false,
128129
smartTransactionsOptInStatus: true,
130+
tokenNetworkFilter: {},
129131
},
130132
QueuedRequestController: {
131133
queuedRequestCount: 0,
@@ -664,6 +666,7 @@ class FixtureBuilder {
664666
return this.withPreferencesController({
665667
preferences: {
666668
smartTransactionsOptInStatus: true,
669+
tokenNetworkFilter: {},
667670
},
668671
});
669672
}

test/e2e/tests/confirmations/transactions/erc20-approve-redesign.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ async function mocks(server: MockttpServer) {
118118
export async function importTST(driver: Driver) {
119119
await driver.switchToWindowWithTitle(WINDOW_TITLES.ExtensionInFullScreenView);
120120
await driver.clickElement('[data-testid="import-token-button"]');
121+
await driver.clickElement('[data-testid="importTokens"]');
121122

122123
await driver.waitForSelector({
123124
css: '.import-tokens-modal__button-tab',

test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,13 @@
237237
"redesignedConfirmationsEnabled": true,
238238
"redesignedTransactionsEnabled": "boolean",
239239
"tokenSortConfig": "object",
240-
"tokenNetworkFilter": "object",
240+
"tokenNetworkFilter": {
241+
"0x1": "boolean",
242+
"0xaa36a7": "boolean",
243+
"0xe705": "boolean",
244+
"0xe708": "boolean",
245+
"0x539": "boolean"
246+
},
241247
"shouldShowAggregatedBalancePopover": "boolean"
242248
},
243249
"ipfsGateway": "string",

test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,13 @@
3838
"redesignedConfirmationsEnabled": true,
3939
"redesignedTransactionsEnabled": "boolean",
4040
"tokenSortConfig": "object",
41-
"tokenNetworkFilter": "object",
41+
"tokenNetworkFilter": {
42+
"0x1": "boolean",
43+
"0xaa36a7": "boolean",
44+
"0xe705": "boolean",
45+
"0xe708": "boolean",
46+
"0x539": "boolean"
47+
},
4248
"shouldShowAggregatedBalancePopover": "boolean"
4349
},
4450
"firstTimeFlowType": "import",

test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
"tokenSortConfig": "object",
121121
"showMultiRpcModal": "boolean",
122122
"shouldShowAggregatedBalancePopover": "boolean",
123-
"tokenNetworkFilter": "object"
123+
"tokenNetworkFilter": {}
124124
},
125125
"selectedAddress": "string",
126126
"theme": "light",

test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
"isRedesignedConfirmationsDeveloperEnabled": "boolean",
136136
"showConfirmationAdvancedDetails": false,
137137
"tokenSortConfig": "object",
138-
"tokenNetworkFilter": "object",
138+
"tokenNetworkFilter": {},
139139
"showMultiRpcModal": "boolean",
140140
"shouldShowAggregatedBalancePopover": "boolean"
141141
},

test/e2e/tests/privacy/basic-functionality.spec.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ describe('MetaMask onboarding @no-mmi', function () {
102102

103103
// Wait until network is fully switched and refresh tokens before asserting to mitigate flakiness
104104
await driver.assertElementNotPresent('.loading-overlay');
105-
await driver.clickElement('[data-testid="refresh-list-button"]');
105+
await driver.clickElement(`[data-testid="import-token-button"]`);
106+
await driver.clickElement('[data-testid="refreshList"]');
106107

107108
for (let i = 0; i < mockedEndpoints.length; i += 1) {
108109
const requests = await mockedEndpoints[i].getSeenRequests();
@@ -157,7 +158,8 @@ describe('MetaMask onboarding @no-mmi', function () {
157158

158159
// Wait until network is fully switched and refresh tokens before asserting to mitigate flakiness
159160
await driver.assertElementNotPresent('.loading-overlay');
160-
await driver.clickElement('[data-testid="refresh-list-button"]');
161+
await driver.clickElement(`[data-testid="import-token-button"]`);
162+
await driver.clickElement('[data-testid="refreshList"]');
161163
// intended delay to allow for network requests to complete
162164
await driver.delay(1000);
163165
for (let i = 0; i < mockedEndpoints.length; i += 1) {

test/e2e/tests/tokens/add-hide-token.spec.js

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ describe('Add existing token using search', function () {
130130
await unlockWallet(driver);
131131

132132
await driver.clickElement(`[data-testid="import-token-button"]`);
133+
await driver.clickElement(`[data-testid="importTokens"]`);
133134
await driver.fill('input[placeholder="Search tokens"]', 'BAT');
134135
await driver.clickElement({
135136
text: 'BAT',

test/e2e/tests/tokens/custom-token-add-approve.spec.js

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ describe('Create token, approve token and approve token without gas', function (
3636
await clickNestedButton(driver, 'Tokens');
3737

3838
await driver.clickElement(`[data-testid="import-token-button"]`);
39+
await driver.clickElement(`[data-testid="importTokens"]`);
3940
await clickNestedButton(driver, 'Custom token');
4041
await driver.fill(
4142
'[data-testid="import-tokens-modal-custom-address"]',

test/e2e/tests/tokens/import-tokens.spec.js

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ describe('Import flow', function () {
6969
await driver.assertElementNotPresent('.loading-overlay');
7070

7171
await driver.clickElement('[data-testid="import-token-button"]');
72+
await driver.clickElement('[data-testid="importTokens"]');
7273

7374
await driver.fill('input[placeholder="Search tokens"]', 'cha');
7475

test/e2e/tests/tokens/token-details.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('Token Details', function () {
2828

2929
const importToken = async (driver: Driver) => {
3030
await driver.clickElement(`[data-testid="import-token-button"]`);
31+
await driver.clickElement(`[data-testid="importTokens"]`);
3132
await clickNestedButton(driver, 'Custom token');
3233
await driver.fill(
3334
'[data-testid="import-tokens-modal-custom-address"]',

test/e2e/tests/tokens/token-list.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('Token List', function () {
2828

2929
const importToken = async (driver: Driver) => {
3030
await driver.clickElement(`[data-testid="import-token-button"]`);
31+
await driver.clickElement(`[data-testid="importTokens"]`);
3132
await clickNestedButton(driver, 'Custom token');
3233
await driver.fill(
3334
'[data-testid="import-tokens-modal-custom-address"]',

test/e2e/tests/tokens/token-sort.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ describe('Token List', function () {
2626

2727
const importToken = async (driver: Driver) => {
2828
await driver.clickElement(`[data-testid="import-token-button"]`);
29+
await driver.clickElement(`[data-testid="importTokens"]`);
2930
await clickNestedButton(driver, 'Custom token');
3031
await driver.fill(
3132
'[data-testid="import-tokens-modal-custom-address"]',

ui/components/app/assets/asset-list/asset-list-control-bar/index.scss

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
justify-content: space-between;
1111
width: auto;
1212
min-width: auto;
13-
max-width: none;
1413
border-radius: 8px;
1514
padding: 0 8px !important;
1615
gap: 5px;

ui/components/app/assets/asset-list/asset-list.test.tsx

+32-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import React from 'react';
22
import { screen, act, waitFor } from '@testing-library/react';
3+
import configureMockStore from 'redux-mock-store';
4+
import thunk from 'redux-thunk';
35
import { renderWithProvider } from '../../../../../test/jest';
4-
import configureStore, { MetaMaskReduxState } from '../../../../store/store';
6+
import { MetaMaskReduxState } from '../../../../store/store';
57
import mockState from '../../../../../test/data/mock-state.json';
68
import { CHAIN_IDS } from '../../../../../shared/constants/network';
79
import { useIsOriginalNativeTokenSymbol } from '../../../../hooks/useIsOriginalNativeTokenSymbol';
10+
import useMultiPolling from '../../../../hooks/useMultiPolling';
811
import { getTokenSymbol } from '../../../../store/actions';
912
import { getSelectedInternalAccountFromMockState } from '../../../../../test/jest/mocks';
1013
import { mockNetworkState } from '../../../../../test/stub/networks';
@@ -64,11 +67,19 @@ jest.mock('../../../../hooks/useIsOriginalNativeTokenSymbol', () => {
6467
jest.mock('../../../../store/actions', () => {
6568
return {
6669
getTokenSymbol: jest.fn(),
70+
setTokenNetworkFilter: jest.fn(() => ({
71+
type: 'TOKEN_NETWORK_FILTER',
72+
})),
6773
tokenBalancesStartPolling: jest.fn().mockResolvedValue('pollingToken'),
6874
tokenBalancesStopPollingByPollingToken: jest.fn(),
6975
};
7076
});
7177

78+
jest.mock('../../../../hooks/useMultiPolling', () => ({
79+
__esModule: true,
80+
default: jest.fn(),
81+
}));
82+
7283
const mockSelectedInternalAccount = getSelectedInternalAccountFromMockState(
7384
mockState as unknown as MetaMaskReduxState,
7485
);
@@ -103,14 +114,30 @@ const render = (balance = ETH_BALANCE, chainId = CHAIN_IDS.MAINNET) => {
103114
},
104115
},
105116
};
106-
const store = configureStore(state);
117+
const store = configureMockStore([thunk])(state);
107118
return renderWithProvider(
108119
<AssetList onClickAsset={() => undefined} showTokensLinks />,
109120
store,
110121
);
111122
};
112123

113124
describe('AssetList', () => {
125+
(useMultiPolling as jest.Mock).mockClear();
126+
127+
// Mock implementation for useMultiPolling
128+
(useMultiPolling as jest.Mock).mockImplementation(({ input }) => {
129+
// Mock startPolling and stopPollingByPollingToken for each input
130+
const startPolling = jest.fn().mockResolvedValue('mockPollingToken');
131+
const stopPollingByPollingToken = jest.fn();
132+
133+
input.forEach((inputItem: string) => {
134+
const key = JSON.stringify(inputItem);
135+
// Simulate returning a unique token for each input
136+
startPolling.mockResolvedValueOnce(`mockToken-${key}`);
137+
});
138+
139+
return { startPolling, stopPollingByPollingToken };
140+
});
114141
(useIsOriginalNativeTokenSymbol as jest.Mock).mockReturnValue(true);
115142

116143
(getTokenSymbol as jest.Mock).mockImplementation(async (address) => {
@@ -126,13 +153,14 @@ describe('AssetList', () => {
126153
return null;
127154
});
128155

129-
it('renders AssetList component and shows Refresh List text', async () => {
156+
it('renders AssetList component and shows AssetList control bar', async () => {
130157
await act(async () => {
131158
render();
132159
});
133160

134161
await waitFor(() => {
135-
expect(screen.getByText('Refresh list')).toBeInTheDocument();
162+
expect(screen.getByTestId('sort-by-popover-toggle')).toBeInTheDocument();
163+
expect(screen.getByTestId('import-token-button')).toBeInTheDocument();
136164
});
137165
});
138166
});

0 commit comments

Comments
 (0)