Skip to content

Commit 4f9b43d

Browse files
authored
Merge pull request Expensify#88611 from Expensify/valforte/fixAccountManagerBanner
[Payment due @mkhutornyi] Fix empty Account Manager banner
2 parents 610c06f + 8fb5e5c commit 4f9b43d

5 files changed

Lines changed: 102 additions & 10 deletions

File tree

src/components/HTMLEngineProvider/HTMLRenderers/AccountManagerLinkRenderer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type AccountManagerLinkRendererProps = CustomRendererProps<TText | TPhrasing>;
1616

1717
function AccountManagerLinkRenderer({tnode, style}: AccountManagerLinkRendererProps) {
1818
const styles = useThemeStyles();
19-
const [accountManagerReportID] = useOnyx(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID);
19+
const [accountManagerReportID] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => account?.accountManagerReportID});
2020

2121
// Define link style based on context
2222
let linkStyle: StyleProp<TextStyle> = styles.link;

src/pages/inbox/AccountManagerBanner.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
88
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
99
import Navigation from '@libs/Navigation/Navigation';
1010
import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
11-
import {getParticipantsAccountIDsForDisplay, isConciergeChatReport} from '@libs/ReportUtils';
11+
import {isConciergeChatReport} from '@libs/ReportUtils';
1212
import CONST from '@src/CONST';
1313
import ONYXKEYS from '@src/ONYXKEYS';
1414
import ROUTES from '@src/ROUTES';
@@ -22,9 +22,14 @@ function AccountManagerBanner({reportID}: AccountManagerBannerProps) {
2222
const {translate} = useLocalize();
2323
const expensifyIcons = useMemoizedLazyExpensifyIcons(['Lightbulb']);
2424
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(reportID)}`);
25-
const [accountManagerReportID] = useOnyx(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID);
26-
const [accountManagerReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(accountManagerReportID)}`);
27-
const accountManagerAccountID = getParticipantsAccountIDsForDisplay(accountManagerReport, false, true)?.at(0) ?? CONST.DEFAULT_MISSING_ID;
25+
const [accountManagerData] = useOnyx(ONYXKEYS.ACCOUNT, {
26+
selector: (account) => ({
27+
accountManagerReportID: account?.accountManagerReportID,
28+
accountManagerAccountID: account?.accountManagerAccountID,
29+
}),
30+
});
31+
const accountManagerReportID = accountManagerData?.accountManagerReportID;
32+
const accountManagerAccountID = Number(accountManagerData?.accountManagerAccountID ?? CONST.DEFAULT_MISSING_ID);
2833
const [participantPersonalDetail] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {
2934
selector: personalDetailsSelector(accountManagerAccountID),
3035
});

