Skip to content

Commit ce9eb1c

Browse files
Merge branch 'main' of github.com:NHSDigital/bcss-playwright into feature/BCSS-20327-compartment-5-utils
# Conflicts: # tests/smokescreen/test_compartment_5.py
2 parents 8a9a491 + c1d41de commit ce9eb1c

File tree

10 files changed

+163
-44
lines changed

10 files changed

+163
-44
lines changed

.github/actions/run-playwright-tests/action.yml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ inputs:
88
required: true
99
type: string
1010
marker_to_use:
11-
description: "The test marker to use when running tests (e.g. smokescreen)"
11+
description: "The test marker to use when running tests (e.g. smoke)"
1212
required: true
1313
type: string
1414

@@ -29,13 +29,10 @@ runs:
2929
run: python -m playwright install --with-deps
3030
- name: Run specified tests
3131
shell: bash
32-
run: pytest -m ${{ inputs.marker_to_use }} --base-url=$URL_TO_USE --ignore=tests_utils/
33-
env:
34-
BCSS_PASS: ${{ secrets.BCSS_PASS }}
35-
ORACLE_DB: ${ ${{ secrets.ORACLE_DB }}/<REPLACE>/${{ inputs.bcss_cloud_environment }} }
36-
ORACLE_USERNAME: ${{ secrets.ORACLE_USERNAME }}
37-
ORACLE_PASS: ${{ secrets.ORACLE_PASS }}
38-
URL_TO_USE: ${ ${{ vars.CLOUD_ENV_URL }}/<REPLACE>/${{ inputs.bcss_cloud_environment }} }
32+
run: |
33+
URL_TO_USE="${URL_TO_USE_DEFAULT//<REPLACE>/${{ inputs.bcss_cloud_environment }}}"
34+
ORACLE_DB="${ORACLE_DB_DEFAULT//<REPLACE>/${{ inputs.bcss_cloud_environment }}}"
35+
pytest -m ${{ inputs.marker_to_use }} --base-url=https://$URL_TO_USE --ignore=tests_utils/
3936
- uses: actions/upload-artifact@v4
4037
if: ${{ !cancelled() }}
4138
with:

.github/workflows/execute-tests.yaml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
name: "Test Runner"
22

3+
# This workflow is triggered manually and allows the user to specify the environment and test marker to run.
4+
# It is functional, however will not work against BCSS test environments until we configure some self-hosted
5+
# GitHub runners, as the GitHub-hosted runners are based outside of the UK so get blocked by the WAF on the
6+
# environments.
7+
38
on:
49
workflow_dispatch:
510
inputs:
@@ -17,12 +22,19 @@ jobs:
1722
run-tests:
1823
name: "Run Specified Tests"
1924
runs-on: ubuntu-latest
20-
timeout-minutes: 3
25+
timeout-minutes: 10
2126
steps:
2227
- name: "Checkout code"
2328
uses: actions/checkout@v4
2429
- name: "Run Tests"
30+
id: run-tests
2531
uses: ./.github/actions/run-playwright-tests
2632
with:
2733
bcss_cloud_environment: "${{ inputs.bcss_cloud_environment }}"
2834
marker_to_use: "${{ inputs.marker_to_use }}"
35+
env:
36+
BCSS_PASS: ${{ secrets.BCSS_PASS }}
37+
ORACLE_DB_DEFAULT: ${{ secrets.ORACLE_DB }}
38+
ORACLE_USERNAME: ${{ secrets.ORACLE_USERNAME }}
39+
ORACLE_PASS: ${{ secrets.ORACLE_PASS }}
40+
URL_TO_USE_DEFAULT: ${{ vars.CLOUD_ENV_URL }}

pages/screening_practitioner_appointments/appointment_detail_page.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ def __init__(self, page: Page):
88
self.page = page
99
# Appointment Detail - page filters
1010
self.attendance_radio = self.page.get_by_role("radio", name="Attendance")
11-
self.attendented_check_box = self.page.locator("#UI_ATTENDED")
11+
self.attended_check_box = self.page.locator("#UI_ATTENDED")
1212
self.calendar_button = self.page.get_by_role("button", name="Calendar")
1313
self.save_button = self.page.get_by_role("button", name="Save")
1414

1515
def check_attendance_radio(self) -> None:
1616
self.attendance_radio.check()
1717

18-
def check_attendented_check_box(self) -> None:
19-
self.attendented_check_box.check()
18+
def check_attended_check_box(self) -> None:
19+
self.attended_check_box.check()
2020

