diff --git a/InvenTree/InvenTree/api_tester.py b/InvenTree/InvenTree/api_tester.py index 368ca3a765..935252de5b 100644 --- a/InvenTree/InvenTree/api_tester.py +++ b/InvenTree/InvenTree/api_tester.py @@ -13,10 +13,7 @@ from django.http.response import StreamingHttpResponse from rest_framework.test import APITestCase -class InvenTreeAPITestCase(APITestCase): - """ - Base class for running InvenTree API tests - """ +class UserMixin: # User information username = 'testuser' @@ -53,37 +50,49 @@ class InvenTreeAPITestCase(APITestCase): self.user.save() - for role in self.roles: - self.assignRole(role) + # Assign all roles if set + if self.roles == 'all': + self.assignRole(assign_all=True) + # else filter the roles + else: + for role in self.roles: + self.assignRole(role) if self.auto_login: self.client.login(username=self.username, password=self.password) - def assignRole(self, role): + def assignRole(self, role=None, assign_all: bool = False): """ Set the user roles for the registered user """ # role is of the format 'rule.permission' e.g. 'part.add' - rule, perm = role.split('.') + if not assign_all and role: + rule, perm = role.split('.') for ruleset in self.group.rule_sets.all(): - if ruleset.name == rule: + if assign_all or ruleset.name == rule: - if perm == 'view': + if assign_all or perm == 'view': ruleset.can_view = True - elif perm == 'change': + elif assign_all or perm == 'change': ruleset.can_change = True - elif perm == 'delete': + elif assign_all or perm == 'delete': ruleset.can_delete = True - elif perm == 'add': + elif assign_all or perm == 'add': ruleset.can_add = True ruleset.save() break + +class InvenTreeAPITestCase(UserMixin, APITestCase): + """ + Base class for running InvenTree API tests + """ + def getActions(self, url): """ Return a dict of the 'actions' available at a given endpoint. diff --git a/InvenTree/InvenTree/ci_render_js.py b/InvenTree/InvenTree/ci_render_js.py index 2c9d05dfc4..824fe5d7e3 100644 --- a/InvenTree/InvenTree/ci_render_js.py +++ b/InvenTree/InvenTree/ci_render_js.py @@ -4,13 +4,12 @@ only used for testing the js files! - This file is omited from coverage """ import os # pragma: no cover -import pathlib # pragma: no cover +import pathlib -from django.contrib.auth import get_user_model # pragma: no cover -from django.test import TestCase # pragma: no cover +from InvenTree.helpers import InvenTreeTestCase # pragma: no cover -class RenderJavascriptFiles(TestCase): # pragma: no cover +class RenderJavascriptFiles(InvenTreeTestCase): # pragma: no cover """ A unit test to "render" javascript files. @@ -18,18 +17,6 @@ class RenderJavascriptFiles(TestCase): # pragma: no cover we need the fully-rendered files for linting and static tests. """ - def setUp(self): - - user = get_user_model() - - self.user = user.objects.create_user( - username='testuser', - password='testpassword', - email='user@gmail.com', - ) - - self.client.login(username='testuser', password='testpassword') - def download_file(self, filename, prefix): url = os.path.join(prefix, filename) diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index d5722a2e99..c541ce4ef5 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -12,6 +12,7 @@ from wsgiref.util import FileWrapper from django.contrib.auth.models import Permission from django.core.exceptions import FieldError, ValidationError from django.http import StreamingHttpResponse +from django.test import TestCase from django.utils.translation import gettext_lazy as _ from djmoney.money import Money @@ -21,6 +22,7 @@ import InvenTree.version from common.models import InvenTreeSetting from common.settings import currency_code_default +from .api_tester import UserMixin from .settings import MEDIA_URL, STATIC_URL @@ -779,3 +781,7 @@ def inheritors(cls): subcls.add(child) work.append(child) return subcls + + +class InvenTreeTestCase(UserMixin, TestCase): + pass diff --git a/InvenTree/InvenTree/test_api.py b/InvenTree/InvenTree/test_api.py index b01c939d1c..889ff674b3 100644 --- a/InvenTree/InvenTree/test_api.py +++ b/InvenTree/InvenTree/test_api.py @@ -2,18 +2,16 @@ from base64 import b64encode -from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group -from django.test import TestCase from django.urls import reverse from rest_framework import status from InvenTree.api_tester import InvenTreeAPITestCase +from InvenTree.helpers import InvenTreeTestCase from users.models import RuleSet -class HTMLAPITests(TestCase): +class HTMLAPITests(InvenTreeTestCase): """ Test that we can access the REST API endpoints via the HTML interface. @@ -21,33 +19,7 @@ class HTMLAPITests(TestCase): which raised an AssertionError when using the HTML API interface, while the regular JSON interface continued to work as expected. """ - - def setUp(self): - super().setUp() - - # Create a user - user = get_user_model() - - self.user = user.objects.create_user( - username='username', - email='user@email.com', - password='password' - ) - - # Put the user into a group with the correct permissions - group = Group.objects.create(name='mygroup') - self.user.groups.add(group) - - # Give the group *all* the permissions! - for rule in group.rule_sets.all(): - rule.can_view = True - rule.can_change = True - rule.can_add = True - rule.can_delete = True - - rule.save() - - self.client.login(username='username', password='password') + roles = 'all' def test_part_api(self): url = reverse('api-part-list') diff --git a/InvenTree/InvenTree/test_middleware.py b/InvenTree/InvenTree/test_middleware.py index 0e86ec6a0f..e9e5e4846f 100644 --- a/InvenTree/InvenTree/test_middleware.py +++ b/InvenTree/InvenTree/test_middleware.py @@ -1,11 +1,11 @@ """Tests for middleware functions""" -from django.contrib.auth import get_user_model -from django.test import TestCase from django.urls import reverse +from InvenTree.helpers import InvenTreeTestCase -class MiddlewareTests(TestCase): + +class MiddlewareTests(InvenTreeTestCase): """Test for middleware functions""" def check_path(self, url, code=200, **kwargs): @@ -13,15 +13,6 @@ class MiddlewareTests(TestCase): self.assertEqual(response.status_code, code) return response - def setUp(self): - super().setUp() - - # Create a user - user = get_user_model() - - self.user = user.objects.create_user(username='username', email='user@email.com', password='password') - self.client.login(username='username', password='password') - def test_AuthRequiredMiddleware(self): """Test the auth middleware""" diff --git a/InvenTree/InvenTree/test_views.py b/InvenTree/InvenTree/test_views.py index 509eea142e..425de5e46c 100644 --- a/InvenTree/InvenTree/test_views.py +++ b/InvenTree/InvenTree/test_views.py @@ -5,28 +5,17 @@ Unit tests for the main web views import os import re -from django.contrib.auth import get_user_model -from django.test import TestCase from django.urls import reverse +from InvenTree.helpers import InvenTreeTestCase -class ViewTests(TestCase): + +class ViewTests(InvenTreeTestCase): """ Tests for various top-level views """ username = 'test_user' password = 'test_pass' - def setUp(self): - - # Create a user - self.user = get_user_model().objects.create_user(self.username, 'user@email.com', self.password) - self.user.set_password(self.password) - self.user.save() - - result = self.client.login(username=self.username, password=self.password) - - self.assertEqual(result, True) - def test_api_doc(self): """ Test that the api-doc view works """ diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index fe13d75562..5bb97ae934 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -450,18 +450,12 @@ class TestStatus(TestCase): self.assertEqual(ready.isImportingData(), False) -class TestSettings(TestCase): +class TestSettings(helpers.InvenTreeTestCase): """ Unit tests for settings """ - def setUp(self) -> None: - self.user_mdl = get_user_model() - - # Create a user for auth - user = get_user_model() - self.user = user.objects.create_superuser('testuser1', 'test1@testing.com', 'password1') - self.client.login(username='testuser1', password='password1') + superuser = True def in_env_context(self, envs={}): """Patch the env to include the given dict""" @@ -476,8 +470,9 @@ class TestSettings(TestCase): @override_settings(TESTING_ENV=True) def test_set_user_to_few(self): + user_model = get_user_model() # add shortcut - user_count = self.user_mdl.objects.count + user_count = user_model.objects.count # enable testing mode settings.TESTING_ENV = True @@ -499,14 +494,18 @@ class TestSettings(TestCase): }) self.assertEqual(user_count(), 2) + username2 = 'testuser1' + email2 = 'test1@testing.com' + password2 = 'password1' + # create user manually - self.user_mdl.objects.create_user('testuser', 'test@testing.com', 'password') + user_model.objects.create_user(username2, email2, password2) self.assertEqual(user_count(), 3) # check it will not be created again self.run_reload({ - 'INVENTREE_ADMIN_USER': 'testuser', - 'INVENTREE_ADMIN_EMAIL': 'test@testing.com', - 'INVENTREE_ADMIN_PASSWORD': 'password', + 'INVENTREE_ADMIN_USER': username2, + 'INVENTREE_ADMIN_EMAIL': email2, + 'INVENTREE_ADMIN_PASSWORD': password2, }) self.assertEqual(user_count(), 3) @@ -541,7 +540,7 @@ class TestSettings(TestCase): # with env set with self.in_env_context({'INVENTREE_CONFIG_FILE': 'my_special_conf.yaml'}): - self.assertIn('inventree/inventree/my_special_conf.yaml', config.get_config_file().lower()) + self.assertIn('inventree/my_special_conf.yaml', config.get_config_file().lower()) def test_helpers_plugin_file(self): # normal run - not configured @@ -567,18 +566,11 @@ class TestSettings(TestCase): self.assertEqual(config.get_setting(TEST_ENV_NAME, None), '321') -class TestInstanceName(TestCase): +class TestInstanceName(helpers.InvenTreeTestCase): """ Unit tests for instance name """ - def setUp(self): - # Create a user for auth - user = get_user_model() - self.user = user.objects.create_superuser('testuser', 'test@testing.com', 'password') - - self.client.login(username='testuser', password='password') - def test_instance_name(self): # default setting diff --git a/InvenTree/build/test_api.py b/InvenTree/build/test_api.py index 4ba54e9c73..1adf38935e 100644 --- a/InvenTree/build/test_api.py +++ b/InvenTree/build/test_api.py @@ -2,10 +2,6 @@ from datetime import datetime, timedelta from django.urls import reverse -from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group - -from rest_framework.test import APITestCase from rest_framework import status from part.models import Part @@ -16,7 +12,7 @@ from InvenTree.status_codes import BuildStatus from InvenTree.api_tester import InvenTreeAPITestCase -class TestBuildAPI(APITestCase): +class TestBuildAPI(InvenTreeAPITestCase): """ Series of tests for the Build DRF API - Tests for Build API @@ -30,25 +26,11 @@ class TestBuildAPI(APITestCase): 'build', ] - def setUp(self): - # Create a user for auth - user = get_user_model() - self.user = user.objects.create_user('testuser', 'test@testing.com', 'password') - - g = Group.objects.create(name='builders') - self.user.groups.add(g) - - for rule in g.rule_sets.all(): - if rule.name == 'build': - rule.can_change = True - rule.can_add = True - rule.can_delete = True - - rule.save() - - g.save() - - self.client.login(username='testuser', password='password') + roles = [ + 'build.change', + 'build.add', + 'build.delete', + ] def test_get_build_list(self): """ diff --git a/InvenTree/build/tests.py b/InvenTree/build/tests.py index d3c0c41112..8bd7553a52 100644 --- a/InvenTree/build/tests.py +++ b/InvenTree/build/tests.py @@ -1,18 +1,16 @@ -from django.test import TestCase from django.urls import reverse -from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group - from datetime import datetime, timedelta +from InvenTree.helpers import InvenTreeTestCase + from .models import Build from stock.models import StockItem from InvenTree.status_codes import BuildStatus -class BuildTestSimple(TestCase): +class BuildTestSimple(InvenTreeTestCase): fixtures = [ 'category', @@ -21,27 +19,11 @@ class BuildTestSimple(TestCase): 'build', ] - def setUp(self): - # Create a user for auth - user = get_user_model() - user.objects.create_user('testuser', 'test@testing.com', 'password') - - self.user = user.objects.get(username='testuser') - - g = Group.objects.create(name='builders') - self.user.groups.add(g) - - for rule in g.rule_sets.all(): - if rule.name == 'build': - rule.can_change = True - rule.can_add = True - rule.can_delete = True - - rule.save() - - g.save() - - self.client.login(username='testuser', password='password') + roles = [ + 'build.change', + 'build.add', + 'build.delete', + ] def test_build_objects(self): # Ensure the Build objects were correctly created @@ -106,7 +88,7 @@ class BuildTestSimple(TestCase): self.assertEqual(build.status, BuildStatus.CANCELLED) -class TestBuildViews(TestCase): +class TestBuildViews(InvenTreeTestCase): """ Tests for Build app views """ fixtures = [ @@ -116,28 +98,15 @@ class TestBuildViews(TestCase): 'build', ] + roles = [ + 'build.change', + 'build.add', + 'build.delete', + ] + def setUp(self): super().setUp() - # Create a user - user = get_user_model() - self.user = user.objects.create_user('username', 'user@email.com', 'password') - - g = Group.objects.create(name='builders') - self.user.groups.add(g) - - for rule in g.rule_sets.all(): - if rule.name == 'build': - rule.can_change = True - rule.can_add = True - rule.can_delete = True - - rule.save() - - g.save() - - self.client.login(username='username', password='password') - # Create a build output for build # 1 self.build = Build.objects.get(pk=1) diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index 5820a6cda8..66f9846043 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -3,12 +3,11 @@ import json from datetime import timedelta from http import HTTPStatus -from django.contrib.auth import get_user_model from django.test import Client, TestCase from django.urls import reverse from InvenTree.api_tester import InvenTreeAPITestCase -from InvenTree.helpers import str2bool +from InvenTree.helpers import InvenTreeTestCase, str2bool from plugin import registry from plugin.models import NotificationUserSetting, PluginConfig @@ -19,7 +18,7 @@ from .models import (ColorTheme, InvenTreeSetting, InvenTreeUserSetting, CONTENT_TYPE_JSON = 'application/json' -class SettingsTest(TestCase): +class SettingsTest(InvenTreeTestCase): """ Tests for the 'settings' model """ @@ -28,16 +27,6 @@ class SettingsTest(TestCase): 'settings', ] - def setUp(self): - - user = get_user_model() - - self.user = user.objects.create_user('username', 'user@email.com', 'password') - self.user.is_staff = True - self.user.save() - - self.client.login(username='username', password='password') - def test_settings_objects(self): # There should be two settings objects in the database diff --git a/InvenTree/company/test_views.py b/InvenTree/company/test_views.py index a39e2be041..a3ecd1651a 100644 --- a/InvenTree/company/test_views.py +++ b/InvenTree/company/test_views.py @@ -1,12 +1,11 @@ """ Unit tests for Company views (see views.py) """ -from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group -from django.test import TestCase from django.urls import reverse +from InvenTree.helpers import InvenTreeTestCase -class CompanyViewTestBase(TestCase): + +class CompanyViewTestBase(InvenTreeTestCase): fixtures = [ 'category', @@ -17,32 +16,7 @@ class CompanyViewTestBase(TestCase): 'supplier_part', ] - def setUp(self): - super().setUp() - - # Create a user - user = get_user_model() - - self.user = user.objects.create_user( - username='username', - email='user@email.com', - password='password' - ) - - # Put the user into a group with the correct permissions - group = Group.objects.create(name='mygroup') - self.user.groups.add(group) - - # Give the group *all* the permissions! - for rule in group.rule_sets.all(): - rule.can_view = True - rule.can_change = True - rule.can_add = True - rule.can_delete = True - - rule.save() - - self.client.login(username='username', password='password') + roles = 'all' class CompanyViewTest(CompanyViewTestBase): diff --git a/InvenTree/order/templates/order/sales_order_base.html b/InvenTree/order/templates/order/sales_order_base.html index aa51c04f39..1e7ddc168f 100644 --- a/InvenTree/order/templates/order/sales_order_base.html +++ b/InvenTree/order/templates/order/sales_order_base.html @@ -57,6 +57,7 @@ src="{% static 'img/blank_image.png' %}" @@ -223,6 +224,16 @@ $("#edit-order").click(function() { }); }); +$("#complete-order-shipments").click(function() { + + completePendingShipments( + {{ order.pk }}, + { + reload: true, + } + ); +}); + $("#cancel-order").click(function() { cancelSalesOrder( diff --git a/InvenTree/order/test_views.py b/InvenTree/order/test_views.py index b1025959f1..aad0fed25d 100644 --- a/InvenTree/order/test_views.py +++ b/InvenTree/order/test_views.py @@ -1,12 +1,11 @@ """ Unit tests for Order views (see views.py) """ -from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group -from django.test import TestCase from django.urls import reverse +from InvenTree.helpers import InvenTreeTestCase -class OrderViewTestCase(TestCase): + +class OrderViewTestCase(InvenTreeTestCase): fixtures = [ 'category', @@ -19,27 +18,14 @@ class OrderViewTestCase(TestCase): 'order', ] - def setUp(self): - super().setUp() - - # Create a user - user = get_user_model().objects.create_user('username', 'user@email.com', 'password') - - # Ensure that the user has the correct permissions! - g = Group.objects.create(name='orders') - user.groups.add(g) - - for rule in g.rule_sets.all(): - if rule.name in ['purchase_order', 'sales_order']: - rule.can_change = True - rule.can_add = True - rule.can_delete = True - - rule.save() - - g.save() - - self.client.login(username='username', password='password') + roles = [ + 'purchase_order.change', + 'purchase_order.add', + 'purchase_order.delete', + 'sales_order.change', + 'sales_order.add', + 'sales_order.delete', + ] class OrderListTest(OrderViewTestCase): diff --git a/InvenTree/part/test_bom_export.py b/InvenTree/part/test_bom_export.py index 04e666f248..d78ced4b17 100644 --- a/InvenTree/part/test_bom_export.py +++ b/InvenTree/part/test_bom_export.py @@ -4,13 +4,12 @@ Unit testing for BOM export functionality import csv -from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group -from django.test import TestCase from django.urls import reverse +from InvenTree.helpers import InvenTreeTestCase -class BomExportTest(TestCase): + +class BomExportTest(InvenTreeTestCase): fixtures = [ 'category', @@ -19,33 +18,11 @@ class BomExportTest(TestCase): 'bom', ] + roles = 'all' + def setUp(self): super().setUp() - # Create a user - user = get_user_model() - - self.user = user.objects.create_user( - username='username', - email='user@email.com', - password='password' - ) - - # Put the user into a group with the correct permissions - group = Group.objects.create(name='mygroup') - self.user.groups.add(group) - - # Give the group *all* the permissions! - for rule in group.rule_sets.all(): - rule.can_view = True - rule.can_change = True - rule.can_add = True - rule.can_delete = True - - rule.save() - - self.client.login(username='username', password='password') - self.url = reverse('bom-download', kwargs={'pk': 100}) def test_bom_template(self): diff --git a/InvenTree/part/test_part.py b/InvenTree/part/test_part.py index 293f13d63f..e6d01e9aa9 100644 --- a/InvenTree/part/test_part.py +++ b/InvenTree/part/test_part.py @@ -3,7 +3,6 @@ import os from django.conf import settings -from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError from django.test import TestCase @@ -14,21 +13,16 @@ from common.models import (InvenTreeSetting, InvenTreeUserSetting, NotificationEntry, NotificationMessage) from common.notifications import UIMessageNotification, storage from InvenTree import version +from InvenTree.helpers import InvenTreeTestCase from .models import (Part, PartCategory, PartCategoryStar, PartStar, PartTestTemplate, rename_part_image) from .templatetags import inventree_extras -class TemplateTagTest(TestCase): +class TemplateTagTest(InvenTreeTestCase): """ Tests for the custom template tag code """ - def setUp(self): - # Create a user for auth - user = get_user_model() - self.user = user.objects.create_user('testuser', 'test@testing.com', 'password') - self.client.login(username='testuser', password='password') - def test_define(self): self.assertEqual(int(inventree_extras.define(3)), 3) @@ -329,24 +323,13 @@ class TestTemplateTest(TestCase): self.assertEqual(variant.getTestTemplates().count(), n + 1) -class PartSettingsTest(TestCase): +class PartSettingsTest(InvenTreeTestCase): """ Tests to ensure that the user-configurable default values work as expected. Some fields for the Part model can have default values specified by the user. """ - def setUp(self): - # Create a user for auth - user = get_user_model() - - self.user = user.objects.create_user( - username='testuser', - email='test@testing.com', - password='password', - is_staff=True - ) - def make_part(self): """ Helper function to create a simple part @@ -460,7 +443,7 @@ class PartSettingsTest(TestCase): Part.objects.create(name='abc', revision='6', description='A part', IPN=' ') -class PartSubscriptionTests(TestCase): +class PartSubscriptionTests(InvenTreeTestCase): fixtures = [ 'location', @@ -469,15 +452,7 @@ class PartSubscriptionTests(TestCase): ] def setUp(self): - # Create a user for auth - user = get_user_model() - - self.user = user.objects.create_user( - username='testuser', - email='test@testing.com', - password='password', - is_staff=True - ) + super().setUp() # electronics / IC / MCU self.category = PartCategory.objects.get(pk=4) @@ -577,7 +552,7 @@ class PartSubscriptionTests(TestCase): self.assertTrue(self.part.is_starred_by(self.user)) -class BaseNotificationIntegrationTest(TestCase): +class BaseNotificationIntegrationTest(InvenTreeTestCase): """ Integration test for notifications """ fixtures = [ @@ -588,15 +563,7 @@ class BaseNotificationIntegrationTest(TestCase): ] def setUp(self): - # Create a user for auth - user = get_user_model() - - self.user = user.objects.create_user( - username='testuser', - email='test@testing.com', - password='password', - is_staff=True - ) + super().setUp() # Add Mailadress EmailAddress.objects.create(user=self.user, email='test@testing.com') diff --git a/InvenTree/part/test_views.py b/InvenTree/part/test_views.py index c0eff13a2e..21ec6f7909 100644 --- a/InvenTree/part/test_views.py +++ b/InvenTree/part/test_views.py @@ -1,14 +1,13 @@ """ Unit tests for Part Views (see views.py) """ -from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group -from django.test import TestCase from django.urls import reverse +from InvenTree.helpers import InvenTreeTestCase + from .models import Part -class PartViewTestCase(TestCase): +class PartViewTestCase(InvenTreeTestCase): fixtures = [ 'category', @@ -19,33 +18,12 @@ class PartViewTestCase(TestCase): 'supplier_part', ] + roles = 'all' + superuser = True + def setUp(self): super().setUp() - # Create a user - user = get_user_model() - - self.user = user.objects.create_user( - username='username', - email='user@email.com', - password='password' - ) - - # Put the user into a group with the correct permissions - group = Group.objects.create(name='mygroup') - self.user.groups.add(group) - - # Give the group *all* the permissions! - for rule in group.rule_sets.all(): - rule.can_view = True - rule.can_change = True - rule.can_add = True - rule.can_delete = True - - rule.save() - - self.client.login(username='username', password='password') - class PartListTest(PartViewTestCase): diff --git a/InvenTree/plugin/base/action/test_action.py b/InvenTree/plugin/base/action/test_action.py index 995ce77be9..5678a0d44e 100644 --- a/InvenTree/plugin/base/action/test_action.py +++ b/InvenTree/plugin/base/action/test_action.py @@ -1,8 +1,8 @@ """ Unit tests for action plugins """ -from django.contrib.auth import get_user_model from django.test import TestCase +from InvenTree.helpers import InvenTreeTestCase from plugin import InvenTreePlugin from plugin.mixins import ActionMixin @@ -65,15 +65,9 @@ class ActionMixinTests(TestCase): }) -class APITests(TestCase): +class APITests(InvenTreeTestCase): """ Tests for action api """ - def setUp(self): - # Create a user for auth - user = get_user_model() - self.test_user = user.objects.create_user('testuser', 'test@testing.com', 'password') - self.client.login(username='testuser', password='password') - def test_post_errors(self): """Check the possible errors with post""" diff --git a/InvenTree/plugin/base/barcodes/test_barcode.py b/InvenTree/plugin/base/barcodes/test_barcode.py index 88d4109069..4b66f8b208 100644 --- a/InvenTree/plugin/base/barcodes/test_barcode.py +++ b/InvenTree/plugin/base/barcodes/test_barcode.py @@ -4,16 +4,15 @@ Unit tests for Barcode endpoints """ -from django.contrib.auth import get_user_model from django.urls import reverse from rest_framework import status -from rest_framework.test import APITestCase +from InvenTree.api_tester import InvenTreeAPITestCase from stock.models import StockItem -class BarcodeAPITest(APITestCase): +class BarcodeAPITest(InvenTreeAPITestCase): fixtures = [ 'category', @@ -23,11 +22,7 @@ class BarcodeAPITest(APITestCase): ] def setUp(self): - # Create a user for auth - user = get_user_model() - user.objects.create_user('testuser', 'test@testing.com', 'password') - - self.client.login(username='testuser', password='password') + super().setUp() self.scan_url = reverse('api-barcode-scan') self.assign_url = reverse('api-barcode-link') diff --git a/InvenTree/plugin/base/integration/test_mixins.py b/InvenTree/plugin/base/integration/test_mixins.py index d5dac21746..9759e0f00d 100644 --- a/InvenTree/plugin/base/integration/test_mixins.py +++ b/InvenTree/plugin/base/integration/test_mixins.py @@ -1,13 +1,12 @@ """ Unit tests for base mixins for plugins """ from django.conf import settings -from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group from django.test import TestCase from django.urls import include, re_path, reverse from error_report.models import Error +from InvenTree.helpers import InvenTreeTestCase from plugin import InvenTreePlugin from plugin.helpers import MixinNotImplementedError from plugin.mixins import (APICallMixin, AppMixin, NavigationMixin, @@ -24,7 +23,7 @@ class BaseMixinDefinition: self.assertIn(self.MIXIN_HUMAN_NAME, [item['human_name'] for item in self.mixin.registered_mixins]) -class SettingsMixinTest(BaseMixinDefinition, TestCase): +class SettingsMixinTest(BaseMixinDefinition, InvenTreeTestCase): MIXIN_HUMAN_NAME = 'Settings' MIXIN_NAME = 'settings' MIXIN_ENABLE_CHECK = 'has_settings' @@ -40,9 +39,7 @@ class SettingsMixinTest(BaseMixinDefinition, TestCase): pass self.mixin_nothing = NoSettingsCls() - user = get_user_model() - self.test_user = user.objects.create_user('testuser', 'test@testing.com', 'password') - self.test_user.is_staff = True + super().setUp() def test_function(self): # settings variable @@ -54,7 +51,7 @@ class SettingsMixinTest(BaseMixinDefinition, TestCase): self.assertEqual(self.mixin_nothing.get_setting('ABCD'), '') # right setting - self.mixin.set_setting('SETTING1', '12345', self.test_user) + self.mixin.set_setting('SETTING1', '12345', self.user) self.assertEqual(self.mixin.get_setting('SETTING1'), '12345') # no setting @@ -251,7 +248,7 @@ class APICallMixinTest(BaseMixinDefinition, TestCase): self.mixin_wrong2.has_api_call() -class PanelMixinTests(TestCase): +class PanelMixinTests(InvenTreeTestCase): """Test that the PanelMixin plugin operates correctly""" fixtures = [ @@ -261,32 +258,7 @@ class PanelMixinTests(TestCase): 'stock', ] - def setUp(self): - super().setUp() - - # Create a user which has all the privelages - user = get_user_model() - - self.user = user.objects.create_user( - username='username', - email='user@email.com', - password='password' - ) - - # Put the user into a group with the correct permissions - group = Group.objects.create(name='mygroup') - self.user.groups.add(group) - - # Give the group *all* the permissions! - for rule in group.rule_sets.all(): - rule.can_view = True - rule.can_change = True - rule.can_add = True - rule.can_delete = True - - rule.save() - - self.client.login(username='username', password='password') + roles = 'all' def test_installed(self): """Test that the sample panel plugin is installed""" diff --git a/InvenTree/plugin/builtin/action/test_simpleactionplugin.py b/InvenTree/plugin/builtin/action/test_simpleactionplugin.py index f9276ba7c3..e645dea85c 100644 --- a/InvenTree/plugin/builtin/action/test_simpleactionplugin.py +++ b/InvenTree/plugin/builtin/action/test_simpleactionplugin.py @@ -1,20 +1,15 @@ """ Unit tests for action plugins """ -from django.contrib.auth import get_user_model -from django.test import TestCase - +from InvenTree.helpers import InvenTreeTestCase from plugin.builtin.action.simpleactionplugin import SimpleActionPlugin -class SimpleActionPluginTests(TestCase): +class SimpleActionPluginTests(InvenTreeTestCase): """ Tests for SampleIntegrationPlugin """ def setUp(self): - # Create a user for auth - user = get_user_model() - self.test_user = user.objects.create_user('testuser', 'test@testing.com', 'password') + super().setUp() - self.client.login(username='testuser', password='password') self.plugin = SimpleActionPlugin() def test_name(self): @@ -33,7 +28,7 @@ class SimpleActionPluginTests(TestCase): "action": 'simple', "result": True, "info": { - "user": "testuser", + "user": self.username, "hello": "world", }, } diff --git a/InvenTree/plugin/builtin/barcodes/test_inventree_barcode.py b/InvenTree/plugin/builtin/barcodes/test_inventree_barcode.py index 4226ff055b..0bc855aa24 100644 --- a/InvenTree/plugin/builtin/barcodes/test_inventree_barcode.py +++ b/InvenTree/plugin/builtin/barcodes/test_inventree_barcode.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- """Unit tests for InvenTreeBarcodePlugin""" -from django.contrib.auth import get_user_model from django.urls import reverse from rest_framework import status -from rest_framework.test import APITestCase + +from InvenTree.api_tester import InvenTreeAPITestCase -class TestInvenTreeBarcode(APITestCase): +class TestInvenTreeBarcode(InvenTreeAPITestCase): fixtures = [ 'category', @@ -17,13 +17,6 @@ class TestInvenTreeBarcode(APITestCase): 'stock' ] - def setUp(self): - # Create a user for auth - user = get_user_model() - user.objects.create_user('testuser', 'test@testing.com', 'password') - - self.client.login(username='testuser', password='password') - def test_errors(self): """ Test all possible error cases for assigment action diff --git a/InvenTree/plugin/samples/integration/test_sample.py b/InvenTree/plugin/samples/integration/test_sample.py index aaff9f9624..577aa5812e 100644 --- a/InvenTree/plugin/samples/integration/test_sample.py +++ b/InvenTree/plugin/samples/integration/test_sample.py @@ -1,19 +1,11 @@ """ Unit tests for action plugins """ -from django.contrib.auth import get_user_model -from django.test import TestCase +from InvenTree.helpers import InvenTreeTestCase -class SampleIntegrationPluginTests(TestCase): +class SampleIntegrationPluginTests(InvenTreeTestCase): """ Tests for SampleIntegrationPlugin """ - def setUp(self): - # Create a user for auth - user = get_user_model() - user.objects.create_user('testuser', 'test@testing.com', 'password') - - self.client.login(username='testuser', password='password') - def test_view(self): """check the function of the custom sample plugin """ response = self.client.get('/plugin/sample/ho/he/') diff --git a/InvenTree/stock/test_views.py b/InvenTree/stock/test_views.py index dbe9a2a87e..dba39334de 100644 --- a/InvenTree/stock/test_views.py +++ b/InvenTree/stock/test_views.py @@ -1,14 +1,13 @@ """ Unit tests for Stock views (see views.py) """ -from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group -from django.test import TestCase from django.urls import reverse +from InvenTree.helpers import InvenTreeTestCase + # from common.models import InvenTreeSetting -class StockViewTestCase(TestCase): +class StockViewTestCase(InvenTreeTestCase): fixtures = [ 'category', @@ -19,35 +18,7 @@ class StockViewTestCase(TestCase): 'stock', ] - def setUp(self): - super().setUp() - - # Create a user - user = get_user_model() - - self.user = user.objects.create_user( - username='username', - email='user@email.com', - password='password' - ) - - self.user.is_staff = True - self.user.save() - - # Put the user into a group with the correct permissions - group = Group.objects.create(name='mygroup') - self.user.groups.add(group) - - # Give the group *all* the permissions! - for rule in group.rule_sets.all(): - rule.can_view = True - rule.can_change = True - rule.can_add = True - rule.can_delete = True - - rule.save() - - self.client.login(username='username', password='password') + roles = 'all' class StockListTest(StockViewTestCase): diff --git a/InvenTree/stock/tests.py b/InvenTree/stock/tests.py index e0c09d8e34..fed4b9858e 100644 --- a/InvenTree/stock/tests.py +++ b/InvenTree/stock/tests.py @@ -1,11 +1,10 @@ import datetime -from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError from django.db.models import Sum -from django.test import TestCase from build.models import Build +from InvenTree.helpers import InvenTreeTestCase from InvenTree.status_codes import StockHistoryCode from part.models import Part @@ -13,7 +12,7 @@ from .models import (StockItem, StockItemTestResult, StockItemTracking, StockLocation) -class StockTest(TestCase): +class StockTest(InvenTreeTestCase): """ Tests to ensure that the stock location tree functions correcly """ @@ -28,6 +27,8 @@ class StockTest(TestCase): ] def setUp(self): + super().setUp() + # Extract some shortcuts from the fixtures self.home = StockLocation.objects.get(name='Home') self.bathroom = StockLocation.objects.get(name='Bathroom') @@ -38,14 +39,6 @@ class StockTest(TestCase): self.drawer2 = StockLocation.objects.get(name='Drawer_2') self.drawer3 = StockLocation.objects.get(name='Drawer_3') - # Create a user - user = get_user_model() - user.objects.create_user('username', 'user@email.com', 'password') - - self.client.login(username='username', password='password') - - self.user = user.objects.get(username='username') - # Ensure the MPTT objects are correctly rebuild Part.objects.rebuild() StockItem.objects.rebuild() diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js index a4af51ef74..ec23fd93e1 100644 --- a/InvenTree/templates/js/translated/forms.js +++ b/InvenTree/templates/js/translated/forms.js @@ -561,6 +561,11 @@ function constructFormBody(fields, options) { insertPersistButton(options); } + // Insert secondary buttons (if required) + if (options.buttons) { + insertSecondaryButtons(options); + } + // Display the modal $(modal).modal('show'); @@ -650,6 +655,31 @@ function insertPersistButton(options) { $(options.modal).find('#modal-footer-buttons').append(html); } +/* + * Add secondary buttons to the left of the close and submit buttons + * with callback functions + */ +function insertSecondaryButtons(options) { + for (var idx = 0; idx < options.buttons.length; idx++) { + + var html = ` + + `; + + $(options.modal).find('#modal-footer-secondary-buttons').append(html); + + if (options.buttons[idx].onClick instanceof Function) { + // Copy callback reference to prevent errors if `idx` changes value before execution + var onclick_callback = options.buttons[idx].onClick; + + $(options.modal).find(`#modal-form-${options.buttons[idx].name}`).click(function() { + onclick_callback(options); + }); + } + } +} /* * Extract all specified form values as a single object diff --git a/InvenTree/templates/js/translated/modals.js b/InvenTree/templates/js/translated/modals.js index 128278dd23..b5ea52005b 100644 --- a/InvenTree/templates/js/translated/modals.js +++ b/InvenTree/templates/js/translated/modals.js @@ -75,6 +75,9 @@ function createNewModal(options={}) {

