Files
django-dav-events/dav_base/tests/generic.py

321 lines
12 KiB
Python

import datetime
import os
from django.apps import apps
from django.contrib.auth.models import AbstractUser
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.core import mail as django_mail
from django.test import SimpleTestCase, TestCase, tag
from django.urls import reverse
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from six.moves.urllib.parse import quote
class AppSetting(object):
def __init__(self, name, of=None):
self.name = name
self.of = of
class AppsTestCase(SimpleTestCase):
app_config = None
settings = ()
def setUp(self):
super(AppsTestCase, self).setUp()
if self.app_config:
self.configured_settings = self.app_config.settings
else:
self.configured_settings = None
def test_settings(self):
config = self.configured_settings
for setting in self.settings:
name = setting.name
self.assertTrue(hasattr(config, name), 'Settings do not contain {}'.format(name))
value = getattr(config, name)
of = setting.of
if of is not None:
self.assertIsInstance(value, of)
class EmailTestMixin(object):
email_sender = 'Automatic Software Test <root@localhost>'
email_base_url = 'http://localhost'
email_subject_prefix = '[Test]'
def get_mail_for_user(self, user):
recipient = '"{fullname}" <{email}>'.format(fullname=user.get_full_name(), email=user.email)
mails = []
for mail in django_mail.outbox:
if recipient in mail.recipients():
mails.append(mail)
return mails
def assertSender(self, mail):
self.assertEqual(mail.from_email, self.email_sender)
def assertReplyTo(self, mail, addresses):
self.assertEqual(len(mail.reply_to), len(addresses))
for expected_address in addresses:
if isinstance(expected_address, AbstractUser):
expected_address = u'"%s" <%s>' % (expected_address.get_full_name(), expected_address.email)
self.assertIn(expected_address, mail.reply_to)
def assertRecipients(self, mail, recipients):
self.assertEqual(len(mail.recipients()), len(recipients))
for expected_recipient in recipients:
if isinstance(expected_recipient, AbstractUser):
expected_recipient = u'"%s" <%s>' % (expected_recipient.get_full_name(), expected_recipient.email)
recipients = mail.recipients()
self.assertIn(expected_recipient, recipients)
def assertSubject(self, mail, subject):
expected_subject = u'{} {}'.format(self.email_subject_prefix, subject)
self.assertEqual(mail.subject, expected_subject)
def assertBody(self, mail, body):
expected_lines = body.splitlines()
lines = mail.body.splitlines()
i = 0
for expected_line in expected_lines:
try:
line = lines[i]
except IndexError:
self.fail('line %d: no such line: %s' % (i, expected_line))
i += 1
try:
self.assertEqual(line, expected_line)
except AssertionError as e:
self.fail('line %d: %s' % (i, e))
self.assertEqual(mail.body, body)
def setUp(self):
app_config = apps.get_app_config('dav_base')
app_config.settings.email_sender = self.email_sender
app_config.settings.email_base_url = self.email_base_url
app_config.settings.email_subject_prefix = self.email_subject_prefix
class FormDataSet(object):
def __init__(self, data, expected_errors=None, form_kwargs=None):
self.data = data
self.expected_errors = expected_errors
self.form_kwargs = form_kwargs
class FormsTestCase(TestCase):
form_class = None
valid_data_sets = ()
invalid_data_sets = ()
def test_valid_data(self, form_class=None, data_sets=None, form_kwargs=None):
if form_class is None:
form_class = self.form_class
if form_class is None:
return True
if data_sets is None:
data_sets = self.valid_data_sets
for data_set in data_sets:
fk = {}
if form_kwargs is not None:
fk.update(form_kwargs)
if data_set.form_kwargs is not None:
fk.update(data_set.form_kwargs)
fk['data'] = data_set.data
form = form_class(**fk)
if not form.is_valid():
errors = []
for key in form.errors.as_data():
for ve in form.errors[key].as_data():
errors.append(u'%s (%s)' % (ve.code, ve.message))
self.fail(u'Invalid form data \'%s\': %s' % (data_set.data, errors))
def test_invalid_data(self, form_class=None, data_sets=None, form_kwargs=None):
if form_class is None:
form_class = self.form_class
if form_class is None:
return True
if data_sets is None:
data_sets = self.invalid_data_sets
for data_set in data_sets:
fk = {}
if form_kwargs is not None:
fk.update(form_kwargs)
if data_set.form_kwargs is not None:
fk.update(data_set.form_kwargs)
fk['data'] = data_set.data
form = form_class(**fk)
if form.is_valid():
self.fail('Valid form data: \'%s\'' % data_set.data)
if data_set.expected_errors:
error_dicts = form.errors.as_data()
for key, code in data_set.expected_errors:
error_codes = [ve.code for ve in error_dicts[key]]
self.assertIn(code, error_codes)
class Url(object):
def __init__(self, location, name=None, func=None, **kwargs):
self.location = location
self.name = name
self.func = func
self.redirect = kwargs.get('redirect', False)
self.status_code = kwargs.get('status_code', 200)
self.follow = kwargs.get('follow', False)
class UrlsTestCase(SimpleTestCase):
urls = ()
def test_locations(self):
for url in self.urls:
if url.location:
response = self.client.get(url.location, follow=url.follow)
if url.redirect:
self.assertRedirects(response, url.redirect)
else:
self.assertEqual(response.status_code, url.status_code,
'Getting \'{}\' is not OK'.format(url.location))
if url.func:
self.assertEqual(response.resolver_match.func.__name__,
url.func.__name__,
'Getting \'{}\' resolve to wrong function'.format(url.location))
def test_names(self):
for url in self.urls:
if url.name:
response = self.client.get(reverse(url.name), follow=url.follow)
if url.redirect:
self.assertRedirects(response, url.redirect)
else:
self.assertEqual(response.status_code, url.status_code,
'Getting url named \'{}\' is not OK'.format(url.name))
if url.func:
self.assertEqual(response.resolver_match.func.__name__,
url.func.__name__,
'Getting url named \'{}\' resolve to wrong function'.format(url.name))
class SeleniumTestCase(StaticLiveServerTestCase):
headless = True
window_width = 1024
window_height = 768
def __init__(self, *args, **kwargs):
super(SeleniumTestCase, self).__init__(*args, **kwargs)
self._driver = None
self._driver_options = webdriver.FirefoxOptions()
self.quit_selenium = None
@property
def selenium(self):
if self._driver is None:
if self.headless:
self._driver_options.add_argument('--headless')
self._driver = webdriver.Firefox(options=self._driver_options)
if self.quit_selenium is None:
self.quit_selenium = True
if self.window_width and self.window_height:
self._driver.set_window_size(self.window_width, self.window_height)
return self._driver
def tearDown(self):
if self.quit_selenium:
self.selenium.quit()
super(SeleniumTestCase, self).tearDown()
def complete_url(self, location):
base_url = self.live_server_url
return '{}/{}'.format(base_url, location.lstrip('/'))
def get(self, location):
return self.selenium.get(self.complete_url(location))
def wait_on(self, driver, ec_name, ec_argument, timeout=30):
ec = getattr(EC, ec_name)
return WebDriverWait(driver, timeout).until(ec(ec_argument))
def wait_on_presence(self, driver, locator, timeout=30):
ec_name = 'presence_of_element_located'
return self.wait_on(driver, ec_name, locator, timeout)
def wait_until_stale(self, driver, element, timeout=30):
ec_name = 'staleness_of'
return self.wait_on(driver, ec_name, element, timeout)
class ScreenshotTestCase(SeleniumTestCase):
screenshot_prefix = ''
locations = ()
def __init__(self, *args, **kwargs):
super(ScreenshotTestCase, self).__init__(*args, **kwargs)
screenshot_base_dir = os.path.join('tmp', 'test-screenshots')
self.screenshot_path = screenshot_base_dir
self.screenshot_sequences = {}
def sanitize_filename(self, location):
return quote(location).replace('/', '--')
def save_screenshot(self, title=None, sequence=None, resize=True):
if sequence is None:
sequence = ''
else:
if sequence in self.screenshot_sequences:
self.screenshot_sequences[sequence] += 1
else:
self.screenshot_sequences[sequence] = 1
n = self.screenshot_sequences[sequence]
sequence = u'%s-%04d-' % (sequence, n)
if title is None:
location = self.selenium.current_url
if location.startswith(self.live_server_url):
location = location[len(self.live_server_url):]
location = location.lstrip('/')
if location == '':
location = 'root'
title = location
base_name = u'{timestamp}-{prefix}{sequence}{title}.png'.format(
timestamp=datetime.datetime.now().strftime('%Y%m%d-%H%M%S.%f'),
prefix=self.screenshot_prefix,
sequence=sequence,
title=title,
)
path = os.path.join(self.screenshot_path, self.sanitize_filename(base_name))
if not os.path.isdir(self.screenshot_path):
os.makedirs(self.screenshot_path, 0o700)
restore_size = False
if resize:
window_size = self.selenium.get_window_size()
deco_height = self.selenium.execute_script('return window.outerHeight - window.innerHeight')
doc_height = self.selenium.execute_script('return document.body.scrollHeight')
if (window_size['height'] - deco_height) < doc_height:
self.selenium.set_window_size(window_size['width'], doc_height + deco_height)
restore_size = True
self.selenium.save_screenshot(path)
if restore_size:
self.selenium.set_window_size(window_size['width'], window_size['height'])
@tag('screenshots', 'browser')
def test_screenshots(self):
for location in self.locations:
self.get(location)
self.save_screenshot()