Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
558f6ea
refactor(5564): migrate confirmation and settings page to v5-compat
DDDDDanica Nov 4, 2025
6e64965
Merge branch 'main' into refactor/settings-v5-compat
DDDDDanica Nov 4, 2025
020aa7e
refactor(5564): fix Navigation mocks ignored in Suggestion components…
DDDDDanica Nov 4, 2025
bd0b495
refactor(5564): fix lint and unit/e2e tests
DDDDDanica Nov 5, 2025
2ea4539
Merge remote-tracking branch 'origin/main' into refactor/settings-v5-…
DDDDDanica Nov 5, 2025
08ff7c2
refactor(5564): fix missing currentPathname breaks navigation guard l…
DDDDDanica Nov 5, 2025
aad0f27
refactor: fix lint and e2e test Confirmation Navigation navigates bet…
DDDDDanica Nov 5, 2025
7e72a7a
Merge remote-tracking branch 'origin/main' into refactor/settings-v5-…
DDDDDanica Nov 6, 2025
a1d2e59
refactor(5564): fix bridge navigate function to add state
DDDDDanica Nov 7, 2025
7032807
Merge remote-tracking branch 'origin/refactor/settings-v5-compat' int…
DDDDDanica Nov 7, 2025
24096d6
Merge branch 'main' into refactor/settings-v5-compat
DDDDDanica Nov 7, 2025
e761117
refactor(5564): try to fix e2e tests
DDDDDanica Nov 7, 2025
a06ac1f
refactor(5564): Fixed confirmation navigation issue and migrate rest …
DDDDDanica Nov 7, 2025
dc5dfc9
Merge remote-tracking branch 'origin/main' into refactor/settings-v5-…
DDDDDanica Nov 7, 2025
e7845ea
refactor(5564): fix nested v5-compat Routes in v5 context and optimiz…
DDDDDanica Nov 7, 2025
5a2346c
refactor(5564): fix Infinite navigation loop in useSyncConfirmPath hook
DDDDDanica Nov 7, 2025
b5a5535
refactor(5564): fix Missing queryString in navigateToConfirmation call
DDDDDanica Nov 7, 2025
2ea20b3
refactor: add comment to change of assertion
DDDDDanica Nov 7, 2025
7111c2f
refactor: simplify confirm-transaction-switch.container.js
DDDDDanica Nov 9, 2025
af3d374
refactor: refactor renderWithConfirmContextProvider
DDDDDanica Nov 10, 2025
f3064da
refactor: replace / with DEFAULT_ROUTE
DDDDDanica Nov 10, 2025
426ccc9
refactor: refactor extractIdFromPathname and add unit tests
DDDDDanica Nov 10, 2025
4ad2742
Merge remote-tracking branch 'origin/main' into refactor/settings-v5-…
DDDDDanica Nov 10, 2025
ae8bbe7
refactor: refactor new migrated route to use createV5CompatRoute
DDDDDanica Nov 10, 2025
5dcfefd
refactor: prevent useEffect Dependency Over-Execution
DDDDDanica Nov 10, 2025
c9f24d1
refactor: fix navigation Optimization Flawed, Causes Unnecessary Navi…
DDDDDanica Nov 11, 2025
e6d0d34
refactor: remove providedConfirmationId Parameter
DDDDDanica Nov 11, 2025
90d8d81
Revert "refactor: remove providedConfirmationId Parameter"
DDDDDanica Nov 11, 2025
ae84bb2
refactor: fix Nested Routes: Path Mismatch Disrupts Routing
DDDDDanica Nov 11, 2025
a27cf37
refactor: fix v5-compat: Nested Route Matching Fails
DDDDDanica Nov 11, 2025
9f94d58
refactor: Fixed: Reverted to Absolute Paths in Nested Routes
DDDDDanica Nov 12, 2025
d22aabd
Merge remote-tracking branch 'origin/main' into refactor/settings-v5-…
DDDDDanica Nov 13, 2025
bd32c8c
refactor: fix lint and unit tests
DDDDDanica Nov 13, 2025
790aa9d
refactor: fix Bug: Nested Routes: Pathing Mismatch
DDDDDanica Nov 13, 2025
0ca70d5
refactor: modify navigate(-1) to DEFAULT_ROUTE
DDDDDanica Nov 13, 2025
4677b9f
refactor(5564): fix Broken Back Navigation Across Flows
DDDDDanica Nov 13, 2025
75cf1ee
refactor: revert all changes and stick to original working path
DDDDDanica Nov 13, 2025
d06f499
Merge remote-tracking branch 'origin/main' into refactor/settings-v5-…
DDDDDanica Nov 13, 2025
bf823b3
refactor: fix Lingering Merge Conflicts Cripple Functionality
DDDDDanica Nov 13, 2025
9b23f52
Merge remote-tracking branch 'origin/main' into refactor/settings-v5-…
DDDDDanica Nov 13, 2025
9ac561b
refactor: remove unneeded component props for new wrapper
DDDDDanica Nov 13, 2025
889943c
refactor: fix missing Hook Call Breaks Navigatio
DDDDDanica Nov 13, 2025
9b0daa7
Merge branch 'main' into refactor/settings-v5-compat
DDDDDanica Nov 14, 2025
64b1ea4
Merge branch 'main' into refactor/settings-v5-compat
DDDDDanica Nov 14, 2025
24b27ab
Merge branch 'main' into refactor/settings-v5-compat
DDDDDanica Nov 14, 2025
4e1d964
Merge remote-tracking branch 'origin/main' into refactor/settings-v5-…
DDDDDanica Nov 16, 2025
c2d1877
Merge remote-tracking branch 'origin/main' into refactor/settings-v5-…
DDDDDanica Nov 18, 2025
975a04a
refactor: fix lint
DDDDDanica Nov 18, 2025
4992d67
Revert: Return to last working state at d22aabdb89
DDDDDanica Nov 18, 2025
f45a154
Merge remote-tracking branch 'origin/main' into refactor/settings-v5-…
DDDDDanica Nov 18, 2025
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
19 changes: 15 additions & 4 deletions test/lib/confirmations/render-helpers.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import React, { ReactChildren, ReactElement } from 'react';

