Skip to content
Merged
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
21 changes: 14 additions & 7 deletions src/pages/settings/Wallet/ExpensifyCardPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -453,13 +453,20 @@ function ExpensifyCardPage({route}: ExpensifyCardPageProps) {
}
Navigation.navigate(ROUTES.SETTINGS_WALLET_CARD_DIGITAL_DETAILS_UPDATE_ADDRESS.getRoute(domain));
}}
limitType={card?.nameValuePairs?.limitType}
cardHintText={getCardHintText(
card?.nameValuePairs?.validFrom,
card?.nameValuePairs?.validThru,
personalDetails?.[card?.accountID ?? CONST.DEFAULT_NUMBER_ID]?.timezone?.selected,
translate,
)}
// The top-level "Limit type" row already shows the current card's limit. On combo card pages the
// revealed virtual card differs from the current (physical) card, so render its own limit here to
// avoid losing it; otherwise omit it to prevent a duplicate row for a single card.
limitType={card.cardID === currentCard.cardID ? undefined : card?.nameValuePairs?.limitType}
cardHintText={
card.cardID === currentCard.cardID
? undefined
: getCardHintText(
card?.nameValuePairs?.validFrom,
card?.nameValuePairs?.validThru,
personalDetails?.[card?.accountID ?? CONST.DEFAULT_NUMBER_ID]?.timezone?.selected,
translate,
)
}
/>
Comment thread
neil-marcellini marked this conversation as resolved.
) : (
<>
Expand Down
59 changes: 59 additions & 0 deletions tests/ui/WalletExpensifyCardPageTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {CurrentReportIDContextProvider} from '@hooks/useCurrentReportID';
import * as useResponsiveLayoutModule from '@hooks/useResponsiveLayout';
import type ResponsiveLayoutResult from '@hooks/useResponsiveLayout/types';
import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator';
import {clearRevealedVirtualCardDetails, setRevealedVirtualCardDetails} from '@libs/RevealedCardSecretsStore';
import type {SettingsNavigatorParamList} from '@navigation/types';
import ExpensifyCardPage from '@pages/settings/Wallet/ExpensifyCardPage';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -71,6 +72,8 @@ describe('ExpensifyCardPage', () => {
// Clear Onyx data and reset all mocks after each test to ensure a clean state.
await act(async () => {
await Onyx.clear();
// Clear the in-memory revealed card secrets so state doesn't leak between tests.
clearRevealedVirtualCardDetails();
});
jest.clearAllMocks();
});
Expand Down Expand Up @@ -330,4 +333,60 @@ describe('ExpensifyCardPage', () => {
unmount();
await waitForBatchedUpdatesWithAct();
});

it('should show the Limit type row only once after revealing a single virtual card', async () => {
// Sign in as a test user before running the test.
await TestHelper.signInWithTestUser();

// Add a single virtual card with a limit type set. The page renders a canonical top-level
// "Limit type" row for the current card; before this fix the revealed details section also
// rendered its own "Limit type" row, duplicating it for a single card.
await act(async () => {
await Onyx.merge(ONYXKEYS.CARD_LIST, {
[userCardID]: {
cardID: 1234,
state: CONST.EXPENSIFY_CARD.STATE.OPEN,
fundID: '12345',
domainName: 'xyz',
nameValuePairs: {
isVirtual: true,
cardTitle: 'Test Virtual Card',
limitType: CONST.EXPENSIFY_CARD.LIMIT_TYPES.SMART,
},
availableSpend: 50000,
fraud: null,
},
});
});

// Render the page with the specified card ID.
const {unmount} = renderPage(SCREENS.SETTINGS.WALLET.DOMAIN_CARD, {cardID: '1234'});

await waitForBatchedUpdatesWithAct();

// Simulate the card details being revealed, as the SCA reveal flow would, which mounts the
// revealed card details section (CardDetails).
act(() => {
setRevealedVirtualCardDetails(userCardID, {
pan: '4111111111111111',
expiration: '1225',
cvv: '123',
});
});

await waitForBatchedUpdatesWithAct();

// The revealed card number row confirms the details section is now rendered.
await waitFor(() => {
expect(screen.getByText(TestHelper.translateLocal('cardPage.cardDetails.cardNumber'))).toBeOnTheScreen();
});

// The "Limit type" row should appear exactly once — only the canonical top-level row — and
// not be duplicated by the revealed card details section for a single card.
expect(screen.getAllByText(TestHelper.translateLocal('workspace.card.issueNewCard.limitType'))).toHaveLength(1);

// Unmount the component after assertions to clean up.
unmount();
await waitForBatchedUpdatesWithAct();
});
});
Loading