Skip to content

Commit e3a844d

Browse files
committed
fix: test
1 parent f17ee42 commit e3a844d

3 files changed

Lines changed: 52 additions & 7 deletions

File tree

src/libs/Violations/ViolationsUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ const ViolationsUtils = {
239239

240240
let newTransactionViolations = [...transactionViolations];
241241

242-
const shouldShowSmartScanFailedError = !isScanning && isScanRequest && isPartialTransaction;
242+
const shouldShowSmartScanFailedError = isScanRequest && updatedTransaction.receipt?.state === CONST.IOU.RECEIPT_STATE.SCAN_FAILED;
243243
const hasSmartScanFailedError = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.SMARTSCAN_FAILED);
244244
if (shouldShowSmartScanFailedError && !hasSmartScanFailedError) {
245245
return {

src/pages/workspace/WorkspaceOverviewPage.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import {useFocusEffect} from '@react-navigation/native';
22
import React, {useCallback, useContext, useEffect, useRef, useState} from 'react';
33
import type {ImageStyle, StyleProp} from 'react-native';
44
import {Image, StyleSheet, View} from 'react-native';
5+
import type {ValueOf} from 'type-fest';
56
import Avatar from '@components/Avatar';
67
import AvatarWithImagePicker from '@components/AvatarWithImagePicker';
78
import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu';
89
import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types';
910
import ConfirmModal from '@components/ConfirmModal';
10-
import {FallbackWorkspaceAvatar, ImageCropSquareMask, QrCode, Trashcan, UserPlus} from '@components/Icon/Expensicons';
11+
import {FallbackWorkspaceAvatar, ImageCropSquareMask, QrCode, Transfer, Trashcan, UserPlus} from '@components/Icon/Expensicons';
1112
import {Building} from '@components/Icon/Illustrations';
1213
import {LockedAccountContext} from '@components/LockedAccountModalProvider';
1314
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
@@ -21,7 +22,7 @@ import usePayAndDowngrade from '@hooks/usePayAndDowngrade';
2122
import useResponsiveLayout from '@hooks/useResponsiveLayout';
2223
import useThemeIllustrations from '@hooks/useThemeIllustrations';
2324
import useThemeStyles from '@hooks/useThemeStyles';
24-
import {clearInviteDraft} from '@libs/actions/Policy/Member';
25+
import {clearInviteDraft, clearWorkspaceOwnerChangeFlow, requestWorkspaceOwnerChange} from '@libs/actions/Policy/Member';
2526
import {
2627
calculateBillNewDot,
2728
clearAvatarErrors,
@@ -39,6 +40,7 @@ import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavig
3940
import type {WorkspaceSplitNavigatorParamList} from '@libs/Navigation/types';
4041
import {getUserFriendlyWorkspaceType, goBackFromInvalidPolicy, isPolicyAdmin as isPolicyAdminPolicyUtils, isPolicyOwner} from '@libs/PolicyUtils';
4142
import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils';
43+
import shouldRenderTransferOwnerButton from '@libs/shouldRenderTransferOwnerButton';
4244
import StringUtils from '@libs/StringUtils';
4345
import {shouldCalculateBillNewDot} from '@libs/SubscriptionUtils';
4446
import {getFullSizeAvatar} from '@libs/UserUtils';
@@ -70,6 +72,7 @@ function WorkspaceOverviewPage({policyDraft, policy: policyProp, route}: Workspa
7072

7173
// When we create a new workspace, the policy prop will be empty on the first render. Therefore, we have to use policyDraft until policy has been set in Onyx.
7274
const policy = policyDraft?.id ? policyDraft : policyProp;
75+
7376
const isPolicyAdmin = isPolicyAdminPolicyUtils(policy);
7477
const outputCurrency = policy?.outputCurrency ?? '';
7578
const currencySymbol = currencyList?.[outputCurrency]?.symbol ?? '';
@@ -221,6 +224,15 @@ function WorkspaceOverviewPage({policyDraft, policy: policyProp, route}: Workspa
221224
Navigation.popToSidebar();
222225
};
223226

227+
const startChangeOwnershipFlow = useCallback(() => {
228+
if (!policy?.id || !policy?.ownerAccountID) {
229+
return;
230+
}
231+
clearWorkspaceOwnerChangeFlow(policy.id);
232+
requestWorkspaceOwnerChange(policy.id);
233+
Navigation.navigate(ROUTES.WORKSPACE_OWNER_CHANGE_CHECK.getRoute(policy.id, policy.ownerAccountID, 'amountOwed' as ValueOf<typeof CONST.POLICY.OWNERSHIP_ERRORS>));
234+
}, [policy]);
235+
224236
const getHeaderButtons = () => {
225237
if (readOnly) {
226238
return null;
@@ -258,6 +270,14 @@ function WorkspaceOverviewPage({policyDraft, policy: policyProp, route}: Workspa
258270
shouldCloseModalOnSelect: !shouldCalculateBillNewDot(),
259271
});
260272
}
273+
if (isPolicyAdmin && !isOwner && shouldRenderTransferOwnerButton()) {
274+
secondaryActions.push({
275+
value: 'transferOwner',
276+
text: translate('workspace.people.transferOwner'),
277+
icon: Transfer,
278+
onSelected: startChangeOwnershipFlow,
279+
});
280+
}
261281
return (
262282
<View style={[!shouldUseNarrowLayout && styles.flexRow, !shouldUseNarrowLayout && styles.gap2]}>
263283
<ButtonWithDropdownMenu

tests/unit/ViolationUtilsTest.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,14 @@ describe('getViolationsOnyxData', () => {
282282
expect(result.value).not.toContainEqual(categoryOutOfPolicyViolation);
283283
});
284284

285-
it('should not add a category violation when the transaction is partial', () => {
286-
const partialTransaction = {...transaction, amount: 0, merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, category: undefined};
285+
it('should not add a category violation when the transaction is scanning', () => {
286+
const partialTransaction = {
287+
...transaction,
288+
amount: 0,
289+
merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT,
290+
category: undefined,
291+
receipt: {state: CONST.IOU.RECEIPT_STATE.SCANNING},
292+
};
287293
const result = ViolationsUtils.getViolationsOnyxData(partialTransaction, transactionViolations, policy, policyTags, policyCategories, false, false);
288294
expect(result.value).not.toContainEqual(missingCategoryViolation);
289295
});
@@ -307,6 +313,19 @@ describe('getViolationsOnyxData', () => {
307313

308314
expect(result.value).toEqual(expect.arrayContaining([missingCategoryViolation, ...transactionViolations]));
309315
});
316+
317+
it('should only return smartscanFailed violation for smart scan failed transactions', () => {
318+
const partialTransaction = {
319+
...transaction,
320+
amount: 0,
321+
merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT,
322+
category: undefined,
323+
iouRequestType: CONST.IOU.REQUEST_TYPE.SCAN,
324+
receipt: {state: CONST.IOU.RECEIPT_STATE.SCAN_FAILED},
325+
};
326+
const result = ViolationsUtils.getViolationsOnyxData(partialTransaction, transactionViolations, policy, policyTags, policyCategories, false, false);
327+
expect(result.value).toEqual([{name: CONST.VIOLATIONS.SMARTSCAN_FAILED, type: CONST.VIOLATION_TYPES.WARNING, showInReview: true}]);
328+
});
310329
});
311330

312331
describe('policy does not require Categories', () => {
@@ -361,8 +380,14 @@ describe('getViolationsOnyxData', () => {
361380
expect(result.value).toEqual([]);
362381
});
363382

364-
it('should not add a tag violation when the transaction is partial', () => {
365-
const partialTransaction = {...transaction, amount: 0, merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, tag: undefined};
383+
it('should not add a tag violation when the transaction is scanning', () => {
384+
const partialTransaction = {
385+
...transaction,
386+
amount: 0,
387+
merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT,
388+
tag: undefined,
389+
receipt: {state: CONST.IOU.RECEIPT_STATE.SCANNING},
390+
};
366391
const result = ViolationsUtils.getViolationsOnyxData(partialTransaction, transactionViolations, policy, policyTags, policyCategories, false, false);
367392
expect(result.value).not.toContainEqual(missingTagViolation);
368393
});

0 commit comments

Comments
 (0)