Skip to content

Commit 1c9949d

Browse files
adrianoaru-nhsAndyg79
authored andcommitted
Feature/bcss 20021 creating a new calendar picker util (#17)
<!-- markdownlint-disable-next-line first-line-heading --> ## Description <!-- Describe your changes in detail. --> This PR is adding a new util to be able to use the calendar clickers on BCSS. There are two main methods that cover both the v1 and v2 calendar clicker along with functions to do specific tasks within each method. ## Context <!-- Why is this change required? What problem does it solve? --> This was added so that we could have a general utility to call whenever we need to use a calendar clicker during our automated tests. ## Type of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply. --> - [x] Refactoring (non-breaking change) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would change existing functionality) - [ ] Bug fix (non-breaking change which fixes an issue) ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [x] I am familiar with the [contributing guidelines](https://github.com/nhs-england-tools/playwright-python-blueprint/blob/main/CONTRIBUTING.md) - [x] I have followed the code style of the project - [x] I have added tests to cover my changes (where appropriate) - [x] I have updated the documentation accordingly - [ ] This PR is a result of pair or mob programming --- ## Sensitive Information Declaration To ensure the utmost confidentiality and protect your and others privacy, we kindly ask you to NOT including [PII (Personal Identifiable Information) / PID (Personal Identifiable Data)](https://digital.nhs.uk/data-and-information/keeping-data-safe-and-benefitting-the-public) or any other sensitive data in this PR (Pull Request) and the codebase changes. We will remove any PR that do contain any sensitive information. We really appreciate your cooperation in this matter. - [x] I confirm that neither PII/PID nor sensitive data are included in this PR and the codebase changes.
1 parent ea3d768 commit 1c9949d

File tree

9 files changed

+555
-50
lines changed

9 files changed

+555
-50
lines changed

pages/base_page.py

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from playwright.sync_api import Page, expect
1+
from playwright.sync_api import Page, expect, Locator
22
import logging
33

44

@@ -19,21 +19,35 @@ def __init__(self, page: Page):
1919
# Main menu - page links
2020
self.contacts_list_page = self.page.get_by_role("link", name="Contacts List")
2121
self.bowel_scope_page = self.page.get_by_role("link", name="Bowel Scope")
22-
self.call_and_recall_page = self.page.get_by_role("link", name="Call and Recall")
23-
self.communications_production_page = self.page.get_by_role("link", name="Communications Production")
22+
self.call_and_recall_page = self.page.get_by_role(
23+
"link", name="Call and Recall"
24+
)
25+
self.communications_production_page = self.page.get_by_role(
26+
"link", name="Communications Production"
27+
)
2428
self.download_page = self.page.get_by_role("link", name="Download")
2529
self.fit_test_kits_page = self.page.get_by_role("link", name="FIT Test Kits")
26-
self.gfobt_test_kits_page = self.page.get_by_role("link", name="gFOBT Test Kits")
27-
self.lynch_surveillance_page = self.page.get_by_role("link", name="Lynch Surveillance")
30+
self.gfobt_test_kits_page = self.page.get_by_role(
31+
"link", name="gFOBT Test Kits"
32+
)
33+
self.lynch_surveillance_page = self.page.get_by_role(
34+
"link", name="Lynch Surveillance"
35+
)
2836
self.organisations_page = self.page.get_by_role("link", name="Organisations")
2937
self.reports_page = self.page.get_by_role("link", name="Reports")
30-
self.screening_practitioner_appointments_page = self.page.get_by_role("link", name="Screening Practitioner")
31-
self.screening_subject_search_page = self.page.get_by_role("link", name="Screening Subject Search")
38+
self.screening_practitioner_appointments_page = self.page.get_by_role(
39+
"link", name="Screening Practitioner"
40+
)
41+
self.screening_subject_search_page = self.page.get_by_role(
42+
"link", name="Screening Subject Search"
43+
)
3244
# Bowel Cancer Screening System header
3345
self.bowel_cancer_screening_system_header = self.page.locator("#ntshAppTitle")
3446
# Bowel Cancer Screening Page header
3547
self.bowel_cancer_screening_page_title = self.page.locator("#page-title")
36-
self.bowel_cancer_screening_ntsh_page_title = self.page.locator("#ntshPageTitle")
48+
self.bowel_cancer_screening_ntsh_page_title = self.page.locator(
49+
"#ntshPageTitle"
50+
)
3751
self.main_menu__header = self.page.locator("#ntshPageTitle")
3852

3953
def click_main_menu_link(self) -> None:
@@ -70,24 +84,26 @@ def click_help_link(self) -> None:
7084
self.click(self.help_link)
7185

7286
def bowel_cancer_screening_system_header_is_displayed(self) -> None:
73-
expect(self.bowel_cancer_screening_system_header).to_contain_text("Bowel Cancer Screening System")
87+
expect(self.bowel_cancer_screening_system_header).to_contain_text(
88+
"Bowel Cancer Screening System"
89+
)
7490

7591
def main_menu_header_is_displayed(self) -> None:
7692
expect(self.main_menu__header).to_contain_text("Main Menu")
7793

7894
def bowel_cancer_screening_page_title_contains_text(self, text: str) -> None:
7995
"""Asserts that the page title contains the specified text.
8096
81-
Args:
82-
text (str): The expected text that you want to assert for the page title ("#page-title") element.
97+
Args:
98+
text (str): The expected text that you want to assert for the page title ("#page-title") element.
8399
"""
84100
expect(self.bowel_cancer_screening_page_title).to_contain_text(text)
85101

86102
def bowel_cancer_screening_ntsh_page_title_contains_text(self, text: str) -> None:
87103
"""Asserts that the page title contains the specified text.
88104
89-
Args:
90-
text (str): The expected text that you want to assert for the page title ("#ntshPageTitle") element.
105+
Args:
106+
text (str): The expected text that you want to assert for the page title ("#ntshPageTitle") element.
91107
"""
92108
expect(self.bowel_cancer_screening_ntsh_page_title).to_contain_text(text)
93109

@@ -127,20 +143,22 @@ def go_to_screening_practitioner_appointments_page(self) -> None:
127143
def go_to_screening_subject_search_page(self) -> None:
128144
self.click(self.screening_subject_search_page)
129145

130-
def click(self, locator) -> None:
146+
def click(self, locator: Locator) -> None:
131147
"""
132148
This is used to click on a locator
133149
The reason for this being used over the normal playwright click method is due to:
134150
- BCSS sometimes takes a while to render and so the normal click function 'clicks' on a locator before its available
135151
- Increases the reliability of clicks to avoid issues with the normal click method
136152
"""
137153
try:
138-
self.page.wait_for_load_state('load')
139-
self.page.wait_for_load_state('domcontentloaded')
154+
self.page.wait_for_load_state("load")
155+
self.page.wait_for_load_state("domcontentloaded")
140156
locator.wait_for(state="attached")
141157
locator.wait_for(state="visible")
142158
locator.click()
143159

144160
except Exception as locatorClickError:
145-
logging.warning(f"Failed to click element with error: {locatorClickError}, trying again...")
161+
logging.warning(
162+
f"Failed to click element with error: {locatorClickError}, trying again..."
163+
)
146164
locator.click()

pages/communication_production/batch_list_page.py

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

46

57
class BatchList(BasePage):
@@ -20,6 +22,11 @@ def __init__(self, page: Page):
2022
'text="Batch Successfully Archived and Printed"'
2123
)
2224
self.batch_list_page_title = self.page.locator("#page-title")
25+
self.deadline_calendar_picker = self.page.locator("i")
26+
self.deadline_date_filter = self.page.get_by_role("cell", name="").get_by_role(
27+
"textbox"
28+
)
29+
self.deadline_date_clear_button = self.page.get_by_role("cell", name="Clear")
2330

