diff --git a/PyRIGS/tests/base.py b/PyRIGS/tests/base.py index f1ca8a9b..a6fc7689 100644 --- a/PyRIGS/tests/base.py +++ b/PyRIGS/tests/base.py @@ -7,6 +7,7 @@ import pytz from django.conf import settings from django.test import LiveServerTestCase from selenium import webdriver +from selenium.webdriver.support.wait import WebDriverWait from RIGS import models as rigsmodels from . import pages @@ -31,6 +32,7 @@ class BaseTest(LiveServerTestCase): def setUp(self): super().setUpClass() self.driver = create_browser() + self.wait = WebDriverWait(self.driver, 5) def tearDown(self): super().tearDown() @@ -44,8 +46,8 @@ class AutoLoginTest(BaseTest): 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") + login_page = pages.LoginPage(self.driver, self.live_server_url).open() + login_page.login("EventTest", "EventTestPassword") def screenshot_failure(func): @@ -60,6 +62,7 @@ def screenshot_failure(func): self.driver.save_screenshot(screenshot_file) print("Error in test {} is at path {}".format(screenshot_name, screenshot_file), file=sys.stderr) raise e + return wrapper_func @@ -70,12 +73,9 @@ def screenshot_failure_cls(cls): return cls -# Checks if animation is done -class animation_is_finished(): - def __call__(self, driver): - numberAnimating = driver.execute_script('return $(":animated").length') - finished = numberAnimating == 0 - if finished: - import time - time.sleep(0.1) - return finished +def assert_times_equal(first_time, second_time): + assert first_time.replace(microsecond=0) == second_time.replace(microsecond=0) + + +def response_contains(response, needle): + return str(needle) in str(response.content) diff --git a/PyRIGS/tests/pages.py b/PyRIGS/tests/pages.py index a50b94cd..98673df6 100644 --- a/PyRIGS/tests/pages.py +++ b/PyRIGS/tests/pages.py @@ -44,6 +44,7 @@ class FormPage(BasePage): submit = self.find_element(*self._submit_locator) ActionChains(self.driver).move_to_element(submit).perform() submit.click() + self.wait.until(animation_is_finished()) self.wait.until(lambda x: self.errors != previous_errors or self.success) @property @@ -73,3 +74,13 @@ class LoginPage(BasePage): password_element.send_keys(password) self.find_element(*self._submit_locator).click() + + +class animation_is_finished(): + def __call__(self, driver): + number_animating = driver.execute_script('return $(":animated").length') + finished = number_animating == 0 + if finished: + import time + time.sleep(0.1) + return finished \ No newline at end of file diff --git a/RIGS/tests/test_interaction.py b/RIGS/tests/test_interaction.py index 0b99a4bb..4048b730 100644 --- a/RIGS/tests/test_interaction.py +++ b/RIGS/tests/test_interaction.py @@ -10,8 +10,8 @@ from selenium.webdriver.support.ui import WebDriverWait from PyRIGS.tests import base from PyRIGS.tests import regions as base_regions -from PyRIGS.tests.base import (AutoLoginTest, animation_is_finished, - screenshot_failure_cls) +from PyRIGS.tests.base import (AutoLoginTest, screenshot_failure_cls) +from PyRIGS.tests.pages import animation_is_finished from RIGS import models from RIGS.tests import regions from . import pages diff --git a/RIGS/tests/test_unit.py b/RIGS/tests/test_unit.py index dbeaa8a4..5a798570 100644 --- a/RIGS/tests/test_unit.py +++ b/RIGS/tests/test_unit.py @@ -4,9 +4,11 @@ from django.core.exceptions import ObjectDoesNotExist from django.core.management import call_command from django.test import TestCase from django.test.utils import override_settings -from django.urls import reverse +from django.urls import reverse, reverse_lazy from django.utils import timezone +from pytest_django.asserts import assertRedirects +from PyRIGS.tests.base import assert_times_equal, response_contains from RIGS import models @@ -353,203 +355,105 @@ class TestSampleDataGenerator(TestCase): self.assertRaisesRegex(CommandError, ".*production", call_command, 'generateSampleRIGSData') -class TestSearchLogic(TestCase): - @classmethod - def setUpTestData(cls): - cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True, - is_active=True, is_staff=True) - - cls.persons = { - 1: models.Person.objects.create(name="Right Person", phone="1234"), - 2: models.Person.objects.create(name="Wrong Person", phone="5678"), - } - - cls.organisations = { - 1: models.Organisation.objects.create(name="Right Organisation", email="test@example.com"), - 2: models.Organisation.objects.create(name="Wrong Organisation", email="check@fake.co.uk"), - } - - cls.venues = { - 1: models.Venue.objects.create(name="Right Venue", address="1 Test Street, EX1"), - 2: models.Venue.objects.create(name="Wrong Venue", address="2 Check Way, TS2"), - } - - cls.events = { - 1: models.Event.objects.create(name="Right Event", start_date=date.today(), person=cls.persons[1], - organisation=cls.organisations[1], venue=cls.venues[1]), - 2: models.Event.objects.create(name="Wrong Event", start_date=date.today(), person=cls.persons[2], - organisation=cls.organisations[2], venue=cls.venues[2]), - } - - def setUp(self): - self.profile.set_password('testuser') - self.profile.save() - self.assertTrue(self.client.login(username=self.profile.username, password='testuser')) - - def test_event_search(self): - # Test search by name - request_url = "%s?q=%s" % (reverse('event_archive'), self.events[1].name) - response = self.client.get(request_url, follow=True) - self.assertContains(response, self.events[1].name) - self.assertNotContains(response, self.events[2].name) - - # Test search by ID - request_url = "%s?q=%s" % (reverse('event_archive'), self.events[1].pk) - response = self.client.get(request_url, follow=True) - self.assertContains(response, self.events[1].name) - self.assertNotContains(response, self.events[2].name) - - def test_people_search(self): - # Test search by name - request_url = "%s?q=%s" % (reverse('person_list'), self.persons[1].name) - response = self.client.get(request_url, follow=True) - self.assertContains(response, self.persons[1].name) - self.assertNotContains(response, self.persons[2].name) - - # Test search by ID - request_url = "%s?q=%s" % (reverse('person_list'), self.persons[1].pk) - response = self.client.get(request_url, follow=True) - self.assertContains(response, self.persons[1].name) - self.assertNotContains(response, self.persons[2].name) - - # Test search by phone - request_url = "%s?q=%s" % (reverse('person_list'), self.persons[1].phone) - response = self.client.get(request_url, follow=True) - self.assertContains(response, self.persons[1].name) - self.assertNotContains(response, self.persons[2].name) - - def test_organisation_search(self): - # Test search by name - request_url = "%s?q=%s" % (reverse('organisation_list'), self.organisations[1].name) - response = self.client.get(request_url, follow=True) - self.assertContains(response, self.organisations[1].name) - self.assertNotContains(response, self.organisations[2].name) - - # Test search by ID - request_url = "%s?q=%s" % (reverse('organisation_list'), self.organisations[1].pk) - response = self.client.get(request_url, follow=True) - self.assertContains(response, self.organisations[1].name) - self.assertNotContains(response, self.organisations[2].name) - - # Test search by email - request_url = "%s?q=%s" % (reverse('organisation_list'), self.organisations[1].email) - response = self.client.get(request_url, follow=True) - self.assertContains(response, self.organisations[1].email) - self.assertNotContains(response, self.organisations[2].email) - - def test_venue_search(self): - # Test search by name - request_url = "%s?q=%s" % (reverse('venue_list'), self.venues[1].name) - response = self.client.get(request_url, follow=True) - self.assertContains(response, self.venues[1].name) - self.assertNotContains(response, self.venues[2].name) - - # Test search by ID - request_url = "%s?q=%s" % (reverse('venue_list'), self.venues[1].pk) - response = self.client.get(request_url, follow=True) - self.assertContains(response, self.venues[1].name) - self.assertNotContains(response, self.venues[2].name) - - # Test search by address - request_url = "%s?q=%s" % (reverse('venue_list'), self.venues[1].address) - response = self.client.get(request_url, follow=True) - self.assertContains(response, self.venues[1].address) - self.assertNotContains(response, self.venues[2].address) +def search(client, url, found, notfound, arguments): + for argument in arguments: + query = getattr(found, argument) + request_url = "%s?q=%s" % (reverse_lazy(url), query) + response = client.get(request_url, follow=True) + assert response_contains(response, getattr(found, 'name')) + assert not response_contains(response, getattr(notfound, 'name')) -class TestHSLogic(TestCase): - @classmethod - def setUpTestData(cls): - cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True, - is_active=True, is_staff=True) +def test_search(admin_client): + persons = { + 1: models.Person.objects.create(name="Right Person", phone="1234"), + 2: models.Person.objects.create(name="Wrong Person", phone="5678"), + } + organisations = { + 1: models.Organisation.objects.create(name="Right Organisation", email="test@example.com"), + 2: models.Organisation.objects.create(name="Wrong Organisation", email="check@fake.co.uk"), + } + venues = { + 1: models.Venue.objects.create(name="Right Venue", address="1 Test Street, EX1"), + 2: models.Venue.objects.create(name="Wrong Venue", address="2 Check Way, TS2"), + } + events = { + 1: models.Event.objects.create(name="Right Event", start_date=date.today(), venue=venues[1], person=persons[1], + organisation=organisations[1]), + 2: models.Event.objects.create(name="Wrong Event", start_date=date.today(), venue=venues[2], person=persons[2], + organisation=organisations[2]), + } + search(admin_client, 'event_archive', events[1], events[2], ['name', 'id']) + search(admin_client, 'person_list', persons[1], persons[2], ['name', 'id', 'phone']) + search(admin_client, 'organisation_list', organisations[1], organisations[2], + ['name', 'id', 'email']) + search(admin_client, 'venue_list', venues[1], venues[2], + ['name', 'id', 'address']) - cls.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') - cls.venue = models.Venue.objects.create(name="Venue 1") - cls.events = { - 1: models.Event.objects.create(name="TE E1", start_date=date.today(), - description="This is an event description\nthat for a very specific reason spans two lines.", - venue=cls.venue), - 2: models.Event.objects.create(name="TE E2", start_date=date.today()), - } +def setup_for_hs(): + models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') + venue = models.Venue.objects.create(name="Venue 1") + return venue, { + 1: models.Event.objects.create(name="TE E1", start_date=date.today(), + description="This is an event description\nthat for a very specific reason spans two lines.", + venue=venue), + 2: models.Event.objects.create(name="TE E2", start_date=date.today()), + } - cls.ras = { - 1: models.RiskAssessment.objects.create(event=cls.events[1], - nonstandard_equipment=False, - nonstandard_use=False, - contractors=False, - other_companies=False, - crew_fatigue=False, - big_power=False, - power_mic=cls.profile, - generators=False, - other_companies_power=False, - nonstandard_equipment_power=False, - multiple_electrical_environments=False, - noise_monitoring=False, - known_venue=True, - safe_loading=True, - safe_storage=True, - area_outside_of_control=True, - barrier_required=True, - nonstandard_emergency_procedure=True, - special_structures=False, - suspended_structures=False, - outside=False), - } - cls.checklists = { - 1: models.EventChecklist.objects.create(event=cls.events[1], - power_mic=cls.profile, - safe_parking=False, - safe_packing=False, - exits=False, - trip_hazard=False, - warning_signs=False, - ear_plugs=False, - hs_location="Locked away safely", - extinguishers_location="Somewhere, I forgot", - earthing=False, - pat=False, - date=timezone.now(), - venue=cls.venue), - } +def create_ra(usr): + venue, events = setup_for_hs() + return models.RiskAssessment.objects.create(event=events[1], nonstandard_equipment=False, nonstandard_use=False, + contractors=False, other_companies=False, crew_fatigue=False, + big_power=False, power_mic=usr, generators=False, + other_companies_power=False, nonstandard_equipment_power=False, + multiple_electrical_environments=False, noise_monitoring=False, + known_venue=True, safe_loading=True, safe_storage=True, + area_outside_of_control=True, barrier_required=True, + nonstandard_emergency_procedure=True, special_structures=False, + suspended_structures=False, outside=False) - def setUp(self): - self.profile.set_password('testuser') - self.profile.save() - self.assertTrue(self.client.login(username=self.profile.username, password='testuser')) - def test_list(self): - request_url = reverse('hs_list') - response = self.client.get(request_url, follow=True) - self.assertContains(response, self.events[1].name) - self.assertContains(response, self.events[2].name) - self.assertContains(response, 'Create') +def create_checklist(usr): + venue, events = setup_for_hs() + return models.EventChecklist.objects.create(event=events[1], power_mic=usr, safe_parking=False, + safe_packing=False, exits=False, trip_hazard=False, warning_signs=False, + ear_plugs=False, hs_location="Locked away safely", + extinguishers_location="Somewhere, I forgot", earthing=False, pat=False, + date=timezone.now(), venue=venue) - def test_ra_review(self): - request_url = reverse('ra_review', kwargs={'pk': self.ras[1].pk}) - response = self.client.get(request_url, follow=True) - self.assertContains(response, 'Reviewed by') - self.assertContains(response, self.profile.name) - ra = models.RiskAssessment.objects.get(event=self.events[1]) - self.assertEqual(timezone.now().date(), ra.reviewed_at.date()) - self.assertEqual(timezone.now().hour, ra.reviewed_at.hour) - self.assertEqual(timezone.now().minute, ra.reviewed_at.minute) - def test_checklist_review(self): - request_url = reverse('ec_review', kwargs={'pk': self.checklists[1].pk}) - response = self.client.get(request_url, follow=True) - self.assertContains(response, 'Reviewed by') - self.assertContains(response, self.profile.name) - checklist = models.EventChecklist.objects.get(event=self.events[1]) - self.assertEqual(timezone.now().date(), checklist.reviewed_at.date()) - self.assertEqual(timezone.now().hour, checklist.reviewed_at.hour) - self.assertEqual(timezone.now().minute, checklist.reviewed_at.minute) +def test_list(admin_client): + venue, events = setup_for_hs() + request_url = reverse('hs_list') + response = admin_client.get(request_url, follow=True) + assert response_contains(response, events[1].name) + assert response_contains(response, events[2].name) + assert response_contains(response, 'Create') - def test_ra_redirect(self): - request_url = reverse('event_ra', kwargs={'pk': self.events[1].pk}) - expected_url = reverse('ra_edit', kwargs={'pk': self.ras[1].pk}) - response = self.client.get(request_url, follow=True) - self.assertRedirects(response, expected_url, status_code=302, target_status_code=200) +def review(client, profile, obj, request_url): + time = timezone.now() + response = client.get(reverse(request_url, kwargs={'pk': obj.pk}), follow=True) + obj.refresh_from_db() + assert response_contains(response, 'Reviewed by') + assert response_contains(response, profile.name) + assert_times_equal(time, obj.reviewed_at) + + +def test_ra_review(admin_client, admin_user): + review(admin_client, admin_user, create_ra(admin_user), 'ra_review') + + +def test_checklist_review(admin_client, admin_user): + review(admin_client, admin_user, create_checklist(admin_user), 'ec_review') + + +def test_ra_redirect(admin_client, admin_user): + ra = create_ra(admin_user) + request_url = reverse('event_ra', kwargs={'pk': ra.event.pk}) + expected_url = reverse('ra_edit', kwargs={'pk': ra.pk}) + + response = admin_client.get(request_url, follow=True) + assertRedirects(response, expected_url, status_code=302, target_status_code=200) diff --git a/assets/tests/pages.py b/assets/tests/pages.py index d21ed039..a0d9d365 100644 --- a/assets/tests/pages.py +++ b/assets/tests/pages.py @@ -3,9 +3,10 @@ from django.urls import reverse from pypom import Region from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions from PyRIGS.tests import regions -from PyRIGS.tests.pages import BasePage, FormPage +from PyRIGS.tests.pages import BasePage, FormPage, animation_is_finished class AssetList(BasePage): @@ -196,6 +197,7 @@ class AssetAuditList(AssetList): def search(self): self.find_element(*self._go_button_locator).click() + self.wait.until(animation_is_finished()) @property def error(self): @@ -237,13 +239,13 @@ class AssetAuditList(AssetList): 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) TODO + self.wait.until(expected_conditions.invisibility_of_element_located((By.ID, 'modal'))) def close(self): - previous_errors = self.errors self.page.find_element(*self._close_selector).click() + self.wait.until(expected_conditions.invisibility_of_element_located((By.ID, 'modal'))) def remove_all_required(self): self.driver.execute_script("Array.from(document.getElementsByTagName(\"input\")).forEach(function (el, ind, arr) { el.removeAttribute(\"required\")});") diff --git a/assets/tests/test_interaction.py b/assets/tests/test_interaction.py index 6c181306..b9f53a1a 100644 --- a/assets/tests/test_interaction.py +++ b/assets/tests/test_interaction.py @@ -2,11 +2,11 @@ import datetime from django.utils import timezone from selenium.webdriver.common.by import By -from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support import expected_conditions as ec from selenium.webdriver.support.ui import WebDriverWait -from PyRIGS.tests.base import AutoLoginTest, screenshot_failure_cls -from PyRIGS.tests.base import animation_is_finished +from PyRIGS.tests.base import AutoLoginTest, screenshot_failure_cls, assert_times_equal +from PyRIGS.tests.pages import animation_is_finished from assets import models from . import pages @@ -29,10 +29,10 @@ class TestAssetList(AutoLoginTest): def test_default_statuses_applied(self): # Only the working stuff should be shown initially - assetDescriptions = list(map(lambda x: x.description, self.page.assets)) - self.assertEqual(2, len(assetDescriptions)) - self.assertIn("A light", assetDescriptions) - self.assertIn("Working Mic", assetDescriptions) + asset_descriptions = list(map(lambda x: x.description, self.page.assets)) + self.assertEqual(2, len(asset_descriptions)) + self.assertIn("A light", asset_descriptions) + self.assertIn("Working Mic", asset_descriptions) def test_asset_order(self): # Only the working stuff should be shown initially @@ -42,11 +42,11 @@ class TestAssetList(AutoLoginTest): self.page.search() - assetIDs = list(map(lambda x: x.id, self.page.assets)) - self.assertEqual("1", assetIDs[0]) - self.assertEqual("2", assetIDs[1]) - self.assertEqual("10", assetIDs[2]) - self.assertEqual("C1", assetIDs[3]) + asset_ids = list(map(lambda x: x.id, self.page.assets)) + self.assertEqual("1", asset_ids[0]) + self.assertEqual("2", asset_ids[1]) + self.assertEqual("10", asset_ids[2]) + self.assertEqual("C1", asset_ids[3]) def test_search(self): self.page.set_query("10") @@ -84,9 +84,9 @@ class TestAssetList(AutoLoginTest): self.assertFalse(self.page.category_selector.is_open) self.page.search() self.assertTrue(len(self.page.assets) == 2) - assetIDs = list(map(lambda x: x.id, self.page.assets)) - self.assertEqual("1", assetIDs[0]) - self.assertEqual("10", assetIDs[1]) + asset_ids = list(map(lambda x: x.id, self.page.assets)) + self.assertEqual("1", asset_ids[0]) + self.assertEqual("10", asset_ids[1]) @screenshot_failure_cls @@ -99,7 +99,6 @@ class TestAssetForm(AutoLoginTest): self.parent = models.Asset.objects.create(asset_id="9000", description="Shelf", status=self.status, category=self.category, date_acquired=datetime.date(2000, 1, 1)) self.connector = models.Connector.objects.create(description="IEC", current_rating=10, voltage_rating=240, num_pins=3) self.cable_type = models.CableType.objects.create(plug=self.connector, socket=self.connector, circuits=1, cores=3) - self.wait = WebDriverWait(self.driver, 5) self.page = pages.AssetCreate(self.driver, self.live_server_url).open() def test_asset_create(self): @@ -141,7 +140,6 @@ class TestAssetForm(AutoLoginTest): self.assertFalse(self.driver.find_element_by_id('cable-table').is_displayed()) self.page.submit() - self.wait.until(animation_is_finished()) self.assertTrue(self.page.success) # Check that data is right asset = models.Asset.objects.get(asset_id="9001") @@ -285,48 +283,41 @@ class TestAssetAudit(AutoLoginTest): asset_id = "1111" self.page.set_query(asset_id) self.page.search() - self.wait.until(EC.visibility_of_element_located((By.ID, 'modal'))) + self.wait.until(ec.visibility_of_element_located((By.ID, 'modal'))) # Do it wrong on purpose to check error display self.page.modal.remove_all_required() self.page.modal.description = "" self.page.modal.submit() - self.wait.until(animation_is_finished()) - self.wait.until(EC.visibility_of_element_located((By.ID, 'modal'))) self.assertIn("This field is required.", self.page.modal.errors["Description"]) # Now do it properly self.page.modal.description = new_desc = "A BIG hammer" self.page.modal.submit() submit_time = timezone.now() - self.wait.until(EC.invisibility_of_element_located((By.ID, 'modal'))) self.assertFalse(self.driver.find_element_by_id('modal').is_displayed()) - # Check data is correct audited = models.Asset.objects.get(asset_id=asset_id) 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(submit_time.replace(microsecond=0), audited.last_audited_at.replace(microsecond=0)) + assert_times_equal(submit_time, audited.last_audited_at) # Check we've removed it from the 'needing audit' list self.assertNotIn(asset_id, self.page.assets) def test_audit_list(self): self.assertEqual(len(models.Asset.objects.filter(last_audited_at=None)), len(self.page.assets)) - asset_row = self.page.assets[0] self.driver.find_element(By.XPATH, "//a[contains(@class,'btn') and contains(., 'Audit')]").click() - self.wait.until(EC.visibility_of_element_located((By.ID, 'modal'))) + self.wait.until(ec.visibility_of_element_located((By.ID, 'modal'))) self.assertEqual(self.page.modal.asset_id, asset_row.id) self.page.modal.close() - self.wait.until(EC.invisibility_of_element_located((By.ID, 'modal'))) self.assertFalse(self.driver.find_element_by_id('modal').is_displayed()) # Make sure audit log was NOT filled out audited = models.Asset.objects.get(asset_id=asset_row.id) - self.assertEqual(None, audited.last_audited_by) + assert audited.last_audited_by is None def test_audit_search(self): # Check that a failed search works self.page.set_query("NOTFOUND") self.page.search() - self.wait.until(animation_is_finished()) self.assertFalse(self.driver.find_element_by_id('modal').is_displayed()) - self.assertIn("Asset with that ID does not exist!", self.page.error.text) + self.assertIn("Asset with that ID does not exist!", self.page.error.text) \ No newline at end of file diff --git a/assets/tests/test_unit.py b/assets/tests/test_unit.py index 1e86d1a6..352afbf4 100644 --- a/assets/tests/test_unit.py +++ b/assets/tests/test_unit.py @@ -6,19 +6,16 @@ from django.test.utils import override_settings from django.urls import reverse from pytest_django.asserts import assertFormError, assertRedirects +from PyRIGS.tests.base import response_contains from assets import models, urls pytestmark = pytest.mark.django_db # TODO -def response_contains(response, needle): - return needle in str(response.content) - - def login(client, django_user_model): pwd = 'testuser' usr = "TestUser" - profile = django_user_model.objects.create_user(username=usr, email="TestUser@test.com", password=pwd, is_superuser=True, is_active=True, is_staff=True) + django_user_model.objects.create_user(username=usr, email="TestUser@test.com", password=pwd, is_superuser=True, is_active=True, is_staff=True) assert client.login(username=usr, password=pwd)