diff --git a/PyRIGS/tests/base.py b/PyRIGS/tests/base.py index 7830df0f..ecdacd21 100644 --- a/PyRIGS/tests/base.py +++ b/PyRIGS/tests/base.py @@ -16,7 +16,6 @@ def create_browser(): class BaseTest(LiveServerTestCase): - @classmethod def setUp(self): super().setUpClass() self.driver = create_browser() diff --git a/PyRIGS/tests/pages.py b/PyRIGS/tests/pages.py index b17d837b..4bf34a6d 100644 --- a/PyRIGS/tests/pages.py +++ b/PyRIGS/tests/pages.py @@ -1,9 +1,69 @@ -from pypom import Page +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): - _user_locator = (By.CSS_SELECTOR, "#user>a") + 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): diff --git a/PyRIGS/tests/regions.py b/PyRIGS/tests/regions.py index fbbb9f0e..e64adaa2 100644 --- a/PyRIGS/tests/regions.py +++ b/PyRIGS/tests/regions.py @@ -3,6 +3,8 @@ 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): @@ -19,8 +21,10 @@ 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') + _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): @@ -49,15 +53,22 @@ class BootstrapSelectElement(Region): 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 = (x for x in self.options if x.name == name) - for option in options: - option.set_selected(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') @@ -76,3 +87,47 @@ class BootstrapSelectElement(Region): @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) diff --git a/assets/templates/partials/parent_form.html b/assets/templates/partials/parent_form.html index 5f29240d..c21e0ab1 100644 --- a/assets/templates/partials/parent_form.html +++ b/assets/templates/partials/parent_form.html @@ -6,7 +6,7 @@
{% if create or edit or duplicate %} -
+
{% include 'partials/asset_picker.html' %}
diff --git a/assets/templates/partials/purchasedetails_form.html b/assets/templates/partials/purchasedetails_form.html index 0e2f1aa6..b098e656 100644 --- a/assets/templates/partials/purchasedetails_form.html +++ b/assets/templates/partials/purchasedetails_form.html @@ -23,7 +23,7 @@
{% if create or edit or duplicate %} -
+