diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 892680409..7861ae368 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -250,7 +250,7 @@ def build_icon(icon): pix_map = QtGui.QPixmap(icon) elif isinstance(icon, Path): pix_map = QtGui.QPixmap(str(icon)) - elif isinstance(icon, QtGui.QImage): + elif isinstance(icon, QtGui.QImage): # pragma: no cover pix_map = QtGui.QPixmap.fromImage(icon) if pix_map: button_icon.addPixmap(pix_map, QtGui.QIcon.Normal, QtGui.QIcon.Off) diff --git a/openlp/plugins/songs/lib/songstab.py b/openlp/plugins/songs/lib/songstab.py index 198702e0d..4a477a3a0 100644 --- a/openlp/plugins/songs/lib/songstab.py +++ b/openlp/plugins/songs/lib/songstab.py @@ -154,17 +154,16 @@ class SongsTab(SettingsTab): ['ccli_number', translate('SongsPlugin.SongsTab', 'Song CCLI Number'), True, False], ['topics', translate('SongsPlugin.SongsTab', 'Topics'), False, True], ] - placeholder_info = '\n\n'\ - .format(ph=translate('SongsPlugin.SongsTab', 'Placeholder'), - desc=translate('SongsPlugin.SongsTab', 'Description')) + placeholder_info = '
{ph}{desc}
'.format( + ph=translate('SongsPlugin.SongsTab', 'Placeholder'), desc=translate('SongsPlugin.SongsTab', 'Description')) for placeholder in placeholders: - placeholder_info += '\n'\ - .format(pl=placeholder[0], des=placeholder[1], - opt=(' ¹' if placeholder[2] else '') + - (' ²' if placeholder[3] else '')) + placeholder_info += ''.format( + pl=placeholder[0], des=placeholder[1], opt=(' 1' if placeholder[2] else '') + + (' 2' if placeholder[3] else '')) placeholder_info += '
{ph}{desc}
${{{pl}}}{des}{opt}
${{{pl}}}{des}{opt}
' - placeholder_info += '\n
¹ {}'.format(translate('SongsPlugin.SongsTab', 'can be empty')) - placeholder_info += '\n
² {}'.format(translate('SongsPlugin.SongsTab', 'list of entries, can be empty')) + placeholder_info += '

1 {}
'.format(translate('SongsPlugin.SongsTab', 'can be empty')) + placeholder_info += '2 {}

