Extra unit testing for settings forms / views

This commit is contained in:
Oliver Walters 2020-11-13 11:50:58 +11:00
parent d777549a1a
commit 01ff562dcd
4 changed files with 189 additions and 14 deletions

View File

@ -82,7 +82,7 @@ settings_urls = [
url(r'^purchase-order/?', SettingsView.as_view(template_name='InvenTree/settings/po.html'), name='settings-po'),
url(r'^sales-order/?', SettingsView.as_view(template_name='InvenTree/settings/so.html'), name='settings-so'),
url(r'^(?P<pk>\d+)/edit/?', SettingEdit.as_view(), name='setting-edit'),
url(r'^(?P<pk>\d+)/edit/', SettingEdit.as_view(), name='setting-edit'),
# Catch any other urls
url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings'),

View File

@ -165,6 +165,11 @@ class InvenTreeSetting(models.Model):
verbose_name = "InvenTree Setting"
verbose_name_plural = "InvenTree Settings"
def save(self, *args, **kwargs):
self.clean()
super().save(*args, **kwargs)
@classmethod
def get_setting_name(cls, key):
"""
@ -281,20 +286,15 @@ class InvenTreeSetting(models.Model):
try:
setting = InvenTreeSetting.objects.filter(key__iexact=key).first()
except OperationalError:
# Settings table has not been created yet!
return None
except (ValueError, InvenTreeSetting.DoesNotExist):
try:
# Attempt Create the setting if it does not exist
setting = InvenTreeSetting.create(
key=key,
value=InvenTreeSetting.get_default_value(key)
)
except OperationalError:
# Settings table has not been created yet
setting = None
setting = None
if not setting:
# Attempt Create the setting if it does not exist
setting = InvenTreeSetting.objects.create(
key=key,
value=InvenTreeSetting.get_default_value(key)
)
return setting
@ -403,6 +403,9 @@ class InvenTreeSetting(models.Model):
if validator is not None:
self.run_validator(validator)
if self.is_bool():
self.value = InvenTree.helpers.str2bool(self.value)
def run_validator(self, validator):
"""
Run a validator against the 'value' field for this InvenTreeSetting object.

View File

@ -0,0 +1,143 @@
"""
Unit tests for the views associated with the 'common' app
"""
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import json
from django.test import TestCase
from django.urls import reverse
from django.contrib.auth import get_user_model
from common.models import InvenTreeSetting
class SettingsViewTest(TestCase):
"""
Tests for the settings management views
"""
fixtures = [
'settings',
]
def setUp(self):
super().setUp()
# Create a user (required to access the views / forms)
self.user = get_user_model().objects.create_user(
username='username',
email='me@email.com',
password='password',
)
self.client.login(username='username', password='password')
def get_url(self, pk):
return reverse('setting-edit', args=(pk,))
def get_setting(self, title):
return InvenTreeSetting.get_setting_object(title)
def get(self, url, status=200):
response = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, status)
data = json.loads(response.content)
return response, data
def post(self, url, data, valid=None):
response = self.client.post(url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
json_data = json.loads(response.content)
# If a particular status code is required
if valid is not None:
if valid:
self.assertEqual(json_data['form_valid'], True)
else:
self.assertEqual(json_data['form_valid'], False)
form_errors = json.loads(json_data['form_errors'])
return json_data, form_errors
def test_instance_name(self):
"""
Test that we can get the settings view for particular setting objects.
"""
# Start with something basic - load the settings view for INVENTREE_INSTANCE
setting = self.get_setting('INVENTREE_INSTANCE')
self.assertIsNotNone(setting)
self.assertEqual(setting.value, 'My very first InvenTree Instance')
url = self.get_url(setting.pk)
response = self.get(url)
new_name = 'A new instance name!'
# Change the instance name via the form
data, errors = self.post(url, {'value': new_name}, valid=True)
name = InvenTreeSetting.get_setting('INVENTREE_INSTANCE')
self.assertEqual(name, new_name)
def test_choices(self):
"""
Tests for a setting which has choices
"""
setting = InvenTreeSetting.get_setting_object('INVENTREE_DEFAULT_CURRENCY')
# Default value!
self.assertEqual(setting.value, 'USD')
url = self.get_url(setting.pk)
# Try posting an invalid currency option
data, errors = self.post(url, {'value': 'XPQaaa'}, valid=False)
self.assertIsNotNone(errors.get('value'), None)
# Try posting a valid currency option
data, errors = self.post(url, {'value': 'AUD'}, valid=True)
def test_binary_values(self):
"""
Test for binary value
"""
setting = InvenTreeSetting.get_setting_object('PART_COMPONENT')
self.assertTrue(setting.as_bool())
url = self.get_url(setting.pk)
setting.value = True
setting.save()
# Try posting some invalid values
# The value should be "cleaned" and stay the same
for value in ['', 'abc', 'cat', 'TRUETRUETRUE']:
self.post(url, {'value': value}, valid=True)
# Try posting some valid (True) values
for value in [True, 'True', '1', 'yes']:
self.post(url, {'value': value}, valid=True)
self.assertTrue(InvenTreeSetting.get_setting('PART_COMPONENT'))
# Try posting some valid (False) values
for value in [False, 'False']:
self.post(url, {'value': value}, valid=True)
self.assertFalse(InvenTreeSetting.get_setting('PART_COMPONENT'))

View File

@ -72,3 +72,32 @@ class SettingEdit(AjaxUpdateView):
form.fields['value'].help_text = description
return form
def validate(self, setting, form):
"""
Perform custom validation checks on the form data.
"""
data = form.cleaned_data
value = data.get('value', None)
if setting.choices():
"""
If a set of choices are provided for a given setting,
the provided value must be one of those choices.
"""
choices = [choice[0] for choice in setting.choices()]
if value not in choices:
form.add_error('value', _('Supplied value is not allowed'))
if setting.is_bool():
"""
If a setting is defined as a boolean setting,
the provided value must look somewhat like a boolean value!
"""
if not str2bool(value, test=True) and not str2bool(value, test=False):
form.add_error('value', _('Supplied value must be a boolean'))