Skip to content

Commit ddbf909

Browse files
Completing scenario 18 (#154)
<!-- markdownlint-disable-next-line first-line-heading --> ## Description <!-- Describe your changes in detail. --> Migrating scenario 18 of FOBTRegressionTests into playwright ## Context <!-- Why is this change required? What problem does it solve? --> Migrating scenario 18 of FOBTRegressionTests into playwright ## Type of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply. --> - [x] Refactoring (non-breaking change) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would change existing functionality) - [ ] Bug fix (non-breaking change which fixes an issue) ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [x] I am familiar with the [contributing guidelines](https://github.com/nhs-england-tools/playwright-python-blueprint/blob/main/CONTRIBUTING.md) - [x] I have followed the code style of the project - [x] I have added tests to cover my changes (where appropriate) - [x] I have updated the documentation accordingly - [ ] This PR is a result of pair or mob programming --- ## Sensitive Information Declaration To ensure the utmost confidentiality and protect your and others privacy, we kindly ask you to NOT including [PII (Personal Identifiable Information) / PID (Personal Identifiable Data)](https://digital.nhs.uk/data-and-information/keeping-data-safe-and-benefitting-the-public) or any other sensitive data in this PR (Pull Request) and the codebase changes. We will remove any PR that do contain any sensitive information. We really appreciate your cooperation in this matter. - [x] I confirm that neither PII/PID nor sensitive data are included in this PR and the codebase changes.
1 parent 23ba447 commit ddbf909

14 files changed

+1526
-36
lines changed

pages/screening_subject_search/advance_fobt_screening_episode_page.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ def __init__(self, page: Page):
3737
self.invite_for_diagnostic_test_button = self.page.get_by_role(
3838
"button", name="Invite for Diagnostic Test >>"
3939
)
40+
self.cancer_result_refer_mdt_button = self.page.get_by_role(
41+
"button", name="Cancer Result, Refer MDT >>"
42+
)
4043
self.attend_diagnostic_test_button = self.page.get_by_role(
4144
"button", name="Attend Diagnostic Test"
4245
)
@@ -136,6 +139,10 @@ def __init__(self, page: Page):
136139
"button", name="Redirect to Establish Attendance at Appointment"
137140
)
138141
)
142+
self.date_of_symptomatic_procedure_input = self.page.get_by_role(
143+
"textbox", name="Date of Symptomatic Procedure"
144+
)
145+
139146
# Contact recording locators
140147
self.contact_direction_dropdown = self.page.get_by_label("Contact Direction")
141148
self.contact_made_between_dropdown = self.page.get_by_label(
@@ -150,6 +157,7 @@ def __init__(self, page: Page):
150157
self.patient_contacted_dropdown = self.page.get_by_label("Patient Contacted")
151158
self.outcome_dropdown = self.page.get_by_label("Outcome")
152159
self.save_button = self.page.locator("input[name='UI_BUTTON_SAVE']")
160+
153161
# CT Colonography invitation locators
154162
self.ct_colonography_appointment_date_input = self.page.locator(
155163
"#UI_APPT_DATE_38"
@@ -177,6 +185,10 @@ def click_invite_for_diagnostic_test_button(self) -> None:
177185
"""Click the 'Invite for Diagnostic Test' button."""
178186
self.safe_accept_dialog(self.invite_for_diagnostic_test_button)
179187

188+
def click_cancer_result_refer_mdt_button(self) -> None:
189+
"""Click the 'Cancer Result, Refer MDT >>' button."""
190+
self.safe_accept_dialog(self.cancer_result_refer_mdt_button)
191+
180192
def click_attend_diagnostic_test_button(self) -> None:
181193
"""Click the 'Attend Diagnostic Test' button."""
182194
self.click(self.attend_diagnostic_test_button)
@@ -498,3 +510,13 @@ def click_redirect_to_establish_attendance_at_appointment_button(
498510
self.safe_accept_dialog(
499511
self.redirect_to_establish_attendance_at_appointment_button
500512
)
513+
514+
def enter_date_of_symptomatic_procedure(self, date: datetime) -> None:
515+
"""
516+
Enter a date of symptomatic procedure.
517+
Args:
518+
date (datetime): The date to enter
519+
"""
520+
CalendarPicker(self.page).calendar_picker_ddmmyyyy(
521+
date, self.date_of_symptomatic_procedure_input
522+
)

pages/screening_subject_search/close_fobt_screening_episode_page.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ def close_fobt_screening_episode(self, reason_text: str) -> None:
3232

3333
# Step 4: Click final 'Close Episode' button
3434
self.safe_accept_dialog(self.final_close_button)
35+
36+
self.page.wait_for_timeout(500) # Timeout to allow subject to be updated on DB.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from playwright.sync_api import Page
2+
from pages.base_page import BasePage
3+
4+
5+
class ConfirmTheManualSendingOfADisclaimerLetterPage(BasePage):
6+
"""Confirm The Manual Sending Of A Disclaimer Letter Page locators, and methods for interacting with the page."""
7+
8+
def __init__(self, page: Page):
9+
super().__init__(page)
10+
self.page = page
11+
12+
# Confirm The Manual Sending Of A Disclaimer Letter - page locators
13+
self.confirm_button = self.page.get_by_role("button", name="Confirm")
14+
15+
def click_confirm_button(self) -> None:
16+
"""Click the 'Confirm' button."""
17+
self.click(self.confirm_button)

pages/screening_subject_search/handover_into_symptomatic_care_page.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from playwright.sync_api import Page
22
from pages.base_page import BasePage
3+
from datetime import datetime
4+
from utils.calendar_picker import CalendarPicker
35

46

57
class HandoverIntoSymptomaticCarePage(BasePage):
@@ -18,6 +20,8 @@ def __init__(self, page: Page):
1820
self.cease_from_program_dropdown = self.page.locator(
1921
"#UI_CEASE_FROM_PROGRAM_ID"
2022
)
23+
self.mdt_date_field = self.page.locator("#UI_MDT_DATE")
24+
self.site_dropdown = self.page.locator("#UI_NS_SITE_SELECT_LINK")
2125

2226
def select_referral_dropdown_option(self, value: str) -> None:
2327
"""
@@ -75,3 +79,42 @@ def select_cease_from_program(self, cease: bool) -> None:
7579
"""
7680
value = "Yes" if cease else "No"
7781
self.cease_from_program_dropdown.select_option(value)
82+
83+
def enter_mdt_date(self, date: datetime) -> None:
84+
"""
85+
Enters a date into the 'MDT Date' field
86+
Args:
87+
date (datetime): The date to enter.
88+
"""
89+
CalendarPicker(self.page).calendar_picker_ddmmyyyy(date, self.mdt_date_field)
90+
91+
def select_site_dropdown_option_index(self, index: int) -> None:
92+
"""
93+
Select a given option from the site dropdown.
94+
95+
Args:
96+
value (str): The value of the option you want to select
97+
"""
98+
self.click(self.site_dropdown)
99+
select_locator = self.page.locator('select[id^="UI_RESULTS_"]:visible')
100+
select_locator.first.wait_for(state="visible")
101+
102+
# Find all option elements inside the select and click the one at the given index
103+
option_elements = select_locator.first.locator("option")
104+
option_elements.nth(index).wait_for(state="visible")
105+
self.click(option_elements.nth(index))
106+
107+
def fill_with_cancer_details(self) -> None:
108+
"""
109+
Complete the Handover into Symptomatic Care form with the cancer details scenario:
110+
MDT Date: Today
111+
Site: Last option
112+
Screening Practitioner: 1st option
113+
Note: Handover notes for Cancer scenario
114+
"""
115+
self.enter_mdt_date(datetime.today())
116+
self.select_site_dropdown_option_index(-1)
117+
self.select_practitioner_from_index(1)
118+
self.fill_notes("Handover notes for Cancer scenario")
119+
self.click_save_button()
120+
self.page.wait_for_timeout(500) # Timeout to allow subject to update in the DB.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from playwright.sync_api import Page
2+
from pages.base_page import BasePage
3+
from datetime import datetime
4+
from utils.calendar_picker import CalendarPicker
5+
6+
7+
class RecordInformedDissentPage(BasePage):
8+
"""Record Informed Dissent Page locators, and methods for interacting with the page."""
9+
10+
def __init__(self, page: Page):
11+
super().__init__(page)
12+
self.page = page
13+
14+
# Record informed Dissent - page locators
15+
self.notes_field = self.page.locator("#UI_NOTES_TEXT")
16+
self.date_confimed_input = self.page.locator("#UI_CEASING_CONFIRM_DATE")
17+
self.confirm_cease_button = self.page.get_by_role(
18+
"button", name="Confirm Cease"
19+
)
20+
21+
def fill_notes_field(self, note: str) -> None:
22+
"""
23+
Fill the notes field.
24+
Args:
25+
note (str): The note to add.
26+
"""
27+
self.notes_field.fill(note)
28+
29+
def enter_date_confirmed(self, date: datetime) -> None:
30+
"""
31+
Enter date confirmed
32+
Args:
33+
date (datetime): The date to enter
34+
"""
35+
CalendarPicker(self.page).calendar_picker_ddmmyyyy(
36+
date, self.date_confimed_input
37+
)
38+
39+
def click_confirm_cease_button(self) -> None:
40+
"""Click the 'Confirm Cease' button."""
41+
self.safe_accept_dialog(self.confirm_cease_button)
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
from playwright.sync_api import Page
2+
from pages.base_page import BasePage
3+
from enum import Enum
4+
from utils.calendar_picker import CalendarPicker
5+
from datetime import datetime
6+
from pages.screening_subject_search.subject_screening_summary_page import (
7+
SubjectScreeningSummaryPage,
8+
)
9+
from pages.screening_subject_search.confirm_the_manual_sending_of_a_disclaimer_letter_page import (
10+
ConfirmTheManualSendingOfADisclaimerLetterPage,
11+
)
12+
from pages.screening_subject_search.record_informed_dissent_page import (
13+
RecordInformedDissentPage,
14+
)
15+
from typing import Optional
16+
17+
18+
class RecordRequestToCeasePage(BasePage):
19+
"""Record Request To Cease Page locators, and methods for interacting with the page."""
20+
21+
def __init__(self, page: Page):
22+
super().__init__(page)
23+
self.page = page
24+
25+
# Record Request To Cease - page locators
26+
self.reason_dropdown = self.page.locator("#A_C_RequestCeaseReason")
27+
self.save_request_cease_button = self.page.get_by_role(
28+
"button", name="Save Request Cease"
29+
)
30+
self.confirm_cease_button = self.page.get_by_role(
31+
"button", name="Confirm Cease"
32+
)
33+
self.cease_notes = self.page.locator("#UI_NOTES_TEXT")
34+
self.cease_date_confirmed_textbox = self.page.locator(
35+
"#UI_CEASING_CONFIRM_DATE"
36+
)
37+
38+
def select_request_cease_reason(self, cease_reason: "ReasonForCeasing") -> None:
39+
"""
40+
Selects a request cease option.
41+
Args:
42+
cease_reason (ReasonForCeasing): The reason to request cease
43+
"""
44+
self.reason_dropdown.select_option(label=cease_reason.reason)
45+
46+
def click_save_request_cease_button(self) -> None:
47+
"""Click the 'Save Request Cease' button."""
48+
self.click(self.save_request_cease_button)
49+
self.page.wait_for_timeout(
50+
500
51+
) # Timeout to allow time for the subject to be updated on the DB.
52+
53+
def fill_cease_notes(self, note: str) -> None:
54+
"""
55+
Enter a Note in the cease notes
56+
Args:
57+
note (str): The note to add.
58+
"""
59+
self.cease_notes.fill(note)
60+
61+
def enter_cease_date_confirmed(self, date: datetime) -> None:
62+
"""
63+
Enter cease date confirmed
64+
Args:
65+
date (datetime): The date to enter
66+
"""
67+
CalendarPicker(self.page).calendar_picker_ddmmyyyy(
68+
date, self.cease_date_confirmed_textbox
69+
)
70+
71+
def click_confirm_cease_button(self) -> None:
72+
"""Click the 'Confirm Cease' button."""
73+
self.click(self.confirm_cease_button)
74+
75+
def cease_subject_with_reason(self, reason: str) -> None:
76+
"""
77+
Ceases a subject with a reason
78+
Args:
79+
reason (str): The reason to request cease:
80+
Can be any of the following:
81+
- Informed Dissent
82+
- Informed Dissent (verbal only)
83+
- No Colon (subject request)
84+
- No Colon (programme assessed)
85+
- Informal Death
86+
"""
87+
reason_for_ceasing = ReasonForCeasing.by_description_case_insensitive(reason)
88+
if reason_for_ceasing is None:
89+
raise ValueError("Incorrect reason for cease selected.")
90+
self.select_request_cease_reason(reason_for_ceasing)
91+
92+
if reason_for_ceasing.is_immediate_cease:
93+
self.fill_cease_notes("AUTO TESTING: confirm immediate manual cease")
94+
self.enter_cease_date_confirmed(datetime.now())
95+
self.click_confirm_cease_button()
96+
97+
else:
98+
self.click_save_request_cease_button()
99+
SubjectScreeningSummaryPage(self.page).click_record_disclaimer_letter_sent()
100+
ConfirmTheManualSendingOfADisclaimerLetterPage(
101+
self.page
102+
).click_confirm_button()
103+
SubjectScreeningSummaryPage(
104+
self.page
105+
).click_record_return_of_disclaimer_letter()
106+
RecordInformedDissentPage(self.page).fill_notes_field(
107+
"AUTO TESTING: confirm not-immediate manual cease"
108+
)
109+
RecordInformedDissentPage(self.page).enter_date_confirmed(datetime.now())
110+
RecordInformedDissentPage(self.page).click_confirm_cease_button()
111+
112+
113+
class ReasonForCeasing(Enum):
114+
INFORMED_DISSENT = ("Informed Dissent", 43, False)
115+
INFORMED_DISSENT_VERBAL_ONLY = ("Informed Dissent (verbal only)", 44, True)
116+
NO_COLON_SUBJECT_REQUEST = ("No Colon (subject request)", 45, False)
117+
NO_COLON_PROGRAMME_ASSESSED = ("No Colon (programme assessed)", 46, True)
118+
INFORMAL_DEATH = ("Informal Death", 47, True)
119+
120+
def __init__(self, reason: str, value: int, is_immediate_cease_reason: bool):
121+
self._reason = reason
122+
self._value = value
123+
self._is_immediate_cease_reason = is_immediate_cease_reason
124+
125+
@property
126+
def reason(self) -> str:
127+
return self._reason
128+
129+
@property
130+
def value(self) -> int:
131+
return self._value
132+
133+
@property
134+
def is_immediate_cease(self) -> bool:
135+
return self._is_immediate_cease_reason
136+
137+
@classmethod
138+
def by_description_case_insensitive(
139+
cls, reason: str
140+
) -> Optional["ReasonForCeasing"]:
141+
"""
142+
Get ReasonForCeasing member by description, case insensitive.
143+
Args:
144+
reason (str): The reason description to look for.
145+
Returns:
146+
Optional[ReasonForCeasing]: The matching ReasonForCeasing member, or None if not found.
147+
"""
148+
reason_lower = reason.lower()
149+
for member in cls:
150+
if member.reason.lower() == reason_lower:
151+
return member
152+
return None

pages/screening_subject_search/refer_to_mdt_page.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ def __init__(self, page: Page):
1515
self.mdt_discussion_date_field = self.page.locator("#UI_MDT_DATE_LinkOrButton")
1616
self.mdt_location = self.page.locator("#UI_NS_SITE_SELECT_LINK")
1717
self.visible_ui_results_string = 'select[id^="UI_RESULTS_"]:visible'
18-
self.record_MDT_appointment_button = self.page.locator(
18+
self.record_mdt_appointment_button = self.page.locator(
1919
'[name="UI_BUTTON_SAVE"]'
2020
)
2121

22-
def enter_date_in_Mdt_discussion_date_field(self, date: datetime) -> None:
22+
def enter_date_in_mdt_discussion_date_field(self, date: datetime) -> None:
2323
"""
2424
Enters a date in the MDT discussion date field.
2525
Args:
@@ -43,6 +43,6 @@ def select_mdt_location_lookup(self, option: int) -> None:
4343
option_elements.nth(option).wait_for(state="visible")
4444
self.click(option_elements.nth(option))
4545

46-
def click_record_MDT_appointment_button(self) -> None:
46+
def click_record_mdt_appointment_button(self) -> None:
4747
"""Clicks the record MDT appointment button."""
48-
self.click(self.record_MDT_appointment_button)
48+
self.click(self.record_mdt_appointment_button)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from playwright.sync_api import Page
2+
from pages.base_page import BasePage
3+
4+
5+
class ReopenScreeningEpisodeAfterManualCeasePage(BasePage):
6+
"""Reopen Screening Episode After Manual Cease Page locators, and methods for interacting with the page."""
7+
8+
def __init__(self, page: Page):
9+
super().__init__(page)
10+
self.page = page
11+
12+
# Reopen Screening Episode After Manual Cease - page locators
13+
self.notes_field = self.page.locator("#UI_NOTES_TEXT")
14+
self.uncease_and_reopen_episode_button = self.page.get_by_role(
15+
"button", name="uncease and reopen Episode"
16+
)
17+
18+
def fill_notes_field(self, note: str) -> None:
19+
"""
20+
Enter a note into the notes field.
21+
Args:
22+
note (str): The note to enter.
23+
"""
24+
self.notes_field.fill(note)
25+
26+
def click_uncease_and_reopen_episode_button(self) -> None:
27+
"""Click the 'uncease and reopen Episode' button."""
28+
self.click(self.uncease_and_reopen_episode_button)

0 commit comments

Comments
 (0)