import os import re import csv import urllib.parse as urlparse from urllib.parse import parse_qs import requests from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait class BaseTester: BASE_URL = "https://crm-dev.caat.org.uk/" USERNAME = "roxie" PASSWORD = "" def __init__(self): firefox_options = webdriver.FirefoxOptions() firefox_options.headless = True self.browser = webdriver.Firefox(options=firefox_options) self.wait = WebDriverWait(self.browser, 20) def login(self): """ Login to civicrm so we can continue with the proper cookies """ self.browser.get(self.BASE_URL) username = self.browser.find_element_by_id("edit-name") password = self.browser.find_element_by_id("edit-pass") submit = self.browser.find_element_by_id("edit-submit") username.send_keys(self.USERNAME) password.send_keys(self.PASSWORD) submit.click() # Wait for the js elements load so we know the cookies are good. # Waits for "Recent Items" part of sidebar which is unique when logged in self.wait.until( EC.visibility_of_element_located((By.ID, "block-civicrm-2")) ) def logout(self): self.browser.get(self.BASE_URL + "/user/logout") # Wait for the next page to load to finish logging out self.wait.until( EC.visibility_of_element_located((By.ID, "tabs-wrapper")) ) def find_element_by_id(self, *args, **kwargs): return self.browser.find_element_by_id(*args, **kwargs) def find_element_by_css_selector(self, *args, **kwargs): return self.browser.find_element_by_css_selector(*args, **kwargs) def wait_until_visible(self, locator): return self.wait.until(EC.visibility_of_element_located(locator)) class ContactExport(BaseTester): def __init__(self): super().__init__() 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): session_cookie = {} for cookie in self.browser.get_cookies(): if re.findall(r"^SSESS.*", cookie.get("name")): session_cookie = cookie if not session_cookie: print("NO SESSION COOKIE FOUND. Are you logged in?") raise RuntimeError("No session cookie found.") session = requests.Session() session.cookies.update( {session_cookie["name"]: session_cookie["value"]} ) qf_key = self.get_export_id() data = { "qfKey": qf_key, "entryURL": self.BASE_URL + "/civicrm/contact/search", "_qf_Select_next": "Continue", "exportOption": 1, "mergeOption": 0, "postal_greeting": 1, "addressee": 1, } req = session.request( "POST", self.BASE_URL + "/civicrm/contact/search", data=data ) return req def calculate_exported_contacts_number(self): req = self.download_csv() file_name = "/tmp/exportedRecords.csv" with open(file_name, "w") as csv_file: csv_file.write(req.text) with open(file_name, "r") as csv_file: # Dict reader should remove headers exported_csv = csv.DictReader(csv_file) exported_number_exports = sum(1 for row in exported_csv) os.remove(file_name) return exported_number_exports def get_export_id(self) -> str: export_page_url = self.browser.current_url parsed = urlparse.urlparse(export_page_url) return parse_qs(parsed.query)['qfKey'] def test(self, search_term: str): try: self.login() self.browser.get(self.search_url) search_box = self.find_element_by_id("sort_name") search_box.send_keys(search_term) search_box.send_keys(Keys.ENTER) self.wait_until_visible( (By.ID, "alpha-filter") ) #wait for table to load 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.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")) req = self.download_csv() exported_number_exports = self.calculate_exported_contacts_number() if exported_number_exports == (result_no): print( "TEST PASSED: Number of expected contact exports for '{}' matches actual number of exports - Expected: {}, Actual: {}" .format(search_term, result_no, exported_number_exports) ) else: print( "TEST FAILED: Number of expected contact exports for '{}' WAS NOT EQUAL to actual number of exports - Expected: {}, Actual: {}" .format(search_term, result_no, exported_number_exports) ) finally: self.logout() self.browser.close() if __name__ == "__main__": ContactExport().test("John")