Skip to content

Commit 72fc8da

Browse files
Merge branch 'feature/BCSS-21309-fobt-regression-scenario-6' of github.com:NHSDigital/bcss-playwright into feature/BCSS-21310-selenium-to-playwright-fobtregressiontests-scenario-7
# Conflicts: # pages/screening_subject_search/advance_fobt_screening_episode_page.py # pages/screening_subject_search/reopen_fobt_screening_episode_page.py
2 parents 59ca80e + cf05623 commit 72fc8da

File tree

9 files changed

+157
-63
lines changed

9 files changed

+157
-63
lines changed

pages/datasets/colonoscopy_dataset_page.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
from enum import Enum
44

55

6+
class FitForColonoscopySspOptions(Enum):
7+
YES = "17058"
8+
NO = "17059"
9+
UNABLE_TO_ASSESS = "17954"
10+
11+
612
class ColonoscopyDatasetsPage(BasePage):
713
"""Colonoscopy Datasets Page locators, and methods for interacting with the page"""
814

@@ -44,16 +50,19 @@ def select_asa_grade_option(self, option: str) -> None:
4450
"""
4551
self.select_asa_grade_dropdown.select_option(option)
4652

47-
def select_fit_for_colonoscopy_option(self, option: str) -> None:
53+
def select_fit_for_colonoscopy_option(
54+
self, option: FitForColonoscopySspOptions
55+
) -> None:
4856
"""
49-
This method is designed to select a specific option from the colonoscopy dataset page, Fit for Colonoscopy (SSP) dropdown menu.
57+
Selects a specific option from the 'Fit for Colonoscopy (SSP)' dropdown.
58+
5059
Args:
51-
option (str): The option to be selected. This should be a string that matches one of the available options in the dropdown menu.
52-
Valid options are: "YES", "NO", or "UNABLE_TO_ASSESS".
60+
option (FitForColonoscopySspOptions): Enum member representing the desired option.
61+
5362
Returns:
5463
None
5564
"""
56-
self.select_fit_for_colonoscopy_dropdown.select_option(option)
65+
self.select_fit_for_colonoscopy_dropdown.select_option(option.value)
5766

5867
def click_dataset_complete_radio_button_yes(self) -> None:
5968
"""Clicks the 'Yes' radio button for the dataset complete option."""
@@ -72,9 +81,3 @@ class AsaGradeOptions(Enum):
7281
MORIBUND = "17013"
7382
NOT_APPLICABLE = "17014"
7483
NOT_KNOWN = "17015"
75-
76-
77-
class FitForColonoscopySspOptions(Enum):
78-
YES = "17058"
79-
NO = "17059"
80-
UNABLE_TO_ASSESS = "17954"

pages/screening_subject_search/advance_fobt_screening_episode_page.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@ def __init__(self, page: Page):
5454
self.subsequent_assessment_appointment_required_button = self.page.get_by_role(
5555
"button", name="Subsequent Assessment Appointment Required"
5656
)
57+
self.suitable_for_radiological_test_button = self.page.get_by_role(
58+
"button", name="Suitable for Radiological Test"
59+
)
60+
self.decision_not_to_continue_with_diagnostic_test_button = (
61+
self.page.get_by_role(
62+
"button", name="Decision not to Continue with Diagnostic Test"
63+
)
64+
)
65+
self.waiting_decision_to_proceed_with_diagnostic_test_button = (
66+
self.page.get_by_role(
67+
"button", name="Waiting Decision to Proceed with Diagnostic Test"
68+
)
69+
)
5770

5871
def click_suitable_for_endoscopic_test_button(self) -> None:
5972
"""Click the 'Suitable for Endoscopic Test' button."""
@@ -150,3 +163,21 @@ def click_and_select_subsequent_assessment_appointment_required(
150163
label=option
151164
)
152165
self.safe_accept_dialog(self.subsequent_assessment_appointment_required_button)
166+
167+
def click_suitable_for_radiological_test_button(self) -> None:
168+
"""Click the 'Suitable for Radiological Test' button."""
169+
AdvanceFOBTScreeningEpisodePage(self.page).safe_accept_dialog(
170+
self.suitable_for_radiological_test_button
171+
)
172+
173+
def click_decision_not_to_continue_with_diagnostic_test(self) -> None:
174+
"""Click the 'Decision not to Continue with Diagnostic Test' button."""
175+
AdvanceFOBTScreeningEpisodePage(self.page).safe_accept_dialog(
176+
self.decision_not_to_continue_with_diagnostic_test_button
177+
)
178+
179+
def click_waiting_decision_to_proceed_with_diagnostic_test(self) -> None:
180+
"""Click the 'Waiting Decision to Proceed with Diagnostic Test' button."""
181+
AdvanceFOBTScreeningEpisodePage(self.page).safe_accept_dialog(
182+
self.waiting_decision_to_proceed_with_diagnostic_test_button
183+
)

pages/screening_subject_search/reopen_fobt_screening_episode_page.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ def __init__(self, page: Page):
1818
self.reopen_due_to_subject_or_patient_decision = self.page.get_by_role(
1919
"button", name="Reopen due to subject or patient decision"
2020
)
21+
self.reopen_following_non_response_button = self.page.get_by_role(
22+
"button", name="Reopen following Non-Response"
23+
)
2124

2225
def click_reopen_to_book_an_assessment_button(self) -> None:
2326
"""Click the 'Reopen to book an assessment' button."""
@@ -29,4 +32,6 @@ def click_reopen_episode_for_correction_button(self) -> None:
2932

3033
def click_reopen_due_to_subject_or_patient_decision(self) -> None:
3134
"""Click the 'Reopen due to subject or patient decision' button."""
32-
self.safe_accept_dialog(self.reopen_due_to_subject_or_patient_decision)
35+
36+
def click_reopen_following_non_response_button(self) -> None:
37+
"""Click the 'Reopen following Non-Response' button."""

tests/regression/regression_tests/fobt_regression_tests/test_scenario_3.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from utils.batch_processing import batch_processing
99
from utils.fit_kit import FitKitGeneration, FitKitLogged
1010
from utils import screening_subject_page_searcher
11-
from utils.calendar_picker import CalendarPicker
1211
from utils.sspi_change_steps import SSPIChangeSteps
1312
from utils.appointments import book_appointments
1413
from pages.base_page import BasePage
@@ -22,9 +21,6 @@
2221
from pages.screening_subject_search.advance_fobt_screening_episode_page import (
2322
AdvanceFOBTScreeningEpisodePage,
2423
)
25-
from pages.screening_practitioner_appointments.book_appointment_page import (
26-
BookAppointmentPage,
27-
)
2824
from pages.screening_subject_search.record_diagnosis_date_page import (
2925
RecordDiagnosisDatePage,
3026
)

tests/regression/regression_tests/fobt_regression_tests/test_scenario_5.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from pages.screening_subject_search.record_diagnosis_date_page import (
2222
RecordDiagnosisDatePage,
2323
)
24-
from utils.appointments import mark_appointment_as_dna
24+
from utils.appointments import AppointmentAttendance
2525

2626

2727
@pytest.mark.usefixtures("setup_org_and_appointments")
@@ -61,6 +61,7 @@ def test_scenario_5(page: Page) -> None:
6161
"""
6262

