99from pages .session_logged_out import SessionLoggedOutPage
1010from pages .session_timeout_modal import SessionTimeoutModal
1111
12+ FAKE_TIME_TAG_ERROR = "Fake_time tag required in this scenario"
13+
1214
1315@when ("the session expires because of automatic timeout" )
1416def clear_active_session (context ):
@@ -26,14 +28,19 @@ def verify_timeout_logged_out_page(context):
2628 expect (logged_out_page .timeout_description2 ).to_be_visible ()
2729
2830
31+ @when ("I minimise the browser window" )
32+ def minimise_browser_window (context ):
33+ context .active_page .evaluate ("() => window.blur()" )
34+ context .active_page .evaluate ("() => document.dispatchEvent(new Event('visibilitychange'))" )
35+
36+
2937@when ("I set lastActivityTime to be 13 minutes ago" )
3038def set_last_activity_time_13_minutes_ago (context ):
3139 """Call the test support endpoint to set lastActivityTime to 13 minutes in the past"""
3240 # pylint: disable=broad-exception-raised
3341 if "fake_time" not in context .tags :
34- raise Exception ( "Fake_time tag required in this scenario. See README.md" )
42+ raise ValueError ( FAKE_TIME_TAG_ERROR )
3543
36- # Wait a moment to ensure session is established after login
3744 context .active_page .wait_for_timeout (3000 )
3845
3946 # Determine username based on scenario tags
@@ -53,14 +60,12 @@ def set_last_activity_time_13_minutes_ago(context):
5360 break
5461
5562 if not username :
56- raise Exception ("No valid account tag found for setting lastActivityTime" )
63+ raise ValueError ("No valid account tag found for setting lastActivityTime" )
5764
5865 request_id = str (uuid .uuid4 ())
59- print (f"Setting lastActivityTime to 13 minutes ago for { username } . Request ID: { request_id } " )
6066
6167 payload = json .dumps ({"username" : username , "request_id" : request_id })
6268
63- # Call the lambda endpoint to set lastActivityTime to 13 minutes in the past
6469 response = requests .post (
6570 f"{ context .cpts_ui_base_url } /api/test-support-fake-timer" ,
6671 data = payload ,
@@ -73,9 +78,8 @@ def set_last_activity_time_13_minutes_ago(context):
7378
7479 if response .status_code != 200 :
7580 print (f"Failed to set lastActivityTime. Response: { response .status_code } - { response .text } " )
76- raise Exception ( f"Failed to set lastActivityTime: { response .status_code } " )
81+ response .raise_for_status ( )
7782
78- # fast forward clock by 13 minutes to make frontend think time has passed
7983 context .active_page .clock .fast_forward (13 * 60 * 1000 )
8084
8185
@@ -84,48 +88,76 @@ def fast_forward_1_minute(context):
8488 """Fast forward 1 minute to trigger the updateTracker periodic check"""
8589 # pylint: disable=broad-exception-raised
8690 if "fake_time" not in context .tags :
87- raise Exception ( "Fake_time tag required in this scenario. See README.md" )
91+ raise ValueError ( FAKE_TIME_TAG_ERROR )
8892
89- # Fast forward by 1 minute to trigger the 60-second interval
9093 context .active_page .clock .fast_forward (60 * 1000 )
9194
92- # Give a moment for the periodic check to execute and React to update
9395 context .active_page .wait_for_timeout (2000 )
9496
9597
9698@then ("I should see the timeout session modal" )
9799def verify_timeout_session_modal (context ):
98100 """Verify the timeout session modal is displayed"""
99101 modal = SessionTimeoutModal (context .active_page )
100-
101102 context .active_page .wait_for_timeout (3000 )
102103
103- expect (modal .modal_container ).to_be_visible (timeout = 15000 )
104- expect (modal .stay_logged_in_button ).to_be_visible (timeout = 5000 )
105- expect (modal .logout_button ).to_be_visible (timeout = 5000 )
106-
107-
108- @when ("I fast forward 3 minutes so that updateTracker event happens" )
109- def fast_forward_3_minutes (context ):
104+ try :
105+ expect (modal .modal_container ).to_be_visible (timeout = 15000 )
106+ expect (modal .stay_logged_in_button ).to_be_visible (timeout = 5000 )
107+ expect (modal .logout_button ).to_be_visible (timeout = 5000 )
108+ except (TimeoutError , AssertionError ) as e :
109+ print (f"Modal detection failed: { e } " )
110+ page_content = context .active_page .content ()
111+ print (f"Page contains 'session-timeout-modal': { 'session-timeout-modal' in page_content } " )
112+ print (f"Page contains 'For your security': { 'For your security' in page_content } " )
113+ raise AssertionError ("Timeout session modal was not found" )
114+
115+
116+ def _capture_countdown_with_fallback (modal , page , description ):
117+ try :
118+ countdown = modal .countdown_time .text_content (timeout = 2000 )
119+ return countdown
120+ except (TimeoutError , AttributeError ) as e :
121+ print (f"Could not read countdown { description .lower ()} : { e } " )
122+ page_text = page .text_content ("body" )
123+ import re
124+
125+ countdown_match = re .search (r"sign you out in (\d+) seconds?" , page_text )
126+ if countdown_match :
127+ countdown = f"{ countdown_match .group (1 )} seconds"
128+ return countdown
129+ return None
130+
131+
132+ @when ("I fast forward 2 minutes so that updateTracker event happens" )
133+ def fast_forward_2_minutes (context ):
110134 """Wait 2 minutes for natural session timeout"""
111135 # pylint: disable=broad-exception-raised
112136 if "fake_time" not in context .tags :
113- raise Exception ( "Fake_time tag required in this scenario. See README.md" )
137+ raise ValueError ( FAKE_TIME_TAG_ERROR )
114138
115- # Wait 2 minutes in real time for natural timeout
116- context .active_page .wait_for_timeout (120000 )
139+ modal = SessionTimeoutModal (context .active_page )
117140
141+ context .active_page .wait_for_timeout (120000 )
118142
119- @then ("I am redirected to the logged out for inactivity page" )
120- def verify_timed_out_session_and_logged_out_page (context ):
121- """Verify that the session has timed out and user is redirected to session-logged-out URL"""
143+ expected_logout_path = "session-logged-out"
144+ current_url = context .active_page .url
145+ if expected_logout_path in current_url :
146+ return
122147
123- # Wait for navigation to complete
124- context .active_page .wait_for_load_state ("networkidle" , timeout = 10000 )
148+ countdown_after = _capture_countdown_with_fallback (modal , context .active_page , "AFTER 2min wait" )
149+ if not countdown_after :
150+ expected_logout_paths = ["session-logged-out" , "logout" ]
151+ current_url = context .active_page .url
152+ if any (path in current_url for path in expected_logout_paths ):
153+ return
125154
126- # Get current URL and verify it contains session-logged-out
127- current_url = context .active_page .url
128155
129- assert (
130- "/session-logged-out" in current_url
131- ), f"Expected URL to contain '/session-logged-out', but got: { current_url } "
156+ @then ("I am redirected to the logged out for inactivity page" )
157+ def verify_timed_out_session_and_logged_out_page (context ):
158+ """Verify that the session has timed out and user is on the logged out page"""
159+ logged_out_page = SessionLoggedOutPage (context .active_page )
160+ expect (logged_out_page .timeout_session_container ).to_be_visible ()
161+ expect (logged_out_page .timeout_title ).to_have_text ("For your security, we have logged you out" )
162+ expect (logged_out_page .timeout_description ).to_be_visible ()
163+ expect (logged_out_page .timeout_description2 ).to_be_visible ()
0 commit comments