Skip to content

Commit 93e5f43

Browse files
adrianoaru-nhsAndyg79
authored andcommitted
Adding scenario 11 from feature file (#115)
<!-- markdownlint-disable-next-line first-line-heading --> ## Description <!-- Describe your changes in detail. --> Migrating scenario 11 from EndoscopyInvestigationDatasetScenarios.feature into playwright ## Context <!-- Why is this change required? What problem does it solve? --> Migrating scenario 11 from EndoscopyInvestigationDatasetScenarios.feature into playwright ## Type of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply. --> - [ ] 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 63b1466 commit 93e5f43

File tree

5 files changed

+617
-103
lines changed

5 files changed

+617
-103
lines changed

pages/base_page.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,15 @@ def handle_dialog(dialog: Dialog, accept: bool = False):
276276
except AssertionError as e:
277277
self._dialog_assertion_error = e
278278
if accept:
279-
dialog.accept()
279+
try:
280+
dialog.accept()
281+
except Exception:
282+
logging.warning("Dialog already accepted or handled")
280283
else:
281-
dialog.dismiss() # Dismiss dialog
284+
try:
285+
dialog.dismiss() # Dismiss dialog
286+
except Exception:
287+
logging.warning("Dialog already dismissed or handled")
282288

283289
self.page.once("dialog", lambda dialog: handle_dialog(dialog, accept))
284290

pages/datasets/investigation_dataset_page.py

Lines changed: 153 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import re
22
from playwright.sync_api import Page, expect, Locator
33
from pages.base_page import BasePage
4-
from enum import StrEnum
4+
from enum import Enum, StrEnum
55
from utils.oracle.oracle_specific_functions import (
66
get_investigation_dataset_polyp_category,
77
get_investigation_dataset_polyp_algorithm_size,
88
)
9-
from typing import Optional
9+
from typing import Optional, Any, Union, List
1010
import logging
11+
import sys
1112

1213

1314
class InvestigationDatasetsPage(BasePage):
@@ -80,6 +81,11 @@ def __init__(self, page: Page):
8081
self.visible_ui_results_string = 'select[id^="UI_RESULTS_"]:visible'
8182
self.sections = self.page.locator(".DatasetSection")
8283

84+
# Repeat strings:
85+
self.bowel_preparation_administered_string = "Bowel Preparation Administered"
86+
self.antibiotics_administered_string = "Antibiotics Administered"
87+
self.other_drugs_administered_string = "Other Drugs Administered"
88+
8389
def select_site_lookup_option(self, option: str) -> None:
8490
"""
8591
This method is designed to select a site from the site lookup options.
@@ -750,11 +756,13 @@ def assert_drug_type_text(
750756
locator = self.get_drug_type_locator(drug_type, drug_number)
751757
actual_text = locator.input_value().strip()
752758
logging.info(
753-
f"Drug type text for drug {drug_number}: '{actual_text}' (expected: '{expected_text}')"
759+
f"Drug type text for drug {drug_number}: "
760+
f"'{to_enum_name_or_value(actual_text)}' "
761+
f"(expected: '{to_enum_name_or_value(expected_text)}')"
754762
)
755763
assert (
756764
actual_text == expected_text
757-
), f"Expected drug type text '{expected_text}' but found '{actual_text}'"
765+
), f"Expected drug type text '{to_enum_name_or_value(expected_text)}' but found '{to_enum_name_or_value(actual_text)}'"
758766

759767
def assert_drug_dose_text(
760768
self, drug_type: str, drug_number: int, expected_text: str
@@ -784,11 +792,11 @@ def get_drug_type_locator(self, drug_type: str, drug_number: int) -> Locator:
784792
drug_type (str): The drug type to check
785793
drug_number (int): The number of the drug to check
786794
"""
787-
if drug_type == "Bowel Preparation Administered":
795+
if drug_type == self.bowel_preparation_administered_string:
788796
locator_prefix = "#UI_BOWEL_PREP_DRUG"
789-
elif drug_type == "Antibiotics Administered":
797+
elif drug_type == self.antibiotics_administered_string:
790798
locator_prefix = "#UI_ANTIBIOTIC"
791-
elif drug_type == "Other Drugs Administered":
799+
elif drug_type == self.other_drugs_administered_string:
792800
locator_prefix = "#UI_DRUG"
793801
return self.page.locator(f"{locator_prefix}{drug_number}")
794802

@@ -799,11 +807,11 @@ def get_drug_dose_locator(self, drug_type: str, drug_number: int) -> Locator:
799807
drug_type (str): The drug type to check
800808
drug_number (int): The number of the drug to check
801809
"""
802-
if drug_type.lower() == "bowel preparation administered":
810+
if drug_type == self.bowel_preparation_administered_string:
803811
locator_prefix = "#UI_BOWEL_PREP_DRUG_DOSE"
804-
elif drug_type.lower() == "antibiotics administered":
812+
elif drug_type == self.antibiotics_administered_string:
805813
locator_prefix = "#UI_ANTIBIOTIC_DOSE"
806-
elif drug_type.lower() == "other drugs administered":
814+
elif drug_type == self.other_drugs_administered_string:
807815
locator_prefix = "#UI_DOSE"
808816
return self.page.locator(f"{locator_prefix}{drug_number}")
809817

@@ -847,7 +855,11 @@ def assert_all_drug_information(
847855
drug_numbers = [
848856
int(match.group(1))
849857
for key in drug_information
850-
if (match := re.match(r"drug_(?:dose|type)(\d+)", key))
858+
if (
859+
match := re.match(
860+
r"(?:drug|other_drug|antibiotic_drug)_(?:dose|type)(\d+)", key
861+
)
862+
)
851863
]
852864

853865
if not drug_numbers:
@@ -857,8 +869,16 @@ def assert_all_drug_information(
857869
max_drug_number = max(drug_numbers)
858870

859871
for drug_index in range(1, max_drug_number + 1):
860-
drug_type = drug_information.get(f"drug_type{drug_index}")
861-
drug_dose = drug_information.get(f"drug_dose{drug_index}")
872+
drug_type = (
873+
drug_information.get(f"drug_type{drug_index}")
874+
or drug_information.get(f"other_drug_type{drug_index}")
875+
or drug_information.get(f"antibiotic_drug_type{drug_index}")
876+
)
877+
drug_dose = (
878+
drug_information.get(f"drug_dose{drug_index}")
879+
or drug_information.get(f"other_drug_dose{drug_index}")
880+
or drug_information.get(f"antibiotic_drug_dose{drug_index}")
881+
)
862882

863883
if drug_type is not None:
864884
self.assert_drug_type_text(drug_type_label, drug_index, drug_type)
@@ -891,8 +911,25 @@ def assert_all_drug_information(
891911
| DrugTypeOptions.FLEET_PHOSPHO_SODA
892912
):
893913
expected_unit = "Mls Solution"
894-
case DrugTypeOptions.OTHER:
914+
case (
915+
DrugTypeOptions.OTHER
916+
| AntibioticsAdministeredDrugTypeOptions.OTHER_ANTIBIOTIC
917+
):
895918
expected_unit = ""
919+
case (
920+
AntibioticsAdministeredDrugTypeOptions.AMOXYCILLIN
921+
| AntibioticsAdministeredDrugTypeOptions.CEFOTAXIME
922+
| AntibioticsAdministeredDrugTypeOptions.VANCOMYCIN
923+
):
924+
expected_unit = "g"
925+
case (
926+
AntibioticsAdministeredDrugTypeOptions.CIPROFLAXACIN
927+
| AntibioticsAdministeredDrugTypeOptions.CO_AMOXICLAV
928+
| AntibioticsAdministeredDrugTypeOptions.GENTAMICIN
929+
| AntibioticsAdministeredDrugTypeOptions.METRONIDAZOLE
930+
| AntibioticsAdministeredDrugTypeOptions.TEICOPLANIN
931+
):
932+
expected_unit = "mg"
896933
case _:
897934
expected_unit = None
898935

@@ -911,11 +948,11 @@ def get_drug_dose_unit_locator(self, drug_type: str, drug_number: int) -> Locato
911948
drug_type (str): The drug type to check
912949
drug_number (int): The number of the drug to check
913950
"""
914-
if drug_type.lower() == "bowel preparation administered":
951+
if drug_type == self.bowel_preparation_administered_string:
915952
locator_prefix = "#spanBowelPrepDrugDosageUnit"
916-
elif drug_type.lower() == "antibiotics administered":
953+
elif drug_type == self.antibiotics_administered_string:
917954
locator_prefix = "#spanAntibioticDosageUnit"
918-
elif drug_type.lower() == "other drugs administered":
955+
elif drug_type == self.other_drugs_administered_string:
919956
locator_prefix = "#spanDosageUnit"
920957
return self.page.locator(f"{locator_prefix}{drug_number}")
921958

@@ -940,6 +977,46 @@ def assert_drug_dose_unit_text(
940977
actual_text == expected_text
941978
), f"Expected drug unit dose text '{expected_text}' but found '{actual_text}'"
942979

980+
def assert_drug_dosage_unit_text(
981+
self, drug_type: str, drug_number: int, expected_text: str
982+
) -> None:
983+
"""
984+
Asserts that the drug dosage unit contains the expected text.
985+
986+
Args:
987+
drug_type (str): The drug type to check
988+
drug_number (int): The number of the drug dosage unit cell to check.
989+
expected_text (str): The expected text content of the cell.
990+
991+
Raises:
992+
AssertionError: If the actual text does not match the expected text.
993+
"""
994+
locator = self.get_drug_dosage_text_locator(drug_type, drug_number)
995+
actual_text = locator.inner_text().strip()
996+
997+
logging.info(
998+
f"Drug dosage unit text for drug {drug_number}: "
999+
f"'{actual_text}' (expected: '{expected_text}')"
1000+
)
1001+
1002+
assert actual_text == expected_text, (
1003+
f"Expected drug dosage unit text '{expected_text}' "
1004+
f"but found '{actual_text}'"
1005+
)
1006+
1007+
def get_drug_dosage_text_locator(self, drug_type: str, drug_number: int) -> Locator:
1008+
"""
1009+
Returns the drug dosage text locator for the matching drug type and number
1010+
Args:
1011+
drug_type (str): The drug type to check
1012+
drug_number (int): The number of the drug to check
1013+
"""
1014+
if drug_type == self.bowel_preparation_administered_string:
1015+
locator_prefix = "#HILITE_spanBowelPrepDrugDosageUnit"
1016+
elif drug_type == self.antibiotics_administered_string:
1017+
locator_prefix = "#HILITE_spanAntibioticDosageUnit"
1018+
return self.page.locator(f"{locator_prefix}{drug_number}")
1019+
9431020

9441021
def normalize_label(text: str) -> str:
9451022
"""
@@ -1276,3 +1353,62 @@ class PolypReasonLeftInSituOptions(StrEnum):
12761353
REQUIRES_SURGICAL_RESECTION = "200558"
12771354
CANNOT_FIND_POLYP_ON_WITHDRAWAL = "200559"
12781355
CLINICAL_DECISION_NOT_TO_EXCISE = "203082"
1356+
1357+
1358+
class AntibioticsAdministeredDrugTypeOptions(StrEnum):
1359+
"""Enum for antobiotics administered drug type options"""
1360+
1361+
AMOXYCILLIN = "17941~g"
1362+
CEFOTAXIME = "17950~g"
1363+
CIPROFLAXACIN = "17945~mg"
1364+
CO_AMOXICLAV = "17951~mg"
1365+
GENTAMICIN = "17942~mg"
1366+
METRONIDAZOLE = "17949~mg"
1367+
TEICOPLANIN = "17944~mg"
1368+
VANCOMYCIN = "17943~g"
1369+
OTHER_ANTIBIOTIC = "305493"
1370+
1371+
1372+
# Registry of all known Enums to search when matching string values
1373+
ALL_ENUMS: List[type[Enum]] = [
1374+
obj
1375+
for obj in globals().values()
1376+
if (
1377+
isinstance(obj, type)
1378+
and issubclass(obj, Enum)
1379+
and obj is not Enum
1380+
and obj is not StrEnum # Exclude only the base classes, not subclasses
1381+
)
1382+
]
1383+
1384+
1385+
def to_enum_name_or_value(val: Any) -> Union[str, Any]:
1386+
"""
1387+
Convert an Enum member or matching string value to its Enum name.
1388+
1389+
If the input is:
1390+
- An Enum member → returns the `.name` (e.g., "KLEAN_PREP")
1391+
- A string matching any Enum value in ALL_ENUMS → returns that member's `.name`
1392+
- Anything else → returns the value unchanged
1393+
1394+
Args:
1395+
val (Any): The value to convert. Can be an Enum member, a string,
1396+
or any other type.
1397+
1398+
Returns:
1399+
Union[str, Any]: The Enum name (string) if matched, otherwise the original value.
1400+
"""
1401+
# Directly handle Enum instances
1402+
if isinstance(val, Enum):
1403+
return val.name
1404+
1405+
# Handle strings that match known Enum values
1406+
if isinstance(val, str):
1407+
for enum_cls in ALL_ENUMS:
1408+
try:
1409+
return enum_cls(val).name
1410+
except ValueError:
1411+
continue
1412+
1413+
# Fallback: return unchanged
1414+
return val

0 commit comments

Comments
 (0)