2121
def click_calendar_button(self) -> None:
2222
self.click(self.calendar_button)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from playwright.sync_api import Page, expect
2+
from pages.base_page import BasePage
3+
from enum import Enum
4+
5+
6+
class SubjectDatasets(BasePage):
7+
def __init__(self, page: Page):
8+
super().__init__(page)
9+
self.page = page
10+
11+
self.show_dataset_button = self.page.get_by_role("link", name="Show Dataset")
12+
13+
self.save_dataset_button = self.page.locator(
14+
"#UI_DIV_BUTTON_SAVE1"
15+
).get_by_role("button", name="Save Dataset")
16+
17+
self.select_asa_grade_dropdown = self.page.get_by_label("ASA Grade")
18+
19+
self.select_fit_for_colonoscopy_dropdown = self.page.get_by_label(
20+
"Fit for Colonoscopy (SSP)"
21+
)
22+
23+
self.dataset_complete_radio_button_yes = self.page.get_by_role(
24+
"radio", name="Yes"
25+
)
26+
27+
self.dataset_complete_radio_button_no = self.page.get_by_role(
28+
"radio", name="No"
29+
)
30+
31+
def click_show_datasets(self) -> None:
32+
self.click(self.show_dataset_button)
33+
34+
def save_dataset(self) -> None:
35+
self.click(self.save_dataset_button)
36+
37+
def select_asa_grade_option(self, option: str) -> None:
38+
self.select_asa_grade_dropdown.select_option(option)
39+
40+
def select_fit_for_colonoscopy_option(self, option: str) -> None:
41+
self.select_fit_for_colonoscopy_dropdown.select_option(option)
42+
43+
def click_dataset_complete_radio_button_yes(self) -> None:
44+
self.dataset_complete_radio_button_yes.check()
45+
46+
def click_dataset_complete_radio_button_no(self) -> None:
47+
self.dataset_complete_radio_button_no.check()
48+
49+
50+
class AsaGradeOptions(Enum):
51+
FIT = "17009"
52+
53+
54+
class FitForColonoscopySspOptions(Enum):
55+
YES = "17058"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from playwright.sync_api import Page
2+
from pages.base_page import BasePage
3+
4+
5+
class AttendDiagnosticTest(BasePage):
6+
def __init__(self, page: Page):
7+
super().__init__(page)
8+
self.page = page
9+
# Advance Diagnostic Test - page locators
10+
self.actual_type_of_test_dropdown = self.page.locator("#UI_CONFIRMED_TYPE_OF_TEST")
11+
self.calendar_button = self.page.get_by_role("button", name="Calendar")
12+
self.save_button =self.page.get_by_role("button", name="Save")
13+
14+
def select_actual_type_of_test_dropdown_option(self, text: str) -> None:
15+
self.actual_type_of_test_dropdown.select_option(label=text)
16+
17+
def click_calendar_button(self) -> None:
18+
self.click(self.calendar_button)
19+
20+
def click_save_button(self) -> None:
21+
self.click(self.save_button)

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ pandas~=2.2.3
77
python-dotenv~=1.0.1
88
sqlalchemy>=2.0.38
99
jproperties~=2.1.2
10+
pypdf>=5.3.0

tests/smokescreen/test_compartment_1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def test_compartment_1(page: Page, smokescreen_properties: dict) -> None:
8888
"Skipping S1 Pre-invitation (FIT) (digital leaflet) as no self referral invitations were generated"
8989
)
9090
batch_processing(
91-
page, "S1", "Pre-invitation (FIT)", "S9 - Pre-invitation Sent", True
91+
page, "S1", "Pre-invitation (FIT)", "S9 - Pre-invitation Sent", True, True
9292
)
9393

9494
# Print the batch of Invitation & Test Kit Letters - England

tests/smokescreen/test_compartment_5.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
from pages.screening_practitioner_appointments.screening_practitioner_appointments import (
66
ScreeningPractitionerAppointmentsPage,
77
)
8+
from pages.screening_practitioner_appointments.subject_datasets import (
9+
SubjectDatasets,
10+
FitForColonoscopySspOptions,
11+
AsaGradeOptions,
12+
)
813
from pages.screening_subject_search.subject_screening_summary import (
914
SubjectScreeningSummary,
1015
)
@@ -20,6 +25,9 @@
2025
from pages.screening_practitioner_appointments.appointment_calendar_page import (
2126
AppointmentCalendar,
2227
)
28+
from pages.screening_subject_search.attend_diagnostic_test_page import (
29+
AttendDiagnosticTest,
30+
)
2331
from utils.user_tools import UserTools
2432
from utils.load_properties_file import PropertiesFile
2533
from utils.screening_subject_page_searcher import verify_subject_event_status_by_nhs_no
@@ -91,7 +99,7 @@ def test_compartment_5(page: Page, smokescreen_properties: dict) -> None:
9199
pytest.fail(f"Unable to find {name_from_util}: {e}")
92100