6363
summary_page = SubjectScreeningSummaryPage(page)
64+
attendance = AppointmentAttendance(page)
6465
logging.info(
6566
"[TEST START] Regression - Scenario: 5: DNA colonoscopy assessment twice"
6667
)
@@ -227,7 +228,7 @@ def test_scenario_5(page: Page) -> None:
227228
# And I view the event history for the subject's latest episode
228229
# And I view the latest practitioner appointment in the subject's episode
229230
# And The subject DNAs the practitioner appointment
230-
mark_appointment_as_dna(page, "Patient did not attend")
231+
attendance.mark_as_dna("Patient did not attend")
231232

232233
# And there is a "J11" letter batch for my subject with the exact title "Practitioner Clinic 1st Appointment Non Attendance (Patient)"
233234
# When I process the open "J11" letter batch for my subject
@@ -278,7 +279,7 @@ def test_scenario_5(page: Page) -> None:
278279
# And I view the event history for the subject's latest episode
279280
# And I view the latest practitioner appointment in the subject's episode
280281
# And The subject DNAs the practitioner appointment
281-
mark_appointment_as_dna(page, "Patient did not attend")
282+
attendance.mark_as_dna("Patient did not attend")
282283

283284
# Then my subject has been updated as follows:
284285
subject_assertion(

tests/regression/subject/episodes/datasets/investigation/endoscopy/polypcategories/test_setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ def setup_a99_status(page: Page, df: pd.DataFrame) -> pd.DataFrame:
338338
SubjectDatasetsPage(page).click_colonoscopy_show_datasets()
339339

340340
ColonoscopyDatasetsPage(page).select_fit_for_colonoscopy_option(
341-
FitForColonoscopySspOptions.YES.value
341+
FitForColonoscopySspOptions.YES
342342
)
343343
ColonoscopyDatasetsPage(page).click_dataset_complete_radio_button_yes()
344344
ColonoscopyDatasetsPage(page).save_dataset()

tests/smokescreen/test_compartment_5.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ def test_compartment_5(page: Page, smokescreen_properties: dict) -> None:
143143

144144
ColonoscopyDatasetsPage(page).select_asa_grade_option(AsaGradeOptions.FIT.value)
145145
ColonoscopyDatasetsPage(page).select_fit_for_colonoscopy_option(
146-
FitForColonoscopySspOptions.YES.value
146+
FitForColonoscopySspOptions.YES
147147
)
148148
ColonoscopyDatasetsPage(page).click_dataset_complete_radio_button_yes()
149149
ColonoscopyDatasetsPage(page).save_dataset()

utils/appointments.py

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from pages.screening_practitioner_appointments.book_appointment_page import (
1515
BookAppointmentPage,
1616
)
17-
from pages.screening_subject_search import subject_screening_summary_page
1817
from utils.calendar_picker import CalendarPicker
1918
from utils.user_tools import UserTools
2019
from datetime import datetime
@@ -133,36 +132,81 @@ def book_appointments(page: Page, screening_centre: str, site: str) -> None:
133132
)
134133

135134

136-
def mark_appointment_as_dna(page: Page, non_attendance_reason: str) -> None:
137-
"""
138-
Marks an appointment as DNA (Did Not Attend)
139-
This process starts from the subject screening summary page.
135+
class AppointmentAttendance:
136+
def __init__(self, page: Page):
137+
self.page = page
138+
self.appointment_detail_page = AppointmentDetailPage(page)
139+
self.subject_screening_summary_page = SubjectScreeningSummaryPage(page)
140+
self.episode_events_and_notes_page = EpisodeEventsAndNotesPage(page)
141+
142+
def mark_as_dna(self, non_attendance_reason: str) -> None:
143+
"""
144+
Marks an appointment as DNA (Did Not Attend)
145+
This process starts from the subject screening summary page.
146+
147+
This method navigates through the subject's episode and appointment pages,
148+
selects the 'Attendance' radio option, and sets the given DNA reason.
149+
150+
Args:
151+
page (Page): Playwright page object.
152+
non_attendance_reason (str): Reason for non-attendance. Must match one of the following:
153+
- "Patient did not attend"
154+
- "Screening Centre did not attend"
155+
- "Patient and Screening Centre did not attend"
156+
157+
Raises:
158+
AssertionError: If expected elements are not found or interaction fails.
159+
"""
160+
logging.info(
161+
f"[APPOINTMENT DNA] Starting DNA flow with reason: {non_attendance_reason}"
162+
)
163+
self.subject_screening_summary_page.click_list_episodes()
164+
self.subject_screening_summary_page.click_view_events_link()
165+
self.episode_events_and_notes_page.click_most_recent_view_appointment_link()
166+
self.appointment_detail_page.check_attendance_radio()
167+
self.page.locator("#UI_NON_ATTENDANCE_REASON").select_option(
168+
label=non_attendance_reason
169+
)
170+
self.appointment_detail_page.click_save_button(accept_dialog=True)
140171

141-
This method navigates through the subject's episode and appointment pages,
142-
selects the 'Attendance' radio option, and sets the given DNA reason.
172+
logging.info("[APPOINTMENT DNA] DNA flow completed successfully")
143173

144-
Args:
145-
page (Page): Playwright page object.
146-
non_attendance_reason (str): Reason for non-attendance. Must match one of the following:
147-
- "Patient did not attend"
148-
- "Screening Centre did not attend"
149-
- "Patient and Screening Centre did not attend"
174+
def mark_as_attended(self) -> None:
175+
"""
176+
Marks an appointment as attended and logs the auto-filled attendance details.
177+
This process starts from the subject screening summary page.
150178
151-
Raises:
152-
AssertionError: If expected elements are not found or interaction fails.
153-
"""
154-
appointment_detail_page = AppointmentDetailPage(page)
155-
subject_screening_summary_page = SubjectScreeningSummaryPage(page)
156-
episode_events_and_notes_page = EpisodeEventsAndNotesPage(page)
179+
This method navigates through the subject's episode and appointment pages,
180+
selects the 'Attendance' radio option, checks the 'Attended' checkbox,
181+
and logs the resulting date and time values.
157182
158-
logging.info(
159-
f"[APPOINTMENT DNA] Starting DNA flow with reason: {non_attendance_reason}"
160-
)
161-
subject_screening_summary_page.click_list_episodes()
162-
subject_screening_summary_page.click_view_events_link()
163-
episode_events_and_notes_page.click_most_recent_view_appointment_link()
164-
appointment_detail_page.check_attendance_radio()
165-
page.locator("#UI_NON_ATTENDANCE_REASON").select_option(label=non_attendance_reason)
166-
appointment_detail_page.click_save_button(accept_dialog=True)
167-
168-
logging.info("[APPOINTMENT DNA] DNA flow completed successfully")
183+
Args:
184+
page (Page): Playwright page object.
185+
186+
Raises:
187+
AssertionError: If expected elements are not found or interaction fails.
188+
"""
189+
logging.info("[APPOINTMENT ATTENDED] Starting attended flow")
190+
191+
self.subject_screening_summary_page.click_list_episodes()
192+
self.subject_screening_summary_page.click_view_events_link()
193+
self.episode_events_and_notes_page.click_most_recent_view_appointment_link()
194+
self.appointment_detail_page.check_attendance_radio()
195+
196+
# Check the 'Attended' checkbox
197+
attended_checkbox = self.page.locator("#UI_ATTENDED")
198+
attended_checkbox.check()
199+
200+
# Log the auto-filled attendance details
201+
attended_date = self.page.locator("#UI_ATTENDED_DATE").input_value()
202+
time_from = self.page.locator("#UI_ATTENDED_TIME_FROM").input_value()
203+
time_to = self.page.locator("#UI_ATTENDED_TIME_TO").input_value()
204+
meeting_mode = self.page.locator("#UI_NEW_MEETING_MODE").input_value()
205+
206+
logging.info(
207+
f"[APPOINTMENT ATTENDED] How Attended: {meeting_mode}, Date: {attended_date}, Time From: {time_from}, Time To: {time_to}"
208+
)
209+
210+
self.appointment_detail_page.click_save_button(accept_dialog=True)
211+
212+
logging.info("[APPOINTMENT ATTENDED] Attended flow completed successfully")

