feat: added print label test and some refacoring
Couldn't add the refactoring in seperate commits so history is messy
This commit is contained in:
parent
926d086537
commit
9bca740133
1
Pipfile
1
Pipfile
@ -7,6 +7,7 @@ name = "pypi"
|
|||||||
selenium = "*"
|
selenium = "*"
|
||||||
requests = "*"
|
requests = "*"
|
||||||
rich = "*"
|
rich = "*"
|
||||||
|
"pdfminer.six" = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
pylint = "*"
|
pylint = "*"
|
||||||
|
173
main.py
173
main.py
@ -1,11 +1,13 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import csv
|
import csv
|
||||||
import logging
|
import logging
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import urllib.parse as urlparse
|
import urllib.parse as urlparse
|
||||||
from urllib.parse import parse_qs
|
from urllib.parse import parse_qs
|
||||||
|
|
||||||
|
from pdfminer.high_level import extract_text
|
||||||
import requests
|
import requests
|
||||||
from rich.logging import RichHandler
|
from rich.logging import RichHandler
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
@ -28,6 +30,8 @@ class BaseTester:
|
|||||||
"""
|
"""
|
||||||
def __init__(self, user: str, passwd: str, dev: bool, show_browser: bool):
|
def __init__(self, user: str, passwd: str, dev: bool, show_browser: bool):
|
||||||
self.console = Console()
|
self.console = Console()
|
||||||
|
# Disable pdfminers logger cause its annoying as fuck and provides no value
|
||||||
|
logging.getLogger("pdfminer").setLevel(100)
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level="INFO",
|
level="INFO",
|
||||||
format="%(message)s",
|
format="%(message)s",
|
||||||
@ -147,23 +151,21 @@ class BaseTester:
|
|||||||
return self.wait.until(EC.element_to_be_clickable(locator))
|
return self.wait.until(EC.element_to_be_clickable(locator))
|
||||||
|
|
||||||
|
|
||||||
class ContactExport(BaseTester):
|
class SearchExportTester(BaseTester):
|
||||||
"""Tests if exporting contacts from a search returns a CSV file with all contacts."""
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.title("Contact Export")
|
|
||||||
self.desc(
|
|
||||||
"Testing if exporting contacts from a search returns a CSV file with all contacts."
|
|
||||||
)
|
|
||||||
self.search_url = self.base_url + "/civicrm/contact/search"
|
|
||||||
|
|
||||||
self.contact_selectall_id = "CIVICRM_QFID_ts_all_4"
|
def _get_export_id(self) -> str:
|
||||||
self.contact_dropdown_id = "select2-chosen-4"
|
"""Parses url to get the param used to ID what search we are currently doing"""
|
||||||
self.contact_exportoption_id = "select2-result-label-15"
|
export_page_url = self.browser.current_url
|
||||||
|
parsed = urlparse.urlparse(export_page_url)
|
||||||
|
qf_key = parse_qs(parsed.query)['qfKey']
|
||||||
|
self.debug("got qf_key '%s' from url" % qf_key)
|
||||||
|
return qf_key
|
||||||
|
|
||||||
def download_csv(self):
|
def download(self, data: dict) -> requests.Response:
|
||||||
"""
|
"""
|
||||||
Download export CSV manually using requests
|
Download exports from searches manually using requests
|
||||||
This is because managing downloads within Selenium is a nightmare
|
This is because managing downloads within Selenium is a nightmare
|
||||||
Function tries to replicate the download within the browser as much as possible
|
Function tries to replicate the download within the browser as much as possible
|
||||||
"""
|
"""
|
||||||
@ -180,33 +182,51 @@ class ContactExport(BaseTester):
|
|||||||
session.cookies.update(
|
session.cookies.update(
|
||||||
{session_cookie["name"]: session_cookie["value"]}
|
{session_cookie["name"]: session_cookie["value"]}
|
||||||
)
|
)
|
||||||
qf_key = self.get_export_id()
|
|
||||||
# Most of this data is replicating the export settings so that the response is the csv data
|
# Most of this data is replicating the export settings so that the response is the csv data
|
||||||
data = {
|
data = {
|
||||||
"qfKey": qf_key,
|
"qfKey": self._get_export_id(),
|
||||||
"entryURL": self.base_url + "/civicrm/contact/search",
|
"entryURL": self.base_url + "/civicrm/contact/search",
|
||||||
"_qf_Select_next": "Continue",
|
**data # Add provided export data required for specific export requests
|
||||||
"exportOption": 1,
|
|
||||||
"mergeOption": 0,
|
|
||||||
"postal_greeting": 1,
|
|
||||||
"addressee": 1,
|
|
||||||
}
|
}
|
||||||
res = session.request(
|
res = session.request(
|
||||||
"POST", self.base_url + "/civicrm/contact/search", data=data
|
"POST", self.base_url + "/civicrm/contact/search", data=data
|
||||||
)
|
)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def calculate_exported_contacts_number(self):
|
|
||||||
|
class ContactExport(SearchExportTester):
|
||||||
|
"""Tests if exporting contacts from a search returns a CSV file with all contacts."""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.title("Contact Export")
|
||||||
|
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 = {
|
||||||
|
"_qf_Select_next": "Continue",
|
||||||
|
"exportOption": 1,
|
||||||
|
"mergeOption": 0,
|
||||||
|
"postal_greeting": 1,
|
||||||
|
"addressee": 1,
|
||||||
|
}
|
||||||
|
return self.download(data)
|
||||||
|
|
||||||
|
def calculate_exported_contacts_number(self) -> int:
|
||||||
"""
|
"""
|
||||||
Downloads csv, saves it to tmp, opens it and checks if the number of contacts is equal to the reported number of contacts within the web ui
|
Downloads csv, opens it in a string buffer using StringIO and passes it to the csv lib
|
||||||
Has to be saved to disk due to the csv lib only wanting a file object, not str
|
Counts number of rows (excl. header) and returns that value
|
||||||
"""
|
"""
|
||||||
res = self.download_csv()
|
res = self.download_csv()
|
||||||
file_name = "/tmp/exportedRecords.csv"
|
csv_file = io.StringIO(res.text)
|
||||||
with open(file_name, "w") as csv_file:
|
|
||||||
self.debug("writing csv to '%s'" % file_name)
|
|
||||||
csv_file.write(res.text)
|
|
||||||
with open(file_name, "r") as csv_file:
|
|
||||||
# Dict reader should remove headers
|
# Dict reader should remove headers
|
||||||
exported_csv = csv.DictReader(csv_file)
|
exported_csv = csv.DictReader(csv_file)
|
||||||
exported_number_exports = sum(1 for row in exported_csv)
|
exported_number_exports = sum(1 for row in exported_csv)
|
||||||
@ -214,17 +234,8 @@ class ContactExport(BaseTester):
|
|||||||
"found %d rows in exported csv excluding header row" %
|
"found %d rows in exported csv excluding header row" %
|
||||||
exported_number_exports
|
exported_number_exports
|
||||||
)
|
)
|
||||||
self.debug("deleting '%s'" % file_name)
|
|
||||||
os.remove(file_name)
|
|
||||||
return exported_number_exports
|
|
||||||
|
|
||||||
def get_export_id(self) -> str:
|
return exported_number_exports
|
||||||
"""Parses url to get the param used to ID what search we are currently doing"""
|
|
||||||
export_page_url = self.browser.current_url
|
|
||||||
parsed = urlparse.urlparse(export_page_url)
|
|
||||||
qf_key = parse_qs(parsed.query)['qfKey']
|
|
||||||
self.debug("got qf_key '%s' from url" % qf_key)
|
|
||||||
return qf_key
|
|
||||||
|
|
||||||
def _test(self, search_term: str):
|
def _test(self, search_term: str):
|
||||||
"""
|
"""
|
||||||
@ -341,6 +352,79 @@ class ActivitiesTab(BaseTester):
|
|||||||
self._test_all(cid_tuple)
|
self._test_all(cid_tuple)
|
||||||
|
|
||||||
|
|
||||||
|
class SteeringCommitteePrintLabels(SearchExportTester):
|
||||||
|
"""Tests the pdf labels for the SteeringCommitee show all contacts names"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.title("Steering Committee Print Labels")
|
||||||
|
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"
|
||||||
|
|
||||||
|
def _test(self):
|
||||||
|
self.browser.get(self.search_url)
|
||||||
|
group_dropdown = self.find_element_by_id(self.group_dropdown)
|
||||||
|
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.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"))
|
||||||
|
# By omitting the field, we are effectively disabling the do not mail filter
|
||||||
|
data = {
|
||||||
|
"_qf_default": "Label:submit",
|
||||||
|
# Smallest labels, will show 24 contacts on one page
|
||||||
|
"label_name": "3475",
|
||||||
|
"location_type_id": "",
|
||||||
|
"_qf_Label_submit": "Make+Mailing+Labels"
|
||||||
|
}
|
||||||
|
res = self.download(data)
|
||||||
|
pdf_text = extract_text(io.BytesIO(res.content))
|
||||||
|
label_count = pdf_text.count(self.pdf_search_string)
|
||||||
|
if result_no == label_count:
|
||||||
|
self.passed(
|
||||||
|
"no missing mailing labels from pdf export. Expected: %d, Actual %d"
|
||||||
|
% (result_no, label_count)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.failed(
|
||||||
|
"missing mailing labels from the pdf export. Expected: %d, Actual %d"
|
||||||
|
% (result_no, label_count)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
try:
|
||||||
|
self.login()
|
||||||
|
self._test()
|
||||||
|
finally:
|
||||||
|
self.logout()
|
||||||
|
self.browser.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="")
|
parser = argparse.ArgumentParser(description="")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@ -365,19 +449,14 @@ if __name__ == "__main__":
|
|||||||
)
|
)
|
||||||
parser.set_defaults(dev=False, show_browser=False)
|
parser.set_defaults(dev=False, show_browser=False)
|
||||||
arguments = parser.parse_args()
|
arguments = parser.parse_args()
|
||||||
ActivitiesTab(
|
cl_arg = (
|
||||||
arguments.user, arguments.passwd, arguments.dev, arguments.show_browser
|
arguments.user, arguments.passwd, arguments.dev, arguments.show_browser
|
||||||
).test_all_hardcoded_contacts()
|
)
|
||||||
ContactExport(
|
SteeringCommitteePrintLabels(*cl_arg).test()
|
||||||
arguments.user, arguments.passwd, arguments.dev, arguments.show_browser
|
ActivitiesTab(*cl_arg).test_all_hardcoded_contacts()
|
||||||
).test_hardcoded_search_terms()
|
ContactExport(*cl_arg).test_hardcoded_search_terms()
|
||||||
|
|
||||||
# Mailing list
|
# Mailing list
|
||||||
# Load mailing list test and enter test data
|
# Load mailing list test and enter test data
|
||||||
# send test email and check for soft crashes
|
# send test email and check for soft crashes
|
||||||
# manual check the email sent
|
# manual check the email sent
|
||||||
|
|
||||||
# Print labels
|
|
||||||
# Search contacts > by group "Steering Committee" > search
|
|
||||||
# Print labels for group, untick "do not mail" check box
|
|
||||||
# Resulting pdf might have missing names
|
|
||||||
|
Loading…
Reference in New Issue
Block a user