Skip to content

Commit ab85423

Browse files
committed
WIP - completed some scenarios and added scaffolding for others. Test env is down so will need to verify these next week.
1 parent 1840707 commit ab85423

File tree

10 files changed

+833
-83
lines changed

10 files changed

+833
-83
lines changed

pages/call_and_recall/generate_invitations_page.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
from pages.base_page import BasePage
33
import pytest
44
import logging
5+
from utils.oracle.oracle import OracleDB
6+
from utils.oracle.subject_selection_query_builder import SubjectSelectionQueryBuilder
7+
from classes.user import User
8+
from classes.subject import Subject
59

610

711
class GenerateInvitationsPage(BasePage):
@@ -17,7 +21,7 @@ def __init__(self, page: Page):
1721
self.display_rs = self.page.locator("#displayRS")
1822
self.refresh_button = self.page.get_by_role("button", name="Refresh")
1923
self.planned_invitations_total = self.page.locator("#col8_total")
20-
self.self_referrals_total = self.page.locator("#col9_total")
24+
self.self_referrals_total = self.page.locator("#col5_total")
2125

2226
def click_generate_invitations_button(self) -> None:
2327
"""This function is used to click the Generate Invitations button."""
@@ -103,3 +107,67 @@ def wait_for_invitation_generation_complete(
103107
else:
104108
logging.warning("No S1 Digital Leaflet batch will be generated")
105109
return False
110+
111+
def check_self_referral_subjects_ready(
112+
self, search_scope: str, volume: str
113+
) -> None:
114+
"""
115+
Asserts whether self-referral subjects are ready to invite.
116+
117+
Args:
118+
search_scope (str): Either "currently" or "now" to determine when to assert
119+
volume (str): Either "some" or "no" — expected number of self-referrals
120+
121+
Raises:
122+
AssertionError if the count doesn't match expectation and search_scope is "now"
123+
"""
124+
assert search_scope in (
125+
"currently",
126+
"now",
127+
), f"Invalid search_scope: '{search_scope}'"
128+
assert volume in ("some", "no"), f"Invalid volume: '{volume}'"
129+
130+
# Confirm we're on the Generate Invitations page
131+
self.verify_generate_invitations_title()
132+
133+
# Extract and clean the count
134+
self_referrals_text = self.self_referrals_total.text_content()
135+
if self_referrals_text is None:
136+
pytest.fail("Failed to read self-referrals total")
137+
self_referrals_count = int(self_referrals_text.strip())
138+
139+
# Determine if condition is met
140+
condition_met = (
141+
self_referrals_count > 0 if volume == "some" else self_referrals_count == 0
142+
)
143+
144+
logging.debug(f"[DEBUG] Self-referral subject count = {self_referrals_count}")
145+
146+
if search_scope == "now":
147+
assert (
148+
condition_met
149+
), f"Expected {volume.upper()} self-referral subjects, but got {self_referrals_count}"
150+
logging.info(
151+
f"[SELF-REFERRAL CHECK] scope='{search_scope}' | expected='{volume}' | actual={self_referrals_count}"
152+
)
153+
154+
def get_self_referral_eligible_subject(self, user: User, subject: Subject) -> str:
155+
criteria = {
156+
"screening status": "Inactive",
157+
"subject age": ">= 75",
158+
"has GP practice": "Yes - active",
159+
"subject hub code": "BCS02",
160+
}
161+
162+
builder = SubjectSelectionQueryBuilder()
163+
query, bind_vars = builder.build_subject_selection_query(
164+
criteria, user, subject
165+
)
166+
167+
oracle = OracleDB()
168+
result = oracle.execute_query(query, bind_vars)
169+
170+
if result.empty:
171+
raise RuntimeError("No eligible subject found")
172+
173+
return result.iloc[0]["subject_nhs_number"]

pages/communication_production/batch_list_page.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from datetime import datetime
44
from utils.calendar_picker import CalendarPicker
55
from utils.table_util import TableUtils
6+
import logging
67

78

89
class BatchListPage(BasePage):
@@ -218,6 +219,16 @@ def get_open_original_batch_row(self) -> Locator | None:
218219
return row
219220
return None
220221

222+
def assert_s83f_batch_present(self) -> None:
223+
self.enter_type_filter("Original")
224+
self.enter_event_code_filter("S83")
225+
self.enter_description_filter("Invitation & Test Kit (Self-referral) (FIT)")
226+
expect(
227+
self.table_data.filter(
228+
has_text="Invitation & Test Kit (Self-referral) (FIT)"
229+
)
230+
).to_be_visible()
231+
221232

222233
class ArchivedBatchListPage(BatchListPage):
223234
"""Archived Batch List Page-specific setup."""
@@ -274,3 +285,57 @@ def get_archived_batch_row(
274285
return row
275286
except IndexError:
276287
return None
288+
289+
290+
class LetterBatchDetailsPage(BasePage):
291+
"""Page object for the Letter Batch Details view."""
292+
293+
def __init__(self, page: Page):
294+
super().__init__(page)
295+
self.page = page
296+
self.letter_table = self.page.locator("table#letterBatchDetails")
297+
298+
def assert_letter_component_present(self, letter_type: str, format: str) -> None:
299+
"""
300+
Asserts that a letter component with the given type and format is listed.
301+
302+
Args:
303+
letter_type (str): The letter type (e.g. "Invitation & Test Kit (Self-referral) (FIT)")
304+
format (str): The file format (e.g. "PDF-A4-V03")
305+
"""
306+
row_locator = self.letter_table.locator("tbody tr")
307+
match_found = False
308+
309+
for i in range(row_locator.count()):
310+
row = row_locator.nth(i)
311+
type_cell = row.locator("td").nth(1)
312+
format_cell = row.locator("td").nth(2)
313+
314+
if (
315+
letter_type.lower() in type_cell.inner_text().lower()
316+
and format.lower() in format_cell.inner_text().lower()
317+
):
318+
match_found = True
319+
break
320+
321+
assert (
322+
match_found
323+
), f"Letter type '{letter_type}' with format '{format}' not found"
324+
325+
def get_first_subject_nhs_number(self) -> str:
326+
"""
327+
Retrieves the NHS number of the first subject listed in the letter batch details table.
328+
329+
Returns:
330+
str: The NHS number of the subject.
331+
"""
332+
table_utils = TableUtils(self.page, "table#letterBatchDetails")
333+
row_data = table_utils.get_row_data_with_headers(0) # First row (0-based index)
334+
335+
nhs_number = row_data.get("NHS Number")
336+
if not nhs_number:
337+
raise RuntimeError("NHS Number not found in the first row of the letter batch table")
338+
339+
logging.info(f"Retrieved NHS number from batch: {nhs_number}")
340+
return nhs_number
341+

pages/screening_subject_search/subject_screening_search_page.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import logging
12
from playwright.sync_api import Page, expect
23
from pages.base_page import BasePage
34
from enum import Enum
@@ -154,6 +155,71 @@ def verify_date_of_birth_filter_input(self, expected_text: str) -> None:
154155
"""Verifies that the Date of Birth filter input field has the expected value."""
155156
expect(self.date_of_birth_filter).to_have_value(expected_text)
156157

158+
def search_by_nhs_number(self, nhs_number: str) -> None:
159+
"""
160+
Searches for a subject using NHS number.
161+
Selects 'Whole Database' to bypass modal dialogs triggered by limited search scopes.
162+
"""
163+
# Step 1: Clear any existing filters
164+
self.click_clear_filters_button()
165+
166+
# Step 2: Enter NHS number
167+
self.nhs_number_filter.fill(nhs_number)
168+
self.nhs_number_filter.press("Tab")
169+
170+
# Step 3: Select 'Whole Database' to avoid confirmation modal
171+
self.select_search_area_option(
172+
SearchAreaSearchOptions.SEARCH_AREA_WHOLE_DATABASE.value
173+
)
174+
175+
# Step 4: Run search
176+
self.click_search_button()
177+
178+
def click_send_kit_button(self) -> None:
179+
"""
180+
Clicks the 'Send a kit' button for self-referral.
181+
Verifies the transition to the 'Send a kit' page and logs errors if that check fails.
182+
"""
183+
send_kit_button = self.page.locator("#self_ref_button")
184+
expect(send_kit_button).to_be_visible()
185+
send_kit_button.click()
186+
187+
try:
188+
expect(self.page.locator("h1")).to_contain_text("Send a kit")
189+
logging.info("[PAGE TRANSITION] Successfully landed on Send a kit page")
190+
except Exception as e:
191+
logging.error(
192+
f"[PAGE TRANSITION FAILED] Did not reach Send a kit page: {e}"
193+
)
194+
raise
195+
196+
def complete_send_kit_form(self, request_from: str = "Subject", previous_kit_status: str = "Lost", note_text: str = "Test") -> None:
197+
"""
198+
Completes the 'Send a kit' form by:
199+
- Selecting who requested the kit
200+
- Selecting previous kit status
201+
- Entering reason text
202+
- Clicking the 'Send a kit' button
203+
204+
Args:
205+
request_from (str): Option to select for who requested the kit.
206+
previous_kit_status (str): Reason for replacing or requesting the kit.
207+
note_text (str): Free-text note to include with the request.
208+
"""
209+
# Fill dropdowns
210+
self.page.locator("#kitRequestFrom").select_option(request_from)
211+
self.page.locator("#previousKit").select_option(previous_kit_status)
212+
213+
# Fill note text
214+
self.page.locator("textarea[data-testid='reasonNote']").fill(note_text)
215+
216+
# Wait for button to become enabled before clicking
217+
send_button = self.page.locator("button[data-testid='sendKitButton']")
218+
expect(send_button).to_be_enabled()
219+
send_button.click()
220+
221+
logging.info("[KIT REQUEST] 'Send a kit' form submitted successfully")
222+
157223

158224
class ScreeningStatusSearchOptions(Enum):
159225
"""Enum for Screening Status Search Options"""

pages/screening_subject_search/subject_screening_summary_page.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,37 @@ def click_close_button(self) -> None:
227227
"""Click on the close button in the temporary address popup."""
228228
self.click(self.close_button)
229229

230+
def assert_view_letter_links_for_event(
231+
self, event_name: str, expected_count: int
232+
) -> None:
233+
"""
234+
Asserts that the specified event row contains the expected number of 'View Letter' links.
235+
236+
Args:
237+
event_name (str): The name of the event to locate (e.g. "S84 - Invitation and Test Kit Sent (Self-referral)")
238+
expected_count (int): The expected number of 'View Letter' links in that row
239+
"""
240+
# Locate the event row by its name
241+
event_row = (
242+
self.page.locator("table#subjectEvents tbody tr")
243+
.filter(has=self.page.locator("td", has_text=event_name))
244+
.first
245+
)
246+
247+
if event_row.count() == 0:
248+
raise RuntimeError(f"Event row for '{event_name}' not found")
249+
250+
# Count the 'View Letter' links in that row
251+
view_links = event_row.locator("a", has_text="View Letter")
252+
actual_count = view_links.count()
253+
254+
logging.info(
255+
f"Found {actual_count} 'View Letter' links for event '{event_name}'"
256+
)
257+
assert (
258+
actual_count == expected_count
259+
), f"Expected {expected_count} 'View Letter' links for event '{event_name}', but found {actual_count}"
260+
230261

231262
class ChangeScreeningStatusOptions(Enum):
232263
"""Enum for Change Screening Status options."""

tests/regression/communications_production/test_basic_archived_batch_list_functionality.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,4 @@ def test_navigation_to_manage_archived_batch_screen(select_user) -> None:
8787

8888
# Step 3: Assert navigation to the Manage Archived Batch page
8989
manage_batch_page = ManageArchivedBatchPage(page)
90-
manage_batch_page.assert_batch_details_visible()
90+
manage_batch_page.assert_archived_batch_details_visible()

0 commit comments

Comments
 (0)