diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index ccdf2e2d43..2d1b345687 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -1,5 +1,6 @@ from django.test import TestCase import django.core.exceptions as django_exceptions +from django.core.exceptions import ValidationError from .validators import validate_overage, validate_part_name from . import helpers @@ -100,3 +101,50 @@ class TestDownloadFile(TestCase): def test_download(self): helpers.DownloadFile("hello world", "out.txt") helpers.DownloadFile(bytes("hello world".encode("utf8")), "out.bin") + + +class TestSerialNumberExtraction(TestCase): + """ Tests for serial number extraction code """ + + def test_simple(self): + + e = helpers.ExtractSerialNumbers + + sn = e("1-5", 5) + self.assertEqual(len(sn), 5) + for i in range(1, 6): + self.assertIn(i, sn) + + sn = e("1, 2, 3, 4, 5", 5) + self.assertEqual(len(sn), 5) + + sn = e("1-5, 10-15", 11) + self.assertIn(3, sn) + self.assertIn(13, sn) + + def test_failures(self): + + e = helpers.ExtractSerialNumbers + + # Test duplicates + with self.assertRaises(ValidationError): + e("1,2,3,3,3", 5) + + # Test invalid length + with self.assertRaises(ValidationError): + e("1,2,3", 5) + + # Test empty string + with self.assertRaises(ValidationError): + e(", , ,", 0) + + # Test incorrect sign in group + with self.assertRaises(ValidationError): + e("10-2", 8) + + # Test invalid group + with self.assertRaises(ValidationError): + e("1-5-10", 10) + + with self.assertRaises(ValidationError): + e("10, a, 7-70j", 4) diff --git a/InvenTree/order/test_views.py b/InvenTree/order/test_views.py new file mode 100644 index 0000000000..a197455a8a --- /dev/null +++ b/InvenTree/order/test_views.py @@ -0,0 +1,77 @@ +""" Unit tests for Order views (see views.py) """ + +from django.test import TestCase +from django.urls import reverse +from django.contrib.auth import get_user_model + + +class OrderViewTestCase(TestCase): + + fixtures = [ + 'category', + 'part', + 'bom', + 'location', + 'company', + 'supplier_part', + 'stock', + 'order', + ] + + def setUp(self): + super().setUp() + + # Create a user + User = get_user_model() + User.objects.create_user('username', 'user@email.com', 'password') + + self.client.login(username='username', password='password') + + +class OrderListTest(OrderViewTestCase): + + def test_order_list(self): + response = self.client.get(reverse('purchase-order-index')) + + self.assertEqual(response.status_code, 200) + + +class POTests(OrderViewTestCase): + """ Tests for PurchaseOrder views """ + + def test_detail_view(self): + """ Retrieve PO detail view """ + response = self.client.get(reverse('purchase-order-detail', args=(1,))) + self.assertEqual(response.status_code, 200) + keys = response.context.keys() + self.assertIn('OrderStatus', keys) + + def test_po_create(self): + """ Launch forms to create new PurchaseOrder""" + url = reverse('purchase-order-create') + + # Without a supplier ID + response = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + # With a valid supplier ID + response = self.client.get(url, {'supplier': 1}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + # With an invalid supplier ID + response = self.client.get(url, {'supplier': 'goat'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + def test_po_edit(self): + """ Launch form to edit a PurchaseOrder """ + + response = self.client.get(reverse('purchase-order-edit', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + def test_po_export(self): + """ Export PurchaseOrder """ + + response = self.client.get(reverse('purchase-order-export', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest') + + # Response should be streaming-content (file download) + self.assertIn('streaming_content', dir(response)) diff --git a/InvenTree/order/views.py b/InvenTree/order/views.py index ae27a0bcc4..5b7aca5cdc 100644 --- a/InvenTree/order/views.py +++ b/InvenTree/order/views.py @@ -84,7 +84,7 @@ class PurchaseOrderCreate(AjaxCreateView): try: supplier = Company.objects.get(id=supplier_id) initials['supplier'] = supplier - except Company.DoesNotExist: + except (Company.DoesNotExist, ValueError): pass return initials diff --git a/InvenTree/part/fixtures/part.yaml b/InvenTree/part/fixtures/part.yaml index 7d669cf49d..3f7fbb9886 100644 --- a/InvenTree/part/fixtures/part.yaml +++ b/InvenTree/part/fixtures/part.yaml @@ -58,4 +58,4 @@ name: 'Bob' description: 'Can we build it?' assembly: true - \ No newline at end of file + purchaseable: false \ No newline at end of file diff --git a/InvenTree/part/test_views.py b/InvenTree/part/test_views.py new file mode 100644 index 0000000000..c1dc38862c --- /dev/null +++ b/InvenTree/part/test_views.py @@ -0,0 +1,218 @@ +""" Unit tests for Part Views (see views.py) """ + +from django.test import TestCase +from django.urls import reverse +from django.contrib.auth import get_user_model + +from .models import Part + + +class PartViewTestCase(TestCase): + + fixtures = [ + 'category', + 'part', + 'bom', + 'location', + 'company', + 'supplier_part', + ] + + def setUp(self): + super().setUp() + + # Create a user + User = get_user_model() + User.objects.create_user('username', 'user@email.com', 'password') + + self.client.login(username='username', password='password') + + +class PartListTest(PartViewTestCase): + + def test_part_index(self): + response = self.client.get(reverse('part-index')) + self.assertEqual(response.status_code, 200) + + keys = response.context.keys() + self.assertIn('csrf_token', keys) + self.assertIn('parts', keys) + self.assertIn('user', keys) + + def test_export(self): + """ Export part data to CSV """ + + response = self.client.get(reverse('part-export'), {'parts': '1,2,3,4,5,6,7,8,9,10'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + + self.assertEqual(response.status_code, 200) + self.assertIn('streaming_content', dir(response)) + + +class PartDetailTest(PartViewTestCase): + + def test_part_detail(self): + """ Test that we can retrieve a part detail page """ + + pk = 1 + + response = self.client.get(reverse('part-detail', args=(pk,))) + self.assertEqual(response.status_code, 200) + + part = Part.objects.get(pk=pk) + + keys = response.context.keys() + + self.assertIn('part', keys) + self.assertIn('category', keys) + + self.assertEqual(response.context['part'].pk, pk) + self.assertEqual(response.context['category'], part.category) + + self.assertFalse(response.context['editing_enabled']) + + def test_editable(self): + + pk = 1 + response = self.client.get(reverse('part-detail', args=(pk,)), {'edit': True}) + + self.assertEqual(response.status_code, 200) + self.assertTrue(response.context['editing_enabled']) + + def test_bom_download(self): + """ Test downloading a BOM for a valid part """ + + response = self.client.get(reverse('bom-export', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + self.assertIn('streaming_content', dir(response)) + + +class PartTests(PartViewTestCase): + """ Tests for Part forms """ + + def test_part_edit(self): + response = self.client.get(reverse('part-edit', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + keys = response.context.keys() + data = str(response.content) + + self.assertIn('part', keys) + self.assertIn('csrf_token', keys) + + self.assertIn('html_form', data) + self.assertIn('"title":', data) + + def test_part_create(self): + """ Launch form to create a new part """ + response = self.client.get(reverse('part-create'), {'category': 1}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + # And again, with an invalid category + response = self.client.get(reverse('part-create'), {'category': 9999}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + # And again, with no category + response = self.client.get(reverse('part-create'), {'name': 'Test part'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + def test_part_duplicate(self): + """ Launch form to duplicate part """ + + # First try with an invalid part + response = self.client.get(reverse('part-duplicate', args=(9999,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + response = self.client.get(reverse('part-duplicate', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + def test_make_variant(self): + + response = self.client.get(reverse('make-part-variant', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + +class PartAttachmentTests(PartViewTestCase): + + def test_valid_create(self): + """ test creation of an attachment for a valid part """ + + response = self.client.get(reverse('part-attachment-create'), {'part': 1}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + def test_invalid_create(self): + """ test creation of an attachment for an invalid part """ + + with self.assertRaises(Part.DoesNotExist): + self.client.get(reverse('part-attachment-create'), {'part': 999}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + + def test_edit(self): + """ test editing an attachment """ + + # TODO + pass + + +class PartQRTest(PartViewTestCase): + """ Tests for the Part QR Code AJAX view """ + + def test_html_redirect(self): + # A HTML request for a QR code should be redirected (use an AJAX request instead) + response = self.client.get(reverse('part-qr', args=(1,))) + self.assertEqual(response.status_code, 302) + + def test_valid_part(self): + response = self.client.get(reverse('part-qr', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + data = str(response.content) + + self.assertIn('Part QR Code', data) + self.assertIn('