93101
AppointmentDetail(page).check_attendance_radio()
94-
AppointmentDetail(page).check_attendented_check_box()
102+
AppointmentDetail(page).check_attended_check_box()
95103
AppointmentDetail(page).click_calendar_button()
96104
CalendarPicker(page).v1_calender_picker(datetime.today() - timedelta(1))
97105
AppointmentDetail(page).click_save_button()
@@ -120,14 +128,14 @@ def test_compartment_5(page: Page, smokescreen_properties: dict) -> None:
120128
)
121129

122130
SubjectScreeningSummary(page).click_datasets_link()
131+
SubjectDatasets(page).click_show_datasets()
123132

124-
page.get_by_role("link", name="Show Dataset").click()
125-
page.get_by_label("ASA Grade").select_option("17009")
126-
page.get_by_label("Fit for Colonoscopy (SSP)").select_option("17058")
127-
page.get_by_role("radio", name="Yes").check()
128-
page.locator("#UI_DIV_BUTTON_SAVE1").get_by_role(
129-
"button", name="Save Dataset"
130-
).click()
133+
SubjectDatasets(page).select_asa_grade_option(AsaGradeOptions.FIT.value)
134+
SubjectDatasets(page).select_fit_for_colonoscopy_option(
135+
FitForColonoscopySspOptions.YES.value
136+
)
137+
SubjectDatasets(page).click_dataset_complete_radio_button_yes()
138+
SubjectDatasets(page).save_dataset()
131139
BasePage(page).click_back_button()
132140
BasePage(page).click_back_button()
133141

@@ -150,18 +158,16 @@ def test_compartment_5(page: Page, smokescreen_properties: dict) -> None:
150158
logging.info(f"{name_from_util} attended diagnostic test")
151159
AdvanceFOBTScreeningEpisode(page).click_attend_diagnostic_test_button()
152160

153-
page.locator("#UI_CONFIRMED_TYPE_OF_TEST").select_option(label="Colonoscopy")
154-
page.get_by_role("button", name="Calendar").click()
161+
AttendDiagnosticTest(page).select_actual_type_of_test_dropdown_option(
162+
"Colonoscopy"
163+
)
164+
AttendDiagnosticTest(page).click_calendar_button()
155165
CalendarPicker(page).v1_calender_picker(datetime.today())
156-
page.get_by_role("button", name="Save").click()
166+
AttendDiagnosticTest(page).click_save_button()
157167
SubjectScreeningSummary(page).verify_latest_event_status_value(
158168
"A259 - Attended Diagnostic Test"
159169
)
160170

161-
# verify_subject_event_status_by_nhs_no(
162-
# page, nhs_no, "A259 - Attended Diagnostic Test"
163-
# )
164-
165171
SubjectScreeningSummary(page).click_advance_fobt_screening_episode_button()
166172

167173
AdvanceFOBTScreeningEpisode(page).click_other_post_investigation_button()

utils/batch_processing.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
from utils.screening_subject_page_searcher import verify_subject_event_status_by_nhs_no
1111
from utils.oracle.oracle_specific_functions import get_nhs_no_from_batch_id
1212
from utils.oracle.oracle import OracleDB
13+
from utils.pdf_reader import extract_nhs_no_from_pdf
1314
import os
1415
import pytest
1516
from playwright.sync_api import Page
1617
import logging
18+
import pandas as pd
1719

1820

