diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py deleted file mode 100644 index bf21ab5a3..000000000 --- a/tests/functional/__init__.py +++ /dev/null @@ -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 . # -########################################################################## -""" -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'] diff --git a/tests/functional/openlp_core/common/test_json.py b/tests/functional/openlp_core/common/test_json.py deleted file mode 100644 index 317991f82..000000000 --- a/tests/functional/openlp_core/common/test_json.py +++ /dev/null @@ -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 . # -########################################################################## -""" -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}} diff --git a/tests/functional/openlp_core/ui/__init__.py b/tests/functional/openlp_core/ui/__init__.py deleted file mode 100644 index 7c5dfadc5..000000000 --- a/tests/functional/openlp_core/ui/__init__.py +++ /dev/null @@ -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 . # -########################################################################## -""" -Package to test the openlp.core.ui package. -""" diff --git a/tests/interfaces/__init__.py b/tests/interfaces/__init__.py deleted file mode 100644 index 2178b469f..000000000 --- a/tests/interfaces/__init__.py +++ /dev/null @@ -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 . # -########################################################################## -""" -Interface tests -""" diff --git a/tests/interfaces/openlp_core/lib/test_pluginmanager.py b/tests/interfaces/openlp_core/lib/test_pluginmanager.py deleted file mode 100644 index 76d1b09ac..000000000 --- a/tests/interfaces/openlp_core/lib/test_pluginmanager.py +++ /dev/null @@ -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 . # -########################################################################## -""" -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' diff --git a/tests/interfaces/openlp_core/ui/__init__.py b/tests/interfaces/openlp_core/ui/__init__.py deleted file mode 100644 index 712629a4e..000000000 --- a/tests/interfaces/openlp_core/ui/__init__.py +++ /dev/null @@ -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 . # -########################################################################## -""" -Module-level functions for the functional test suite -""" diff --git a/tests/interfaces/openlp_core/ui/test_firsttimeform.py b/tests/interfaces/openlp_core/ui/test_firsttimeform.py deleted file mode 100644 index 17cf1fa3c..000000000 --- a/tests/interfaces/openlp_core/ui/test_firsttimeform.py +++ /dev/null @@ -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 . # -########################################################################## -""" -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())]) diff --git a/tests/interfaces/openlp_core/ui/test_servicemanager.py b/tests/interfaces/openlp_core/ui/test_servicemanager.py deleted file mode 100644 index a1fa1dc41..000000000 --- a/tests/interfaces/openlp_core/ui/test_servicemanager.py +++ /dev/null @@ -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 . # -########################################################################## -""" - 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) diff --git a/tests/interfaces/openlp_core/ui/test_thememanager.py b/tests/interfaces/openlp_core/ui/test_thememanager.py deleted file mode 100644 index a2aa3cc82..000000000 --- a/tests/interfaces/openlp_core/ui/test_thememanager.py +++ /dev/null @@ -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 . # -########################################################################## -""" -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() diff --git a/tests/interfaces/openlp_core/widgets/test_edits.py b/tests/interfaces/openlp_core/widgets/test_edits.py deleted file mode 100644 index 24bdcc181..000000000 --- a/tests/interfaces/openlp_core/widgets/test_edits.py +++ /dev/null @@ -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 . # -########################################################################## -""" -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'] diff --git a/tests/interfaces/openlp_core/widgets/test_views.py b/tests/interfaces/openlp_core/widgets/test_views.py deleted file mode 100644 index c85327212..000000000 --- a/tests/interfaces/openlp_core/widgets/test_views.py +++ /dev/null @@ -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 . # -########################################################################## -""" - 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.' diff --git a/tests/interfaces/openlp_plugins/__init__.py b/tests/interfaces/openlp_plugins/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/interfaces/openlp_plugins/bibles/__init__.py b/tests/interfaces/openlp_plugins/bibles/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/bibles/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/interfaces/openlp_plugins/bibles/forms/__init__.py b/tests/interfaces/openlp_plugins/bibles/forms/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/bibles/forms/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/interfaces/openlp_plugins/custom/__init__.py b/tests/interfaces/openlp_plugins/custom/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/custom/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/interfaces/openlp_plugins/custom/forms/__init__.py b/tests/interfaces/openlp_plugins/custom/forms/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/custom/forms/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/interfaces/openlp_plugins/images/__init__.py b/tests/interfaces/openlp_plugins/images/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/images/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/interfaces/openlp_plugins/media/__init__.py b/tests/interfaces/openlp_plugins/media/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/media/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/interfaces/openlp_plugins/media/forms/__init__.py b/tests/interfaces/openlp_plugins/media/forms/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/media/forms/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/interfaces/openlp_plugins/planningcenter/__init__.py b/tests/interfaces/openlp_plugins/planningcenter/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/planningcenter/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/interfaces/openlp_plugins/planningcenter/forms/__init__.py b/tests/interfaces/openlp_plugins/planningcenter/forms/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/planningcenter/forms/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/interfaces/openlp_plugins/planningcenter/lib/__init__.py b/tests/interfaces/openlp_plugins/planningcenter/lib/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/planningcenter/lib/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/interfaces/openlp_plugins/planningcenter/test_planningcenterplugin.py b/tests/interfaces/openlp_plugins/planningcenter/test_planningcenterplugin.py deleted file mode 100644 index 3d74b7d27..000000000 --- a/tests/interfaces/openlp_plugins/planningcenter/test_planningcenterplugin.py +++ /dev/null @@ -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 . # -########################################################################## -""" -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) diff --git a/tests/interfaces/openlp_plugins/songs/__init__.py b/tests/interfaces/openlp_plugins/songs/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/songs/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/interfaces/openlp_plugins/songs/forms/__init__.py b/tests/interfaces/openlp_plugins/songs/forms/__init__.py deleted file mode 100644 index 2ce7ad377..000000000 --- a/tests/interfaces/openlp_plugins/songs/forms/__init__.py +++ /dev/null @@ -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 . # -########################################################################## diff --git a/tests/functional/openlp_core/__init__.py b/tests/openlp_core/api/__init__.py similarity index 100% rename from tests/functional/openlp_core/__init__.py rename to tests/openlp_core/api/__init__.py diff --git a/tests/functional/openlp_core/api/endpoint/__init__.py b/tests/openlp_core/api/endpoint/__init__.py similarity index 100% rename from tests/functional/openlp_core/api/endpoint/__init__.py rename to tests/openlp_core/api/endpoint/__init__.py diff --git a/tests/functional/openlp_core/api/endpoint/test_controller.py b/tests/openlp_core/api/endpoint/test_controller.py similarity index 100% rename from tests/functional/openlp_core/api/endpoint/test_controller.py rename to tests/openlp_core/api/endpoint/test_controller.py diff --git a/tests/functional/openlp_core/api/__init__.py b/tests/openlp_core/api/http_server/__init__.py similarity index 100% rename from tests/functional/openlp_core/api/__init__.py rename to tests/openlp_core/api/http_server/__init__.py diff --git a/tests/functional/openlp_core/api/http_server/test_http.py b/tests/openlp_core/api/http_server/test_http.py similarity index 100% rename from tests/functional/openlp_core/api/http_server/test_http.py rename to tests/openlp_core/api/http_server/test_http.py diff --git a/tests/functional/openlp_core/api/http_server/test_init.py b/tests/openlp_core/api/http_server/test_init.py similarity index 100% rename from tests/functional/openlp_core/api/http_server/test_init.py rename to tests/openlp_core/api/http_server/test_init.py diff --git a/tests/functional/openlp_core/api/test_deploy.py b/tests/openlp_core/api/test_deploy.py similarity index 100% rename from tests/functional/openlp_core/api/test_deploy.py rename to tests/openlp_core/api/test_deploy.py diff --git a/tests/functional/openlp_core/api/test_tab.py b/tests/openlp_core/api/test_tab.py similarity index 100% rename from tests/functional/openlp_core/api/test_tab.py rename to tests/openlp_core/api/test_tab.py diff --git a/tests/functional/openlp_core/api/test_websockets.py b/tests/openlp_core/api/test_websockets.py similarity index 100% rename from tests/functional/openlp_core/api/test_websockets.py rename to tests/openlp_core/api/test_websockets.py diff --git a/tests/functional/openlp_core/api/v2/__init__.py b/tests/openlp_core/api/v2/__init__.py similarity index 100% rename from tests/functional/openlp_core/api/v2/__init__.py rename to tests/openlp_core/api/v2/__init__.py diff --git a/tests/functional/openlp_core/api/v2/conftest.py b/tests/openlp_core/api/v2/conftest.py similarity index 100% rename from tests/functional/openlp_core/api/v2/conftest.py rename to tests/openlp_core/api/v2/conftest.py diff --git a/tests/functional/openlp_core/api/v2/test_controller.py b/tests/openlp_core/api/v2/test_controller.py similarity index 100% rename from tests/functional/openlp_core/api/v2/test_controller.py rename to tests/openlp_core/api/v2/test_controller.py diff --git a/tests/functional/openlp_core/api/v2/test_core.py b/tests/openlp_core/api/v2/test_core.py similarity index 100% rename from tests/functional/openlp_core/api/v2/test_core.py rename to tests/openlp_core/api/v2/test_core.py diff --git a/tests/functional/openlp_core/api/v2/test_plugins.py b/tests/openlp_core/api/v2/test_plugins.py similarity index 100% rename from tests/functional/openlp_core/api/v2/test_plugins.py rename to tests/openlp_core/api/v2/test_plugins.py diff --git a/tests/functional/openlp_core/api/v2/test_service.py b/tests/openlp_core/api/v2/test_service.py similarity index 100% rename from tests/functional/openlp_core/api/v2/test_service.py rename to tests/openlp_core/api/v2/test_service.py diff --git a/tests/functional/openlp_core/api/http_server/__init__.py b/tests/openlp_core/common/__init__.py similarity index 100% rename from tests/functional/openlp_core/api/http_server/__init__.py rename to tests/openlp_core/common/__init__.py diff --git a/tests/functional/openlp_core/common/test_actions.py b/tests/openlp_core/common/test_actions.py similarity index 100% rename from tests/functional/openlp_core/common/test_actions.py rename to tests/openlp_core/common/test_actions.py diff --git a/tests/functional/openlp_core/common/test_applocation.py b/tests/openlp_core/common/test_applocation.py similarity index 100% rename from tests/functional/openlp_core/common/test_applocation.py rename to tests/openlp_core/common/test_applocation.py diff --git a/tests/functional/openlp_core/common/test_db.py b/tests/openlp_core/common/test_db.py similarity index 100% rename from tests/functional/openlp_core/common/test_db.py rename to tests/openlp_core/common/test_db.py diff --git a/tests/functional/openlp_core/common/test_httputils.py b/tests/openlp_core/common/test_httputils.py similarity index 100% rename from tests/functional/openlp_core/common/test_httputils.py rename to tests/openlp_core/common/test_httputils.py diff --git a/tests/functional/openlp_core/common/test_i18n.py b/tests/openlp_core/common/test_i18n.py similarity index 100% rename from tests/functional/openlp_core/common/test_i18n.py rename to tests/openlp_core/common/test_i18n.py diff --git a/tests/functional/openlp_core/common/test_init.py b/tests/openlp_core/common/test_init.py similarity index 99% rename from tests/functional/openlp_core/common/test_init.py rename to tests/openlp_core/common/test_init.py index 7e0f49a34..b3196cc27 100644 --- a/tests/functional/openlp_core/common/test_init.py +++ b/tests/openlp_core/common/test_init.py @@ -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) diff --git a/tests/openlp_core/common/test_json.py b/tests/openlp_core/common/test_json.py new file mode 100644 index 000000000..0122debdf --- /dev/null +++ b/tests/openlp_core/common/test_json.py @@ -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 . # +########################################################################## +""" +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}} diff --git a/tests/functional/openlp_core/common/test_mixins.py b/tests/openlp_core/common/test_mixins.py similarity index 100% rename from tests/functional/openlp_core/common/test_mixins.py rename to tests/openlp_core/common/test_mixins.py diff --git a/tests/functional/openlp_core/common/test_path.py b/tests/openlp_core/common/test_path.py similarity index 100% rename from tests/functional/openlp_core/common/test_path.py rename to tests/openlp_core/common/test_path.py diff --git a/tests/functional/openlp_core/common/test_registry.py b/tests/openlp_core/common/test_registry.py similarity index 100% rename from tests/functional/openlp_core/common/test_registry.py rename to tests/openlp_core/common/test_registry.py diff --git a/tests/functional/openlp_core/common/test_settings.py b/tests/openlp_core/common/test_settings.py similarity index 100% rename from tests/functional/openlp_core/common/test_settings.py rename to tests/openlp_core/common/test_settings.py diff --git a/tests/functional/openlp_core/display/__init__.py b/tests/openlp_core/display/__init__.py similarity index 100% rename from tests/functional/openlp_core/display/__init__.py rename to tests/openlp_core/display/__init__.py diff --git a/tests/functional/openlp_core/display/test_render.py b/tests/openlp_core/display/test_render.py similarity index 100% rename from tests/functional/openlp_core/display/test_render.py rename to tests/openlp_core/display/test_render.py diff --git a/tests/functional/openlp_core/display/test_screens.py b/tests/openlp_core/display/test_screens.py similarity index 100% rename from tests/functional/openlp_core/display/test_screens.py rename to tests/openlp_core/display/test_screens.py diff --git a/tests/functional/openlp_core/display/test_window.py b/tests/openlp_core/display/test_window.py similarity index 100% rename from tests/functional/openlp_core/display/test_window.py rename to tests/openlp_core/display/test_window.py diff --git a/tests/functional/openlp_core/lib/__init__.py b/tests/openlp_core/lib/__init__.py similarity index 100% rename from tests/functional/openlp_core/lib/__init__.py rename to tests/openlp_core/lib/__init__.py diff --git a/tests/functional/openlp_core/lib/test_db.py b/tests/openlp_core/lib/test_db.py similarity index 100% rename from tests/functional/openlp_core/lib/test_db.py rename to tests/openlp_core/lib/test_db.py diff --git a/tests/functional/openlp_core/lib/test_exceptions.py b/tests/openlp_core/lib/test_exceptions.py similarity index 100% rename from tests/functional/openlp_core/lib/test_exceptions.py rename to tests/openlp_core/lib/test_exceptions.py diff --git a/tests/functional/openlp_core/lib/test_formattingtags.py b/tests/openlp_core/lib/test_formattingtags.py similarity index 100% rename from tests/functional/openlp_core/lib/test_formattingtags.py rename to tests/openlp_core/lib/test_formattingtags.py diff --git a/tests/functional/openlp_core/lib/test_lib.py b/tests/openlp_core/lib/test_lib.py similarity index 100% rename from tests/functional/openlp_core/lib/test_lib.py rename to tests/openlp_core/lib/test_lib.py diff --git a/tests/functional/openlp_core/lib/test_mediamanageritem.py b/tests/openlp_core/lib/test_mediamanageritem.py similarity index 100% rename from tests/functional/openlp_core/lib/test_mediamanageritem.py rename to tests/openlp_core/lib/test_mediamanageritem.py diff --git a/tests/functional/openlp_core/lib/test_pluginmanager.py b/tests/openlp_core/lib/test_pluginmanager.py similarity index 89% rename from tests/functional/openlp_core/lib/test_pluginmanager.py rename to tests/openlp_core/lib/test_pluginmanager.py index 8cc104bd7..35c137bd6 100644 --- a/tests/functional/openlp_core/lib/test_pluginmanager.py +++ b/tests/openlp_core/lib/test_pluginmanager.py @@ -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' diff --git a/tests/functional/openlp_core/lib/test_serviceitem.py b/tests/openlp_core/lib/test_serviceitem.py similarity index 100% rename from tests/functional/openlp_core/lib/test_serviceitem.py rename to tests/openlp_core/lib/test_serviceitem.py diff --git a/tests/functional/openlp_core/lib/test_theme.py b/tests/openlp_core/lib/test_theme.py similarity index 100% rename from tests/functional/openlp_core/lib/test_theme.py rename to tests/openlp_core/lib/test_theme.py diff --git a/tests/functional/openlp_core/lib/test_ui.py b/tests/openlp_core/lib/test_ui.py similarity index 100% rename from tests/functional/openlp_core/lib/test_ui.py rename to tests/openlp_core/lib/test_ui.py diff --git a/tests/functional/openlp_core/test_app.py b/tests/openlp_core/test_app.py similarity index 100% rename from tests/functional/openlp_core/test_app.py rename to tests/openlp_core/test_app.py diff --git a/tests/functional/openlp_core/test_server.py b/tests/openlp_core/test_server.py similarity index 100% rename from tests/functional/openlp_core/test_server.py rename to tests/openlp_core/test_server.py diff --git a/tests/functional/openlp_core/test_state.py b/tests/openlp_core/test_state.py similarity index 100% rename from tests/functional/openlp_core/test_state.py rename to tests/openlp_core/test_state.py diff --git a/tests/functional/openlp_core/test_threading.py b/tests/openlp_core/test_threading.py similarity index 100% rename from tests/functional/openlp_core/test_threading.py rename to tests/openlp_core/test_threading.py diff --git a/tests/functional/openlp_core/test_version.py b/tests/openlp_core/test_version.py similarity index 100% rename from tests/functional/openlp_core/test_version.py rename to tests/openlp_core/test_version.py diff --git a/tests/functional/openlp_core/ui/media/__init__.py b/tests/openlp_core/ui/media/__init__.py similarity index 100% rename from tests/functional/openlp_core/ui/media/__init__.py rename to tests/openlp_core/ui/media/__init__.py diff --git a/tests/functional/openlp_core/ui/media/test_media.py b/tests/openlp_core/ui/media/test_media.py similarity index 100% rename from tests/functional/openlp_core/ui/media/test_media.py rename to tests/openlp_core/ui/media/test_media.py diff --git a/tests/functional/openlp_core/ui/media/test_mediacontroller.py b/tests/openlp_core/ui/media/test_mediacontroller.py similarity index 100% rename from tests/functional/openlp_core/ui/media/test_mediacontroller.py rename to tests/openlp_core/ui/media/test_mediacontroller.py diff --git a/tests/functional/openlp_core/ui/media/test_remote.py b/tests/openlp_core/ui/media/test_remote.py similarity index 100% rename from tests/functional/openlp_core/ui/media/test_remote.py rename to tests/openlp_core/ui/media/test_remote.py diff --git a/tests/functional/openlp_core/ui/media/test_vlcplayer.py b/tests/openlp_core/ui/media/test_vlcplayer.py similarity index 100% rename from tests/functional/openlp_core/ui/media/test_vlcplayer.py rename to tests/openlp_core/ui/media/test_vlcplayer.py diff --git a/tests/functional/openlp_core/ui/test_aboutform.py b/tests/openlp_core/ui/test_aboutform.py similarity index 100% rename from tests/functional/openlp_core/ui/test_aboutform.py rename to tests/openlp_core/ui/test_aboutform.py diff --git a/tests/functional/openlp_core/ui/test_advancedtab.py b/tests/openlp_core/ui/test_advancedtab.py similarity index 100% rename from tests/functional/openlp_core/ui/test_advancedtab.py rename to tests/openlp_core/ui/test_advancedtab.py diff --git a/tests/functional/openlp_core/ui/test_exceptionform.py b/tests/openlp_core/ui/test_exceptionform.py similarity index 100% rename from tests/functional/openlp_core/ui/test_exceptionform.py rename to tests/openlp_core/ui/test_exceptionform.py diff --git a/tests/interfaces/openlp_core/ui/test_filerenamedialog.py b/tests/openlp_core/ui/test_filerenamedialog.py similarity index 100% rename from tests/interfaces/openlp_core/ui/test_filerenamedialog.py rename to tests/openlp_core/ui/test_filerenamedialog.py diff --git a/tests/functional/openlp_core/ui/test_first_time.py b/tests/openlp_core/ui/test_first_time.py similarity index 100% rename from tests/functional/openlp_core/ui/test_first_time.py rename to tests/openlp_core/ui/test_first_time.py diff --git a/tests/functional/openlp_core/ui/test_firsttimeform.py b/tests/openlp_core/ui/test_firsttimeform.py similarity index 88% rename from tests/functional/openlp_core/ui/test_firsttimeform.py rename to tests/openlp_core/ui/test_firsttimeform.py index 16bcadce1..51e18deaf 100644 --- a/tests/functional/openlp_core/ui/test_firsttimeform.py +++ b/tests/openlp_core/ui/test_firsttimeform.py @@ -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())]) diff --git a/tests/functional/openlp_core/ui/test_formattingtagscontroller.py b/tests/openlp_core/ui/test_formattingtagscontroller.py similarity index 100% rename from tests/functional/openlp_core/ui/test_formattingtagscontroller.py rename to tests/openlp_core/ui/test_formattingtagscontroller.py diff --git a/tests/functional/openlp_core/ui/test_formattingtagsform.py b/tests/openlp_core/ui/test_formattingtagsform.py similarity index 100% rename from tests/functional/openlp_core/ui/test_formattingtagsform.py rename to tests/openlp_core/ui/test_formattingtagsform.py diff --git a/tests/functional/openlp_core/ui/test_icons.py b/tests/openlp_core/ui/test_icons.py similarity index 100% rename from tests/functional/openlp_core/ui/test_icons.py rename to tests/openlp_core/ui/test_icons.py diff --git a/tests/interfaces/openlp_core/ui/test_init.py b/tests/openlp_core/ui/test_init.py similarity index 100% rename from tests/interfaces/openlp_core/ui/test_init.py rename to tests/openlp_core/ui/test_init.py diff --git a/tests/functional/openlp_core/ui/test_servicemanager.py b/tests/openlp_core/ui/test_servicemanager.py similarity index 60% rename from tests/functional/openlp_core/ui/test_servicemanager.py rename to tests/openlp_core/ui/test_servicemanager.py index 3965e3ed3..b02dfe57b 100644 --- a/tests/functional/openlp_core/ui/test_servicemanager.py +++ b/tests/openlp_core/ui/test_servicemanager.py @@ -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) diff --git a/tests/interfaces/openlp_core/ui/test_servicenotedialog.py b/tests/openlp_core/ui/test_servicenotedialog.py similarity index 100% rename from tests/interfaces/openlp_core/ui/test_servicenotedialog.py rename to tests/openlp_core/ui/test_servicenotedialog.py diff --git a/tests/interfaces/openlp_core/ui/test_settings_form.py b/tests/openlp_core/ui/test_settings_form.py similarity index 100% rename from tests/interfaces/openlp_core/ui/test_settings_form.py rename to tests/openlp_core/ui/test_settings_form.py diff --git a/tests/functional/openlp_core/ui/test_settingsform.py b/tests/openlp_core/ui/test_settingsform.py similarity index 100% rename from tests/functional/openlp_core/ui/test_settingsform.py rename to tests/openlp_core/ui/test_settingsform.py diff --git a/tests/functional/openlp_core/ui/test_shortcutlistdialog.py b/tests/openlp_core/ui/test_shortcutlistdialog.py similarity index 100% rename from tests/functional/openlp_core/ui/test_shortcutlistdialog.py rename to tests/openlp_core/ui/test_shortcutlistdialog.py diff --git a/tests/interfaces/openlp_core/ui/test_shortcutlistform.py b/tests/openlp_core/ui/test_shortcutlistform.py similarity index 100% rename from tests/interfaces/openlp_core/ui/test_shortcutlistform.py rename to tests/openlp_core/ui/test_shortcutlistform.py diff --git a/tests/functional/openlp_core/ui/test_slidecontroller.py b/tests/openlp_core/ui/test_slidecontroller.py similarity index 100% rename from tests/functional/openlp_core/ui/test_slidecontroller.py rename to tests/openlp_core/ui/test_slidecontroller.py diff --git a/tests/functional/openlp_core/ui/test_splashscreen.py b/tests/openlp_core/ui/test_splashscreen.py similarity index 100% rename from tests/functional/openlp_core/ui/test_splashscreen.py rename to tests/openlp_core/ui/test_splashscreen.py diff --git a/tests/interfaces/openlp_core/ui/test_starttimedialog.py b/tests/openlp_core/ui/test_starttimedialog.py similarity index 100% rename from tests/interfaces/openlp_core/ui/test_starttimedialog.py rename to tests/openlp_core/ui/test_starttimedialog.py diff --git a/tests/functional/openlp_core/ui/test_style.py b/tests/openlp_core/ui/test_style.py similarity index 100% rename from tests/functional/openlp_core/ui/test_style.py rename to tests/openlp_core/ui/test_style.py diff --git a/tests/functional/openlp_core/ui/test_thememanager.py b/tests/openlp_core/ui/test_thememanager.py similarity index 86% rename from tests/functional/openlp_core/ui/test_thememanager.py rename to tests/openlp_core/ui/test_thememanager.py index 34592bdfb..faca65b84 100644 --- a/tests/functional/openlp_core/ui/test_thememanager.py +++ b/tests/openlp_core/ui/test_thememanager.py @@ -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() diff --git a/tests/functional/openlp_core/ui/test_themetab.py b/tests/openlp_core/ui/test_themetab.py similarity index 100% rename from tests/functional/openlp_core/ui/test_themetab.py rename to tests/openlp_core/ui/test_themetab.py diff --git a/tests/functional/openlp_core/common/__init__.py b/tests/openlp_core/widgets/__init__.py similarity index 100% rename from tests/functional/openlp_core/common/__init__.py rename to tests/openlp_core/widgets/__init__.py diff --git a/tests/functional/openlp_core/widgets/test_buttons.py b/tests/openlp_core/widgets/test_buttons.py similarity index 100% rename from tests/functional/openlp_core/widgets/test_buttons.py rename to tests/openlp_core/widgets/test_buttons.py diff --git a/tests/functional/openlp_core/widgets/test_dialogs.py b/tests/openlp_core/widgets/test_dialogs.py similarity index 100% rename from tests/functional/openlp_core/widgets/test_dialogs.py rename to tests/openlp_core/widgets/test_dialogs.py diff --git a/tests/functional/openlp_core/widgets/test_edits.py b/tests/openlp_core/widgets/test_edits.py similarity index 76% rename from tests/functional/openlp_core/widgets/test_edits.py rename to tests/openlp_core/widgets/test_edits.py index d81371f49..0b1ef1472 100755 --- a/tests/functional/openlp_core/widgets/test_edits.py +++ b/tests/openlp_core/widgets/test_edits.py @@ -24,13 +24,50 @@ This module contains tests for the openlp.core.widgets.edits module import os import pytest from pathlib import Path -from unittest.mock import MagicMock, PropertyMock, patch +from unittest.mock import MagicMock, PropertyMock, patch, call +from PyQt5 import QtCore, QtGui, QtTest, QtWidgets + +from openlp.core.common.registry import Registry from openlp.core.widgets.dialogs import FileDialog -from openlp.core.widgets.edits import PathEdit +from openlp.core.widgets.edits import PathEdit, HistoryComboBox, SearchEdit from openlp.core.widgets.enums import PathEditType +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 + + @pytest.fixture() def widget(): with patch('openlp.core.widgets.edits.PathEdit._setup'): @@ -308,3 +345,82 @@ def test_on_new_path_change(widget): # THEN: The `pathChanged` signal should be emitted widget.pathChanged.emit.assert_called_once_with(Path('/new', 'test', 'pat.h')) + + +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'] diff --git a/tests/functional/openlp_core/widgets/test_views.py b/tests/openlp_core/widgets/test_views.py similarity index 92% rename from tests/functional/openlp_core/widgets/test_views.py rename to tests/openlp_core/widgets/test_views.py index 785b450c3..6ef666d25 100644 --- a/tests/functional/openlp_core/widgets/test_views.py +++ b/tests/openlp_core/widgets/test_views.py @@ -26,10 +26,14 @@ import pytest from types import GeneratorType from unittest.mock import MagicMock, call, patch +from PyQt5 import QtWidgets from openlp.core.common.i18n import UiStrings +from openlp.core.common.registry import Registry +from openlp.core.lib.serviceitem import ServiceItem from openlp.core.widgets.views import ListPreviewWidget, ListWidgetWithDnD, TreeWidgetWithDnD, handle_mime_data_urls from openlp.core.ui.icons import UiIcons +from tests.utils.osdinteraction import read_service_from_file @pytest.fixture @@ -55,6 +59,14 @@ def preview_widget_env(): viewport_patcher.stop() +@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_files(): """ Test handle_mime_data_urls when the data points to some files. @@ -689,3 +701,55 @@ def test_treewidgetwithdnd_constructor(): assert widget.allow_internal_dnd is False assert widget.indentation() == 0 assert widget.isAnimated() is True + + +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.' diff --git a/tests/openlp_core/widgets/test_widgets.py b/tests/openlp_core/widgets/test_widgets.py index 6588ce9f1..3047b9e31 100644 --- a/tests/openlp_core/widgets/test_widgets.py +++ b/tests/openlp_core/widgets/test_widgets.py @@ -19,9 +19,11 @@ # along with this program. If not, see . # ########################################################################## """ -Package to test the openlp.core.widgets.widgets package. +Package to test the screens tab functionality (primarily ScreenSelectionWidget and ScreenButton classes) +within openlp/core/widgets/widgets.py """ import pytest + from unittest.mock import MagicMock, call, patch from PyQt5 import QtCore, QtWidgets, QtTest @@ -55,6 +57,107 @@ def mocked_screens(custom_geometry): return [screen0, screen1] +@patch('openlp.core.display.screens.ScreenList') +def test_screen_buttons_show_pixels(mocked_screenList, form): + ''' + Test that the screen buttons show the screen sizes in pixels + ''' + # GIVEN: A mocked extended desktop configuration + + mocked_screenList.return_value = mocked_screens(None) + form.screens = mocked_screenList() + + # WHEN: When I go into screen settings for the display screen + ScreenSelectionWidget.load(form) + + # THEN: The screen buttons should show the correct size of that screen + screen_0_button = form.findChild(QtWidgets.QPushButton, 'screen_0_button') + screen_1_button = form.findChild(QtWidgets.QPushButton, 'screen_1_button') + assert '1920' in str(screen_0_button.text()) + assert '1080' in str(screen_0_button.text()) + assert '1366' in str(screen_1_button.text()) + assert '768' in str(screen_1_button.text()) + + +@patch('openlp.core.display.screens.ScreenList') +def test_spinboxes_no_previous_custom_geometry(mocked_screenList, form): + """ + Test screen custom geometry can be changed from None + """ + # GIVEN: A mocked extended desktop configuration + + mocked_screenList.return_value = mocked_screens(None) + form.screens = mocked_screenList() + + # WHEN: When I go into screen settings for the display screen and set the custom geometry + ScreenSelectionWidget.load(form) + QtTest.QTest.mouseClick(form.custom_geometry_button, QtCore.Qt.LeftButton) + QtTest.QTest.keyClick(form.left_spin_box, QtCore.Qt.Key_Up) + QtTest.QTest.keyClick(form.top_spin_box, QtCore.Qt.Key_Up) + QtTest.QTest.keyClick(form.width_spin_box, QtCore.Qt.Key_Down) + QtTest.QTest.keyClick(form.height_spin_box, QtCore.Qt.Key_Down) + + # THEN: The spin boxes should show the correct values + assert form.left_spin_box.value() == 1 + assert form.top_spin_box.value() == 1 + assert form.width_spin_box.value() == 1919 + assert form.height_spin_box.value() == 1079 + + +@patch('openlp.core.display.screens.ScreenList') +def test_spinboxes_with_previous_custom_geometry(mocked_screenList, form): + """ + Test screen existing custom geometry can be changed + """ + # GIVEN: A mocked extended desktop configuration + + testGeometry = QtCore.QRect(1, 1, 1919, 1079) + mocked_screenList.return_value = mocked_screens(testGeometry) + form.screens = mocked_screenList() + + # WHEN: When I go into screen settings for the display screen and update the custom geometry + ScreenSelectionWidget.load(form) + QtTest.QTest.mouseClick(form.custom_geometry_button, QtCore.Qt.LeftButton) + QtTest.QTest.keyClick(form.left_spin_box, QtCore.Qt.Key_Up) + QtTest.QTest.keyClick(form.top_spin_box, QtCore.Qt.Key_Up) + QtTest.QTest.keyClick(form.width_spin_box, QtCore.Qt.Key_Down) + QtTest.QTest.keyClick(form.height_spin_box, QtCore.Qt.Key_Down) + + # THEN: The spin boxes should show the updated values + assert form.left_spin_box.value() == 2 + assert form.top_spin_box.value() == 2 + assert form.width_spin_box.value() == 1918 + assert form.height_spin_box.value() == 1078 + + +@patch('openlp.core.display.screens.ScreenList') +def test_spinboxes_going_outside_screen_geometry(mocked_screenList, form): + """ + Test screen existing custom geometry can be increased beyond the bounds of the screen + """ + # GIVEN: A mocked extended desktop configuration + + testGeometry = QtCore.QRect(1, 1, 1919, 1079) + mocked_screenList.return_value = mocked_screens(testGeometry) + form.screens = mocked_screenList() + + # WHEN: When I go into screen settings for the display screen and + # update the custom geometry to be outside the screen coordinates + ScreenSelectionWidget.load(form) + QtTest.QTest.mouseClick(form.custom_geometry_button, QtCore.Qt.LeftButton) + for _ in range(2): + QtTest.QTest.keyClick(form.left_spin_box, QtCore.Qt.Key_Down) + QtTest.QTest.keyClick(form.top_spin_box, QtCore.Qt.Key_Down) + QtTest.QTest.keyClick(form.width_spin_box, QtCore.Qt.Key_Up) + QtTest.QTest.keyClick(form.height_spin_box, QtCore.Qt.Key_Up) + + # THEN: The spin boxes should show the updated values + assert form.left_spin_box.value() == -1 + assert form.top_spin_box.value() == -1 + assert form.width_spin_box.value() == 1921 + assert form.height_spin_box.value() == 1081 + + def test_radio_button_exclusivity_no_proxy(settings): """ Test that only one radio button can be checked at a time, and that the line edits are only enabled when the @@ -523,104 +626,3 @@ def test_screen_selection_save(mock_settings): instance._save_screen.assert_called_once_with(mocked_screen) mocked_screen.to_dict.assert_called_once() mock_settings.setValue.assert_called_once_with('core/screens', {0: {'number': 0}}) - - -@patch('openlp.core.display.screens.ScreenList') -def test_screen_buttons_show_pixels(mocked_screenList, form): - ''' - Test that the screen buttons show the screen sizes in pixels - ''' - # GIVEN: A mocked extended desktop configuration - - mocked_screenList.return_value = mocked_screens(None) - form.screens = mocked_screenList() - - # WHEN: When I go into screen settings for the display screen - ScreenSelectionWidget.load(form) - - # THEN: The screen buttons should show the correct size of that screen - screen_0_button = form.findChild(QtWidgets.QPushButton, 'screen_0_button') - screen_1_button = form.findChild(QtWidgets.QPushButton, 'screen_1_button') - assert '1920' in str(screen_0_button.text()) - assert '1080' in str(screen_0_button.text()) - assert '1366' in str(screen_1_button.text()) - assert '768' in str(screen_1_button.text()) - - -@patch('openlp.core.display.screens.ScreenList') -def test_spinboxes_no_previous_custom_geometry(mocked_screenList, form): - """ - Test screen custom geometry can be changed from None - """ - # GIVEN: A mocked extended desktop configuration - - mocked_screenList.return_value = mocked_screens(None) - form.screens = mocked_screenList() - - # WHEN: When I go into screen settings for the display screen and set the custom geometry - ScreenSelectionWidget.load(form) - QtTest.QTest.mouseClick(form.custom_geometry_button, QtCore.Qt.LeftButton) - QtTest.QTest.keyClick(form.left_spin_box, QtCore.Qt.Key_Up) - QtTest.QTest.keyClick(form.top_spin_box, QtCore.Qt.Key_Up) - QtTest.QTest.keyClick(form.width_spin_box, QtCore.Qt.Key_Down) - QtTest.QTest.keyClick(form.height_spin_box, QtCore.Qt.Key_Down) - - # THEN: The spin boxes should show the correct values - assert form.left_spin_box.value() == 1 - assert form.top_spin_box.value() == 1 - assert form.width_spin_box.value() == 1919 - assert form.height_spin_box.value() == 1079 - - -@patch('openlp.core.display.screens.ScreenList') -def test_spinboxes_with_previous_custom_geometry(mocked_screenList, form): - """ - Test screen existing custom geometry can be changed - """ - # GIVEN: A mocked extended desktop configuration - - testGeometry = QtCore.QRect(1, 1, 1919, 1079) - mocked_screenList.return_value = mocked_screens(testGeometry) - form.screens = mocked_screenList() - - # WHEN: When I go into screen settings for the display screen and update the custom geometry - ScreenSelectionWidget.load(form) - QtTest.QTest.mouseClick(form.custom_geometry_button, QtCore.Qt.LeftButton) - QtTest.QTest.keyClick(form.left_spin_box, QtCore.Qt.Key_Up) - QtTest.QTest.keyClick(form.top_spin_box, QtCore.Qt.Key_Up) - QtTest.QTest.keyClick(form.width_spin_box, QtCore.Qt.Key_Down) - QtTest.QTest.keyClick(form.height_spin_box, QtCore.Qt.Key_Down) - - # THEN: The spin boxes should show the updated values - assert form.left_spin_box.value() == 2 - assert form.top_spin_box.value() == 2 - assert form.width_spin_box.value() == 1918 - assert form.height_spin_box.value() == 1078 - - -@patch('openlp.core.display.screens.ScreenList') -def test_spinboxes_going_outside_screen_geometry(mocked_screenList, form): - """ - Test screen existing custom geometry can be increased beyond the bounds of the screen - """ - # GIVEN: A mocked extended desktop configuration - - testGeometry = QtCore.QRect(1, 1, 1919, 1079) - mocked_screenList.return_value = mocked_screens(testGeometry) - form.screens = mocked_screenList() - - # WHEN: When I go into screen settings for the display screen and - # update the custom geometry to be outside the screen coordinates - ScreenSelectionWidget.load(form) - QtTest.QTest.mouseClick(form.custom_geometry_button, QtCore.Qt.LeftButton) - for _ in range(2): - QtTest.QTest.keyClick(form.left_spin_box, QtCore.Qt.Key_Down) - QtTest.QTest.keyClick(form.top_spin_box, QtCore.Qt.Key_Down) - QtTest.QTest.keyClick(form.width_spin_box, QtCore.Qt.Key_Up) - QtTest.QTest.keyClick(form.height_spin_box, QtCore.Qt.Key_Up) - - # THEN: The spin boxes should show the updated values - assert form.left_spin_box.value() == -1 - assert form.top_spin_box.value() == -1 - assert form.width_spin_box.value() == 1921 - assert form.height_spin_box.value() == 1081 diff --git a/tests/functional/openlp_core/widgets/__init__.py b/tests/openlp_plugins/alerts/__init__.py similarity index 100% rename from tests/functional/openlp_core/widgets/__init__.py rename to tests/openlp_plugins/alerts/__init__.py diff --git a/tests/functional/openlp_plugins/alerts/test_manager.py b/tests/openlp_plugins/alerts/test_manager.py similarity index 100% rename from tests/functional/openlp_plugins/alerts/test_manager.py rename to tests/openlp_plugins/alerts/test_manager.py diff --git a/tests/functional/openlp_plugins/alerts/test_plugin.py b/tests/openlp_plugins/alerts/test_plugin.py similarity index 100% rename from tests/functional/openlp_plugins/alerts/test_plugin.py rename to tests/openlp_plugins/alerts/test_plugin.py diff --git a/tests/functional/openlp_plugins/__init__.py b/tests/openlp_plugins/bibles/__init__.py similarity index 100% rename from tests/functional/openlp_plugins/__init__.py rename to tests/openlp_plugins/bibles/__init__.py diff --git a/tests/functional/openlp_plugins/alerts/__init__.py b/tests/openlp_plugins/bibles/forms/__init__.py similarity index 100% rename from tests/functional/openlp_plugins/alerts/__init__.py rename to tests/openlp_plugins/bibles/forms/__init__.py diff --git a/tests/interfaces/openlp_plugins/bibles/forms/test_bibleimportform.py b/tests/openlp_plugins/bibles/forms/test_bibleimportform.py similarity index 100% rename from tests/interfaces/openlp_plugins/bibles/forms/test_bibleimportform.py rename to tests/openlp_plugins/bibles/forms/test_bibleimportform.py diff --git a/tests/functional/openlp_plugins/bibles/test_bibleimport.py b/tests/openlp_plugins/bibles/test_bibleimport.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_bibleimport.py rename to tests/openlp_plugins/bibles/test_bibleimport.py diff --git a/tests/functional/openlp_plugins/bibles/test_bibleserver.py b/tests/openlp_plugins/bibles/test_bibleserver.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_bibleserver.py rename to tests/openlp_plugins/bibles/test_bibleserver.py diff --git a/tests/functional/openlp_plugins/bibles/test_csvimport.py b/tests/openlp_plugins/bibles/test_csvimport.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_csvimport.py rename to tests/openlp_plugins/bibles/test_csvimport.py diff --git a/tests/functional/openlp_plugins/bibles/test_db.py b/tests/openlp_plugins/bibles/test_db.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_db.py rename to tests/openlp_plugins/bibles/test_db.py diff --git a/tests/functional/openlp_plugins/bibles/test_lib.py b/tests/openlp_plugins/bibles/test_lib.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_lib.py rename to tests/openlp_plugins/bibles/test_lib.py diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py b/tests/openlp_plugins/bibles/test_lib_http.py similarity index 100% rename from tests/interfaces/openlp_plugins/bibles/test_lib_http.py rename to tests/openlp_plugins/bibles/test_lib_http.py diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_manager.py b/tests/openlp_plugins/bibles/test_lib_manager.py similarity index 100% rename from tests/interfaces/openlp_plugins/bibles/test_lib_manager.py rename to tests/openlp_plugins/bibles/test_lib_manager.py diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py b/tests/openlp_plugins/bibles/test_lib_parse_reference.py similarity index 100% rename from tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py rename to tests/openlp_plugins/bibles/test_lib_parse_reference.py diff --git a/tests/functional/openlp_plugins/bibles/test_manager.py b/tests/openlp_plugins/bibles/test_manager.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_manager.py rename to tests/openlp_plugins/bibles/test_manager.py diff --git a/tests/functional/openlp_plugins/bibles/test_mediaitem.py b/tests/openlp_plugins/bibles/test_mediaitem.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_mediaitem.py rename to tests/openlp_plugins/bibles/test_mediaitem.py diff --git a/tests/functional/openlp_plugins/bibles/test_opensongimport.py b/tests/openlp_plugins/bibles/test_opensongimport.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_opensongimport.py rename to tests/openlp_plugins/bibles/test_opensongimport.py diff --git a/tests/functional/openlp_plugins/bibles/test_osisimport.py b/tests/openlp_plugins/bibles/test_osisimport.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_osisimport.py rename to tests/openlp_plugins/bibles/test_osisimport.py diff --git a/tests/functional/openlp_plugins/bibles/test_plugin.py b/tests/openlp_plugins/bibles/test_plugin.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_plugin.py rename to tests/openlp_plugins/bibles/test_plugin.py diff --git a/tests/functional/openlp_plugins/bibles/test_swordimport.py b/tests/openlp_plugins/bibles/test_swordimport.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_swordimport.py rename to tests/openlp_plugins/bibles/test_swordimport.py diff --git a/tests/functional/openlp_plugins/bibles/test_upgrade.py b/tests/openlp_plugins/bibles/test_upgrade.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_upgrade.py rename to tests/openlp_plugins/bibles/test_upgrade.py diff --git a/tests/functional/openlp_plugins/bibles/test_versereferencelist.py b/tests/openlp_plugins/bibles/test_versereferencelist.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_versereferencelist.py rename to tests/openlp_plugins/bibles/test_versereferencelist.py diff --git a/tests/functional/openlp_plugins/bibles/test_wordprojectimport.py b/tests/openlp_plugins/bibles/test_wordprojectimport.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_wordprojectimport.py rename to tests/openlp_plugins/bibles/test_wordprojectimport.py diff --git a/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py b/tests/openlp_plugins/bibles/test_zefaniaimport.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/test_zefaniaimport.py rename to tests/openlp_plugins/bibles/test_zefaniaimport.py diff --git a/tests/functional/openlp_plugins/bibles/__init__.py b/tests/openlp_plugins/custom/__init__.py similarity index 100% rename from tests/functional/openlp_plugins/bibles/__init__.py rename to tests/openlp_plugins/custom/__init__.py diff --git a/tests/functional/openlp_plugins/custom/__init__.py b/tests/openlp_plugins/custom/forms/__init__.py similarity index 100% rename from tests/functional/openlp_plugins/custom/__init__.py rename to tests/openlp_plugins/custom/forms/__init__.py diff --git a/tests/interfaces/openlp_plugins/custom/forms/test_customform.py b/tests/openlp_plugins/custom/forms/test_customform.py similarity index 100% rename from tests/interfaces/openlp_plugins/custom/forms/test_customform.py rename to tests/openlp_plugins/custom/forms/test_customform.py diff --git a/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py b/tests/openlp_plugins/custom/forms/test_customslideform.py similarity index 100% rename from tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py rename to tests/openlp_plugins/custom/forms/test_customslideform.py diff --git a/tests/functional/openlp_plugins/custom/test_mediaitem.py b/tests/openlp_plugins/custom/test_mediaitem.py similarity index 100% rename from tests/functional/openlp_plugins/custom/test_mediaitem.py rename to tests/openlp_plugins/custom/test_mediaitem.py diff --git a/tests/functional/openlp_plugins/custom/test_plugin.py b/tests/openlp_plugins/custom/test_plugin.py similarity index 100% rename from tests/functional/openlp_plugins/custom/test_plugin.py rename to tests/openlp_plugins/custom/test_plugin.py diff --git a/tests/functional/openlp_plugins/images/__init__.py b/tests/openlp_plugins/images/__init__.py similarity index 100% rename from tests/functional/openlp_plugins/images/__init__.py rename to tests/openlp_plugins/images/__init__.py diff --git a/tests/interfaces/openlp_plugins/images/forms/test_choosegroupform.py b/tests/openlp_plugins/images/forms/test_choosegroupform.py similarity index 100% rename from tests/interfaces/openlp_plugins/images/forms/test_choosegroupform.py rename to tests/openlp_plugins/images/forms/test_choosegroupform.py diff --git a/tests/functional/openlp_plugins/images/test_imagetab.py b/tests/openlp_plugins/images/test_imagetab.py similarity index 100% rename from tests/functional/openlp_plugins/images/test_imagetab.py rename to tests/openlp_plugins/images/test_imagetab.py diff --git a/tests/functional/openlp_plugins/images/test_mediaitem.py b/tests/openlp_plugins/images/test_mediaitem.py similarity index 100% rename from tests/functional/openlp_plugins/images/test_mediaitem.py rename to tests/openlp_plugins/images/test_mediaitem.py diff --git a/tests/functional/openlp_plugins/images/test_plugin.py b/tests/openlp_plugins/images/test_plugin.py similarity index 100% rename from tests/functional/openlp_plugins/images/test_plugin.py rename to tests/openlp_plugins/images/test_plugin.py diff --git a/tests/functional/openlp_plugins/images/test_upgrade.py b/tests/openlp_plugins/images/test_upgrade.py similarity index 100% rename from tests/functional/openlp_plugins/images/test_upgrade.py rename to tests/openlp_plugins/images/test_upgrade.py diff --git a/tests/functional/openlp_plugins/media/__init__.py b/tests/openlp_plugins/media/__init__.py similarity index 100% rename from tests/functional/openlp_plugins/media/__init__.py rename to tests/openlp_plugins/media/__init__.py diff --git a/tests/functional/openlp_plugins/presentations/__init__.py b/tests/openlp_plugins/media/forms/__init__.py similarity index 100% rename from tests/functional/openlp_plugins/presentations/__init__.py rename to tests/openlp_plugins/media/forms/__init__.py diff --git a/tests/interfaces/openlp_plugins/media/forms/test_mediaclipselectorform.py b/tests/openlp_plugins/media/forms/test_mediaclipselectorform.py similarity index 100% rename from tests/interfaces/openlp_plugins/media/forms/test_mediaclipselectorform.py rename to tests/openlp_plugins/media/forms/test_mediaclipselectorform.py diff --git a/tests/functional/openlp_plugins/media/test_mediaitem.py b/tests/openlp_plugins/media/test_mediaitem.py similarity index 100% rename from tests/functional/openlp_plugins/media/test_mediaitem.py rename to tests/openlp_plugins/media/test_mediaitem.py diff --git a/tests/functional/openlp_plugins/media/test_mediaplugin.py b/tests/openlp_plugins/media/test_mediaplugin.py similarity index 100% rename from tests/functional/openlp_plugins/media/test_mediaplugin.py rename to tests/openlp_plugins/media/test_mediaplugin.py diff --git a/tests/functional/openlp_plugins/media/test_plugin.py b/tests/openlp_plugins/media/test_plugin.py similarity index 100% rename from tests/functional/openlp_plugins/media/test_plugin.py rename to tests/openlp_plugins/media/test_plugin.py diff --git a/tests/interfaces/openlp_core/__init__.py b/tests/openlp_plugins/planningcenter/__init__.py similarity index 100% rename from tests/interfaces/openlp_core/__init__.py rename to tests/openlp_plugins/planningcenter/__init__.py diff --git a/tests/interfaces/openlp_core/api/__init__.py b/tests/openlp_plugins/planningcenter/forms/__init__.py similarity index 100% rename from tests/interfaces/openlp_core/api/__init__.py rename to tests/openlp_plugins/planningcenter/forms/__init__.py diff --git a/tests/interfaces/openlp_plugins/planningcenter/forms/test_selectplanform.py b/tests/openlp_plugins/planningcenter/forms/test_selectplanform.py similarity index 99% rename from tests/interfaces/openlp_plugins/planningcenter/forms/test_selectplanform.py rename to tests/openlp_plugins/planningcenter/forms/test_selectplanform.py index 90bbcfd4c..fd8eafd22 100644 --- a/tests/interfaces/openlp_plugins/planningcenter/forms/test_selectplanform.py +++ b/tests/openlp_plugins/planningcenter/forms/test_selectplanform.py @@ -44,9 +44,9 @@ from openlp.plugins.planningcenter.planningcenterplugin import PlanningCenterPlu from openlp.plugins.songs.lib.mediaitem import SongMediaItem from openlp.plugins.songs.songsplugin import SongsPlugin from tests.helpers.testmixin import TestMixin +from tests.utils.constants import TEST_RESOURCES_PATH -TEST_PATH = os.path.abspath( - os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', 'resources', 'planningcenter')) +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), TEST_RESOURCES_PATH, 'planningcenter')) class TestSelectPlanForm(TestCase, TestMixin): diff --git a/tests/interfaces/openlp_core/lib/__init__.py b/tests/openlp_plugins/planningcenter/lib/__init__.py similarity index 100% rename from tests/interfaces/openlp_core/lib/__init__.py rename to tests/openlp_plugins/planningcenter/lib/__init__.py diff --git a/tests/interfaces/openlp_plugins/planningcenter/lib/test_planningcenter_api.py b/tests/openlp_plugins/planningcenter/lib/test_planningcenter_api.py similarity index 100% rename from tests/interfaces/openlp_plugins/planningcenter/lib/test_planningcenter_api.py rename to tests/openlp_plugins/planningcenter/lib/test_planningcenter_api.py diff --git a/tests/interfaces/openlp_plugins/planningcenter/lib/test_planningcentertab.py b/tests/openlp_plugins/planningcenter/lib/test_planningcentertab.py similarity index 100% rename from tests/interfaces/openlp_plugins/planningcenter/lib/test_planningcentertab.py rename to tests/openlp_plugins/planningcenter/lib/test_planningcentertab.py diff --git a/tests/interfaces/openlp_plugins/planningcenter/lib/test_songimport.py b/tests/openlp_plugins/planningcenter/lib/test_songimport.py similarity index 100% rename from tests/interfaces/openlp_plugins/planningcenter/lib/test_songimport.py rename to tests/openlp_plugins/planningcenter/lib/test_songimport.py diff --git a/tests/openlp_plugins/planningcenter/test_planningcenterplugin.py b/tests/openlp_plugins/planningcenter/test_planningcenterplugin.py new file mode 100644 index 000000000..1f027385a --- /dev/null +++ b/tests/openlp_plugins/planningcenter/test_planningcenterplugin.py @@ -0,0 +1,162 @@ +# -*- 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 . # +########################################################################## +""" +Package to test the openlp.plugins.planningcenter.planningcenterplugin package. +""" +import pytest +from unittest.mock import MagicMock, patch + +from PyQt5 import QtWidgets + +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 + + +@pytest.fixture +def plugin_env(qapp, settings, state): + """An instance of the PlanningcenterPlugin""" + plugin = PlanningCenterPlugin() + settings_form = SettingsForm() + return plugin, settings_form + + +def test_class_init_defaults(plugin_env): + """ + Test that the plugin class is instantiated with the correct defaults + """ + plugin = plugin_env[0] + # GIVEN: A PlanningcenterPlugin Class + # WHEN: the class has been through __init__ + # THEN: + # planningcenter form is set to None + assert plugin.planningcenter_form is None, "Init plugin set to None" + # icon is set correctly + assert plugin.icon == UiIcons().planning_center, "Init icon set to planning_center icon" + # weight is -1 + assert plugin.weight == -1, "Init weight set to -1" + # the planning_center module is registered active + assert State().is_module_active('planning_center') is True, "Init State() is active" + + +def test_initialise(plugin_env): + """ + Test that the initialise function can be called and it passes a call along + to its parent class + """ + plugin = plugin_env[0] + # 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 = 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 + assert return_value is None, "Initialise was called on the class and it didn't crash" + + +def test_import_menu_item_added(plugin_env): + """ + Test that the add_import_menu_item function adds the menu item + """ + plugin = plugin_env[0] + # GIVEN: A PlanningcenterPlugin Class + # WHEN: add_import_menu_item is called + import_menu = QtWidgets.QMenu() + plugin.add_import_menu_item(import_menu) + plugin.import_planning_center.setVisible(True) + # THEN: + # the menu should not be empty + assert import_menu.isEmpty() is 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(mock_editauth_exec, mock_selectplan_exec, plugin_env): + """ + Test that the on_import_planning_center_triggered function correctly returns + the correct form to display. + """ + plugin = plugin_env[0] + # 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 + plugin.on_import_planning_center_triggered() + # THEN: + assert mock_selectplan_exec.call_count == 1, "Select Plan Form was shown" + assert 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(mock_editauth_exec, + mock_selectplan_exec, plugin_env): + """ + Test that the on_import_planning_center_triggered function correctly returns + the correct form to display. + """ + plugin = plugin_env[0] + # 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 + plugin.on_import_planning_center_triggered() + # THEN: + assert mock_selectplan_exec.call_count == 0, "Select Plan Form was not shown" + assert mock_editauth_exec.call_count == 1, "Edit Auth Form was shown" + + +def test_finalise(plugin_env): + """ + Test that the finalise function cleans up after the plugin + """ + plugin = plugin_env[0] + # GIVEN: A PlanningcenterPlugin Class with a bunch of mocks + plugin.import_planning_center = MagicMock() + + # WHEN: finalise has been called on the class + plugin.finalise() + + # THEN: it cleans up after itself + plugin.import_planning_center.setVisible.assert_called_once_with(False) + + +def test_about(plugin_env): + plugin = plugin_env[0] + result = plugin.about() + + assert result == ( + 'PlanningCenter Plugin' + '
The planningcenter plugin provides an interface to import ' + 'service plans from the Planning Center Online v2 API.' + ) diff --git a/tests/interfaces/openlp_core/ui/lib/__init__.py b/tests/openlp_plugins/presentations/__init__.py similarity index 100% rename from tests/interfaces/openlp_core/ui/lib/__init__.py rename to tests/openlp_plugins/presentations/__init__.py diff --git a/tests/functional/openlp_plugins/presentations/conftest.py b/tests/openlp_plugins/presentations/conftest.py similarity index 100% rename from tests/functional/openlp_plugins/presentations/conftest.py rename to tests/openlp_plugins/presentations/conftest.py diff --git a/tests/functional/openlp_plugins/presentations/test_impresscontroller.py b/tests/openlp_plugins/presentations/test_impresscontroller.py similarity index 100% rename from tests/functional/openlp_plugins/presentations/test_impresscontroller.py rename to tests/openlp_plugins/presentations/test_impresscontroller.py diff --git a/tests/functional/openlp_plugins/presentations/test_libreofficeserver.py b/tests/openlp_plugins/presentations/test_libreofficeserver.py similarity index 100% rename from tests/functional/openlp_plugins/presentations/test_libreofficeserver.py rename to tests/openlp_plugins/presentations/test_libreofficeserver.py diff --git a/tests/functional/openlp_plugins/presentations/test_maclocontroller.py b/tests/openlp_plugins/presentations/test_maclocontroller.py similarity index 100% rename from tests/functional/openlp_plugins/presentations/test_maclocontroller.py rename to tests/openlp_plugins/presentations/test_maclocontroller.py diff --git a/tests/functional/openlp_plugins/presentations/test_mediaitem.py b/tests/openlp_plugins/presentations/test_mediaitem.py similarity index 100% rename from tests/functional/openlp_plugins/presentations/test_mediaitem.py rename to tests/openlp_plugins/presentations/test_mediaitem.py diff --git a/tests/functional/openlp_plugins/presentations/test_messagelistener.py b/tests/openlp_plugins/presentations/test_messagelistener.py similarity index 100% rename from tests/functional/openlp_plugins/presentations/test_messagelistener.py rename to tests/openlp_plugins/presentations/test_messagelistener.py diff --git a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py b/tests/openlp_plugins/presentations/test_pdfcontroller.py similarity index 100% rename from tests/functional/openlp_plugins/presentations/test_pdfcontroller.py rename to tests/openlp_plugins/presentations/test_pdfcontroller.py diff --git a/tests/functional/openlp_plugins/presentations/test_plugin.py b/tests/openlp_plugins/presentations/test_plugin.py similarity index 100% rename from tests/functional/openlp_plugins/presentations/test_plugin.py rename to tests/openlp_plugins/presentations/test_plugin.py diff --git a/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py b/tests/openlp_plugins/presentations/test_powerpointcontroller.py similarity index 100% rename from tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py rename to tests/openlp_plugins/presentations/test_powerpointcontroller.py diff --git a/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py b/tests/openlp_plugins/presentations/test_presentationcontroller.py similarity index 100% rename from tests/functional/openlp_plugins/presentations/test_presentationcontroller.py rename to tests/openlp_plugins/presentations/test_presentationcontroller.py diff --git a/tests/functional/openlp_plugins/presentations/test_presentationtab.py b/tests/openlp_plugins/presentations/test_presentationtab.py similarity index 100% rename from tests/functional/openlp_plugins/presentations/test_presentationtab.py rename to tests/openlp_plugins/presentations/test_presentationtab.py diff --git a/tests/functional/openlp_plugins/songs/__init__.py b/tests/openlp_plugins/songs/__init__.py similarity index 100% rename from tests/functional/openlp_plugins/songs/__init__.py rename to tests/openlp_plugins/songs/__init__.py diff --git a/tests/interfaces/openlp_core/widgets/__init__.py b/tests/openlp_plugins/songs/forms/__init__.py similarity index 100% rename from tests/interfaces/openlp_core/widgets/__init__.py rename to tests/openlp_plugins/songs/forms/__init__.py diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py b/tests/openlp_plugins/songs/forms/test_authorsform.py similarity index 100% rename from tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py rename to tests/openlp_plugins/songs/forms/test_authorsform.py diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py b/tests/openlp_plugins/songs/forms/test_editsongform.py similarity index 100% rename from tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py rename to tests/openlp_plugins/songs/forms/test_editsongform.py diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py b/tests/openlp_plugins/songs/forms/test_editverseform.py similarity index 100% rename from tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py rename to tests/openlp_plugins/songs/forms/test_editverseform.py diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_songmaintenanceform.py b/tests/openlp_plugins/songs/forms/test_songmaintenanceform.py similarity index 100% rename from tests/interfaces/openlp_plugins/songs/forms/test_songmaintenanceform.py rename to tests/openlp_plugins/songs/forms/test_songmaintenanceform.py diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_topicsform.py b/tests/openlp_plugins/songs/forms/test_topicsform.py similarity index 100% rename from tests/interfaces/openlp_plugins/songs/forms/test_topicsform.py rename to tests/openlp_plugins/songs/forms/test_topicsform.py diff --git a/tests/functional/openlp_plugins/songs/test_chordproimport.py b/tests/openlp_plugins/songs/test_chordproimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_chordproimport.py rename to tests/openlp_plugins/songs/test_chordproimport.py diff --git a/tests/functional/openlp_plugins/songs/test_db.py b/tests/openlp_plugins/songs/test_db.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_db.py rename to tests/openlp_plugins/songs/test_db.py diff --git a/tests/functional/openlp_plugins/songs/test_easyslidesimport.py b/tests/openlp_plugins/songs/test_easyslidesimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_easyslidesimport.py rename to tests/openlp_plugins/songs/test_easyslidesimport.py diff --git a/tests/functional/openlp_plugins/songs/test_editsongform.py b/tests/openlp_plugins/songs/test_editsongform.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_editsongform.py rename to tests/openlp_plugins/songs/test_editsongform.py diff --git a/tests/functional/openlp_plugins/songs/test_editverseform.py b/tests/openlp_plugins/songs/test_editverseform.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_editverseform.py rename to tests/openlp_plugins/songs/test_editverseform.py diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/openlp_plugins/songs/test_ewimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_ewimport.py rename to tests/openlp_plugins/songs/test_ewimport.py diff --git a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py b/tests/openlp_plugins/songs/test_foilpresenterimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_foilpresenterimport.py rename to tests/openlp_plugins/songs/test_foilpresenterimport.py diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/openlp_plugins/songs/test_lib.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_lib.py rename to tests/openlp_plugins/songs/test_lib.py diff --git a/tests/functional/openlp_plugins/songs/test_liveworshipimport.py b/tests/openlp_plugins/songs/test_liveworshipimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_liveworshipimport.py rename to tests/openlp_plugins/songs/test_liveworshipimport.py diff --git a/tests/functional/openlp_plugins/songs/test_lyriximport.py b/tests/openlp_plugins/songs/test_lyriximport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_lyriximport.py rename to tests/openlp_plugins/songs/test_lyriximport.py diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/openlp_plugins/songs/test_mediaitem.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_mediaitem.py rename to tests/openlp_plugins/songs/test_mediaitem.py diff --git a/tests/functional/openlp_plugins/songs/test_mediashout.py b/tests/openlp_plugins/songs/test_mediashout.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_mediashout.py rename to tests/openlp_plugins/songs/test_mediashout.py diff --git a/tests/functional/openlp_plugins/songs/test_openlpimporter.py b/tests/openlp_plugins/songs/test_openlpimporter.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_openlpimporter.py rename to tests/openlp_plugins/songs/test_openlpimporter.py diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsexport.py b/tests/openlp_plugins/songs/test_openlyricsexport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_openlyricsexport.py rename to tests/openlp_plugins/songs/test_openlyricsexport.py diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/openlp_plugins/songs/test_openlyricsimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_openlyricsimport.py rename to tests/openlp_plugins/songs/test_openlyricsimport.py diff --git a/tests/functional/openlp_plugins/songs/test_openoffice.py b/tests/openlp_plugins/songs/test_openoffice.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_openoffice.py rename to tests/openlp_plugins/songs/test_openoffice.py diff --git a/tests/functional/openlp_plugins/songs/test_opensongimport.py b/tests/openlp_plugins/songs/test_opensongimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_opensongimport.py rename to tests/openlp_plugins/songs/test_opensongimport.py diff --git a/tests/functional/openlp_plugins/songs/test_opsproimport.py b/tests/openlp_plugins/songs/test_opsproimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_opsproimport.py rename to tests/openlp_plugins/songs/test_opsproimport.py diff --git a/tests/functional/openlp_plugins/songs/test_plugin.py b/tests/openlp_plugins/songs/test_plugin.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_plugin.py rename to tests/openlp_plugins/songs/test_plugin.py diff --git a/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py b/tests/openlp_plugins/songs/test_powerpraiseimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_powerpraiseimport.py rename to tests/openlp_plugins/songs/test_powerpraiseimport.py diff --git a/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py b/tests/openlp_plugins/songs/test_presentationmanagerimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py rename to tests/openlp_plugins/songs/test_presentationmanagerimport.py diff --git a/tests/functional/openlp_plugins/songs/test_propresenterimport.py b/tests/openlp_plugins/songs/test_propresenterimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_propresenterimport.py rename to tests/openlp_plugins/songs/test_propresenterimport.py diff --git a/tests/functional/openlp_plugins/songs/test_reporting.py b/tests/openlp_plugins/songs/test_reporting.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_reporting.py rename to tests/openlp_plugins/songs/test_reporting.py diff --git a/tests/functional/openlp_plugins/songs/test_singingthefaithimport.py b/tests/openlp_plugins/songs/test_singingthefaithimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_singingthefaithimport.py rename to tests/openlp_plugins/songs/test_singingthefaithimport.py diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/openlp_plugins/songs/test_songbeamerimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_songbeamerimport.py rename to tests/openlp_plugins/songs/test_songbeamerimport.py diff --git a/tests/functional/openlp_plugins/songs/test_songformat.py b/tests/openlp_plugins/songs/test_songformat.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_songformat.py rename to tests/openlp_plugins/songs/test_songformat.py diff --git a/tests/functional/openlp_plugins/songs/test_songproimport.py b/tests/openlp_plugins/songs/test_songproimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_songproimport.py rename to tests/openlp_plugins/songs/test_songproimport.py diff --git a/tests/functional/openlp_plugins/songs/test_songselect.py b/tests/openlp_plugins/songs/test_songselect.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_songselect.py rename to tests/openlp_plugins/songs/test_songselect.py diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/openlp_plugins/songs/test_songshowplusimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_songshowplusimport.py rename to tests/openlp_plugins/songs/test_songshowplusimport.py diff --git a/tests/functional/openlp_plugins/songs/test_songstab.py b/tests/openlp_plugins/songs/test_songstab.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_songstab.py rename to tests/openlp_plugins/songs/test_songstab.py diff --git a/tests/functional/openlp_plugins/songs/test_sundayplusimport.py b/tests/openlp_plugins/songs/test_sundayplusimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_sundayplusimport.py rename to tests/openlp_plugins/songs/test_sundayplusimport.py diff --git a/tests/functional/openlp_plugins/songs/test_videopsalm.py b/tests/openlp_plugins/songs/test_videopsalm.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_videopsalm.py rename to tests/openlp_plugins/songs/test_videopsalm.py diff --git a/tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py b/tests/openlp_plugins/songs/test_wordsofworshipimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py rename to tests/openlp_plugins/songs/test_wordsofworshipimport.py diff --git a/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py b/tests/openlp_plugins/songs/test_worshipassistantimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_worshipassistantimport.py rename to tests/openlp_plugins/songs/test_worshipassistantimport.py diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/openlp_plugins/songs/test_worshipcenterproimport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py rename to tests/openlp_plugins/songs/test_worshipcenterproimport.py diff --git a/tests/functional/openlp_plugins/songs/test_zionworximport.py b/tests/openlp_plugins/songs/test_zionworximport.py similarity index 100% rename from tests/functional/openlp_plugins/songs/test_zionworximport.py rename to tests/openlp_plugins/songs/test_zionworximport.py diff --git a/tests/functional/openlp_plugins/songusage/__init__.py b/tests/openlp_plugins/songusage/__init__.py similarity index 100% rename from tests/functional/openlp_plugins/songusage/__init__.py rename to tests/openlp_plugins/songusage/__init__.py diff --git a/tests/functional/openlp_plugins/songusage/test_songusage.py b/tests/openlp_plugins/songusage/test_songusage.py similarity index 100% rename from tests/functional/openlp_plugins/songusage/test_songusage.py rename to tests/openlp_plugins/songusage/test_songusage.py