Skip to content

Commit

Permalink
Merge 3e11448 into 116c497
Browse files Browse the repository at this point in the history
  • Loading branch information
FreneticScribbler committed Feb 8, 2020
2 parents 116c497 + 3e11448 commit aa34f62
Show file tree
Hide file tree
Showing 33 changed files with 1,079 additions and 418 deletions.
36 changes: 36 additions & 0 deletions PyRIGS/tests/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from django.test import LiveServerTestCase
from selenium import webdriver
from RIGS import models as rigsmodels
from . import pages
import os


def create_browser():
options = webdriver.ChromeOptions()
options.add_argument("--window-size=1920,1080")
if os.environ.get('CI', False):
options.add_argument("--headless")
options.add_argument("--no-sandbox")
driver = webdriver.Chrome(chrome_options=options)
return driver


class BaseTest(LiveServerTestCase):
def setUp(self):
super().setUpClass()
self.driver = create_browser()

def tearDown(self):
super().tearDown()
self.driver.quit()


class AutoLoginTest(BaseTest):
def setUp(self):
super().setUp()
self.profile = rigsmodels.Profile(
username="EventTest", first_name="Event", last_name="Test", initials="ETU", is_superuser=True)
self.profile.set_password("EventTestPassword")
self.profile.save()
loginPage = pages.LoginPage(self.driver, self.live_server_url).open()
loginPage.login("EventTest", "EventTestPassword")
86 changes: 86 additions & 0 deletions PyRIGS/tests/pages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from pypom import Page, Region
from selenium.webdriver.common.by import By
from selenium.webdriver import Chrome
from selenium.common.exceptions import NoSuchElementException


class BasePage(Page):
form_items = {}

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def __getattr__(self, name):
if name in self.form_items:
element = self.form_items[name]
form_element = element[0](self, self.find_element(*element[1]))
return form_element.value
else:
return super().__getattribute__(name)

def __setattr__(self, name, value):
if name in self.form_items:
element = self.form_items[name]
form_element = element[0](self, self.find_element(*element[1]))
form_element.set_value(value)
else:
self.__dict__[name] = value


class FormPage(BasePage):
_errors_selector = (By.CLASS_NAME, "alert-danger")

def remove_all_required(self):
self.driver.execute_script("Array.from(document.getElementsByTagName(\"input\")).forEach(function (el, ind, arr) { el.removeAttribute(\"required\")});")
self.driver.execute_script("Array.from(document.getElementsByTagName(\"select\")).forEach(function (el, ind, arr) { el.removeAttribute(\"required\")});")

@property
def errors(self):
try:
error_page = self.ErrorPage(self, self.find_element(*self._errors_selector))
return error_page.errors
except NoSuchElementException:
return None

class ErrorPage(Region):
_error_item_selector = (By.CSS_SELECTOR, "dl>span")

class ErrorItem(Region):
_field_selector = (By.CSS_SELECTOR, "dt")
_error_selector = (By.CSS_SELECTOR, "dd>ul>li")

@property
def field_name(self):
return self.find_element(*self._field_selector).text

@property
def errors(self):
return [x.text for x in self.find_elements(*self._error_selector)]

@property
def errors(self):
error_items = [self.ErrorItem(self, x) for x in self.find_elements(*self._error_item_selector)]
errors = {}
for error in error_items:
errors[error.field_name] = error.errors
return errors


class LoginPage(BasePage):
URL_TEMPLATE = '/user/login'

_username_locator = (By.ID, 'id_username')
_password_locator = (By.ID, 'id_password')
_submit_locator = (By.ID, 'id_submit')
_error_locator = (By.CSS_SELECTOR, '.errorlist>li')

def login(self, username, password):
username_element = self.find_element(*self._username_locator)
username_element.clear()
username_element.send_keys(username)

password_element = self.find_element(*self._password_locator)
password_element.clear()
password_element.send_keys(password)

self.find_element(*self._submit_locator).click()
133 changes: 133 additions & 0 deletions PyRIGS/tests/regions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
from pypom import Region
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.select import Select
import datetime


def parse_bool_from_string(string):
# Used to convert from attribute strings to boolean values, written after I found this:
# >>> bool("false")
# True
if string == "true":
return True
else:
return False


