From dbe85f1e161642e5a2d3a1829d917e15e8d17d3c Mon Sep 17 00:00:00 2001 From: allgandalf Date: Fri, 19 Sep 2025 18:57:59 +0530 Subject: [PATCH 1/2] remove usage of onyx.connect for ONYXKEYS.ACCOUNT --- src/libs/SubscriptionUtils.ts | 11 +- src/pages/workspace/WorkspaceOverviewPage.tsx | 7 +- src/pages/workspace/WorkspacesListPage.tsx | 3 +- tests/unit/SubscriptionUtilsTest.ts | 115 ++++++++++++++++++ 4 files changed, 122 insertions(+), 14 deletions(-) diff --git a/src/libs/SubscriptionUtils.ts b/src/libs/SubscriptionUtils.ts index 1799b9c38eb3..e17f43a42cd7 100644 --- a/src/libs/SubscriptionUtils.ts +++ b/src/libs/SubscriptionUtils.ts @@ -166,15 +166,6 @@ Onyx.connect({ waitForCollectionCallback: true, }); -// Indicates if downgrading the current subscription plan is allowed for the user. -let canDowngrade = false; -Onyx.connect({ - key: ONYXKEYS.ACCOUNT, - callback: (val) => { - canDowngrade = val?.canDowngrade ?? false; - }, -}); - /** * @returns The date when the grace period ends. */ @@ -575,7 +566,7 @@ function shouldRestrictUserBillableActions(policyID: string): boolean { return false; } -function shouldCalculateBillNewDot(): boolean { +function shouldCalculateBillNewDot(canDowngrade: boolean | undefined = false): boolean { return canDowngrade && getOwnedPaidPolicies(allPolicies, currentUserAccountID).length === 1; } diff --git a/src/pages/workspace/WorkspaceOverviewPage.tsx b/src/pages/workspace/WorkspaceOverviewPage.tsx index 4f2af38348d7..6363c6959aea 100644 --- a/src/pages/workspace/WorkspaceOverviewPage.tsx +++ b/src/pages/workspace/WorkspaceOverviewPage.tsx @@ -69,6 +69,7 @@ function WorkspaceOverviewPage({policyDraft, policy: policyProp, route}: Workspa const illustrations = useThemeIllustrations(); const backTo = route.params.backTo; const [currencyList = getEmptyObject()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); + const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); const [currentUserAccountID = -1] = useOnyx(ONYXKEYS.SESSION, { selector: accountIDSelector, canBeMissing: true, @@ -211,14 +212,14 @@ function WorkspaceOverviewPage({policyDraft, policy: policyProp, route}: Workspa }, [isLoadingBill]); const onDeleteWorkspace = useCallback(() => { - if (shouldCalculateBillNewDot()) { + if (shouldCalculateBillNewDot(account?.canDowngrade)) { setIsDeletingPaidWorkspace(true); calculateBillNewDot(); return; } setIsDeleteModalOpen(true); - }, [setIsDeletingPaidWorkspace]); + }, [setIsDeletingPaidWorkspace, account?.canDowngrade]); const handleBackButtonPress = () => { if (isComingFromGlobalReimbursementsFlow) { @@ -278,7 +279,7 @@ function WorkspaceOverviewPage({policyDraft, policy: policyProp, route}: Workspa onSelected: onDeleteWorkspace, disabled: isLoadingBill, shouldShowLoadingSpinnerIcon: isLoadingBill, - shouldCloseModalOnSelect: !shouldCalculateBillNewDot(), + shouldCloseModalOnSelect: !shouldCalculateBillNewDot(account?.canDowngrade), }); } const isCurrentUserAdmin = policy?.employeeList?.[currentUserPersonalDetails?.login ?? '']?.role === CONST.POLICY.ROLE.ADMIN; diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index fb30eae0ac76..2e662638d60e 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -120,6 +120,7 @@ function WorkspacesListPage() { const isDuplicatedWorkspaceEnabled = isBetaEnabled(CONST.BETAS.DUPLICATE_WORKSPACE); const [myDomainSecurityGroups] = useOnyx(ONYXKEYS.MY_DOMAIN_SECURITY_GROUPS, {canBeMissing: true}); const [securityGroups] = useOnyx(ONYXKEYS.COLLECTION.SECURITY_GROUP, {canBeMissing: true}); + const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); // This hook preloads the screens of adjacent tabs to make changing tabs faster. usePreloadFullScreenNavigators(); @@ -169,7 +170,7 @@ function WorkspacesListPage() { setIsDeleteModalOpen(false); }; - const shouldCalculateBillNewDot: boolean = shouldCalculateBillNewDotFn(); + const shouldCalculateBillNewDot: boolean = shouldCalculateBillNewDotFn(account?.canDowngrade); const resetLoadingSpinnerIconIndex = useCallback(() => { setLoadingSpinnerIconIndex(null); diff --git a/tests/unit/SubscriptionUtilsTest.ts b/tests/unit/SubscriptionUtilsTest.ts index 2c84d844b9aa..0a4167b702e7 100644 --- a/tests/unit/SubscriptionUtilsTest.ts +++ b/tests/unit/SubscriptionUtilsTest.ts @@ -12,6 +12,7 @@ import { shouldRestrictUserBillableActions, shouldShowDiscountBanner, shouldShowPreTrialBillingBanner, + shouldCalculateBillNewDot, } from '@libs/SubscriptionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -653,4 +654,118 @@ describe('SubscriptionUtils', () => { expect(shouldShowPreTrialBillingBanner(introSelected)).toBeFalsy(); }); }); + describe('shouldCalculateBillNewDot', () => { + const testUserAccountID = 1; // A consistent account ID for tests + const paidPolicyID = '12345'; + const freePolicyID = '67890'; + const secondPaidPolicyID = '98765'; + + beforeEach(async () => { + // Clear Onyx and set up session for each test + await Onyx.clear(); + await Onyx.set(ONYXKEYS.SESSION, {email: 'test@example.com', accountID: testUserAccountID}); + // Ensure allPolicies is initialized as empty or cleared before each test + await Onyx.multiSet({ + [ONYXKEYS.COLLECTION.POLICY]: null, + }); + // Reset the mock for getOwnedPaidPolicies before each test + jest.clearAllMocks(); + }); + + it('should return false if canDowngrade is false (default or explicitly passed)', async () => { + // Set up a policy that would normally count as owned and paid + await Onyx.multiSet({ + [`${ONYXKEYS.COLLECTION.POLICY}${paidPolicyID}` as const]: { + ...createRandomPolicy(Number(paidPolicyID)), + ownerAccountID: testUserAccountID, + type: CONST.POLICY.TYPE.CORPORATE, + }, + }); + // Test with canDowngrade as false (explicitly) + expect(shouldCalculateBillNewDot(false)).toBeFalsy(); + // Test with canDowngrade as undefined (defaults to false in the function signature) + expect(shouldCalculateBillNewDot(undefined)).toBeFalsy(); + // Test without passing canDowngrade (defaults to false) + expect(shouldCalculateBillNewDot()).toBeFalsy(); + }); + + it('should return false if the user owns zero paid policies', async () => { + // Only free policies or no policies at all + await Onyx.multiSet({ + [`${ONYXKEYS.COLLECTION.POLICY}${freePolicyID}` as const]: { + ...createRandomPolicy(Number(freePolicyID)), + ownerAccountID: testUserAccountID, + type: CONST.POLICY.TYPE.PERSONAL, + }, + }); + expect(shouldCalculateBillNewDot(true)).toBeFalsy(); + }); + + it('should return false if the user owns more than one paid policy', async () => { + await Onyx.multiSet({ + [`${ONYXKEYS.COLLECTION.POLICY}${paidPolicyID}` as const]: { + ...createRandomPolicy(Number(paidPolicyID)), + ownerAccountID: testUserAccountID, + type: CONST.POLICY.TYPE.CORPORATE, + }, + [`${ONYXKEYS.COLLECTION.POLICY}${secondPaidPolicyID}` as const]: { + ...createRandomPolicy(Number(secondPaidPolicyID)), + ownerAccountID: testUserAccountID, + type: CONST.POLICY.TYPE.TEAM, + }, + }); + expect(shouldCalculateBillNewDot(true)).toBeFalsy(); + }); + + it('should return true if canDowngrade is true and the user owns exactly one paid policy', async () => { + await Onyx.multiSet({ + [`${ONYXKEYS.COLLECTION.POLICY}${paidPolicyID}` as const]: { + ...createRandomPolicy(Number(paidPolicyID)), + ownerAccountID: testUserAccountID, + type: CONST.POLICY.TYPE.CORPORATE, + }, + [`${ONYXKEYS.COLLECTION.POLICY}${freePolicyID}` as const]: { // Include a free policy to confirm it's correctly ignored + ...createRandomPolicy(Number(freePolicyID)), + ownerAccountID: testUserAccountID, + type: CONST.POLICY.TYPE.PERSONAL, + }, + }); + expect(shouldCalculateBillNewDot(true)).toBeTruthy(); + }); + + it('should return false if the user owns exactly one paid policy but is not the owner', async () => { + // Set up a paid policy owned by another user + const thirdUserAccountID = 2; + await Onyx.multiSet({ + [`${ONYXKEYS.COLLECTION.POLICY}${paidPolicyID}` as const]: { + ...createRandomPolicy(Number(paidPolicyID)), + ownerAccountID: thirdUserAccountID, // Owned by someone else + type: CONST.POLICY.TYPE.CORPORATE, + }, + }); + expect(shouldCalculateBillNewDot(true)).toBeFalsy(); + }); + + it('should return true if canDowngrade is true and the single paid policy is a team policy', async () => { + await Onyx.multiSet({ + [`${ONYXKEYS.COLLECTION.POLICY}${paidPolicyID}` as const]: { + ...createRandomPolicy(Number(paidPolicyID)), + ownerAccountID: testUserAccountID, + type: CONST.POLICY.TYPE.TEAM, + }, + }); + expect(shouldCalculateBillNewDot(true)).toBeTruthy(); + }); + + it('should return true if canDowngrade is true and the single paid policy is a corporate policy', async () => { + await Onyx.multiSet({ + [`${ONYXKEYS.COLLECTION.POLICY}${paidPolicyID}` as const]: { + ...createRandomPolicy(Number(paidPolicyID)), + ownerAccountID: testUserAccountID, + type: CONST.POLICY.TYPE.CORPORATE, + }, + }); + expect(shouldCalculateBillNewDot(true)).toBeTruthy(); + }); + }); }); From ea4ed7c50fa98d41afc69caf489b0ac1ab9bdd8f Mon Sep 17 00:00:00 2001 From: allgandalf Date: Fri, 19 Sep 2025 19:01:15 +0530 Subject: [PATCH 2/2] fix prettier --- tests/unit/SubscriptionUtilsTest.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/unit/SubscriptionUtilsTest.ts b/tests/unit/SubscriptionUtilsTest.ts index 0a4167b702e7..e8b11c2a9888 100644 --- a/tests/unit/SubscriptionUtilsTest.ts +++ b/tests/unit/SubscriptionUtilsTest.ts @@ -9,10 +9,10 @@ import { hasUserFreeTrialEnded, isUserOnFreeTrial, PAYMENT_STATUS, + shouldCalculateBillNewDot, shouldRestrictUserBillableActions, shouldShowDiscountBanner, shouldShowPreTrialBillingBanner, - shouldCalculateBillNewDot, } from '@libs/SubscriptionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -724,7 +724,8 @@ describe('SubscriptionUtils', () => { ownerAccountID: testUserAccountID, type: CONST.POLICY.TYPE.CORPORATE, }, - [`${ONYXKEYS.COLLECTION.POLICY}${freePolicyID}` as const]: { // Include a free policy to confirm it's correctly ignored + [`${ONYXKEYS.COLLECTION.POLICY}${freePolicyID}` as const]: { + // Include a free policy to confirm it's correctly ignored ...createRandomPolicy(Number(freePolicyID)), ownerAccountID: testUserAccountID, type: CONST.POLICY.TYPE.PERSONAL,