Skip to content

Commit 38c1de6

Browse files
authored
Feature/bcss 21307 fobt regression tests scenario 4 (#130)
<!-- markdownlint-disable-next-line first-line-heading --> ## Description <!-- Describe your changes in detail. --> This is scenario 4 (Screening Centre discharges patient prior to colonoscopy assessment) of the fobt regression tests, migrated from selenium to playwright. ## Context <!-- Why is this change required? What problem does it solve? --> This is part of the selenium to playwright migration work. ## 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 2884cfd commit 38c1de6

File tree

8 files changed

+586
-12
lines changed

8 files changed

+586
-12
lines changed

.gitleaksignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,6 @@ cd9c0efec38c5d63053dd865e5d4e207c0760d91:docs/guides/Perform_static_analysis.md:
221221
751ddbd178e91293c20282df62e8d3fe1086310a:utils/resources/axe.js:ipv4:32080
222222
751ddbd178e91293c20282df62e8d3fe1086310a:utils/resources/axe.js:ipv4:32089
223223
751ddbd178e91293c20282df62e8d3fe1086310a:utils/resources/axe.js:ipv4:32103
224+
cae84544e2852202f5d0abb9ad18f9d83a0c6d80:subject_criteria_builder/criteria.json:generic-api-key:4210
225+
203cd8811be75d33859eb7c7cc8a48beb5a2b8b2:subject_criteria_builder/criteria.json:generic-api-key:4210
226+
60468a7d6f3ff3259616757be85d6f07e62d00b6:subject_criteria_builder/criteria.json:generic-api-key:4210

pages/base_page.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ def assert_dialog_text(self, expected_text: str, accept: bool = False) -> None:
256256
If no dialog appears, logs an error.
257257
Args:
258258
expected_text (str): The text that should be present in the dialog.
259-
accept (bool): Set to True if you want to accept the dialog, by deafult is is set to False.
259+
accept (bool): Set to True if you want to accept the dialog, by default is is set to False.
260260
"""
261261
self._dialog_assertion_error = None
262262

@@ -265,7 +265,7 @@ def handle_dialog(dialog: Dialog, accept: bool = False):
265265
Handles the dialog and asserts that the dialog contains the expected text.
266266
Args:
267267
dialog (Dialog): the playwright dialog object
268-
accept (bool): Set to True if you want to accept the dialog, by deafult is is set to False.
268+
accept (bool): Set to True if you want to accept the dialog, by default is is set to False.
269269
"""
270270
logging.info(f"Dialog appeared with message: {dialog.message}")
271271
actual_text = dialog.message

pages/screening_practitioner_appointments/appointment_detail_page.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def __init__(self, page: Page):
1515
self.calendar_button = self.page.get_by_role("button", name="Calendar")
1616
self.save_button = self.page.get_by_role("button", name="Save")
1717
self.cancel_radio = self.page.get_by_role("radio", name="Cancel")
18-
self.reason_for_cancellation_dropwdown = self.page.get_by_label(
18+
self.reason_for_cancellation_dropdown = self.page.get_by_label(
1919
"Reason for Cancellation"
2020
)
2121

@@ -84,7 +84,7 @@ def select_reason_for_cancellation_option(self, option: str) -> None:
8484
option: The reason for cancellation to select.
8585
The options are in the ReasonForCancellationOptions class
8686
"""
87-
self.reason_for_cancellation_dropwdown.select_option(value=option)
87+
self.reason_for_cancellation_dropdown.select_option(value=option)
8888

8989

9090
class ReasonForCancellationOptions(StrEnum):
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from pages.base_page import BasePage
2+
from playwright.sync_api import Page
3+
4+
5+
class CloseFobtScreeningEpisodePage(BasePage):
6+
"""Close FOBT Screening Episode 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+
self.close_fobt_screening_episode_button = self.page.get_by_role(
12+
"button", name="Close FOBT Screening Episode"
13+
)
14+
self.reason_dropdown = self.page.locator("#CLOSE_REASON")
15+
self.notes_textarea = self.page.locator("#UI_NOTES_TEXT")
16+
self.final_close_button = self.page.locator("#UI_BUTTON_CLOSE")
17+
18+
def close_fobt_screening_episode(self, reason_text: str) -> None:
19+
"""
20+
Complete the process of closing a FOBT screening episode.
21+
Args:
22+
reason_text (str): The visible text of the reason to select from the dropdown.
23+
"""
24+
# Step 1: Trigger the dialog and accept it
25+
self.safe_accept_dialog(self.close_fobt_screening_episode_button)
26+
27+
# Step 2: Select reason from dropdown
28+
self.reason_dropdown.select_option(label=reason_text)
29+
30+
# Step 3: Enter note
31+
self.notes_textarea.fill("automation test note")
32+
33+
# Step 4: Click final 'Close Episode' button
34+
self.safe_accept_dialog(self.final_close_button)

pages/screening_subject_search/record_diagnosis_date_page.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ class RecordDiagnosisDatePage(BasePage):
1010
def __init__(self, page: Page):
1111
super().__init__(page)
1212
self.page = page
13+
1314
# Record Diagnosis Date - page locators
1415
self.diagnosis_date_field = self.page.locator("#diagnosisDate")
16+
self.reason_dropdown = self.page.locator("#reason")
1517
self.save_button = self.page.get_by_role("button", name="Save")
1618

1719
def enter_date_in_diagnosis_date_field(self, date: datetime) -> None:
@@ -41,3 +43,22 @@ def get_alert_message(self) -> str:
4143
return self.alert_message.inner_text()
4244
else:
4345
return ""
46+
47+
def record_diagnosis_reason(self, reason_text: str) -> None:
48+
"""
49+
Selects a diagnosis reason from the dropdown and saves the form.
50+
51+
Args:
52+
reason_text (str): The visible text of the reason to select.
53+
Valid options include:
54+
- "Patient declined all appointments"
55+
- "2 DNAs of colonoscopy assessment appt"
56+
- "Patient emigrated"
57+
- "Patient deceased"
58+
- "Patient choice"
59+
- "Patient could not be contacted"
60+
- "Reopened old episode, date unknown"
61+
- "Other"
62+
"""
63+
self.reason_dropdown.select_option(label=reason_text)
64+
self.safe_accept_dialog(self.save_button)

pages/screening_subject_search/subject_screening_summary_page.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def __init__(self, page: Page):
4444
self.update_subject_data = self.page.get_by_role(
4545
"button", name="Update Subject Data"
4646
)
47-
self.close_fobt_screening_episode = self.page.get_by_role(
47+
self.close_fobt_screening_episode_button = self.page.get_by_role(
4848
"button", name="Close FOBT Screening Episode"
4949
)
5050
self.a_page_to_advance_the_episode = self.page.get_by_text(
@@ -86,6 +86,9 @@ def __init__(self, page: Page):
8686
self.reopen_fobt_screening_episode_button = self.page.get_by_role(
8787
"button", name="Reopen FOBT Screening Episode"
8888
)
89+
self.reopen_for_correction_button = self.page.get_by_role(
90+
"button", name="Reopen episode for correction"
91+
)
8992

9093
def wait_for_page_title(self) -> None:
9194
"""Waits for the page to be the Subject Screening Summary"""
@@ -189,7 +192,7 @@ def click_update_subject_data(self) -> None:
189192

190193
def click_close_fobt_screening_episode(self) -> None:
191194
"""Click on the 'Close FOBT Screening Episode' button."""
192-
self.click(self.close_fobt_screening_episode)
195+
self.click(self.close_fobt_screening_episode_button)
193196

194197
def go_to_a_page_to_advance_the_episode(self) -> None:
195198
"""Click on the link to go to a page to advance the episode."""
@@ -365,6 +368,7 @@ def assert_subject_age(self, expected_age: int) -> None:
365368
assert (
366369
actual_age == expected_age
367370
), f"[AGE MISMATCH] Expected age {expected_age}, but found {actual_age} in UI."
371+
logging.info("[UI ASSERTIONS COMPLETE] Subject age checked in the UI")
368372

369373
def assert_screening_status(self, expected_status: str) -> None:
370374
"""
@@ -380,6 +384,9 @@ def assert_screening_status(self, expected_status: str) -> None:
380384
assert (
381385
actual_status.lower() == expected_status.lower()
382386
), f"[SCREENING STATUS MISMATCH] Expected '{expected_status}', but found '{actual_status}' in UI."
387+
logging.info(
388+
"[UI ASSERTIONS COMPLETE] Subject screening status checked in the UI"
389+
)
383390

384391
def assert_latest_event_status(self, expected_status: str) -> None:
385392
"""
@@ -395,11 +402,26 @@ def assert_latest_event_status(self, expected_status: str) -> None:
395402
assert (
396403
actual_status == expected_status
397404
), f"[LATEST EVENT STATUS MISMATCH] Expected '{expected_status}', but found '{actual_status}' in UI."
405+
logging.info(
406+
"[UI ASSERTIONS COMPLETE] Subject latest event status checked in the UI"
407+
)
398408

399409
def click_reopen_fobt_screening_episode_button(self) -> None:
400410
"""Click on the 'Reopen FOBT Screening Episode' button"""
401411
self.click(self.reopen_fobt_screening_episode_button)
402412

413+
def reopen_fobt_screening_episode(self) -> None:
414+
"""
415+
Reopen a previously closed FOBT screening episode, including confirmation modal.
416+
- Clicks the 'Reopen FOBT Screening Episode' button
417+
- Then safely accepts the dialog triggered by 'Reopen episode for correction'
418+
"""
419+
# Step 1: Click the initial 'Reopen FOBT Screening Episode' button
420+
self.click_reopen_fobt_screening_episode_button()
421+
422+
# Step 2: Safely accept the confirmation dialog triggered by the correction button
423+
self.safe_accept_dialog(self.reopen_for_correction_button)
424+
403425

404426
class ChangeScreeningStatusOptions(Enum):
405427
"""Enum for Change Screening Status options."""

tests/regression/regression_tests/fobt_regression_tests/test_scenario_2.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import logging
33
from datetime import datetime
44
from playwright.sync_api import Page
5-
from pages.communication_production.batch_list_page import ActiveBatchListPage
65
from utils.oracle.subject_creation_util import CreateSubjectSteps
76
from utils.user_tools import UserTools
87
from utils.subject_assertion import subject_assertion
@@ -13,7 +12,6 @@
1312
from pages.screening_subject_search.subject_screening_summary_page import (
1413
SubjectScreeningSummaryPage,
1514
)
16-
from pages.communication_production.batch_list_page import BatchListPage
1715
from pages.logout.log_out_page import LogoutPage
1816

1917

@@ -79,7 +77,6 @@ def test_scenario_2(page: Page) -> None:
7977
# Assert subject details in the UI
8078
summary_page.assert_subject_age(66)
8179
summary_page.assert_screening_status("Inactive")
82-
logging.info("[UI ASSERTIONS COMPLETE] Updated subject details checked in the UI")
8380

8481
# When I run the FOBT failsafe trawl for my subject
8582
CallAndRecallUtils().run_failsafe(nhs_no)
@@ -103,7 +100,6 @@ def test_scenario_2(page: Page) -> None:
103100

104101
# Assert subject details in the UI
105102
summary_page.assert_screening_status("Call")
106-
logging.info("[UI ASSERTIONS COMPLETE] Updated subject details checked in the UI")
107103

108104
# When I invite my subject for FOBT screening
109105
CallAndRecallUtils().invite_subject_for_fobt_screening(nhs_no, user_role)
@@ -170,7 +166,6 @@ def test_scenario_2(page: Page) -> None:
170166
summary_page.assert_latest_event_status(
171167
"S43 - Kit Returned and Logged (Initial Test)"
172168
)
173-
logging.info("[UI ASSERTIONS COMPLETE] Updated subject details checked in the UI")
174169

175170
# When I read my subject's latest logged FIT kit as "NORMAL"
176171
FitKitLogged().read_latest_logged_kit(user_role, 2, fit_kit, "NORMAL")
@@ -188,7 +183,6 @@ def test_scenario_2(page: Page) -> None:
188183

189184
# Assert subject details in the UI
190185
summary_page.assert_latest_event_status("S2 - Normal")
191-
logging.info("[UI ASSERTIONS COMPLETE] Updated subject details checked in the UI")
192186

193187
# And there is a "S2" letter batch for my subject with the exact title "Subject Result (Normal)"
194188
# When I process the open "S2" letter batch for my subject

0 commit comments

Comments
 (0)