Skip to content

Commit 05d32d5

Browse files
authored
Merge pull request #93761 from abzokhattab/fix/93685-submit-next-step-approve
Fix missing "Waiting to approve" next step on Submit workspaces
2 parents 35d22a6 + 6128bfd commit 05d32d5

2 files changed

Lines changed: 117 additions & 4 deletions

File tree

src/components/MoneyReportHeaderMoreContent.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import useThemeStyles from '@hooks/useThemeStyles';
1111
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
1212
import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
1313
import type {ReportsSplitNavigatorParamList, RightModalNavigatorParamList} from '@libs/Navigation/types';
14+
import {isGroupPolicy} from '@libs/PolicyUtils';
1415
import {isInvoiceReport as isInvoiceReportUtil} from '@libs/ReportUtils';
15-
import CONST from '@src/CONST';
16+
import type CONST from '@src/CONST';
1617
import ONYXKEYS from '@src/ONYXKEYS';
1718
import SCREENS from '@src/SCREENS';
1819
import type * as OnyxTypes from '@src/types/onyx';
@@ -42,10 +43,8 @@ function MoneyReportHeaderMoreContent({reportID}: MoneyReportHeaderMoreContentPr
4243
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getNonEmptyStringOnyxID(moneyRequestReport?.policyID)}`);
4344
const {shouldShowStatusBar, statusBarType} = useMoneyReportHeaderStatusBar(reportID, moneyRequestReport?.chatReportID);
4445

45-
const policyType = policy?.type;
46-
const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE;
4746
const isInvoiceReport = isInvoiceReportUtil(moneyRequestReport);
48-
const shouldShowNextStep = isFromPaidPolicy && !isInvoiceReport && !shouldShowStatusBar;
47+
const shouldShowNextStep = isGroupPolicy(policy) && !isInvoiceReport && !shouldShowStatusBar;
4948

5049
const shouldShowMoreContent = shouldShowNextStep || !!statusBarType || isReportInSearch;
5150

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import {render} from '@testing-library/react-native';
2+
import React from 'react';
3+
import type {UseOnyxResult} from 'react-native-onyx';
4+
import MoneyReportHeaderMoreContent from '@components/MoneyReportHeaderMoreContent';
5+
import MoneyReportHeaderNextStep from '@components/MoneyReportHeaderNextStep';
6+
import useMoneyReportHeaderStatusBar from '@hooks/useMoneyReportHeaderStatusBar';
7+
import useOnyx from '@hooks/useOnyx';
8+
import CONST from '@src/CONST';
9+
import ONYXKEYS from '@src/ONYXKEYS';
10+
import type {Policy, Report} from '@src/types/onyx';
11+
import createRandomPolicy from '../../utils/collections/policies';
12+
13+
const TEST_REPORT_ID = '1001';
14+
const TEST_POLICY_ID = 'policy1';
15+
16+
const report = {
17+
reportID: TEST_REPORT_ID,
18+
policyID: TEST_POLICY_ID,
19+
type: CONST.REPORT.TYPE.EXPENSE,
20+
} as Report;
21+
22+
function createOnyxResult<T>(value: NonNullable<T> | undefined): UseOnyxResult<T> {
23+
return [value, {status: 'loaded'}];
24+
}
25+
26+
// `useRoute` is only used to detect Search routes; default to a regular report route so `isReportInSearch` is false.
27+
// Spread the real module so navigation internals (e.g. createNavigationContainerRef) pulled in by the real
28+
// PolicyUtils import chain keep working.
29+
jest.mock('@react-navigation/native', () => {
30+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
31+
const actualNavigation = jest.requireActual('@react-navigation/native');
32+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
33+
return {
34+
...actualNavigation,
35+
__esModule: true,
36+
useRoute: jest.fn(() => ({name: 'report'})),
37+
};
38+
});
39+
40+
// The next-step bar is the element under test; render nothing but track whether it was mounted via the visibility gate.
41+
jest.mock('@components/MoneyReportHeaderNextStep', () => ({__esModule: true, default: jest.fn(() => null)}));
42+
jest.mock('@components/MoneyReportHeaderStatusBarSection', () => ({__esModule: true, default: () => null}));
43+
jest.mock('@components/MoneyRequestReportView/MoneyRequestReportNavigation', () => ({__esModule: true, default: () => null}));
44+
jest.mock('@components/MoneyReportTransactionThreadContext', () => ({__esModule: true, useMoneyReportTransactionThread: jest.fn(() => ({iouTransactionID: undefined}))}));
45+
46+
// Spread the real module: the live PolicyUtils import chain relies on many other ReportUtils exports.
47+
jest.mock('@libs/ReportUtils', () => {
48+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
49+
const actualReportUtils = jest.requireActual('@libs/ReportUtils');
50+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
51+
return {
52+
...actualReportUtils,
53+
__esModule: true,
54+
isInvoiceReport: jest.fn(() => false),
55+
};
56+
});
57+
58+
jest.mock('@hooks/useThemeStyles', () => ({__esModule: true, default: jest.fn(() => ({}))}));
59+
jest.mock('@hooks/useResponsiveLayout', () => ({__esModule: true, default: jest.fn(() => ({shouldUseNarrowLayout: false, isMediumScreenWidth: false}))}));
60+
jest.mock('@hooks/useResponsiveLayoutOnWideRHP', () => ({__esModule: true, default: jest.fn(() => ({isWideRHPDisplayedOnWideLayout: false, isSuperWideRHPDisplayedOnWideLayout: false}))}));
61+
jest.mock('@hooks/useMoneyReportHeaderStatusBar', () => ({__esModule: true, default: jest.fn(() => ({shouldShowStatusBar: false, statusBarType: undefined}))}));
62+
jest.mock('@hooks/useOnyx', () => jest.fn());
63+
64+
const mockedUseOnyx = jest.mocked(useOnyx);
65+
const mockedStatusBar = jest.mocked(useMoneyReportHeaderStatusBar);
66+
const mockedNextStepBar = jest.mocked(MoneyReportHeaderNextStep);
67+
68+
let policyValue: Policy | undefined;
69+
70+
function mockPolicyType(type: Policy['type']) {
71+
policyValue = {...createRandomPolicy(1, type), id: TEST_POLICY_ID};
72+
}
73+
74+
describe('MoneyReportHeaderMoreContent', () => {
75+
beforeEach(() => {
76+
jest.clearAllMocks();
77+
policyValue = undefined;
78+
mockedStatusBar.mockReturnValue({shouldShowStatusBar: false, statusBarType: undefined});
79+
mockedUseOnyx.mockImplementation((key) => {
80+
if (key === `${ONYXKEYS.COLLECTION.REPORT}${TEST_REPORT_ID}`) {
81+
return createOnyxResult<Report>(report);
82+
}
83+
if (key === `${ONYXKEYS.COLLECTION.POLICY}${TEST_POLICY_ID}`) {
84+
return createOnyxResult<Policy>(policyValue);
85+
}
86+
return createOnyxResult(undefined);
87+
});
88+
});
89+
90+
it('renders the next step bar for a Submit workspace', () => {
91+
mockPolicyType(CONST.POLICY.TYPE.SUBMIT);
92+
render(<MoneyReportHeaderMoreContent reportID={TEST_REPORT_ID} />);
93+
expect(mockedNextStepBar).toHaveBeenCalled();
94+
});
95+
96+
it('renders the next step bar for a paid (team) workspace', () => {
97+
mockPolicyType(CONST.POLICY.TYPE.TEAM);
98+
render(<MoneyReportHeaderMoreContent reportID={TEST_REPORT_ID} />);
99+
expect(mockedNextStepBar).toHaveBeenCalled();
100+
});
101+
102+
it('does not render the next step bar for a personal workspace', () => {
103+
mockPolicyType(CONST.POLICY.TYPE.PERSONAL);
104+
render(<MoneyReportHeaderMoreContent reportID={TEST_REPORT_ID} />);
105+
expect(mockedNextStepBar).not.toHaveBeenCalled();
106+
});
107+
108+
it('does not render the next step bar when a status bar is shown', () => {
109+
mockPolicyType(CONST.POLICY.TYPE.SUBMIT);
110+
mockedStatusBar.mockReturnValue({shouldShowStatusBar: true, statusBarType: CONST.REPORT.STATUS_BAR_TYPE.ON_HOLD});
111+
render(<MoneyReportHeaderMoreContent reportID={TEST_REPORT_ID} />);
112+
expect(mockedNextStepBar).not.toHaveBeenCalled();
113+
});
114+
});

0 commit comments

Comments
 (0)