# -*- coding: utf-8 -*- ########################################################################## # OpenLP - Open Source Lyrics Projection # # ---------------------------------------------------------------------- # # Copyright (c) 2008-2022 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.forms.selectplanform package. """ import os import re from datetime import date, datetime from unittest import TestCase, skip from unittest.mock import patch, MagicMock from PyQt5 import QtWidgets, QtTest, QtCore from openlp.core.common.registry import Registry from openlp.core.common.settings import Settings from openlp.core.state import State from openlp.core.ui.servicemanager import ServiceManager from openlp.core.ui.settingsform import SettingsForm from openlp.core.ui.thememanager import ThemeManager from openlp.plugins.bibles.bibleplugin import BiblePlugin from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem from openlp.plugins.custom.customplugin import CustomPlugin from openlp.plugins.custom.lib.mediaitem import CustomMediaItem from openlp.plugins.planningcenter.forms.selectplanform import SelectPlanForm from openlp.plugins.planningcenter.planningcenterplugin import PlanningCenterPlugin 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__), TEST_RESOURCES_PATH, 'planningcenter')) class TestSelectPlanForm(TestCase, TestMixin): """ Test the SelectPlanForm class """ def setUp(self): """ Create the UI """ self.registry = Registry() Registry.create() self.setup_application() self.build_settings() State().load_settings() Registry().register('settings', Settings()) Registry().register('main_window', MagicMock()) self.application_id = 'abc' self.secret = '123' Settings().setValue('planningcenter/application_id', self.application_id) Settings().setValue('planningcenter/secret', self.secret) # init the planning center plugin so we have default values defined for Settings() self.planning_center_plugin = PlanningCenterPlugin() # setup our form self.form = SelectPlanForm() self.form.planning_center_api.airplane_mode = True self.form.planning_center_api.airplane_mode_directory = TEST_PATH self.theme_manager = ThemeManager(None) self.theme_manager.get_theme_names = MagicMock() self.theme_manager.get_theme_names.return_value = ['themeA', 'themeB'] def tearDown(self): """ Delete all the C++ objects at the end so that we don't have a segfault """ del self.form del self.planning_center_plugin del self.theme_manager del self.registry self.destroy_settings() def test_initial_defaults(self): """ Test that the SelectPlanForm displays with correct defaults """ # GIVEN: An SelectPlanForm instance with airplane mode enabled, resources available, # a theme manager with mocked themes, and a fake date = Sunday (7/29/2018) with patch('PyQt5.QtWidgets.QDialog.exec'), \ patch('openlp.plugins.planningcenter.forms.selectplanform.date') as mock_date: # need to always return 9/29/2019 for date.today() mock_date.today.return_value = date(2019, 9, 29) mock_date.side_effect = lambda *args, **kw: date(*args, **kw) # WHEN: The form is shown self.form.exec() # THEN: The correct count of service types show up in the combo box combo_box_count = self.form.service_type_combo_box.count() self.assertEqual(combo_box_count, 2, 'The service_type_combo_box contains 2 items') # The first service type is selected self.assertEqual(self.form.service_type_combo_box.currentText(), 'gbf', 'The service_type_combo_box defaults to "gbf"') # the selected plan is today (the mocked date is a Sunday). Set to lowercase beacuse in some locales # months is not capitalized. self.assertEqual(self.form.plan_selection_combo_box.currentText().lower(), date.strftime(mock_date.today.return_value, '%B %d, %Y').lower(), 'Incorrect default date selected for Plan Date') # count the number of themes listed and make sure it matches expected value self.assertEqual(self.form.song_theme_selection_combo_box.count(), 2, 'Count of song themes is incorrect') self.assertEqual(self.form.slide_theme_selection_combo_box.count(), 2, 'Count of custom slide themes is incorrect') def test_warning_messagebox_shown_for_bad_credentials(self): """ Test that if we don't have good credentials, then it will show a QMessageBox with a warning in it """ # GIVEN: A SelectPlanForm instance with airplane mode enabled, resources, available, # mocked check_credentials function to return '' with patch('PyQt5.QtWidgets.QDialog.exec'), \ patch.object(self.form.planning_center_api, 'check_credentials', return_value=''), \ patch('PyQt5.QtWidgets.QMessageBox.warning') as mock_warning: # WHEN: form is displayed self.form.exec() # THEN: we should have called a warning messagebox mock_warning.assert_called_once() def test_disable_import_buttons(self): """ Test that the import buttons are disabled when the "Select Plan Date" element in the Plan Selection List is selected. """ # GIVEN: An SelectPlanForm instance with airplane mode enabled, resources available, and the form with patch('PyQt5.QtWidgets.QDialog.exec'): self.form.exec() # WHEN: The Select Plan combo box is set to "Select Plan Date" index = self.form.plan_selection_combo_box.findText('Select Plan Date') self.form.plan_selection_combo_box.setCurrentIndex(index) # THEN: "Import New" and "Refresh Service" buttons become inactive self.assertEqual(self.form.import_as_new_button.isEnabled(), False, '"Import as New" button should be disabled') self.assertEqual(self.form.update_existing_button.isEnabled(), False, '"Refresh Service" button should be disabled') def test_default_plan_date_is_next_sunday(self): """ Test that the SelectPlanForm displays Next Sunday's Date by Default """ # GIVEN: An SelectPlanForm instance with airplane mode enabled, resources available, # a theme manager with mocked themes, and a fake date = (9/24/2019) with patch('PyQt5.QtWidgets.QDialog.exec'), \ patch('openlp.plugins.planningcenter.forms.selectplanform.date') as mock_date: # need to always return 9/24/2019 for date.today() mock_date.today.return_value = date(2019, 9, 24) mock_date.side_effect = lambda *args, **kw: date(*args, **kw) self.form.exec() # WHEN: The second (index=1) service type is selected self.form.service_type_combo_box.setCurrentIndex(1) # THEN: The plan selection date is 9/29 (the following Sunday) self.assertEqual(self.form.plan_selection_combo_box.currentText(), 'September 29, 2019', 'The next Sunday\'s Date is not selected in the plan_selection_combo_box') def test_service_type_changed_called_when_service_type_combo_changed(self): """ Test that the "on_service_type_combobox_changed" function is executed when the service_type_combobox is changed """ # GIVEN: An SelectPlanForm instance with airplane mode enabled, resources available with patch('PyQt5.QtWidgets.QDialog.exec'): self.form.exec() # WHEN: The Service Type combo is set to index 1 self.form.service_type_combo_box.setCurrentIndex(1) # THEN: The on_service_type_combobox_changed function is called assert self.form.plan_selection_combo_box.count() > 0, 'Plan Selection Combo Box is not empty' assert self.form.plan_selection_combo_box.itemText(0) == 'Select Plan Date', 'Plan Combo Box has default text' def test_plan_selection_changed_called_when_plan_selection_combo_changed(self): """ Test that the "on_plan_selection_combobox_changed" function is executed when the plan_selection_combobox is changed """ # GIVEN: An SelectPlanForm instance with airplane mode enabled, resources available, with patch('PyQt5.QtWidgets.QDialog.exec'): self.form.exec() # WHEN: The Service Type combo is set to index 1 self.form.service_type_combo_box.setCurrentIndex(1) self.form.plan_selection_combo_box.setCurrentIndex(1) # THEN: The import and update buttons should be enabled assert self.form.import_as_new_button.isEnabled() is True, 'Import button should be enabled' assert self.form.update_existing_button.isEnabled() is True, 'Update button should be enabled' def test_settings_tab_displayed_when_edit_auth_button_clicked(self): """ Test that the settings dialog is displayed when the edit_auth_button is clicked """ # GIVEN: A SelectPlanForm instance with airplane mode enabled and resources available with patch('PyQt5.QtWidgets.QDialog.exec'), \ patch('openlp.core.ui.settingsform.SettingsForm.exec') as mock_settings_form: SettingsForm() self.form.exec() # WHEN: the edit_auth_button is clicked QtTest.QTest.mouseClick(self.form.edit_auth_button, QtCore.Qt.LeftButton) self.assertEqual(mock_settings_form.called, 1, "Settings Form opened when edit_auth_button clicked") def test_import_function_called_when_import_button_clicked(self): """ Test that the "on_import_as_new_button_clicked" function is executed when the "Import New" button is clicked """ # GIVEN: An SelectPlanForm instance with airplane mode enabled, resources available, with patch('PyQt5.QtWidgets.QDialog.exec'), \ patch('openlp.plugins.planningcenter.forms.selectplanform.SelectPlanForm._do_import') \ as mock_do_import: self.form.exec() # WHEN: The Service Type combo is set to index 1 and the Select Plan combo box is set # to index 1 and the "Import New" button is clicked self.form.service_type_combo_box.setCurrentIndex(1) self.form.plan_selection_combo_box.setCurrentIndex(4) QtTest.QTest.mouseClick(self.form.import_as_new_button, QtCore.Qt.LeftButton) # THEN: The on_import_as_new_button_cliced function is called mock_do_import.assert_called_with(update=False) def test_service_imported_when_import_button_clicked(self): """ Test that a service is imported when the "Import New" button is clicked """ # GIVEN: An SelectPlanForm instance with airplane mode enabled, resources available, # mocked out "on_new_service_clicked" with patch('PyQt5.QtWidgets.QDialog.exec'), \ patch('openlp.plugins.planningcenter.lib.songimport.PlanningCenterSongImport.finish') \ as mock_song_import, \ patch('openlp.plugins.planningcenter.lib.customimport.CustomSlide') as mock_custom_slide_import, \ patch('openlp.plugins.planningcenter.forms.selectplanform.parse_reference') as mock_bible_import, \ patch('openlp.plugins.planningcenter.forms.selectplanform.date') as mock_date: # need to always return 9/29/2019 for date.today() mock_date.today.return_value = date(2019, 9, 29) mock_date.side_effect = lambda *args, **kw: date(*args, **kw) self.form.exec() Registry().register('service_manager', MagicMock()) Registry().register('plugin_manager', MagicMock()) Registry().register('songs', MagicMock()) Registry().register('bibles', MagicMock()) Registry().register('custom', MagicMock()) # WHEN: The Service Type combo is set to index 1 and the Select Plan combo box is set to # index 1 and the "Import New" button is clicked self.form.service_type_combo_box.setCurrentIndex(1) QtTest.QTest.mouseClick(self.form.import_as_new_button, QtCore.Qt.LeftButton) # THEN: There should be 5 service items added, 1 song, 3 custom slides (one is a bible # title slide), and 1 bible verse self.assertEqual(mock_song_import.call_count, 1, '1 song added via song_media_item') self.assertEqual(mock_custom_slide_import.call_count, 4, '4 custom slide added via custom_media_item') self.assertEqual(mock_bible_import.call_count, 2, '2 bible verses submitted for parsing') def test_service_refreshed_when_refresh_button_clicked(self): """ Test that a service is refreshed when the "Refresh Service" button is clicked """ # GIVEN: An SelectPlanForm instance with airplane mode enabled, resources available, # mocked out "on_new_service_clicked" with patch('PyQt5.QtWidgets.QDialog.exec'), \ patch('openlp.plugins.planningcenter.lib.songimport.PlanningCenterSongImport.finish') \ as mock_song_import, \ patch('openlp.plugins.planningcenter.lib.customimport.CustomSlide') as mock_custom_slide_import, \ patch('openlp.plugins.planningcenter.forms.selectplanform.parse_reference') as mock_bible_import, \ patch('openlp.plugins.planningcenter.forms.selectplanform.date') as mock_date: # need to always return 9/29/2019 for date.today() mock_date.today.return_value = date(2019, 9, 29) mock_date.side_effect = lambda *args, **kw: date(*args, **kw) self.form.exec() Registry().register('service_manager', MagicMock()) Registry().register('plugin_manager', MagicMock()) Registry().register('songs', MagicMock()) Registry().register('bibles', MagicMock()) Registry().register('custom', MagicMock()) # WHEN: The Service Type combo is set to index 1 and the Select Plan combo box is # set to index 1 and the "Update" button is clicked self.form.service_type_combo_box.setCurrentIndex(1) QtTest.QTest.mouseClick(self.form.update_existing_button, QtCore.Qt.LeftButton) # THEN: There should be 5 service items added, 1 song, 3 custom slides (one is a bible # title slide), and 1 bible verse self.assertEqual(mock_song_import.call_count, 1, '1 song added via song_media_item') self.assertEqual(mock_custom_slide_import.call_count, 4, '4 custom slide added via custom_media_item') self.assertEqual(mock_bible_import.call_count, 2, '2 bible verses submitted for parsing') def test_other_bible_is_used_when_bible_gui_form_is_blank(self): """ Test that an other bible is used when the GUI has an empty string for current selected bible """ # GIVEN: An SelectPlanForm instance with airplane mode enabled, resources available, # mocked out "on_new_service_clicked" with patch('PyQt5.QtWidgets.QDialog.exec'), \ patch('openlp.plugins.planningcenter.lib.songimport.PlanningCenterSongImport.finish'), \ patch('openlp.plugins.planningcenter.lib.customimport.CustomSlide'), \ patch('openlp.plugins.planningcenter.forms.selectplanform.parse_reference') as mock_bible_import, \ patch('openlp.plugins.planningcenter.forms.selectplanform.date') as mock_date: # need to always return 9/29/2019 for date.today() mock_date.today.return_value = date(2019, 9, 29) mock_date.side_effect = lambda *args, **kw: date(*args, **kw) mock_bibles = {} mock_bibles['other_bible'] = MagicMock() Registry().register('service_manager', MagicMock()) Registry().register('plugin_manager', MagicMock()) Registry().register('songs', MagicMock()) Registry().register('bibles', MagicMock()) Registry().register('custom', MagicMock()) self.form.exec() # WHEN: The Service Type combo is set to index 1 and the Select Plan combo box # is set to index 1 and the "Import New" button is clicked self.form.service_type_combo_box.setCurrentIndex(1) QtTest.QTest.mouseClick(self.form.import_as_new_button, QtCore.Qt.LeftButton) # THEN: There should be 2 bible verse parse attempts self.assertEqual(mock_bible_import.call_count, 2, '2 bible verses submitted for parsing') def _create_mock_action(self, name, **kwargs): """ Create a fake action with some "real" attributes for Service Manager """ 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 @skip("fails to run when executed with all other openlp tests. awaiting pytest fixtures to enable again") def test_less_mocking_service_refreshed_when_refresh_button_clicked_test(self): """ Test that a service is refreshed when the "Refresh Service" button is clicked """ # GIVEN: An SelectPlanForm instance with airplane mode enabled, resources available, # mocked out "on_new_service_clicked" with patch('PyQt5.QtWidgets.QDialog.exec'), \ patch('openlp.plugins.planningcenter.forms.selectplanform.date') as mock_date: # need to always return 9/29/2019 for date.today() mock_date.today.return_value = date(2019, 9, 29) mock_date.side_effect = lambda *args, **kw: date(*args, **kw) # init ServiceManager Registry().register('plugin_manager', MagicMock()) Registry().register('application', MagicMock()) Registry().register('renderer', MagicMock()) self.service_manager = ServiceManager() self.service_manager.setup_ui(self.service_manager) # init songs plugin with patch('openlp.plugins.songs.lib.mediaitem.EditSongForm'), \ patch('openlp.plugins.custom.lib.mediaitem.EditCustomForm'), \ patch('openlp.core.lib.mediamanageritem.create_widget_action'), \ patch('openlp.core.widgets.toolbar.create_widget_action'): # init songs plugin songs_plugin = SongsPlugin() song_media_item = SongMediaItem(None, songs_plugin) song_media_item.search_text_edit = MagicMock() song_media_item.initialise() # init custom plugin custom_plugin = CustomPlugin() CustomMediaItem(None, custom_plugin) # init bible plugin bible_plugin = BiblePlugin() bible_media_item = BibleMediaItem(None, bible_plugin) bible_media_item.build_display_results = MagicMock() self.form.exec() # WHEN: # The Service Type combo is set to index 1 and "Import New" button is clicked self.form.service_type_combo_box.setCurrentIndex(1) QtTest.QTest.mouseClick(self.form.import_as_new_button, QtCore.Qt.LeftButton) # make changes to the now imported service items # first, for serviceitem[0] update last_updated in xml_string and change "sweet" to "sublime" old_match = re.search('modifiedDate="(.+?)Z*"', self.service_manager.service_items[0]['service_item'].xml_version) old_string = old_match.group(1) now_string = datetime.now().strftime('%Y-%m-%dT%H:%M:%S') self.service_manager.service_items[0]['service_item'].xml_version = \ self.service_manager.service_items[0]['service_item'].xml_version.replace(old_string, now_string) self.service_manager.service_items[0]['service_item'].xml_version = \ self.service_manager.service_items[0]['service_item'].xml_version.replace("sweet", "sublime") # second, add the word modified to the slide text for serviceitem[1] self.service_manager.service_items[1]['service_item'].slides[0]['text'] = \ self.service_manager.service_items[1]['service_item'].slides[0]['text'].replace("Test", "Modified Test") # third, delete serviceitems[2] and serviceitem[3] del self.service_manager.service_items[3] del self.service_manager.service_items[2] # last, draw the form again and request refresh self.form.exec() self.form.service_type_combo_box.setCurrentIndex(1) QtTest.QTest.mouseClick(self.form.update_existing_button, QtCore.Qt.LeftButton) # THEN: # There should be 4 service items added self.assertEqual(len(self.service_manager.service_items), 5, '5 items should be in the ServiceManager') # Amazon Grace should still include sublime self.assertTrue('sublime' in self.service_manager.service_items[0]['service_item'].xml_version) # Slides in service_item[1] should still contain the word "Modified" self.assertTrue('Modified' in self.service_manager.service_items[1]['service_item'].slides[0]['text'])