class BootstrapSelectElement(Region):
_main_button_locator = (By.CSS_SELECTOR, 'button.dropdown-toggle')
_option_box_locator = (By.CSS_SELECTOR, 'ul.dropdown-menu')
_option_locator = (By.CSS_SELECTOR, 'ul.dropdown-menu.inner>li>a[role=option]')
_select_all_locator = (By.CLASS_NAME, 'bs-select-all')
_deselect_all_locator = (By.CLASS_NAME, 'bs-deselect-all')
_search_locator = (By.CSS_SELECTOR, '.bs-searchbox>input')
_status_locator = (By.CLASS_NAME, 'status')

@property
def is_open(self):
return parse_bool_from_string(self.find_element(*self._main_button_locator).get_attribute("aria-expanded"))

def toggle(self):
original_state = self.is_open
return self.find_element(*self._main_button_locator).click()
option_box = self.find_element(*self._option_box_locator)
if original_state:
self.wait.until(expected_conditions.invisibility_of_element_located(option_box))
else:
self.wait.until(expected_conditions.visibility_of_element_located(option_box))

def open(self):
if not self.is_open:
self.toggle()

def close(self):
if self.is_open:
self.toggle()

def select_all(self):
self.find_element(*self._select_all_locator).click()

def deselect_all(self):
self.find_element(*self._deselect_all_locator).click()

def search(self, query):
search_box = self.find_element(*self._search_locator)
search_box.clear()
search_box.send_keys(query)
status_text = self.find_element(*self._status_locator)
self.wait.until(expected_conditions.invisibility_of_element_located(self._status_locator))

@property
def options(self):
options = list(self.find_elements(*self._option_locator))
return [self.BootstrapSelectOption(self, i) for i in options]

def set_option(self, name, selected):
options = list((x for x in self.options if x.name == name))
assert len(options) == 1
options[0].set_selected(selected)

class BootstrapSelectOption(Region):
_text_locator = (By.CLASS_NAME, 'text')

@property
def selected(self):
return parse_bool_from_string(self.root.get_attribute("aria-selected"))

def toggle(self):
self.root.click()

def set_selected(self, selected):
if self.selected != selected:
self.toggle()

@property
def name(self):
return self.find_element(*self._text_locator).text


class TextBox(Region):
@property
def value(self):
return self.root.get_attribute("value")

def set_value(self, value):
self.root.clear()
self.root.send_keys(value)


class CheckBox(Region):
def toggle(self):
self.root.click()

@property
def value(self):
return parse_bool_from_string(self.root.get_attribute("checked"))

def set_value(self, value):
if value != self.value:
self.toggle()


class DatePicker(Region):
@property
def value(self):
return datetime.datetime.strptime(self.root.get_attribute("value"), "%Y-%m-%d")

def set_value(self, value):
self.root.clear()
self.root.send_keys(value.strftime("%d%m%Y"))


class SingleSelectPicker(Region):
@property
def value(self):
picker = Select(self.root)
return picker.first_selected_option.text

def set_value(self, value):
picker = Select(self.root)
picker.select_by_visible_text(value)
2 changes: 1 addition & 1 deletion RIGS/templates/RIGS/organisation_detail.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
{% load widget_tweaks %}

{% block title %}Organisation | {{ object.name }}{% endblock %}
Expand Down
14 changes: 6 additions & 8 deletions RIGS/test_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,13 @@

from RIGS import models

from reversion import revisions as reversion
from django.urls import reverse
from django.core import mail, signing
from PyRIGS.tests.base import create_browser
from django.conf import settings

def create_browser():
options = webdriver.ChromeOptions()
options.add_argument("--window-size=1920,1080")
if os.environ.get('CI', False):
options.add_argument("--headless")
options.add_argument("--no-sandbox")
driver = webdriver.Chrome(chrome_options=options)
return driver
import sys


class UserRegistrationTest(LiveServerTestCase):
Expand Down
3 changes: 1 addition & 2 deletions RIGS/test_models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


import pytz
from reversion import revisions as reversion
from django.conf import settings
Expand All @@ -8,6 +6,7 @@
from RIGS import models, versioning
from datetime import date, timedelta, datetime, time
from decimal import *
from PyRIGS.tests.base import create_browser


class ProfileTestCase(TestCase):
Expand Down
5 changes: 0 additions & 5 deletions assets/apps.py

This file was deleted.

9 changes: 0 additions & 9 deletions assets/filters.py

This file was deleted.

Loading

0 comments on commit aa34f62

Please sign in to comment.