From 6d47be72fec6932841e9214f10b621df59d25d99 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Wed, 27 May 2020 01:47:10 +0100 Subject: [PATCH] Added screenshot recording of test failures --- .gitignore | 1 + PyRIGS/settings.py | 3 +++ PyRIGS/tests/base.py | 37 +++++++++++++++++++++++++++++++++++ RIGS/tests/test_functional.py | 18 ++++++++--------- assets/tests/test_assets.py | 26 ++++++++++++------------ requirements.txt | 1 + 6 files changed, 65 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 436fd2e9..1cf59cc5 100644 --- a/.gitignore +++ b/.gitignore @@ -110,3 +110,4 @@ crashlytics.properties crashlytics-build.properties .vscode/ /package-lock.json +screenshots/ \ No newline at end of file diff --git a/PyRIGS/settings.py b/PyRIGS/settings.py index 77e6c557..58e67073 100644 --- a/PyRIGS/settings.py +++ b/PyRIGS/settings.py @@ -247,3 +247,6 @@ RISK_ASSESSMENT_URL = os.environ.get('RISK_ASSESSMENT_URL') if os.environ.get( 'RISK_ASSESSMENT_URL') else "http://example.com" RISK_ASSESSMENT_SECRET = os.environ.get('RISK_ASSESSMENT_SECRET') if os.environ.get( 'RISK_ASSESSMENT_SECRET') else secrets.token_hex(15) + +IMGUR_UPLOAD_CLIENT_ID = os.environ.get('IMGUR_UPLOAD_CLIENT_ID', '') +IMGUR_UPLOAD_CLIENT_SECRET = os.environ.get('IMGUR_UPLOAD_CLIENT_SECRET', '') \ No newline at end of file diff --git a/PyRIGS/tests/base.py b/PyRIGS/tests/base.py index 114bbcc6..3bdbf1a7 100644 --- a/PyRIGS/tests/base.py +++ b/PyRIGS/tests/base.py @@ -6,6 +6,11 @@ import os import pytz from datetime import date, time, datetime, timedelta from django.conf import settings +import imgurpython +import PyRIGS.settings +import sys +import pathlib +import inspect def create_datetime(year, month, day, hour, min): @@ -47,6 +52,38 @@ class AutoLoginTest(BaseTest): loginPage.login("EventTest", "EventTestPassword") +def screenshot_failure(func): + def wrapper_func(self, *args, **kwargs): + try: + func(self, *args, **kwargs) + except Exception as e: + screenshot_name = func.__module__ + "." + func.__qualname__ + screenshot_file = "screenshots/"+func.__qualname__+".png" + if not pathlib.Path("screenshots").is_dir(): + os.mkdir("screenshots") + self.driver.save_screenshot(screenshot_file) + + if settings.IMGUR_UPLOAD_CLIENT_ID != "": + config = { + 'album': None, + 'name': screenshot_name, + 'title': screenshot_name, + 'description': "" + } + client = imgurpython.ImgurClient(settings.IMGUR_UPLOAD_CLIENT_ID, settings.IMGUR_UPLOAD_CLIENT_SECRET) + image = client.upload_from_path(screenshot_file, config=config) + print("Error in test {} is at url {}".format(screenshot_name, image['link']), file=sys.stderr) + else: + print("Error in test {} is at path {}".format(screenshot_name, screenshot_file), file=sys.stderr) + raise e + return wrapper_func + +def screenshot_failure_cls(cls): + for attr in cls.__dict__: + if callable(getattr(cls, attr)) and attr.startswith("test"): + setattr(cls, attr, screenshot_failure(getattr(cls, attr))) + return cls + # Checks if animation is done class animation_is_finished(object): def __init__(self): diff --git a/RIGS/tests/test_functional.py b/RIGS/tests/test_functional.py index 4ccb7178..6658449d 100644 --- a/RIGS/tests/test_functional.py +++ b/RIGS/tests/test_functional.py @@ -24,7 +24,7 @@ from django.core import mail, signing from django.http import HttpResponseBadRequest from django.conf import settings - +@screenshot_failure_cls class BaseRigboardTest(AutoLoginTest): def setUp(self): self.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1') @@ -39,7 +39,7 @@ class BaseRigboardTest(AutoLoginTest): self.wait.until(animation_is_finished()) self.assertTrue(self.page.is_expanded) - +@screenshot_failure_cls class TestRigboard(BaseRigboardTest): def setUp(self): super().setUp() @@ -100,7 +100,7 @@ class TestRigboard(BaseRigboardTest): self.assertIn('create', self.driver.current_url) # Ideally get a response object to assert 200 on - +@screenshot_failure_cls class TestEventCreate(BaseRigboardTest): def setUp(self): super().setUp() @@ -328,7 +328,7 @@ class TestEventCreate(BaseRigboardTest): def test_subhire_creation(self): pass - +@screenshot_failure_cls class TestEventDuplicate(BaseRigboardTest): def setUp(self): super().setUp() @@ -427,7 +427,7 @@ class TestEventDuplicate(BaseRigboardTest): self.assertIn("Test Item 2", table.text) self.assertNotIn("Test Item 3", table.text) - +@screenshot_failure_cls class TestEventEdit(BaseRigboardTest): def setUp(self): super().setUp() @@ -487,7 +487,7 @@ class TestEventEdit(BaseRigboardTest): table = self.page.item_table self.assertIn("Test Item 3", table.text) - +@screenshot_failure_cls class TestEventDetail(BaseRigboardTest): def setUp(self): super().setUp() @@ -523,7 +523,7 @@ class TestEventDetail(BaseRigboardTest): self.assertEqual(self.client.email, self.page.email) self.assertEqual(self.client.phone, None) - +@screenshot_failure_cls class TestCalendar(BaseRigboardTest): def setUp(self): super().setUp() @@ -708,7 +708,7 @@ class TestCalendar(BaseRigboardTest): # Wow - that was a lot of tests - +@screenshot_failure_cls class ClientEventAuthorisationTest(TestCase): auth_data = { 'name': 'Test ABC', @@ -814,7 +814,7 @@ class ClientEventAuthorisationTest(TestCase): self.assertEqual(mail.outbox[0].to, ['authemail@function.test']) self.assertEqual(mail.outbox[1].to, [settings.AUTHORISATION_NOTIFICATION_ADDRESS]) - +@screenshot_failure_cls class TECEventAuthorisationTest(TestCase): @classmethod def setUpTestData(cls): diff --git a/assets/tests/test_assets.py b/assets/tests/test_assets.py index 4519926e..1c0c8692 100644 --- a/assets/tests/test_assets.py +++ b/assets/tests/test_assets.py @@ -6,7 +6,7 @@ from django.test.utils import override_settings from django.urls import reverse from urllib.parse import urlparse from RIGS import models as rigsmodels -from PyRIGS.tests.base import BaseTest, AutoLoginTest +from PyRIGS.tests.base import BaseTest, AutoLoginTest, screenshot_failure_cls from assets import models, urls from reversion import revisions as reversion from selenium.webdriver.support import expected_conditions as EC @@ -17,7 +17,7 @@ from PyRIGS.tests.base import animation_is_finished import datetime from django.utils import timezone - +@screenshot_failure_cls class TestAssetList(AutoLoginTest): def setUp(self): super().setUp() @@ -40,6 +40,7 @@ class TestAssetList(AutoLoginTest): self.assertIn("A light", assetDescriptions) self.assertIn("Working Mic", assetDescriptions) + def test_asset_order(self): # Only the working stuff should be shown initially self.page.status_selector.open() @@ -94,7 +95,7 @@ class TestAssetList(AutoLoginTest): self.assertEqual("1", assetIDs[0]) self.assertEqual("10", assetIDs[1]) - +@screenshot_failure_cls class TestAssetForm(AutoLoginTest): def setUp(self): super().setUp() @@ -201,7 +202,7 @@ class TestAssetForm(AutoLoginTest): self.assertTrue(self.page.success) self.assertEqual(models.Asset.objects.last().description, self.parent.description) - +@screenshot_failure_cls class TestSupplierList(AutoLoginTest): def setUp(self): super().setUp() @@ -240,7 +241,7 @@ class TestSupplierList(AutoLoginTest): self.page.search() self.assertTrue(len(self.page.suppliers) == 0) - +@screenshot_failure_cls class TestSupplierCreateAndEdit(AutoLoginTest): def setUp(self): super().setUp() @@ -267,7 +268,7 @@ class TestSupplierCreateAndEdit(AutoLoginTest): self.page.submit() self.assertTrue(self.page.success) - +@screenshot_failure_cls class TestAssetAudit(AutoLoginTest): def setUp(self): super().setUp() @@ -337,7 +338,7 @@ class TestAssetAudit(AutoLoginTest): self.assertFalse(self.driver.find_element_by_id('modal').is_displayed()) self.assertIn("Asset with that ID does not exist!", self.page.error.text) - +@screenshot_failure_cls class TestSupplierValidation(TestCase): @classmethod def setUpTestData(cls): @@ -359,7 +360,7 @@ class TestSupplierValidation(TestCase): response = self.client.post(url, {'name': ""}) self.assertFormError(response, 'form', 'name', 'This field is required.') - +@screenshot_failure_cls class Test404(TestCase): @classmethod def setUpTestData(cls): @@ -379,6 +380,7 @@ class Test404(TestCase): # @tag('slow') TODO: req. Django 3.0 +@screenshot_failure_cls class TestAccessLevels(TestCase): @override_settings(DEBUG=True) def setUp(self): @@ -448,7 +450,7 @@ class TestAccessLevels(TestCase): # def test_finance_access(self): Level not used in assets currently - +@screenshot_failure_cls class TestFormValidation(TestCase): @classmethod def setUpTestData(cls): @@ -516,7 +518,7 @@ class TestFormValidation(TestCase): self.assertFormError(response, 'form', 'length', 'The length of a cable must be more than 0') self.assertFormError(response, 'form', 'csa', 'The CSA of a cable must be more than 0') - +@screenshot_failure_cls class TestSampleDataGenerator(TestCase): @override_settings(DEBUG=True) def test_generate_sample_data(self): @@ -540,7 +542,7 @@ class TestSampleDataGenerator(TestCase): self.assertRaisesRegex(CommandError, ".*production", call_command, 'generateSampleAssetsData') self.assertRaisesRegex(CommandError, ".*production", call_command, 'deleteSampleData') - +@screenshot_failure_cls class TestVersioningViews(TestCase): @classmethod def setUpTestData(cls): @@ -583,7 +585,7 @@ class TestVersioningViews(TestCase): response = self.client.get(request_url, follow=True) self.assertEqual(response.status_code, 200) - +@screenshot_failure_cls class TestEmbeddedViews(TestCase): @classmethod def setUpTestData(cls): diff --git a/requirements.txt b/requirements.txt index 2a602d9c..db40565d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,3 +23,4 @@ simplejson==3.17.0 whitenoise==5.0.1 reportlab==3.4.0 z3c.rml==3.9.1 +imgurpython==1.1.7 \ No newline at end of file