2431
def verify_batch_list_page_title(self, text) -> None:
2532
expect(self.batch_list_page_title).to_contain_text(text)
@@ -59,6 +66,14 @@ def enter_count_filter(self, search_text: str) -> None:
5966
self.count_filter.fill(search_text)
6067
self.count_filter.press("Enter")
6168

69+
def enter_deadline_date_filter(self, date: datetime) -> None:
70+
self.click(self.deadline_calendar_picker)
71+
CalendarPicker(self.page).v2_calendar_picker(date)
72+
73+
def clear_deadline_filter_date(self) -> None:
74+
self.click(self.deadline_calendar_picker)
75+
self.click(self.deadline_date_clear_button)
76+
6277

6378
class ActiveBatchList(BatchList):
6479
def __init__(self, page):

pages/fit_test_kits/log_devices_page.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from playwright.sync_api import Page, expect
22
from pages.base_page import BasePage
33
from enum import Enum
4+
from datetime import datetime
5+
from utils.calendar_picker import CalendarPicker
46

57

68
class LogDevices(BasePage):
@@ -41,9 +43,8 @@ def click_save_and_log_device_button(self) -> None:
4143
def click_device_spoilt_button(self) -> None:
4244
self.click(self.device_spoilt_button)
4345

44-
def fill_sample_date_field(self, value) -> None:
45-
self.sample_date_field.fill(value)
46-
self.sample_date_field.press("Enter")
46+
def fill_sample_date_field(self, date: datetime) -> None:
47+
CalendarPicker(self.page).calendar_picker_ddmonyy(date, self.sample_date_field)
4748

