Skip to content

Commit c391999

Browse files
davibrocclaude
andcommitted
test(bridge): fix QuoteSelectorView component view tests
Three issues corrected: - Add `setGlobalDevModeChecks({ inputStabilityCheck: 'never' })` in beforeAll to suppress the reselect false-positive thrown by selectBridgeAppState (which spreads controller slices into a new object on every call and is app code we cannot change). - Add missing `trade` field to makeEvmQuote factory; calcRelayerFee destructures { quote, trade } from the quote response and crashes when trade is absent. - Replace the post-render dispatch spy with a store state assertion. useDispatch() captures store.dispatch at mount time, so a spy set up after render is never called. Reading selectSelectedQuoteRequestId from the store after the press is the correct pattern. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 014f254 commit c391999

1 file changed

Lines changed: 57 additions & 8 deletions

File tree

app/components/UI/Bridge/components/QuoteSelectorView/QuoteSelectorView.view.test.tsx

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import '../../../../../../tests/component-view/mocks';
2+
import { setGlobalDevModeChecks } from 'reselect';
23
import { renderQuoteSelectorView } from '../../../../../../tests/component-view/renderers/quoteSelectorView';
3-
import { waitFor } from '@testing-library/react-native';
4+
import { fireEvent, waitFor } from '@testing-library/react-native';
45
import { strings } from '../../../../../../locales/i18n';
56
import { RequestStatus } from '@metamask/bridge-controller';
67
import {
@@ -9,6 +10,9 @@ import {
910
USDC_DEST,
1011
} from '../../_mocks_/bridgeViewTestConstants';
1112
import { describeForPlatforms } from '../../../../../../tests/component-view/platform';
13+
import { QUOTES_PLACEHOLDER_DATA } from './constants';
14+
import { selectSelectedQuoteRequestId } from '../../../../../core/redux/slices/bridge';
15+
import type { RootState } from '../../../../../reducers';
1216

1317
/** Minimal EVM QuoteResponse with a lifi bridge for use in BridgeController.quotes. */
1418
const makeEvmQuote = (requestId = 'req-evm-1') => ({
@@ -48,19 +52,59 @@ const makeEvmQuote = (requestId = 'req-evm-1') => ({
4852
gasIncluded: false,
4953
steps: [],
5054
},
55+
trade: {
56+
chainId: 1,
57+
to: '0x1111111254eeb25477b68fb85ed929f73a960582',
58+
from: '0x0000000000000000000000000000000000000001',
59+
value: '0x0',
60+
data: '0x',
61+
gasLimit: null,
62+
},
5163
totalNetworkFee: { amount: '0.001', valueInCurrency: '2', usd: '2' },
5264
estimatedProcessingTimeInSeconds: 30,
5365
});
5466

67+
// selectBridgeAppState spreads multiple controller slices into a new object on every call,
68+
// which causes reselect's inputStabilityCheck to throw a false positive in tests.
69+
beforeAll(() => {
70+
setGlobalDevModeChecks({ inputStabilityCheck: 'never' });
71+
});
72+
5573
describeForPlatforms('QuoteSelectorView', () => {
56-
it('renders the sort-order info text', () => {
57-
const { getByText } = renderQuoteSelectorView();
74+
it('dispatches setSelectedQuoteRequestId when user selects a quote', async () => {
75+
const now = Date.now();
76+
const quote = makeEvmQuote('req-selected');
77+
78+
const { store, findByText } = renderQuoteSelectorView({
79+
deterministicFiat: true,
80+
overrides: {
81+
bridge: {
82+
...DEFAULT_BRIDGE,
83+
},
84+
engine: {
85+
backgroundState: {
86+
BridgeController: {
87+
quotes: [quote],
88+
recommendedQuote: quote,
89+
quotesLastFetched: now,
90+
quotesLoadingStatus: 'SUCCEEDED',
91+
quotesRefreshCount: 0,
92+
quoteFetchError: null,
93+
},
94+
},
95+
},
96+
} as unknown as Record<string, unknown>,
97+
});
98+
99+
fireEvent.press(await findByText('Lifi'));
58100

59-
expect(getByText(strings('bridge.select_quote_info'))).toBeOnTheScreen();
101+
expect(
102+
selectSelectedQuoteRequestId(store.getState() as unknown as RootState),
103+
).toBe('req-selected');
60104
});
61105

62-
it('shows placeholder skeleton rows while quotes are loading', () => {
63-
const { getByText } = renderQuoteSelectorView({
106+
it('hides quote content behind Skeleton rows while quotes are loading', async () => {
107+
const { queryByText } = renderQuoteSelectorView({
64108
overrides: {
65109
engine: {
66110
backgroundState: {
@@ -77,8 +121,13 @@ describeForPlatforms('QuoteSelectorView', () => {
77121
} as unknown as Record<string, unknown>,
78122
});
79123

80-
// Info text is always visible
81-
expect(getByText(strings('bridge.select_quote_info'))).toBeOnTheScreen();
124+
// Each placeholder row's content is hidden by the Skeleton component while loading.
125+
// If skeleton rendering broke the content would become visible and these assertions would fail.
126+
await waitFor(() => {
127+
QUOTES_PLACEHOLDER_DATA.forEach(({ provider }) => {
128+
expect(queryByText(provider.name)).toBeNull();
129+
});
130+
});
82131
});
83132

84133
it('shows provider name and total cost label when quotes are available', async () => {

0 commit comments

Comments
 (0)