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