utils/oracle/oracle.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,25 +44,38 @@ def disconnect_from_db(self, conn: oracledb.Connection) -> None:
4444
logging.debug("Connection Closed")
4545

4646
def exec_bcss_timed_events(
47-
self, nhs_number_df: pd.DataFrame
48-
) -> None: # Executes bcss_timed_events when given NHS numbers
47+
self,
48+
nhs_number_df: Optional[pd.DataFrame] = None,
49+
nhs_number: Optional[str] = None,
50+
) -> None:
4951
"""
50-
This function is used to execute bcss_timed_events against NHS Numbers.
51-
It expects the nhs_numbers to be in a dataframe, and runs a for loop to get the subject_screening_id for each nhs number
52-
Once a subject_screening_id is retrieved, it will then run the command: exec bcss_timed_events [<subject_id>,'Y']
52+
Executes bcss_timed_events for either a DataFrame of NHS numbers or a single NHS number.
5353
5454
Args:
55-
nhs_number_df (pd.DataFrame): A dataframe containing all of the NHS numbers as separate rows
55+
nhs_number_df (Optional[pd.DataFrame]): DataFrame with NHS numbers under 'subject_nhs_number'.
56+
nhs_number (Optional[str]): A single NHS number.
57+
58+
Raises:
59+
ValueError: If neither nhs_number_df nor nhs_number is provided.
5660
"""
5761
conn = self.connect_to_db()
5862
try:
59-
for index, row in nhs_number_df.iterrows():
60-
subject_id = self.get_subject_id_from_nhs_number(
61-
row["subject_nhs_number"]
62-
)
63+
subject_ids = []
64+
65+
if nhs_number_df is not None:
66+
subject_ids = [
67+
self.get_subject_id_from_nhs_number(row["subject_nhs_number"])
68+
for _, row in nhs_number_df.iterrows()
69+
]
70+
elif nhs_number is not None:
71+
subject_ids = [self.get_subject_id_from_nhs_number(nhs_number)]
72+
else:
73+
raise ValueError("Must provide either nhs_number_df or nhs_number")
74+
75+
for subject_id in subject_ids:
6376
try:
6477
logging.info(
65-
f"[ORACLE] Attempting to execute stored procedure: {f"'bcss_timed_events', [{subject_id},'Y']"}"
78+
f"[ORACLE] Attempting to execute stored procedure: 'bcss_timed_events', [{subject_id}, 'Y']"
6679
)
6780
cursor = conn.cursor()
6881
cursor.callproc("bcss_timed_events", [subject_id, "Y"])
@@ -71,9 +84,10 @@ def exec_bcss_timed_events(
7184
logging.error(
7285
f"[ORACLE] Failed to execute stored procedure with execution error: {spExecutionError}"
7386
)
87+
7488
except Exception as queryExecutionError:
7589
logging.error(
76-
f"[ORACLE] Failed to to extract subject ID with error: {queryExecutionError}"
90+
f"[ORACLE] Failed to extract subject ID with error: {queryExecutionError}"
7791
)
7892
finally:
7993
if conn is not None:

0 commit comments

Comments
 (0)