4849
def verify_successfully_logged_device_text(self) -> None:
4950
expect(self.successfully_logged_device_text).to_be_visible()

pages/screening_subject_search/subject_screening_search_page.py

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from playwright.sync_api import Page
22
from pages.base_page import BasePage
33
from enum import Enum
4+
from utils.calendar_picker import CalendarPicker
5+
46

57
class SubjectScreeningPage(BasePage):
68
def __init__(self, page: Page):
@@ -15,90 +17,113 @@ def __init__(self, page: Page):
1517
self.soundex_filter = self.page.get_by_role("checkbox", name="Use soundex")
1618
self.forename_filter = self.page.get_by_role("textbox", name="Forename")
1719
self.date_of_birth_filter = self.page.locator("#A_C_DOB_From")
18-
self.data_of_birth_range_filter = self.page.get_by_role("textbox", name="(for a date range, enter a to")
20+
self.data_of_birth_range_filter = self.page.get_by_role(
21+
"textbox", name="(for a date range, enter a to"
22+
)
1923
self.postcode_filter = self.page.get_by_role("textbox", name="Postcode")
20-
self.episode_closed_date_filter = self.page.get_by_role("textbox", name="Episode Closed Date")
21-
self.kit_batch_number_filter = self.page.get_by_role("textbox", name="Kit Batch Number")
24+
self.episode_closed_date_filter = self.page.get_by_role(
25+
"textbox", name="Episode Closed Date"
26+
)
27+
self.kit_batch_number_filter = self.page.get_by_role(
28+
"textbox", name="Kit Batch Number"
29+
)
2230
self.kit_number_filter = self.page.get_by_role("textbox", name="Kit Number")
23-
self.fit_device_id_filter = self.page.get_by_role("textbox", name="FIT Device ID")
24-
self.laboratory_name_filter = self.page.get_by_role("textbox", name="Laboratory Name")
25-
self.laboratory_test_date_filter = self.page.get_by_role("textbox", name="Laboratory Test Date")
26-
self.diagnostic_test_actual_date_filter = self.page.get_by_role("textbox", name="Diagnostic Test Actual Date")
31+
self.fit_device_id_filter = self.page.get_by_role(
32+
"textbox", name="FIT Device ID"
33+
)
34+
self.laboratory_name_filter = self.page.get_by_role(
35+
"textbox", name="Laboratory Name"
36+
)
37+
self.laboratory_test_date_filter = self.page.get_by_role(
38+
"textbox", name="Laboratory Test Date"
39+
)
40+
self.diagnostic_test_actual_date_filter = self.page.get_by_role(
41+
"textbox", name="Diagnostic Test Actual Date"
42+
)
2743
self.search_button = self.page.get_by_role("button", name="Search")
28-
self.clear_filters_button = self.page.get_by_role("button", name="Clear Filters")
44+
self.clear_filters_button = self.page.get_by_role(
45+
"button", name="Clear Filters"
46+
)
2947
self.appropriate_code_filter = self.page.get_by_label("Appropriate Code")
3048
self.gp_practice_in_ccg_filter = self.page.get_by_label("GP Practice in CCG")
3149

3250
self.select_screening_status = self.page.locator("#A_C_ScreeningStatus")
3351
self.select_episode_status = self.page.locator("#A_C_EpisodeStatus")
3452
self.select_search_area = self.page.locator("#A_C_SEARCH_DOMAIN")
3553