import { ConfirmContextProvider } from '../../../ui/pages/confirmations/context/confirm';
import { renderHookWithProvider, renderWithProvider } from '../render-helpers';
import {
renderHookWithProvider,
renderWithProvider,
} from '../render-helpers-navigate';

export function renderWithConfirmContextProvider(
component: ReactElement,
store: unknown,
pathname = '/',
confirmationId?: string,
) {
return renderWithProvider(
<ConfirmContextProvider>{component}</ConfirmContextProvider>,
<ConfirmContextProvider confirmationId={confirmationId}>
{component}
</ConfirmContextProvider>,
store,
pathname,
);
Expand All @@ -23,14 +29,19 @@ export function renderHookWithConfirmContextProvider(
// TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31973
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Container?: any,
confirmationId?: string,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Prevent Default Route Drift

The renderHookWithConfirmContextProvider function has an inconsistent default value for the pathname parameter. It uses '/' as the default while renderWithConfirmContextProvider uses DEFAULT_ROUTE (which equals '/'). More importantly, this inconsistency could cause subtle issues when the default route constant is later changed, as one function would be updated while the other wouldn't be. For consistency with the other helper function and to follow the PR's pattern of using constants throughout, this should be DEFAULT_ROUTE.

Fix in Cursor Fix in Web

) {
const contextContainer = Container
? ({ children }: { children: ReactChildren }) => (
<ConfirmContextProvider>
<ConfirmContextProvider confirmationId={confirmationId}>
<Container>{children}</Container>
</ConfirmContextProvider>
)
: ConfirmContextProvider;
: ({ children }: { children: ReactChildren }) => (
<ConfirmContextProvider confirmationId={confirmationId}>
{children}
</ConfirmContextProvider>
);

return renderHookWithProvider(hook, state, pathname, contextContainer);
}
7 changes: 6 additions & 1 deletion test/lib/render-helpers-navigate.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,12 @@ export function renderHookWithProvider(hook, state, pathname = '/', Container) {
)
: ProviderWrapper;

return renderHook(hook, { wrapper });
const hookResult = renderHook(hook, { wrapper });

return {
...hookResult,
store,
};
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ import PermissionConnectHeader from './permission-connect-header';

const STORE_MOCK = configureMockStore()({ metamask: { pendingApprovals: {} } });

const mockUseNavigate = jest.fn();
const mockUseLocation = jest.fn();
jest.mock('react-router-dom-v5-compat', () => {
return {
...jest.requireActual('react-router-dom-v5-compat'),
useNavigate: () => mockUseNavigate,
useLocation: () => mockUseLocation(),
};
});

describe('Permission Connect Header', () => {
const mockOriginData = {
origin: 'https://metamask.github.io',
Expand All @@ -14,6 +24,17 @@ describe('Permission Connect Header', () => {
const expectedTitle = 'metamask.github.io';
const expectedAltImageText = 'metamask.github.io logo';

beforeEach(() => {
jest.clearAllMocks();
mockUseLocation.mockReturnValue({
pathname: '/',
search: '',
hash: '',
state: null,
key: 'test',
});
});

it('renders permission connect header', () => {
const { getByAltText } = renderWithProvider(
<PermissionConnectHeader
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { Navigate } from 'react-router-dom-v5-compat';
import { useLocation } from 'react-router-dom';
import { Navigate, useLocation } from 'react-router-dom-v5-compat';
import { UNLOCK_ROUTE, ONBOARDING_ROUTE } from '../../constants/routes';

type AuthenticatedV5CompatProps = {
Expand Down
38 changes: 28 additions & 10 deletions ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useCallback, useContext, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory, useLocation } from 'react-router-dom';
import { useNavigate, useLocation } from 'react-router-dom-v5-compat';
import { providerErrors, serializeError } from '@metamask/rpc-errors';
import { getTokenTrackerLink } from '@metamask/etherscan-link';
import classnames from 'classnames';
Expand Down Expand Up @@ -63,12 +64,18 @@ import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
import { Nav } from '../confirmations/components/confirm/nav';
import { hideAppHeader } from '../routes/utils';

const ConfirmAddSuggestedNFT = () => {
const ConfirmAddSuggestedNFT = ({
navigate: routeNavigate,
location: routeLocation,
} = {}) => {
const t = useContext(I18nContext);
const dispatch = useDispatch();
const history = useHistory();
const hookNavigate = useNavigate();
const hookLocation = useLocation();
// Use navigate/location from props (v5 route) if available, otherwise fall back to hooks (v6)
const navigate = routeNavigate || hookNavigate;
const location = routeLocation || hookLocation;

const location = useLocation();
const hasAppHeader = location?.pathname ? !hideAppHeader({ location }) : true;

const classNames = classnames('confirm-add-suggested-nft page-container', {
Expand Down Expand Up @@ -126,8 +133,8 @@ const ConfirmAddSuggestedNFT = () => {
});
}),
);
history.push(mostRecentOverviewPage);
}, [dispatch, history, trackEvent, mostRecentOverviewPage, suggestedNfts]);
navigate(mostRecentOverviewPage);
}, [dispatch, navigate, trackEvent, mostRecentOverviewPage, suggestedNfts]);

const handleCancelNftClick = useCallback(async () => {
await Promise.all(
Expand All @@ -140,17 +147,17 @@ const ConfirmAddSuggestedNFT = () => {
);
}),
);
history.push(mostRecentOverviewPage);
}, [dispatch, history, mostRecentOverviewPage, suggestedNfts]);
navigate(mostRecentOverviewPage);
}, [dispatch, navigate, mostRecentOverviewPage, suggestedNfts]);

useEffect(() => {
const goBackIfNoSuggestedNftsOnFirstRender = () => {
if (!suggestedNfts.length) {
history.push(mostRecentOverviewPage);
navigate(mostRecentOverviewPage);
}
};
goBackIfNoSuggestedNftsOnFirstRender();
}, [history, mostRecentOverviewPage, suggestedNfts]);
}, [navigate, mostRecentOverviewPage, suggestedNfts]);

let origin;
let link;
Expand Down Expand Up @@ -455,4 +462,15 @@ const ConfirmAddSuggestedNFT = () => {
);
};

ConfirmAddSuggestedNFT.propTypes = {
navigate: PropTypes.func,
location: PropTypes.shape({
pathname: PropTypes.string,
search: PropTypes.string,
hash: PropTypes.string,
state: PropTypes.object,
key: PropTypes.string,
}),
};

export default ConfirmAddSuggestedNFT;
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,20 @@ import {
} from '../../store/actions';
import configureStore from '../../store/store';
import mockState from '../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../test/jest/rendering';
import { renderWithProvider } from '../../../test/lib/render-helpers-navigate';
import { CHAIN_IDS } from '../../../shared/constants/network';
import { mockNetworkState } from '../../../test/stub/networks';
import * as util from '../../helpers/utils/util';
import ConfirmAddSuggestedNFT from '.';

const mockNavigate = jest.fn();
const mockUseLocation = jest.fn();
jest.mock('react-router-dom-v5-compat', () => ({
...jest.requireActual('react-router-dom-v5-compat'),
useNavigate: () => mockNavigate,
useLocation: mockUseLocation,
}));

const PENDING_NFT_APPROVALS = {
1: {
id: '1',
Expand Down Expand Up @@ -70,6 +78,15 @@ jest.mock('../../store/actions', () => ({
}));

const renderComponent = (pendingNfts = {}) => {
mockNavigate.mockClear();
mockUseLocation.mockReturnValue({
pathname: '/',
search: '',
hash: '',
key: 'test-key',
state: undefined,
});

const store = configureStore({
metamask: {
...mockState.metamask,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useCallback, useContext, useEffect, useMemo } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory, useLocation } from 'react-router-dom';
import { useNavigate, useLocation } from 'react-router-dom-v5-compat';
import classnames from 'classnames';
import { providerErrors, serializeError } from '@metamask/rpc-errors';
import {
Expand Down Expand Up @@ -83,12 +84,17 @@ function hasDuplicateSymbolAndDiffAddress(suggestedTokens, tokens) {
return Boolean(duplicate);
}

const ConfirmAddSuggestedToken = () => {
const ConfirmAddSuggestedToken = ({
navigate: routeNavigate,
location: routeLocation,
} = {}) => {
const t = useContext(I18nContext);
const dispatch = useDispatch();
const history = useHistory();
const hookNavigate = useNavigate();
const hookLocation = useLocation();
const navigate = routeNavigate || hookNavigate;
const location = routeLocation || hookLocation;

const location = useLocation();
const hasAppHeader = location?.pathname ? !hideAppHeader({ location }) : true;

const classNames = classnames('confirm-add-suggested-token page-container', {
Expand Down Expand Up @@ -154,8 +160,8 @@ const ConfirmAddSuggestedToken = () => {
});
}),
);
history.push(mostRecentOverviewPage);
}, [dispatch, history, trackEvent, mostRecentOverviewPage, suggestedTokens]);
navigate(mostRecentOverviewPage);
}, [dispatch, navigate, trackEvent, mostRecentOverviewPage, suggestedTokens]);

const handleCancelTokenClick = useCallback(async () => {
await Promise.all(
Expand All @@ -168,12 +174,12 @@ const ConfirmAddSuggestedToken = () => {
),
),
);
history.push(mostRecentOverviewPage);
}, [dispatch, history, mostRecentOverviewPage, suggestedTokens]);
navigate(mostRecentOverviewPage);
}, [dispatch, navigate, mostRecentOverviewPage, suggestedTokens]);

