From da0c9ba87b3a334c7bfd2ae49832482b35a6658a Mon Sep 17 00:00:00 2001 From: FreneticScribbler Date: Sun, 23 Feb 2020 23:31:12 +0000 Subject: [PATCH] Start tests for audit --- assets/templates/asset_audit.html | 4 +- assets/tests/pages.py | 108 +++++++++++++++++++++++++++++- assets/tests/test_assets.py | 50 ++++++++++++++ 3 files changed, 159 insertions(+), 3 deletions(-) diff --git a/assets/templates/asset_audit.html b/assets/templates/asset_audit.html index 748b0d4f..caaee4d7 100644 --- a/assets/templates/asset_audit.html +++ b/assets/templates/asset_audit.html @@ -142,7 +142,7 @@ {% if not request.is_ajax %}
- +
{% endif %} @@ -150,6 +150,6 @@ {% block footer %}
- +
{% endblock %} diff --git a/assets/tests/pages.py b/assets/tests/pages.py index e31859e1..f0665133 100644 --- a/assets/tests/pages.py +++ b/assets/tests/pages.py @@ -6,7 +6,7 @@ from selenium.webdriver import Chrome from django.urls import reverse from PyRIGS.tests import regions from PyRIGS.tests.pages import BasePage, FormPage -import pdb +from selenium.common.exceptions import NoSuchElementException class AssetList(BasePage): @@ -186,3 +186,109 @@ class SupplierEdit(SupplierForm): @property def success(self): return '/edit' not in self.driver.current_url + + +class AssetAuditList(AssetList): + URL_TEMPLATE = reverse('asset_audit_list') + + _search_text_locator = (By.ID, 'id_query') + _go_button_locator = (By.ID, 'searchButton') + _modal_locator = (By.ID, 'modal') + + @property + def modal(self): + return self.AssetAuditModal(self, self.find_element(*self._modal_locator)) + + @property + def query(self): + return self.find_element(*self._search_text_locator).text + + def set_query(self, queryString): + element = self.find_element(*self._search_text_locator) + element.clear() + element.send_keys(queryString) + + def search(self): + self.find_element(*self._go_button_locator).click() + + class AssetAuditModal(Region): + _errors_selector = (By.CLASS_NAME, "alert-danger") + # Don't use the usual success selector - that tries and fails to hit the '10m long cable' helper button... + _submit_locator = (By.ID, "id_mark_audited") + form_items = { + 'asset_id': (regions.TextBox, (By.ID, 'id_asset_id')), + 'description': (regions.TextBox, (By.ID, 'id_description')), + 'is_cable': (regions.CheckBox, (By.ID, 'id_is_cable')), + 'serial_number': (regions.TextBox, (By.ID, 'id_serial_number')), + 'salvage_value': (regions.TextBox, (By.ID, 'id_salvage_value')), + 'date_acquired': (regions.DatePicker, (By.ID, 'id_date_acquired')), + 'category': (regions.SingleSelectPicker, (By.ID, 'id_category')), + 'status': (regions.SingleSelectPicker, (By.ID, 'id_status')), + + 'plug': (regions.SingleSelectPicker, (By.ID, 'id_plug')), + 'socket': (regions.SingleSelectPicker, (By.ID, 'id_socket')), + 'length': (regions.TextBox, (By.ID, 'id_length')), + 'csa': (regions.TextBox, (By.ID, 'id_csa')), + 'circuits': (regions.TextBox, (By.ID, 'id_circuits')), + 'cores': (regions.TextBox, (By.ID, 'id_cores')) + } + + @property + def is_displayed(self): + return self.root.is_displayed() + + @property + def errors(self): + try: + error_page = self.ErrorPage(self, self.find_element(*self._errors_selector)) + return error_page.errors + except NoSuchElementException: + return None + + def submit(self): + previous_errors = self.errors + self.root.find_element(*self._submit_locator).click() + # self.wait.until(lambda x: not self.is_displayed) + + 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 + + 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\")});") + + 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 diff --git a/assets/tests/test_assets.py b/assets/tests/test_assets.py index 9a96fcbe..778cba2e 100644 --- a/assets/tests/test_assets.py +++ b/assets/tests/test_assets.py @@ -10,6 +10,8 @@ from PyRIGS.tests.base import BaseTest, AutoLoginTest from assets import models, urls from reversion import revisions as reversion from selenium.webdriver.common.keys import Keys +from selenium.webdriver.support.ui import WebDriverWait +from RIGS.test_functional import animation_is_finished import datetime @@ -256,6 +258,54 @@ class TestSupplierCreateAndEdit(AutoLoginTest): self.assertTrue(self.page.success) +class TestAssetAudit(AutoLoginTest): + def setUp(self): + super().setUp() + self.category = models.AssetCategory.objects.create(name="Haulage") + self.status = models.AssetStatus.objects.create(name="Probably Fine", should_show=True) + self.supplier = models.Supplier.objects.create(name="The Bazaar") + self.connector = models.Connector.objects.create(description="Trailer Socket", current_rating=1, voltage_rating=40, num_pins=13) + models.Asset.objects.create(asset_id="1", description="Trailer Cable", status=self.status, category=self.category, date_acquired=datetime.date(2020, 2, 1)) + models.Asset.objects.create(asset_id="11", description="Trailerboard", status=self.status, category=self.category, date_acquired=datetime.date(2020, 2, 1)) + models.Asset.objects.create(asset_id="111", description="Erms", status=self.status, category=self.category, date_acquired=datetime.date(2020, 2, 1)) + models.Asset.objects.create(asset_id="1111", description="A hammer", status=self.status, category=self.category, date_acquired=datetime.date(2020, 2, 1)) + self.page = pages.AssetAuditList(self.driver, self.live_server_url).open() + + def test_audit_process(self): + asset_id = "1111" + self.page.set_query(asset_id) + self.page.search() + wait = WebDriverWait(self.driver, 3) + wait.until(animation_is_finished()) + + mdl = self.page.modal + self.assertTrue(mdl.is_displayed) + # Do it wrong on purpose to check error display + mdl.remove_all_required() + mdl.description = "" + mdl.submit() + wait.until(animation_is_finished()) + self.assertTrue(mdl.is_displayed) + self.assertIn("This field is required.", mdl.errors["Description"]) + # Now do it properly + new_desc = "A BIG hammer" + mdl.description = new_desc + mdl.submit() + wait.until(animation_is_finished()) + self.assertFalse(mdl.is_displayed) + + # Check data is correct + audited = models.Asset.objects.get(asset_id="1111") + self.assertEqual(audited.description, new_desc) + # Make sure audit 'log' was filled out + self.assertEqual(self.profile.initials, audited.last_audited_by.initials) + self.assertEqual(datetime.datetime.now().date(), audited.last_audited_at.date()) + self.assertEqual(datetime.datetime.now().hour, audited.last_audited_at.hour) + self.assertEqual(datetime.datetime.now().minute, audited.last_audited_at.minute) + # Check we've removed it from the 'needing audit' list + self.assertNotIn(asset_id, self.page.assets) + + class TestSupplierValidation(TestCase): @classmethod def setUpTestData(cls):