@@ -186,21 +186,11 @@ describe('Auth', () => {
186186 } ) ;
187187 } ) ;
188188
189- describe ( 'loginWithPopup - popup closure detection ' , ( ) => {
189+ describe ( 'loginWithPopup' , ( ) => {
190190 let mockUserManager : any ;
191- let mockPopupWindow : any ;
192- let originalWindowOpen : any ;
193191 let originalCryptoRandomUUID : any ;
194192
195193 beforeEach ( ( ) => {
196- // Mock window.open to return a controllable popup reference
197- originalWindowOpen = window . open ;
198- mockPopupWindow = {
199- closed : false ,
200- close : jest . fn ( ) ,
201- } ;
202- window . open = jest . fn ( ) . mockReturnValue ( mockPopupWindow ) ;
203-
204194 // Mock crypto.randomUUID
205195 originalCryptoRandomUUID = window . crypto . randomUUID ;
206196 window . crypto . randomUUID = jest . fn ( ) . mockReturnValue ( 'test-popup-id' ) ;
@@ -212,17 +202,13 @@ describe('Auth', () => {
212202 extraQueryParams : { } ,
213203 } ,
214204 } ;
215-
216- jest . useFakeTimers ( ) ;
217205 } ) ;
218206
219207 afterEach ( ( ) => {
220- window . open = originalWindowOpen ;
221208 window . crypto . randomUUID = originalCryptoRandomUUID ;
222- jest . useRealTimers ( ) ;
223209 } ) ;
224210
225- it ( 'resolves successfully when authentication completes before popup closes ' , async ( ) => {
211+ it ( 'successfully completes authentication and returns user ' , async ( ) => {
226212 const mockOidcUser = {
227213 id_token : 'token' ,
228214 access_token : 'access' ,
@@ -236,12 +222,7 @@ describe('Auth', () => {
236222 passport : undefined ,
237223 } ) ;
238224
239- // Simulate successful authentication that resolves quickly
240- mockUserManager . signinPopup . mockImplementation ( ( ) => new Promise ( ( resolve ) => {
241- setTimeout ( ( ) => {
242- resolve ( mockOidcUser ) ;
243- } , 100 ) ;
244- } ) ) ;
225+ mockUserManager . signinPopup . mockResolvedValue ( mockOidcUser ) ;
245226
246227 const auth = Object . create ( Auth . prototype ) as Auth ;
247228 ( auth as any ) . userManager = mockUserManager ;
@@ -250,60 +231,17 @@ describe('Auth', () => {
250231 } ;
251232 getDetailMock . mockReturnValue ( 'runtime-id-value' ) ;
252233
253- // Pass directLoginOptions to skip embeddedLoginPrompt
254- const loginPromise = ( auth as any ) . loginWithPopup ( {
234+ const user = await ( auth as any ) . loginWithPopup ( {
255235 directLoginMethod : 'google' ,
256236 marketingConsentStatus : 'opted_in' ,
257237 } ) ;
258238
259- // Fast-forward time to complete authentication
260- jest . advanceTimersByTime ( 150 ) ;
261-
262- const user = await loginPromise ;
263-
264239 expect ( user ) . toBeDefined ( ) ;
265240 expect ( user . profile . sub ) . toBe ( 'user-123' ) ;
266- expect ( mockPopupWindow . close ) . toHaveBeenCalled ( ) ;
267- } ) ;
268-
269- it ( 'rejects with error when popup is closed manually before authentication completes' , async ( ) => {
270- // Simulate authentication that takes a long time
271- mockUserManager . signinPopup . mockImplementation ( ( ) => new Promise ( ( resolve ) => {
272- setTimeout ( ( ) => {
273- resolve ( {
274- id_token : 'token' ,
275- access_token : 'access' ,
276- refresh_token : 'refresh' ,
277- expired : false ,
278- profile : { sub : 'user-123' , email : 'test@example.com' , nickname : 'tester' } ,
279- } ) ;
280- } , 5000 ) ; // Takes 5 seconds
281- } ) ) ;
282-
283- const auth = Object . create ( Auth . prototype ) as Auth ;
284- ( auth as any ) . userManager = mockUserManager ;
285- ( auth as any ) . config = {
286- popupOverlayOptions : { disableHeadlessLoginPromptOverlay : true } ,
287- } ;
288- getDetailMock . mockReturnValue ( 'runtime-id-value' ) ;
289-
290- // Pass directLoginOptions to skip embeddedLoginPrompt
291- const loginPromise = ( auth as any ) . loginWithPopup ( {
292- directLoginMethod : 'google' ,
293- marketingConsentStatus : 'opted_in' ,
294- } ) ;
295-
296- // Simulate user closing popup after 1 second
297- jest . advanceTimersByTime ( 1000 ) ;
298- mockPopupWindow . closed = true ;
299-
300- // Advance time to trigger the polling check
301- jest . advanceTimersByTime ( 500 ) ;
302-
303- await expect ( loginPromise ) . rejects . toThrow ( 'Popup closed by user' ) ;
241+ expect ( user . profile . email ) . toBe ( 'test@example.com' ) ;
304242 } ) ;
305243
306- it ( 'does not reject when popup closes after authentication completes successfully ' , async ( ) => {
244+ it ( 'calls signinPopup with correct configuration ' , async ( ) => {
307245 const mockOidcUser = {
308246 id_token : 'token' ,
309247 access_token : 'access' ,
@@ -317,12 +255,7 @@ describe('Auth', () => {
317255 passport : undefined ,
318256 } ) ;
319257
320- // Simulate successful authentication
321- mockUserManager . signinPopup . mockImplementation ( ( ) => new Promise ( ( resolve ) => {
322- setTimeout ( ( ) => {
323- resolve ( mockOidcUser ) ;
324- } , 100 ) ;
325- } ) ) ;
258+ mockUserManager . signinPopup . mockResolvedValue ( mockOidcUser ) ;
326259
327260 const auth = Object . create ( Auth . prototype ) as Auth ;
328261 ( auth as any ) . userManager = mockUserManager ;
@@ -331,41 +264,24 @@ describe('Auth', () => {
331264 } ;
332265 getDetailMock . mockReturnValue ( 'runtime-id-value' ) ;
333266
334- // Pass directLoginOptions to skip embeddedLoginPrompt
335- const loginPromise = ( auth as any ) . loginWithPopup ( {
267+ await ( auth as any ) . loginWithPopup ( {
336268 directLoginMethod : 'google' ,
337269 marketingConsentStatus : 'opted_in' ,
338270 } ) ;
339271
340- // Complete authentication
341- jest . advanceTimersByTime ( 150 ) ;
342-
343- // Now close the popup AFTER auth completed
344- mockPopupWindow . closed = true ;
345-
346- // Advance polling interval - should NOT reject
347- jest . advanceTimersByTime ( 600 ) ;
348-
349- const user = await loginPromise ;
350- expect ( user ) . toBeDefined ( ) ;
351- expect ( user . profile . sub ) . toBe ( 'user-123' ) ;
272+ expect ( mockUserManager . signinPopup ) . toHaveBeenCalledWith (
273+ expect . objectContaining ( {
274+ popupWindowTarget : 'test-popup-id' ,
275+ popupWindowFeatures : { width : 410 , height : 450 } ,
276+ popupAbortOnClose : true ,
277+ extraQueryParams : expect . any ( Object ) ,
278+ } ) ,
279+ ) ;
352280 } ) ;
353281
354- it ( 'stops polling after authentication completes' , async ( ) => {
355- const mockOidcUser = {
356- id_token : 'token' ,
357- access_token : 'access' ,
358- refresh_token : 'refresh' ,
359- expired : false ,
360- profile : { sub : 'user-123' , email : 'test@example.com' , nickname : 'tester' } ,
361- } ;
362-
363- ( decodeJwtPayload as jest . Mock ) . mockReturnValue ( {
364- username : 'username123' ,
365- passport : undefined ,
366- } ) ;
367-
368- mockUserManager . signinPopup . mockResolvedValue ( mockOidcUser ) ;
282+ it ( 'rejects when signinPopup rejects' , async ( ) => {
283+ const error = new Error ( 'Authentication failed' ) ;
284+ mockUserManager . signinPopup . mockRejectedValue ( error ) ;
369285
370286 const auth = Object . create ( Auth . prototype ) as Auth ;
371287 ( auth as any ) . userManager = mockUserManager ;
@@ -374,26 +290,10 @@ describe('Auth', () => {
374290 } ;
375291 getDetailMock . mockReturnValue ( 'runtime-id-value' ) ;
376292
377- const setIntervalSpy = jest . spyOn ( global , 'setInterval' ) ;
378- const clearIntervalSpy = jest . spyOn ( global , 'clearInterval' ) ;
379-
380- // Pass directLoginOptions to skip embeddedLoginPrompt
381- const loginPromise = ( auth as any ) . loginWithPopup ( {
293+ await expect ( ( auth as any ) . loginWithPopup ( {
382294 directLoginMethod : 'google' ,
383295 marketingConsentStatus : 'opted_in' ,
384- } ) ;
385-
386- // Let authentication complete
387- await Promise . resolve ( ) ;
388- jest . runAllTimers ( ) ;
389-
390- await loginPromise ;
391-
392- // Verify that clearInterval was called (timer stopped)
393- expect ( clearIntervalSpy ) . toHaveBeenCalled ( ) ;
394-
395- setIntervalSpy . mockRestore ( ) ;
396- clearIntervalSpy . mockRestore ( ) ;
296+ } ) ) . rejects . toThrow ( 'Authentication failed' ) ;
397297 } ) ;
398298 } ) ;
399299} ) ;
0 commit comments