import os import time from datetime import datetime from django.contrib.staticfiles.testing import StaticLiveServerTestCase from pathlib import Path from selenium import webdriver from selenium.common.exceptions import WebDriverException from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from .container_commands import reset_database MAX_WAIT = 10 SCREEN_DUMP_LOCATION = Path(__file__).absolute().parent / "screendumps" # Decorator fns def wait(fn): def modified_fn(*args, **kwargs): start_time = time.time() while True: try: return fn(*args, **kwargs) except (AssertionError, WebDriverException) as e: if time.time() - start_time > MAX_WAIT: raise e time.sleep(0.5) return modified_fn # Functional Tests class FunctionalTest(StaticLiveServerTestCase): # Helper methods def setUp(self): options = webdriver.FirefoxOptions() if os.environ.get("HEADLESS"): options.add_argument("--headless") self.browser = webdriver.Firefox(options=options) self.test_server = os.environ.get("TEST_SERVER") if self.test_server: self.live_server_url = 'http://' + self.test_server reset_database(self.test_server) def tearDown(self): if self._test_has_failed(): if not SCREEN_DUMP_LOCATION.exists(): SCREEN_DUMP_LOCATION.mkdir(parents=True) self.take_screenshot() self.dump_html() self.browser.quit() super().tearDown() def _test_has_failed(self): return self._outcome.result.failures or self._outcome.result.errors def take_screenshot(self): path = SCREEN_DUMP_LOCATION / self._get_filename("png") print("screendumping to", path) self.browser.get_screenshot_as_file(str(path)) def dump_html(self): path = SCREEN_DUMP_LOCATION / self._get_filename("html") print("dumping page html to", path) path.write_text(self.browser.page_source) def _get_filename(self, extension): timestamp = datetime.now().isoformat().replace(":", ".") return ( f"{self.__class__.__name__}.{self._testMethodName}-{timestamp}.{extension}" ) @wait def wait_for(self, fn): return fn() def get_item_input_box(self): return self.browser.find_element(By.ID, "id_text") @wait def wait_for_row_in_list_table(self, row_text): rows = self.browser.find_elements(By.CSS_SELECTOR, "#id_list_table tr") self.assertIn(row_text, [row.text for row in rows]) def add_list_item(self, item_text): num_rows = len(self.browser.find_elements(By.CSS_SELECTOR, "#id_list_table tr")) self.get_item_input_box().send_keys(item_text) self.get_item_input_box().send_keys(Keys.ENTER) item_number = num_rows + 1 self.wait_for_row_in_list_table(f"{item_number}. {item_text}") @wait def wait_to_be_logged_in(self, email): self.browser.find_element(By.CSS_SELECTOR, "#id_logout"), navbar = self.browser.find_element(By.CSS_SELECTOR, ".navbar") self.assertIn(email, navbar.text) @wait def wait_to_be_logged_out(self, email): lambda: self.browser.find_element(By.CSS_SELECTOR, "input[name=email]"), navbar = self.browser.find_element(By.CSS_SELECTOR, ".navbar") self.assertNotIn(email, navbar.text)