54+
self.dob_calendar_picker = self.page.locator("#A_C_DOB_From_LinkOrButton")
55+
3656
def click_clear_filters_button(self) -> None:
3757
self.click(self.clear_filters_button)
3858

39-
def click_search_button(self)->None:
59+
def click_search_button(self) -> None:
4060
self.click(self.search_button)
4161

42-
def click_episodes_filter(self)->None:
62+
def click_episodes_filter(self) -> None:
4363
self.episodes_filter.check()
4464

45-
def click_demographics_filter(self)->None:
65+
def click_demographics_filter(self) -> None:
4666
self.demographics_filter.check()
4767

48-
def click_datasets_filter(self)->None:
68+
def click_datasets_filter(self) -> None:
4969
self.datasets_filter.check()
5070

51-
def click_nhs_number_filter(self)->None:
71+
def click_nhs_number_filter(self) -> None:
5272
self.click(self.nhs_number_filter)
5373

54-
def click_surname_filter(self)->None:
74+
def click_surname_filter(self) -> None:
5575
self.click(self.surname_filter)
5676

57-
def click_soundex_filter(self)->None:
77+
def click_soundex_filter(self) -> None:
5878
self.soundex_filter.check()
5979

60-
def click_forename_filter(self)->None:
80+
def click_forename_filter(self) -> None:
6181
self.click(self.forename_filter)
6282

63-
def click_date_of_birth_filter(self)->None:
83+
def click_date_of_birth_filter(self) -> None:
6484
self.click(self.date_of_birth_filter)
6585

66-
def click_date_of_birth_range_filter(self)->None:
86+
def click_date_of_birth_range_filter(self) -> None:
6787
self.click(self.data_of_birth_range_filter)
6888

69-
def click_postcode_filter(self)->None:
89+
def click_postcode_filter(self) -> None:
7090
self.click(self.postcode_filter)
7191

72-
def click_episodes_closed_date_filter(self)->None:
92+
def click_episodes_closed_date_filter(self) -> None:
7393
self.click(self.episode_closed_date_filter)
7494

75-
def click_kit_batch_number_filter(self)->None:
95+
def click_kit_batch_number_filter(self) -> None:
7696
self.click(self.kit_batch_number_filter)
7797

78-
def click_kit_number_filter(self)->None:
98+
def click_kit_number_filter(self) -> None:
7999
self.click(self.kit_number_filter)
80100

81-
def click_fit_device_id_filter(self)->None:
101+
def click_fit_device_id_filter(self) -> None:
82102
self.click(self.fit_device_id_filter)
83103

84-
def click_laboratory_name_filter(self)->None:
104+
def click_laboratory_name_filter(self) -> None:
85105
self.click(self.laboratory_name_filter)
86106

87-
def click_laboratory_test_date_filter(self)->None:
107+
def click_laboratory_test_date_filter(self) -> None:
88108
self.click(self.laboratory_test_date_filter)
89109

90-
def click_diagnostic_test_actual_date_filter(self)->None:
110+
def click_diagnostic_test_actual_date_filter(self) -> None:
91111
self.click(self.diagnostic_test_actual_date_filter)
92112

93-
def select_screening_status_options(self, option: str)->None:
113+
def select_screening_status_options(self, option: str) -> None:
94114
self.select_screening_status.select_option(option)
95115

96-
def select_episode_status_option(self, option: str)->None:
116+
def select_episode_status_option(self, option: str) -> None:
97117
self.select_episode_status.select_option(option)
98118

99-
def select_search_area_option(self, option: str)->None:
119+
def select_search_area_option(self, option: str) -> None:
100120
self.select_search_area.select_option(option)
101121

122+
def select_dob_using_calendar_picker(self, date) -> None:
123+
self.click(self.dob_calendar_picker)
124+
CalendarPicker(self.page).v1_calender_picker(date)
125+
126+
102127
class ScreeningStatusSearchOptions(Enum):
103128
CALL_STATUS = "4001"
104129
INACTIVE_STATUS = "4002"
@@ -112,11 +137,13 @@ class ScreeningStatusSearchOptions(Enum):
112137
LYNCH_SURVEILLANCE_STATUS = "306442"
113138
LYNCH_SELF_REFERRAL_STATUS = "307129"
114139