const goBackIfNoSuggestedTokensOnFirstRender = () => {
if (!suggestedTokens.length) {
history.push(mostRecentOverviewPage);
navigate(mostRecentOverviewPage);
}
};

Expand Down Expand Up @@ -237,4 +243,15 @@ const ConfirmAddSuggestedToken = () => {
);
};

ConfirmAddSuggestedToken.propTypes = {
navigate: PropTypes.func,
location: PropTypes.shape({
pathname: PropTypes.string,
search: PropTypes.string,
hash: PropTypes.string,
state: PropTypes.object,
key: PropTypes.string,
}),
};

export default ConfirmAddSuggestedToken;
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,20 @@ import {
rejectPendingApproval,
} from '../../store/actions';
import configureStore from '../../store/store';
import { renderWithProvider } from '../../../test/jest/rendering';
import { renderWithProvider } from '../../../test/lib/render-helpers-navigate';
import { ETH_EOA_METHODS } from '../../../shared/constants/eth-methods';
import { mockNetworkState } from '../../../test/stub/networks';
import { CHAIN_IDS } from '../../../shared/constants/network';
import ConfirmAddSuggestedToken from '.';

const mockNavigate = jest.fn();
const mockUseLocation = jest.fn();
jest.mock('react-router-dom-v5-compat', () => ({
...jest.requireActual('react-router-dom-v5-compat'),
useNavigate: () => mockNavigate,
useLocation: mockUseLocation,
}));

const PENDING_APPROVALS = {
1: {
id: '1',
Expand Down Expand Up @@ -67,6 +75,15 @@ jest.mock('../../hooks/useIsOriginalTokenSymbol', () => {
});

const renderComponent = (tokens = []) => {
mockNavigate.mockClear();
mockUseLocation.mockReturnValue({
pathname: '/',
search: '',
hash: '',
key: 'test-key',
state: undefined,
});

const store = configureStore({
metamask: {
pendingApprovals: PENDING_APPROVALS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import copyToClipboard from 'copy-to-clipboard';
import classnames from 'classnames';
import log from 'loglevel';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory, useParams } from 'react-router-dom';
import { useNavigate, useParams } from 'react-router-dom-v5-compat';
import { cloneDeep } from 'lodash';

import AccountListItem from '../../components/app/account-list-item';
Expand Down Expand Up @@ -327,7 +327,7 @@ const Footer = ({
messageData,
}) => {
const dispatch = useDispatch();
const history = useHistory();
const navigate = useNavigate();
const t = useI18nContext();
const trackEvent = useContext(MetaMetricsContext);

Expand All @@ -344,7 +344,7 @@ const Footer = ({
},
});
dispatch(clearConfirmTransaction());
history.push(mostRecentOverviewPage);
navigate(mostRecentOverviewPage);
};

const onSubmitClick = async (event) => {
Expand All @@ -362,7 +362,7 @@ const Footer = ({
},
});
dispatch(clearConfirmTransaction());
history.push(mostRecentOverviewPage);
navigate(mostRecentOverviewPage);
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import configureMockStore from 'redux-mock-store';
import { merge } from 'lodash';
import copyToClipboard from 'copy-to-clipboard';
import mockState from '../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../test/lib/render-helpers';
import { renderWithProvider } from '../../../test/lib/render-helpers-navigate';
import { flushPromises } from '../../../test/lib/timer-helpers';
import {
decryptMsg,
Expand All @@ -16,12 +16,14 @@ import ConfirmDecryptMessage from './confirm-decrypt-message.component';

const messageIdMock = '12345';

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: () => ({
id: messageIdMock,
}),
}));
jest.mock('react-router-dom-v5-compat', () => {
return {
...jest.requireActual('react-router-dom-v5-compat'),
useParams: () => ({
id: messageIdMock,
}),
};
});

const messageData = {
domain: {
Expand Down
Loading
Loading