1921
def batch_processing(
@@ -22,6 +24,7 @@ def batch_processing(
2224
batch_description: str,
2325
latest_event_status: str,
2426
run_timed_events: bool = False,
27+
get_subjects_from_pdf: bool = False,
2528
) -> None:
2629
"""
2730
This util is used to process batches. It expects the following inputs:
@@ -30,6 +33,7 @@ def batch_processing(
3033
- batch_description: This is the description of the batch. E.g. Pre-invitation (FIT)
3134
- latest_event_status: This is the status the subject will get updated to after the batch has been processed.
3235
- run_timed_events: This is an optional input that executes bcss_timed_events if set to True
36+
- get_subjects_from_pdf: This is an optial input to change the method of retrieving subjects from the batch from the Db to the PDF file.
3337
"""
3438
logging.info(f"Processing {batch_type} - {batch_description} batch")
3539
BasePage(page).click_main_menu_link()
@@ -39,8 +43,9 @@ def batch_processing(
3943

4044
batch_description_cells = page.locator(f"//td[text()='{batch_description}']")
4145

42-
if batch_description_cells.count() == 0 and page.locator(
43-
"td", has_text="No matching records found"
46+
if (
47+
batch_description_cells.count() == 0
48+
and page.locator("td", has_text="No matching records found").is_visible()
4449
):
4550
pytest.fail(f"No {batch_type} {batch_description} batch found")
4651

@@ -55,24 +60,18 @@ def batch_processing(
5560
logging.info(
5661
f"Successfully found open '{batch_type} - {batch_description}' batch"
5762
)
58-
try:
59-
logging.info(
60-
f"Attempting to get NHS Numbers for batch {link_text} from the DB"
61-
)
62-
nhs_no_df = get_nhs_no_from_batch_id(link_text)
63-
logging.info(
64-
f"Successfully retrieved NHS Numbers from batch {link_text}"
65-
)
66-
except Exception as e:
67-
pytest.fail(
68-
f"Failed to retrieve NHS Numbers from batch {link_text}, {str(e)}"
69-
)
7063
link.click()
7164
break
7265
elif (i + 1) == batch_description_cells.count():
7366
pytest.fail(f"No open '{batch_type} - {batch_description}' batch found")
7467

75-
prepare_and_print_batch(page, link_text)
68+
if get_subjects_from_pdf:
69+
logging.info(f"Getting NHS Numbers for batch {link_text} from the PDF File")
70+
nhs_no_df = prepare_and_print_batch(page, link_text, get_subjects_from_pdf)
71+
else:
72+
logging.info(f"Getting NHS Numbers for batch {link_text} from the DB")
73+
prepare_and_print_batch(page, link_text, get_subjects_from_pdf)
74+
nhs_no_df = get_nhs_no_from_batch_id(link_text)
7675

7776
check_batch_in_archived_batch_list(page, link_text)
7877

@@ -83,7 +82,9 @@ def batch_processing(
8382
OracleDB().exec_bcss_timed_events(nhs_no_df)
8483

8584

86-
def prepare_and_print_batch(page: Page, link_text) -> None:
85+
def prepare_and_print_batch(
86+
page: Page, link_text: str, get_subjects_from_pdf: bool = False
87+
) -> pd.DataFrame | None:
8788
"""
8889
This method prepares the batch, retreives the files and confirms them as printed
8990
Once those buttons have been pressed it waits for the message 'Batch Successfully Archived'
@@ -108,6 +109,11 @@ def prepare_and_print_batch(page: Page, link_text) -> None:
108109
file = download_file.suggested_filename
109110
# Wait for the download process to complete and save the downloaded file in a temp folder
110111
download_file.save_as(file)
112+
nhs_no_df = (
113+
extract_nhs_no_from_pdf(file)
114+
if file.endswith(".pdf") and get_subjects_from_pdf
115+
else None
116+
)
111117
os.remove(file) # Deletes the file after extracting the necessary data
112118
except Exception as e:
113119
pytest.fail(f"No retrieve button available to click: {str(e)}")
@@ -131,6 +137,8 @@ def prepare_and_print_batch(page: Page, link_text) -> None:
131137
except Exception as e:
132138
pytest.fail(f"Batch successfully archived message is not shown: {str(e)}")
133139

140+
return nhs_no_df
141+
134142

135143
def check_batch_in_archived_batch_list(page: Page, link_text) -> None:
136144
"""

utils/pdf_reader.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from pypdf import PdfReader
2+
import pandas as pd
3+
4+
5+
def extract_nhs_no_from_pdf(file: str) -> pd.DataFrame:
6+
reader = PdfReader(file)
7+
nhs_no_df = pd.DataFrame(columns=["subject_nhs_number"])
8+
# For loop looping through all pages of the file to find the NHS Number
9+
for i, pages in enumerate(reader.pages):
10+
text = pages.extract_text()
11+
if "NHS No" in text:
12+
# If NHS number is found split the text by every new line into a list
13+
text = text.splitlines(True)
14+
for split_text in text:
15+
if "NHS No" in split_text:
16+
# If a string is found containing "NHS No" only digits are stored into nhs_no
17+
nhs_no = "".join([ele for ele in split_text if ele.isdigit()])
18+
nhs_no_df.loc[i] = [nhs_no]
19+
return nhs_no_df

0 commit comments

Comments
 (0)