Skip to content

Commit 280336c

Browse files
committed
test: browser login integ tests
1 parent 7d1c6a1 commit 280336c

File tree

4 files changed

+303
-2
lines changed

4 files changed

+303
-2
lines changed

src/nls/root/strings.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,8 +1605,8 @@ define({
16051605
"OPEN_SIGN_IN_URL": "Open Sign In Page",
16061606
"PROFILE_POP_TITLE": "{APP_NAME} Account",
16071607
"PROFILE_SIGN_IN": "Sign in to your account",
1608-
"CONTACT_SUPPORT": "Contact support",
1609-
"SIGN_OUT": "Sign out",
1608+
"CONTACT_SUPPORT": "Contact Support",
1609+
"SIGN_OUT": "Sign Out",
16101610
"ACCOUNT_DETAILS": "Account Details",
16111611
"LOGIN_REFRESH": "Check Login Status",
16121612
"SIGN_IN_WAITING_TITLE": "Waiting for Sign In",

src/services/login-browser.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,15 @@ define(function (require, exports, module) {
425425
init();
426426
}
427427

428+
// Test-only exports for integration testing
429+
if (Phoenix.isTestWindow) {
430+
window._test_login_browser_exports = {
431+
setFetchFn: function _setFetchFn(fn) {
432+
fetchFn = fn;
433+
}
434+
};
435+
}
436+
428437
// public exports
429438
exports.isLoggedIn = isLoggedIn;
430439

test/UnitTestSuite.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ define(function (require, exports, module) {
119119
require("spec/Generic-integ-test");
120120
require("spec/spacing-auto-detect-integ-test");
121121
require("spec/promotions-integ-test");
122+
require("spec/login-browser-integ-test");
122123
require("spec/LocalizationUtils-test");
123124
require("spec/ScrollTrackHandler-integ-test");
124125
require("spec/login-utils-test");
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
/*
2+
* GNU AGPL-3.0 License
3+
*
4+
* Copyright (c) 2021 - present core.ai . All rights reserved.
5+
*
6+
* This program is free software: you can redistribute it and/or modify it
7+
* under the terms of the GNU Affero General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
14+
* for more details.
15+
*
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
18+
*
19+
*/
20+
21+
/*global describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, awaitsFor, awaitsForDone, awaits*/
22+
23+
define(function (require, exports, module) {
24+
25+
const SpecRunnerUtils = require("spec/SpecRunnerUtils");
26+
27+
describe("integration:LoginBrowser", function () {
28+
29+
if (Phoenix.isNativeApp) {
30+
// Browser login tests are not applicable for native apps
31+
it("This test disabled in native apps as its browser login tests", function () {
32+
expect(1).toEqual(1);
33+
});
34+
return;
35+
}
36+
37+
let testWindow,
38+
LoginServiceExports,
39+
LoginBrowserExports,
40+
originalOpen,
41+
originalFetch;
42+
43+
beforeAll(async function () {
44+
testWindow = await SpecRunnerUtils.createTestWindowAndRun();
45+
46+
// Wait for test exports to be available (KernalModeTrust is sandboxed, use test exports)
47+
await awaitsFor(
48+
function () {
49+
return testWindow._test_login_service_exports &&
50+
testWindow._test_login_browser_exports;
51+
},
52+
"Test exports to be available",
53+
5000
54+
);
55+
56+
// Access the login service exports from the test window
57+
LoginServiceExports = testWindow._test_login_service_exports;
58+
LoginBrowserExports = testWindow._test_login_browser_exports;
59+
60+
// Store original functions for restoration
61+
originalOpen = testWindow.open;
62+
if (LoginServiceExports.setFetchFn) {
63+
originalFetch = testWindow.fetch;
64+
}
65+
66+
// Wait for profile menu to be initialized
67+
await awaitsFor(
68+
function () {
69+
return testWindow.$("#user-profile-button").length > 0;
70+
},
71+
"Profile button to be available",
72+
3000
73+
);
74+
}, 30000);
75+
76+
afterAll(async function () {
77+
// Restore original functions
78+
if (originalOpen) {
79+
testWindow.open = originalOpen;
80+
}
81+
82+
// Restore all fetch function overrides
83+
if (originalFetch) {
84+
if (LoginServiceExports && LoginServiceExports.setFetchFn) {
85+
LoginServiceExports.setFetchFn(originalFetch);
86+
}
87+
if (LoginBrowserExports && LoginBrowserExports.setFetchFn) {
88+
LoginBrowserExports.setFetchFn(originalFetch);
89+
}
90+
}
91+
92+
testWindow = null;
93+
LoginServiceExports = null;
94+
LoginBrowserExports = null;
95+
originalOpen = null;
96+
originalFetch = null;
97+
await SpecRunnerUtils.closeTestWindow();
98+
}, 30000);
99+
100+
beforeEach(function () {
101+
// Ensure we start each test in a logged-out state
102+
// Note: We can't easily reset login state, so tests should handle this
103+
});
104+
105+
describe("Browser Login Flow", function () {
106+
107+
it("should complete browser login flow successfully", async function () {
108+
// Ensure user starts logged out
109+
const initialLoginState = LoginServiceExports.LoginService.isLoggedIn();
110+
if (initialLoginState) {
111+
throw new Error("Login browser tests require user to be logged out. Please log out before running these tests.");
112+
}
113+
expect(initialLoginState).toBe(false);
114+
115+
// Step 1: Click profile icon to open login popup
116+
const $profileButton = testWindow.$("#user-profile-button");
117+
expect($profileButton.length).toBe(1);
118+
119+
// Debug: Check if the button is visible and clickable
120+
expect($profileButton.is(':visible')).toBe(true);
121+
122+
// Try jQuery trigger first
123+
$profileButton.trigger('click');
124+
125+
// Step 2: Wait for login popup to appear (check both modal and profile-popup)
126+
await awaitsFor(
127+
function () {
128+
return testWindow.$('.modal').length > 0 || testWindow.$('.profile-popup').length > 0;
129+
},
130+
"Login popup to appear",
131+
3000
132+
);
133+
134+
// Get the popup content (could be modal or profile-popup)
135+
let popupContent = testWindow.$('.modal');
136+
if (popupContent.length === 0) {
137+
popupContent = testWindow.$('.profile-popup');
138+
}
139+
expect(testWindow.$('.profile-popup').is(':visible')).toBe(true);
140+
141+
// Verify it's the login popup (contains sign in button)
142+
const signInButton = popupContent.find('#phoenix-signin-btn');
143+
expect(signInButton.length).toBe(1);
144+
expect(signInButton.text().trim().toLocaleLowerCase()).toContain('sign in');
145+
146+
// Step 3: Mock successful login response FIRST (using login-browser test exports)
147+
console.log("llgT: Setting up fetch mock EARLY using login-browser exports");
148+
console.log("llgT: LoginBrowserExports.setFetchFn available?", !!LoginBrowserExports.setFetchFn);
149+
150+
if (LoginBrowserExports.setFetchFn) {
151+
LoginBrowserExports.setFetchFn((url, options) => {
152+
console.log("llgT: login-browser fetchFn called with URL:", url);
153+
console.log("llgT: login-browser fetchFn called with options:", options);
154+
return Promise.resolve({
155+
ok: true,
156+
status: 200,
157+
json: () => Promise.resolve({
158+
isSuccess: true,
159+
160+
firstName: "Test",
161+
lastName: "User",
162+
customerID: "test-customer-id",
163+
loginTime: Date.now(),
164+
profileIcon: {
165+
initials: "TU",
166+
color: "#14b8a6"
167+
}
168+
})
169+
});
170+
});
171+
console.log("llgT: login-browser fetch mock set up successfully");
172+
} else {
173+
console.log("llgT: LoginBrowserExports.setFetchFn not available!");
174+
}
175+
176+
// Step 4: Mock window.open to capture account URL opening
177+
let capturedURL = null;
178+
let capturedTarget = null;
179+
testWindow.open = function(url, target) {
180+
capturedURL = url;
181+
capturedTarget = target;
182+
// Return a mock window object with focus method
183+
return {
184+
focus: function() {},
185+
close: function() {},
186+
closed: false
187+
};
188+
};
189+
190+
// Step 5: Click the sign in button
191+
console.log("llgT: About to click sign in button");
192+
signInButton.trigger('click');
193+
194+
// Verify window.open was called with the account URL
195+
expect(capturedURL).toBeDefined();
196+
expect(capturedURL).toContain('phcode.dev'); // Should contain account domain
197+
expect(capturedTarget).toBe('_blank');
198+
199+
// Step 6: Wait for login waiting dialog to appear
200+
await testWindow.__PR.waitForModalDialog(".browser-login-waiting-dialog");
201+
202+
// Get waiting dialog content
203+
console.log("llgT: Looking for waiting dialog...");
204+
let waitingDialog = testWindow.$('.modal');
205+
if (waitingDialog.length === 0) {
206+
// Look for the waiting dialog by its unique button
207+
waitingDialog = testWindow.$('[data-button-id="check"]').closest('div');
208+
}
209+
console.log("llgT: Waiting dialog found:", waitingDialog.length);
210+
expect(waitingDialog.length).toBeGreaterThan(0);
211+
212+
// Verify waiting dialog content
213+
const checkNowButton = testWindow.$('[data-button-id="check"]');
214+
expect(checkNowButton.length).toBe(1);
215+
expect(checkNowButton.text().trim()).toContain('Check Now');
216+
217+
// Step 7: Click "Check Now" button to verify login (in iframe testWindow)
218+
// Click the Check Now button
219+
console.log("llgT: Clicking Check Now button");
220+
checkNowButton.trigger('click');
221+
222+
// Wait for login verification to complete and success message
223+
await awaitsFor(
224+
function () {
225+
const isLoggedIn = LoginServiceExports.LoginService.isLoggedIn();
226+
if (isLoggedIn) {
227+
console.log("llgT: User is now logged in!");
228+
}
229+
return isLoggedIn;
230+
},
231+
"User to be logged in",
232+
5000
233+
);
234+
235+
// Verify user is now logged in
236+
expect(LoginServiceExports.LoginService.isLoggedIn()).toBe(true);
237+
238+
// Verify profile icon has been updated with user initials
239+
const $profileIcon = testWindow.$("#user-profile-button");
240+
const profileIconContent = $profileIcon.html();
241+
expect(profileIconContent).toContain('svg'); // Should contain SVG element
242+
expect(profileIconContent).toContain('TU'); // Should contain user initials
243+
244+
// Step 8: Wait for login dialog to close automatically (or close it manually)
245+
await awaitsFor(
246+
function () {
247+
return testWindow.$('.modal:visible').length === 0 &&
248+
testWindow.$('[data-button-id="check"]:visible').length === 0;
249+
},
250+
"Login dialog to close",
251+
3000
252+
);
253+
254+
// Step 9: Click profile icon again to verify profile popup appears
255+
$profileButton.trigger('click');
256+
257+
// Wait for profile popup (not login popup) to appear
258+
await awaitsFor(
259+
function () {
260+
return testWindow.$('.modal').length > 0 ||
261+
testWindow.$('.profile-popup').length > 0;
262+
},
263+
"Profile popup to appear",
264+
3000
265+
);
266+
267+
// Get the new popup content
268+
let newPopupContent = testWindow.$('.modal');
269+
if (newPopupContent.length === 0) {
270+
newPopupContent = testWindow.$('.profile-popup');
271+
}
272+
273+
// Verify it's the profile popup (contains sign out button, not sign in)
274+
const signOutButton = newPopupContent.find('#phoenix-signout-btn');
275+
const newSignInButton = newPopupContent.find('#phoenix-signin-btn');
276+
277+
expect(signOutButton.length).toBe(1); // Should have sign out button
278+
expect(newSignInButton.length).toBe(0); // Should NOT have sign in button
279+
280+
// Close the profile popup (try different methods)
281+
if (testWindow.$('.modal').length > 0) {
282+
testWindow.__PR.clickDialogButtonID(testWindow.__PR.Dialogs.DIALOG_BTN_CANCEL);
283+
await testWindow.__PR.waitForModalDialogClosed(".modal");
284+
} else {
285+
// If it's not a modal, just click outside or use popup close method
286+
$profileButton.trigger('click'); // Toggle to close
287+
}
288+
});
289+
});
290+
});
291+
});

0 commit comments

Comments
 (0)