From 20956f5b05da915b67d4604cf095cb6e4ab9b947 Mon Sep 17 00:00:00 2001 From: Adriano Aru Date: Fri, 9 Jan 2026 10:37:56 +0000 Subject: [PATCH 1/8] Initial commit --- pages/organisations/parameters_page.py | 159 ++++++++++++++++++ .../organisation/test_parameter_pages.py | 83 +++++++++ .../organisation_parameters.py | 17 ++ utils/table_util.py | 30 +++- 4 files changed, 287 insertions(+), 2 deletions(-) create mode 100644 pages/organisations/parameters_page.py create mode 100644 tests/regression/organisation/test_parameter_pages.py diff --git a/pages/organisations/parameters_page.py b/pages/organisations/parameters_page.py new file mode 100644 index 00000000..7090ff77 --- /dev/null +++ b/pages/organisations/parameters_page.py @@ -0,0 +1,159 @@ +from playwright.sync_api import Page +from pages.base_page import BasePage +from typing import List +from utils.table_util import TableUtils +import logging +from utils.oracle.oracle_specific_functions.organisation_parameters import ( + get_org_parameter_value, +) +from datetime import datetime +from utils.calendar_picker import CalendarPicker + + +class ParametersPage(BasePage): + """Organisations Page locators, and methods for interacting with the page.""" + + def __init__(self, page: Page): + super().__init__(page) + + self.parameters_table = TableUtils(self.page, "#displayRS") + self.parameters_output_table = TableUtils(self.page, "#displayRS") + self.add_new_parameter_value_button = self.page.get_by_role( + "button", name="Add new parameter value" + ) + self.parameter_value_input_field = self.page.locator("#A_C_VALUE") + self.parameter_effective_from_date_input_field = self.page.locator( + "#A_C_START_DATE" + ) + self.parameter_reason_input_field = self.page.locator("#A_C_AUDIT_REASON") + self.save_button = self.page.get_by_role("button", name="Save") + self.row_index = None + + def get_current_value_of_parameter(self, parameter_id: str) -> str: + """ + Gets the current value of a parameter from the parameters table. + + Args: + parameter_id (str): The ID of the parameter to look for. + Returns: + str: The current value of the parameter. + """ + row_index = self.parameters_table.get_row_index("ID", parameter_id) + if row_index is None: + raise ValueError( + f"Parameter ID {parameter_id} not found in parameters table." + ) + return self.parameters_table.get_cell_value("Current Value", row_index) + + def assert_parameter_value_matches_expected( + self, parameter_id: str, expected_value: str + ) -> None: + """ + Asserts that the current value of a parameter matches the expected value. + Args: + parameter_id (str): The ID of the parameter to look for. + expected_value (str): The expected value of the parameter. + """ + actual_value = self.get_current_value_of_parameter(parameter_id) + assert ( + actual_value == expected_value + ), f"Parameter ID {parameter_id} value mismatch: expected {expected_value}, got {actual_value}" + logging.info( + f"Parameter ID {parameter_id} value matches expected: {expected_value}" + ) + + def get_parameter_lower_value(self, parameter_id: str) -> str: + """ + Gets the lower value of a parameter from the parameters table. + Args: + parameter_id (str): The ID of the parameter to look for. + Returns: + str: The lower value of the parameter. + """ + if self.row_index is None: + self.row_index = self.parameters_table.get_row_index("ID", parameter_id) + return self.parameters_table.get_cell_value("Lower Value", self.row_index) + + def get_parameter_upper_value(self, parameter_id: str) -> str: + """ + Gets the upper value of a parameter from the parameters table. + Args: + parameter_id (str): The ID of the parameter to look for. + Returns: + str: The upper value of the parameter. + """ + if self.row_index is None: + self.row_index = self.parameters_table.get_row_index("ID", parameter_id) + return self.parameters_table.get_cell_value("Upper Value", self.row_index) + + def click_parameter_id_link(self, parameter_id: str) -> None: + """Clicks on the parameter ID link in the parameters table. + + Args: + parameter_id (str): The ID of the parameter to click. + """ + self.click(self.page.get_by_role("link", name=parameter_id, exact=True)) + + def click_add_new_parameter_value_button(self) -> None: + """Clicks the 'Add new parameter value' button.""" + self.click(self.add_new_parameter_value_button) + + def enter_new_parameter_value(self, new_value: str) -> None: + """Enters a new value into the parameter value input field.""" + self.parameter_value_input_field.fill(new_value) + + def enter_new_parameter_effective_from_date(self, new_date: datetime) -> None: + """Enters a new effective from date into the parameter effective from date input field.""" + CalendarPicker(self.page).calendar_picker_ddmmyyyy( + new_date, self.parameter_effective_from_date_input_field + ) + + def enter_parameter_reason(self, reason: str) -> None: + """Enters a reason into the parameter reason input field.""" + self.parameter_reason_input_field.fill(reason) + + def click_save_button(self) -> None: + """Clicks the 'Save' button.""" + self.click(self.save_button) + + def click_save_button_and_accept_dialog(self) -> None: + """Clicks the 'Save' button and accepts the confirmation dialog.""" + self.safe_accept_dialog(self.save_button) + + def enter_new_parameter_details( + self, new_value: str, new_date: datetime, reason: str + ) -> None: + """ + Enters new parameter details. + Args: + new_value (str): The new value for the parameter. + new_date (datetime): The effective from date for the new parameter value. + reason (str): The reason for the change. + """ + self.enter_new_parameter_value(new_value) + self.enter_new_parameter_effective_from_date(new_date) + self.enter_parameter_reason(reason) + + +class Parameter: + """Class representing a parameter with its attributes.""" + + def __init__( + self, + page: Page, + param_id: str, + ): + self.page = page + self.param_id = param_id + self.lower_value = ParametersPage(self.page).get_parameter_lower_value( + self.param_id + ) + self.upper_value = ParametersPage(self.page).get_parameter_upper_value( + self.param_id + ) + self.current_value = ParametersPage(self.page).get_current_value_of_parameter( + self.param_id + ) + self.current_value_db = get_org_parameter_value(int(self.param_id), "23159")[ + "val" + ].values[0] diff --git a/tests/regression/organisation/test_parameter_pages.py b/tests/regression/organisation/test_parameter_pages.py new file mode 100644 index 00000000..12ef4802 --- /dev/null +++ b/tests/regression/organisation/test_parameter_pages.py @@ -0,0 +1,83 @@ +import pytest +from playwright.sync_api import Page, expect +from pages.base_page import BasePage +from pages.organisations import organisations_and_site_details_page +from pages.organisations.organisations_page import OrganisationsPage +from pages.organisations.parameters_page import ParametersPage, Parameter +from pages.organisations.organisations_and_site_details_page import ( + OrganisationsAndSiteDetailsPage, +) +from pages.organisations.list_all_organisations_page import ( + ListAllOrganisationsPage, + OrganisationType, +) +from pages.organisations.create_organisation_page import CreateOrganisationPage +from pages.organisations.view_organisation_page import ViewOrganisationPage +from pages.organisations.list_all_sites_page import ListAllSitesPage, SiteType +from pages.organisations.create_site_page import CreateSitePage +from utils.user_tools import UserTools +from utils.table_util import TableUtils +from utils.calendar_picker import CalendarPicker +from datetime import datetime, timedelta +import logging +from utils.oracle.oracle_specific_functions.organisation_parameters import ( + get_national_parameter_value, +) + + +@pytest.mark.wip +def test_parameter_pages_as_hub_manager(page) -> None: + """ + Test to verify parameter page functionality as a Hub Manager. + Tests both the organisation and screening centre parameter pages. + """ + + # Given I log in to BCSS "England" as user role "Hub Manager" + UserTools.user_login(page, "Hub Manager at BCS01") + + # When I navigate to the Organisation Parameters page for my hub + BasePage(page).go_to_organisations_page() + OrganisationsPage(page).go_to_organisation_parameters_page() + + # Then I verify that Parameter ID "23" current value matches the value in the database + parameter_23 = Parameter(page, "23") + ParametersPage(page).assert_parameter_value_matches_expected( + parameter_23.param_id, parameter_23.current_value_db + ) + + # When I add a new parameter value for Parameter ID "23" that is less than the Lower Value + ParametersPage(page).click_parameter_id_link(parameter_23.param_id) + ParametersPage(page).click_add_new_parameter_value_button() + ParametersPage(page).enter_new_parameter_details( + new_value=str(int(parameter_23.lower_value) - 1), + new_date=datetime.today() + timedelta(days=1), + reason="Automated Test", + ) + + # When I click the Save button + # Then I get a confirmation prompt that "contains" "Value must not be less than Lower Value." + ParametersPage(page).assert_dialog_text( + "Value must not be less than Lower Value.", True + ) + ParametersPage(page).click_save_button() + + # When I add a new parameter value for Parameter ID "23" that is greater than the Upper Value + ParametersPage(page).enter_new_parameter_details( + new_value=str(int(parameter_23.upper_value) + 1), + new_date=datetime.today() + timedelta(days=1), + reason="Automated Test", + ) + + # When I click the Save button + # Then I get a confirmation prompt that "contains" "Value must not exceed Upper Value." + ParametersPage(page).assert_dialog_text("Value must not exceed Upper Value.", True) + ParametersPage(page).click_save_button() + + # When I add a new parameter value for Parameter ID "23" that is empty + ParametersPage(page).enter_new_parameter_details( + new_value="", + new_date=datetime.today() + timedelta(days=1), + reason="Automated Test", + ) + + national_param_value = get_national_parameter_value(int(parameter_23.param_id)) diff --git a/utils/oracle/oracle_specific_functions/organisation_parameters.py b/utils/oracle/oracle_specific_functions/organisation_parameters.py index 5ec79dc9..4c66016c 100644 --- a/utils/oracle/oracle_specific_functions/organisation_parameters.py +++ b/utils/oracle/oracle_specific_functions/organisation_parameters.py @@ -97,3 +97,20 @@ def check_parameter(param_id: int, org_id: str, expected_param_value: str) -> bo logging.warning(f"Parameter {param_id} is not set correctly, updating parameter.") return False + + +def get_national_parameter_value(param_id: int) -> str: + """ + Retrieves the default value of a national parameter from the database. + Args: + param_id (int): The ID of the national parameter to retrieve. + """ + query = """SELECT p.default_value + FROM parameters p + WHERE p.param_id = :param_id""" + params = {"param_id": param_id} + df = OracleDB().execute_query(query, params) + if not df.empty: + return df.iloc[0]["default_value"] + else: + raise ValueError(f"Parameter ID {param_id} not found in parameters table.") diff --git a/utils/table_util.py b/utils/table_util.py index 0f25a36e..7cd8ee71 100644 --- a/utils/table_util.py +++ b/utils/table_util.py @@ -52,7 +52,7 @@ def get_column_index(self, column_name: str) -> int: # Extract first-row headers (general headers) header_texts = headers.evaluate_all("ths => ths.map(th => th.innerText.trim())") - logging.info(f"First Row Headers Found: {header_texts}") + logging.debug(f"First Row Headers Found: {header_texts}") # Attempt to extract a second row of headers (commonly used for filters or alternate titles) second_row_headers = self.table.locator( @@ -65,7 +65,7 @@ def get_column_index(self, column_name: str) -> int: for h in second_row_headers ): header_texts = second_row_headers - logging.info(f"Second Row Headers Found: {second_row_headers}") + logging.debug(f"Second Row Headers Found: {second_row_headers}") for index, header in enumerate(header_texts): if column_name.strip().lower() == header.strip().lower(): @@ -395,3 +395,29 @@ def verify_value_for_label(self, label_text: str, expected_text: str) -> None: logging.info( f"Verified: label '{label_text}' has expected text '{expected_text}'" ) + + def get_row_index(self, column_name: str, row_value: str) -> int: + """ + Returns the 1-based index of the first row where the specified column contains the given value. + Args: + column_name (str): The name of the column to search in. + row_value (str): The value to match in the specified column. + Returns: + int: The 1-based index of the matching row, or -1 if not found. + """ + column_index = self.get_column_index(column_name) + if column_index == -1: + raise ValueError(f"Column '{column_name}' not found in table") + + row_count = self.get_row_count() + for row_index in range(row_count): + cell_locator = self.page.locator( + f"{self.table_id} > tbody tr:nth-child({row_index+1}) td:nth-child({column_index})" + ) + if cell_locator.count() > 0: + cell_text = cell_locator.first.inner_text().strip() + if cell_text == row_value: + return row_index + 1 # 1-based index + raise ValueError( + f"No row found with the value '{row_value}' in the column '{column_name}'." + ) From 1250d1650cebee19e7bedaed44309a47e55aa0e3 Mon Sep 17 00:00:00 2001 From: Adriano Aru Date: Wed, 14 Jan 2026 16:39:16 +0000 Subject: [PATCH 2/8] Refactoring and adding new test --- pages/base_page.py | 2 +- pages/organisations/parameters_page.py | 168 ++++++- .../organisation/test_parameter_pages.py | 440 +++++++++++++++--- 3 files changed, 536 insertions(+), 74 deletions(-) diff --git a/pages/base_page.py b/pages/base_page.py index c611cf2b..e9f7f292 100644 --- a/pages/base_page.py +++ b/pages/base_page.py @@ -286,7 +286,7 @@ def handle_dialog(dialog: Dialog, accept: bool = False): expected_text in actual_text ), f"Expected dialog to contain '{expected_text}', but got '{actual_text}'" except AssertionError as e: - self._dialog_assertion_error = e + raise AssertionError(f"Dialog text assertion failed: {e}") from e if accept: try: dialog.accept() diff --git a/pages/organisations/parameters_page.py b/pages/organisations/parameters_page.py index 7090ff77..da729994 100644 --- a/pages/organisations/parameters_page.py +++ b/pages/organisations/parameters_page.py @@ -1,17 +1,19 @@ +from typing import Optional from playwright.sync_api import Page from pages.base_page import BasePage -from typing import List from utils.table_util import TableUtils import logging +import pandas as pd from utils.oracle.oracle_specific_functions.organisation_parameters import ( get_org_parameter_value, + get_national_parameter_value, ) -from datetime import datetime +from datetime import datetime, timedelta from utils.calendar_picker import CalendarPicker class ParametersPage(BasePage): - """Organisations Page locators, and methods for interacting with the page.""" + """Parameters Page locators, and methods for interacting with the page.""" def __init__(self, page: Page): super().__init__(page) @@ -98,6 +100,9 @@ def click_add_new_parameter_value_button(self) -> None: """Clicks the 'Add new parameter value' button.""" self.click(self.add_new_parameter_value_button) + def select_new_parameter_value(self, new_parameter_value: str) -> None: + self.parameter_value_input_field.select_option(label=new_parameter_value) + def enter_new_parameter_value(self, new_value: str) -> None: """Enters a new value into the parameter value input field.""" self.parameter_value_input_field.fill(new_value) @@ -108,6 +113,10 @@ def enter_new_parameter_effective_from_date(self, new_date: datetime) -> None: new_date, self.parameter_effective_from_date_input_field ) + def enter_new_parameter_effective_from_date_str(self, new_date: str) -> None: + """Enters a new effective from date (string) into the parameter effective from date input field.""" + self.parameter_effective_from_date_input_field.fill(new_date) + def enter_parameter_reason(self, reason: str) -> None: """Enters a reason into the parameter reason input field.""" self.parameter_reason_input_field.fill(reason) @@ -120,19 +129,92 @@ def click_save_button_and_accept_dialog(self) -> None: """Clicks the 'Save' button and accepts the confirmation dialog.""" self.safe_accept_dialog(self.save_button) - def enter_new_parameter_details( - self, new_value: str, new_date: datetime, reason: str - ) -> None: + def complete_parameter_page_form(self, criteria: dict) -> None: + """ + Completes the parameter page form with the provided details. + Args: + criteria (dict): A dictionary containing the details to enter into the form. + """ + for field, value in criteria.items(): + match field: + case "input value": + self.enter_new_parameter_value(value) + case "select value": + self.select_new_parameter_value(value) + case "effective from date": + if isinstance(value, datetime): + self.enter_new_parameter_effective_from_date(value) + elif isinstance(value, str): + self.enter_new_parameter_effective_from_date_str(value) + case "reason for change": + self.enter_parameter_reason(value) + + def get_next_available_date(self) -> datetime: + if not ( + most_recent_date := ParameterDetails( + self.page + ).get_most_recent_parameter_date() + ): + most_recent_date = datetime.today() + if most_recent_date < datetime.today(): + most_recent_date = datetime.today() + return most_recent_date + timedelta(days=1) + + def select_screening_centre_parameters_organisation(self, org: str) -> None: + """ + Selects the organisation for screening centre parameters. + """ + self.click(self.page.get_by_role("link", name=org)) + + +class ParameterDetails(BasePage): + + def __init__(self, page: Page): + super().__init__(page) + self.parameters_table = TableUtils(self.page, "#displayRS") + self.warning_messages = self.page.locator("th.warningHeader") + + def get_most_recent_parameter_date(self) -> Optional[datetime]: """ - Enters new parameter details. + Gets the datetime of the most recent parameter value showing in the parameter details table. + Args: - new_value (str): The new value for the parameter. - new_date (datetime): The effective from date for the new parameter value. - reason (str): The reason for the change. + None + Returns: + datetime: The datetime of the most recent parameter value showing in the parameter details table. """ - self.enter_new_parameter_value(new_value) - self.enter_new_parameter_effective_from_date(new_date) - self.enter_parameter_reason(reason) + num_rows = self.parameters_table.get_row_count() + + most_recent_date = self.parameters_table.get_cell_value( + "Effective From Date", num_rows + ) + if most_recent_date == "": + return None + return datetime.strptime(most_recent_date, "%d/%m/%Y") + + def get_most_recent_value_of_parameter(self, effective_from_date: datetime) -> str: + """ + Gets the most value of a parameter from the parameters table. + + Args: + effective_from_date (datetime): The date to look for. + Returns: + str: The most recent value of the parameter. + """ + date_text = datetime.strftime(effective_from_date, "%d/%m/%Y") + + row_index = self.parameters_table.get_row_index( + "Effective From Date", date_text + ) + if row_index is None: + raise ValueError(f"Parameter with date {date_text} not found in table.") + return self.parameters_table.get_cell_value("Value", row_index) + + def search_for_warning(self, message: str) -> bool: + for warning_index in range(self.warning_messages.count()): + if self.warning_messages.nth(warning_index).inner_text().strip() == message: + return True + return False class Parameter: @@ -154,6 +236,60 @@ def __init__( self.current_value = ParametersPage(self.page).get_current_value_of_parameter( self.param_id ) - self.current_value_db = get_org_parameter_value(int(self.param_id), "23159")[ - "val" - ].values[0] + self.current_value_db = self.get_current_value_from_db() + self.national_parameter_value = get_national_parameter_value(int(self.param_id)) + + def get_current_value_from_db(self) -> str: + """Gets the current value of the parameter from the database. + + Returns: + str: The current value of the parameter from the database. + """ + # First check for organisation parameters + param_df = get_org_parameter_value(int(self.param_id), "23159") + if not param_df.empty: + return param_df["val"].values[0] + else: + # Then check for screening centre parameters + param_df2 = get_org_parameter_value(int(self.param_id), "23162") + if not param_df2.empty: + return param_df2["val"].values[0] + else: + # Otherwise use the national default value + return get_national_parameter_value(int(self.param_id)) + + def get_add_to_time_value(self, time_str: str, add_minutes: int) -> str: + """ + Adds minutes to a "hh:mm" formatted time string and returns the new time string. + Args: + time_str (str): Time in "hh:mm" format. + add_minutes (int): Minutes to add. + Returns: + str: New time in "hh:mm" format. + """ + + def to_minutes(time_str: str) -> int: + """ + Converts "hh:mm" formatted string to total minutes. + Args: + time_str (str): Time in "hh:mm" format. + Returns: + int: Total minutes. + """ + h, m = map(int, time_str.split(":")) + return h * 60 + m + + def to_hh_mm(minutes: int) -> str: + """ + Converts total minutes to "hh:mm" formatted string. + Args: + minutes (int): Total minutes. + Returns: + str: Time in "hh:mm" format. + """ + h = minutes // 60 + m = minutes % 60 + return f"{h:02d}:{m:02d}" + + total_minutes = to_minutes(time_str) + add_minutes + return to_hh_mm(total_minutes) diff --git a/tests/regression/organisation/test_parameter_pages.py b/tests/regression/organisation/test_parameter_pages.py index 12ef4802..eb59b78a 100644 --- a/tests/regression/organisation/test_parameter_pages.py +++ b/tests/regression/organisation/test_parameter_pages.py @@ -1,9 +1,14 @@ import pytest +from numpy.ma.testutils import assert_equal from playwright.sync_api import Page, expect from pages.base_page import BasePage from pages.organisations import organisations_and_site_details_page from pages.organisations.organisations_page import OrganisationsPage -from pages.organisations.parameters_page import ParametersPage, Parameter +from pages.organisations.parameters_page import ( + ParametersPage, + Parameter, + ParameterDetails, +) from pages.organisations.organisations_and_site_details_page import ( OrganisationsAndSiteDetailsPage, ) @@ -23,61 +28,382 @@ from utils.oracle.oracle_specific_functions.organisation_parameters import ( get_national_parameter_value, ) +from pages.logout.log_out_page import LogoutPage + + +class TestParameterChanges: + + @pytest.fixture(autouse=True) + def setup(self, page): + self.page = page + + @pytest.mark.wip + def test_parameter_pages(self) -> None: + """ + Test various interactions on the Organisation Parameters page, including adding new parameter values + and validating their correctness against database values. + 1. Log in as "Hub Manager" + 2. Navigate to Organisation Parameters page + 3. Validate integer parameter (ID 25) + 4. Validate Yes/No parameter (ID 199) + 5. Validate string parameter (ID 182) + 6. Navigate to Screening Centre Parameters page + 7. Validate time parameter (ID 28) + 8. Log out + """ + + # Given I log in to BCSS "England" as user role "Hub Manager" + UserTools.user_login(self.page, "Hub Manager at BCS01") + + # When I navigate to the Organisation Parameters + BasePage(self.page).go_to_organisations_page() + OrganisationsPage(self.page).go_to_organisation_parameters_page() + + # Checking integer fields - Parameter 25 + chosen_parameter = Parameter(self.page, "25") + self.check_chosen_parameter_correct_in_db(chosen_parameter) + ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) + + # When I add a new parameter value for Parameter ID "25" that is lower than the allowed minimum + next_available_date = ParametersPage(self.page).get_next_available_date() + ParametersPage(self.page).click_add_new_parameter_value_button() + ParametersPage(self.page).complete_parameter_page_form( + { + "input value": str(int(chosen_parameter.lower_value) - 1), + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + + # Then I get a confirmation prompt that contains "Value must not be less than Lower Value." + ParametersPage(self.page).assert_dialog_text( + "Value must not be less than Lower Value.", True + ) + ParametersPage(self.page).click_save_button() + + # When I add a new parameter value for Parameter ID "25" that is higher than the allowed maximum + ParametersPage(self.page).complete_parameter_page_form( + { + "input value": str(int(chosen_parameter.upper_value) + 1), + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + + # Then I get a confirmation prompt that contains "Value must not exceed Upper Value." + ParametersPage(self.page).assert_dialog_text( + "Value must not exceed Upper Value.", True + ) + ParametersPage(self.page).click_save_button() + + # When I add a new parameter value for my parameter that is empty + ParametersPage(self.page).click_back_button() + next_available_date = ParametersPage(self.page).get_next_available_date() + ParametersPage(self.page).click_add_new_parameter_value_button() + ParametersPage(self.page).complete_parameter_page_form( + { + "input value": "", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(self.page).click_save_button_and_accept_dialog() + + # Then it will default to the national value + most_recent_value = ParameterDetails( + self.page + ).get_most_recent_value_of_parameter(next_available_date) + assert ( + chosen_parameter.national_parameter_value + ) == most_recent_value, f"Parameter value does not match national value when empty value entered. Expected {chosen_parameter.national_parameter_value}, got {most_recent_value}" + logging.info( + f"[UI ASSERTIONS COMPLETE] Parameter value matches national value when empty value entered:\n Expected Value: {chosen_parameter.national_parameter_value}\n Actual Value: {most_recent_value}" + ) + + # When I add a new parameter value for my parameter that is valid + next_available_date = ParametersPage(self.page).get_next_available_date() + ParametersPage(self.page).click_add_new_parameter_value_button() + ParametersPage(self.page).complete_parameter_page_form( + { + "input value": "15", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(self.page).click_save_button_and_accept_dialog() + + # Then the parameter is saved with the correct value + most_recent_value = ParameterDetails( + self.page + ).get_most_recent_value_of_parameter(next_available_date) + assert ( + most_recent_value == "15" + ), f"Parameter value does not match the expect value. Expected {chosen_parameter.national_parameter_value}, got {most_recent_value}" + logging.info( + f"[UI ASSERTIONS COMPLETE] Parameter value matches expected value:\n Expected Value: 15\n Actual Value: {most_recent_value}" + ) + + # When I add a new parameter value for my parameter that is of an incorrect type + next_available_date = ParametersPage(self.page).get_next_available_date() + ParametersPage(self.page).click_add_new_parameter_value_button() + ParametersPage(self.page).complete_parameter_page_form( + { + "input value": "test incorrect type", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(self.page).click_save_button_and_accept_dialog() + + # Then a warning message appears on the following page + assert ParameterDetails(self.page).search_for_warning( + "The update has failed, your changes have not been saved" + ) + assert ParameterDetails(self.page).search_for_warning( + "Parameter value is of the wrong data type" + ) + logging.info( + "[UI ASSERTIONS COMPLETE] Warning messages displayed for incorrect parameter value type" + ) + + # WHen I go pack to rgw "Organisation Parameters" page + ParametersPage(self.page).click_back_button() + ParametersPage(self.page).click_back_button() + + # Checking Yes/No fields - Parameter 199 + chosen_parameter = Parameter(self.page, "199") + self.check_chosen_parameter_correct_in_db(chosen_parameter) + ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) + + # When I select "Yes" for my parameter + next_available_date = ParametersPage(self.page).get_next_available_date() + ParametersPage(self.page).click_add_new_parameter_value_button() + ParametersPage(self.page).complete_parameter_page_form( + { + "select value": "Yes", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(self.page).click_save_button_and_accept_dialog() + + # Then it will save the value as "Y" + most_recent_value = ParameterDetails( + self.page + ).get_most_recent_value_of_parameter(next_available_date) + assert ( + most_recent_value == "Y" + ), f"Parameter value not saved as 'Y' when 'Yes' selected. Got {most_recent_value}" + logging.info( + f"[UI ASSERTIONS COMPLETE] Parameter value saved as 'Y' when 'Yes' selected:\n Expected Value: Y\n Actual Value: {most_recent_value}" + ) + + # When I select "No" for my parameter + next_available_date = ParametersPage(self.page).get_next_available_date() + ParametersPage(self.page).click_add_new_parameter_value_button() + ParametersPage(self.page).complete_parameter_page_form( + { + "select value": "No", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(self.page).click_save_button_and_accept_dialog() + + # Then it will save the value as "N" + most_recent_value = ParameterDetails( + self.page + ).get_most_recent_value_of_parameter(next_available_date) + assert ( + most_recent_value == "N" + ), f"Parameter value not saved as 'N' when 'No' selected. Got {most_recent_value}" + logging.info( + f"[UI ASSERTIONS COMPLETE] Parameter value saved as 'N' when 'No' selected:\n Expected Value: N\n Actual Value: {most_recent_value}" + ) + + # When I select "Yes" for my parameter but do not enter a reason + next_available_date = ParametersPage(self.page).get_next_available_date() + ParametersPage(self.page).click_add_new_parameter_value_button() + ParametersPage(self.page).complete_parameter_page_form( + { + "select value": "Yes", + "effective from date": next_available_date, + "reason for change": "", + } + ) + ParametersPage(self.page).click_save_button_and_accept_dialog() + # Then I get a confirmation prompt that contains "The fields 'Parameter Value', 'Effective From Date' and 'Reason For Change or Addition' are mandatory." + ParametersPage(self.page).assert_dialog_text( + "The fields 'Parameter Value', 'Effective From Date' and 'Reason For Change or Addition' are mandatory.", + True, + ) + + # When I go back to the "Organisation Parameters" page + ParametersPage(self.page).click_back_button() + ParametersPage(self.page).click_back_button() + + # Checking string fields - Parameter 182 + chosen_parameter = Parameter(self.page, "182") + self.check_chosen_parameter_correct_in_db(chosen_parameter) + ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) + + # When I add a new parameter value for my parameter that is valid + next_available_date = ParametersPage(self.page).get_next_available_date() + ParametersPage(self.page).click_add_new_parameter_value_button() + ParametersPage(self.page).complete_parameter_page_form( + { + "input value": "Valid String Test", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(self.page).click_save_button_and_accept_dialog() + + # Then the parameter is saved with the correct value + most_recent_value = ParameterDetails( + self.page + ).get_most_recent_value_of_parameter(next_available_date) + assert ( + most_recent_value == "Valid String Test" + ), f"Parameter value does not match the expect value. Expected 'Valid String Test', got {most_recent_value}" + logging.info( + f"[UI ASSERTIONS COMPLETE] Parameter value matches expected value:\n Expected Value: 'Valid String Test'\n Actual Value: {most_recent_value}" + ) + + # When I navigate to the Screening Centre Parameters + ParametersPage(self.page).click_main_menu_link() + BasePage(self.page).go_to_organisations_page() + OrganisationsPage(self.page).go_to_screening_centre_parameters_page() + ParametersPage(self.page).select_screening_centre_parameters_organisation( + "BCS001" + ) + + # Checking time fields - Parameter 28 + chosen_parameter = Parameter(self.page, "28") + self.check_chosen_parameter_correct_in_db(chosen_parameter) + ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) + + # When I add a value for my parameter that is lower than the allowed minimum + next_available_date = ParametersPage(self.page).get_next_available_date() + ParametersPage(self.page).click_add_new_parameter_value_button() + ParametersPage(self.page).complete_parameter_page_form( + { + "input value": chosen_parameter.get_add_to_time_value( + chosen_parameter.lower_value, -1 + ), + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(self.page).click_save_button_and_accept_dialog() + + # Then a warning message appears on the following page + assert ParameterDetails(self.page).search_for_warning( + "The update has failed, your changes have not been saved" + ) + assert ParameterDetails(self.page).search_for_warning( + "Parameter value is not within the defined range of values" + ) + logging.info( + "[UI ASSERTIONS COMPLETE] Warning messages displayed for out-of-bounds time parameter value" + ) + + # When I add a value for my parameter that is higher than the allowed maximum + ParametersPage(self.page).click_back_button() + ParametersPage(self.page).click_add_new_parameter_value_button() + ParametersPage(self.page).complete_parameter_page_form( + { + "input value": chosen_parameter.get_add_to_time_value( + chosen_parameter.upper_value, 1 + ), + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(self.page).click_save_button_and_accept_dialog() + + # Then a warning message appears on the following page + assert ParameterDetails(self.page).search_for_warning( + "The update has failed, your changes have not been saved" + ) + assert ParameterDetails(self.page).search_for_warning( + "Parameter value is not within the defined range of values" + ) + logging.info( + "[UI ASSERTIONS COMPLETE] Warning messages displayed for out-of-bounds time parameter value" + ) + + # When I add a valid value for my parameter + ParametersPage(self.page).click_back_button() + ParametersPage(self.page).click_add_new_parameter_value_button() + ParametersPage(self.page).complete_parameter_page_form( + { + "input value": "10:00", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(self.page).click_save_button_and_accept_dialog() + + # Then the parameter is saved with the correct value + most_recent_value = ParameterDetails( + self.page + ).get_most_recent_value_of_parameter(next_available_date) + assert ( + most_recent_value == "10:00" + ), f"Parameter value does not match the expect value. Expected '10:00', got {most_recent_value}" + logging.info( + f"[UI ASSERTIONS COMPLETE] Parameter value matches expected value:\n Expected Value: '10:00'\n Actual Value: {most_recent_value}" + ) + + # Finally, I log out + LogoutPage(self.page).log_out() + + def test_parameter_212(self) -> None: + """ """ + # Given I log in to BCSS "England" as user role "Screening Centre Manager" + UserTools.user_login(self.page, "Screening Centre Manager at BCS001") + + # When I navigate to the Organisation Parameters page + BasePage(self.page).go_to_organisations_page() + OrganisationsPage(self.page).go_to_organisation_parameters_page() + + # Then I can see that Parameter ID "212" has the correct value from the database + chosen_parameter = Parameter(self.page, "212") + self.check_chosen_parameter_correct_in_db(chosen_parameter) + + # I am able to view Parameter ID "212" as a Screening Centre Manager + ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) + + # I "cannot" add a new parameter value + assert ParametersPage( + self.page + ).add_new_parameter_value_button.is_hidden(), "Add New Parameter Value button is visible, but should be hidden for Screening Centre Manager" + logging.info( + "[UI ASSERTIONS COMPLETE] 'Add New Parameter Value' button is hidden for Screening Centre Manager" + ) + + # When I switch users to BCSS "England" as user role "Hub Manager" + LogoutPage(self.page).log_out(close_page=False) + BasePage(self.page).go_to_log_in_page() + UserTools.user_login(self.page, "Hub Manager at BCS01") + + # Then i go to screening centre parameters page + # I am able to view Parameter ID "212" as a hub manager + # I "cannot" add a new parameter value + + # When I switch users to BCSS "England" as user role "BCSS Support - SC" + LogoutPage(self.page).log_out() -@pytest.mark.wip -def test_parameter_pages_as_hub_manager(page) -> None: - """ - Test to verify parameter page functionality as a Hub Manager. - Tests both the organisation and screening centre parameter pages. - """ - - # Given I log in to BCSS "England" as user role "Hub Manager" - UserTools.user_login(page, "Hub Manager at BCS01") - - # When I navigate to the Organisation Parameters page for my hub - BasePage(page).go_to_organisations_page() - OrganisationsPage(page).go_to_organisation_parameters_page() - - # Then I verify that Parameter ID "23" current value matches the value in the database - parameter_23 = Parameter(page, "23") - ParametersPage(page).assert_parameter_value_matches_expected( - parameter_23.param_id, parameter_23.current_value_db - ) - - # When I add a new parameter value for Parameter ID "23" that is less than the Lower Value - ParametersPage(page).click_parameter_id_link(parameter_23.param_id) - ParametersPage(page).click_add_new_parameter_value_button() - ParametersPage(page).enter_new_parameter_details( - new_value=str(int(parameter_23.lower_value) - 1), - new_date=datetime.today() + timedelta(days=1), - reason="Automated Test", - ) - - # When I click the Save button - # Then I get a confirmation prompt that "contains" "Value must not be less than Lower Value." - ParametersPage(page).assert_dialog_text( - "Value must not be less than Lower Value.", True - ) - ParametersPage(page).click_save_button() - - # When I add a new parameter value for Parameter ID "23" that is greater than the Upper Value - ParametersPage(page).enter_new_parameter_details( - new_value=str(int(parameter_23.upper_value) + 1), - new_date=datetime.today() + timedelta(days=1), - reason="Automated Test", - ) - - # When I click the Save button - # Then I get a confirmation prompt that "contains" "Value must not exceed Upper Value." - ParametersPage(page).assert_dialog_text("Value must not exceed Upper Value.", True) - ParametersPage(page).click_save_button() - - # When I add a new parameter value for Parameter ID "23" that is empty - ParametersPage(page).enter_new_parameter_details( - new_value="", - new_date=datetime.today() + timedelta(days=1), - reason="Automated Test", - ) - - national_param_value = get_national_parameter_value(int(parameter_23.param_id)) + def check_chosen_parameter_correct_in_db( + self, chosen_parameter: "Parameter" + ) -> None: + """ + Checks that the chosen parameter's current value matches the expected value from the database. + Args: + chosen_parameter (Parameter): The parameter to check. + """ + ParametersPage(self.page).assert_parameter_value_matches_expected( + chosen_parameter.param_id, chosen_parameter.current_value_db + ) From bf5ef77d025b7ed491b7c37be372ee32198977e4 Mon Sep 17 00:00:00 2001 From: Megha Prasannan Date: Wed, 14 Jan 2026 17:10:18 +0000 Subject: [PATCH 3/8] Finishing 212 test --- .../organisation/test_parameter_pages.py | 56 ++++++++++++++++++- users.json | 8 +++ 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/tests/regression/organisation/test_parameter_pages.py b/tests/regression/organisation/test_parameter_pages.py index eb59b78a..73fbaa41 100644 --- a/tests/regression/organisation/test_parameter_pages.py +++ b/tests/regression/organisation/test_parameter_pages.py @@ -3,7 +3,7 @@ from playwright.sync_api import Page, expect from pages.base_page import BasePage from pages.organisations import organisations_and_site_details_page -from pages.organisations.organisations_page import OrganisationsPage +from pages.organisations.organisations_page import OrganisationSwitchPage, OrganisationsPage from pages.organisations.parameters_page import ( ParametersPage, Parameter, @@ -37,7 +37,6 @@ class TestParameterChanges: def setup(self, page): self.page = page - @pytest.mark.wip def test_parameter_pages(self) -> None: """ Test various interactions on the Organisation Parameters page, including adding new parameter values @@ -359,6 +358,7 @@ def test_parameter_pages(self) -> None: # Finally, I log out LogoutPage(self.page).log_out() + @pytest.mark.wip def test_parameter_212(self) -> None: """ """ # Given I log in to BCSS "England" as user role "Screening Centre Manager" @@ -389,10 +389,60 @@ def test_parameter_212(self) -> None: UserTools.user_login(self.page, "Hub Manager at BCS01") # Then i go to screening centre parameters page + BasePage(self.page).go_to_organisations_page() + OrganisationsPage(self.page).go_to_screening_centre_parameters_page() + ParametersPage(self.page).select_screening_centre_parameters_organisation("BCS001") # I am able to view Parameter ID "212" as a hub manager + ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) # I "cannot" add a new parameter value - + assert ParametersPage( + self.page + ).add_new_parameter_value_button.is_hidden(), "Add New Parameter Value button is visible, but should be hidden for Hub Manager" + logging.info( + "[UI ASSERTIONS COMPLETE] 'Add New Parameter Value' button is hidden for Hub Manager" + ) # When I switch users to BCSS "England" as user role "BCSS Support - SC" + LogoutPage(self.page).log_out(close_page=False) + BasePage(self.page).go_to_log_in_page() + UserTools.user_login(self.page, "BCSS Support - SC at BCS001") + + # Then i go to organisation parameters page + BasePage(self.page).go_to_organisations_page() + OrganisationsPage(self.page).go_to_organisation_parameters_page() + + # I am able to view Parameter ID "212" as a support user + ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) + + # I "can" add a new parameter value + assert ParametersPage( + self.page + ).add_new_parameter_value_button.is_visible(), "Add New Parameter Value button is hidden, but should be visible for BCSS Support - SC" + logging.info( + "[UI ASSERTIONS COMPLETE] 'Add New Parameter Value' button is visible for BCSS Support - SC" + ) + + # When I switch users to BCSS "England" as user role "BCSS Support - HUB" + LogoutPage(self.page).log_out(close_page=False) + BasePage(self.page).go_to_log_in_page() + UserTools.user_login(self.page, "BCSS Support - HUB") + OrganisationSwitchPage(self.page).select_organisation_by_id("BCS01") + OrganisationSwitchPage(self.page).click_continue() + + # Then i go to organisation parameters page + BasePage(self.page).go_to_organisations_page() + OrganisationsPage(self.page).go_to_screening_centre_parameters_page() + ParametersPage(self.page).select_screening_centre_parameters_organisation("BCS001") + + # I am able to view Parameter ID "212" as a support user + ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) + + # I "can" add a new parameter value + assert ParametersPage( + self.page + ).add_new_parameter_value_button.is_visible(), "Add New Parameter Value button is hidden, but should be visible for BCSS Support - HUB" + logging.info( + "[UI ASSERTIONS COMPLETE] 'Add New Parameter Value' button is visible for BCSS Support - HUB" + ) LogoutPage(self.page).log_out() diff --git a/users.json b/users.json index 6c1ba5af..540a5f15 100644 --- a/users.json +++ b/users.json @@ -154,6 +154,14 @@ "Wolverhampton SC" ] }, + "BCSS Support - HUB": { + "username": "BCSS306", + "role_id": 202245, + "org_code": "BCS01", + "roles": [ + "Midlands and North West Bowel Cancer Screening Programme Hub, BCSS Hub Support" + ] + }, "BCSS Bureau Staff at X26": { "username": "BCSS33", "role_id": 5042, From 7b94be0aa95cfc1f84e909f270b4325dcf4110d5 Mon Sep 17 00:00:00 2001 From: Adriano Aru Date: Thu, 15 Jan 2026 10:27:29 +0000 Subject: [PATCH 4/8] Refactoring and cleaning up code --- pages/organisations/parameters_page.py | 117 ++- pytest.ini | 1 + .../organisation/test_parameter_pages.py | 881 +++++++++--------- utils/date_time_utils.py | 37 + .../organisation_parameters.py | 4 +- 5 files changed, 528 insertions(+), 512 deletions(-) diff --git a/pages/organisations/parameters_page.py b/pages/organisations/parameters_page.py index da729994..c41649fb 100644 --- a/pages/organisations/parameters_page.py +++ b/pages/organisations/parameters_page.py @@ -3,7 +3,6 @@ from pages.base_page import BasePage from utils.table_util import TableUtils import logging -import pandas as pd from utils.oracle.oracle_specific_functions.organisation_parameters import ( get_org_parameter_value, get_national_parameter_value, @@ -29,7 +28,6 @@ def __init__(self, page: Page): ) self.parameter_reason_input_field = self.page.locator("#A_C_AUDIT_REASON") self.save_button = self.page.get_by_role("button", name="Save") - self.row_index = None def get_current_value_of_parameter(self, parameter_id: str) -> str: """ @@ -72,9 +70,8 @@ def get_parameter_lower_value(self, parameter_id: str) -> str: Returns: str: The lower value of the parameter. """ - if self.row_index is None: - self.row_index = self.parameters_table.get_row_index("ID", parameter_id) - return self.parameters_table.get_cell_value("Lower Value", self.row_index) + row_index = self.parameters_table.get_row_index("ID", parameter_id) + return self.parameters_table.get_cell_value("Lower Value", row_index) def get_parameter_upper_value(self, parameter_id: str) -> str: """ @@ -84,9 +81,8 @@ def get_parameter_upper_value(self, parameter_id: str) -> str: Returns: str: The upper value of the parameter. """ - if self.row_index is None: - self.row_index = self.parameters_table.get_row_index("ID", parameter_id) - return self.parameters_table.get_cell_value("Upper Value", self.row_index) + row_index = self.parameters_table.get_row_index("ID", parameter_id) + return self.parameters_table.get_cell_value("Upper Value", row_index) def click_parameter_id_link(self, parameter_id: str) -> None: """Clicks on the parameter ID link in the parameters table. @@ -101,24 +97,45 @@ def click_add_new_parameter_value_button(self) -> None: self.click(self.add_new_parameter_value_button) def select_new_parameter_value(self, new_parameter_value: str) -> None: + """ + Selects a new value from the parameter value input field dropdown. + Args: + new_parameter_value (str): The new parameter value to select. + """ self.parameter_value_input_field.select_option(label=new_parameter_value) def enter_new_parameter_value(self, new_value: str) -> None: - """Enters a new value into the parameter value input field.""" + """ + Enters a new value into the parameter value input field. + Args: + new_value (str): The new parameter value to enter. + """ self.parameter_value_input_field.fill(new_value) def enter_new_parameter_effective_from_date(self, new_date: datetime) -> None: - """Enters a new effective from date into the parameter effective from date input field.""" + """ + Enters a new effective from date into the parameter effective from date input field. + Args: + new_date (datetime): The new effective from date to enter. + """ CalendarPicker(self.page).calendar_picker_ddmmyyyy( new_date, self.parameter_effective_from_date_input_field ) def enter_new_parameter_effective_from_date_str(self, new_date: str) -> None: - """Enters a new effective from date (string) into the parameter effective from date input field.""" + """ + Enters a new effective from date (string) into the parameter effective from date input field. + Args: + new_date (str): The new effective from date (string) to enter. + """ self.parameter_effective_from_date_input_field.fill(new_date) def enter_parameter_reason(self, reason: str) -> None: - """Enters a reason into the parameter reason input field.""" + """ + Enters a reason into the parameter reason input field. + Args: + reason (str): The reason for the parameter change. + """ self.parameter_reason_input_field.fill(reason) def click_save_button(self) -> None: @@ -150,19 +167,26 @@ def complete_parameter_page_form(self, criteria: dict) -> None: self.enter_parameter_reason(value) def get_next_available_date(self) -> datetime: - if not ( - most_recent_date := ParameterDetails( - self.page - ).get_most_recent_parameter_date() - ): - most_recent_date = datetime.today() - if most_recent_date < datetime.today(): - most_recent_date = datetime.today() - return most_recent_date + timedelta(days=1) + """ + Gets the next available date for entering a new parameter value. + Returns: + datetime: The next available date for entering a new parameter value. + """ + most_recent_date = ParameterDetails(self.page).get_most_recent_parameter_date() + today = datetime.today() + # If no date found or most recent date is in the past, use today + base_date = ( + today + if not most_recent_date or most_recent_date < today + else most_recent_date + ) + return base_date + timedelta(days=1) def select_screening_centre_parameters_organisation(self, org: str) -> None: """ Selects the organisation for screening centre parameters. + Args: + org (str): The organisation to select. """ self.click(self.page.get_by_role("link", name=org)) @@ -177,9 +201,6 @@ def __init__(self, page: Page): def get_most_recent_parameter_date(self) -> Optional[datetime]: """ Gets the datetime of the most recent parameter value showing in the parameter details table. - - Args: - None Returns: datetime: The datetime of the most recent parameter value showing in the parameter details table. """ @@ -195,7 +216,6 @@ def get_most_recent_parameter_date(self) -> Optional[datetime]: def get_most_recent_value_of_parameter(self, effective_from_date: datetime) -> str: """ Gets the most value of a parameter from the parameters table. - Args: effective_from_date (datetime): The date to look for. Returns: @@ -211,6 +231,13 @@ def get_most_recent_value_of_parameter(self, effective_from_date: datetime) -> s return self.parameters_table.get_cell_value("Value", row_index) def search_for_warning(self, message: str) -> bool: + """ + Searches for a warning message in the parameter details page. + Args: + message (str): The warning message to search for. + Returns: + bool: True if the warning message is found, False otherwise. + """ for warning_index in range(self.warning_messages.count()): if self.warning_messages.nth(warning_index).inner_text().strip() == message: return True @@ -240,8 +267,8 @@ def __init__( self.national_parameter_value = get_national_parameter_value(int(self.param_id)) def get_current_value_from_db(self) -> str: - """Gets the current value of the parameter from the database. - + """ + Gets the current value of the parameter from the database. Returns: str: The current value of the parameter from the database. """ @@ -257,39 +284,3 @@ def get_current_value_from_db(self) -> str: else: # Otherwise use the national default value return get_national_parameter_value(int(self.param_id)) - - def get_add_to_time_value(self, time_str: str, add_minutes: int) -> str: - """ - Adds minutes to a "hh:mm" formatted time string and returns the new time string. - Args: - time_str (str): Time in "hh:mm" format. - add_minutes (int): Minutes to add. - Returns: - str: New time in "hh:mm" format. - """ - - def to_minutes(time_str: str) -> int: - """ - Converts "hh:mm" formatted string to total minutes. - Args: - time_str (str): Time in "hh:mm" format. - Returns: - int: Total minutes. - """ - h, m = map(int, time_str.split(":")) - return h * 60 + m - - def to_hh_mm(minutes: int) -> str: - """ - Converts total minutes to "hh:mm" formatted string. - Args: - minutes (int): Total minutes. - Returns: - str: Time in "hh:mm" format. - """ - h = minutes // 60 - m = minutes % 60 - return f"{h:02d}:{m:02d}" - - total_minutes = to_minutes(time_str) + add_minutes - return to_hh_mm(total_minutes) diff --git a/pytest.ini b/pytest.ini index 5ccc82a8..faf2fb17 100644 --- a/pytest.ini +++ b/pytest.ini @@ -58,3 +58,4 @@ markers = lynch_self_referral_tests: tests that are part of the lynch self referral test suite surveillance_regression_tests: tests that are part of the surveillance regression test suite lynch_regression_tests: tests that are part of the lynch regression test suite + parameter_212: tests the parameter 212 changes diff --git a/tests/regression/organisation/test_parameter_pages.py b/tests/regression/organisation/test_parameter_pages.py index 73fbaa41..f2d29884 100644 --- a/tests/regression/organisation/test_parameter_pages.py +++ b/tests/regression/organisation/test_parameter_pages.py @@ -1,459 +1,446 @@ import pytest -from numpy.ma.testutils import assert_equal -from playwright.sync_api import Page, expect +from playwright.sync_api import Page from pages.base_page import BasePage -from pages.organisations import organisations_and_site_details_page -from pages.organisations.organisations_page import OrganisationSwitchPage, OrganisationsPage +from pages.organisations.organisations_page import ( + OrganisationSwitchPage, + OrganisationsPage, +) from pages.organisations.parameters_page import ( ParametersPage, Parameter, ParameterDetails, ) -from pages.organisations.organisations_and_site_details_page import ( - OrganisationsAndSiteDetailsPage, -) -from pages.organisations.list_all_organisations_page import ( - ListAllOrganisationsPage, - OrganisationType, -) -from pages.organisations.create_organisation_page import CreateOrganisationPage -from pages.organisations.view_organisation_page import ViewOrganisationPage -from pages.organisations.list_all_sites_page import ListAllSitesPage, SiteType -from pages.organisations.create_site_page import CreateSitePage from utils.user_tools import UserTools -from utils.table_util import TableUtils -from utils.calendar_picker import CalendarPicker -from datetime import datetime, timedelta +from utils.date_time_utils import DateTimeUtils import logging -from utils.oracle.oracle_specific_functions.organisation_parameters import ( - get_national_parameter_value, -) from pages.logout.log_out_page import LogoutPage -class TestParameterChanges: - - @pytest.fixture(autouse=True) - def setup(self, page): - self.page = page - - def test_parameter_pages(self) -> None: - """ - Test various interactions on the Organisation Parameters page, including adding new parameter values - and validating their correctness against database values. - 1. Log in as "Hub Manager" - 2. Navigate to Organisation Parameters page - 3. Validate integer parameter (ID 25) - 4. Validate Yes/No parameter (ID 199) - 5. Validate string parameter (ID 182) - 6. Navigate to Screening Centre Parameters page - 7. Validate time parameter (ID 28) - 8. Log out - """ - - # Given I log in to BCSS "England" as user role "Hub Manager" - UserTools.user_login(self.page, "Hub Manager at BCS01") - - # When I navigate to the Organisation Parameters - BasePage(self.page).go_to_organisations_page() - OrganisationsPage(self.page).go_to_organisation_parameters_page() - - # Checking integer fields - Parameter 25 - chosen_parameter = Parameter(self.page, "25") - self.check_chosen_parameter_correct_in_db(chosen_parameter) - ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) - - # When I add a new parameter value for Parameter ID "25" that is lower than the allowed minimum - next_available_date = ParametersPage(self.page).get_next_available_date() - ParametersPage(self.page).click_add_new_parameter_value_button() - ParametersPage(self.page).complete_parameter_page_form( - { - "input value": str(int(chosen_parameter.lower_value) - 1), - "effective from date": next_available_date, - "reason for change": "Automated Test", - } - ) - - # Then I get a confirmation prompt that contains "Value must not be less than Lower Value." - ParametersPage(self.page).assert_dialog_text( - "Value must not be less than Lower Value.", True - ) - ParametersPage(self.page).click_save_button() - - # When I add a new parameter value for Parameter ID "25" that is higher than the allowed maximum - ParametersPage(self.page).complete_parameter_page_form( - { - "input value": str(int(chosen_parameter.upper_value) + 1), - "effective from date": next_available_date, - "reason for change": "Automated Test", - } - ) - - # Then I get a confirmation prompt that contains "Value must not exceed Upper Value." - ParametersPage(self.page).assert_dialog_text( - "Value must not exceed Upper Value.", True - ) - ParametersPage(self.page).click_save_button() - - # When I add a new parameter value for my parameter that is empty - ParametersPage(self.page).click_back_button() - next_available_date = ParametersPage(self.page).get_next_available_date() - ParametersPage(self.page).click_add_new_parameter_value_button() - ParametersPage(self.page).complete_parameter_page_form( - { - "input value": "", - "effective from date": next_available_date, - "reason for change": "Automated Test", - } - ) - ParametersPage(self.page).click_save_button_and_accept_dialog() - - # Then it will default to the national value - most_recent_value = ParameterDetails( - self.page - ).get_most_recent_value_of_parameter(next_available_date) - assert ( - chosen_parameter.national_parameter_value - ) == most_recent_value, f"Parameter value does not match national value when empty value entered. Expected {chosen_parameter.national_parameter_value}, got {most_recent_value}" - logging.info( - f"[UI ASSERTIONS COMPLETE] Parameter value matches national value when empty value entered:\n Expected Value: {chosen_parameter.national_parameter_value}\n Actual Value: {most_recent_value}" - ) - - # When I add a new parameter value for my parameter that is valid - next_available_date = ParametersPage(self.page).get_next_available_date() - ParametersPage(self.page).click_add_new_parameter_value_button() - ParametersPage(self.page).complete_parameter_page_form( - { - "input value": "15", - "effective from date": next_available_date, - "reason for change": "Automated Test", - } - ) - ParametersPage(self.page).click_save_button_and_accept_dialog() - - # Then the parameter is saved with the correct value - most_recent_value = ParameterDetails( - self.page - ).get_most_recent_value_of_parameter(next_available_date) - assert ( - most_recent_value == "15" - ), f"Parameter value does not match the expect value. Expected {chosen_parameter.national_parameter_value}, got {most_recent_value}" - logging.info( - f"[UI ASSERTIONS COMPLETE] Parameter value matches expected value:\n Expected Value: 15\n Actual Value: {most_recent_value}" - ) - - # When I add a new parameter value for my parameter that is of an incorrect type - next_available_date = ParametersPage(self.page).get_next_available_date() - ParametersPage(self.page).click_add_new_parameter_value_button() - ParametersPage(self.page).complete_parameter_page_form( - { - "input value": "test incorrect type", - "effective from date": next_available_date, - "reason for change": "Automated Test", - } - ) - ParametersPage(self.page).click_save_button_and_accept_dialog() - - # Then a warning message appears on the following page - assert ParameterDetails(self.page).search_for_warning( - "The update has failed, your changes have not been saved" - ) - assert ParameterDetails(self.page).search_for_warning( - "Parameter value is of the wrong data type" - ) - logging.info( - "[UI ASSERTIONS COMPLETE] Warning messages displayed for incorrect parameter value type" - ) - - # WHen I go pack to rgw "Organisation Parameters" page - ParametersPage(self.page).click_back_button() - ParametersPage(self.page).click_back_button() - - # Checking Yes/No fields - Parameter 199 - chosen_parameter = Parameter(self.page, "199") - self.check_chosen_parameter_correct_in_db(chosen_parameter) - ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) - - # When I select "Yes" for my parameter - next_available_date = ParametersPage(self.page).get_next_available_date() - ParametersPage(self.page).click_add_new_parameter_value_button() - ParametersPage(self.page).complete_parameter_page_form( - { - "select value": "Yes", - "effective from date": next_available_date, - "reason for change": "Automated Test", - } - ) - ParametersPage(self.page).click_save_button_and_accept_dialog() - - # Then it will save the value as "Y" - most_recent_value = ParameterDetails( - self.page - ).get_most_recent_value_of_parameter(next_available_date) - assert ( - most_recent_value == "Y" - ), f"Parameter value not saved as 'Y' when 'Yes' selected. Got {most_recent_value}" - logging.info( - f"[UI ASSERTIONS COMPLETE] Parameter value saved as 'Y' when 'Yes' selected:\n Expected Value: Y\n Actual Value: {most_recent_value}" - ) - - # When I select "No" for my parameter - next_available_date = ParametersPage(self.page).get_next_available_date() - ParametersPage(self.page).click_add_new_parameter_value_button() - ParametersPage(self.page).complete_parameter_page_form( - { - "select value": "No", - "effective from date": next_available_date, - "reason for change": "Automated Test", - } - ) - ParametersPage(self.page).click_save_button_and_accept_dialog() - - # Then it will save the value as "N" - most_recent_value = ParameterDetails( - self.page - ).get_most_recent_value_of_parameter(next_available_date) - assert ( - most_recent_value == "N" - ), f"Parameter value not saved as 'N' when 'No' selected. Got {most_recent_value}" - logging.info( - f"[UI ASSERTIONS COMPLETE] Parameter value saved as 'N' when 'No' selected:\n Expected Value: N\n Actual Value: {most_recent_value}" - ) - - # When I select "Yes" for my parameter but do not enter a reason - next_available_date = ParametersPage(self.page).get_next_available_date() - ParametersPage(self.page).click_add_new_parameter_value_button() - ParametersPage(self.page).complete_parameter_page_form( - { - "select value": "Yes", - "effective from date": next_available_date, - "reason for change": "", - } - ) - ParametersPage(self.page).click_save_button_and_accept_dialog() - # Then I get a confirmation prompt that contains "The fields 'Parameter Value', 'Effective From Date' and 'Reason For Change or Addition' are mandatory." - ParametersPage(self.page).assert_dialog_text( - "The fields 'Parameter Value', 'Effective From Date' and 'Reason For Change or Addition' are mandatory.", - True, - ) - - # When I go back to the "Organisation Parameters" page - ParametersPage(self.page).click_back_button() - ParametersPage(self.page).click_back_button() - - # Checking string fields - Parameter 182 - chosen_parameter = Parameter(self.page, "182") - self.check_chosen_parameter_correct_in_db(chosen_parameter) - ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) - - # When I add a new parameter value for my parameter that is valid - next_available_date = ParametersPage(self.page).get_next_available_date() - ParametersPage(self.page).click_add_new_parameter_value_button() - ParametersPage(self.page).complete_parameter_page_form( - { - "input value": "Valid String Test", - "effective from date": next_available_date, - "reason for change": "Automated Test", - } - ) - ParametersPage(self.page).click_save_button_and_accept_dialog() - - # Then the parameter is saved with the correct value - most_recent_value = ParameterDetails( - self.page - ).get_most_recent_value_of_parameter(next_available_date) - assert ( - most_recent_value == "Valid String Test" - ), f"Parameter value does not match the expect value. Expected 'Valid String Test', got {most_recent_value}" - logging.info( - f"[UI ASSERTIONS COMPLETE] Parameter value matches expected value:\n Expected Value: 'Valid String Test'\n Actual Value: {most_recent_value}" - ) - - # When I navigate to the Screening Centre Parameters - ParametersPage(self.page).click_main_menu_link() - BasePage(self.page).go_to_organisations_page() - OrganisationsPage(self.page).go_to_screening_centre_parameters_page() - ParametersPage(self.page).select_screening_centre_parameters_organisation( - "BCS001" - ) - - # Checking time fields - Parameter 28 - chosen_parameter = Parameter(self.page, "28") - self.check_chosen_parameter_correct_in_db(chosen_parameter) - ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) - - # When I add a value for my parameter that is lower than the allowed minimum - next_available_date = ParametersPage(self.page).get_next_available_date() - ParametersPage(self.page).click_add_new_parameter_value_button() - ParametersPage(self.page).complete_parameter_page_form( - { - "input value": chosen_parameter.get_add_to_time_value( - chosen_parameter.lower_value, -1 - ), - "effective from date": next_available_date, - "reason for change": "Automated Test", - } - ) - ParametersPage(self.page).click_save_button_and_accept_dialog() - - # Then a warning message appears on the following page - assert ParameterDetails(self.page).search_for_warning( - "The update has failed, your changes have not been saved" - ) - assert ParameterDetails(self.page).search_for_warning( - "Parameter value is not within the defined range of values" - ) - logging.info( - "[UI ASSERTIONS COMPLETE] Warning messages displayed for out-of-bounds time parameter value" - ) - - # When I add a value for my parameter that is higher than the allowed maximum - ParametersPage(self.page).click_back_button() - ParametersPage(self.page).click_add_new_parameter_value_button() - ParametersPage(self.page).complete_parameter_page_form( - { - "input value": chosen_parameter.get_add_to_time_value( - chosen_parameter.upper_value, 1 - ), - "effective from date": next_available_date, - "reason for change": "Automated Test", - } - ) - ParametersPage(self.page).click_save_button_and_accept_dialog() - - # Then a warning message appears on the following page - assert ParameterDetails(self.page).search_for_warning( - "The update has failed, your changes have not been saved" - ) - assert ParameterDetails(self.page).search_for_warning( - "Parameter value is not within the defined range of values" - ) - logging.info( - "[UI ASSERTIONS COMPLETE] Warning messages displayed for out-of-bounds time parameter value" - ) - - # When I add a valid value for my parameter - ParametersPage(self.page).click_back_button() - ParametersPage(self.page).click_add_new_parameter_value_button() - ParametersPage(self.page).complete_parameter_page_form( - { - "input value": "10:00", - "effective from date": next_available_date, - "reason for change": "Automated Test", - } - ) - ParametersPage(self.page).click_save_button_and_accept_dialog() - - # Then the parameter is saved with the correct value - most_recent_value = ParameterDetails( - self.page - ).get_most_recent_value_of_parameter(next_available_date) - assert ( - most_recent_value == "10:00" - ), f"Parameter value does not match the expect value. Expected '10:00', got {most_recent_value}" - logging.info( - f"[UI ASSERTIONS COMPLETE] Parameter value matches expected value:\n Expected Value: '10:00'\n Actual Value: {most_recent_value}" - ) - - # Finally, I log out - LogoutPage(self.page).log_out() - - @pytest.mark.wip - def test_parameter_212(self) -> None: - """ """ - # Given I log in to BCSS "England" as user role "Screening Centre Manager" - UserTools.user_login(self.page, "Screening Centre Manager at BCS001") - - # When I navigate to the Organisation Parameters page - BasePage(self.page).go_to_organisations_page() - OrganisationsPage(self.page).go_to_organisation_parameters_page() - - # Then I can see that Parameter ID "212" has the correct value from the database - chosen_parameter = Parameter(self.page, "212") - self.check_chosen_parameter_correct_in_db(chosen_parameter) - - # I am able to view Parameter ID "212" as a Screening Centre Manager - ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) - - # I "cannot" add a new parameter value - assert ParametersPage( - self.page - ).add_new_parameter_value_button.is_hidden(), "Add New Parameter Value button is visible, but should be hidden for Screening Centre Manager" - logging.info( - "[UI ASSERTIONS COMPLETE] 'Add New Parameter Value' button is hidden for Screening Centre Manager" - ) - - # When I switch users to BCSS "England" as user role "Hub Manager" - LogoutPage(self.page).log_out(close_page=False) - BasePage(self.page).go_to_log_in_page() - UserTools.user_login(self.page, "Hub Manager at BCS01") - - # Then i go to screening centre parameters page - BasePage(self.page).go_to_organisations_page() - OrganisationsPage(self.page).go_to_screening_centre_parameters_page() - ParametersPage(self.page).select_screening_centre_parameters_organisation("BCS001") - # I am able to view Parameter ID "212" as a hub manager - ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) - # I "cannot" add a new parameter value - assert ParametersPage( - self.page - ).add_new_parameter_value_button.is_hidden(), "Add New Parameter Value button is visible, but should be hidden for Hub Manager" - logging.info( - "[UI ASSERTIONS COMPLETE] 'Add New Parameter Value' button is hidden for Hub Manager" - ) - # When I switch users to BCSS "England" as user role "BCSS Support - SC" - LogoutPage(self.page).log_out(close_page=False) - BasePage(self.page).go_to_log_in_page() - UserTools.user_login(self.page, "BCSS Support - SC at BCS001") - - # Then i go to organisation parameters page - BasePage(self.page).go_to_organisations_page() - OrganisationsPage(self.page).go_to_organisation_parameters_page() - - # I am able to view Parameter ID "212" as a support user - ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) - - # I "can" add a new parameter value - assert ParametersPage( - self.page - ).add_new_parameter_value_button.is_visible(), "Add New Parameter Value button is hidden, but should be visible for BCSS Support - SC" - logging.info( - "[UI ASSERTIONS COMPLETE] 'Add New Parameter Value' button is visible for BCSS Support - SC" - ) - - # When I switch users to BCSS "England" as user role "BCSS Support - HUB" - LogoutPage(self.page).log_out(close_page=False) - BasePage(self.page).go_to_log_in_page() - UserTools.user_login(self.page, "BCSS Support - HUB") - OrganisationSwitchPage(self.page).select_organisation_by_id("BCS01") - OrganisationSwitchPage(self.page).click_continue() - - # Then i go to organisation parameters page - BasePage(self.page).go_to_organisations_page() - OrganisationsPage(self.page).go_to_screening_centre_parameters_page() - ParametersPage(self.page).select_screening_centre_parameters_organisation("BCS001") - - # I am able to view Parameter ID "212" as a support user - ParametersPage(self.page).click_parameter_id_link(chosen_parameter.param_id) - - # I "can" add a new parameter value - assert ParametersPage( - self.page - ).add_new_parameter_value_button.is_visible(), "Add New Parameter Value button is hidden, but should be visible for BCSS Support - HUB" - logging.info( - "[UI ASSERTIONS COMPLETE] 'Add New Parameter Value' button is visible for BCSS Support - HUB" - ) - - LogoutPage(self.page).log_out() - - def check_chosen_parameter_correct_in_db( - self, chosen_parameter: "Parameter" - ) -> None: - """ - Checks that the chosen parameter's current value matches the expected value from the database. - Args: - chosen_parameter (Parameter): The parameter to check. - """ - ParametersPage(self.page).assert_parameter_value_matches_expected( - chosen_parameter.param_id, chosen_parameter.current_value_db - ) +@pytest.mark.regression +def test_parameter_pages(page: Page) -> None: + """ + Test various interactions on the Organisation Parameters page, including adding new parameter values + and validating their correctness against database values. + 1. Log in as "Hub Manager" + 2. Navigate to Organisation Parameters page + 3. Validate integer parameter (ID 25) + 4. Validate Yes/No parameter (ID 199) + 5. Validate string parameter (ID 182) + 6. Navigate to Screening Centre Parameters page + 7. Validate time parameter (ID 28) + 8. Log out + """ + + # Given I log in to BCSS "England" as user role "Hub Manager" + UserTools.user_login(page, "Hub Manager at BCS01") + + # When I navigate to the Organisation Parameters + BasePage(page).go_to_organisations_page() + OrganisationsPage(page).go_to_organisation_parameters_page() + + # Checking integer fields - Parameter 25 + chosen_parameter = Parameter(page, "25") + ParametersPage(page).assert_parameter_value_matches_expected( + chosen_parameter.param_id, chosen_parameter.current_value_db + ) + ParametersPage(page).click_parameter_id_link(chosen_parameter.param_id) + + # When I add a new parameter value that is lower than the allowed minimum + next_available_date = ParametersPage(page).get_next_available_date() + ParametersPage(page).click_add_new_parameter_value_button() + ParametersPage(page).complete_parameter_page_form( + { + "input value": str(int(chosen_parameter.lower_value) - 1), + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + + # Then I get a confirmation prompt that contains "Value must not be less than Lower Value." + ParametersPage(page).assert_dialog_text( + "Value must not be less than Lower Value.", True + ) + ParametersPage(page).click_save_button() + + # When I add a new parameter value that is higher than the allowed maximum + ParametersPage(page).complete_parameter_page_form( + { + "input value": str(int(chosen_parameter.upper_value) + 1), + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + + # Then I get a confirmation prompt that contains "Value must not exceed Upper Value." + ParametersPage(page).assert_dialog_text("Value must not exceed Upper Value.", True) + ParametersPage(page).click_save_button() + + # When I add a new parameter value for my parameter that is empty + ParametersPage(page).click_back_button() + next_available_date = ParametersPage(page).get_next_available_date() + ParametersPage(page).click_add_new_parameter_value_button() + ParametersPage(page).complete_parameter_page_form( + { + "input value": "", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(page).click_save_button_and_accept_dialog() + + # Then it will default to the national value + most_recent_value = ParameterDetails(page).get_most_recent_value_of_parameter( + next_available_date + ) + assert ( + chosen_parameter.national_parameter_value + ) == most_recent_value, f"Parameter value does not match national value when empty value entered. Expected {chosen_parameter.national_parameter_value}, got {most_recent_value}" + logging.info( + f"[UI ASSERTIONS COMPLETE] Parameter value matches national value when empty value entered:\n Expected Value: {chosen_parameter.national_parameter_value}\n Actual Value: {most_recent_value}" + ) + + # When I add a new parameter value for my parameter that is valid + next_available_date = ParametersPage(page).get_next_available_date() + ParametersPage(page).click_add_new_parameter_value_button() + ParametersPage(page).complete_parameter_page_form( + { + "input value": "15", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(page).click_save_button_and_accept_dialog() + + # Then the parameter is saved with the correct value + most_recent_value = ParameterDetails(page).get_most_recent_value_of_parameter( + next_available_date + ) + assert ( + most_recent_value == "15" + ), f"Parameter value does not match the expect value. Expected {chosen_parameter.national_parameter_value}, got {most_recent_value}" + logging.info( + f"[UI ASSERTIONS COMPLETE] Parameter value matches expected value:\n Expected Value: 15\n Actual Value: {most_recent_value}" + ) + + # When I add a new parameter value for my parameter that is of an incorrect type + next_available_date = ParametersPage(page).get_next_available_date() + ParametersPage(page).click_add_new_parameter_value_button() + ParametersPage(page).complete_parameter_page_form( + { + "input value": "test incorrect type", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(page).click_save_button_and_accept_dialog() + + # Then a warning message appears on the following page + assert ParameterDetails(page).search_for_warning( + "The update has failed, your changes have not been saved" + ) + assert ParameterDetails(page).search_for_warning( + "Parameter value is of the wrong data type" + ) + logging.info( + "[UI ASSERTIONS COMPLETE] Warning messages displayed for incorrect parameter value type" + ) + + # When I go pack to rgw "Organisation Parameters" page + ParametersPage(page).click_back_button() + ParametersPage(page).click_back_button() + + # Checking Yes/No fields - Parameter 199 + chosen_parameter = Parameter(page, "199") + ParametersPage(page).assert_parameter_value_matches_expected( + chosen_parameter.param_id, chosen_parameter.current_value_db + ) + ParametersPage(page).click_parameter_id_link(chosen_parameter.param_id) + + # When I select "Yes" for my parameter + next_available_date = ParametersPage(page).get_next_available_date() + ParametersPage(page).click_add_new_parameter_value_button() + ParametersPage(page).complete_parameter_page_form( + { + "select value": "Yes", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(page).click_save_button_and_accept_dialog() + + # Then it will save the value as "Y" + most_recent_value = ParameterDetails(page).get_most_recent_value_of_parameter( + next_available_date + ) + assert ( + most_recent_value == "Y" + ), f"Parameter value not saved as 'Y' when 'Yes' selected. Got {most_recent_value}" + logging.info( + f"[UI ASSERTIONS COMPLETE] Parameter value saved as 'Y' when 'Yes' selected:\n Expected Value: Y\n Actual Value: {most_recent_value}" + ) + + # When I select "No" for my parameter + next_available_date = ParametersPage(page).get_next_available_date() + ParametersPage(page).click_add_new_parameter_value_button() + ParametersPage(page).complete_parameter_page_form( + { + "select value": "No", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(page).click_save_button_and_accept_dialog() + + # Then it will save the value as "N" + most_recent_value = ParameterDetails(page).get_most_recent_value_of_parameter( + next_available_date + ) + assert ( + most_recent_value == "N" + ), f"Parameter value not saved as 'N' when 'No' selected. Got {most_recent_value}" + logging.info( + f"[UI ASSERTIONS COMPLETE] Parameter value saved as 'N' when 'No' selected:\n Expected Value: N\n Actual Value: {most_recent_value}" + ) + + # When I select "Yes" for my parameter but do not enter a reason + next_available_date = ParametersPage(page).get_next_available_date() + ParametersPage(page).click_add_new_parameter_value_button() + ParametersPage(page).complete_parameter_page_form( + { + "select value": "Yes", + "effective from date": next_available_date, + "reason for change": "", + } + ) + ParametersPage(page).click_save_button_and_accept_dialog() + + # Then I get a confirmation prompt that contains "The fields 'Parameter Value', 'Effective From Date' and 'Reason For Change or Addition' are mandatory." + ParametersPage(page).assert_dialog_text( + "The fields 'Parameter Value', 'Effective From Date' and 'Reason For Change or Addition' are mandatory.", + True, + ) + + # When I go back to the "Organisation Parameters" page + ParametersPage(page).click_back_button() + ParametersPage(page).click_back_button() + + # Checking string fields - Parameter 182 + chosen_parameter = Parameter(page, "182") + ParametersPage(page).assert_parameter_value_matches_expected( + chosen_parameter.param_id, chosen_parameter.current_value_db + ) + ParametersPage(page).click_parameter_id_link(chosen_parameter.param_id) + + # When I add a new parameter value for my parameter that is valid + next_available_date = ParametersPage(page).get_next_available_date() + ParametersPage(page).click_add_new_parameter_value_button() + ParametersPage(page).complete_parameter_page_form( + { + "input value": "Valid String Test", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(page).click_save_button_and_accept_dialog() + + # Then the parameter is saved with the correct value + most_recent_value = ParameterDetails(page).get_most_recent_value_of_parameter( + next_available_date + ) + assert ( + most_recent_value == "Valid String Test" + ), f"Parameter value does not match the expect value. Expected 'Valid String Test', got {most_recent_value}" + logging.info( + f"[UI ASSERTIONS COMPLETE] Parameter value matches expected value:\n Expected Value: 'Valid String Test'\n Actual Value: {most_recent_value}" + ) + + # When I navigate to the Screening Centre Parameters + ParametersPage(page).click_main_menu_link() + BasePage(page).go_to_organisations_page() + OrganisationsPage(page).go_to_screening_centre_parameters_page() + ParametersPage(page).select_screening_centre_parameters_organisation("BCS001") + + # Checking time fields - Parameter 28 + chosen_parameter = Parameter(page, "28") + ParametersPage(page).assert_parameter_value_matches_expected( + chosen_parameter.param_id, chosen_parameter.current_value_db + ) + ParametersPage(page).click_parameter_id_link(chosen_parameter.param_id) + + # When I add a value for my parameter that is lower than the allowed minimum + next_available_date = ParametersPage(page).get_next_available_date() + ParametersPage(page).click_add_new_parameter_value_button() + ParametersPage(page).complete_parameter_page_form( + { + "input value": DateTimeUtils.add_time_to_time_string( + chosen_parameter.lower_value, -1 + ), + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(page).click_save_button_and_accept_dialog() + + # Then a warning message appears on the following page + assert ParameterDetails(page).search_for_warning( + "The update has failed, your changes have not been saved" + ) + assert ParameterDetails(page).search_for_warning( + "Parameter value is not within the defined range of values" + ) + logging.info( + "[UI ASSERTIONS COMPLETE] Warning messages displayed for out-of-bounds time parameter value" + ) + + # When I add a value for my parameter that is higher than the allowed maximum + ParametersPage(page).click_back_button() + ParametersPage(page).click_add_new_parameter_value_button() + ParametersPage(page).complete_parameter_page_form( + { + "input value": DateTimeUtils.add_time_to_time_string( + chosen_parameter.upper_value, 1 + ), + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(page).click_save_button_and_accept_dialog() + + # Then a warning message appears on the following page + assert ParameterDetails(page).search_for_warning( + "The update has failed, your changes have not been saved" + ) + assert ParameterDetails(page).search_for_warning( + "Parameter value is not within the defined range of values" + ) + logging.info( + "[UI ASSERTIONS COMPLETE] Warning messages displayed for out-of-bounds time parameter value" + ) + + # When I add a valid value for my parameter + ParametersPage(page).click_back_button() + ParametersPage(page).click_add_new_parameter_value_button() + ParametersPage(page).complete_parameter_page_form( + { + "input value": "10:00", + "effective from date": next_available_date, + "reason for change": "Automated Test", + } + ) + ParametersPage(page).click_save_button_and_accept_dialog() + + # Then the parameter is saved with the correct value + most_recent_value = ParameterDetails(page).get_most_recent_value_of_parameter( + next_available_date + ) + assert ( + most_recent_value == "10:00" + ), f"Parameter value does not match the expect value. Expected '10:00', got {most_recent_value}" + logging.info( + f"[UI ASSERTIONS COMPLETE] Parameter value matches expected value:\n Expected Value: '10:00'\n Actual Value: {most_recent_value}" + ) + + # Finally, I log out + LogoutPage(page).log_out() + + +@pytest.mark.parameter_212 +def test_parameter_212(page: Page) -> None: + """ + Test viewing and adding new values for Parameter ID "212" across different user roles. + 1. Log in as "Screening Centre Manager" and verify inability to add new parameter value. + 2. Switch to "Hub Manager" and verify inability to add new parameter value. + 3. Switch to "BCSS Support - SC" and verify ability to add new parameter value. + 4. Switch to "BCSS Support - HUB" and verify ability to add new parameter value. + 5. Log out. + """ + # Given I log in to BCSS "England" as user role "Screening Centre Manager" + UserTools.user_login(page, "Screening Centre Manager at BCS001") + + # When I navigate to the Organisation Parameters page + BasePage(page).go_to_organisations_page() + OrganisationsPage(page).go_to_organisation_parameters_page() + + # Then I can see that Parameter ID "212" has the correct value from the database + chosen_parameter = Parameter(page, "212") + ParametersPage(page).assert_parameter_value_matches_expected( + chosen_parameter.param_id, chosen_parameter.current_value_db + ) + + # I am able to view Parameter ID "212" as a Screening Centre Manager + ParametersPage(page).click_parameter_id_link(chosen_parameter.param_id) + + # I "cannot" add a new parameter value + assert ParametersPage( + page + ).add_new_parameter_value_button.is_hidden(), "Add New Parameter Value button is visible, but should be hidden for Screening Centre Manager" + logging.info( + "[UI ASSERTIONS COMPLETE] 'Add New Parameter Value' button is hidden for Screening Centre Manager" + ) + + # When I switch users to BCSS "England" as user role "Hub Manager" + LogoutPage(page).log_out(close_page=False) + BasePage(page).go_to_log_in_page() + UserTools.user_login(page, "Hub Manager at BCS01") + + # Then I go to the Screening Centre Parameters page + BasePage(page).go_to_organisations_page() + OrganisationsPage(page).go_to_screening_centre_parameters_page() + ParametersPage(page).select_screening_centre_parameters_organisation("BCS001") + + # I am able to view Parameter ID "212" as a hub manager + ParametersPage(page).click_parameter_id_link(chosen_parameter.param_id) + + # I "cannot" add a new parameter value + assert ParametersPage( + page + ).add_new_parameter_value_button.is_hidden(), "Add New Parameter Value button is visible, but should be hidden for Hub Manager" + logging.info( + "[UI ASSERTIONS COMPLETE] 'Add New Parameter Value' button is hidden for Hub Manager" + ) + + # When I switch users to BCSS "England" as user role "BCSS Support - SC" + LogoutPage(page).log_out(close_page=False) + BasePage(page).go_to_log_in_page() + UserTools.user_login(page, "BCSS Support - SC at BCS001") + + # Then I go to the Organisation Parameters page + BasePage(page).go_to_organisations_page() + OrganisationsPage(page).go_to_organisation_parameters_page() + + # I am able to view Parameter ID "212" as a support user + ParametersPage(page).click_parameter_id_link(chosen_parameter.param_id) + + # I "can" add a new parameter value + assert ParametersPage( + page + ).add_new_parameter_value_button.is_visible(), "Add New Parameter Value button is hidden, but should be visible for BCSS Support - SC" + logging.info( + "[UI ASSERTIONS COMPLETE] 'Add New Parameter Value' button is visible for BCSS Support - SC" + ) + + # When I switch users to BCSS "England" as user role "BCSS Support - HUB" + LogoutPage(page).log_out(close_page=False) + BasePage(page).go_to_log_in_page() + UserTools.user_login(page, "BCSS Support - HUB") + OrganisationSwitchPage(page).select_organisation_by_id("BCS01") + OrganisationSwitchPage(page).click_continue() + + # Then i go to organisation parameters page + BasePage(page).go_to_organisations_page() + OrganisationsPage(page).go_to_screening_centre_parameters_page() + ParametersPage(page).select_screening_centre_parameters_organisation("BCS001") + + # I am able to view Parameter ID "212" as a support user + ParametersPage(page).click_parameter_id_link(chosen_parameter.param_id) + + # I "can" add a new parameter value + assert ParametersPage( + page + ).add_new_parameter_value_button.is_visible(), "Add New Parameter Value button is hidden, but should be visible for BCSS Support - HUB" + logging.info( + "[UI ASSERTIONS COMPLETE] 'Add New Parameter Value' button is visible for BCSS Support - HUB" + ) + + # Finally, I log out + LogoutPage(page).log_out() diff --git a/utils/date_time_utils.py b/utils/date_time_utils.py index 3937f335..614402e9 100644 --- a/utils/date_time_utils.py +++ b/utils/date_time_utils.py @@ -229,3 +229,40 @@ def calculate_birth_date_for_age(age: int) -> date: except ValueError: # Handles February 29 for non-leap years return today.replace(month=2, day=28, year=today.year - age) + + @staticmethod + def add_time_to_time_string(time_str: str, add_minutes: int) -> str: + """ + Adds minutes to a "hh:mm" formatted time string and returns the new time string. + Args: + time_str (str): Time in "hh:mm" format. + add_minutes (int): Minutes to add. + Returns: + str: New time in "hh:mm" format. + """ + + def to_minutes(time_str: str) -> int: + """ + Converts "hh:mm" formatted string to total minutes. + Args: + time_str (str): Time in "hh:mm" format. + Returns: + int: Total minutes. + """ + h, m = map(int, time_str.split(":")) + return h * 60 + m + + def to_hh_mm(minutes: int) -> str: + """ + Converts total minutes to "hh:mm" formatted string. + Args: + minutes (int): Total minutes. + Returns: + str: Time in "hh:mm" format. + """ + h = minutes // 60 + m = minutes % 60 + return f"{h:02d}:{m:02d}" + + total_minutes = to_minutes(time_str) + add_minutes + return to_hh_mm(total_minutes) diff --git a/utils/oracle/oracle_specific_functions/organisation_parameters.py b/utils/oracle/oracle_specific_functions/organisation_parameters.py index 4c66016c..879eca9e 100644 --- a/utils/oracle/oracle_specific_functions/organisation_parameters.py +++ b/utils/oracle/oracle_specific_functions/organisation_parameters.py @@ -106,8 +106,8 @@ def get_national_parameter_value(param_id: int) -> str: param_id (int): The ID of the national parameter to retrieve. """ query = """SELECT p.default_value - FROM parameters p - WHERE p.param_id = :param_id""" + FROM parameters p + WHERE p.param_id = :param_id""" params = {"param_id": param_id} df = OracleDB().execute_query(query, params) if not df.empty: From 5f6b0565071c8f27dbe35ddebe8b7e6ccc030d83 Mon Sep 17 00:00:00 2001 From: adrianoaru-nhs <194267545+adrianoaru-nhs@users.noreply.github.com> Date: Thu, 15 Jan 2026 10:31:11 +0000 Subject: [PATCH 5/8] Delete .env Removing unnecessary .env file Signed-off-by: adrianoaru-nhs <194267545+adrianoaru-nhs@users.noreply.github.com> --- .env | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index 04745946..00000000 --- a/.env +++ /dev/null @@ -1,3 +0,0 @@ -# DO NOT COMMIT THIS FILE - -BCSS_PASS= From 399c3d8b00898b037c9be0acb5ce28ec92b3315b Mon Sep 17 00:00:00 2001 From: Adriano Aru Date: Thu, 15 Jan 2026 12:46:42 +0000 Subject: [PATCH 6/8] Updating docstrings and comments --- tests/regression/organisation/test_parameter_pages.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/regression/organisation/test_parameter_pages.py b/tests/regression/organisation/test_parameter_pages.py index f2d29884..6e869985 100644 --- a/tests/regression/organisation/test_parameter_pages.py +++ b/tests/regression/organisation/test_parameter_pages.py @@ -145,7 +145,7 @@ def test_parameter_pages(page: Page) -> None: "[UI ASSERTIONS COMPLETE] Warning messages displayed for incorrect parameter value type" ) - # When I go pack to rgw "Organisation Parameters" page + # When I go pack to "Organisation Parameters" page ParametersPage(page).click_back_button() ParametersPage(page).click_back_button() @@ -348,10 +348,10 @@ def test_parameter_pages(page: Page) -> None: def test_parameter_212(page: Page) -> None: """ Test viewing and adding new values for Parameter ID "212" across different user roles. - 1. Log in as "Screening Centre Manager" and verify inability to add new parameter value. - 2. Switch to "Hub Manager" and verify inability to add new parameter value. - 3. Switch to "BCSS Support - SC" and verify ability to add new parameter value. - 4. Switch to "BCSS Support - HUB" and verify ability to add new parameter value. + 1. Log in as "Screening Centre Manager" and verify inability to edit new parameter value. + 2. Switch to "Hub Manager" and verify inability to edit new parameter value. + 3. Switch to "BCSS Support - SC" and verify ability to edit new parameter value. + 4. Switch to "BCSS Support - HUB" and verify ability to edit new parameter value. 5. Log out. """ # Given I log in to BCSS "England" as user role "Screening Centre Manager" From 7720f47ade73bc04e8dadcbec19f1da44d1d8bf1 Mon Sep 17 00:00:00 2001 From: Adriano Aru Date: Thu, 15 Jan 2026 16:51:44 +0000 Subject: [PATCH 7/8] Refactoring and fixing broken test --- pages/organisations/parameters_page.py | 42 ++++++++++++++----- .../organisation/test_parameter_pages.py | 3 +- utils/oracle/oracle.py | 18 ++++---- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/pages/organisations/parameters_page.py b/pages/organisations/parameters_page.py index c41649fb..b6584b80 100644 --- a/pages/organisations/parameters_page.py +++ b/pages/organisations/parameters_page.py @@ -268,19 +268,41 @@ def __init__( def get_current_value_from_db(self) -> str: """ - Gets the current value of the parameter from the database. + Gets the current value of the parameter from the database, using the most recent effective_from date. Returns: str: The current value of the parameter from the database. """ + now = datetime.now() # First check for organisation parameters param_df = get_org_parameter_value(int(self.param_id), "23159") if not param_df.empty: - return param_df["val"].values[0] - else: - # Then check for screening centre parameters - param_df2 = get_org_parameter_value(int(self.param_id), "23162") - if not param_df2.empty: - return param_df2["val"].values[0] - else: - # Otherwise use the national default value - return get_national_parameter_value(int(self.param_id)) + # Convert effective_from to datetime + param_df["effective_from"] = param_df["effective_from"].apply( + lambda x: ( + x + if isinstance(x, datetime) + else datetime.strptime(x, "%Y-%m-%dT%H:%M") + ) + ) + # Filter for dates <= now + valid_rows = param_df[param_df["effective_from"] <= now] + if not valid_rows.empty: + # Get the row with the latest effective_from + latest_row = valid_rows.loc[valid_rows["effective_from"].idxmax()] + return str(latest_row["val"]) + # Then check for screening centre parameters + param_df2 = get_org_parameter_value(int(self.param_id), "23162") + if not param_df2.empty: + param_df2["effective_from"] = param_df2["effective_from"].apply( + lambda x: ( + x + if isinstance(x, datetime) + else datetime.strptime(x, "%Y-%m-%dT%H:%M") + ) + ) + valid_rows2 = param_df2[param_df2["effective_from"] <= now] + if not valid_rows2.empty: + latest_row2 = valid_rows2.loc[valid_rows2["effective_from"].idxmax()] + return str(latest_row2["val"]) + # Otherwise use the national default value + return get_national_parameter_value(int(self.param_id)) diff --git a/tests/regression/organisation/test_parameter_pages.py b/tests/regression/organisation/test_parameter_pages.py index 6e869985..9c082d51 100644 --- a/tests/regression/organisation/test_parameter_pages.py +++ b/tests/regression/organisation/test_parameter_pages.py @@ -16,6 +16,7 @@ from pages.logout.log_out_page import LogoutPage +@pytest.mark.vpn_required @pytest.mark.regression def test_parameter_pages(page: Page) -> None: """ @@ -212,13 +213,13 @@ def test_parameter_pages(page: Page) -> None: "reason for change": "", } ) - ParametersPage(page).click_save_button_and_accept_dialog() # Then I get a confirmation prompt that contains "The fields 'Parameter Value', 'Effective From Date' and 'Reason For Change or Addition' are mandatory." ParametersPage(page).assert_dialog_text( "The fields 'Parameter Value', 'Effective From Date' and 'Reason For Change or Addition' are mandatory.", True, ) + ParametersPage(page).click_save_button() # When I go back to the "Organisation Parameters" page ParametersPage(page).click_back_button() diff --git a/utils/oracle/oracle.py b/utils/oracle/oracle.py index 35ae26ee..96d3589a 100644 --- a/utils/oracle/oracle.py +++ b/utils/oracle/oracle.py @@ -73,12 +73,12 @@ def exec_bcss_timed_events( for subject_id in subject_ids: try: - logging.info( + logging.debug( f"[ORACLE] Attempting to execute stored procedure: 'bcss_timed_events', [{subject_id}, 'Y']" ) cursor = conn.cursor() cursor.callproc("bcss_timed_events", [subject_id, "Y"]) - logging.info("Stored procedure execution successful!") + logging.debug("Stored procedure execution successful!") except Exception as spExecutionError: logging.error( f"[ORACLE] Failed to execute stored procedure with execution error: {spExecutionError}" @@ -103,7 +103,7 @@ def get_subject_id_from_nhs_number(self, nhs_number: str) -> str: subject_id (str): The subject id for the provided nhs number """ conn = self.connect_to_db() - logging.info( + logging.debug( f"[ORACLE] Attempting to get subject_id from nhs number: {nhs_number}" ) cursor = conn.cursor() @@ -112,7 +112,7 @@ def get_subject_id_from_nhs_number(self, nhs_number: str) -> str: ) result = cursor.fetchall() subject_id = result[0][0] - logging.info(f"Able to extract subject ID: {subject_id}") + logging.debug(f"Able to extract subject ID: {subject_id}") return subject_id def populate_ui_approved_users_table( @@ -181,11 +181,11 @@ def execute_query(self, query: str, parameters: dict | None = None) -> pd.DataFr try: if parameters: params_str = pprint.pformat(parameters, indent=2) - logging.info( + logging.debug( f"[ORACLE] Executing query: {query} with parameters:\n{params_str}" ) else: - logging.info(f"[ORACLE] Executing query: {query}") + logging.debug(f"[ORACLE] Executing query: {query}") df = ( pd.read_sql(query, engine) if parameters == None @@ -221,13 +221,13 @@ def execute_stored_procedure( if conn is None: conn = self.connect_to_db() try: - logging.info(f"[ORACLE] Executing stored procedure: {procedure}") + logging.debug(f"[ORACLE] Executing stored procedure: {procedure}") cursor = conn.cursor() params = self._prepare_params(cursor, in_params, out_params) cursor.callproc(procedure, params) results = self._collect_outputs(params, out_params, in_params) conn.commit() - logging.info("[ORACLE] Stored procedure execution successful") + logging.debug("[ORACLE] Stored procedure execution successful") return results except Exception as executionError: raise RuntimeError( @@ -306,7 +306,7 @@ def update_or_insert_data_to_table( conn = self.connect_to_db() try: logging.debug("Attempting to insert/update table") - logging.info( + logging.debug( f"[ORACLE] Executing query: {statement} with parameters:\n{pprint.pformat(params, indent=2)}" ) cursor = conn.cursor() From eb7b1a5c30aed8b2fe4ea435276f80ac74b293fe Mon Sep 17 00:00:00 2001 From: Adriano Aru Date: Thu, 15 Jan 2026 16:56:50 +0000 Subject: [PATCH 8/8] Fixing SonarQube issue --- pages/organisations/parameters_page.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pages/organisations/parameters_page.py b/pages/organisations/parameters_page.py index b6584b80..b60d6347 100644 --- a/pages/organisations/parameters_page.py +++ b/pages/organisations/parameters_page.py @@ -10,6 +10,8 @@ from datetime import datetime, timedelta from utils.calendar_picker import CalendarPicker +displayrs_str = "#displayRS" + class ParametersPage(BasePage): """Parameters Page locators, and methods for interacting with the page.""" @@ -17,8 +19,8 @@ class ParametersPage(BasePage): def __init__(self, page: Page): super().__init__(page) - self.parameters_table = TableUtils(self.page, "#displayRS") - self.parameters_output_table = TableUtils(self.page, "#displayRS") + self.parameters_table = TableUtils(self.page, displayrs_str) + self.parameters_output_table = TableUtils(self.page, displayrs_str) self.add_new_parameter_value_button = self.page.get_by_role( "button", name="Add new parameter value" ) @@ -195,7 +197,7 @@ class ParameterDetails(BasePage): def __init__(self, page: Page): super().__init__(page) - self.parameters_table = TableUtils(self.page, "#displayRS") + self.parameters_table = TableUtils(self.page, displayrs_str) self.warning_messages = self.page.locator("th.warningHeader") def get_most_recent_parameter_date(self) -> Optional[datetime]: