diff --git a/test/spec/login-browser-integ-test.js b/test/spec/login-browser-integ-test.js index 61c6560736..f058a5e3d6 100644 --- a/test/spec/login-browser-integ-test.js +++ b/test/spec/login-browser-integ-test.js @@ -23,6 +23,7 @@ define(function (require, exports, module) { const SpecRunnerUtils = require("spec/SpecRunnerUtils"); + const LoginShared = require("./login-shared"); describe("integration: login/logout browser app tests", function () { @@ -41,6 +42,20 @@ define(function (require, exports, module) { originalOpen, originalFetch; + let setupTrialState, + setupExpiredTrial, + verifyProBranding, + verifyProfilePopupContent, + cleanupTrialState, + popupToAppear, + performFullLogoutFlow, + verifyProfileIconBlanked, + VIEW_TRIAL_DAYS_LEFT, + VIEW_PHOENIX_PRO, + VIEW_PHOENIX_FREE, + SIGNIN_POPUP, + PROFILE_POPUP; + beforeAll(async function () { testWindow = await SpecRunnerUtils.createTestWindowAndRun(); @@ -72,6 +87,20 @@ define(function (require, exports, module) { "Profile button to be available", 3000 ); + LoginShared.setup(testWindow, LoginServiceExports, setupProUserMock, performFullLoginFlow); + VIEW_TRIAL_DAYS_LEFT = LoginShared.VIEW_TRIAL_DAYS_LEFT; + VIEW_PHOENIX_PRO = LoginShared.VIEW_PHOENIX_PRO; + VIEW_PHOENIX_FREE = LoginShared.VIEW_PHOENIX_FREE; + SIGNIN_POPUP = LoginShared.SIGNIN_POPUP; + PROFILE_POPUP = LoginShared.PROFILE_POPUP; + setupTrialState = LoginShared.setupTrialState; + setupExpiredTrial = LoginShared.setupExpiredTrial; + verifyProBranding = LoginShared.verifyProBranding; + verifyProfilePopupContent = LoginShared.verifyProfilePopupContent; + cleanupTrialState = LoginShared.cleanupTrialState; + popupToAppear = LoginShared.popupToAppear; + performFullLogoutFlow = LoginShared.performFullLogoutFlow; + verifyProfileIconBlanked = LoginShared.verifyProfileIconBlanked; }, 30000); afterAll(async function () { @@ -97,31 +126,6 @@ define(function (require, exports, module) { // Note: We can't easily reset login state, so tests should handle this }); - // Helper functions for promotion testing (browser-specific) - async function setupTrialState(daysRemaining) { - const PromotionExports = testWindow._test_promo_login_exports; - const mockNow = Date.now(); - await PromotionExports._setTrialData({ - proVersion: "3.1.0", - endDate: mockNow + (daysRemaining * PromotionExports.TRIAL_CONSTANTS.MS_PER_DAY) - }); - // Trigger entitlements changed event to update branding - const LoginService = PromotionExports.LoginService; - LoginService.trigger(LoginService.EVENT_ENTITLEMENTS_CHANGED); - } - - async function setupExpiredTrial() { - const PromotionExports = testWindow._test_promo_login_exports; - const mockNow = Date.now(); - await PromotionExports._setTrialData({ - proVersion: "3.1.0", - endDate: mockNow - PromotionExports.TRIAL_CONSTANTS.MS_PER_DAY - }); - // Trigger entitlements changed event to update branding - const LoginService = PromotionExports.LoginService; - LoginService.trigger(LoginService.EVENT_ENTITLEMENTS_CHANGED); - } - function setupProUserMock(hasActiveSubscription = true) { let userSignedOut = false; @@ -216,115 +220,6 @@ define(function (require, exports, module) { ProDialogsExports.setFetchFn(fetchMock); } - async function verifyProBranding(shouldShowPro, testDescription) { - const $brandingLink = testWindow.$("#phcode-io-main-nav"); - console.log(`llgT: Browser verifying branding for ${testDescription}, shouldShowPro: ${shouldShowPro}`); - console.log(`llgT: Browser branding link classes: ${$brandingLink.attr('class')}`); - console.log(`llgT: Browser branding link text: '${$brandingLink.text()}'`); - - if (shouldShowPro) { - await awaitsFor( - function () { - return testWindow.$("#phcode-io-main-nav").hasClass("phoenix-pro"); - }, - `Verify Pro branding to appear: ${testDescription}`, 5000 - ); - expect($brandingLink.hasClass("phoenix-pro")).toBe(true); - expect($brandingLink.text()).toContain("Phoenix Pro"); - expect($brandingLink.find(".fa-feather").length).toBe(1); - } else { - await awaitsFor( - function () { - return !testWindow.$("#phcode-io-main-nav").hasClass("phoenix-pro"); - }, - `Verify Pro branding to go away: ${testDescription}`, 5000 - ); - expect($brandingLink.hasClass("phoenix-pro")).toBe(false); - expect($brandingLink.text()).toBe("phcode.io"); - } - } - - const VIEW_TRIAL_DAYS_LEFT = "VIEW_TRIAL_DAYS_LEFT"; - const VIEW_PHOENIX_PRO = "VIEW_PHOENIX_PRO"; - const VIEW_PHOENIX_FREE = "VIEW_PHOENIX_FREE"; - async function verifyProfilePopupContent(expectedView, testDescription) { - await awaitsFor( - function () { - return testWindow.$('.profile-popup').length > 0; - }, - `Profile popup to appear: ${testDescription}`, - 3000 - ); - - if (expectedView === VIEW_PHOENIX_PRO) { - await awaitsFor( - function () { - const $popup = testWindow.$('.profile-popup'); - const $planName = $popup.find('.user-plan-name'); - const planText = $planName.text(); - return planText.includes("Phoenix Pro"); - }, - `Profile popup should say phoenix pro: ${testDescription}`, 5000 - ); - const $popup = testWindow.$('.profile-popup'); - const $planName = $popup.find('.user-plan-name'); - const planText = $planName.text(); - expect(planText).toContain("Phoenix Pro"); - expect(planText).not.toContain("days left"); - expect($popup.find(".fa-feather").length).toBe(1); - } else if (expectedView === VIEW_TRIAL_DAYS_LEFT) { - await awaitsFor( - function () { - const $popup = testWindow.$('.profile-popup'); - const $planName = $popup.find('.user-plan-name'); - const planText = $planName.text(); - return planText.includes("Phoenix Pro") && planText.includes("days left"); - }, - `Profile popup should say phoenix pro trial: ${testDescription}`, 5000 - ); - const $popup = testWindow.$('.profile-popup'); - const $planName = $popup.find('.user-plan-name'); - const planText = $planName.text(); - expect(planText).toContain("Phoenix Pro"); - expect(planText).toContain("days left"); - expect($popup.find(".fa-feather").length).toBe(1); - } else { - await awaitsFor( - function () { - const $popup = testWindow.$('.profile-popup'); - const $planName = $popup.find('.user-plan-name'); - const planText = $planName.text(); - return !planText.includes("Phoenix Pro"); - }, - `Profile popup should not say phoenix pro: ${testDescription}`, 5000 - ); - const $popup = testWindow.$('.profile-popup'); - const $planName = $popup.find('.user-plan-name'); - const planText = $planName.text(); - expect(planText).not.toContain("Phoenix Pro"); - expect($popup.find(".fa-feather").length).toBe(0); - } - } - - async function cleanupTrialState() { - const PromotionExports = testWindow._test_promo_login_exports; - await PromotionExports._cleanTrialData(); - } - - const SIGNIN_POPUP = "SIGNIN_POPUP"; - const PROFILE_POPUP = "PROFILE_POPUP"; - async function popupToAppear(popupType = SIGNIN_POPUP) { - const statusText = popupType === SIGNIN_POPUP ? - "Sign In popup to appear" : "Profile popup to appear"; - await awaitsFor( - function () { - const selector = popupType === SIGNIN_POPUP ? ".login-profile-popup" : ".user-profile-popup"; - return testWindow.$('.modal').length > 0 || testWindow.$(selector).length > 0; - }, - statusText, 3000 - ); - } - async function performFullLoginFlow() { // Mock window.open like the original test let capturedURL = null; @@ -385,42 +280,6 @@ define(function (require, exports, module) { ); } - function verifyProfileIconBlanked() { - const $profileIcon = testWindow.$("#user-profile-button"); - const initialContent = $profileIcon.html(); - expect(initialContent).not.toContain('TU'); - } - - async function performFullLogoutFlow() { - // Click profile button to open popup - const $profileButton = testWindow.$("#user-profile-button"); - $profileButton.trigger('click'); - - // Wait for profile popup - await popupToAppear(PROFILE_POPUP); - - // Find and click sign out button - let popupContent = testWindow.$('.profile-popup'); - const signOutButton = popupContent.find('#phoenix-signout-btn'); - signOutButton.trigger('click'); - - // Wait for sign out confirmation dialog and dismiss it - await testWindow.__PR.waitForModalDialog(".modal"); - testWindow.__PR.clickDialogButtonID(testWindow.__PR.Dialogs.DIALOG_BTN_OK); - await testWindow.__PR.waitForModalDialogClosed(".modal"); - - // Wait for sign out to complete - await awaitsFor( - function () { - return !LoginServiceExports.LoginService.isLoggedIn(); - }, - "User to be signed out", - 10000 - ); - - verifyProfileIconBlanked(); - } - describe("Browser Login Tests", function () { beforeEach(async function () { @@ -431,203 +290,7 @@ define(function (require, exports, module) { await cleanupTrialState(); }); - it("should complete login and logout flow", async function () { - // Setup basic user mock - setupProUserMock(false); - - // Perform full login flow - await performFullLoginFlow(); - expect(LoginServiceExports.LoginService.isLoggedIn()).toBe(true); - - // Perform full logout flow - await performFullLogoutFlow(); - expect(LoginServiceExports.LoginService.isLoggedIn()).toBe(false); - }); - - it("should update profile icon after login", async function () { - // Setup basic user mock - setupProUserMock(false); - - // Verify initial state - verifyProfileIconBlanked(); - - // Perform login - await performFullLoginFlow(); - - // Verify profile icon updated with user initials - const $profileIcon = testWindow.$("#user-profile-button"); - const updatedContent = $profileIcon.html(); - expect(updatedContent).toContain('svg'); - expect(updatedContent).toContain('TU'); - - // Logout for cleanup - await performFullLogoutFlow(); - }); - - it("should show correct popup states", async function () { - // Setup basic user mock - setupProUserMock(false); - - const $profileButton = testWindow.$("#user-profile-button"); - - // Test initial state - should show signin popup - $profileButton.trigger('click'); - await popupToAppear(SIGNIN_POPUP); - - let popupContent = testWindow.$('.profile-popup'); - const signInButton = popupContent.find('#phoenix-signin-btn'); - const signOutButton = popupContent.find('#phoenix-signout-btn'); - - expect(signInButton.length).toBe(1); - expect(signOutButton.length).toBe(0); - - // Close popup - $profileButton.trigger('click'); - - // Perform login - await performFullLoginFlow(); - - // Test logged in state - should show profile popup - $profileButton.trigger('click'); - await popupToAppear(PROFILE_POPUP); - - popupContent = testWindow.$('.profile-popup'); - const newSignInButton = popupContent.find('#phoenix-signin-btn'); - const newSignOutButton = popupContent.find('#phoenix-signout-btn'); - - expect(newSignInButton.length).toBe(0); - expect(newSignOutButton.length).toBe(1); - - // Close popup and logout for cleanup - $profileButton.trigger('click'); - await performFullLogoutFlow(); - - // Test final state - should be back to signin popup - $profileButton.trigger('click'); - await popupToAppear(SIGNIN_POPUP); - - popupContent = testWindow.$('.profile-popup'); - const finalSignInButton = popupContent.find('#phoenix-signin-btn'); - const finalSignOutButton = popupContent.find('#phoenix-signout-btn'); - - expect(finalSignInButton.length).toBe(1); - expect(finalSignOutButton.length).toBe(0); - - // Close popup - $profileButton.trigger('click'); - }); - - it("should show pro branding for user with pro subscription (expired trial)", async function () { - console.log("llgT: Starting browser pro user with expired trial test"); - - // Setup: Pro subscription + expired trial - setupProUserMock(true); - await setupExpiredTrial(); - - // Verify initial state (no pro branding) - await verifyProBranding(false, "no pro branding to start with"); - - // Perform login - await performFullLoginFlow(); - await verifyProBranding(true, "pro branding to appear after pro user login"); - - // Check profile popup shows pro status (not trial) - const $profileButton = testWindow.$("#user-profile-button"); - $profileButton.trigger('click'); - - // Wait for profile popup to show "phoenix pro " - await verifyProfilePopupContent(VIEW_PHOENIX_PRO, "pro user profile popup"); - - // Close popup - $profileButton.trigger('click'); - - // Perform logout - await performFullLogoutFlow(); - - // For user with pro subscription + expired trial: - // After logout, pro branding should disappear because: - // 1. No server entitlements (logged out) - // 2. Trial is expired (0 days remaining) - await verifyProBranding(false, "Pro branding to disappear after logout"); - }); - - it("should show trial branding for user without pro subscription (active trial)", async function () { - console.log("llgT: Starting browser trial user test"); - - // Setup: No pro subscription + active trial (15 days) - setupProUserMock(false); - await setupTrialState(15); - - // Verify initial state shows pro branding due to trial - await verifyProBranding(true, "Trial branding to appear initially"); - - // Perform login - await performFullLoginFlow(); - - // Verify pro branding remains after login - await verifyProBranding(true, "after trial user login"); - - // Check profile popup shows trial status - const $profileButton = testWindow.$("#user-profile-button"); - $profileButton.trigger('click'); - await popupToAppear(PROFILE_POPUP); - await verifyProfilePopupContent(VIEW_TRIAL_DAYS_LEFT, - "trial user profile popup for logged in user"); - - // Close popup - $profileButton.trigger('click'); - - // Perform logout - await performFullLogoutFlow(); - - // Verify pro branding remains after logout (trial continues) - await verifyProBranding(true, "Trial branding to remain after logout"); - - // Check profile popup still shows trial status - $profileButton.trigger('click'); - await popupToAppear(SIGNIN_POPUP); - await verifyProfilePopupContent(VIEW_TRIAL_DAYS_LEFT, - "trial user profile popup for logged out user"); - - // Close popup - $profileButton.trigger('click'); - }); - - it("should prioritize pro subscription over trial in profile popup", async function () { - console.log("llgT: Starting browser trial user with pro subscription test"); - - // Setup: Pro subscription + active trial - setupProUserMock(true); - await setupTrialState(10); - - // Perform login - await performFullLoginFlow(); - - // Verify pro branding appears - await verifyProBranding(true, "Pro branding to appear for pro user"); - - // Check profile popup shows pro status (not trial text) - const $profileButton = testWindow.$("#user-profile-button"); - $profileButton.trigger('click'); - await popupToAppear(PROFILE_POPUP); - - // Should show pro, not trial, since user has paid subscription - await verifyProfilePopupContent(VIEW_PHOENIX_PRO, - "pro+trial user profile should not show trial branding"); - - // Close popup - $profileButton.trigger('click'); - - // Perform logout - await performFullLogoutFlow(); - - // Verify pro branding remains due to trial (even though subscription is gone) - await verifyProBranding(true, "Pro branding should remain after logout as trial user"); - $profileButton.trigger('click'); - await popupToAppear(SIGNIN_POPUP); - await verifyProfilePopupContent(VIEW_TRIAL_DAYS_LEFT, - "trial user profile popup for logged out user"); - }); + LoginShared.setupSharedTests(); }); }); }); diff --git a/test/spec/login-desktop-integ-test.js b/test/spec/login-desktop-integ-test.js index 49a599a510..3184733d7b 100644 --- a/test/spec/login-desktop-integ-test.js +++ b/test/spec/login-desktop-integ-test.js @@ -23,6 +23,7 @@ define(function (require, exports, module) { const SpecRunnerUtils = require("spec/SpecRunnerUtils"); + const LoginShared = require("./login-shared"); describe("integration: login/logout desktop app tests", function () { @@ -49,6 +50,20 @@ define(function (require, exports, module) { originalCopyToClipboard, originalFetch; + let setupTrialState, + setupExpiredTrial, + verifyProBranding, + verifyProfilePopupContent, + cleanupTrialState, + popupToAppear, + performFullLogoutFlow, + verifyProfileIconBlanked, + VIEW_TRIAL_DAYS_LEFT, + VIEW_PHOENIX_PRO, + VIEW_PHOENIX_FREE, + SIGNIN_POPUP, + PROFILE_POPUP; + beforeAll(async function () { testWindow = await SpecRunnerUtils.createTestWindowAndRun(); @@ -80,6 +95,20 @@ define(function (require, exports, module) { "Profile button to be available", 3000 ); + LoginShared.setup(testWindow, LoginServiceExports, setupProUserMock, performFullLoginFlow); + VIEW_TRIAL_DAYS_LEFT = LoginShared.VIEW_TRIAL_DAYS_LEFT; + VIEW_PHOENIX_PRO = LoginShared.VIEW_PHOENIX_PRO; + VIEW_PHOENIX_FREE = LoginShared.VIEW_PHOENIX_FREE; + SIGNIN_POPUP = LoginShared.SIGNIN_POPUP; + PROFILE_POPUP = LoginShared.PROFILE_POPUP; + setupTrialState = LoginShared.setupTrialState; + setupExpiredTrial = LoginShared.setupExpiredTrial; + verifyProBranding = LoginShared.verifyProBranding; + verifyProfilePopupContent = LoginShared.verifyProfilePopupContent; + cleanupTrialState = LoginShared.cleanupTrialState; + popupToAppear = LoginShared.popupToAppear; + performFullLogoutFlow = LoginShared.performFullLogoutFlow; + verifyProfileIconBlanked = LoginShared.verifyProfileIconBlanked; }, 30000); afterAll(async function () { @@ -107,31 +136,6 @@ define(function (require, exports, module) { // Note: We can't easily reset login state, so tests should handle this }); - // Helper functions for desktop login testing - async function setupTrialState(daysRemaining) { - const PromotionExports = testWindow._test_promo_login_exports; - const mockNow = Date.now(); - await PromotionExports._setTrialData({ - proVersion: "3.1.0", - endDate: mockNow + (daysRemaining * PromotionExports.TRIAL_CONSTANTS.MS_PER_DAY) - }); - // Trigger entitlements changed event to update branding - const LoginService = PromotionExports.LoginService; - LoginService.trigger(LoginService.EVENT_ENTITLEMENTS_CHANGED); - } - - async function setupExpiredTrial() { - const PromotionExports = testWindow._test_promo_login_exports; - const mockNow = Date.now(); - await PromotionExports._setTrialData({ - proVersion: "3.1.0", - endDate: mockNow - PromotionExports.TRIAL_CONSTANTS.MS_PER_DAY - }); - // Trigger entitlements changed event to update branding - const LoginService = PromotionExports.LoginService; - LoginService.trigger(LoginService.EVENT_ENTITLEMENTS_CHANGED); - } - function setupProUserMock(hasActiveSubscription = true) { let userSignedOut = false; @@ -238,115 +242,6 @@ define(function (require, exports, module) { ProDialogsExports.setFetchFn(fetchMock); } - async function verifyProBranding(shouldShowPro, testDescription) { - const $brandingLink = testWindow.$("#phcode-io-main-nav"); - console.log(`llgT: Desktop verifying branding for ${testDescription}, shouldShowPro: ${shouldShowPro}`); - console.log(`llgT: Desktop branding link classes: ${$brandingLink.attr('class')}`); - console.log(`llgT: Desktop branding link text: '${$brandingLink.text()}'`); - - if (shouldShowPro) { - await awaitsFor( - function () { - return testWindow.$("#phcode-io-main-nav").hasClass("phoenix-pro"); - }, - `Verify Pro branding to appear: ${testDescription}`, 5000 - ); - expect($brandingLink.hasClass("phoenix-pro")).toBe(true); - expect($brandingLink.text()).toContain("Phoenix Pro"); - expect($brandingLink.find(".fa-feather").length).toBe(1); - } else { - await awaitsFor( - function () { - return !testWindow.$("#phcode-io-main-nav").hasClass("phoenix-pro"); - }, - `Verify Pro branding to go away: ${testDescription}`, 5000 - ); - expect($brandingLink.hasClass("phoenix-pro")).toBe(false); - expect($brandingLink.text()).toBe("phcode.io"); - } - } - - const VIEW_TRIAL_DAYS_LEFT = "VIEW_TRIAL_DAYS_LEFT"; - const VIEW_PHOENIX_PRO = "VIEW_PHOENIX_PRO"; - const VIEW_PHOENIX_FREE = "VIEW_PHOENIX_FREE"; - async function verifyProfilePopupContent(expectedView, testDescription) { - await awaitsFor( - function () { - return testWindow.$('.profile-popup').length > 0; - }, - `Profile popup to appear: ${testDescription}`, - 3000 - ); - - if (expectedView === VIEW_PHOENIX_PRO) { - await awaitsFor( - function () { - const $popup = testWindow.$('.profile-popup'); - const $planName = $popup.find('.user-plan-name'); - const planText = $planName.text(); - return planText.includes("Phoenix Pro"); - }, - `Profile popup should say phoenix pro: ${testDescription}`, 5000 - ); - const $popup = testWindow.$('.profile-popup'); - const $planName = $popup.find('.user-plan-name'); - const planText = $planName.text(); - expect(planText).toContain("Phoenix Pro"); - expect(planText).not.toContain("days left"); - expect($popup.find(".fa-feather").length).toBe(1); - } else if (expectedView === VIEW_TRIAL_DAYS_LEFT) { - await awaitsFor( - function () { - const $popup = testWindow.$('.profile-popup'); - const $planName = $popup.find('.user-plan-name'); - const planText = $planName.text(); - return planText.includes("Phoenix Pro") && planText.includes("days left"); - }, - `Profile popup should say phoenix pro trial: ${testDescription}`, 5000 - ); - const $popup = testWindow.$('.profile-popup'); - const $planName = $popup.find('.user-plan-name'); - const planText = $planName.text(); - expect(planText).toContain("Phoenix Pro"); - expect(planText).toContain("days left"); - expect($popup.find(".fa-feather").length).toBe(1); - } else { - await awaitsFor( - function () { - const $popup = testWindow.$('.profile-popup'); - const $planName = $popup.find('.user-plan-name'); - const planText = $planName.text(); - return !planText.includes("Phoenix Pro"); - }, - `Profile popup should not say phoenix pro: ${testDescription}`, 5000 - ); - const $popup = testWindow.$('.profile-popup'); - const $planName = $popup.find('.user-plan-name'); - const planText = $planName.text(); - expect(planText).not.toContain("Phoenix Pro"); - expect($popup.find(".fa-feather").length).toBe(0); - } - } - - async function cleanupTrialState() { - const PromotionExports = testWindow._test_promo_login_exports; - await PromotionExports._cleanTrialData(); - } - - const SIGNIN_POPUP = "SIGNIN_POPUP"; - const PROFILE_POPUP = "PROFILE_POPUP"; - async function popupToAppear(popupType = SIGNIN_POPUP) { - const statusText = popupType === SIGNIN_POPUP ? - "Sign In popup to appear" : "Profile popup to appear"; - await awaitsFor( - function () { - const selector = popupType === SIGNIN_POPUP ? ".login-profile-popup" : ".user-profile-popup"; - return testWindow.$('.modal').length > 0 || testWindow.$(selector).length > 0; - }, - statusText, 3000 - ); - } - async function performFullLoginFlow() { // Mock desktop app functions for login flow testWindow.Phoenix.app.openURLInDefaultBrowser = function(url) { @@ -397,41 +292,6 @@ define(function (require, exports, module) { ); } - async function performFullLogoutFlow() { - // Click profile button to open popup - const $profileButton = testWindow.$("#user-profile-button"); - $profileButton.trigger('click'); - - // Wait for profile popup - await popupToAppear(PROFILE_POPUP); - - // Find and click sign out button - let popupContent = testWindow.$('.profile-popup'); - const signOutButton = popupContent.find('#phoenix-signout-btn'); - signOutButton.trigger('click'); - - // Wait for sign out confirmation dialog and dismiss it - await testWindow.__PR.waitForModalDialog(".modal"); - testWindow.__PR.clickDialogButtonID(testWindow.__PR.Dialogs.DIALOG_BTN_OK); - await testWindow.__PR.waitForModalDialogClosed(".modal"); - - // Wait for sign out to complete - await awaitsFor( - function () { - return !LoginServiceExports.LoginService.isLoggedIn(); - }, - "User to be signed out", - 10000 - ); - verifyProfileIconBlanked(); - } - - function verifyProfileIconBlanked() { - const $profileIcon = testWindow.$("#user-profile-button"); - const initialContent = $profileIcon.html(); - expect(initialContent).not.toContain('TU'); - } - describe("Desktop Login and Promotion Tests", function () { beforeEach(async function () { @@ -446,20 +306,6 @@ define(function (require, exports, module) { await cleanupTrialState(); }); - it("should complete login and logout flow", async function () { - // Setup basic user mock - setupProUserMock(false); - - // Perform full login flow - await performFullLoginFlow(); - expect(LoginServiceExports.LoginService.isLoggedIn()).toBe(true); - - // Perform full logout flow - await performFullLogoutFlow(); - expect(LoginServiceExports.LoginService.isLoggedIn()).toBe(false); - verifyProfileIconBlanked(); - }); - it("should open browser and copy validation code", async function () { // Setup basic user mock setupProUserMock(false); @@ -504,202 +350,7 @@ define(function (require, exports, module) { expect(capturedBrowserURL).toContain('test-session-123'); }); - it("should update profile icon after login", async function () { - // Setup basic user mock - setupProUserMock(false); - - // Verify initial state - const $profileIcon = testWindow.$("#user-profile-button"); - const initialContent = $profileIcon.html(); - expect(initialContent).not.toContain('TU'); - - // Perform login - await performFullLoginFlow(); - - // Wait for profile icon to update - await awaitsFor( - function () { - const $profileIcon = testWindow.$("#user-profile-button"); - const profileIconContent = $profileIcon.html(); - return profileIconContent && profileIconContent.includes('TU'); - }, - "profile icon to contain user initials", - 5000 - ); - - // Verify profile icon updated with user initials - const updatedContent = $profileIcon.html(); - expect(updatedContent).toContain('svg'); - expect(updatedContent).toContain('TU'); - - // Logout for cleanup - await performFullLogoutFlow(); - }); - - it("should show correct popup states", async function () { - // Setup basic user mock - setupProUserMock(false); - - const $profileButton = testWindow.$("#user-profile-button"); - - // Test initial state - should show signin popup - $profileButton.trigger('click'); - await popupToAppear(SIGNIN_POPUP); - - let popupContent = testWindow.$('.profile-popup'); - const signInButton = popupContent.find('#phoenix-signin-btn'); - const signOutButton = popupContent.find('#phoenix-signout-btn'); - - expect(signInButton.length).toBe(1); - expect(signOutButton.length).toBe(0); - - // Close popup - $profileButton.trigger('click'); - - // Perform login - await performFullLoginFlow(); - - // Test logged in state - should show profile popup - $profileButton.trigger('click'); - await popupToAppear(PROFILE_POPUP); - - popupContent = testWindow.$('.profile-popup'); - const newSignInButton = popupContent.find('#phoenix-signin-btn'); - const newSignOutButton = popupContent.find('#phoenix-signout-btn'); - - expect(newSignInButton.length).toBe(0); - expect(newSignOutButton.length).toBe(1); - - // Close popup and logout for cleanup - $profileButton.trigger('click'); - await performFullLogoutFlow(); - - // Test final state - should be back to signin popup - $profileButton.trigger('click'); - await popupToAppear(SIGNIN_POPUP); - - popupContent = testWindow.$('.profile-popup'); - const finalSignInButton = popupContent.find('#phoenix-signin-btn'); - const finalSignOutButton = popupContent.find('#phoenix-signout-btn'); - - expect(finalSignInButton.length).toBe(1); - expect(finalSignOutButton.length).toBe(0); - - // Close popup - $profileButton.trigger('click'); - }); - - it("should show pro branding for user with pro subscription (expired trial)", async function () { - console.log("llgT: Starting desktop pro user with expired trial test"); - - // Setup: Pro subscription + expired trial - setupProUserMock(true); - await setupExpiredTrial(); - - // Verify initial state (no pro branding) - await verifyProBranding(false, "no pro branding to start with"); - - // Perform login - await performFullLoginFlow(); - await verifyProBranding(true, "pro branding to appear after pro user login"); - - // Check profile popup shows pro status (not trial) - const $profileButton = testWindow.$("#user-profile-button"); - $profileButton.trigger('click'); - - // Wait for profile popup to show "phoenix pro " - await verifyProfilePopupContent(VIEW_PHOENIX_PRO, "pro user profile popup"); - - // Close popup - $profileButton.trigger('click'); - - // Perform logout - await performFullLogoutFlow(); - - // For user with pro subscription + expired trial: - // After logout, pro branding should disappear because: - // 1. No server entitlements (logged out) - // 2. Trial is expired (0 days remaining) - await verifyProBranding(false, "Pro branding to disappear after logout"); - }); - - it("should show trial branding for user without pro subscription (active trial)", async function () { - console.log("llgT: Starting desktop trial user test"); - - // Setup: No pro subscription + active trial (15 days) - setupProUserMock(false); - await setupTrialState(15); - - // Verify initial state shows pro branding due to trial - await verifyProBranding(true, "Trial branding to appear initially"); - - // Perform login - await performFullLoginFlow(); - - // Verify pro branding remains after login - await verifyProBranding(true, "after trial user login"); - - // Check profile popup shows trial status - const $profileButton = testWindow.$("#user-profile-button"); - $profileButton.trigger('click'); - await popupToAppear(PROFILE_POPUP); - await verifyProfilePopupContent(VIEW_TRIAL_DAYS_LEFT, - "trial user profile popup for logged in user"); - - // Close popup - $profileButton.trigger('click'); - - // Perform logout - await performFullLogoutFlow(); - - // Verify pro branding remains after logout (trial continues) - await verifyProBranding(true, "Trial branding to remain after logout"); - - // Check profile popup still shows trial status - $profileButton.trigger('click'); - await popupToAppear(SIGNIN_POPUP); - await verifyProfilePopupContent(VIEW_TRIAL_DAYS_LEFT, - "trial user profile popup for logged out user"); - - // Close popup - $profileButton.trigger('click'); - }); - - it("should prioritize pro subscription over trial in profile popup", async function () { - console.log("llgT: Starting desktop trial user with pro subscription test"); - - // Setup: Pro subscription + active trial - setupProUserMock(true); - await setupTrialState(10); - - // Perform login - await performFullLoginFlow(); - - // Verify pro branding appears - await verifyProBranding(true, "Pro branding to appear for pro user"); - - // Check profile popup shows pro status (not trial text) - const $profileButton = testWindow.$("#user-profile-button"); - $profileButton.trigger('click'); - await popupToAppear(PROFILE_POPUP); - - // Should show pro, not trial, since user has paid subscription - await verifyProfilePopupContent(VIEW_PHOENIX_PRO, - "pro+trial user profile should not show trial branding"); - - // Close popup - $profileButton.trigger('click'); - - // Perform logout - await performFullLogoutFlow(); - - // Verify pro branding remains due to trial (even though subscription is gone) - await verifyProBranding(true, "Pro branding should remain after logout as trial user"); - $profileButton.trigger('click'); - await popupToAppear(SIGNIN_POPUP); - await verifyProfilePopupContent(VIEW_TRIAL_DAYS_LEFT, - "trial user profile popup for logged out user"); - }); + LoginShared.setupSharedTests(); }); }); }); diff --git a/test/spec/login-shared.js b/test/spec/login-shared.js new file mode 100644 index 0000000000..ca8842c765 --- /dev/null +++ b/test/spec/login-shared.js @@ -0,0 +1,454 @@ + +/*global expect, it, awaitsFor*/ + +define(function (require, exports, module) { + let testWindow, LoginServiceExports, setupProUserMock, performFullLoginFlow; + + async function setupTrialState(daysRemaining) { + const PromotionExports = testWindow._test_promo_login_exports; + const mockNow = Date.now(); + await PromotionExports._setTrialData({ + proVersion: "3.1.0", + endDate: mockNow + (daysRemaining * PromotionExports.TRIAL_CONSTANTS.MS_PER_DAY) + }); + // Trigger entitlements changed event to update branding + const LoginService = PromotionExports.LoginService; + LoginService.trigger(LoginService.EVENT_ENTITLEMENTS_CHANGED); + } + + async function setupExpiredTrial() { + const PromotionExports = testWindow._test_promo_login_exports; + const mockNow = Date.now(); + await PromotionExports._setTrialData({ + proVersion: "3.1.0", + endDate: mockNow - PromotionExports.TRIAL_CONSTANTS.MS_PER_DAY + }); + // Trigger entitlements changed event to update branding + const LoginService = PromotionExports.LoginService; + LoginService.trigger(LoginService.EVENT_ENTITLEMENTS_CHANGED); + } + + async function verifyProBranding(shouldShowPro, testDescription) { + const $brandingLink = testWindow.$("#phcode-io-main-nav"); + + if (shouldShowPro) { + await awaitsFor( + function () { + return testWindow.$("#phcode-io-main-nav").hasClass("phoenix-pro"); + }, + `Verify Pro branding to appear: ${testDescription}`, 5000 + ); + expect($brandingLink.hasClass("phoenix-pro")).toBe(true); + expect($brandingLink.text()).toContain("Phoenix Pro"); + expect($brandingLink.find(".fa-feather").length).toBe(1); + } else { + await awaitsFor( + function () { + return !testWindow.$("#phcode-io-main-nav").hasClass("phoenix-pro"); + }, + `Verify Pro branding to go away: ${testDescription}`, 5000 + ); + expect($brandingLink.hasClass("phoenix-pro")).toBe(false); + expect($brandingLink.text()).toBe("phcode.io"); + } + } + + const VIEW_TRIAL_DAYS_LEFT = "VIEW_TRIAL_DAYS_LEFT"; + const VIEW_PHOENIX_PRO = "VIEW_PHOENIX_PRO"; + const VIEW_PHOENIX_FREE = "VIEW_PHOENIX_FREE"; + async function verifyProfilePopupContent(expectedView, testDescription) { + await awaitsFor( + function () { + return testWindow.$('.profile-popup').length > 0; + }, + `Profile popup to appear: ${testDescription}`, + 3000 + ); + + if (expectedView === VIEW_PHOENIX_PRO) { + await awaitsFor( + function () { + const $popup = testWindow.$('.profile-popup'); + const $planName = $popup.find('.user-plan-name'); + const planText = $planName.text(); + return planText.includes("Phoenix Pro"); + }, + `Profile popup should say phoenix pro: ${testDescription}`, 5000 + ); + const $popup = testWindow.$('.profile-popup'); + const $planName = $popup.find('.user-plan-name'); + const planText = $planName.text(); + expect(planText).toContain("Phoenix Pro"); + expect(planText).not.toContain("days left"); + expect($popup.find(".fa-feather").length).toBe(1); + } else if (expectedView === VIEW_TRIAL_DAYS_LEFT) { + await awaitsFor( + function () { + const $popup = testWindow.$('.profile-popup'); + const $planName = $popup.find('.user-plan-name'); + const planText = $planName.text(); + return planText.includes("Phoenix Pro") && planText.includes("days left"); + }, + `Profile popup should say phoenix pro trial: ${testDescription}`, 5000 + ); + const $popup = testWindow.$('.profile-popup'); + const $planName = $popup.find('.user-plan-name'); + const planText = $planName.text(); + expect(planText).toContain("Phoenix Pro"); + expect(planText).toContain("days left"); + expect($popup.find(".fa-feather").length).toBe(1); + } else { + await awaitsFor( + function () { + const $popup = testWindow.$('.profile-popup'); + const $planName = $popup.find('.user-plan-name'); + const planText = $planName.text(); + return !planText.includes("Phoenix Pro"); + }, + `Profile popup should not say phoenix pro: ${testDescription}`, 5000 + ); + const $popup = testWindow.$('.profile-popup'); + const $planName = $popup.find('.user-plan-name'); + const planText = $planName.text(); + expect(planText).not.toContain("Phoenix Pro"); + expect($popup.find(".fa-feather").length).toBe(0); + } + } + + async function cleanupTrialState() { + const PromotionExports = testWindow._test_promo_login_exports; + await PromotionExports._cleanTrialData(); + } + + const SIGNIN_POPUP = "SIGNIN_POPUP"; + const PROFILE_POPUP = "PROFILE_POPUP"; + async function popupToAppear(popupType = SIGNIN_POPUP) { + const statusText = popupType === SIGNIN_POPUP ? + "Sign In popup to appear" : "Profile popup to appear"; + await awaitsFor( + function () { + const selector = popupType === SIGNIN_POPUP ? ".login-profile-popup" : ".user-profile-popup"; + return testWindow.$('.modal').length > 0 || testWindow.$(selector).length > 0; + }, + statusText, 3000 + ); + } + + function verifyProfileIconBlanked() { + const $profileIcon = testWindow.$("#user-profile-button"); + const initialContent = $profileIcon.html(); + expect(initialContent).not.toContain('TU'); + } + + async function performFullLogoutFlow() { + // Click profile button to open popup + const $profileButton = testWindow.$("#user-profile-button"); + $profileButton.trigger('click'); + + // Wait for profile popup + await popupToAppear(PROFILE_POPUP); + + // Find and click sign out button + let popupContent = testWindow.$('.profile-popup'); + const signOutButton = popupContent.find('#phoenix-signout-btn'); + signOutButton.trigger('click'); + + // Wait for sign out confirmation dialog and dismiss it + await testWindow.__PR.waitForModalDialog(".modal"); + testWindow.__PR.clickDialogButtonID(testWindow.__PR.Dialogs.DIALOG_BTN_OK); + await testWindow.__PR.waitForModalDialogClosed(".modal"); + + // Wait for sign out to complete + await awaitsFor( + function () { + return !LoginServiceExports.LoginService.isLoggedIn(); + }, + "User to be signed out", + 10000 + ); + verifyProfileIconBlanked(); + } + + function setup(_testWindow, _LoginServiceExports, _setupProUserMock, _performFullLoginFlow) { + testWindow = _testWindow; + LoginServiceExports = _LoginServiceExports; + setupProUserMock = _setupProUserMock; + performFullLoginFlow = _performFullLoginFlow; + } + + function setupSharedTests() { + + it("should complete login and logout flow", async function () { + // Setup basic user mock + setupProUserMock(false); + + // Perform full login flow + await performFullLoginFlow(); + expect(LoginServiceExports.LoginService.isLoggedIn()).toBe(true); + + // Perform full logout flow + await performFullLogoutFlow(); + expect(LoginServiceExports.LoginService.isLoggedIn()).toBe(false); + verifyProfileIconBlanked(); + }); + + it("should update profile icon after login", async function () { + // Setup basic user mock + setupProUserMock(false); + + // Verify initial state + verifyProfileIconBlanked(); + + // Perform login + await performFullLoginFlow(); + + // Wait for profile icon to update + await awaitsFor( + function () { + const $profileIcon = testWindow.$("#user-profile-button"); + const profileIconContent = $profileIcon.html(); + return profileIconContent && profileIconContent.includes('TU'); + }, + "profile icon to contain user initials", + 5000 + ); + + // Verify profile icon updated with user initials + const $profileIcon = testWindow.$("#user-profile-button"); + const updatedContent = $profileIcon.html(); + expect(updatedContent).toContain('svg'); + expect(updatedContent).toContain('TU'); + + // Logout for cleanup + await performFullLogoutFlow(); + }); + + it("should show correct popup states", async function () { + // Setup basic user mock + setupProUserMock(false); + + const $profileButton = testWindow.$("#user-profile-button"); + + // Test initial state - should show signin popup + $profileButton.trigger('click'); + await popupToAppear(SIGNIN_POPUP); + + let popupContent = testWindow.$('.profile-popup'); + const signInButton = popupContent.find('#phoenix-signin-btn'); + const signOutButton = popupContent.find('#phoenix-signout-btn'); + + expect(signInButton.length).toBe(1); + expect(signOutButton.length).toBe(0); + + // Close popup + $profileButton.trigger('click'); + + // Perform login + await performFullLoginFlow(); + + // Test logged in state - should show profile popup + $profileButton.trigger('click'); + await popupToAppear(PROFILE_POPUP); + + popupContent = testWindow.$('.profile-popup'); + const newSignInButton = popupContent.find('#phoenix-signin-btn'); + const newSignOutButton = popupContent.find('#phoenix-signout-btn'); + + expect(newSignInButton.length).toBe(0); + expect(newSignOutButton.length).toBe(1); + + // Close popup and logout for cleanup + $profileButton.trigger('click'); + await performFullLogoutFlow(); + + // Test final state - should be back to signin popup + $profileButton.trigger('click'); + await popupToAppear(SIGNIN_POPUP); + + popupContent = testWindow.$('.profile-popup'); + const finalSignInButton = popupContent.find('#phoenix-signin-btn'); + const finalSignOutButton = popupContent.find('#phoenix-signout-btn'); + + expect(finalSignInButton.length).toBe(1); + expect(finalSignOutButton.length).toBe(0); + + // Close popup + $profileButton.trigger('click'); + }); + + it("should show pro branding for user with pro subscription (expired trial)", async function () { + console.log("llgT: Starting pro user with expired trial test"); + + // Setup: Pro subscription + expired trial + setupProUserMock(true); + await setupExpiredTrial(); + + // Verify initial state (no pro branding) + await verifyProBranding(false, "no pro branding to start with"); + + // Perform login + await performFullLoginFlow(); + await verifyProBranding(true, "pro branding to appear after pro user login"); + + // Check profile popup shows pro status (not trial) + const $profileButton = testWindow.$("#user-profile-button"); + $profileButton.trigger('click'); + + // Wait for profile popup to show "phoenix pro " + await verifyProfilePopupContent(VIEW_PHOENIX_PRO, "pro user profile popup"); + + // Close popup + $profileButton.trigger('click'); + + // Perform logout + await performFullLogoutFlow(); + + // For user with pro subscription + expired trial: + // After logout, pro branding should disappear because: + // 1. No server entitlements (logged out) + // 2. Trial is expired (0 days remaining) + await verifyProBranding(false, "Pro branding to disappear after logout"); + }); + + it("should show trial branding for user without pro subscription (active trial)", async function () { + console.log("llgT: Starting trial user test"); + + // Setup: No pro subscription + active trial (15 days) + setupProUserMock(false); + await setupTrialState(15); + + // Verify initial state shows pro branding due to trial + await verifyProBranding(true, "Trial branding to appear initially"); + + // Perform login + await performFullLoginFlow(); + + // Verify pro branding remains after login + await verifyProBranding(true, "after trial user login"); + + // Check profile popup shows trial status + const $profileButton = testWindow.$("#user-profile-button"); + $profileButton.trigger('click'); + await popupToAppear(PROFILE_POPUP); + await verifyProfilePopupContent(VIEW_TRIAL_DAYS_LEFT, + "trial user profile popup for logged in user"); + + // Close popup + $profileButton.trigger('click'); + + // Perform logout + await performFullLogoutFlow(); + + // Verify pro branding remains after logout (trial continues) + await verifyProBranding(true, "Trial branding to remain after logout"); + + // Check profile popup still shows trial status + $profileButton.trigger('click'); + await popupToAppear(SIGNIN_POPUP); + await verifyProfilePopupContent(VIEW_TRIAL_DAYS_LEFT, + "trial user profile popup for logged out user"); + + // Close popup + $profileButton.trigger('click'); + }); + + it("should prioritize pro subscription over trial in profile popup", async function () { + console.log("llgT: Starting trial user with pro subscription test"); + + // Setup: Pro subscription + active trial + setupProUserMock(true); + await setupTrialState(10); + + // Perform login + await performFullLoginFlow(); + + // Verify pro branding appears + await verifyProBranding(true, "Pro branding to appear for pro user"); + + // Check profile popup shows pro status (not trial text) + const $profileButton = testWindow.$("#user-profile-button"); + $profileButton.trigger('click'); + await popupToAppear(PROFILE_POPUP); + + // Should show pro, not trial, since user has paid subscription + await verifyProfilePopupContent(VIEW_PHOENIX_PRO, + "pro+trial user profile should not show trial branding"); + + // Close popup + $profileButton.trigger('click'); + + // Perform logout + await performFullLogoutFlow(); + + // Verify pro branding remains due to trial (even though subscription is gone) + await verifyProBranding(true, "Pro branding should remain after logout as trial user"); + $profileButton.trigger('click'); + await popupToAppear(SIGNIN_POPUP); + await verifyProfilePopupContent(VIEW_TRIAL_DAYS_LEFT, + "trial user profile popup for logged out user"); + + // Close popup + $profileButton.trigger('click'); + }); + + it("should show free branding for user without pro subscription (expired trial)", async function () { + console.log("llgT: Starting desktop trial user test"); + + // Setup: No pro subscription + active trial (15 days) + setupProUserMock(false); + await setupExpiredTrial(); + + // Verify initial state (no pro branding) + await verifyProBranding(false, "no pro branding to start with"); + + // Perform login + await performFullLoginFlow(); + + // Verify pro branding remains after login + await verifyProBranding(false, "after trial free user login"); + + // Check profile popup shows free plan status + const $profileButton = testWindow.$("#user-profile-button"); + $profileButton.trigger('click'); + await popupToAppear(PROFILE_POPUP); + await verifyProfilePopupContent(VIEW_PHOENIX_FREE, + "free plan user profile popup for logged in user"); + + // Close popup + $profileButton.trigger('click'); + + // Perform logout + await performFullLogoutFlow(); + + // Verify pro branding remains after logout (trial continues) + await verifyProBranding(false, "Trial branding to remain after logout"); + + // Check profile popup still shows free plan status as trial expired + $profileButton.trigger('click'); + await popupToAppear(SIGNIN_POPUP); + // not logged in user, we wont show free plan tag as base editor is always free. + expect(testWindow.$(`.profile-popup .trial-plan-info`).length).toBe(0); + + // Close popup + $profileButton.trigger('click'); + }); + } + + exports.setup = setup; + exports.setupTrialState = setupTrialState; + exports.setupExpiredTrial = setupExpiredTrial; + exports.verifyProBranding = verifyProBranding; + exports.verifyProfilePopupContent = verifyProfilePopupContent; + exports.cleanupTrialState = cleanupTrialState; + exports.popupToAppear = popupToAppear; + exports.performFullLogoutFlow = performFullLogoutFlow; + exports.verifyProfileIconBlanked = verifyProfileIconBlanked; + exports.VIEW_TRIAL_DAYS_LEFT = VIEW_TRIAL_DAYS_LEFT; + exports.VIEW_PHOENIX_PRO = VIEW_PHOENIX_PRO; + exports.VIEW_PHOENIX_FREE = VIEW_PHOENIX_FREE; + exports.SIGNIN_POPUP = SIGNIN_POPUP; + exports.PROFILE_POPUP = PROFILE_POPUP; + + // test runner + exports.setupSharedTests = setupSharedTests; +});