+ @@ -99,7 +102,7 @@ function createNewModal(options={}) { $(modal_name).focus(); if (options.hideCloseButton) { - $(modal_name).find('#modal-form-cancel').hide(); + $(modal_name).find('#modal-form-close').hide(); } if (options.preventSubmit || options.hideSubmitButton) { diff --git a/InvenTree/templates/js/translated/order.js b/InvenTree/templates/js/translated/order.js index f2a3684163..4c6e218ea2 100644 --- a/InvenTree/templates/js/translated/order.js +++ b/InvenTree/templates/js/translated/order.js @@ -24,6 +24,7 @@ cancelSalesOrder, completePurchaseOrder, completeShipment, + completePendingShipments, createSalesOrder, createSalesOrderShipment, editPurchaseOrderLineItem, @@ -69,7 +70,7 @@ function salesOrderShipmentFields(options={}) { /* * Complete a shipment */ -function completeShipment(shipment_id) { +function completeShipment(shipment_id, options={}) { // Request the list of stock items which will be shipped inventreeGet(`/api/order/so/shipment/${shipment_id}/`, {}, { @@ -126,28 +127,128 @@ function completeShipment(shipment_id) { constructForm(`/api/order/so/shipment/${shipment_id}/ship/`, { method: 'POST', - title: '{% trans "Complete Shipment" %}', + title: `{% trans "Complete Shipment" %} ${shipment.reference}`, fields: { tracking_number: {}, }, preFormContent: html, confirm: true, confirmMessage: '{% trans "Confirm Shipment" %}', + buttons: options.buttons, onSuccess: function(data) { // Reload tables $('#so-lines-table').bootstrapTable('refresh'); $('#pending-shipments-table').bootstrapTable('refresh'); $('#completed-shipments-table').bootstrapTable('refresh'); + + if (options.onSuccess instanceof Function) { + options.onSuccess(data); + } }, - reload: true + reload: options.reload }); } }); } +/* + * Launches a modal to mark all allocated pending shipments as complete + */ +function completePendingShipments(order_id, options={}) { + var pending_shipments = null; + + // Request the list of stock items which will be shipped + inventreeGet(`/api/order/so/shipment/.*`, + { + order: order_id, + shipped: false + }, + { + async: false, + success: function(shipments) { + pending_shipments = shipments; + } + } + ); + + var allocated_shipments = []; + + for (var idx = 0; idx < pending_shipments.length; idx++) { + if (pending_shipments[idx].allocations.length > 0) { + allocated_shipments.push(pending_shipments[idx]); + } + } + + if (allocated_shipments.length > 0) { + completePendingShipmentsHelper(allocated_shipments, 0, options); + + } else { + html = ` +
+ `; + + if (!pending_shipments.length) { + html += ` + {% trans "No pending shipments found" %} + `; + } else { + html += ` + {% trans "No stock items have been allocated to pending shipments" %} + `; + } + + html += ` +
+ `; + + constructForm(`/api/order/so/shipment/0/ship/`, { + method: 'POST', + title: '{% trans "Complete Shipments" %}', + preFormContent: html, + onSubmit: function(fields, options) { + handleFormSuccess(fields, options); + }, + closeText: 'Close', + hideSubmitButton: true, + }); + } +} + + +/* + * Recursive helper for opening shipment completion modals + */ +function completePendingShipmentsHelper(shipments, shipment_idx, options={}) { + if (shipment_idx < shipments.length) { + completeShipment(shipments[shipment_idx].pk, + { + buttons: [ + { + name: 'skip', + title: `{% trans "Skip" %}`, + onClick: function(form_options) { + if (form_options.modal) { + $(form_options.modal).modal('hide'); + } + + completePendingShipmentsHelper(shipments, shipment_idx + 1, options); + } + } + ], + onSuccess: function(data) { + completePendingShipmentsHelper(shipments, shipment_idx + 1, options); + }, + } + ); + + } else if (options.reload) { + location.reload(); + } +} + /* * Launches a modal form to mark a PurchaseOrder as "complete" -*/ + */ function completePurchaseOrder(order_id, options={}) { constructForm( diff --git a/InvenTree/users/tests.py b/InvenTree/users/tests.py index f545acc682..5678c8d6da 100644 --- a/InvenTree/users/tests.py +++ b/InvenTree/users/tests.py @@ -1,11 +1,11 @@ from django.apps import apps -from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from django.test import TestCase from django.urls import reverse from rest_framework.authtoken.models import Token +from InvenTree.helpers import InvenTreeTestCase from users.models import Owner, RuleSet @@ -160,20 +160,11 @@ class RuleSetModelTest(TestCase): self.assertEqual(group.permissions.count(), 0) -class OwnerModelTest(TestCase): +class OwnerModelTest(InvenTreeTestCase): """ Some simplistic tests to ensure the Owner model is setup correctly. """ - def setUp(self): - """ Add users and groups """ - - # Create a new user - self.user = get_user_model().objects.create_user('username', 'user@email.com', 'password') - # Put the user into a new group - self.group = Group.objects.create(name='new_group') - self.user.groups.add(self.group) - def do_request(self, endpoint, filters, status_code=200): response = self.client.get(endpoint, filters, format='json') self.assertEqual(response.status_code, status_code) @@ -212,11 +203,13 @@ class OwnerModelTest(TestCase): """ Test user APIs """ + self.client.logout() + # not authed self.do_request(reverse('api-owner-list'), {}, 401) self.do_request(reverse('api-owner-detail', kwargs={'pk': self.user.id}), {}, 401) - self.client.login(username='username', password='password') + self.client.login(username=self.username, password=self.password) # user list self.do_request(reverse('api-owner-list'), {}) # user list with search @@ -229,12 +222,14 @@ class OwnerModelTest(TestCase): """ Test token mechanisms """ + self.client.logout() + token = Token.objects.filter(user=self.user) # not authed self.do_request(reverse('api-token'), {}, 401) - self.client.login(username='username', password='password') + self.client.login(username=self.username, password=self.password) # token get response = self.do_request(reverse('api-token'), {}) self.assertEqual(response['token'], token.first().key)