src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,13 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) {
117117
const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
118118
const [betas] = useOnyx(ONYXKEYS.BETAS);
119119
const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST);
120-
const [accountManagerReportID] = useOnyx(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID);
121120
const workspaceCards = getAllCardsForWorkspace(workspaceAccountID, cardList, cardFeeds);
122121
const {showConfirmModal} = useConfirmModal();
123122
const isSmartLimitEnabled = isSmartLimitEnabledUtil(workspaceCards);
124123
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
125124
const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT);
126125
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
126+
const accountManagerReportID = account?.accountManagerReportID;
127127
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
128128
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
129129
const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {render, screen} from '@testing-library/react-native';
2+
import React from 'react';
3+
import Onyx from 'react-native-onyx';
4+
import {LocaleContextProvider} from '@components/LocaleContextProvider';
5+
import OnyxListItemProvider from '@components/OnyxListItemProvider';
6+
import AccountManagerBanner from '@pages/inbox/AccountManagerBanner';
7+
import ONYXKEYS from '@src/ONYXKEYS';
8+
import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
9+
10+
jest.mock('@libs/Navigation/Navigation', () => ({
11+
navigate: jest.fn(),
12+
}));
13+
14+
jest.mock('@hooks/useLazyAsset', () => ({
15+
useMemoizedLazyExpensifyIcons: jest.fn(() => ({
16+
Lightbulb: 'Lightbulb',
17+
})),
18+
}));
19+
20+
const CONCIERGE_REPORT_ID = '1';
21+
const ACCOUNT_MANAGER_REPORT_ID = '2';
22+
const ACCOUNT_MANAGER_ACCOUNT_ID = 42;
23+
const ACCOUNT_MANAGER_LOGIN = 'am@expensify.com';
24+
const ACCOUNT_MANAGER_DISPLAY_NAME = 'Jamie Manager';
25+
26+
function renderBanner() {
27+
return render(
28+
<OnyxListItemProvider>
29+
<LocaleContextProvider>
30+
<AccountManagerBanner reportID={CONCIERGE_REPORT_ID} />
31+
</LocaleContextProvider>
32+
</OnyxListItemProvider>,
33+
);
34+
}
35+
36+
describe('AccountManagerBanner', () => {
37+
beforeAll(() => {
38+
Onyx.init({keys: ONYXKEYS});
39+
});
40+
41+
beforeEach(async () => {
42+
jest.clearAllMocks();
43+
await Onyx.clear();
44+
await Onyx.merge(ONYXKEYS.CONCIERGE_REPORT_ID, CONCIERGE_REPORT_ID);
45+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${CONCIERGE_REPORT_ID}`, {
46+
reportID: CONCIERGE_REPORT_ID,
47+
});
48+
await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {
49+
[ACCOUNT_MANAGER_ACCOUNT_ID]: {
50+
accountID: ACCOUNT_MANAGER_ACCOUNT_ID,
51+
login: ACCOUNT_MANAGER_LOGIN,
52+
displayName: ACCOUNT_MANAGER_DISPLAY_NAME,
53+
},
54+
});
55+
});
56+
57+
it('renders the banner body using account.accountManagerAccountID (the OpenApp-populated field)', async () => {
58+
await Onyx.merge(ONYXKEYS.ACCOUNT, {
59+
accountManagerReportID: ACCOUNT_MANAGER_REPORT_ID,
60+
accountManagerAccountID: String(ACCOUNT_MANAGER_ACCOUNT_ID),
61+
});
62+
63+
renderBanner();
64+
await waitForBatchedUpdatesWithAct();
65+
66+
expect(screen.getByText(new RegExp(ACCOUNT_MANAGER_DISPLAY_NAME))).toBeTruthy();
67+
expect(screen.getByText(new RegExp(ACCOUNT_MANAGER_LOGIN))).toBeTruthy();
68+
});
69+
70+
it('renders nothing when account.accountManagerReportID is not set', async () => {
71+
renderBanner();
72+
await waitForBatchedUpdatesWithAct();
73+
74+
expect(screen.queryByText(new RegExp(ACCOUNT_MANAGER_DISPLAY_NAME))).toBeNull();
75+
});
76+
77+
it('does not populate the banner from the legacy top-level ACCOUNT_MANAGER_REPORT_ID key (regression guard)', async () => {
78+
// The legacy standalone key is never written to by OpenApp. If the banner regresses to reading it,
79+
// this test will start showing the banner text even though `account` is empty — and fail.
80+
await Onyx.merge(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, ACCOUNT_MANAGER_REPORT_ID);
81+
82+
renderBanner();
83+
await waitForBatchedUpdatesWithAct();
84+
85+
expect(screen.queryByText(new RegExp(ACCOUNT_MANAGER_DISPLAY_NAME))).toBeNull();
86+
});
87+
});

tests/unit/AccountManagerLinkRendererTest.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ describe('AccountManagerLinkRenderer', () => {
6363
});
6464

6565
it('should navigate to account manager chat when pressed', async () => {
66-
await Onyx.merge(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, ACCOUNT_MANAGER_REPORT_ID);
66+
await Onyx.merge(ONYXKEYS.ACCOUNT, {accountManagerReportID: ACCOUNT_MANAGER_REPORT_ID});
6767

6868
// @ts-expect-error Ignoring type errors for testing purposes
6969
render(<AccountManagerLinkRenderer tnode={createMockTNode('Account Manager')} />);
@@ -85,7 +85,7 @@ describe('AccountManagerLinkRenderer', () => {
8585
});
8686

8787
it('should handle multiple presses correctly', async () => {
88-
await Onyx.merge(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, ACCOUNT_MANAGER_REPORT_ID);
88+
await Onyx.merge(ONYXKEYS.ACCOUNT, {accountManagerReportID: ACCOUNT_MANAGER_REPORT_ID});
8989

9090
// @ts-expect-error Ignoring type errors for testing purposes
9191
render(<AccountManagerLinkRenderer tnode={createMockTNode('Account Manager')} />);
@@ -101,7 +101,7 @@ describe('AccountManagerLinkRenderer', () => {
101101
});
102102

103103
it('should update navigation when accountManagerReportID changes', async () => {
104-
await Onyx.merge(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, ACCOUNT_MANAGER_REPORT_ID);
104+
await Onyx.merge(ONYXKEYS.ACCOUNT, {accountManagerReportID: ACCOUNT_MANAGER_REPORT_ID});
105105

106106
// @ts-expect-error Ignoring type errors for testing purposes
107107
const {rerender} = render(<AccountManagerLinkRenderer tnode={createMockTNode('Account Manager')} />);
@@ -112,7 +112,7 @@ describe('AccountManagerLinkRenderer', () => {
112112
expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.REPORT_WITH_ID.getRoute(ACCOUNT_MANAGER_REPORT_ID));
113113

114114
const NEW_ACCOUNT_MANAGER_REPORT_ID = '987654321';
115-
await Onyx.merge(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, NEW_ACCOUNT_MANAGER_REPORT_ID);
115+
await Onyx.merge(ONYXKEYS.ACCOUNT, {accountManagerReportID: NEW_ACCOUNT_MANAGER_REPORT_ID});
116116

117117
// @ts-expect-error Ignoring type errors for testing purposes
118118
rerender(<AccountManagerLinkRenderer tnode={createMockTNode('Account Manager')} />);

0 commit comments

Comments
 (0)