diff --git a/PyRIGS/tests/base.py b/PyRIGS/tests/base.py
new file mode 100644
index 00000000..7830df0f
--- /dev/null
+++ b/PyRIGS/tests/base.py
@@ -0,0 +1,37 @@
+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):
+ @classmethod
+ 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")
diff --git a/PyRIGS/tests/pages.py b/PyRIGS/tests/pages.py
new file mode 100644
index 00000000..b17d837b
--- /dev/null
+++ b/PyRIGS/tests/pages.py
@@ -0,0 +1,26 @@
+from pypom import Page
+from selenium.webdriver.common.by import By
+
+
+class BasePage(Page):
+ _user_locator = (By.CSS_SELECTOR, "#user>a")
+
+
+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()
diff --git a/PyRIGS/tests/regions.py b/PyRIGS/tests/regions.py
new file mode 100644
index 00000000..fbbb9f0e
--- /dev/null
+++ b/PyRIGS/tests/regions.py
@@ -0,0 +1,78 @@
+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
+
+
+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')
+
+ @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()
+
+ @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 = (x for x in self.options if x.name == name)
+ for option in options:
+ option.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
diff --git a/RIGS/templates/RIGS/organisation_detail.html b/RIGS/templates/RIGS/organisation_detail.html
index b12b6391..ca16aaa6 100644
--- a/RIGS/templates/RIGS/organisation_detail.html
+++ b/RIGS/templates/RIGS/organisation_detail.html
@@ -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 %}
diff --git a/RIGS/test_functional.py b/RIGS/test_functional.py
index aa65158a..fbe0b0b7 100644
--- a/RIGS/test_functional.py
+++ b/RIGS/test_functional.py
@@ -20,23 +20,12 @@ 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
import sys
-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 UserRegistrationTest(LiveServerTestCase):
def setUp(self):
self.browser = create_browser()
diff --git a/RIGS/test_models.py b/RIGS/test_models.py
index 424e2f06..d35cbbe2 100644
--- a/RIGS/test_models.py
+++ b/RIGS/test_models.py
@@ -1,5 +1,3 @@
-
-
import pytz
from reversion import revisions as reversion
from django.conf import settings
@@ -8,6 +6,7 @@ from django.test import TestCase
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):
diff --git a/assets/templates/asset_list.html b/assets/templates/asset_list.html
index 90bb2346..cbd27b28 100644
--- a/assets/templates/asset_list.html
+++ b/assets/templates/asset_list.html
@@ -17,16 +17,16 @@