forked from openlp/openlp
Test Migration and Cleanup - 1
This commit is contained in:
parent
5caca527e1
commit
9039fe04f8
@ -1,30 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Base directory for tests
|
||||
"""
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
# Only one QApplication can be created. Use QtWidgets.QApplication.instance() when you need to "create" a QApplication.
|
||||
application = QtWidgets.QApplication([])
|
||||
application.setApplicationName('OpenLP')
|
||||
|
||||
__all__ = ['application']
|
@ -1,334 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Package to test the openlp.core.common.json package.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
from openlp.core.common import is_win
|
||||
from openlp.core.common.json import JSONMixin, OpenLPJSONDecoder, OpenLPJSONEncoder, PathSerializer, _registered_classes
|
||||
|
||||
|
||||
class BaseTestClass(object):
|
||||
"""
|
||||
Simple class to avoid repetition
|
||||
"""
|
||||
def __init__(self, a=None, b=None, c=None):
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.c = c
|
||||
|
||||
|
||||
class TestJSONMixin(TestCase):
|
||||
"""
|
||||
Test the JSONMixin class
|
||||
"""
|
||||
def setUp(self):
|
||||
self._registered_classes_patcher = patch.dict(_registered_classes, clear=True)
|
||||
self.addCleanup(self._registered_classes_patcher.stop)
|
||||
self._registered_classes_patcher.start()
|
||||
|
||||
def test_subclass_json_mixin(self):
|
||||
"""
|
||||
Test that a class is `registered` when subclassing JSONMixin
|
||||
"""
|
||||
# GIVEN: The JSONMixin class
|
||||
# WHEN: Subclassing it
|
||||
class TestClass(JSONMixin):
|
||||
pass
|
||||
|
||||
# THEN: The TestClass should have been `registered`
|
||||
assert _registered_classes['TestClass'] == TestClass
|
||||
|
||||
def test_subclass_json_mixin_alt_names(self):
|
||||
"""
|
||||
Test that a class is `registered` using the specified names when subclassing JSONMixin
|
||||
"""
|
||||
# GIVEN: The JSONMixin class
|
||||
# WHEN: Subclassing it with custom names
|
||||
class TestClass(JSONMixin, register_names=('AltName1', 'AltName2')):
|
||||
pass
|
||||
|
||||
# THEN: The TestClass should have been registered with only those names
|
||||
assert 'TestClass' not in _registered_classes
|
||||
assert _registered_classes['AltName1'] == TestClass
|
||||
assert _registered_classes['AltName2'] == TestClass
|
||||
|
||||
def test_encoding_json_mixin_subclass(self):
|
||||
"""
|
||||
Test that an instance of a JSONMixin subclass is properly serialized to a JSON string
|
||||
"""
|
||||
# GIVEN: A instance of a subclass of the JSONMixin class
|
||||
class TestClass(BaseTestClass, JSONMixin):
|
||||
_json_keys = ['a', 'b']
|
||||
|
||||
instance = TestClass(a=1, c=2)
|
||||
|
||||
# WHEN: Serializing the instance
|
||||
json_string = json.dumps(instance, cls=OpenLPJSONEncoder)
|
||||
|
||||
# THEN: Only the attributes specified by `_json_keys` should be serialized, and only if they have been set
|
||||
assert json_string == '{"a": 1, "json_meta": {"class": "TestClass", "version": 1}}'
|
||||
|
||||
def test_decoding_json_mixin_subclass(self):
|
||||
"""
|
||||
Test that an instance of a JSONMixin subclass is properly deserialized from a JSON string
|
||||
"""
|
||||
# GIVEN: A subclass of the JSONMixin class
|
||||
class TestClass(BaseTestClass, JSONMixin):
|
||||
_json_keys = ['a', 'b']
|
||||
|
||||
# WHEN: Deserializing a JSON representation of the TestClass
|
||||
instance = json.loads(
|
||||
'{"a": 1, "c": 2, "json_meta": {"class": "TestClass", "version": 1}}', cls=OpenLPJSONDecoder)
|
||||
|
||||
# THEN: Only the attributes specified by `_json_keys` should have been set
|
||||
assert instance.__class__ == TestClass
|
||||
assert instance.a == 1
|
||||
assert instance.b is None
|
||||
assert instance.c is None
|
||||
|
||||
def test_encoding_json_mixin_subclass_custom_name(self):
|
||||
"""
|
||||
Test that an instance of a JSONMixin subclass is properly serialized to a JSON string when using a custom name
|
||||
"""
|
||||
# GIVEN: A instance of a subclass of the JSONMixin class with a custom name
|
||||
class TestClass(BaseTestClass, JSONMixin, register_names=('AltName', )):
|
||||
_json_keys = ['a', 'b']
|
||||
_name = 'AltName'
|
||||
_version = 2
|
||||
|
||||
instance = TestClass(a=1, c=2)
|
||||
|
||||
# WHEN: Serializing the instance
|
||||
json_string = json.dumps(instance, cls=OpenLPJSONEncoder)
|
||||
|
||||
# THEN: Only the attributes specified by `_json_keys` should be serialized, and only if they have been set
|
||||
assert json_string == '{"a": 1, "json_meta": {"class": "AltName", "version": 2}}'
|
||||
|
||||
def test_decoding_json_mixin_subclass_custom_name(self):
|
||||
"""
|
||||
Test that an instance of a JSONMixin subclass is properly deserialized from a JSON string when using a custom
|
||||
name
|
||||
"""
|
||||
# GIVEN: A instance of a subclass of the JSONMixin class with a custom name
|
||||
class TestClass(BaseTestClass, JSONMixin, register_names=('AltName', )):
|
||||
_json_keys = ['a', 'b']
|
||||
_name = 'AltName'
|
||||
_version = 2
|
||||
|
||||
# WHEN: Deserializing a JSON representation of the TestClass
|
||||
instance = json.loads(
|
||||
'{"a": 1, "c": 2, "json_meta": {"class": "AltName", "version": 2}}', cls=OpenLPJSONDecoder)
|
||||
|
||||
# THEN: Only the attributes specified by `_json_keys` should have been set
|
||||
assert instance.__class__ == TestClass
|
||||
assert instance.a == 1
|
||||
assert instance.b is None
|
||||
assert instance.c is None
|
||||
|
||||
|
||||
class TestOpenLPJSONDecoder(TestCase):
|
||||
"""
|
||||
Test the OpenLPJsonDecoder class
|
||||
"""
|
||||
def test_object_hook_path_object(self):
|
||||
"""
|
||||
Test the object_hook method when called with a decoded Path JSON object
|
||||
"""
|
||||
# GIVEN: An instance of OpenLPJsonDecoder
|
||||
instance = OpenLPJSONDecoder()
|
||||
|
||||
# WHEN: Calling the object_hook method with a decoded JSON object which contains a Path
|
||||
result = instance.object_hook({'parts': ['test', 'path'], "json_meta": {"class": "Path", "version": 1}})
|
||||
|
||||
# THEN: A Path object should be returned
|
||||
assert result == Path('test', 'path')
|
||||
|
||||
def test_object_hook_non_path_object(self):
|
||||
"""
|
||||
Test the object_hook method when called with a decoded JSON object
|
||||
"""
|
||||
# GIVEN: An instance of OpenLPJsonDecoder
|
||||
instance = OpenLPJSONDecoder()
|
||||
|
||||
# WHEN: Calling the object_hook method with a decoded JSON object which contains a Path
|
||||
with patch('openlp.core.common.json.Path') as mocked_path:
|
||||
result = instance.object_hook({'key': 'value'})
|
||||
|
||||
# THEN: The object should be returned unchanged and a Path object should not have been initiated
|
||||
assert result == {'key': 'value'}
|
||||
assert mocked_path.called is False
|
||||
|
||||
def test_json_decode(self):
|
||||
"""
|
||||
Test the OpenLPJsonDecoder when decoding a JSON string
|
||||
"""
|
||||
# GIVEN: A JSON encoded string
|
||||
json_string = '[{"parts": ["test", "path1"], "json_meta": {"class": "Path", "version": 1}}, ' \
|
||||
'{"parts": ["test", "path2"], "json_meta": {"class": "Path", "version": 1}}, ' \
|
||||
'{"key": "value", "json_meta": {"class": "Object"}}]'
|
||||
|
||||
# WHEN: Decoding the string using the OpenLPJsonDecoder class
|
||||
obj = json.loads(json_string, cls=OpenLPJSONDecoder)
|
||||
|
||||
# THEN: The object returned should be a python version of the JSON string
|
||||
assert obj == [Path('test', 'path1'), Path('test', 'path2'), {'key': 'value', 'json_meta': {'class': 'Object'}}]
|
||||
|
||||
def test_json_decode_old_style(self):
|
||||
"""
|
||||
Test the OpenLPJsonDecoder when decoding a JSON string with an old-style Path object
|
||||
"""
|
||||
# GIVEN: A JSON encoded string
|
||||
json_string = '[{"__Path__": ["test", "path1"]}, ' \
|
||||
'{"__Path__": ["test", "path2"]}]'
|
||||
|
||||
# WHEN: Decoding the string using the OpenLPJsonDecoder class
|
||||
obj = json.loads(json_string, cls=OpenLPJSONDecoder)
|
||||
|
||||
# THEN: The object returned should be a python version of the JSON string
|
||||
assert obj == [Path('test', 'path1'), Path('test', 'path2')]
|
||||
|
||||
|
||||
class TestOpenLPJSONEncoder(TestCase):
|
||||
"""
|
||||
Test the OpenLPJSONEncoder class
|
||||
"""
|
||||
def test_default_path_object(self):
|
||||
"""
|
||||
Test the default method when called with a Path object
|
||||
"""
|
||||
# GIVEN: An instance of OpenLPJSONEncoder
|
||||
instance = OpenLPJSONEncoder()
|
||||
|
||||
# WHEN: Calling the default method with a Path object
|
||||
result = instance.default(Path('test', 'path'))
|
||||
|
||||
# THEN: A dictionary object that can be JSON encoded should be returned
|
||||
assert result == {'parts': ('test', 'path'), "json_meta": {"class": "Path", "version": 1}}
|
||||
|
||||
def test_default_non_path_object(self):
|
||||
"""
|
||||
Test the default method when called with a object other than a Path object
|
||||
"""
|
||||
with patch('openlp.core.common.json.JSONEncoder.default') as mocked_super_default:
|
||||
|
||||
# GIVEN: An instance of OpenLPJSONEncoder
|
||||
instance = OpenLPJSONEncoder()
|
||||
|
||||
# WHEN: Calling the default method with a object other than a Path object
|
||||
instance.default('invalid object')
|
||||
|
||||
# THEN: default method of the super class should have been called
|
||||
mocked_super_default.assert_called_once_with('invalid object')
|
||||
|
||||
def test_json_encode(self):
|
||||
"""
|
||||
Test the OpenLPJsonDEncoder when encoding an object conatining Path objects
|
||||
"""
|
||||
# GIVEN: A list of Path objects
|
||||
obj = [Path('test', 'path1'), Path('test', 'path2')]
|
||||
|
||||
# WHEN: Encoding the object using the OpenLPJSONEncoder class
|
||||
json_string = json.dumps(obj, cls=OpenLPJSONEncoder)
|
||||
|
||||
# THEN: The JSON string return should be a representation of the object encoded
|
||||
assert json_string == '[{"parts": ["test", "path1"], "json_meta": {"class": "Path", "version": 1}}, ' \
|
||||
'{"parts": ["test", "path2"], "json_meta": {"class": "Path", "version": 1}}]'
|
||||
|
||||
|
||||
class TestPathSerializer(TestCase):
|
||||
|
||||
def test_path_encode_json(self):
|
||||
"""
|
||||
Test that `Path.encode_json` returns a Path object from a dictionary representation of a Path object decoded
|
||||
from JSON
|
||||
"""
|
||||
# GIVEN: A Path object from openlp.core.common.path
|
||||
# WHEN: Calling encode_json, with a dictionary representation
|
||||
path = PathSerializer.encode_json(
|
||||
{'parts': ['path', 'to', 'fi.le'], "json_meta": {"class": "Path", "version": 1}}, extra=1, args=2)
|
||||
|
||||
# THEN: A Path object should have been returned
|
||||
assert path == Path('path', 'to', 'fi.le')
|
||||
|
||||
def test_path_encode_json_base_path(self):
|
||||
"""
|
||||
Test that `Path.encode_json` returns a Path object from a dictionary representation of a Path object decoded
|
||||
from JSON when the base_path arg is supplied.
|
||||
"""
|
||||
# GIVEN: A Path object from openlp.core.common.path
|
||||
# WHEN: Calling encode_json, with a dictionary representation
|
||||
path = PathSerializer.encode_json(
|
||||
{'parts': ['path', 'to', 'fi.le'], "json_meta": {"class": "Path", "version": 1}}, base_path=Path('/base'))
|
||||
|
||||
# THEN: A Path object should have been returned with an absolute path
|
||||
assert path == Path('/', 'base', 'path', 'to', 'fi.le')
|
||||
|
||||
def test_path_json_object(self):
|
||||
"""
|
||||
Test that `Path.json_object` creates a JSON decode-able object from a Path object
|
||||
"""
|
||||
# GIVEN: A Path object from openlp.core.common.path
|
||||
path = Path('/base', 'path', 'to', 'fi.le')
|
||||
|
||||
# WHEN: Calling json_object
|
||||
obj = PathSerializer().json_object(path, extra=1, args=2)
|
||||
|
||||
# THEN: A JSON decodeable object should have been returned.
|
||||
assert obj == {'parts': (os.sep, 'base', 'path', 'to', 'fi.le'), "json_meta": {"class": "Path", "version": 1}}
|
||||
|
||||
def test_path_json_object_is_js(self):
|
||||
"""
|
||||
Test that `Path.json_object` creates a JSON decode-able object from a Path object
|
||||
"""
|
||||
# GIVEN: A Path object from openlp.core.common.path
|
||||
if is_win():
|
||||
path = Path('c:\\', 'base', 'path', 'to', 'fi.le')
|
||||
else:
|
||||
path = Path('/base', 'path', 'to', 'fi.le')
|
||||
|
||||
# WHEN: Calling json_object
|
||||
obj = PathSerializer().json_object(path, is_js=True, extra=1, args=2)
|
||||
|
||||
# THEN: A URI should be returned
|
||||
if is_win():
|
||||
assert obj == 'file:///c:/base/path/to/fi.le'
|
||||
else:
|
||||
assert obj == 'file:///base/path/to/fi.le'
|
||||
|
||||
def test_path_json_object_base_path(self):
|
||||
"""
|
||||
Test that `Path.json_object` creates a JSON decode-able object from a Path object, that is relative to the
|
||||
base_path
|
||||
"""
|
||||
# GIVEN: A Path object from openlp.core.common.path
|
||||
path = Path('/base', 'path', 'to', 'fi.le')
|
||||
|
||||
# WHEN: Calling json_object with a base_path
|
||||
obj = PathSerializer().json_object(path, base_path=Path('/', 'base'))
|
||||
|
||||
# THEN: A JSON decodable object should have been returned.
|
||||
assert obj == {'parts': ('path', 'to', 'fi.le'), "json_meta": {"class": "Path", "version": 1}}
|
@ -1,23 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Package to test the openlp.core.ui package.
|
||||
"""
|
@ -1,23 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Interface tests
|
||||
"""
|
@ -1,99 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Package to test the openlp.core.lib.pluginmanager package.
|
||||
"""
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from tempfile import mkdtemp
|
||||
from unittest import TestCase, skip
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from openlp.core.common import is_win
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.state import State
|
||||
from openlp.core.lib.pluginmanager import PluginManager
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
|
||||
class TestPluginManager(TestCase, TestMixin):
|
||||
"""
|
||||
Test the PluginManager class
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Some pre-test setup required.
|
||||
"""
|
||||
self.setup_application()
|
||||
self.build_settings()
|
||||
self.temp_dir_path = Path(mkdtemp('openlp'))
|
||||
Settings().setValue('advanced/data path', self.temp_dir_path)
|
||||
Registry.create()
|
||||
Registry().register('service_list', MagicMock())
|
||||
self.main_window = QtWidgets.QMainWindow()
|
||||
Registry().register('main_window', self.main_window)
|
||||
|
||||
def tearDown(self):
|
||||
Settings().remove('advanced/data path')
|
||||
self.destroy_settings()
|
||||
del self.main_window
|
||||
# On windows we need to manually garbage collect to close sqlalchemy files
|
||||
# to avoid errors when temporary files are deleted.
|
||||
if is_win():
|
||||
import gc
|
||||
gc.collect()
|
||||
shutil.rmtree(self.temp_dir_path)
|
||||
|
||||
@skip
|
||||
# This test is broken but totally unable to debug it.
|
||||
@patch('openlp.plugins.songusage.songusageplugin.Manager')
|
||||
@patch('openlp.plugins.songs.songsplugin.Manager')
|
||||
@patch('openlp.plugins.images.imageplugin.Manager')
|
||||
@patch('openlp.plugins.custom.customplugin.Manager')
|
||||
@patch('openlp.plugins.alerts.alertsplugin.Manager')
|
||||
def test_find_plugins(self, mocked_is1, mocked_is2, mocked_is3, mocked_is4, mocked_is5):
|
||||
"""
|
||||
Test the find_plugins() method to ensure it imports the correct plugins
|
||||
"""
|
||||
# GIVEN: A plugin manager
|
||||
plugin_manager = PluginManager()
|
||||
plugin_manager.bootstrap_initialise()
|
||||
|
||||
# WHEN: We mock out sys.platform to make it return "darwin" and then find the plugins
|
||||
old_platform = sys.platform
|
||||
sys.platform = 'darwin'
|
||||
sys.platform = old_platform
|
||||
|
||||
# THEN: We should find the "Songs", "Bibles", etc in the plugins list
|
||||
plugin_names = [plugin.name for plugin in State().list_plugins()]
|
||||
assert 'songs' in plugin_names, 'There should be a "songs" plugin'
|
||||
assert 'bibles' in plugin_names, 'There should be a "bibles" plugin'
|
||||
assert 'presentations' in plugin_names, 'There should be a "presentations" plugin'
|
||||
assert 'images' in plugin_names, 'There should be a "images" plugin'
|
||||
assert 'media' in plugin_names, 'There should be a "media" plugin'
|
||||
assert 'custom' in plugin_names, 'There should be a "custom" plugin'
|
||||
assert 'songusage' in plugin_names, 'There should be a "songusage" plugin'
|
||||
assert 'alerts' in plugin_names, 'There should be a "alerts" plugin'
|
@ -1,23 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Module-level functions for the functional test suite
|
||||
"""
|
@ -1,86 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Package to test the openlp.core.ui.firsttimeform package.
|
||||
"""
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.ui.firsttimeform import ThemeListWidgetItem
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
|
||||
sample_theme_data = {'file_name': 'BlueBurst.otz', 'sha256': 'sha_256_hash',
|
||||
'thumbnail': 'BlueBurst.png', 'title': 'Blue Burst'}
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def mocked_set_icon(mock_settings):
|
||||
move_to_thread_patcher = patch('openlp.core.ui.firsttimeform.DownloadWorker.moveToThread').start()
|
||||
set_icon_patcher = patch('openlp.core.ui.firsttimeform.ThemeListWidgetItem.setIcon').start()
|
||||
q_thread_patcher = patch('openlp.core.ui.firsttimeform.QtCore.QThread').start()
|
||||
mocked_app = MagicMock()
|
||||
mocked_app.worker_threads = {}
|
||||
mocked_main_window = MagicMock()
|
||||
Registry().remove('application')
|
||||
Registry().register('application', mocked_app)
|
||||
Registry().register('main_window', mocked_main_window)
|
||||
yield set_icon_patcher
|
||||
move_to_thread_patcher.stop()
|
||||
set_icon_patcher.stop()
|
||||
q_thread_patcher.stop()
|
||||
|
||||
|
||||
def test_failed_download(mocked_set_icon):
|
||||
"""
|
||||
Test that icon get set to indicate a failure when `DownloadWorker` emits the download_failed signal
|
||||
"""
|
||||
# GIVEN: An instance of `DownloadWorker`
|
||||
instance = ThemeListWidgetItem('url', sample_theme_data, MagicMock()) # noqa Overcome GC issue
|
||||
worker_threads = Registry().get('application').worker_threads
|
||||
worker = worker_threads['thumbnail_download_BlueBurst.png']['worker']
|
||||
|
||||
# WHEN: `DownloadWorker` emits the `download_failed` signal
|
||||
worker.download_failed.emit()
|
||||
|
||||
# THEN: Then the initial loading icon should have been replaced by the exception icon
|
||||
mocked_set_icon.assert_has_calls([call(UiIcons().picture), call(UiIcons().exception)])
|
||||
|
||||
|
||||
@patch('openlp.core.ui.firsttimeform.build_icon')
|
||||
def test_successful_download(mocked_build_icon, mocked_set_icon):
|
||||
"""
|
||||
Test that the downloaded thumbnail is set as the icon when `DownloadWorker` emits the `download_succeeded`
|
||||
signal
|
||||
"""
|
||||
# GIVEN: An instance of `DownloadWorker`
|
||||
instance = ThemeListWidgetItem('url', sample_theme_data, MagicMock()) # noqa Overcome GC issue
|
||||
worker_threads = Registry().get('application').worker_threads
|
||||
worker = worker_threads['thumbnail_download_BlueBurst.png']['worker']
|
||||
test_path = Path('downlaoded', 'file')
|
||||
|
||||
# WHEN: `DownloadWorker` emits the `download_succeeded` signal
|
||||
worker.download_succeeded.emit(test_path)
|
||||
|
||||
# THEN: An icon should have been built from the downloaded file and used to replace the loading icon
|
||||
mocked_build_icon.assert_called_once_with(test_path)
|
||||
mocked_set_icon.assert_has_calls([call(UiIcons().picture), call(mocked_build_icon())])
|
@ -1,545 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Package to test the openlp.core.lib package.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.serviceitem import ItemCapabilities, ServiceItem
|
||||
from openlp.core.ui.servicemanager import ServiceManager
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
|
||||
class TestServiceManager(TestCase, TestMixin):
|
||||
"""
|
||||
Test the service manager
|
||||
"""
|
||||
|
||||
def _create_mock_action(self, name, **kwargs):
|
||||
"""
|
||||
Create a fake action with some "real" attributes
|
||||
"""
|
||||
action = QtWidgets.QAction(self.service_manager)
|
||||
action.setObjectName(name)
|
||||
if kwargs.get('triggers'):
|
||||
action.triggered.connect(kwargs.pop('triggers'))
|
||||
self.service_manager.toolbar.actions[name] = action
|
||||
return action
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the UI
|
||||
"""
|
||||
Registry.create()
|
||||
self.setup_application()
|
||||
Registry().register('application', MagicMock())
|
||||
Registry().register('main_window', MagicMock())
|
||||
Registry().register('settings', Settings())
|
||||
self.service_manager = ServiceManager()
|
||||
self.add_toolbar_action_patcher = patch('openlp.core.ui.servicemanager.OpenLPToolbar.add_toolbar_action')
|
||||
self.mocked_add_toolbar_action = self.add_toolbar_action_patcher.start()
|
||||
self.mocked_add_toolbar_action.side_effect = self._create_mock_action
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects at the end so that we don't have a segfault
|
||||
"""
|
||||
self.add_toolbar_action_patcher.stop()
|
||||
del self.service_manager
|
||||
|
||||
def test_basic_service_manager(self):
|
||||
"""
|
||||
Test the Service Manager UI Functionality
|
||||
"""
|
||||
# GIVEN: A New Service Manager instance
|
||||
# WHEN I have set up the display
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
|
||||
# THEN the count of items should be zero
|
||||
assert self.service_manager.service_manager_list.topLevelItemCount() == 0, \
|
||||
'The service manager list should be empty '
|
||||
|
||||
@patch('openlp.core.ui.servicemanager.QtWidgets.QTreeWidget.itemAt')
|
||||
@patch('openlp.core.ui.servicemanager.QtWidgets.QWidget.mapToGlobal')
|
||||
@patch('openlp.core.ui.servicemanager.QtWidgets.QMenu.exec')
|
||||
def test_default_context_menu(self, mocked_exec, mocked_mapToGlobal, mocked_item_at_method):
|
||||
"""
|
||||
Test the context_menu() method with a default service item
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
mocked_item.data.return_value = 1
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def test_edit_context_menu(self):
|
||||
"""
|
||||
Test the context_menu() method with a edit service item
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
|
||||
patch('PyQt5.QtWidgets.QMenu.exec'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||
service_item.edit_id = 1
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def test_maintain_context_menu(self):
|
||||
"""
|
||||
Test the context_menu() method with a maintain
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
|
||||
patch('PyQt5.QtWidgets.QMenu.exec'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanMaintain)
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def test_loopy_context_menu(self):
|
||||
"""
|
||||
Test the context_menu() method with a loop
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
|
||||
patch('PyQt5.QtWidgets.QMenu.exec'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||
service_item.slides.append("One")
|
||||
service_item.slides.append("Two")
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def test_start_time_context_menu(self):
|
||||
"""
|
||||
Test the context_menu() method with a start time
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
|
||||
patch('PyQt5.QtWidgets.QMenu.exec'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.HasVariableStartTime)
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def test_auto_start_context_menu(self):
|
||||
"""
|
||||
Test the context_menu() method with can auto start
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
|
||||
patch('PyQt5.QtWidgets.QMenu.exec'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
self.service_manager.rename_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.rename_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def test_click_on_new_service(self):
|
||||
"""
|
||||
Test the on_new_service event handler is called by the UI
|
||||
"""
|
||||
# GIVEN: An initial form
|
||||
mocked_event = MagicMock()
|
||||
self.service_manager.on_new_service_clicked = mocked_event
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
|
||||
# WHEN displaying the UI and pressing cancel
|
||||
new_service = self.service_manager.toolbar.actions['newService']
|
||||
new_service.trigger()
|
||||
|
||||
assert mocked_event.call_count == 1, 'The on_new_service_clicked method should have been called once'
|
||||
|
||||
def test_expand_selection_on_right_arrow(self):
|
||||
"""
|
||||
Test that a right arrow key press event calls the on_expand_selection function
|
||||
"""
|
||||
# GIVEN a mocked expand function
|
||||
self.service_manager.on_expand_selection = MagicMock()
|
||||
|
||||
# WHEN the right arrow key event is called
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Right, QtCore.Qt.NoModifier)
|
||||
self.service_manager.service_manager_list.keyPressEvent(event)
|
||||
|
||||
# THEN the on_expand_selection function should have been called.
|
||||
self.service_manager.on_expand_selection.assert_called_once_with()
|
||||
|
||||
def test_collapse_selection_on_left_arrow(self):
|
||||
"""
|
||||
Test that a left arrow key press event calls the on_collapse_selection function
|
||||
"""
|
||||
# GIVEN a mocked collapse function
|
||||
self.service_manager.on_collapse_selection = MagicMock()
|
||||
|
||||
# WHEN the left arrow key event is called
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Left, QtCore.Qt.NoModifier)
|
||||
self.service_manager.service_manager_list.keyPressEvent(event)
|
||||
|
||||
# THEN the on_collapse_selection function should have been called.
|
||||
self.service_manager.on_collapse_selection.assert_called_once_with()
|
||||
|
||||
def test_move_selection_down_on_down_arrow(self):
|
||||
"""
|
||||
Test that a down arrow key press event calls the on_move_selection_down function
|
||||
"""
|
||||
# GIVEN a mocked move down function
|
||||
self.service_manager.on_move_selection_down = MagicMock()
|
||||
|
||||
# WHEN the down arrow key event is called
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Down, QtCore.Qt.NoModifier)
|
||||
self.service_manager.service_manager_list.keyPressEvent(event)
|
||||
|
||||
# THEN the on_move_selection_down function should have been called.
|
||||
self.service_manager.on_move_selection_down.assert_called_once_with()
|
||||
|
||||
def test_move_selection_up_on_up_arrow(self):
|
||||
"""
|
||||
Test that an up arrow key press event calls the on_move_selection_up function
|
||||
"""
|
||||
# GIVEN a mocked move up function
|
||||
self.service_manager.on_move_selection_up = MagicMock()
|
||||
|
||||
# WHEN the up arrow key event is called
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Up, QtCore.Qt.NoModifier)
|
||||
self.service_manager.service_manager_list.keyPressEvent(event)
|
||||
|
||||
# THEN the on_move_selection_up function should have been called.
|
||||
self.service_manager.on_move_selection_up.assert_called_once_with()
|
||||
|
||||
def test_delete_selection_on_delete_key(self):
|
||||
"""
|
||||
Test that a delete key press event calls the on_delete_from_service function
|
||||
"""
|
||||
# GIVEN a mocked on_delete_from_service function
|
||||
self.service_manager.on_delete_from_service = MagicMock()
|
||||
|
||||
# WHEN the delete key event is called
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Delete, QtCore.Qt.NoModifier)
|
||||
self.service_manager.service_manager_list.keyPressEvent(event)
|
||||
|
||||
# THEN the on_delete_from_service function should have been called.
|
||||
self.service_manager.on_delete_from_service.assert_called_once_with()
|
||||
|
||||
def _setup_service_manager_list(self):
|
||||
self.service_manager.expanded = MagicMock()
|
||||
self.service_manager.collapsed = MagicMock()
|
||||
verse_1 = QtWidgets.QTreeWidgetItem(0)
|
||||
verse_2 = QtWidgets.QTreeWidgetItem(0)
|
||||
song_item = QtWidgets.QTreeWidgetItem(0)
|
||||
song_item.addChild(verse_1)
|
||||
song_item.addChild(verse_2)
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
self.service_manager.service_manager_list.addTopLevelItem(song_item)
|
||||
return verse_1, verse_2, song_item
|
||||
|
||||
def test_on_expand_selection(self):
|
||||
"""
|
||||
Test that the on_expand_selection function successfully expands an item and moves to its first child
|
||||
"""
|
||||
# GIVEN a mocked servicemanager list
|
||||
verse_1, verse_2, song_item = self._setup_service_manager_list()
|
||||
self.service_manager.service_manager_list.setCurrentItem(song_item)
|
||||
# Reset expanded function in case it has been called and/or changed in initialisation of the service manager.
|
||||
self.service_manager.expanded = MagicMock()
|
||||
|
||||
# WHEN on_expand_selection is called
|
||||
self.service_manager.on_expand_selection()
|
||||
|
||||
# THEN selection should be expanded
|
||||
selected_index = self.service_manager.service_manager_list.currentIndex()
|
||||
above_selected_index = self.service_manager.service_manager_list.indexAbove(selected_index)
|
||||
assert self.service_manager.service_manager_list.isExpanded(above_selected_index) is True, \
|
||||
'Item should have been expanded'
|
||||
self.service_manager.expanded.assert_called_once_with(song_item)
|
||||
|
||||
def test_on_collapse_selection_with_parent_selected(self):
|
||||
"""
|
||||
Test that the on_collapse_selection function successfully collapses an item
|
||||
"""
|
||||
# GIVEN a mocked servicemanager list
|
||||
verse_1, verse_2, song_item = self._setup_service_manager_list()
|
||||
self.service_manager.service_manager_list.setCurrentItem(song_item)
|
||||
self.service_manager.service_manager_list.expandItem(song_item)
|
||||
|
||||
# Reset collapsed function in case it has been called and/or changed in initialisation of the service manager.
|
||||
self.service_manager.collapsed = MagicMock()
|
||||
|
||||
# WHEN on_expand_selection is called
|
||||
self.service_manager.on_collapse_selection()
|
||||
|
||||
# THEN selection should be expanded
|
||||
selected_index = self.service_manager.service_manager_list.currentIndex()
|
||||
assert self.service_manager.service_manager_list.isExpanded(selected_index) is False, \
|
||||
'Item should have been collapsed'
|
||||
assert self.service_manager.service_manager_list.currentItem() == song_item, \
|
||||
'Top item should have been selected'
|
||||
self.service_manager.collapsed.assert_called_once_with(song_item)
|
||||
|
||||
def test_on_collapse_selection_with_child_selected(self):
|
||||
"""
|
||||
Test that the on_collapse_selection function successfully collapses child's parent item
|
||||
and moves selection to its parent.
|
||||
"""
|
||||
# GIVEN a mocked servicemanager list
|
||||
verse_1, verse_2, song_item = self._setup_service_manager_list()
|
||||
self.service_manager.service_manager_list.setCurrentItem(verse_2)
|
||||
self.service_manager.service_manager_list.expandItem(song_item)
|
||||
# Reset collapsed function in case it has been called and/or changed in initialisation of the service manager.
|
||||
self.service_manager.collapsed = MagicMock()
|
||||
|
||||
# WHEN on_expand_selection is called
|
||||
self.service_manager.on_collapse_selection()
|
||||
|
||||
# THEN selection should be expanded
|
||||
selected_index = self.service_manager.service_manager_list.currentIndex()
|
||||
assert self.service_manager.service_manager_list.isExpanded(selected_index) is False, \
|
||||
'Item should have been collapsed'
|
||||
assert self.service_manager.service_manager_list.currentItem() == song_item, \
|
||||
'Top item should have been selected'
|
||||
self.service_manager.collapsed.assert_called_once_with(song_item)
|
||||
|
||||
def test_replace_service_item(self):
|
||||
"""
|
||||
Tests that the replace_service_item function replaces items as expected
|
||||
"""
|
||||
# GIVEN a service item list and a new item which name and edit_id match a service item
|
||||
self.service_manager.repaint_service_list = MagicMock()
|
||||
Registry().register('live_controller', MagicMock())
|
||||
item1 = MagicMock()
|
||||
item1.edit_id = 'abcd'
|
||||
item1.name = 'itemA'
|
||||
item2 = MagicMock()
|
||||
item2.edit_id = 'abcd'
|
||||
item2.name = 'itemB'
|
||||
item3 = MagicMock()
|
||||
item3.edit_id = 'cfgh'
|
||||
item3.name = 'itemA'
|
||||
self.service_manager.service_items = [
|
||||
{'service_item': item1},
|
||||
{'service_item': item2},
|
||||
{'service_item': item3}
|
||||
]
|
||||
new_item = MagicMock()
|
||||
new_item.edit_id = 'abcd'
|
||||
new_item.name = 'itemA'
|
||||
|
||||
# WHEN replace_service_item is called
|
||||
self.service_manager.replace_service_item(new_item)
|
||||
|
||||
# THEN new_item should replace item1, and only replaces that one item
|
||||
assert self.service_manager.service_items[0]['service_item'] == new_item
|
||||
new_item.merge.assert_called_once_with(item1)
|
@ -1,123 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Interface tests to test the themeManager class and related methods.
|
||||
"""
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.ui.thememanager import ThemeManager
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def theme_manager(settings):
|
||||
thm = ThemeManager()
|
||||
return thm
|
||||
|
||||
|
||||
def test_theme_manager_initialise(theme_manager):
|
||||
"""
|
||||
Test the thememanager initialise - basic test
|
||||
"""
|
||||
# GIVEN: A new a call to initialise
|
||||
theme_manager.setup_ui = MagicMock()
|
||||
theme_manager.build_theme_path = MagicMock()
|
||||
Settings().setValue('themes/global theme', 'my_theme')
|
||||
|
||||
# WHEN: the initialisation is run
|
||||
theme_manager.bootstrap_initialise()
|
||||
|
||||
# THEN:
|
||||
theme_manager.setup_ui.assert_called_once_with(theme_manager)
|
||||
assert theme_manager.global_theme == 'my_theme'
|
||||
theme_manager.build_theme_path.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('openlp.core.ui.thememanager.create_paths')
|
||||
@patch('openlp.core.ui.thememanager.AppLocation.get_section_data_path')
|
||||
def test_build_theme_path(mocked_get_section_data_path, mocked_create_paths, theme_manager):
|
||||
"""
|
||||
Test the thememanager build_theme_path
|
||||
"""
|
||||
# GIVEN: A mocked out AppLocation.get_directory() and mocked create_paths
|
||||
mocked_get_section_data_path.return_value = Path('tests/my_theme')
|
||||
|
||||
# WHEN: the build_theme_path is run
|
||||
theme_manager.build_theme_path()
|
||||
|
||||
# THEN: The theme path and the thumb path should be correct
|
||||
assert theme_manager.theme_path == Path('tests/my_theme')
|
||||
assert theme_manager.thumb_path == Path('tests/my_theme/thumbnails')
|
||||
mocked_create_paths.assert_called_once_with(Path('tests/my_theme'), Path('tests/my_theme/thumbnails'))
|
||||
|
||||
|
||||
def test_click_on_new_theme(theme_manager):
|
||||
"""
|
||||
Test the on_add_theme event handler is called by the UI
|
||||
"""
|
||||
# GIVEN: An initial form
|
||||
Settings().setValue('themes/global theme', 'my_theme')
|
||||
mocked_event = MagicMock()
|
||||
theme_manager.on_add_theme = mocked_event
|
||||
theme_manager.setup_ui(theme_manager)
|
||||
|
||||
# WHEN displaying the UI and pressing cancel
|
||||
new_theme = theme_manager.toolbar.actions['newTheme']
|
||||
new_theme.trigger()
|
||||
|
||||
assert mocked_event.call_count == 1, 'The on_add_theme method should have been called once'
|
||||
|
||||
|
||||
@patch('openlp.core.ui.themeform.ThemeForm._setup')
|
||||
@patch('openlp.core.ui.filerenameform.FileRenameForm._setup')
|
||||
def test_bootstrap_post(mocked_rename_form, mocked_theme_form, theme_manager):
|
||||
"""
|
||||
Test the functions of bootstrap_post_setup are called.
|
||||
"""
|
||||
# GIVEN:
|
||||
theme_manager.theme_path = MagicMock()
|
||||
|
||||
# WHEN:
|
||||
with patch('openlp.core.ui.thememanager.ThemeProgressForm'):
|
||||
theme_manager.bootstrap_post_set_up()
|
||||
|
||||
# THEN:
|
||||
assert theme_manager.progress_form is not None
|
||||
assert theme_manager.theme_form is not None
|
||||
assert theme_manager.file_rename_form is not None
|
||||
|
||||
|
||||
def test_bootstrap_completion(theme_manager):
|
||||
"""
|
||||
Test the functions of bootstrap_post_setup are called.
|
||||
"""
|
||||
# GIVEN:
|
||||
theme_manager.load_themes = MagicMock()
|
||||
theme_manager.upgrade_themes = MagicMock()
|
||||
|
||||
# WHEN:
|
||||
theme_manager.bootstrap_completion()
|
||||
|
||||
# THEN:
|
||||
theme_manager.upgrade_themes.assert_called_once()
|
||||
theme_manager.load_themes.assert_called_once()
|
@ -1,143 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Module to test the EditCustomForm.
|
||||
"""
|
||||
import pytest
|
||||
from unittest.mock import MagicMock, call
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtTest, QtWidgets
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.widgets.edits import HistoryComboBox, SearchEdit
|
||||
|
||||
|
||||
class SearchTypes(object):
|
||||
"""
|
||||
Types of search
|
||||
"""
|
||||
First = 0
|
||||
Second = 1
|
||||
|
||||
|
||||
SECOND_PLACEHOLDER_TEXT = "Second Placeholder Text"
|
||||
SEARCH_TYPES = [(SearchTypes.First, QtGui.QIcon(), "First", "First Placeholder Text"),
|
||||
(SearchTypes.Second, QtGui.QIcon(), "Second", SECOND_PLACEHOLDER_TEXT)]
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def search_edit(mock_settings):
|
||||
main_window = QtWidgets.QMainWindow()
|
||||
Registry().register('main_window', main_window)
|
||||
Registry().remove('settings')
|
||||
Registry().register('settings', MagicMock(**{'value.return_value': SearchTypes.First}))
|
||||
|
||||
s_edit = SearchEdit(main_window, 'settings_section')
|
||||
# To complete set up we have to set the search types.
|
||||
s_edit.set_search_types(SEARCH_TYPES)
|
||||
return s_edit
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def combo(mock_settings):
|
||||
main_window = QtWidgets.QMainWindow()
|
||||
Registry().register('main_window', main_window)
|
||||
s_combo = HistoryComboBox(main_window)
|
||||
return s_combo
|
||||
|
||||
|
||||
def test_set_search_types(search_edit):
|
||||
"""
|
||||
Test setting the search types of the search edit.
|
||||
"""
|
||||
# GIVEN: The search edit with the search types set. NOTE: The set_search_types(types) is called in the setUp()
|
||||
# method!
|
||||
|
||||
# WHEN:
|
||||
|
||||
# THEN: The first search type should be the first one in the list. The selected type should be saved in the
|
||||
# settings
|
||||
assert search_edit.current_search_type() == SearchTypes.First, \
|
||||
"The first search type should be selected."
|
||||
Registry().get('settings').setValue.assert_called_once_with('settings_section/last used search type', 0)
|
||||
|
||||
|
||||
def test_set_current_search_type(search_edit):
|
||||
"""
|
||||
Test if changing the search type works.
|
||||
"""
|
||||
# GIVEN:
|
||||
# WHEN: Change the search type
|
||||
result = search_edit.set_current_search_type(SearchTypes.Second)
|
||||
|
||||
# THEN:
|
||||
assert result is True, "The call should return success (True)."
|
||||
assert search_edit.current_search_type() == SearchTypes.Second, \
|
||||
"The search type should be SearchTypes.Second"
|
||||
assert search_edit.placeholderText() == SECOND_PLACEHOLDER_TEXT, \
|
||||
"The correct placeholder text should be 'Second Placeholder Text'."
|
||||
Registry().get('settings').setValue.assert_has_calls(
|
||||
[call('settings_section/last used search type', 0), call('settings_section/last used search type', 1)])
|
||||
|
||||
|
||||
def test_clear_button_visibility(search_edit):
|
||||
"""
|
||||
Test if the clear button is hidden/shown correctly.
|
||||
"""
|
||||
# GIVEN: Everything is left to its defaults (hidden).
|
||||
assert search_edit.clear_button.isHidden(), "Pre condition not met. Button should be hidden."
|
||||
|
||||
# WHEN: Type something in the search edit.
|
||||
QtTest.QTest.keyPress(search_edit, QtCore.Qt.Key_A)
|
||||
QtTest.QTest.keyRelease(search_edit, QtCore.Qt.Key_A)
|
||||
|
||||
# THEN: The clear button should not be hidden any more.
|
||||
assert not search_edit.clear_button.isHidden(), "The clear button should be visible."
|
||||
|
||||
|
||||
def test_press_clear_button(search_edit):
|
||||
"""
|
||||
Check if the search edit behaves correctly when pressing the clear button.
|
||||
"""
|
||||
# GIVEN: A search edit with text.
|
||||
QtTest.QTest.keyPress(search_edit, QtCore.Qt.Key_A)
|
||||
QtTest.QTest.keyRelease(search_edit, QtCore.Qt.Key_A)
|
||||
|
||||
# WHEN: Press the clear button.
|
||||
QtTest.QTest.mouseClick(search_edit.clear_button, QtCore.Qt.LeftButton)
|
||||
|
||||
# THEN: The search edit text should be cleared and the button be hidden.
|
||||
assert not search_edit.text(), "The search edit should not have any text."
|
||||
assert search_edit.clear_button.isHidden(), "The clear button should be hidden."
|
||||
|
||||
|
||||
def test_history_combo_get_items(combo):
|
||||
"""
|
||||
Test the getItems() method
|
||||
"""
|
||||
# GIVEN: The combo.
|
||||
|
||||
# WHEN: Add two items.
|
||||
combo.addItem('test1')
|
||||
combo.addItem('test2')
|
||||
|
||||
# THEN: The list of items should contain both strings.
|
||||
assert combo.getItems() == ['test1', 'test2']
|
@ -1,92 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Package to test the openlp.core.widgets.views.
|
||||
"""
|
||||
import pytest
|
||||
from unittest.mock import patch
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.serviceitem import ServiceItem
|
||||
from openlp.core.widgets.views import ListPreviewWidget
|
||||
from tests.utils.osdinteraction import read_service_from_file
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def preview_widget(settings):
|
||||
main_window = QtWidgets.QMainWindow()
|
||||
Registry().register('main_window', main_window)
|
||||
p_widget = ListPreviewWidget(main_window, 2)
|
||||
return p_widget
|
||||
|
||||
|
||||
def test_initial_slide_count(preview_widget):
|
||||
"""
|
||||
Test the initial slide count .
|
||||
"""
|
||||
# GIVEN: A new ListPreviewWidget instance.
|
||||
# WHEN: No SlideItem has been added yet.
|
||||
# THEN: The count of items should be zero.
|
||||
assert preview_widget.slide_count() == 0, 'The slide list should be empty.'
|
||||
|
||||
|
||||
def test_initial_slide_number(preview_widget):
|
||||
"""
|
||||
Test the initial current slide number.
|
||||
"""
|
||||
# GIVEN: A new ListPreviewWidget instance.
|
||||
# WHEN: No SlideItem has been added yet.
|
||||
# THEN: The number of the current item should be -1.
|
||||
assert preview_widget.current_slide_number() == -1, 'The slide number should be -1.'
|
||||
|
||||
|
||||
def test_replace_service_item(preview_widget, state_media):
|
||||
"""
|
||||
Test item counts and current number with a service item.
|
||||
"""
|
||||
# GIVEN: A ServiceItem with two frames.
|
||||
service_item = ServiceItem(None)
|
||||
service = read_service_from_file('serviceitem_image_3.osj')
|
||||
with patch('os.path.exists') and patch('openlp.core.lib.serviceitem.sha256_file_hash'):
|
||||
service_item.set_from_service(service[0])
|
||||
# WHEN: Added to the preview widget.
|
||||
preview_widget.replace_service_item(service_item, 1, 1)
|
||||
# THEN: The slide count and number should fit.
|
||||
assert preview_widget.slide_count() == 2, 'The slide count should be 2.'
|
||||
assert preview_widget.current_slide_number() == 1, 'The current slide number should be 1.'
|
||||
|
||||
|
||||
def test_change_slide(preview_widget, state_media):
|
||||
"""
|
||||
Test the change_slide method.
|
||||
"""
|
||||
# GIVEN: A ServiceItem with two frames content.
|
||||
service_item = ServiceItem(None)
|
||||
service = read_service_from_file('serviceitem_image_3.osj')
|
||||
with patch('os.path.exists') and patch('openlp.core.lib.serviceitem.sha256_file_hash'):
|
||||
service_item.set_from_service(service[0])
|
||||
# WHEN: Added to the preview widget and switched to the second frame.
|
||||
preview_widget.replace_service_item(service_item, 1, 0)
|
||||
preview_widget.change_slide(1)
|
||||
# THEN: The current_slide_number should reflect the change.
|
||||
assert preview_widget.current_slide_number() == 1, 'The current slide number should be 1.'
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -1,167 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Package to test the openlp.plugins.planningcenter.planningcenterplugin package.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.state import State
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.core.ui.settingsform import SettingsForm
|
||||
from openlp.plugins.planningcenter.planningcenterplugin import PlanningCenterPlugin
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
|
||||
class TestPlanningCenterPlugin(TestCase, TestMixin):
|
||||
"""
|
||||
Test the PlanningcenterPlugin class
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the UI
|
||||
"""
|
||||
self.setup_application()
|
||||
self.registry = Registry()
|
||||
Registry.create()
|
||||
State().load_settings()
|
||||
Registry().register('settings', Settings())
|
||||
self.plugin = PlanningCenterPlugin()
|
||||
self.settings_form = SettingsForm()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects at the end so that we don't have a segfault
|
||||
"""
|
||||
del self.registry
|
||||
del self.plugin
|
||||
del self.settings_form
|
||||
|
||||
def test_class_init_defaults(self):
|
||||
"""
|
||||
Test that the plugin class is instantiated with the correct defaults
|
||||
"""
|
||||
# GIVEN: A PlanningcenterPlugin Class
|
||||
# WHEN: the class has been through __init__
|
||||
# THEN:
|
||||
# planningcenter form is set to None
|
||||
self.assertEqual(self.plugin.planningcenter_form, None, "Init plugin set to None")
|
||||
# icon is set correctly
|
||||
self.assertEqual(self.plugin.icon, UiIcons().planning_center, "Init icon set to planning_center icon")
|
||||
# weight is -1
|
||||
self.assertEqual(self.plugin.weight, -1, "Init weight set to -1")
|
||||
# the planning_center module is registered active
|
||||
self.assertEqual(State().is_module_active('planning_center'), True, "Init State() is active")
|
||||
|
||||
def test_initialise(self):
|
||||
"""
|
||||
Test that the initialise function can be called and it passes a call along
|
||||
to its parent class
|
||||
"""
|
||||
# GIVEN: A PlanningcenterPlugin Class
|
||||
# WHEN: initialise has been called on the class
|
||||
with patch('openlp.plugins.planningcenter.planningcenterplugin.PlanningCenterPlugin.import_planning_center',
|
||||
create=True):
|
||||
return_value = self.plugin.initialise()
|
||||
# THEN:
|
||||
# the function returns and does not fail... it doesn't do much at this point, so this
|
||||
# is mainly to improve test coverage
|
||||
self.assertEqual(return_value, None, "Initialise was called on the class and it didn't crash")
|
||||
|
||||
def test_import_menu_item_added(self):
|
||||
"""
|
||||
Test that the add_import_menu_item function adds the menu item
|
||||
"""
|
||||
# GIVEN: A PlanningcenterPlugin Class
|
||||
# WHEN: add_import_menu_item is called
|
||||
import_menu = QtWidgets.QMenu()
|
||||
self.plugin.add_import_menu_item(import_menu)
|
||||
self.plugin.import_planning_center.setVisible(True)
|
||||
# THEN:
|
||||
# the menu should not be empty
|
||||
self.assertEqual(import_menu.isEmpty(), False, "Menu Item is populated")
|
||||
|
||||
@patch('openlp.plugins.planningcenter.forms.selectplanform.SelectPlanForm.exec')
|
||||
@patch('openlp.core.ui.settingsform.SettingsForm.exec')
|
||||
def test_on_import_planning_center_triggered_with_auth_settings(self, mock_editauth_exec, mock_selectplan_exec):
|
||||
"""
|
||||
Test that the on_import_planning_center_triggered function correctly returns
|
||||
the correct form to display.
|
||||
"""
|
||||
# GIVEN: A PlanningCenterPlugin Class with mocked exec calls on both
|
||||
# PlanningCenter forms and settings set
|
||||
application_id = 'abc'
|
||||
secret = '123'
|
||||
Settings().setValue('planningcenter/application_id', application_id)
|
||||
Settings().setValue('planningcenter/secret', secret)
|
||||
# init the planning center plugin so we have default values defined for Settings()
|
||||
# WHEN: on_import_planning_center_triggered is called
|
||||
self.plugin.on_import_planning_center_triggered()
|
||||
# THEN:
|
||||
self.assertEqual(mock_selectplan_exec.call_count, 1, "Select Plan Form was shown")
|
||||
self.assertEqual(mock_editauth_exec.call_count, 0, "Edit Auth Form was not shown")
|
||||
|
||||
@patch('openlp.plugins.planningcenter.forms.selectplanform.SelectPlanForm.exec')
|
||||
@patch('openlp.core.ui.settingsform.SettingsForm.exec')
|
||||
def test_on_import_planning_center_triggered_without_auth_settings(self, mock_editauth_exec, mock_selectplan_exec):
|
||||
"""
|
||||
Test that the on_import_planning_center_triggered function correctly returns
|
||||
the correct form to display.
|
||||
"""
|
||||
# GIVEN: A PlanningCenterPlugin Class with mocked exec calls on both
|
||||
# PlanningCenter forms and settings set
|
||||
application_id = ''
|
||||
secret = ''
|
||||
Settings().setValue('planningcenter/application_id', application_id)
|
||||
Settings().setValue('planningcenter/secret', secret)
|
||||
# init the planning center plugin so we have default values defined for Settings()
|
||||
# WHEN: on_import_planning_center_triggered is called
|
||||
self.plugin.on_import_planning_center_triggered()
|
||||
# THEN:
|
||||
self.assertEqual(mock_selectplan_exec.call_count, 0, "Select Plan Form was not shown")
|
||||
self.assertEqual(mock_editauth_exec.call_count, 1, "Edit Auth Form was shown")
|
||||
|
||||
def test_about(self):
|
||||
"""
|
||||
Test that the about function returns text.
|
||||
"""
|
||||
# GIVEN: A PlanningCenterPlugin Class
|
||||
# WHEN: about() is called
|
||||
return_value = self.plugin.about()
|
||||
# THEN:
|
||||
self.assertGreater(len(return_value), 0, "About function returned some text")
|
||||
|
||||
def test_finalise(self):
|
||||
"""
|
||||
Test that the finalise function cleans up after the plugin
|
||||
"""
|
||||
# GIVEN: A PlanningcenterPlugin Class with a bunch of mocks
|
||||
self.plugin.import_planning_center = MagicMock()
|
||||
|
||||
# WHEN: finalise has been called on the class
|
||||
self.plugin.finalise()
|
||||
|
||||
# THEN: it cleans up after itself
|
||||
self.plugin.import_planning_center.setVisible.assert_called_once_with(False)
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
@ -21,6 +21,7 @@
|
||||
"""
|
||||
Functional tests to test the AppLocation class and related methods.
|
||||
"""
|
||||
import os
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from unittest import skipUnless
|
||||
@ -34,7 +35,7 @@ from openlp.core.common import Singleton, add_actions, clean_filename, clean_but
|
||||
trace_error_handler, verify_ip_address
|
||||
|
||||
from tests.resources.projector.data import TEST_HASH, TEST_PIN, TEST_SALT
|
||||
|
||||
from tests.utils.constants import TEST_RESOURCES_PATH
|
||||
|
||||
test_non_ascii_string = '이것은 한국어 시험 문자열'
|
||||
test_non_ascii_hash = 'fc00c7912976f6e9c19099b514ced201'
|
||||
@ -441,7 +442,8 @@ def test_sha256_file_hash():
|
||||
Test SHA256 file hash
|
||||
"""
|
||||
# GIVEN: A mocked Path object
|
||||
filename = Path('tests/resources/presentations/test.ppt')
|
||||
ppt_path = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'test.ppt')
|
||||
filename = Path(ppt_path)
|
||||
|
||||
# WHEN: Given a known salt+data
|
||||
result = sha256_file_hash(filename)
|
333
tests/openlp_core/common/test_json.py
Normal file
333
tests/openlp_core/common/test_json.py
Normal file
@ -0,0 +1,333 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2021 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Package to test the openlp.core.common.json package.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
from openlp.core.common import is_win
|
||||
from openlp.core.common.json import JSONMixin, OpenLPJSONDecoder, OpenLPJSONEncoder, PathSerializer, _registered_classes
|
||||
|
||||
|
||||
class BaseTestClass(object):
|
||||
"""
|
||||
Simple class to avoid repetition
|
||||
"""
|
||||
def __init__(self, a=None, b=None, c=None):
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.c = c
|
||||
|
||||
|
||||
class TestJSONMixin(TestCase):
|
||||
"""
|
||||
Test the JSONMixin class
|
||||
"""
|
||||
def setUp(self):
|
||||
self._registered_classes_patcher = patch.dict(_registered_classes, clear=True)
|
||||
self.addCleanup(self._registered_classes_patcher.stop)
|
||||
self._registered_classes_patcher.start()
|
||||
|
||||
def test_subclass_json_mixin(self):
|
||||
"""
|
||||
Test that a class is `registered` when subclassing JSONMixin
|
||||
"""
|
||||
# GIVEN: The JSONMixin class
|
||||
# WHEN: Subclassing it
|
||||
class TestClass(JSONMixin):
|
||||
pass
|
||||
|
||||
# THEN: The TestClass should have been `registered`
|
||||
assert _registered_classes['TestClass'] == TestClass
|
||||
|
||||
def test_subclass_json_mixin_alt_names(self):
|
||||
"""
|
||||
Test that a class is `registered` using the specified names when subclassing JSONMixin
|
||||
"""
|
||||
# GIVEN: The JSONMixin class
|
||||
# WHEN: Subclassing it with custom names
|
||||
class TestClass(JSONMixin, register_names=('AltName1', 'AltName2')):
|
||||
pass
|
||||
|
||||
# THEN: The TestClass should have been registered with only those names
|
||||
assert 'TestClass' not in _registered_classes
|
||||
assert _registered_classes['AltName1'] == TestClass
|
||||
assert _registered_classes['AltName2'] == TestClass
|
||||
|
||||
def test_encoding_json_mixin_subclass(self):
|
||||
"""
|
||||
Test that an instance of a JSONMixin subclass is properly serialized to a JSON string
|
||||
"""
|
||||
# GIVEN: A instance of a subclass of the JSONMixin class
|
||||
class TestClass(BaseTestClass, JSONMixin):
|
||||
_json_keys = ['a', 'b']
|
||||
|
||||
instance = TestClass(a=1, c=2)
|
||||
|
||||
# WHEN: Serializing the instance
|
||||
json_string = json.dumps(instance, cls=OpenLPJSONEncoder)
|
||||
|
||||
# THEN: Only the attributes specified by `_json_keys` should be serialized, and only if they have been set
|
||||
assert json_string == '{"a": 1, "json_meta": {"class": "TestClass", "version": 1}}'
|
||||
|
||||
def test_decoding_json_mixin_subclass(self):
|
||||
"""
|
||||
Test that an instance of a JSONMixin subclass is properly deserialized from a JSON string
|
||||
"""
|
||||
# GIVEN: A subclass of the JSONMixin class
|
||||
class TestClass(BaseTestClass, JSONMixin):
|
||||
_json_keys = ['a', 'b']
|
||||
|
||||
# WHEN: Deserializing a JSON representation of the TestClass
|
||||
instance = json.loads(
|
||||
'{"a": 1, "c": 2, "json_meta": {"class": "TestClass", "version": 1}}', cls=OpenLPJSONDecoder)
|
||||
|
||||
# THEN: Only the attributes specified by `_json_keys` should have been set
|
||||
assert instance.__class__ == TestClass
|
||||
assert instance.a == 1
|
||||
assert instance.b is None
|
||||
assert instance.c is None
|
||||
|
||||
def test_encoding_json_mixin_subclass_custom_name(self):
|
||||
"""
|
||||
Test that an instance of a JSONMixin subclass is properly serialized to a JSON string when using a custom name
|
||||
"""
|
||||
# GIVEN: A instance of a subclass of the JSONMixin class with a custom name
|
||||
class TestClass(BaseTestClass, JSONMixin, register_names=('AltName', )):
|
||||
_json_keys = ['a', 'b']
|
||||
_name = 'AltName'
|
||||
_version = 2
|
||||
|
||||
instance = TestClass(a=1, c=2)
|
||||
|
||||
# WHEN: Serializing the instance
|
||||
json_string = json.dumps(instance, cls=OpenLPJSONEncoder)
|
||||
|
||||
# THEN: Only the attributes specified by `_json_keys` should be serialized, and only if they have been set
|
||||
assert json_string == '{"a": 1, "json_meta": {"class": "AltName", "version": 2}}'
|
||||
|
||||
def test_decoding_json_mixin_subclass_custom_name(self):
|
||||
"""
|
||||
Test that an instance of a JSONMixin subclass is properly deserialized from a JSON string when using a custom
|
||||
name
|
||||
"""
|
||||
# GIVEN: A instance of a subclass of the JSONMixin class with a custom name
|
||||
class TestClass(BaseTestClass, JSONMixin, register_names=('AltName', )):
|
||||
_json_keys = ['a', 'b']
|
||||
_name = 'AltName'
|
||||
_version = 2
|
||||
|
||||
# WHEN: Deserializing a JSON representation of the TestClass
|
||||
instance = json.loads(
|
||||
'{"a": 1, "c": 2, "json_meta": {"class": "AltName", "version": 2}}', cls=OpenLPJSONDecoder)
|
||||
|
||||
# THEN: Only the attributes specified by `_json_keys` should have been set
|
||||
assert instance.__class__ == TestClass
|
||||
assert instance.a == 1
|
||||
assert instance.b is None
|
||||
assert instance.c is None
|
||||
|
||||
|
||||
def test_object_hook_path_object():
|
||||
"""
|
||||
Test the object_hook method when called with a decoded Path JSON object
|
||||
"""
|
||||
# GIVEN: An instance of OpenLPJsonDecoder
|
||||
instance = OpenLPJSONDecoder()
|
||||
|
||||
# WHEN: Calling the object_hook method with a decoded JSON object which contains a Path
|
||||
result = instance.object_hook({'parts': ['test', 'path'], "json_meta": {"class": "Path", "version": 1}})
|
||||
|
||||
# THEN: A Path object should be returned
|
||||
assert result == Path('test', 'path')
|
||||
|
||||
|
||||
def test_object_hook_non_path_object():
|
||||
"""
|
||||
Test the object_hook method when called with a decoded JSON object
|
||||
"""
|
||||
# GIVEN: An instance of OpenLPJsonDecoder
|
||||
instance = OpenLPJSONDecoder()
|
||||
|
||||
# WHEN: Calling the object_hook method with a decoded JSON object which contains a Path
|
||||
with patch('openlp.core.common.json.Path') as mocked_path:
|
||||
result = instance.object_hook({'key': 'value'})
|
||||
|
||||
# THEN: The object should be returned unchanged and a Path object should not have been initiated
|
||||
assert result == {'key': 'value'}
|
||||
assert mocked_path.called is False
|
||||
|
||||
|
||||
def test_json_decode():
|
||||
"""
|
||||
Test the OpenLPJsonDecoder when decoding a JSON string
|
||||
"""
|
||||
# GIVEN: A JSON encoded string
|
||||
json_string = '[{"parts": ["test", "path1"], "json_meta": {"class": "Path", "version": 1}}, ' \
|
||||
'{"parts": ["test", "path2"], "json_meta": {"class": "Path", "version": 1}}, ' \
|
||||
'{"key": "value", "json_meta": {"class": "Object"}}]'
|
||||
|
||||
# WHEN: Decoding the string using the OpenLPJsonDecoder class
|
||||
obj = json.loads(json_string, cls=OpenLPJSONDecoder)
|
||||
|
||||
# THEN: The object returned should be a python version of the JSON string
|
||||
assert obj == [Path('test', 'path1'), Path('test', 'path2'), {'key': 'value', 'json_meta': {'class': 'Object'}}]
|
||||
|
||||
|
||||
def test_json_decode_old_style():
|
||||
"""
|
||||
Test the OpenLPJsonDecoder when decoding a JSON string with an old-style Path object
|
||||
"""
|
||||
# GIVEN: A JSON encoded string
|
||||
json_string = '[{"__Path__": ["test", "path1"]}, ' \
|
||||
'{"__Path__": ["test", "path2"]}]'
|
||||
|
||||
# WHEN: Decoding the string using the OpenLPJsonDecoder class
|
||||
obj = json.loads(json_string, cls=OpenLPJSONDecoder)
|
||||
|
||||
# THEN: The object returned should be a python version of the JSON string
|
||||
assert obj == [Path('test', 'path1'), Path('test', 'path2')]
|
||||
|
||||
|
||||
def test_default_path_object():
|
||||
"""
|
||||
Test the default method when called with a Path object
|
||||
"""
|
||||
# GIVEN: An instance of OpenLPJSONEncoder
|
||||
instance = OpenLPJSONEncoder()
|
||||
|
||||
# WHEN: Calling the default method with a Path object
|
||||
result = instance.default(Path('test', 'path'))
|
||||
|
||||
# THEN: A dictionary object that can be JSON encoded should be returned
|
||||
assert result == {'parts': ('test', 'path'), "json_meta": {"class": "Path", "version": 1}}
|
||||
|
||||
|
||||
def test_default_non_path_object():
|
||||
"""
|
||||
Test the default method when called with a object other than a Path object
|
||||
"""
|
||||
with patch('openlp.core.common.json.JSONEncoder.default') as mocked_super_default:
|
||||
|
||||
# GIVEN: An instance of OpenLPJSONEncoder
|
||||
instance = OpenLPJSONEncoder()
|
||||
|
||||
# WHEN: Calling the default method with a object other than a Path object
|
||||
instance.default('invalid object')
|
||||
|
||||
# THEN: default method of the super class should have been called
|
||||
mocked_super_default.assert_called_once_with('invalid object')
|
||||
|
||||
|
||||
def test_json_encode():
|
||||
"""
|
||||
Test the OpenLPJsonDEncoder when encoding an object conatining Path objects
|
||||
"""
|
||||
# GIVEN: A list of Path objects
|
||||
obj = [Path('test', 'path1'), Path('test', 'path2')]
|
||||
|
||||
# WHEN: Encoding the object using the OpenLPJSONEncoder class
|
||||
json_string = json.dumps(obj, cls=OpenLPJSONEncoder)
|
||||
|
||||
# THEN: The JSON string return should be a representation of the object encoded
|
||||
assert json_string == '[{"parts": ["test", "path1"], "json_meta": {"class": "Path", "version": 1}}, ' \
|
||||
'{"parts": ["test", "path2"], "json_meta": {"class": "Path", "version": 1}}]'
|
||||
|
||||
|
||||
def test_path_encode_json():
|
||||
"""
|
||||
Test that `Path.encode_json` returns a Path object from a dictionary representation of a Path object decoded
|
||||
from JSON
|
||||
"""
|
||||
# GIVEN: A Path object from openlp.core.common.path
|
||||
# WHEN: Calling encode_json, with a dictionary representation
|
||||
path = PathSerializer.encode_json(
|
||||
{'parts': ['path', 'to', 'fi.le'], "json_meta": {"class": "Path", "version": 1}}, extra=1, args=2)
|
||||
|
||||
# THEN: A Path object should have been returned
|
||||
assert path == Path('path', 'to', 'fi.le')
|
||||
|
||||
|
||||
def test_path_encode_json_base_path():
|
||||
"""
|
||||
Test that `Path.encode_json` returns a Path object from a dictionary representation of a Path object decoded
|
||||
from JSON when the base_path arg is supplied.
|
||||
"""
|
||||
# GIVEN: A Path object from openlp.core.common.path
|
||||
# WHEN: Calling encode_json, with a dictionary representation
|
||||
path = PathSerializer.encode_json(
|
||||
{'parts': ['path', 'to', 'fi.le'], "json_meta": {"class": "Path", "version": 1}}, base_path=Path('/base'))
|
||||
|
||||
# THEN: A Path object should have been returned with an absolute path
|
||||
assert path == Path('/', 'base', 'path', 'to', 'fi.le')
|
||||
|
||||
|
||||
def test_path_json_object():
|
||||
"""
|
||||
Test that `Path.json_object` creates a JSON decode-able object from a Path object
|
||||
"""
|
||||
# GIVEN: A Path object from openlp.core.common.path
|
||||
path = Path('/base', 'path', 'to', 'fi.le')
|
||||
|
||||
# WHEN: Calling json_object
|
||||
obj = PathSerializer().json_object(path, extra=1, args=2)
|
||||
|
||||
# THEN: A JSON decodeable object should have been returned.
|
||||
assert obj == {'parts': (os.sep, 'base', 'path', 'to', 'fi.le'), "json_meta": {"class": "Path", "version": 1}}
|
||||
|
||||
|
||||
def test_path_json_object_is_js():
|
||||
"""
|
||||
Test that `Path.json_object` creates a JSON decode-able object from a Path object
|
||||
"""
|
||||
# GIVEN: A Path object from openlp.core.common.path
|
||||
if is_win():
|
||||
path = Path('c:\\', 'base', 'path', 'to', 'fi.le')
|
||||
else:
|
||||
path = Path('/base', 'path', 'to', 'fi.le')
|
||||
|
||||
# WHEN: Calling json_object
|
||||
obj = PathSerializer().json_object(path, is_js=True, extra=1, args=2)
|
||||
|
||||
# THEN: A URI should be returned
|
||||
if is_win():
|
||||
assert obj == 'file:///c:/base/path/to/fi.le'
|
||||
else:
|
||||
assert obj == 'file:///base/path/to/fi.le'
|
||||
|
||||
|
||||
def test_path_json_object_base_path():
|
||||
"""
|
||||
Test that `Path.json_object` creates a JSON decode-able object from a Path object, that is relative to the
|
||||
base_path
|
||||
"""
|
||||
# GIVEN: A Path object from openlp.core.common.path
|
||||
path = Path('/base', 'path', 'to', 'fi.le')
|
||||
|
||||
# WHEN: Calling json_object with a base_path
|
||||
obj = PathSerializer().json_object(path, base_path=Path('/', 'base'))
|
||||
|
||||
# THEN: A JSON decodable object should have been returned.
|
||||
assert obj == {'parts': ('path', 'to', 'fi.le'), "json_meta": {"class": "Path", "version": 1}}
|
@ -21,14 +21,23 @@
|
||||
"""
|
||||
Package to test the openlp.core.lib.pluginmanager package.
|
||||
"""
|
||||
import shutil
|
||||
import sys
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from tempfile import mkdtemp
|
||||
from unittest import TestCase, skip
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from openlp.core.state import State
|
||||
from openlp.core.common import is_win
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
from openlp.core.lib.pluginmanager import PluginManager
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@ -536,3 +545,64 @@ def test_new_service_created_with_active_plugin(registry, state):
|
||||
# THEN: The is_active() and finalise() methods should have been called
|
||||
mocked_plugin.is_active.assert_called_with()
|
||||
mocked_plugin.new_service_created.assert_called_with()
|
||||
|
||||
|
||||
class TestPluginManager(TestCase, TestMixin):
|
||||
"""
|
||||
Test the PluginManager class
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Some pre-test setup required.
|
||||
"""
|
||||
self.setup_application()
|
||||
self.build_settings()
|
||||
self.temp_dir_path = Path(mkdtemp('openlp'))
|
||||
Settings().setValue('advanced/data path', self.temp_dir_path)
|
||||
Registry.create()
|
||||
Registry().register('service_list', MagicMock())
|
||||
self.main_window = QtWidgets.QMainWindow()
|
||||
Registry().register('main_window', self.main_window)
|
||||
|
||||
def tearDown(self):
|
||||
Settings().remove('advanced/data path')
|
||||
self.destroy_settings()
|
||||
del self.main_window
|
||||
# On windows we need to manually garbage collect to close sqlalchemy files
|
||||
# to avoid errors when temporary files are deleted.
|
||||
if is_win():
|
||||
import gc
|
||||
gc.collect()
|
||||
shutil.rmtree(self.temp_dir_path)
|
||||
|
||||
@skip
|
||||
# This test is broken but totally unable to debug it.
|
||||
@patch('openlp.plugins.songusage.songusageplugin.Manager')
|
||||
@patch('openlp.plugins.songs.songsplugin.Manager')
|
||||
@patch('openlp.plugins.images.imageplugin.Manager')
|
||||
@patch('openlp.plugins.custom.customplugin.Manager')
|
||||
@patch('openlp.plugins.alerts.alertsplugin.Manager')
|
||||
def test_find_plugins(self, mocked_is1, mocked_is2, mocked_is3, mocked_is4, mocked_is5):
|
||||
"""
|
||||
Test the find_plugins() method to ensure it imports the correct plugins
|
||||
"""
|
||||
# GIVEN: A plugin manager
|
||||
plugin_manager = PluginManager()
|
||||
plugin_manager.bootstrap_initialise()
|
||||
|
||||
# WHEN: We mock out sys.platform to make it return "darwin" and then find the plugins
|
||||
old_platform = sys.platform
|
||||
sys.platform = 'darwin'
|
||||
sys.platform = old_platform
|
||||
|
||||
# THEN: We should find the "Songs", "Bibles", etc in the plugins list
|
||||
plugin_names = [plugin.name for plugin in State().list_plugins()]
|
||||
assert 'songs' in plugin_names, 'There should be a "songs" plugin'
|
||||
assert 'bibles' in plugin_names, 'There should be a "bibles" plugin'
|
||||
assert 'presentations' in plugin_names, 'There should be a "presentations" plugin'
|
||||
assert 'images' in plugin_names, 'There should be a "images" plugin'
|
||||
assert 'media' in plugin_names, 'There should be a "media" plugin'
|
||||
assert 'custom' in plugin_names, 'There should be a "custom" plugin'
|
||||
assert 'songusage' in plugin_names, 'There should be a "songusage" plugin'
|
||||
assert 'alerts' in plugin_names, 'There should be a "alerts" plugin'
|
@ -30,6 +30,7 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.ui.firsttimeform import FirstTimeForm, ThemeListWidgetItem
|
||||
from openlp.core.ui.firsttimewizard import RemotePage, ThemeListWidget
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
|
||||
|
||||
INVALID_CONFIG = """
|
||||
@ -59,6 +60,23 @@ def download_env(registry):
|
||||
run_thread_patcher.stop()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def mocked_set_icon(mock_settings):
|
||||
move_to_thread_patcher = patch('openlp.core.ui.firsttimeform.DownloadWorker.moveToThread').start()
|
||||
set_icon_patcher = patch('openlp.core.ui.firsttimeform.ThemeListWidgetItem.setIcon').start()
|
||||
q_thread_patcher = patch('openlp.core.ui.firsttimeform.QtCore.QThread').start()
|
||||
mocked_app = MagicMock()
|
||||
mocked_app.worker_threads = {}
|
||||
mocked_main_window = MagicMock()
|
||||
Registry().remove('application')
|
||||
Registry().register('application', mocked_app)
|
||||
Registry().register('main_window', mocked_main_window)
|
||||
yield set_icon_patcher
|
||||
move_to_thread_patcher.stop()
|
||||
set_icon_patcher.stop()
|
||||
q_thread_patcher.stop()
|
||||
|
||||
|
||||
def test_init_sample_data(download_env):
|
||||
"""
|
||||
Test that the theme data is loaded correctly in to a ThemeListWidgetItem object when instantiated
|
||||
@ -428,3 +446,39 @@ def test_theme_list_widget_resize(ftf_app):
|
||||
|
||||
# THEN: Check that the correct calculations were done
|
||||
mocked_setGridSize.assert_called_once_with(QtCore.QSize(149, 140))
|
||||
|
||||
|
||||
def test_failed_download(mocked_set_icon):
|
||||
"""
|
||||
Test that icon get set to indicate a failure when `DownloadWorker` emits the download_failed signal
|
||||
"""
|
||||
# GIVEN: An instance of `DownloadWorker`
|
||||
instance = ThemeListWidgetItem('url', sample_theme_data, MagicMock()) # noqa Overcome GC issue
|
||||
worker_threads = Registry().get('application').worker_threads
|
||||
worker = worker_threads['thumbnail_download_BlueBurst.png']['worker']
|
||||
|
||||
# WHEN: `DownloadWorker` emits the `download_failed` signal
|
||||
worker.download_failed.emit()
|
||||
|
||||
# THEN: Then the initial loading icon should have been replaced by the exception icon
|
||||
mocked_set_icon.assert_has_calls([call(UiIcons().picture), call(UiIcons().exception)])
|
||||
|
||||
|
||||
@patch('openlp.core.ui.firsttimeform.build_icon')
|
||||
def test_successful_download(mocked_build_icon, mocked_set_icon):
|
||||
"""
|
||||
Test that the downloaded thumbnail is set as the icon when `DownloadWorker` emits the `download_succeeded`
|
||||
signal
|
||||
"""
|
||||
# GIVEN: An instance of `DownloadWorker`
|
||||
instance = ThemeListWidgetItem('url', sample_theme_data, MagicMock()) # noqa Overcome GC issue
|
||||
worker_threads = Registry().get('application').worker_threads
|
||||
worker = worker_threads['thumbnail_download_BlueBurst.png']['worker']
|
||||
test_path = Path('downlaoded', 'file')
|
||||
|
||||
# WHEN: `DownloadWorker` emits the `download_succeeded` signal
|
||||
worker.download_succeeded.emit(test_path)
|
||||
|
||||
# THEN: An icon should have been built from the downloaded file and used to replace the loading icon
|
||||
mocked_build_icon.assert_called_once_with(test_path)
|
||||
mocked_set_icon.assert_has_calls([call(UiIcons().picture), call(mocked_build_icon())])
|
@ -21,17 +21,23 @@
|
||||
"""
|
||||
Package to test the openlp.core.ui.slidecontroller package.
|
||||
"""
|
||||
import PyQt5
|
||||
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import PyQt5
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common import ThemeLevel
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.common.enum import ServiceItemType
|
||||
from openlp.core.lib.serviceitem import ItemCapabilities, ServiceItem
|
||||
from openlp.core.ui.servicemanager import ServiceManager
|
||||
from openlp.core.widgets.toolbar import OpenLPToolbar
|
||||
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
|
||||
def test_initial_service_manager(registry):
|
||||
"""
|
||||
@ -756,3 +762,515 @@ def test_theme_change_song(mocked_regenerate_service_items, registry):
|
||||
# THEN: The the theme toolbar should be visible
|
||||
assert service_manager.toolbar.actions['theme_combo_box'].isVisible() is True, \
|
||||
'The visibility should be True'
|
||||
|
||||
|
||||
class TestServiceManager(TestCase, TestMixin):
|
||||
"""
|
||||
Test the service manager
|
||||
"""
|
||||
|
||||
def _create_mock_action(self, name, **kwargs):
|
||||
"""
|
||||
Create a fake action with some "real" attributes
|
||||
"""
|
||||
action = QtWidgets.QAction(self.service_manager)
|
||||
action.setObjectName(name)
|
||||
if kwargs.get('triggers'):
|
||||
action.triggered.connect(kwargs.pop('triggers'))
|
||||
self.service_manager.toolbar.actions[name] = action
|
||||
return action
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the UI
|
||||
"""
|
||||
Registry.create()
|
||||
self.setup_application()
|
||||
Registry().register('application', MagicMock())
|
||||
Registry().register('main_window', MagicMock())
|
||||
Registry().register('settings', Settings())
|
||||
self.service_manager = ServiceManager()
|
||||
self.add_toolbar_action_patcher = patch('openlp.core.ui.servicemanager.OpenLPToolbar.add_toolbar_action')
|
||||
self.mocked_add_toolbar_action = self.add_toolbar_action_patcher.start()
|
||||
self.mocked_add_toolbar_action.side_effect = self._create_mock_action
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects at the end so that we don't have a segfault
|
||||
"""
|
||||
self.add_toolbar_action_patcher.stop()
|
||||
del self.service_manager
|
||||
|
||||
def test_basic_service_manager(self):
|
||||
"""
|
||||
Test the Service Manager UI Functionality
|
||||
"""
|
||||
# GIVEN: A New Service Manager instance
|
||||
# WHEN I have set up the display
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
|
||||
# THEN the count of items should be zero
|
||||
assert self.service_manager.service_manager_list.topLevelItemCount() == 0, \
|
||||
'The service manager list should be empty '
|
||||
|
||||
@patch('openlp.core.ui.servicemanager.QtWidgets.QTreeWidget.itemAt')
|
||||
@patch('openlp.core.ui.servicemanager.QtWidgets.QWidget.mapToGlobal')
|
||||
@patch('openlp.core.ui.servicemanager.QtWidgets.QMenu.exec')
|
||||
def test_default_context_menu(self, mocked_exec, mocked_mapToGlobal, mocked_item_at_method):
|
||||
"""
|
||||
Test the context_menu() method with a default service item
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
mocked_item.data.return_value = 1
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def test_edit_context_menu(self):
|
||||
"""
|
||||
Test the context_menu() method with a edit service item
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
|
||||
patch('PyQt5.QtWidgets.QMenu.exec'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||
service_item.edit_id = 1
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def test_maintain_context_menu(self):
|
||||
"""
|
||||
Test the context_menu() method with a maintain
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
|
||||
patch('PyQt5.QtWidgets.QMenu.exec'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanMaintain)
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def test_loopy_context_menu(self):
|
||||
"""
|
||||
Test the context_menu() method with a loop
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
|
||||
patch('PyQt5.QtWidgets.QMenu.exec'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||
service_item.slides.append("One")
|
||||
service_item.slides.append("Two")
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def test_start_time_context_menu(self):
|
||||
"""
|
||||
Test the context_menu() method with a start time
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
|
||||
patch('PyQt5.QtWidgets.QMenu.exec'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.HasVariableStartTime)
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def test_auto_start_context_menu(self):
|
||||
"""
|
||||
Test the context_menu() method with can auto start
|
||||
"""
|
||||
# GIVEN: A service item added
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
|
||||
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
|
||||
patch('PyQt5.QtWidgets.QMenu.exec'):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.parent.return_value = None
|
||||
mocked_item_at_method.return_value = mocked_item
|
||||
# We want 1 to be returned for the position
|
||||
mocked_item.data.return_value = 1
|
||||
# A service item without capabilities.
|
||||
service_item = ServiceItem()
|
||||
service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
|
||||
self.service_manager.service_items = [{'service_item': service_item}]
|
||||
q_point = None
|
||||
# Mocked actions.
|
||||
self.service_manager.edit_action.setVisible = MagicMock()
|
||||
self.service_manager.create_custom_action.setVisible = MagicMock()
|
||||
self.service_manager.maintain_action.setVisible = MagicMock()
|
||||
self.service_manager.notes_action.setVisible = MagicMock()
|
||||
self.service_manager.time_action.setVisible = MagicMock()
|
||||
self.service_manager.auto_start_action.setVisible = MagicMock()
|
||||
self.service_manager.rename_action.setVisible = MagicMock()
|
||||
|
||||
# WHEN: Show the context menu.
|
||||
self.service_manager.context_menu(q_point)
|
||||
|
||||
# THEN: The following actions should be not visible.
|
||||
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
|
||||
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
self.service_manager.auto_start_action.setVisible.assert_called_with(True), \
|
||||
'The action should be set visible.'
|
||||
self.service_manager.rename_action.setVisible.assert_called_once_with(False), \
|
||||
'The action should be set invisible.'
|
||||
|
||||
def test_click_on_new_service(self):
|
||||
"""
|
||||
Test the on_new_service event handler is called by the UI
|
||||
"""
|
||||
# GIVEN: An initial form
|
||||
mocked_event = MagicMock()
|
||||
self.service_manager.on_new_service_clicked = mocked_event
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
|
||||
# WHEN displaying the UI and pressing cancel
|
||||
new_service = self.service_manager.toolbar.actions['newService']
|
||||
new_service.trigger()
|
||||
|
||||
assert mocked_event.call_count == 1, 'The on_new_service_clicked method should have been called once'
|
||||
|
||||
def test_expand_selection_on_right_arrow(self):
|
||||
"""
|
||||
Test that a right arrow key press event calls the on_expand_selection function
|
||||
"""
|
||||
# GIVEN a mocked expand function
|
||||
self.service_manager.on_expand_selection = MagicMock()
|
||||
|
||||
# WHEN the right arrow key event is called
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Right, QtCore.Qt.NoModifier)
|
||||
self.service_manager.service_manager_list.keyPressEvent(event)
|
||||
|
||||
# THEN the on_expand_selection function should have been called.
|
||||
self.service_manager.on_expand_selection.assert_called_once_with()
|
||||
|
||||
def test_collapse_selection_on_left_arrow(self):
|
||||
"""
|
||||
Test that a left arrow key press event calls the on_collapse_selection function
|
||||
"""
|
||||
# GIVEN a mocked collapse function
|
||||
self.service_manager.on_collapse_selection = MagicMock()
|
||||
|
||||
# WHEN the left arrow key event is called
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Left, QtCore.Qt.NoModifier)
|
||||
self.service_manager.service_manager_list.keyPressEvent(event)
|
||||
|
||||
# THEN the on_collapse_selection function should have been called.
|
||||
self.service_manager.on_collapse_selection.assert_called_once_with()
|
||||
|
||||
def test_move_selection_down_on_down_arrow(self):
|
||||
"""
|
||||
Test that a down arrow key press event calls the on_move_selection_down function
|
||||
"""
|
||||
# GIVEN a mocked move down function
|
||||
self.service_manager.on_move_selection_down = MagicMock()
|
||||
|
||||
# WHEN the down arrow key event is called
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Down, QtCore.Qt.NoModifier)
|
||||
self.service_manager.service_manager_list.keyPressEvent(event)
|
||||
|
||||
# THEN the on_move_selection_down function should have been called.
|
||||
self.service_manager.on_move_selection_down.assert_called_once_with()
|
||||
|
||||
def test_move_selection_up_on_up_arrow(self):
|
||||
"""
|
||||
Test that an up arrow key press event calls the on_move_selection_up function
|
||||
"""
|
||||
# GIVEN a mocked move up function
|
||||
self.service_manager.on_move_selection_up = MagicMock()
|
||||
|
||||
# WHEN the up arrow key event is called
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Up, QtCore.Qt.NoModifier)
|
||||
self.service_manager.service_manager_list.keyPressEvent(event)
|
||||
|
||||
# THEN the on_move_selection_up function should have been called.
|
||||
self.service_manager.on_move_selection_up.assert_called_once_with()
|
||||
|
||||
def test_delete_selection_on_delete_key(self):
|
||||
"""
|
||||
Test that a delete key press event calls the on_delete_from_service function
|
||||
"""
|
||||
# GIVEN a mocked on_delete_from_service function
|
||||
self.service_manager.on_delete_from_service = MagicMock()
|
||||
|
||||
# WHEN the delete key event is called
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Delete, QtCore.Qt.NoModifier)
|
||||
self.service_manager.service_manager_list.keyPressEvent(event)
|
||||
|
||||
# THEN the on_delete_from_service function should have been called.
|
||||
self.service_manager.on_delete_from_service.assert_called_once_with()
|
||||
|
||||
def _setup_service_manager_list(self):
|
||||
self.service_manager.expanded = MagicMock()
|
||||
self.service_manager.collapsed = MagicMock()
|
||||
verse_1 = QtWidgets.QTreeWidgetItem(0)
|
||||
verse_2 = QtWidgets.QTreeWidgetItem(0)
|
||||
song_item = QtWidgets.QTreeWidgetItem(0)
|
||||
song_item.addChild(verse_1)
|
||||
song_item.addChild(verse_2)
|
||||
self.service_manager.setup_ui(self.service_manager)
|
||||
self.service_manager.service_manager_list.addTopLevelItem(song_item)
|
||||
return verse_1, verse_2, song_item
|
||||
|
||||
def test_on_expand_selection(self):
|
||||
"""
|
||||
Test that the on_expand_selection function successfully expands an item and moves to its first child
|
||||
"""
|
||||
# GIVEN a mocked servicemanager list
|
||||
verse_1, verse_2, song_item = self._setup_service_manager_list()
|
||||
self.service_manager.service_manager_list.setCurrentItem(song_item)
|
||||
# Reset expanded function in case it has been called and/or changed in initialisation of the service manager.
|
||||
self.service_manager.expanded = MagicMock()
|
||||
|
||||
# WHEN on_expand_selection is called
|
||||
self.service_manager.on_expand_selection()
|
||||
|
||||
# THEN selection should be expanded
|
||||
selected_index = self.service_manager.service_manager_list.currentIndex()
|
||||
above_selected_index = self.service_manager.service_manager_list.indexAbove(selected_index)
|
||||
assert self.service_manager.service_manager_list.isExpanded(above_selected_index) is True, \
|
||||
'Item should have been expanded'
|
||||
self.service_manager.expanded.assert_called_once_with(song_item)
|
||||
|
||||
def test_on_collapse_selection_with_parent_selected(self):
|
||||
"""
|
||||
Test that the on_collapse_selection function successfully collapses an item
|
||||
"""
|
||||
# GIVEN a mocked servicemanager list
|
||||
verse_1, verse_2, song_item = self._setup_service_manager_list()
|
||||
self.service_manager.service_manager_list.setCurrentItem(song_item)
|
||||
self.service_manager.service_manager_list.expandItem(song_item)
|
||||
|
||||
# Reset collapsed function in case it has been called and/or changed in initialisation of the service manager.
|
||||
self.service_manager.collapsed = MagicMock()
|
||||
|
||||
# WHEN on_expand_selection is called
|
||||
self.service_manager.on_collapse_selection()
|
||||
|
||||
# THEN selection should be expanded
|
||||
selected_index = self.service_manager.service_manager_list.currentIndex()
|
||||
assert self.service_manager.service_manager_list.isExpanded(selected_index) is False, \
|
||||
'Item should have been collapsed'
|
||||
assert self.service_manager.service_manager_list.currentItem() == song_item, \
|
||||
'Top item should have been selected'
|
||||
self.service_manager.collapsed.assert_called_once_with(song_item)
|
||||
|
||||
def test_on_collapse_selection_with_child_selected(self):
|
||||
"""
|
||||
Test that the on_collapse_selection function successfully collapses child's parent item
|
||||
and moves selection to its parent.
|
||||
"""
|
||||
# GIVEN a mocked servicemanager list
|
||||
verse_1, verse_2, song_item = self._setup_service_manager_list()
|
||||
self.service_manager.service_manager_list.setCurrentItem(verse_2)
|
||||
self.service_manager.service_manager_list.expandItem(song_item)
|
||||
# Reset collapsed function in case it has been called and/or changed in initialisation of the service manager.
|
||||
self.service_manager.collapsed = MagicMock()
|
||||
|
||||
# WHEN on_expand_selection is called
|
||||
self.service_manager.on_collapse_selection()
|
||||
|
||||
# THEN selection should be expanded
|
||||
selected_index = self.service_manager.service_manager_list.currentIndex()
|
||||
assert self.service_manager.service_manager_list.isExpanded(selected_index) is False, \
|
||||
'Item should have been collapsed'
|
||||
assert self.service_manager.service_manager_list.currentItem() == song_item, \
|
||||
'Top item should have been selected'
|
||||
self.service_manager.collapsed.assert_called_once_with(song_item)
|
||||
|
||||
def test_replace_service_item(self):
|
||||
"""
|
||||
Tests that the replace_service_item function replaces items as expected
|
||||
"""
|
||||
# GIVEN a service item list and a new item which name and edit_id match a service item
|
||||
self.service_manager.repaint_service_list = MagicMock()
|
||||
Registry().register('live_controller', MagicMock())
|
||||
item1 = MagicMock()
|
||||
item1.edit_id = 'abcd'
|
||||
item1.name = 'itemA'
|
||||
item2 = MagicMock()
|
||||
item2.edit_id = 'abcd'
|
||||
item2.name = 'itemB'
|
||||
item3 = MagicMock()
|
||||
item3.edit_id = 'cfgh'
|
||||
item3.name = 'itemA'
|
||||
self.service_manager.service_items = [
|
||||
{'service_item': item1},
|
||||
{'service_item': item2},
|
||||
{'service_item': item3}
|
||||
]
|
||||
new_item = MagicMock()
|
||||
new_item.edit_id = 'abcd'
|
||||
new_item.name = 'itemA'
|
||||
|
||||
# WHEN replace_service_item is called
|
||||
self.service_manager.replace_service_item(new_item)
|
||||
|
||||
# THEN new_item should replace item1, and only replaces that one item
|
||||
assert self.service_manager.service_items[0]['service_item'] == new_item
|
||||
new_item.merge.assert_called_once_with(item1)
|
@ -21,6 +21,7 @@
|
||||
"""
|
||||
Package to test the openlp.core.ui.thememanager package.
|
||||
"""
|
||||
import pytest
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
@ -30,10 +31,17 @@ from unittest.mock import ANY, Mock, MagicMock, patch, call, sentinel
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.ui.thememanager import ThemeManager
|
||||
from tests.utils.constants import RESOURCE_PATH
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def theme_manager(settings):
|
||||
thm = ThemeManager()
|
||||
return thm
|
||||
|
||||
|
||||
@patch('openlp.core.ui.thememanager.zipfile.ZipFile.__init__')
|
||||
@patch('openlp.core.ui.thememanager.zipfile.ZipFile.write')
|
||||
def test_export_theme(mocked_zipfile_write, mocked_zipfile_init, registry):
|
||||
@ -421,3 +429,91 @@ def test_update_preview_images(registry):
|
||||
assert theme_manager.save_preview.call_args_list == [call('Default', 'preview'), call('Test', 'preview')]
|
||||
theme_manager.progress_form.close.assert_called_once_with()
|
||||
theme_manager.load_themes.assert_called_once_with()
|
||||
|
||||
|
||||
def test_theme_manager_initialise(theme_manager):
|
||||
"""
|
||||
Test the thememanager initialise - basic test
|
||||
"""
|
||||
# GIVEN: A new a call to initialise
|
||||
theme_manager.setup_ui = MagicMock()
|
||||
theme_manager.build_theme_path = MagicMock()
|
||||
Settings().setValue('themes/global theme', 'my_theme')
|
||||
|
||||
# WHEN: the initialisation is run
|
||||
theme_manager.bootstrap_initialise()
|
||||
|
||||
# THEN:
|
||||
theme_manager.setup_ui.assert_called_once_with(theme_manager)
|
||||
assert theme_manager.global_theme == 'my_theme'
|
||||
theme_manager.build_theme_path.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('openlp.core.ui.thememanager.create_paths')
|
||||
@patch('openlp.core.ui.thememanager.AppLocation.get_section_data_path')
|
||||
def test_build_theme_path(mocked_get_section_data_path, mocked_create_paths, theme_manager):
|
||||
"""
|
||||
Test the thememanager build_theme_path
|
||||
"""
|
||||
# GIVEN: A mocked out AppLocation.get_directory() and mocked create_paths
|
||||
mocked_get_section_data_path.return_value = Path('tests/my_theme')
|
||||
|
||||
# WHEN: the build_theme_path is run
|
||||
theme_manager.build_theme_path()
|
||||
|
||||
# THEN: The theme path and the thumb path should be correct
|
||||
assert theme_manager.theme_path == Path('tests/my_theme')
|
||||
assert theme_manager.thumb_path == Path('tests/my_theme/thumbnails')
|
||||
mocked_create_paths.assert_called_once_with(Path('tests/my_theme'), Path('tests/my_theme/thumbnails'))
|
||||
|
||||
|
||||
def test_click_on_new_theme(theme_manager):
|
||||
"""
|
||||
Test the on_add_theme event handler is called by the UI
|
||||
"""
|
||||
# GIVEN: An initial form
|
||||
Settings().setValue('themes/global theme', 'my_theme')
|
||||
mocked_event = MagicMock()
|
||||
theme_manager.on_add_theme = mocked_event
|
||||
theme_manager.setup_ui(theme_manager)
|
||||
|
||||
# WHEN displaying the UI and pressing cancel
|
||||
new_theme = theme_manager.toolbar.actions['newTheme']
|
||||
new_theme.trigger()
|
||||
|
||||
assert mocked_event.call_count == 1, 'The on_add_theme method should have been called once'
|
||||
|
||||
|
||||
@patch('openlp.core.ui.themeform.ThemeForm._setup')
|
||||
@patch('openlp.core.ui.filerenameform.FileRenameForm._setup')
|
||||
def test_bootstrap_post(mocked_rename_form, mocked_theme_form, theme_manager):
|
||||
"""
|
||||
Test the functions of bootstrap_post_setup are called.
|
||||
"""
|
||||
# GIVEN:
|
||||
theme_manager.theme_path = MagicMock()
|
||||
|
||||
# WHEN:
|
||||
with patch('openlp.core.ui.thememanager.ThemeProgressForm'):
|
||||
theme_manager.bootstrap_post_set_up()
|
||||
|
||||
# THEN:
|
||||
assert theme_manager.progress_form is not None
|
||||
assert theme_manager.theme_form is not None
|
||||
assert theme_manager.file_rename_form is not None
|
||||
|
||||
|
||||
def test_bootstrap_completion(theme_manager):
|
||||
"""
|
||||
Test the functions of bootstrap_post_setup are called.
|
||||
"""
|
||||
# GIVEN:
|
||||
theme_manager.load_themes = MagicMock()
|
||||
theme_manager.upgrade_themes = MagicMock()
|
||||
|
||||
# WHEN:
|
||||
theme_manager.bootstrap_completion()
|
||||
|
||||
# THEN:
|
||||
theme_manager.upgrade_themes.assert_called_once()
|
||||
theme_manager.load_themes.assert_called_once()
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user