Merge branch 'bug-940' into 'master'

Add checks to prevent multiple Linked Audio items on songs

Closes #940

See merge request openlp/openlp!728
This commit is contained in:
Tim Bentley 2024-03-25 07:51:34 +00:00
commit b569a1793d
2 changed files with 132 additions and 0 deletions

View File

@ -278,6 +278,12 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
name=VerseType.translated_name(tag[0]),
number=tag[1:]))
return False
if self.audio_list_widget.count() > 1:
self.song_tab_widget.setCurrentIndex(3)
critical_error_message_box(message=translate('SongsPlugin.EditSongForm',
'Cannot link more than one audio file. Remove items from '
'Linked Audio other than the one you wish to keep.'))
return False
return True
def _validate_tags(self, tags, first_time=True):
@ -942,6 +948,10 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
"""
Loads file(s) from the filesystem.
"""
if self.audio_list_widget.count() > 0:
critical_error_message_box(message=translate('SongsPlugin.EditSongForm',
'Cannot link more than one audio file.'))
return
filters = '{text} (*)'.format(text=UiStrings().AllFiles)
file_paths, filter_used = FileDialog.getOpenFileNames(
parent=self, caption=translate('SongsPlugin.EditSongForm', 'Open File(s)'), filter=filters)
@ -954,6 +964,10 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
"""
Loads file(s) from the media plugin.
"""
if self.audio_list_widget.count() > 0:
critical_error_message_box(message=translate('SongsPlugin.EditSongForm',
'Cannot link more than one audio file.'))
return
if self.media_form.exec():
for file_path in self.media_form.get_selected_files():
item = QtWidgets.QListWidgetItem(file_path.name)

View File

@ -21,10 +21,16 @@
"""
This module contains tests for the lib submodule of the Songs plugin.
"""
import logging
import pytest
from unittest.mock import MagicMock, patch
from PyQt5 import QtCore, QtWidgets
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.plugins.songs.forms.editsongform import EditSongForm
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.db import Author
@pytest.fixture()
@ -33,6 +39,17 @@ def edit_song_form():
return EditSongForm(None, MagicMock(), MagicMock())
@pytest.fixture()
def edit_song_form_with_ui(settings: Settings) -> EditSongForm:
main_window = QtWidgets.QMainWindow()
Registry().register('main_window', main_window)
Registry().register('theme_manager', MagicMock())
form = EditSongForm(None, main_window, MagicMock())
yield form
del form
del main_window
def test_validate_matching_tags(edit_song_form):
# Given a set of tags
tags = ['{r}', '{/r}', '{bl}', '{/bl}', '{su}', '{/su}']
@ -86,3 +103,104 @@ def test_load_objects(mocked_set_case_insensitive_completer, edit_song_form, set
mocked_set_case_insensitive_completer.assert_called_once_with(mocked_cache, mocked_combo)
mocked_combo.setCurrentIndex.assert_called_once_with(-1)
mocked_combo.setCurrentText.assert_called_once_with('')
def test_add_multiple_audio_from_file(edit_song_form_with_ui: EditSongForm):
"""
Test that not more than one Linked Audio item can be added
"""
# GIVEN: A Linked Audio list with 1 item and mocked error message handler
item = QtWidgets.QListWidgetItem('Audio file')
edit_song_form_with_ui.audio_list_widget.addItem(item)
mocked_error_message = MagicMock()
Registry().get('main_window').error_message = mocked_error_message
# WHEN: Add File is clicked
edit_song_form_with_ui.on_audio_add_from_file_button_clicked()
# THEN: A call to show an error message should have been made and no items should have been added
mocked_error_message.assert_called_once()
assert edit_song_form_with_ui.audio_list_widget.count() == 1
def test_add_multiple_audio_from_media(edit_song_form_with_ui: EditSongForm):
"""
Test that not more than one Linked Audio item can be added
"""
# GIVEN: A Linked Audio list with 1 item and mocked error message handler
item = QtWidgets.QListWidgetItem('Audio file')
edit_song_form_with_ui.audio_list_widget.addItem(item)
mocked_error_message = MagicMock()
Registry().get('main_window').error_message = mocked_error_message
# WHEN: Add Media is clicked
edit_song_form_with_ui.on_audio_add_from_media_button_clicked()
# THEN: A call to show an error message should have been made and no items should have been added
mocked_error_message.assert_called_once()
assert edit_song_form_with_ui.audio_list_widget.count() == 1
def test_validate_song_multiple_audio(edit_song_form_with_ui: EditSongForm):
"""
Test that a form with multiple Linked Audio items does not pass validation
"""
# GIVEN: A form with title, lyrics, an author, and 2 Linked Audio items
edit_song_form_with_ui.title_edit.setText('Song Title')
verse_def = '{tag}{number}'.format(tag=VerseType.tags[0], number=1)
song_lyrics = 'Song Lyrics'
verse_item = QtWidgets.QTableWidgetItem(song_lyrics)
verse_item.setData(QtCore.Qt.UserRole, verse_def)
verse_item.setText(song_lyrics)
edit_song_form_with_ui.verse_list_widget.setRowCount(1)
edit_song_form_with_ui.verse_list_widget.setItem(0, 0, verse_item)
item_1 = QtWidgets.QListWidgetItem('Audio file 1')
item_2 = QtWidgets.QListWidgetItem('Audio file 2')
edit_song_form_with_ui.audio_list_widget.addItem(item_1)
edit_song_form_with_ui.audio_list_widget.addItem(item_2)
author = Author(first_name='', last_name='', display_name='Author')
author_type = edit_song_form_with_ui.author_types_combo_box.itemData(0)
edit_song_form_with_ui._add_author_to_list(author, author_type)
mocked_error_message = MagicMock()
Registry().get('main_window').error_message = mocked_error_message
# WHEN: Song is validated
song_valid = edit_song_form_with_ui._validate_song()
# THEN: It should not be valid
assert song_valid is False
def test_validate_song_one_audio(edit_song_form_with_ui: EditSongForm):
"""
Test that a form with one Linked Audio item passes validation
"""
# GIVEN: A form with title, lyrics, an author, and 1 Linked Audio item
edit_song_form_with_ui.title_edit.setText('Song Title')
verse_def = '{tag}{number}'.format(tag=VerseType.tags[0], number=1)
song_lyrics = 'Song Lyrics'
verse_item = QtWidgets.QTableWidgetItem(song_lyrics)
verse_item.setData(QtCore.Qt.UserRole, verse_def)
verse_item.setText(song_lyrics)
edit_song_form_with_ui.verse_list_widget.setRowCount(1)
edit_song_form_with_ui.verse_list_widget.setItem(0, 0, verse_item)
item_1 = QtWidgets.QListWidgetItem('Audio file 1')
edit_song_form_with_ui.audio_list_widget.addItem(item_1)
author = Author(first_name='', last_name='', display_name='Author')
author_type = edit_song_form_with_ui.author_types_combo_box.itemData(0)
edit_song_form_with_ui._add_author_to_list(author, author_type)
# If the validation does fail for some reason it will likely try to display an error message,
# so make sure error_message exists to avoid an error
mocked_error_message = MagicMock()
Registry().get('main_window').error_message = mocked_error_message
# WHEN: Song is validated
song_valid = edit_song_form_with_ui._validate_song()
# Log the error message to help determine the cause in the case validation failed
if mocked_error_message.called:
_title, message = mocked_error_message.call_args.args
logging.error('Validation error message: {message}'.format(message=message))
# THEN: It should be valid
assert song_valid is True