'.format( + translate('SongsPlugin.SongsTab', 'list of entries, can be empty')) self.footer_placeholder_info.setHtml(placeholder_info) self.footer_placeholder_info.setReadOnly(True) diff --git a/tests/functional/openlp_core/api/v2/test_core.py b/tests/functional/openlp_core/api/v2/test_core.py index 5f2428489..3dcf2ba17 100644 --- a/tests/functional/openlp_core/api/v2/test_core.py +++ b/tests/functional/openlp_core/api/v2/test_core.py @@ -113,6 +113,11 @@ def test_toggle_display_invalid_action(flask_client, settings): assert res.status_code == 400 +def test_toggle_display_no_data(flask_client, settings): + res = flask_client.post('/api/v2/core/display', json={}) + assert res.status_code == 400 + + def test_toggle_display_valid_action_updates_controller(flask_client, settings): class FakeController: class Emitter: diff --git a/tests/functional/openlp_core/lib/test_lib.py b/tests/functional/openlp_core/lib/test_lib.py index 756db3147..6bae20572 100644 --- a/tests/functional/openlp_core/lib/test_lib.py +++ b/tests/functional/openlp_core/lib/test_lib.py @@ -220,29 +220,30 @@ def test_build_icon_with_resource(): # best we can do is to assert that we get back a MagicMock object. assert isinstance(result, MagicMock), 'The result should be a MagicMock, because we mocked it out' - def test_image_to_byte(self): - """ - Test the image_to_byte() function - """ - with patch('openlp.core.lib.QtCore') as MockedQtCore: - # GIVEN: A set of mocked-out Qt classes - mocked_byte_array = MagicMock() - MockedQtCore.QByteArray.return_value = mocked_byte_array - mocked_buffer = MagicMock() - MockedQtCore.QBuffer.return_value = mocked_buffer - MockedQtCore.QIODevice.WriteOnly = 'writeonly' - mocked_image = MagicMock() - # WHEN: We convert an image to a byte array - result = image_to_byte(mocked_image, base_64=False) +def test_image_to_byte(): + """ + Test the image_to_byte() function + """ + with patch('openlp.core.lib.QtCore') as MockedQtCore: + # GIVEN: A set of mocked-out Qt classes + mocked_byte_array = MagicMock() + MockedQtCore.QByteArray.return_value = mocked_byte_array + mocked_buffer = MagicMock() + MockedQtCore.QBuffer.return_value = mocked_buffer + MockedQtCore.QIODevice.WriteOnly = 'writeonly' + mocked_image = MagicMock() - # THEN: We should receive the mocked_buffer - MockedQtCore.QByteArray.assert_called_with() - MockedQtCore.QBuffer.assert_called_with(mocked_byte_array) - mocked_buffer.open.assert_called_with('writeonly') - mocked_image.save.assert_called_with(mocked_buffer, "PNG") - assert mocked_byte_array.toBase64.called is False - assert mocked_byte_array == result, 'The mocked out byte array should be returned' + # WHEN: We convert an image to a byte array + result = image_to_byte(mocked_image, base_64=False) + + # THEN: We should receive the mocked_buffer + MockedQtCore.QByteArray.assert_called_with() + MockedQtCore.QBuffer.assert_called_with(mocked_byte_array) + mocked_buffer.open.assert_called_with('writeonly') + mocked_image.save.assert_called_with(mocked_buffer, "PNG") + assert mocked_byte_array.toBase64.called is False + assert mocked_byte_array == result, 'The mocked out byte array should be returned' def test_image_to_byte_base_64(): @@ -498,6 +499,25 @@ def test_create_thumb_empty_img(): pass +@patch('openlp.core.lib.QtGui.QImageReader') +@patch('openlp.core.lib.build_icon') +def test_create_thumb_path_fails(mocked_build_icon, MockQImageReader): + """ + Test that build_icon() is run against the image_path when the thumbnail fails to be created + """ + # GIVEN: A bunch of mocks + image_path = RESOURCE_PATH / 'church.jpg' + thumb_path = Path('doesnotexist') + mocked_image_reader = MagicMock() + mocked_image_reader.size.return_value.isEmpty.return_value = True + + # WHEN: Create the thumb + create_thumb(image_path, thumb_path) + + # THEN: The image path should have been used + mocked_build_icon.assert_called_once_with(image_path) + + @patch('openlp.core.lib.QtWidgets', MagicMock()) def test_check_item_selected_true(): """ @@ -631,6 +651,42 @@ def test_resize_thumb_ignoring_aspect_ratio(): assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.' +def test_resize_thumb_width_aspect_ratio(): + """ + Test the resize_thumb() function using the image's width as the reference + """ + # GIVEN: A path to an image. + image_path = str(RESOURCE_PATH / 'church.jpg') + wanted_width = 10 + wanted_height = 1000 + + # WHEN: Resize the image and add a background. + image = resize_image(image_path, wanted_width, wanted_height) + + # THEN: Check if the size is correct and the background was set. + result_size = image.size() + assert wanted_height == result_size.height(), 'The image should have the requested height.' + assert wanted_width == result_size.width(), 'The image should have the requested width.' + + +def test_resize_thumb_same_aspect_ratio(): + """ + Test the resize_thumb() function when the image and the wanted aspect ratio are the same + """ + # GIVEN: A path to an image. + image_path = str(RESOURCE_PATH / 'church.jpg') + wanted_width = 1122 + wanted_height = 1544 + + # WHEN: Resize the image and add a background. + image = resize_image(image_path, wanted_width, wanted_height) + + # THEN: Check if the size is correct and the background was set. + result_size = image.size() + assert wanted_height == result_size.height(), 'The image should have the requested height.' + assert wanted_width == result_size.width(), 'The image should have the requested width.' + + @patch('openlp.core.lib.QtCore.QLocale.createSeparatedList') def test_create_separated_list_qlocate(mocked_createSeparatedList): """ diff --git a/tests/functional/openlp_core/ui/test_firsttimeform.py b/tests/functional/openlp_core/ui/test_firsttimeform.py index 7a689c030..7b214ec8e 100644 --- a/tests/functional/openlp_core/ui/test_firsttimeform.py +++ b/tests/functional/openlp_core/ui/test_firsttimeform.py @@ -25,10 +25,11 @@ import pytest from pathlib import Path from unittest.mock import MagicMock, call, patch, DEFAULT -from PyQt5 import QtWidgets +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 ThemeListWidget INVALID_CONFIG = """ @@ -364,3 +365,22 @@ def test_on_projectors_check_box_unchecked(mock_settings): # THEN: The visibility of the projects panel should have been set mock_settings.value.assert_called_once_with('projector/show after wizard') mock_settings.setValue.assert_called_once_with('projector/show after wizard', True) + + +def test_theme_list_widget_resize(ftf_app): + """ + Test that the resizeEvent() method in the ThemeListWidget works correctly + """ + # GIVEN: An instance of the ThemeListWidget + widget = ThemeListWidget(None) + + # WHEN: resizeEvent() is called + with patch.object(widget, 'viewport') as mocked_viewport, \ + patch.object(widget, 'setGridSize') as mocked_setGridSize: + mocked_viewport.return_value.width.return_value = 600 + widget.resizeEvent(None) + + # THEN: Check that the correct calculations were done + mocked_setGridSize.assert_called_once_with(QtCore.QSize(149, 140)) + + # THEN: everything resizes correctly diff --git a/tests/functional/openlp_plugins/bibles/test_lib.py b/tests/functional/openlp_plugins/bibles/test_lib.py index b9eae6e67..1ed99537d 100644 --- a/tests/functional/openlp_plugins/bibles/test_lib.py +++ b/tests/functional/openlp_plugins/bibles/test_lib.py @@ -26,7 +26,7 @@ from unittest.mock import MagicMock, patch from openlp.core.common.registry import Registry from openlp.plugins.bibles import lib -from openlp.plugins.bibles.lib import SearchResults, get_reference_match +from openlp.plugins.bibles.lib import SearchResults, get_reference_match, update_reference_separators @pytest.yield_fixture @@ -252,3 +252,44 @@ def test_reference_matched_range_separator(mocked_bible_test): # THEN: The list of references should be as the expected results assert references == ranges + + +def test_update_reference_separators_custom_seps(request, settings): + """ + Test the update_reference_separators() function with custom separators + """ + # Clean up after the test + def cleanup_references(): + lib.REFERENCE_SEPARATORS = {} + settings.remove('bibles/verse separator') + settings.remove('bibles/range separator') + settings.remove('bibles/list separator') + settings.remove('bibles/end separator') + update_reference_separators() + + request.addfinalizer(cleanup_references) + + # GIVEN: A custom set of separators + settings.setValue('bibles/verse separator', ':||v') + settings.setValue('bibles/range separator', '-') + settings.setValue('bibles/list separator', ',') + settings.setValue('bibles/end separator', '.') + + # WHEN: update_reference_separators() is called + update_reference_separators() + + # THEN: The reference separators should be updated and correct + expected_separators = { + 'sep_e': '\\s*(?:\\.)\\s*', + 'sep_e_default': 'end', + 'sep_l': '\\s*(?:(?:[,‚]))\\s*', + 'sep_l_default': ',|and', + 'sep_l_display': ',', + 'sep_r': '\\s*(?:(?:[-\xad‐‑‒——−﹣-]))\\s*', + 'sep_r_default': '-|to', + 'sep_r_display': '-', + 'sep_v': '\\s*(?::|v)\\s*', + 'sep_v_default': ':|v|V|verse|verses', + 'sep_v_display': ':' + } + assert lib.REFERENCE_SEPARATORS == expected_separators