diff --git a/.gitignore b/.gitignore index baa359d..18472dc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ geckodriver.log *.png *.lock -.vscode \ No newline at end of file +.vscode +*.pyc +*__pycache__ \ No newline at end of file diff --git a/civicrm_tester/base.py b/civicrm_tester/base.py index 694ed22..ddd9df0 100644 --- a/civicrm_tester/base.py +++ b/civicrm_tester/base.py @@ -28,7 +28,7 @@ class BaseTester: logging.basicConfig( level="INFO", format="%(message)s", - datefmt="[%X]", + datefmt="[%X]", # TODO: Change date format handlers=[RichHandler()] ) self.log = logging.getLogger("civiCRM-tester") @@ -145,6 +145,12 @@ class BaseTester: class SearchExportTester(BaseTester): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.search_url = self.base_url + "/civicrm/contact/search" + self.contact_selectall_id = "CIVICRM_QFID_ts_all_4" + self.contact_dropdown_id = "select2-chosen-4" + def _get_export_id(self) -> str: """Parses url to get the param used to ID what search we are currently doing""" export_page_url = self.browser.current_url @@ -153,7 +159,37 @@ class SearchExportTester(BaseTester): self.debug("got qf_key '%s' from url" % qf_key) return qf_key - def download(self, data: dict) -> requests.Response: + def _get_contact_search_number(self) -> int: + """Grabs the number of reported matches in a search on civicrm page""" + results = self.find_element_by_css_selector( + ".form-layout-compressed > tbody:nth-child(1) > tr:nth-child(2) > td:nth-child(2) > label:nth-child(2)" + ) + matches = re.findall(r"(\d+)", results.text) + # Should just be one match in normal cases + result_no = int(matches[0]) + return result_no + + def _wait_for_search_to_load(self): + """Wrapper to wait for a contact search to load + MUST BE CALLED AFTER YOU HAVE SEARCHED""" + # "alpha-filter" is element of the table + self.wait_until_visible((By.ID, "alpha-filter")) + self.debug("table of results has loaded") + + def _select_option_from_magic_dropdown(self, option_id: str): + """ + Wrapper to click an option from the dropdown menu within the search on civicrm. + Magic dropdown because it literally is not how dropdowns should work at all + All options have an ID but this can change depending on the context of how you get to the search page + MUST BE CALLED WHEN ON THE SEARCH RESULTS PAGE + """ + self.debug("exporting results using the magic dropdown") + self.find_element_by_id(self.contact_selectall_id).click() + self.find_element_by_id(self.contact_dropdown_id).click() + self.find_element_by_id(option_id).click() + self.wait_until_visible((By.CSS_SELECTOR, ".crm-block")) + + def download_export(self, data: dict) -> requests.Response: """ Download exports from searches manually using requests This is because managing downloads within Selenium is a nightmare @@ -175,10 +211,8 @@ class SearchExportTester(BaseTester): # Most of this data is replicating the export settings so that the response is the csv data data = { "qfKey": self._get_export_id(), - "entryURL": self.base_url + "/civicrm/contact/search", + "entryURL": self.search_url, **data # Add provided export data required for specific export requests } - res = session.request( - "POST", self.base_url + "/civicrm/contact/search", data=data - ) + res = session.request("POST", self.search_url, data=data) return res diff --git a/civicrm_tester/contact_export.py b/civicrm_tester/contact_export.py index a7343f6..c8f3773 100644 --- a/civicrm_tester/contact_export.py +++ b/civicrm_tester/contact_export.py @@ -1,8 +1,6 @@ import csv import io -import re -from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from .base import SearchExportTester @@ -16,14 +14,10 @@ class ContactExport(SearchExportTester): self.desc( "Testing if exporting contacts from a search returns a CSV file with all expected contacts." ) - self.search_url = self.base_url + "/civicrm/contact/search" - - self.contact_selectall_id = "CIVICRM_QFID_ts_all_4" - self.contact_dropdown_id = "select2-chosen-4" self.contact_exportoption_id = "select2-result-label-15" def download_csv(self): - # Data of the request that is specific for this export and not genertic like qr_key + # Data of the request that is specific for this export and not generic like qr_key data = { "_qf_Select_next": "Continue", "exportOption": 1, @@ -31,7 +25,7 @@ class ContactExport(SearchExportTester): "postal_greeting": 1, "addressee": 1, } - return self.download(data) + return self.download_export(data) def calculate_exported_contacts_number(self) -> int: """ @@ -41,7 +35,7 @@ class ContactExport(SearchExportTester): res = self.download_csv() csv_file = io.StringIO(res.text) - # Dict reader should remove headers + # DictReader removes header from count exported_csv = csv.DictReader(csv_file) exported_number_exports = sum(1 for row in exported_csv) self.debug( @@ -66,24 +60,9 @@ class ContactExport(SearchExportTester): search_box.send_keys(search_term) search_box.send_keys(Keys.ENTER) self.debug("searching for contacts with term '%s'" % search_term) - self.wait_until_visible( - (By.ID, "alpha-filter") - ) #wait for table to load - self.debug("table of results has loaded") - - results_text = self.find_element_by_css_selector( - ".form-layout-compressed > tbody:nth-child(1) > tr:nth-child(2) > td:nth-child(2) > label:nth-child(2)" - ).text - matches = re.findall( - r"(\d+)", results_text - ) # Should just be one match in normal cases - result_no = int(matches[0]) - - self.debug("exporting results using the magic dropdown") - self.find_element_by_id(self.contact_selectall_id).click() - self.find_element_by_id(self.contact_dropdown_id).click() - self.find_element_by_id(self.contact_exportoption_id).click() - self.wait_until_visible((By.CSS_SELECTOR, ".crm-block")) + self._wait_for_search_to_load() + result_no = self._get_contact_search_number() + self._select_option_from_magic_dropdown(self.contact_exportoption_id) exported_number_exports = self.calculate_exported_contacts_number() if exported_number_exports == (result_no): diff --git a/civicrm_tester/steeringcommittee_print_labels.py b/civicrm_tester/steeringcommittee_print_labels.py index e43f946..469eb0c 100644 --- a/civicrm_tester/steeringcommittee_print_labels.py +++ b/civicrm_tester/steeringcommittee_print_labels.py @@ -1,8 +1,6 @@ import io -import re from pdfminer.high_level import extract_text -from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from .base import SearchExportTester @@ -16,12 +14,9 @@ class SteeringCommitteePrintLabels(SearchExportTester): self.desc( "Testing the pdf labels for the SteeringCommittee show all contacts names" ) - self.search_url = self.base_url + "/civicrm/contact/search" self.group_dropdown = "s2id_autogen2" self.search_button = "_qf_Basic_refresh" self.mail_label_option = "select2-result-label-19" - self.contact_selectall_id = "CIVICRM_QFID_ts_all_4" - self.contact_dropdown_id = "select2-chosen-4" # Using this to count amount of people in the exported pdf # This will fail if someone is added that doesn't have a UK adddress self.pdf_search_string = "UNITED KINGDOM" @@ -32,24 +27,10 @@ class SteeringCommitteePrintLabels(SearchExportTester): group_dropdown.click() group_dropdown.send_keys("Steering Committee" + Keys.ENTER) self.find_element_by_id(self.search_button).click() - self.wait_until_visible( - (By.ID, "alpha-filter") - ) #wait for table to load - self.debug("table of results has loaded") - # TODO: Refactor this in base class as a helper function because we do this in another test - results_text = self.find_element_by_css_selector( - ".form-layout-compressed > tbody:nth-child(1) > tr:nth-child(2) > td:nth-child(2) > label:nth-child(2)" - ).text - matches = re.findall( - r"(\d+)", results_text - ) # Should just be one match in normal cases - result_no = int(matches[0]) + self._wait_for_search_to_load() + result_no = self._get_contact_search_number() self.debug("exporting results using the magic dropdown") - self.find_element_by_id(self.contact_selectall_id).click() - self.find_element_by_id(self.contact_dropdown_id).click() - self.find_element_by_id(self.mail_label_option).click() - - self.wait_until_visible((By.CSS_SELECTOR, ".crm-block")) + self._select_option_from_magic_dropdown(self.mail_label_option) # By omitting the field, we are effectively disabling the do not mail filter data = { "_qf_default": "Label:submit", @@ -58,7 +39,7 @@ class SteeringCommitteePrintLabels(SearchExportTester): "location_type_id": "", "_qf_Label_submit": "Make+Mailing+Labels" } - res = self.download(data) + res = self.download_export(data) pdf_text = extract_text(io.BytesIO(res.content)) label_count = pdf_text.count(self.pdf_search_string) if result_no == label_count: