2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-16 12:05:53 +00:00

refactor app

This commit is contained in:
Matthias
2021-10-08 22:08:09 +02:00
parent f07df107a9
commit dddd4370cf
22 changed files with 25 additions and 25 deletions

View File

@ -1,70 +0,0 @@
# -*- coding: utf-8 -*-
"""Class for ActionPlugin"""
import logging
import plugins.plugin as plugin
logger = logging.getLogger("inventree")
class ActionPlugin(plugin.InvenTreePlugin):
"""
The ActionPlugin class is used to perform custom actions
"""
ACTION_NAME = ""
@classmethod
def action_name(cls):
"""
Return the action name for this plugin.
If the ACTION_NAME parameter is empty,
look at the PLUGIN_NAME instead.
"""
action = cls.ACTION_NAME
if not action:
action = cls.PLUGIN_NAME
return action
def __init__(self, user, data=None):
"""
An action plugin takes a user reference, and an optional dataset (dict)
"""
plugin.InvenTreePlugin.__init__(self)
self.user = user
self.data = data
def perform_action(self):
"""
Override this method to perform the action!
"""
def get_result(self):
"""
Result of the action?
"""
# Re-implement this for cutsom actions
return False
def get_info(self):
"""
Extra info? Can be a string / dict / etc
"""
return None
def get_response(self):
"""
Return a response. Default implementation is a simple response
which can be overridden.
"""
return {
"action": self.action_name(),
"result": self.get_result(),
"info": self.get_info(),
}

View File

@ -1,362 +0,0 @@
# -*- coding: utf-8 -*-
"""class for IntegrationPluginBase and Mixins for it"""
import logging
import os
import subprocess
import inspect
from datetime import datetime
from django.conf.urls import url, include
from django.conf import settings
from django.utils.text import slugify
from django.utils.translation import ugettext_lazy as _
import plugins.plugin as plugin
logger = logging.getLogger("inventree")
# region mixins
class MixinBase:
"""general base for mixins"""
def __init__(self) -> None:
self._mixinreg = {}
self._mixins = {}
def add_mixin(self, key: str, fnc_enabled=True, cls=None):
"""add a mixin to the plugins registry"""
self._mixins[key] = fnc_enabled
self.setup_mixin(key, cls=cls)
def setup_mixin(self, key, cls=None):
"""define mixin details for the current mixin -> provides meta details for all active mixins"""
# get human name
human_name = getattr(cls.Meta, 'MIXIN_NAME', key) if cls and hasattr(cls, 'Meta') else key
# register
self._mixinreg[key] = {
'key': key,
'human_name': human_name,
}
@property
def registered_mixins(self, with_base: bool = False):
"""get all registered mixins for the plugin"""
mixins = getattr(self, '_mixinreg', None)
if mixins:
# filter out base
if not with_base and 'base' in mixins:
del mixins['base']
# only return dict
mixins = [a for a in mixins.values()]
return mixins
class SettingsMixin:
"""Mixin that enables settings for the plugin"""
class Meta:
"""meta options for this mixin"""
MIXIN_NAME = 'Settings'
def __init__(self):
super().__init__()
self.add_mixin('settings', 'has_settings', __class__)
self.settings = self.setup_settings()
def setup_settings(self):
"""
setup settings for this plugin
"""
return getattr(self, 'SETTINGS', None)
@property
def has_settings(self):
"""
does this plugin use custom settings
"""
return bool(self.settings)
@property
def settingspatterns(self):
"""
get patterns for InvenTreeSetting defintion
"""
if self.has_settings:
return {f'PLUGIN_{self.slug.upper()}_{key}': value for key, value in self.settings.items()}
return None
def get_setting(self, key):
"""
get plugin setting by key
"""
from common.models import InvenTreeSetting
return InvenTreeSetting.get_setting(f'PLUGIN_{self.slug.upper()}_{key}')
class UrlsMixin:
"""Mixin that enables urls for the plugin"""
class Meta:
"""meta options for this mixin"""
MIXIN_NAME = 'URLs'
def __init__(self):
super().__init__()
self.add_mixin('urls', 'has_urls', __class__)
self.urls = self.setup_urls()
def setup_urls(self):
"""
setup url endpoints for this plugin
"""
return getattr(self, 'URLS', None)
@property
def base_url(self):
"""
returns base url for this plugin
"""
return f'{settings.PLUGIN_URL}/{self.slug}/'
@property
def internal_name(self):
"""
returns the internal url pattern name
"""
return f'plugin:{self.slug}:'
@property
def urlpatterns(self):
"""
returns the urlpatterns for this plugin
"""
if self.has_urls:
return url(f'^{self.slug}/', include((self.urls, self.slug)), name=self.slug)
return None
@property
def has_urls(self):
"""
does this plugin use custom urls
"""
return bool(self.urls)
class NavigationMixin:
"""Mixin that enables adding navigation links with the plugin"""
NAVIGATION_TAB_NAME = None
NAVIGATION_TAB_ICON = "fas fa-question"
class Meta:
"""meta options for this mixin"""
MIXIN_NAME = 'Navigation Links'
def __init__(self):
super().__init__()
self.add_mixin('navigation', 'has_naviation', __class__)
self.navigation = self.setup_navigation()
def setup_navigation(self):
"""
setup navigation links for this plugin
"""
nav_links = getattr(self, 'NAVIGATION', None)
if nav_links:
# check if needed values are configured
for link in nav_links:
if False in [a in link for a in ('link', 'name', )]:
raise NotImplementedError('Wrong Link definition', link)
return nav_links
@property
def has_naviation(self):
"""
does this plugin define navigation elements
"""
return bool(self.navigation)
@property
def navigation_name(self):
"""name for navigation tab"""
name = getattr(self, 'NAVIGATION_TAB_NAME', None)
if not name:
name = self.human_name
return name
@property
def navigation_icon(self):
"""icon for navigation tab"""
return getattr(self, 'NAVIGATION_TAB_ICON', "fas fa-question")
class AppMixin:
"""Mixin that enables full django app functions for a plugin"""
class Meta:
"""meta options for this mixin"""
MIXIN_NAME = 'App registration'
def __init__(self):
super().__init__()
self.add_mixin('app', 'has_app', __class__)
@property
def has_app(self):
"""
this plugin is always an app with this plugin
"""
return True
# endregion
# region git-helpers
def get_git_log(path):
"""get dict with info of the last commit to file named in path"""
path = path.replace(os.path.dirname(settings.BASE_DIR), '')[1:]
command = ['git', 'log', '-n', '1', "--pretty=format:'%H%n%aN%n%aE%n%aI%n%f%n%G?%n%GK'", '--follow', '--', path]
try:
output = str(subprocess.check_output(command, cwd=os.path.dirname(settings.BASE_DIR)), 'utf-8')[1:-1]
if output:
output = output.split('\n')
else:
output = 7 * ['']
except subprocess.CalledProcessError:
output = 7 * ['']
return {'hash': output[0], 'author': output[1], 'mail': output[2], 'date': output[3], 'message': output[4], 'verified': output[5], 'key': output[6]}
class GitStatus:
"""class for resolving git gpg singing state"""
class Definition:
"""definition of a git gpg sing state"""
key: str = 'N'
status: int = 2
msg: str = ''
def __init__(self, key: str = 'N', status: int = 2, msg: str = '') -> None:
self.key = key
self.status = status
self.msg = msg
N = Definition(key='N', status=2, msg='no signature',)
G = Definition(key='G', status=0, msg='valid signature',)
B = Definition(key='B', status=2, msg='bad signature',)
U = Definition(key='U', status=1, msg='good signature, unknown validity',)
X = Definition(key='X', status=1, msg='good signature, expired',)
Y = Definition(key='Y', status=1, msg='good signature, expired key',)
R = Definition(key='R', status=2, msg='good signature, revoked key',)
E = Definition(key='E', status=1, msg='cannot be checked',)
# endregion
class IntegrationPluginBase(MixinBase, plugin.InvenTreePlugin):
"""
The IntegrationPluginBase class is used to integrate with 3rd party software
"""
PLUGIN_SLUG = None
PLUGIN_TITLE = None
AUTHOR = None
PUBLISH_DATE = None
VERSION = None
WEBSITE = None
def __init__(self):
super().__init__()
self.add_mixin('base')
self.def_path = inspect.getfile(self.__class__)
self.path = os.path.dirname(self.def_path)
self.set_sign_values()
# properties
@property
def slug(self):
"""slug for the plugin"""
name = getattr(self, 'PLUGIN_SLUG', None)
if not name:
name = self.plugin_name()
return slugify(name)
@property
def human_name(self):
"""human readable name for labels etc."""
name = getattr(self, 'PLUGIN_TITLE', None)
if not name:
name = self.plugin_name()
return name
@property
def author(self):
"""returns author of plugin - either from plugin settings or git"""
name = getattr(self, 'AUTHOR', None)
if not name:
name = self.commit.get('author')
if not name:
name = _('No author found')
return name
@property
def pub_date(self):
"""returns publishing date of plugin - either from plugin settings or git"""
name = getattr(self, 'PUBLISH_DATE', None)
if not name:
name = self.commit.get('date')
else:
name = datetime.fromisoformat(name)
if not name:
name = _('No date found')
return name
@property
def version(self):
"""returns version of plugin"""
name = getattr(self, 'VERSION', None)
return name
@property
def website(self):
"""returns website of plugin"""
name = getattr(self, 'WEBSITE', None)
return name
# mixins
def mixin(self, key):
"""check if mixin is registered"""
return key in self._mixins
def mixin_enabled(self, key):
"""check if mixin is enabled and ready"""
if self.mixin(key):
fnc_name = self._mixins.get(key)
return getattr(self, fnc_name, True)
return False
# git
def get_plugin_commit(self):
"""get last git commit for plugin"""
return get_git_log(self.def_path)
def set_sign_values(self):
"""add the last commit of the plugins class file into plugins context"""
# fetch git log
commit = self.get_plugin_commit()
# resolve state
sign_state = getattr(GitStatus, commit['verified'], GitStatus.N)
# set variables
self.commit = commit
self.sign_state = sign_state
# process date
if self.commit['date']:
self.commit['date'] = datetime.fromisoformat(self.commit['date'])
if sign_state.status == 0:
self.sign_color = 'success'
elif sign_state.status == 1:
self.sign_color = 'warning'
else:
self.sign_color = 'danger'

View File

@ -1,19 +0,0 @@
"""
load templates for loaded plugins
"""
from django.conf import settings
from django.template.loaders.filesystem import Loader as FilesystemLoader
from pathlib import Path
class PluginTemplateLoader(FilesystemLoader):
def get_dirs(self):
dirname = 'templates'
template_dirs = []
for plugin in settings.INTEGRATION_PLUGINS:
new_path = Path(plugin.path) / dirname
if Path(new_path).is_dir():
template_dirs.append(new_path)
return tuple(template_dirs)

View File

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
"""Base Class for InvenTree plugins"""
class InvenTreePlugin():
"""
Base class for a plugin
"""
# Override the plugin name for each concrete plugin instance
PLUGIN_NAME = ''
def plugin_name(self):
"""get plugin name"""
return self.PLUGIN_NAME
def __init__(self):
pass

View File

@ -1,101 +0,0 @@
# -*- coding: utf-8 -*-
"""general functions for plugin handeling"""
import inspect
import importlib
import pkgutil
import logging
# Action plugins
import plugins.samples.action as action
from plugins.action import ActionPlugin
import plugins.samples.integration as integration
from plugins.integration import IntegrationPluginBase
logger = logging.getLogger("inventree")
def iter_namespace(pkg):
"""get all modules in a package"""
return pkgutil.iter_modules(pkg.__path__, pkg.__name__ + ".")
def get_modules(pkg):
"""get all modules in a package"""
return [importlib.import_module(name) for finder, name, ispkg in iter_namespace(pkg)]
def get_classes(module):
"""get all classes in a given module"""
return inspect.getmembers(module, inspect.isclass)
def get_plugins(pkg, baseclass):
"""
Return a list of all modules under a given package.
- Modules must be a subclass of the provided 'baseclass'
- Modules must have a non-empty PLUGIN_NAME parameter
"""
plugins = []
modules = get_modules(pkg)
# Iterate through each module in the package
for mod in modules:
# Iterate through each class in the module
for item in get_classes(mod):
plugin = item[1]
if issubclass(plugin, baseclass) and plugin.PLUGIN_NAME:
plugins.append(plugin)
return plugins
def load_plugins(name: str, module, cls):
"""general function to load a plugin class
:param name: name of the plugin for logs
:type name: str
:param module: module from which the plugins should be loaded
:return: class of the to-be-loaded plugin
"""
logger.debug("Loading %s plugins", name)
plugins = get_plugins(module, cls)
if len(plugins) > 0:
logger.info("Discovered %i %s plugins:", len(plugins), name)
for plugin in plugins:
logger.debug(" - %s", plugin.PLUGIN_NAME)
return plugins
def load_action_plugins():
"""
Return a list of all registered action plugins
"""
return load_plugins('action', action, ActionPlugin)
def load_integration_plugins():
"""
Return a list of all registered integration plugins
"""
return load_plugins('integration', integration, IntegrationPluginBase)
def load_barcode_plugins():
"""
Return a list of all registered barcode plugins
"""
from barcodes import plugins as BarcodePlugins
from barcodes.barcode import BarcodePlugin
return load_plugins('barcode', BarcodePlugins, BarcodePlugin)

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
"""sample implementation for ActionPlugin"""
from plugins.action import ActionPlugin
class SimpleActionPlugin(ActionPlugin):
"""
An EXTREMELY simple action plugin which demonstrates
the capability of the ActionPlugin class
"""
PLUGIN_NAME = "SimpleActionPlugin"
ACTION_NAME = "simple"
def perform_action(self):
print("Action plugin in action!")
def get_info(self):
return {
"user": self.user.username,
"hello": "world",
}
def get_result(self):
return True

View File

@ -1,40 +0,0 @@
""" Unit tests for action plugins """
from django.test import TestCase
from django.contrib.auth import get_user_model
from plugins.samples.action.simpleactionplugin import SimpleActionPlugin
class SimpleActionPluginTests(TestCase):
""" 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')
self.client.login(username='testuser', password='password')
self.plugin = SimpleActionPlugin(user=self.test_user)
def test_name(self):
"""check plugn names """
self.assertEqual(self.plugin.plugin_name(), "SimpleActionPlugin")
self.assertEqual(self.plugin.action_name(), "simple")
def test_function(self):
"""check if functions work """
# test functions
response = self.client.post('/api/action/', data={'action': "simple", 'data': {'foo': "bar", }})
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
str(response.content, encoding='utf8'),
{
"action": 'simple',
"result": True,
"info": {
"user": "testuser",
"hello": "world",
},
}
)

View File

@ -1,18 +0,0 @@
"""sample implementation for IntegrationPlugin"""
from plugins.integration import IntegrationPluginBase, UrlsMixin
class NoIntegrationPlugin(IntegrationPluginBase):
"""
An basic integration plugin
"""
PLUGIN_NAME = "NoIntegrationPlugin"
class WrongIntegrationPlugin(UrlsMixin, IntegrationPluginBase):
"""
An basic integration plugin
"""
PLUGIN_NAME = "WrongIntegrationPlugin"

View File

@ -1,47 +0,0 @@
"""sample implementations for IntegrationPlugin"""
from plugins.integration import AppMixin, SettingsMixin, UrlsMixin, NavigationMixin, IntegrationPluginBase
from django.http import HttpResponse
from django.utils.translation import ugettext_lazy as _
from django.conf.urls import url, include
class SampleIntegrationPlugin(AppMixin, SettingsMixin, UrlsMixin, NavigationMixin, IntegrationPluginBase):
"""
An full integration plugin
"""
PLUGIN_NAME = "SampleIntegrationPlugin"
PLUGIN_SLUG = "sample"
PLUGIN_TITLE = "Sample Plugin"
NAVIGATION_TAB_NAME = "Sample Nav"
NAVIGATION_TAB_ICON = 'fas fa-plus'
def view_test(self, request):
"""very basic view"""
return HttpResponse(f'Hi there {request.user.username} this works')
def setup_urls(self):
he_urls = [
url(r'^he/', self.view_test, name='he'),
url(r'^ha/', self.view_test, name='ha'),
]
return [
url(r'^hi/', self.view_test, name='hi'),
url(r'^ho/', include(he_urls), name='ho'),
]
SETTINGS = {
'PO_FUNCTION_ENABLE': {
'name': _('Enable PO'),
'description': _('Enable PO functionality in InvenTree interface'),
'default': True,
'validator': bool,
},
}
NAVIGATION = [
{'name': 'SampleIntegration', 'link': 'plugin:sample:hi'},
]

View File

@ -1,21 +0,0 @@
""" Unit tests for action plugins """
from django.test import TestCase
from django.contrib.auth import get_user_model
class SampleIntegrationPluginTests(TestCase):
""" 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/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b'Hi there testuser this works')

View File

@ -1,61 +0,0 @@
""" Unit tests for action plugins """
from django.test import TestCase
from plugins.action import ActionPlugin
class ActionPluginTests(TestCase):
""" Tests for ActionPlugin """
ACTION_RETURN = 'a action was performed'
def setUp(self):
self.plugin = ActionPlugin('user')
class TestActionPlugin(ActionPlugin):
"""a action plugin"""
ACTION_NAME = 'abc123'
def perform_action(self):
return ActionPluginTests.ACTION_RETURN + 'action'
def get_result(self):
return ActionPluginTests.ACTION_RETURN + 'result'
def get_info(self):
return ActionPluginTests.ACTION_RETURN + 'info'
self.action_plugin = TestActionPlugin('user')
class NameActionPlugin(ActionPlugin):
PLUGIN_NAME = 'Aplugin'
self.action_name = NameActionPlugin('user')
def test_action_name(self):
"""check the name definition possibilities"""
self.assertEqual(self.plugin.action_name(), '')
self.assertEqual(self.action_plugin.action_name(), 'abc123')
self.assertEqual(self.action_name.action_name(), 'Aplugin')
def test_function(self):
"""check functions"""
# the class itself
self.assertIsNone(self.plugin.perform_action())
self.assertEqual(self.plugin.get_result(), False)
self.assertIsNone(self.plugin.get_info())
self.assertEqual(self.plugin.get_response(), {
"action": '',
"result": False,
"info": None,
})
# overriden functions
self.assertEqual(self.action_plugin.perform_action(), self.ACTION_RETURN + 'action')
self.assertEqual(self.action_plugin.get_result(), self.ACTION_RETURN + 'result')
self.assertEqual(self.action_plugin.get_info(), self.ACTION_RETURN + 'info')
self.assertEqual(self.action_plugin.get_response(), {
"action": 'abc123',
"result": self.ACTION_RETURN + 'result',
"info": self.ACTION_RETURN + 'info',
})

View File