140+
115141
class LatestEpisodeStatusSearchOptions(Enum):
116142
OPEN_PAUSED_STATUS = "1"
117143
CLOSED_STATUS = "2"
118144
NO_EPISODE_STATUS = "3"
119145

146+
120147
class SearchAreaSearchOptions(Enum):
121148
SEARCH_AREA_HOME_HUB = "01"
122149
SEARCH_AREA_GP_PRACTICE = "02"

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ markers =
2525
#main: tests designed to run against the main branch
2626
#release: tests designed to run specifically against a release branch
2727
utils: test setup and support methods
28+
utils_local: test setup and support methods locally
2829
smoke: tests designed to run as part of the smokescreen regression test suite
2930
wip: tests that are currently in progress
3031
smokescreen: all compartments to be run as part of the smokescreen

tests/smokescreen/test_compartment_2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def test_compartment_2(page: Page, smokescreen_properties: dict) -> None:
4040
fit_device_id = subjectdf["fit_device_id"].iloc[subject]
4141
logging.info(f"Logging FIT Device ID: {fit_device_id}")
4242
LogDevices(page).fill_fit_device_id_field(fit_device_id)
43-
sample_date = datetime.now().strftime("%#d %b %Y")
43+
sample_date = datetime.now()
4444
logging.info("Setting sample date to today's date")
4545
LogDevices(page).fill_sample_date_field(sample_date)
4646
LogDevices(page).log_devices_title.get_by_text("Scan Device").wait_for()
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from pages.screening_subject_search.subject_screening_search_page import (
2+
SubjectScreeningPage,
3+
)
4+
from pages.communication_production.communications_production_page import (
5+
CommunicationsProduction,
6+
)
7+
from pages.communication_production.batch_list_page import ActiveBatchList
8+
import pytest
9+
from playwright.sync_api import Page
10+
from pages.base_page import BasePage
11+
from utils.user_tools import UserTools
12+
from datetime import datetime
13+
14+
15+
@pytest.mark.smoke
16+
def test_calender_picker_v1(page: Page) -> None:
17+
"""
18+
This test is used to verify that the v1 calendar picker in utils/calendar_picker.py works as intended
19+
This uses the subject screening search page in order to do so
20+
NOTE: currently there is no validation that it has selected the correct date.
21+
This should be implemented after each date is selected however the locators on BCSS make this task difficult
22+
"""
23+
UserTools.user_login(page, "Hub Manager State Registered at BCS01")
24+
BasePage(page).go_to_screening_subject_search_page()
25+
SubjectScreeningPage(page).select_dob_using_calendar_picker(datetime(2021, 12, 1))
26+
SubjectScreeningPage(page).click_clear_filters_button()
27+
SubjectScreeningPage(page).select_dob_using_calendar_picker(datetime(2020, 3, 30))
28+
SubjectScreeningPage(page).click_clear_filters_button()
29+
SubjectScreeningPage(page).select_dob_using_calendar_picker(datetime(2020, 6, 15))
30+
SubjectScreeningPage(page).click_clear_filters_button()
31+
SubjectScreeningPage(page).select_dob_using_calendar_picker(datetime.today())
32+
33+
34+
@pytest.mark.smoke
35+
def test_calender_picker_v2(page: Page) -> None:
36+
"""
37+
This test is used to verify that the v2 calendar picker in utils/calendar_picker.py works as intended
38+
This uses the active batch list page in order to do so
39+
NOTE: currently there is no validation that it has selected the correct date.
40+
This should be implemented after each date is selected however the locators on BCSS make this task difficult
41+
"""
42+
UserTools.user_login(page, "Hub Manager State Registered at BCS01")
43+
BasePage(page).go_to_communications_production_page()
44+
CommunicationsProduction(page).go_to_active_batch_list_page()
45+
ActiveBatchList(page).enter_deadline_date_filter(datetime(1961, 12, 30))
46+
ActiveBatchList(page).clear_deadline_filter_date()
47+
ActiveBatchList(page).enter_deadline_date_filter(datetime(2026, 12, 1))
48+
ActiveBatchList(page).clear_deadline_filter_date()
49+
ActiveBatchList(page).enter_deadline_date_filter(datetime(1989, 6, 15))
50+
ActiveBatchList(page).clear_deadline_filter_date()
51+
ActiveBatchList(page).enter_deadline_date_filter(datetime.today())

0 commit comments

Comments
 (0)