diff --git a/src/assets/new-project/assets/js/code-editor.js b/src/assets/new-project/assets/js/code-editor.js index 22de25f873..77cb07768b 100644 --- a/src/assets/new-project/assets/js/code-editor.js +++ b/src/assets/new-project/assets/js/code-editor.js @@ -197,7 +197,7 @@ function _updateProBranding() { // Get plan info from window.top.Phoenix.pro.plan const planInfo = window.top.Phoenix && window.top.Phoenix.pro && window.top.Phoenix.pro.plan; - if (planInfo && planInfo.paidSubscriber) { + if (planInfo && planInfo.isSubscriber) { // Hide free title, show pro title $freeTitle.addClass('forced-hidden'); $proTitle.removeClass('forced-hidden'); diff --git a/src/index.html b/src/index.html index 830fa6a5e5..24d37ffa98 100644 --- a/src/index.html +++ b/src/index.html @@ -447,8 +447,10 @@ } async function _startRequireLoop() { - // If running on localhost, fetch proxy config to get dynamic account URL - if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { + // If running on localhost, `npm run serve`, `npm run serveLocalAccount` targets puts in a fetch proxy. + // tTe proxy helps work around cookie domain set by phcode.dev as the dev urls are localhost. so to use + // either the actual services endpoint or localhost endpoints in dev, this is needed. + if (!Phoenix.isTestWindow && (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')) { try { const response = await fetch('/proxy/config'); if (response.ok) { diff --git a/src/services/EntitlementsManager.js b/src/services/EntitlementsManager.js index e5b9acfacc..fb5c9d512c 100644 --- a/src/services/EntitlementsManager.js +++ b/src/services/EntitlementsManager.js @@ -87,8 +87,9 @@ define(function (require, exports, module) { /** * Get the plan details from entitlements with fallback to free plan defaults. If the user is - * in pro trial(isInProTrial API), then paidSubscriber will always be true as we need to treat user as paid. - * you should use isInProTrial API to check if user is in pro trial if some trial-related logic needs to be done. + * in pro trial(isInProTrial API), then isSubscriber will always be true as we need to treat + * user as subcriber. you should use isInProTrial API to check if user is in pro trial if some + * trial-related logic needs to be done. * @returns {Promise} Plan details object */ async function getPlanDetails() { @@ -101,6 +102,7 @@ define(function (require, exports, module) { // Fallback to free plan defaults const currentDate = Date.now(); return { + isSubscriber: false, paidSubscriber: false, name: Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE, validTill: currentDate + (FREE_PLAN_VALIDITY_DAYS * MS_IN_DAY) @@ -108,7 +110,7 @@ define(function (require, exports, module) { } /** - * Check if user is in a pro trial. IF the user is in pro trail, then `plan.paidSubscriber` will always be true. + * Check if user is in a pro trial. IF the user is in pro trail, then `plan.isSubscriber` will always be true. * @returns {Promise} True if user is in pro trial, false otherwise */ async function isInProTrial() { diff --git a/src/services/login-service.js b/src/services/login-service.js index 63da795327..d1980ac8a0 100644 --- a/src/services/login-service.js +++ b/src/services/login-service.js @@ -80,16 +80,20 @@ define(function (require, exports, module) { // Debounced trigger for entitlements changed let entitlementsChangedTimer = null; + const ENTITLEMENT_CHANGED_DEBOUNCE_WINDOW = Phoenix.isTestWindow ? 100 : 1000; + function _debounceEntitlementsChanged() { if (entitlementsChangedTimer) { // already scheduled, skip return; } + // atmost 1 entitlement changed event will be triggered in this window to prevent too many entitlment changed + // events firing. entitlementsChangedTimer = setTimeout(() => { LoginService.trigger(EVENT_ENTITLEMENTS_CHANGED); entitlementsChangedTimer = null; - }, 1000); // atmost 1 entitlement changed event will be triggered in a second + }, ENTITLEMENT_CHANGED_DEBOUNCE_WINDOW); } @@ -461,6 +465,7 @@ define(function (require, exports, module) { if(entitlements.plan && (!entitlements.plan.validTill || currentDate > entitlements.plan.validTill)) { entitlements.plan = { ...entitlements.plan, + isSubscriber: false, paidSubscriber: false, name: Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE, fullName: Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE, @@ -498,7 +503,8 @@ define(function (require, exports, module) { * ```javascript * { * plan: { - * paidSubscriber: true, // Always true for trial users + * isSubscriber: true, // Always true for trial users + * paidSubscriber: false, // if the user is a paid for the plan, or is it an unpaid promo * name: "Phoenix Pro" * fullName: "Phoenix Pro" // this can be deceptive name like "Phoenix Pro For Education" to use in * // profile popup, not main branding @@ -514,11 +520,12 @@ define(function (require, exports, module) { * ``` * * **For logged-in trial users:** - * - If remote response has `plan.paidSubscriber: false`, injects `paidSubscriber: true` + * - If remote response has `plan.isSubscriber: false`, injects `isSubscriber: true` * - Adds `isInProTrial: true` and `trialDaysRemaining` * - Injects `entitlements.liveEdit.activated: true` - * - Note: Trial users may not be actual paid subscribers, but `paidSubscriber: true` is set - * so all Phoenix code treats them as paid subscribers + * - Note: Trial users may not be actual paid subscribers, but `isSubscriber: true` is set + * so all Phoenix code treats them as subscribers. to check if they actually paid or not, use + * `paidSubscriber` field. * * **For logged-in users (full remote response):** * ```javascript @@ -529,6 +536,7 @@ define(function (require, exports, module) { * name: "Phoenix Pro", * fullName: "Phoenix Pro" // this can be deceptive name like "Phoenix Pro For Education" to use in * // profile popup, not main branding + * isSubscriber: boolean, * paidSubscriber: boolean, * validTill: number // Timestamp * }, @@ -574,7 +582,7 @@ define(function (require, exports, module) { * * // Get current entitlements * const entitlements = await LoginService.getEffectiveEntitlements(); - * if (entitlements?.plan?.paidSubscriber) { + * if (entitlements?.plan?.isSubscriber) { * // Enable pro features * } * if (entitlements?.entitlements?.liveEdit?.activated) { @@ -597,8 +605,8 @@ define(function (require, exports, module) { // now we need to grant trial, as user is entitled to trial if he is here. // User has active server plan(either with login or device license) if (serverEntitlements && serverEntitlements.plan) { - if (serverEntitlements.plan.paidSubscriber) { - // Already a paid subscriber(or has device license), return as-is + if (serverEntitlements.plan.isSubscriber) { + // Already a subscriber(or has device license), return as-is // never inject trail data in this case. return serverEntitlements; } @@ -608,7 +616,8 @@ define(function (require, exports, module) { ...serverEntitlements, plan: { ...serverEntitlements.plan, - paidSubscriber: true, + isSubscriber: true, + paidSubscriber: serverEntitlements.plan.paidSubscriber || false, name: brackets.config.main_pro_plan, fullName: brackets.config.main_pro_plan, validTill: dateNowFn() + trialDaysRemaining * MS_IN_DAY @@ -632,7 +641,8 @@ define(function (require, exports, module) { // Non-logged-in, non licensed user with trial - return synthetic entitlements return { plan: { - paidSubscriber: true, + isSubscriber: true, + paidSubscriber: false, name: brackets.config.main_pro_plan, fullName: brackets.config.main_pro_plan, validTill: dateNowFn() + trialDaysRemaining * MS_IN_DAY diff --git a/src/services/login-utils.js b/src/services/login-utils.js index cc6d0d02ab..3eec9b39cf 100644 --- a/src/services/login-utils.js +++ b/src/services/login-utils.js @@ -110,7 +110,10 @@ define(function (require, exports, module) { // Check paidSubscriber changes const currentPaidSub = current.plan && current.plan.paidSubscriber; const lastPaidSub = last.plan && last.plan.paidSubscriber; - if (currentPaidSub !== lastPaidSub) { + // Check isSubscriber changes + const currentIsSubscriber = current.plan && current.plan.isSubscriber; + const lastIsSubscriber = last.plan && last.plan.isSubscriber; + if (currentIsSubscriber !== lastIsSubscriber || currentPaidSub !== lastPaidSub) { return true; } diff --git a/src/services/profile-menu.js b/src/services/profile-menu.js index cbc4218327..36b3f50168 100644 --- a/src/services/profile-menu.js +++ b/src/services/profile-menu.js @@ -179,7 +179,7 @@ define(function (require, exports, module) { `; - } else if (effectiveEntitlements.plan && effectiveEntitlements.plan.paidSubscriber) { + } else if (effectiveEntitlements.plan && effectiveEntitlements.plan.isSubscriber) { // Device-licensed user: show Phoenix Pro branding const planName = effectiveEntitlements.plan.fullName || brackets.config.main_pro_plan; proInfoHtml = `
@@ -234,7 +234,7 @@ define(function (require, exports, module) { // Phoenix.pro is only for display purposes and should not be used to gate features. // Use kernal mode apis for trusted check of pro features. Phoenix.pro.plan = { - paidSubscriber: false, + isSubscriber: false, name: Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE, fullName: Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE }; @@ -242,13 +242,13 @@ define(function (require, exports, module) { if (entitlements && entitlements.plan){ Phoenix.pro.plan = { - paidSubscriber: entitlements.plan.paidSubscriber, + isSubscriber: entitlements.plan.isSubscriber, name: entitlements.plan.name, fullName: entitlements.plan.fullName, validTill: entitlements.plan.validTill }; } - if (entitlements && entitlements.plan && entitlements.plan.paidSubscriber) { + if (entitlements && entitlements.plan && entitlements.plan.isSubscriber) { // Pro user (paid subscriber or trial): show short name branding with `name feather icon`(not full name) let displayName = entitlements.plan.name || brackets.config.main_pro_plan; if (entitlements.isInProTrial) { @@ -384,7 +384,7 @@ define(function (require, exports, module) { // Update plan class and content based on paid subscriber status $planName.removeClass('user-plan-free user-plan-paid'); - if (entitlements.plan.paidSubscriber) { + if (entitlements.plan.isSubscriber) { // Use pro styling with feather icon for pro users (paid or trial) if (entitlements.isInProTrial) { // For trial users: separate "Phoenix Pro" with icon from "(X days left)" text @@ -596,7 +596,7 @@ define(function (require, exports, module) { const effectiveEntitlements = await KernalModeTrust.loginService.getEffectiveEntitlements(); return effectiveEntitlements && (effectiveEntitlements.isInProTrial || - (effectiveEntitlements.plan && effectiveEntitlements.plan.paidSubscriber)); + (effectiveEntitlements.plan && effectiveEntitlements.plan.isSubscriber)); } catch (error) { console.error('Failed to check Pro access status:', error); return false; @@ -611,7 +611,7 @@ define(function (require, exports, module) { const effectiveEntitlements = await KernalModeTrust.loginService.getEffectiveEntitlements(); if (effectiveEntitlements && (effectiveEntitlements.isInProTrial || - (effectiveEntitlements.plan && effectiveEntitlements.plan.paidSubscriber))) { + (effectiveEntitlements.plan && effectiveEntitlements.plan.isSubscriber))) { console.log('Profile Menu: Found Pro entitlements (trial or device license), updating branding...'); _updateBranding(effectiveEntitlements); } else { diff --git a/src/services/promotions.js b/src/services/promotions.js index 193536fb18..ff8de4c9ea 100644 --- a/src/services/promotions.js +++ b/src/services/promotions.js @@ -213,7 +213,7 @@ define(function (require, exports, module) { // getEntitlements() returns null if not logged in const entitlements = await LoginService.getEntitlements(); - return entitlements && entitlements.plan && entitlements.plan.paidSubscriber === true; + return entitlements && entitlements.plan && entitlements.plan.isSubscriber === true; } catch (error) { console.error("Error checking pro subscription:", error); return false; diff --git a/test/spec/login-browser-integ-test.js b/test/spec/login-browser-integ-test.js index 1e48af61d7..58faf469de 100644 --- a/test/spec/login-browser-integ-test.js +++ b/test/spec/login-browser-integ-test.js @@ -183,6 +183,7 @@ define(function (require, exports, module) { Date.now() + 30 * 24 * 60 * 60 * 1000; // valid for 30 days entitlementsResponse.plan = { + isSubscriber: true, paidSubscriber: true, name: "Phoenix Pro", fullName: "Phoenix Pro", @@ -196,6 +197,7 @@ define(function (require, exports, module) { }; } else { entitlementsResponse.plan = { + isSubscriber: false, paidSubscriber: false, name: "Free Plan", fullName: "Free Plan" diff --git a/test/spec/login-desktop-integ-test.js b/test/spec/login-desktop-integ-test.js index 0e46330ddf..b578720187 100644 --- a/test/spec/login-desktop-integ-test.js +++ b/test/spec/login-desktop-integ-test.js @@ -209,7 +209,8 @@ define(function (require, exports, module) { Date.now() + 30 * 24 * 60 * 60 * 1000; // valid for 30 days entitlementsResponse.plan = { - paidSubscriber: true, + isSubscriber: true, + paidSubscriber: !isDeviceIDRequest, // Educational device licenses are unpaid name: "Phoenix Pro", fullName: isDeviceIDRequest ? "Phoenix Pro Test Edu" : "Phoenix Pro", validTill: validTill @@ -222,6 +223,7 @@ define(function (require, exports, module) { }; } else { entitlementsResponse.plan = { + isSubscriber: false, paidSubscriber: false, name: "Free Plan", fullName: "Free Plan" diff --git a/test/spec/login-shared.js b/test/spec/login-shared.js index ae67d8f077..4f246f6122 100644 --- a/test/spec/login-shared.js +++ b/test/spec/login-shared.js @@ -188,8 +188,8 @@ define(function (require, exports, module) { } // Check all expected properties match - if (expectedPlan.paidSubscriber !== undefined && - planDetails.paidSubscriber !== expectedPlan.paidSubscriber) { + if (expectedPlan.isSubscriber !== undefined && + planDetails.isSubscriber !== expectedPlan.isSubscriber) { return false; } if (expectedPlan.name && planDetails.name !== expectedPlan.name) { @@ -213,6 +213,9 @@ define(function (require, exports, module) { const finalPlanDetails = await EntitlementsExports.getPlanDetails(); if (expectedPlan) { expect(finalPlanDetails).toBeDefined(); + if (expectedPlan.isSubscriber !== undefined) { + expect(finalPlanDetails.isSubscriber).toBe(expectedPlan.isSubscriber); + } if (expectedPlan.paidSubscriber !== undefined) { expect(finalPlanDetails.paidSubscriber).toBe(expectedPlan.paidSubscriber); } @@ -399,7 +402,7 @@ define(function (require, exports, module) { // Verify entitlements API consistency for logged in pro user await verifyIsInProTrialEntitlement(false, "pro user should not be in trial"); - await verifyPlanEntitlements({ paidSubscriber: true }, "pro user should have paid subscriber plan"); + await verifyPlanEntitlements({ isSubscriber: true, paidSubscriber: true }, "pro user should have paid subscriber plan"); // Check profile popup shows pro status (not trial) const $profileButton = testWindow.$("#user-profile-button"); @@ -448,7 +451,7 @@ define(function (require, exports, module) { // Verify entitlements API consistency for logged in trial user await verifyIsInProTrialEntitlement(true, "user should still be in trial after login"); - await verifyPlanEntitlements({ paidSubscriber: true }, "trial user should have paidSubscriber true"); + await verifyPlanEntitlements({ isSubscriber: true, paidSubscriber: false }, "trial user should have isSubscriber true but paidSubscriber false"); // Check profile popup shows trial status const $profileButton = testWindow.$("#user-profile-button"); @@ -530,7 +533,7 @@ define(function (require, exports, module) { await verifyProBranding(false, "no pro branding to start with"); // Verify entitlements API consistency for logged out user with expired trial - await verifyPlanEntitlements({ paidSubscriber: false, name: testWindow.Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE }, + await verifyPlanEntitlements({ isSubscriber: false, paidSubscriber: false, name: testWindow.Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE }, "free plan for logged out user with expired trial"); await verifyIsInProTrialEntitlement(false, "no trial for user with expired trial"); await verifyTrialRemainingDaysEntitlement(0, "no trial days remaining for expired trial"); @@ -543,7 +546,7 @@ define(function (require, exports, module) { await verifyProBranding(false, "after trial free user login"); // Verify entitlements API consistency for logged in free user - await verifyPlanEntitlements({ paidSubscriber: false, name: testWindow.Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE }, + await verifyPlanEntitlements({ isSubscriber: false, paidSubscriber: false, name: testWindow.Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE }, "free plan for logged in user with expired trial"); await verifyIsInProTrialEntitlement(false, "still no trial after login"); await verifyLiveEditEntitlement({ activated: false }, "live edit still deactivated after login"); @@ -589,7 +592,7 @@ define(function (require, exports, module) { await verifyProBranding(false, "no pro branding initially due to expired entitlements"); // Verify entitlements API consistency for logged out user with no trial - await verifyPlanEntitlements({ paidSubscriber: false, name: testWindow.Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE }, + await verifyPlanEntitlements({ isSubscriber: false, paidSubscriber: false, name: testWindow.Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE }, "free plan for logged out user with no trial"); await verifyIsInProTrialEntitlement(false, "no trial for logged out user"); await verifyTrialRemainingDaysEntitlement(0, "no trial days remaining"); @@ -603,7 +606,7 @@ define(function (require, exports, module) { await verifyProBranding(false, "no pro branding after login with expired entitlements"); // Verify entitlements API consistency for logged in user with expired entitlements - await verifyPlanEntitlements({ paidSubscriber: false }, + await verifyPlanEntitlements({ isSubscriber: false, paidSubscriber: false }, "expired entitlements filtered to free plan after login"); await verifyIsInProTrialEntitlement(false, "no trial for user with expired entitlements"); await verifyTrialRemainingDaysEntitlement(0, "no trial days for expired entitlements user"); @@ -654,7 +657,7 @@ define(function (require, exports, module) { await verifyProBranding(true, "pro branding initially due to active trial"); // Verify entitlements API consistency for logged out user with active trial - await verifyPlanEntitlements({ paidSubscriber: true, name: testWindow.brackets.config.main_pro_plan }, + await verifyPlanEntitlements({ isSubscriber: true, paidSubscriber: false, name: testWindow.brackets.config.main_pro_plan }, "trial plan for logged out user overrides expired entitlements"); await verifyIsInProTrialEntitlement(true, "user should be in trial initially"); await verifyTrialRemainingDaysEntitlement(10, "should have 10 trial days remaining"); @@ -668,8 +671,8 @@ define(function (require, exports, module) { await verifyProBranding(true, "pro branding after login - trial overrides expired entitlements"); // Verify entitlements API consistency for logged in user (trial overrides expired server entitlements) - await verifyPlanEntitlements({ paidSubscriber: true }, - "trial overrides expired server entitlements to show paid subscriber"); + await verifyPlanEntitlements({ isSubscriber: true, paidSubscriber: false }, + "trial overrides expired server entitlements - user is subscriber but not paid"); await verifyIsInProTrialEntitlement(true, "user should still be in trial after login"); await verifyTrialRemainingDaysEntitlement(10, "trial days should remain 10 after login"); await verifyLiveEditEntitlement({ activated: true }, "live edit should be activated via trial override"); @@ -786,9 +789,11 @@ define(function (require, exports, module) { await verifyProBranding(true, "device license shows Phoenix Pro branding in navbar"); // Verify entitlements API shows Pro access + // Note: Device licenses can be paid (paidSubscriber: true) or educational (paidSubscriber: false) + // This test uses educational license (deviceID request) so paidSubscriber should be false await verifyPlanEntitlements( - { paidSubscriber: true, name: "Phoenix Pro" }, - "device license provides Pro plan" + { isSubscriber: true, paidSubscriber: false, name: "Phoenix Pro" }, + "device license provides Pro plan (educational license is unpaid)" ); await verifyIsInProTrialEntitlement(false, "device license is not a trial"); await verifyLiveEditEntitlement({ activated: true }, "live edit activated via device license"); @@ -797,7 +802,7 @@ define(function (require, exports, module) { const rawEntitlements = await EntitlementsExports.getRawEntitlements(); expect(rawEntitlements).toBeDefined(); expect(rawEntitlements).not.toBeNull(); - expect(rawEntitlements.plan.paidSubscriber).toBe(true); + expect(rawEntitlements.plan.isSubscriber).toBe(true); // Verify login popup shows Pro branding with fullName const $profileButton = testWindow.$("#user-profile-button"); diff --git a/test/spec/login-utils-test.js b/test/spec/login-utils-test.js index 895a9ac168..cf7e8cf8f1 100644 --- a/test/spec/login-utils-test.js +++ b/test/spec/login-utils-test.js @@ -51,7 +51,7 @@ define(function (require, exports, module) { it("should return null when no validTill times exist", function () { const entitlements = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: { activated: true } } @@ -62,7 +62,7 @@ define(function (require, exports, module) { it("should return null when validTill times are in the future", function () { const entitlements = { - plan: { name: "Phoenix Pro", paidSubscriber: true, validTill: futureTime }, + plan: { name: "Phoenix Pro", isSubscriber: true, validTill: futureTime }, entitlements: { liveEdit: { activated: true, validTill: futureTime } } @@ -73,11 +73,11 @@ define(function (require, exports, module) { it("should return plan name when plan validTill is newly expired", function () { const entitlements = { - plan: { name: "Phoenix Pro", paidSubscriber: true, validTill: pastTime }, + plan: { name: "Phoenix Pro", isSubscriber: true, validTill: pastTime }, entitlements: {} }; const lastRecorded = { - plan: { name: "Phoenix Pro", paidSubscriber: true, validTill: futureTime }, + plan: { name: "Phoenix Pro", isSubscriber: true, validTill: futureTime }, entitlements: {} }; const result = LoginUtils.validTillExpired(entitlements, lastRecorded); @@ -86,11 +86,11 @@ define(function (require, exports, module) { it("should return default plan name when plan validTill is newly expired and no name", function () { const entitlements = { - plan: { paidSubscriber: true, validTill: pastTime }, + plan: { isSubscriber: true, validTill: pastTime }, entitlements: {} }; const lastRecorded = { - plan: { paidSubscriber: true, validTill: futureTime }, + plan: { isSubscriber: true, validTill: futureTime }, entitlements: {} }; const result = LoginUtils.validTillExpired(entitlements, lastRecorded); @@ -99,11 +99,11 @@ define(function (require, exports, module) { it("should return null when plan validTill was already expired", function () { const entitlements = { - plan: { name: "Phoenix Pro", paidSubscriber: true, validTill: pastTime }, + plan: { name: "Phoenix Pro", isSubscriber: true, validTill: pastTime }, entitlements: {} }; const lastRecorded = { - plan: { name: "Phoenix Pro", paidSubscriber: true, validTill: recentPastTime }, + plan: { name: "Phoenix Pro", isSubscriber: true, validTill: recentPastTime }, entitlements: {} }; const result = LoginUtils.validTillExpired(entitlements, lastRecorded); @@ -112,14 +112,14 @@ define(function (require, exports, module) { it("should return entitlement key when entitlement validTill is newly expired", function () { const entitlements = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: { activated: true, validTill: pastTime }, liveEditAI: { activated: true, validTill: futureTime } } }; const lastRecorded = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: { activated: true, validTill: futureTime }, liveEditAI: { activated: true, validTill: futureTime } @@ -131,13 +131,13 @@ define(function (require, exports, module) { it("should return null when entitlement validTill was already expired", function () { const entitlements = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: { activated: true, validTill: pastTime } } }; const lastRecorded = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: { activated: true, validTill: recentPastTime } } @@ -148,7 +148,7 @@ define(function (require, exports, module) { it("should handle missing entitlements in lastRecorded", function () { const entitlements = { - plan: { name: "Phoenix Pro", paidSubscriber: true, validTill: pastTime }, + plan: { name: "Phoenix Pro", isSubscriber: true, validTill: pastTime }, entitlements: { liveEdit: { activated: true, validTill: pastTime } } @@ -159,14 +159,14 @@ define(function (require, exports, module) { it("should skip null entitlements in loop", function () { const entitlements = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: null, liveEditAI: { activated: true, validTill: pastTime } } }; const lastRecorded = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: null, liveEditAI: { activated: true, validTill: futureTime } @@ -182,7 +182,7 @@ define(function (require, exports, module) { lang: "en", plan: { name: "Phoenix Pro", - paidSubscriber: true, + isSubscriber: true, validTill: pastTime }, profileview: { @@ -210,7 +210,7 @@ define(function (require, exports, module) { } }; const lastRecorded = { - plan: { name: "Phoenix Pro", paidSubscriber: true, validTill: futureTime }, + plan: { name: "Phoenix Pro", isSubscriber: true, validTill: futureTime }, entitlements: { liveEdit: { activated: false, validTill: futureTime }, liveEditAI: { activated: false, validTill: futureTime } @@ -225,7 +225,7 @@ define(function (require, exports, module) { isSuccess: true, plan: { name: "Phoenix Pro", - paidSubscriber: true, + isSubscriber: true, validTill: pastTime }, isInProTrial: true, @@ -240,7 +240,7 @@ define(function (require, exports, module) { } }; const lastRecorded = { - plan: { name: "Phoenix Pro", paidSubscriber: true, validTill: futureTime }, + plan: { name: "Phoenix Pro", isSubscriber: true, validTill: futureTime }, entitlements: { liveEdit: { activated: true, validTill: futureTime } } @@ -252,7 +252,7 @@ define(function (require, exports, module) { it("should test synthetic trial shape", function () { const syntheticTrial = { plan: { - paidSubscriber: true, + isSubscriber: true, name: "Phoenix Pro", validTill: pastTime }, @@ -268,7 +268,7 @@ define(function (require, exports, module) { } }; const lastRecorded = { - plan: { name: "Phoenix Pro", paidSubscriber: true, validTill: futureTime }, + plan: { name: "Phoenix Pro", isSubscriber: true, validTill: futureTime }, entitlements: { liveEdit: { activated: true, validTill: futureTime } } @@ -326,26 +326,54 @@ define(function (require, exports, module) { expect(result).toBe(true); }); + it("should return true when isSubscriber status changes", function () { + const current = { + plan: { name: "Phoenix Pro", isSubscriber: true }, + entitlements: {} + }; + const last = { + plan: { name: "Phoenix Pro", isSubscriber: false }, + entitlements: {} + }; + const result = LoginUtils.haveEntitlementsChanged(current, last); + expect(result).toBe(true); + }); + it("should return true when paidSubscriber status changes", function () { - const current = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + const current = { + plan: { name: "Phoenix Pro", isSubscriber: true, paidSubscriber: true }, entitlements: {} }; - const last = { - plan: { name: "Phoenix Pro", paidSubscriber: false }, + const last = { + plan: { name: "Phoenix Pro", isSubscriber: true, paidSubscriber: false }, entitlements: {} }; const result = LoginUtils.haveEntitlementsChanged(current, last); expect(result).toBe(true); }); + it("should return true when user goes from trial to paid (paidSubscriber changes)", function () { + // Trial user: isSubscriber true, paidSubscriber false + const trialUser = { + plan: { name: "Phoenix Pro", isSubscriber: true, paidSubscriber: false }, + entitlements: { liveEdit: { activated: true } } + }; + // Paid user: isSubscriber true, paidSubscriber true + const paidUser = { + plan: { name: "Phoenix Pro", isSubscriber: true, paidSubscriber: true }, + entitlements: { liveEdit: { activated: true } } + }; + const result = LoginUtils.haveEntitlementsChanged(paidUser, trialUser); + expect(result).toBe(true); + }); + it("should return true when plan name changes", function () { const current = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: {} }; const last = { - plan: { name: "Phoenix Basic", paidSubscriber: true }, + plan: { name: "Phoenix Basic", isSubscriber: true }, entitlements: {} }; const result = LoginUtils.haveEntitlementsChanged(current, last); @@ -354,14 +382,14 @@ define(function (require, exports, module) { it("should return true when entitlement activation changes", function () { const current = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: { activated: true }, liveEditAI: { activated: false } } }; const last = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: { activated: false }, liveEditAI: { activated: false } @@ -373,14 +401,14 @@ define(function (require, exports, module) { it("should return false when nothing has changed", function () { const current = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: { activated: true }, liveEditAI: { activated: false } } }; const last = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: { activated: true }, liveEditAI: { activated: false } @@ -403,14 +431,14 @@ define(function (require, exports, module) { it("should handle missing entitlement objects", function () { const current = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: { activated: true }, liveEditAI: null } }; const last = { - plan: { name: "Phoenix Pro", paidSubscriber: true }, + plan: { name: "Phoenix Pro", isSubscriber: true }, entitlements: { liveEdit: { activated: true }, liveEditAI: null @@ -426,7 +454,7 @@ define(function (require, exports, module) { lang: "en", plan: { name: "Phoenix Pro", - paidSubscriber: true, + isSubscriber: true, validTill: 1756625665847 }, entitlements: { @@ -439,7 +467,7 @@ define(function (require, exports, module) { lang: "en", plan: { name: "Phoenix Pro", - paidSubscriber: true, + isSubscriber: true, validTill: 1756625665847 }, entitlements: { @@ -455,7 +483,7 @@ define(function (require, exports, module) { const trialCurrent = { plan: { name: "Phoenix Pro", - paidSubscriber: true, + isSubscriber: true, validTill: 1756625665847 }, isInProTrial: true, @@ -467,7 +495,7 @@ define(function (require, exports, module) { const trialLast = { plan: { name: "Phoenix Pro", - paidSubscriber: false, + isSubscriber: false, validTill: 1756625665847 }, entitlements: { @@ -481,7 +509,7 @@ define(function (require, exports, module) { it("should test synthetic trial shape changes", function () { const syntheticCurrent = { plan: { - paidSubscriber: true, + isSubscriber: true, name: "Phoenix Pro", validTill: 1756625665847 }, diff --git a/test/spec/promotions-integ-test.js b/test/spec/promotions-integ-test.js index aae8608148..38c46dee3a 100644 --- a/test/spec/promotions-integ-test.js +++ b/test/spec/promotions-integ-test.js @@ -808,7 +808,7 @@ define(function (require, exports, module) { // Test expired plan gets reset to free plan const expiredPlanEntitlements = { plan: { - paidSubscriber: true, + isSubscriber: true, name: "Phoenix Pro", validTill: mockNow - 86400000 // 1 day ago }, @@ -817,6 +817,7 @@ define(function (require, exports, module) { LoginServiceExports._validateAndFilterEntitlements(expiredPlanEntitlements); + expect(expiredPlanEntitlements.plan.isSubscriber).toBe(false); expect(expiredPlanEntitlements.plan.paidSubscriber).toBe(false); expect(expiredPlanEntitlements.plan.name).toBe(testWindow.Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE); expect(expiredPlanEntitlements.plan.validTill).toBeGreaterThan(mockNow); @@ -824,7 +825,7 @@ define(function (require, exports, module) { // Test valid plan remains unchanged const validPlanEntitlements = { plan: { - paidSubscriber: true, + isSubscriber: true, name: "Phoenix Pro", validTill: mockNow + 86400000 // 1 day from now }, @@ -839,7 +840,7 @@ define(function (require, exports, module) { // Test missing validTill gets reset const noValidTillEntitlements = { plan: { - paidSubscriber: true, + isSubscriber: true, name: "Phoenix Pro" }, entitlements: {} @@ -847,6 +848,7 @@ define(function (require, exports, module) { LoginServiceExports._validateAndFilterEntitlements(noValidTillEntitlements); + expect(noValidTillEntitlements.plan.isSubscriber).toBe(false); expect(noValidTillEntitlements.plan.paidSubscriber).toBe(false); expect(noValidTillEntitlements.plan.name).toBe(testWindow.Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE); }); @@ -855,7 +857,7 @@ define(function (require, exports, module) { // Test expired features get deactivated const entitlementsWithExpiredFeatures = { plan: { - paidSubscriber: true, + isSubscriber: true, name: "Phoenix Pro", validTill: mockNow + 86400000 }, @@ -884,7 +886,7 @@ define(function (require, exports, module) { // Test features without validTill get deactivated (treated as expired) const entitlementsNoValidTill = { plan: { - paidSubscriber: true, + isSubscriber: true, name: "Phoenix Pro", validTill: mockNow + 86400000 }, @@ -932,7 +934,7 @@ define(function (require, exports, module) { // Test entitlements without entitlements object const noEntitlementsObj = { plan: { - paidSubscriber: true, + isSubscriber: true, name: "Phoenix Pro", validTill: mockNow + 86400000 }