Files
python-tdd/src/functional_tests/base.py

103 lines
3.5 KiB
Python
Raw Normal View History

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)