Skip to content

Commit cea68c5

Browse files
Feature/bcss 21316 fobtregressiontests scenario 14 (#148)
<!-- markdownlint-disable-next-line first-line-heading --> ## Description Adding scenario 14 from FOBTRegressionTests. <!-- Describe your changes in detail. --> ## Context Adds a new scenario andPOM s for new pages <!-- Why is this change required? What problem does it solve? --> ## 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. --------- Co-authored-by: Adriano Aru <[email protected]>
1 parent 1194b33 commit cea68c5

13 files changed

+1415
-55
lines changed

classes/repositories/subject_repository.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,58 @@ def get_matching_subject(
225225
except Exception:
226226
raise ValueError("No subject found matching the given criteria")
227227
return subject
228+
229+
def there_is_letter_batch_for_subject(
230+
self,
231+
nhs_no: str,
232+
letter_batch_code: str,
233+
letter_batch_title: str,
234+
assertion: bool = True,
235+
) -> None:
236+
"""
237+
Checks if the subject under test has a specific letter batch assosiated with them.
238+
Args:
239+
nhs_no (str): The subject's NHS number.
240+
letter_batch_code (str): The letter batch code.
241+
letter_batch_title (str): The letter batch title.
242+
assertion (bool): If the subject should have this batch (True), or should not have this batch (False).
243+
"""
244+
sql_query = """ SELECT lb.batch_id
245+
FROM lett_batch_records lbr
246+
INNER JOIN lett_batch lb
247+
ON lb.batch_id = lbr.batch_id
248+
INNER JOIN valid_values ld
249+
ON ld.valid_value_id = lb.description_id
250+
INNER JOIN valid_values lbs
251+
ON lbs.valid_value_id = lb.status_id
252+
WHERE lb.batch_state_id = 12018
253+
AND lbr.screening_subject_id = :subject_id
254+
AND lbs.allowed_value = :batch_code
255+
AND LOWER(ld.description) = LOWER(:batch_title)
256+
AND lbr.non_inclusion_id IS NULL
257+
AND lbr.key_id != 11539
258+
"""
259+
260+
subject_id = self.oracle_db.get_subject_id_from_nhs_number(nhs_no)
261+
262+
params = {
263+
"subject_id": subject_id,
264+
"batch_code": letter_batch_code,
265+
"batch_title": letter_batch_title,
266+
}
267+
268+
batch_df = self.oracle_db.execute_query(sql_query, params)
269+
if assertion:
270+
assert (
271+
not batch_df.empty
272+
), f"[DB ASSERTION FAILED] Subject {nhs_no} does not have a {letter_batch_code} - {letter_batch_title} batch when they are expected to"
273+
logging.info(
274+
f"[DB ASSERTION Passed] Subject {nhs_no} has a {letter_batch_code} - {letter_batch_title} batch"
275+
)
276+
else:
277+
assert (
278+
batch_df.empty
279+
), f"[DB ASSERTION FAILED] Subject {nhs_no} has a {letter_batch_code} - {letter_batch_title} batch when they are expected not to"
280+
logging.info(
281+
f"[DB ASSERTION Passed] Subject {nhs_no} does not have a {letter_batch_code} - {letter_batch_title} batch"
282+
)

pages/screening_subject_search/advance_fobt_screening_episode_page.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,17 @@ def __init__(self, page: Page):
114114
"button",
115115
name="Refer Another Diagnostic Test after return from Symptomatic Referral",
116116
)
117+
self.refer_to_survelliance_after_symptomatic_referral_button = (
118+
self.page.get_by_role(
119+
"button", name="Refer to Surveillance after Symptomatic Referral"
120+
)
121+
)
122+
self.redirect_to_rerecord_the_outcome_of_symptomatic_referral_button = (
123+
self.page.get_by_role(
124+
"button",
125+
name="Redirect to Re-record the Outcome of Symptomatic Referral",
126+
)
127+
)
117128
# Contact recording locators
118129
self.contact_direction_dropdown = self.page.get_by_label("Contact Direction")
119130
self.contact_made_between_dropdown = self.page.get_by_label(
@@ -135,6 +146,9 @@ def __init__(self, page: Page):
135146
self.ct_colonography_test_type_dropdown = self.page.locator(
136147
"#UI_EXT_TEST_TYPE_38"
137148
)
149+
self.lnpcp_result_from_symptomatic_procedure_button = self.page.get_by_role(
150+
"button", name="LNPCP Result from Symptomatic Procedure"
151+
)
138152

139153
def click_suitable_for_endoscopic_test_button(self) -> None:
140154
"""Click the 'Suitable for Endoscopic Test' button."""
@@ -437,3 +451,19 @@ def click_refer_another_diagnostic_test_after_return_from_symptomatic_referral_b
437451
self.click(
438452
self.refer_another_diagnostic_test_after_return_from_symptomatic_referral_button
439453
)
454+
455+
def click_lnpcp_result_from_symptomatic_procedure_button(self) -> None:
456+
"""Click the 'LNPCP Result from Symptomatic Procedure' button."""
457+
self.safe_accept_dialog(self.lnpcp_result_from_symptomatic_procedure_button)
458+
459+
def click_refer_to_survelliance_after_symptomatic_referral_button(
460+
self,
461+
) -> None:
462+
"""Click the 'Refer to Surveillance after Symptomatic Referral' button."""
463+
self.safe_accept_dialog(
464+
self.refer_to_survelliance_after_symptomatic_referral_button
465+
)
466+
467+
def click_redirect_to_rerecord_the_outcome_of_symptomatic_referral_button(self) -> None:
468+
"""Click the 'Redirect to Re-record the Outcome of Symptomatic Referral' button."""
469+
self.safe_accept_dialog(self.redirect_to_rerecord_the_outcome_of_symptomatic_referral_button)

pages/screening_subject_search/diagnostic_test_outcome_page.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ def __init__(self, page: Page):
2929
self.test_outcome_dropdown = self.page.get_by_label(
3030
"Outcome of Diagnostic Test"
3131
)
32+
self.reason_for_sympptomatic_referral_dropdown = self.page.get_by_label(
33+
"Reason for Symptomatic Referral"
34+
)
3235
self.save_button = self.page.get_by_role("button", name="Save")
3336
self.referral_procedure_dropdown = self.page.locator(
3437
"#UI_REFERRAL_PROCEDURE_TYPE"
@@ -56,6 +59,25 @@ def select_test_outcome_option(self, option: str) -> None:
5659
"""
5760
self.test_outcome_dropdown.select_option(option)
5861

62+
def verify_reason_for_symptomatic_referral(self, symptomatic_reason: str) -> None:
63+
"""
64+
Verify reason for symptomatic referral is visible.
65+
66+
Args:
67+
symptomatic_reason(str): The accessible name or visible text of the symptomatic reason cell to verify.
68+
"""
69+
expect(
70+
self.page.get_by_role("cell", name=symptomatic_reason).nth(1)
71+
).to_be_visible()
72+
73+
def select_reason_for_symptomatic_referral_option(self, option: str) -> None:
74+
"""Select an option from the reason for symptomatic referral dropdown.
75+
76+
Args:
77+
option (str): option (str): The option to select from the Reason For Symptomatic Referral options.
78+
"""
79+
self.reason_for_sympptomatic_referral_dropdown.select_option(option)
80+
5981
def click_save_button(self) -> None:
6082
"""Click the 'Save' button."""
6183
self.click(self.save_button)
@@ -89,3 +111,11 @@ class OutcomeOfDiagnosticTest(StrEnum):
89111
REFER_SURVEILLANCE = "20365"
90112
INVESTIGATION_COMPLETE = "20360"
91113
REFER_ANOTHER_DIAGNOSTIC_TEST = "20364"
114+
115+
116+
class ReasonForSymptomaticReferral(StrEnum):
117+
"""Enum for Symptomatic Referral reason options."""
118+
119+
POLYP_EXCISION = "203011"
120+
CORRECTIVE_SURGERY = "203012"
121+
SUSPECTED_CANCER_SURGERY = "203013"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from playwright.sync_api import Page
2+
from pages.screening_subject_search.result_from_symptomatic_procedure_page import (
3+
ResultFromSymptomaticProcedurePage,
4+
)
5+
6+
7+
class LnpcpResultFromSymptomaticProcedure(ResultFromSymptomaticProcedurePage):
8+
"""LNPCP specific implementation of Result from Symptomatic Procedure Page."""
9+
10+
def __init__(self, page: Page):
11+
super().__init__(page)
12+
self.page = page
Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,14 @@
1-
from datetime import datetime
21
from playwright.sync_api import Page
3-
from pages.base_page import BasePage
4-
from utils.calendar_picker import CalendarPicker
2+
from pages.screening_subject_search.result_from_symptomatic_procedure_page import (
3+
ResultFromSymptomaticProcedurePage,
4+
)
55

66

7-
class NonNeoplasticResultFromSymptomaticProcedurePage(BasePage):
8-
"""Non Neoplastic Result From Symptomatic Procedure Page locators, and methods for interacting with the page."""
7+
class NonNeoplasticResultFromSymptomaticProcedurePage(
8+
ResultFromSymptomaticProcedurePage
9+
):
10+
"""Non-Neoplastic specific implementation of Result from Symptomatic Procedure Page."""
911

1012
def __init__(self, page: Page):
1113
super().__init__(page)
12-
# Non Neoplastic Result From Symptomatic Procedure - page locators
13-
self.date_of_symptomatic_procedure_calendar_button = self.page.locator(
14-
"#UI_SURGERY_DATE__LinkOrButton"
15-
)
16-
self.alert_textbox = self.page.locator("#UI_SPAN_RECALL_TEXT")
17-
self.all_tests = self.page.locator("#UI_ID_RECALL_ANCHOR_DATE_EXT_TEST_ID")
18-
self.save_button = self.page.get_by_role("button", name="Save")
19-
20-
def click_date_of_symptomatic_procedure_calendar_button(self) -> None:
21-
"""Click the date of symptomatic procedure calendar button."""
22-
self.click(self.date_of_symptomatic_procedure_calendar_button)
23-
24-
def enter_date_of_symptomatic_procedure(self, date: datetime) -> None:
25-
"""
26-
Enter the date of the symptomatic procedure.
27-
Args:
28-
date (datetime): The date to be entered in the date of symptomatic procedure field. Example: datetime(2023, 10, 25)
29-
"""
30-
self.click_date_of_symptomatic_procedure_calendar_button()
31-
CalendarPicker(self.page).v1_calender_picker(date)
32-
33-
def assert_text_in_alert_textbox(self, expected_text: str) -> None:
34-
"""
35-
Assert that the expected text is present in the alert textbox.
36-
Args:
37-
expected_text (str): The text expected to be found in the alert textbox. Example: "This is a test alert"
38-
"""
39-
actual_text = self.alert_textbox.inner_text()
40-
assert (
41-
expected_text in actual_text
42-
), f"Expected text '{expected_text}' not found in alert textbox. Actual text: '{actual_text}'"
43-
44-
def select_test_number(self, test_number: int) -> None:
45-
"""
46-
Select a test from the all tests dropdown by its index.
47-
Args:
48-
test_number (int): The index of the test to select (1-based index). Example: if you want to select the 1st test pass in 1
49-
"""
50-
self.click(self.all_tests.nth(test_number - 1))
51-
52-
def click_save_button(self) -> None:
53-
"""Click the 'Save' button."""
54-
self.click(self.save_button)
14+
self.page = page
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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 ReferToMDTPage(BasePage):
8+
"""Refer to MDT 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+
# Refer MDT page - page locators
15+
self.mdt_discussion_date_field = self.page.locator("#UI_MDT_DATE_LinkOrButton")
16+
self.mdt_location = self.page.locator("#UI_NS_SITE_SELECT_LINK")
17+
self.visible_ui_results_string = 'select[id^="UI_RESULTS_"]:visible'
18+
self.record_MDT_appointment_button = self.page.locator(
19+
'[name="UI_BUTTON_SAVE"]'
20+
)
21+
22+
def enter_date_in_Mdt_discussion_date_field(self, date: datetime) -> None:
23+
"""
24+
Enters a date in the MDT discussion date field.
25+
Args:
26+
date (datetime): The date to enter in the field.
27+
"""
28+
self.click(self.mdt_discussion_date_field)
29+
CalendarPicker(self.page).v2_calendar_picker(date)
30+
self.mdt_discussion_date_field.press("Tab")
31+
32+
def select_mdt_location_lookup(self, option: int) -> None:
33+
"""
34+
This method is designed to select an option from the MDT location lookup dropdown.
35+
Args:
36+
option (int): The index of the option to select (0-based).
37+
"""
38+
self.click(self.mdt_location)
39+
select_locator = self.page.locator(self.visible_ui_results_string)
40+
select_locator.first.wait_for(state="visible")
41+
# Find all option elements inside the select and click the one at the given index
42+
option_elements = select_locator.first.locator("option")
43+
option_elements.nth(option).wait_for(state="visible")
44+
self.click(option_elements.nth(option))
45+
46+
def click_record_MDT_appointment_button(self) -> None:
47+
"""Clicks the record MDT appointment button."""
48+
self.click(self.record_MDT_appointment_button)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from datetime import datetime
2+
from playwright.sync_api import Page
3+
from pages.base_page import BasePage
4+
from utils.calendar_picker import CalendarPicker
5+
6+
7+
class ResultFromSymptomaticProcedurePage(BasePage):
8+
"""Base class for Result from Symptomatic Procedure pages with common locators and methods."""
9+
10+
def __init__(self, page: Page):
11+
super().__init__(page)
12+
self.page = page
13+
# Result from Symptomatic Procedure Page - page locators
14+
self.date_of_symptomatic_procedure_calendar_button = self.page.locator(
15+
"#UI_SURGERY_DATE__LinkOrButton"
16+
)
17+
self.alert_textbox = self.page.locator("#UI_SPAN_RECALL_TEXT")
18+
self.all_tests = self.page.locator(
19+
"[id^='UI_ID_RECALL_ANCHOR_DATE_EXT_TEST_ID']"
20+
)
21+
self.save_button = self.page.get_by_role("button", name="Save")
22+
23+
def click_date_of_symptomatic_procedure_calendar_button(self) -> None:
24+
"""Click the date of symptomatic procedure calendar button."""
25+
self.click(self.date_of_symptomatic_procedure_calendar_button)
26+
27+
def enter_date_of_symptomatic_procedure(self, date: datetime) -> None:
28+
"""
29+
Enter the date of the symptomatic procedure.
30+
Args:
31+
date (datetime): The date to be entered in the date of symptomatic procedure field. Example: datetime(2023, 10, 25)
32+
"""
33+
self.click_date_of_symptomatic_procedure_calendar_button()
34+
CalendarPicker(self.page).v1_calender_picker(date)
35+
36+
def assert_text_in_alert_textbox(self, expected_text: str) -> None:
37+
"""
38+
Assert that the expected text is present in the alert textbox.
39+
Args:
40+
expected_text (str): The text expected to be found in the alert textbox. Example: "This is a test alert"
41+
"""
42+
actual_text = self.alert_textbox.inner_text()
43+
assert (
44+
expected_text in actual_text
45+
), f"Expected text '{expected_text}' not found in alert textbox. Actual text: '{actual_text}'"
46+
47+
def select_test_number(self, test_number: int) -> None:
48+
"""
49+
Select a test from the all tests dropdown by its index.
50+
Args:
51+
test_number (int): The index of the test to select (1-based index). Example: if you want to select the 1st test pass in 1
52+
"""
53+
self.click(self.all_tests.nth(test_number - 1))
54+
55+
def click_save_button(self) -> None:
56+
"""Click the 'Save' button."""
57+
self.click(self.save_button)

pages/screening_subject_search/subject_screening_summary_page.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,10 @@ def change_screening_status(self, status_option: str, reason_option: str) -> Non
446446
self.click_update_subject_data()
447447
self.page.wait_for_timeout(1000)
448448

449+
def assert_reopen_episode_button_not_visible(self) -> None:
450+
"""Assert that the 'Reopen FOBT Screening Episode' button is not visible"""
451+
expect(self.reopen_fobt_screening_episode_button).not_to_be_visible()
452+
449453

450454
class ChangeScreeningStatusOptions(StrEnum):
451455
"""Enum for Change Screening Status options."""

tests/regression/regression_tests/fobt_regression_tests/test_scenario_12.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,7 @@
5757
YesNoOptions,
5858
EndoscopyLocationOptions,
5959
)
60-
from pages.screening_subject_search.non_neoplastic_result_from_symptomatic_procedure_page import (
61-
NonNeoplasticResultFromSymptomaticProcedurePage,
62-
)
60+
from pages.screening_subject_search.non_neoplastic_result_from_symptomatic_procedure_page import (NonNeoplasticResultFromSymptomaticProcedurePage)
6361
from utils.subject_demographics import SubjectDemographicUtil
6462
from pages.screening_subject_search.reopen_fobt_screening_episode_page import (
6563
ReopenFOBTScreeningEpisodePage,

0 commit comments

Comments
 (0)