@ -1,171 +0,0 @@
""" Unit tests for integration plugins """
from django.test import TestCase
from django.conf import settings
from django.conf.urls import url, include
from datetime import datetime
from plugins.integration import AppMixin, IntegrationPluginBase, SettingsMixin, UrlsMixin, NavigationMixin
class BaseMixinDefinition:
def test_mixin_name(self):
# mixin name
self.assertEqual(self.mixin.registered_mixins[0]['key'], self.MIXIN_NAME)
# human name
self.assertEqual(self.mixin.registered_mixins[0]['human_name'], self.MIXIN_HUMAN_NAME)
class SettingsMixinTest(BaseMixinDefinition, TestCase):
MIXIN_HUMAN_NAME = 'Settings'
MIXIN_NAME = 'settings'
MIXIN_ENABLE_CHECK = 'has_settings'
TEST_SETTINGS = {'setting1': [1, 2, 3]}
def setUp(self):
class SettingsCls(SettingsMixin, IntegrationPluginBase):
SETTINGS = self.TEST_SETTINGS
self.mixin = SettingsCls()
class NoSettingsCls(SettingsMixin, IntegrationPluginBase):
pass
self.mixin_nothing = NoSettingsCls()
def test_function(self):
# settings variable
self.assertEqual(self.mixin.settings, self.TEST_SETTINGS)
# settings pattern
target_pattern = {f'PLUGIN_{self.mixin.slug.upper()}_{key}': value for key, value in self.mixin.settings.items()}
self.assertEqual(self.mixin.settingspatterns, target_pattern)
# no settings
self.assertIsNone(self.mixin_nothing.settings)
self.assertIsNone(self.mixin_nothing.settingspatterns)
class UrlsMixinTest(BaseMixinDefinition, TestCase):
MIXIN_HUMAN_NAME = 'URLs'
MIXIN_NAME = 'urls'
MIXIN_ENABLE_CHECK = 'has_urls'
def setUp(self):
class UrlsCls(UrlsMixin, IntegrationPluginBase):
def test():
return 'ccc'
URLS = [url('testpath', test, name='test'), ]
self.mixin = UrlsCls()
class NoUrlsCls(UrlsMixin, IntegrationPluginBase):
pass
self.mixin_nothing = NoUrlsCls()
def test_function(self):
plg_name = self.mixin.plugin_name()
# base_url
target_url = f'{settings.PLUGIN_URL}/{plg_name}/'
self.assertEqual(self.mixin.base_url, target_url)
# urlpattern
target_pattern = url(f'^{plg_name}/', include((self.mixin.urls, plg_name)), name=plg_name)
self.assertEqual(self.mixin.urlpatterns.reverse_dict, target_pattern.reverse_dict)
# resolve the view
self.assertEqual(self.mixin.urlpatterns.resolve('/testpath').func(), 'ccc')
self.assertEqual(self.mixin.urlpatterns.reverse('test'), 'testpath')
# no url
self.assertIsNone(self.mixin_nothing.urls)
self.assertIsNone(self.mixin_nothing.urlpatterns)
class AppMixinTest(BaseMixinDefinition, TestCase):
MIXIN_HUMAN_NAME = 'App registration'
MIXIN_NAME = 'app'
MIXIN_ENABLE_CHECK = 'has_app'
def setUp(self):
class TestCls(AppMixin, IntegrationPluginBase):
pass
self.mixin = TestCls()
def test_function(self):
# test that this plugin is in settings
self.assertIn('plugins.samples.integration', settings.INSTALLED_APPS)
class NavigationMixinTest(BaseMixinDefinition, TestCase):
MIXIN_HUMAN_NAME = 'Navigation Links'
MIXIN_NAME = 'navigation'
MIXIN_ENABLE_CHECK = 'has_naviation'
def setUp(self):
class NavigationCls(NavigationMixin, IntegrationPluginBase):
NAVIGATION = [
{'name': 'aa', 'link': 'plugin:test:test_view'},
]
self.mixin = NavigationCls()
def test_function(self):
# check right configuration
self.assertEqual(self.mixin.navigation, [{'name': 'aa', 'link': 'plugin:test:test_view'}, ])
# check wrong links fails
with self.assertRaises(NotImplementedError):
class NavigationCls(NavigationMixin, IntegrationPluginBase):
NAVIGATION = ['aa', 'aa']
NavigationCls()
class IntegrationPluginBaseTests(TestCase):
""" Tests for IntegrationPluginBase """
def setUp(self):
self.plugin = IntegrationPluginBase()
class SimpeIntegrationPluginBase(IntegrationPluginBase):
PLUGIN_NAME = 'SimplePlugin'
self.plugin_simple = SimpeIntegrationPluginBase()
class NameIntegrationPluginBase(IntegrationPluginBase):
PLUGIN_NAME = 'Aplugin'
PLUGIN_SLUG = 'a'
PLUGIN_TITLE = 'a titel'
PUBLISH_DATE = "1111-11-11"
VERSION = '1.2.3a'
WEBSITE = 'http://aa.bb/cc'
self.plugin_name = NameIntegrationPluginBase()
def test_action_name(self):
"""check the name definition possibilities"""
# plugin_name
self.assertEqual(self.plugin.plugin_name(), '')
self.assertEqual(self.plugin_simple.plugin_name(), 'SimplePlugin')
self.assertEqual(self.plugin_name.plugin_name(), 'Aplugin')
# slug
self.assertEqual(self.plugin.slug, '')
self.assertEqual(self.plugin_simple.slug, 'simpleplugin')
self.assertEqual(self.plugin_name.slug, 'a')
# human_name
self.assertEqual(self.plugin.human_name, '')
self.assertEqual(self.plugin_simple.human_name, 'SimplePlugin')
self.assertEqual(self.plugin_name.human_name, 'a titel')
# pub_date
self.assertEqual(self.plugin_name.pub_date, datetime(1111, 11, 11, 0, 0))
# version
self.assertEqual(self.plugin.version, None)
self.assertEqual(self.plugin_simple.version, None)
self.assertEqual(self.plugin_name.version, '1.2.3a')
# website
self.assertEqual(self.plugin.website, None)
self.assertEqual(self.plugin_simple.website, None)
self.assertEqual(self.plugin_name.website, 'http://aa.bb/cc')

View File

@ -1,74 +0,0 @@
""" Unit tests for plugins """
from django.test import TestCase
from django.conf import settings
import plugins.plugin
import plugins.integration
from plugins.samples.integration.sample import SampleIntegrationPlugin
from plugins.samples.integration.another_sample import WrongIntegrationPlugin, NoIntegrationPlugin
from plugins.plugins import load_integration_plugins # , load_action_plugins, load_barcode_plugins
import part.templatetags.plugin_extras as plugin_tags
class InvenTreePluginTests(TestCase):
""" Tests for InvenTreePlugin """
def setUp(self):
self.plugin = plugins.plugin.InvenTreePlugin()
class NamedPlugin(plugins.plugin.InvenTreePlugin):
"""a named plugin"""
PLUGIN_NAME = 'abc123'
self.named_plugin = NamedPlugin()
def test_basic_plugin_init(self):
"""check if a basic plugin intis"""
self.assertEqual(self.plugin.PLUGIN_NAME, '')
self.assertEqual(self.plugin.plugin_name(), '')
def test_basic_plugin_name(self):
"""check if the name of a basic plugin can be set"""
self.assertEqual(self.named_plugin.PLUGIN_NAME, 'abc123')
self.assertEqual(self.named_plugin.plugin_name(), 'abc123')
class PluginIntegrationTests(TestCase):
""" Tests for general plugin functions """
def test_plugin_loading(self):
"""check if plugins load as expected"""
plugin_names_integration = [a().plugin_name() for a in load_integration_plugins()]
# plugin_names_barcode = [a().plugin_name() for a in load_barcode_plugins()] # TODO refactor barcode plugin to support standard loading
# plugin_names_action = [a().plugin_name() for a in load_action_plugins()] # TODO refactor action plugin to support standard loading
self.assertEqual(plugin_names_integration, ['NoIntegrationPlugin', 'WrongIntegrationPlugin', 'SampleIntegrationPlugin'])
# self.assertEqual(plugin_names_action, '')
# self.assertEqual(plugin_names_barcode, '')
class PluginTagTests(TestCase):
""" Tests for the plugin extras """
def setUp(self):
self.sample = SampleIntegrationPlugin()
self.plugin_no = NoIntegrationPlugin()
self.plugin_wrong = WrongIntegrationPlugin()
def test_tag_plugin_list(self):
"""test that all plugins are listed"""
self.assertEqual(plugin_tags.plugin_list(), settings.INTEGRATION_PLUGIN_LIST)
def test_tag_plugin_settings(self):
"""check all plugins are listed"""
self.assertEqual(plugin_tags.plugin_settings(self.sample), settings.INTEGRATION_PLUGIN_SETTING.get(self.sample))
def test_tag_mixin_enabled(self):
"""check that mixin enabled functions work"""
key = 'urls'
# mixin enabled
self.assertEqual(plugin_tags.mixin_enabled(self.sample, key), True)
# mixin not enabled
self.assertEqual(plugin_tags.mixin_enabled(self.plugin_wrong, key), False)
# mxixn not existing
self.assertEqual(plugin_tags.mixin_enabled(self.plugin_no, key), False)