forked from openlp/openlp
merge w/ trunk
This commit is contained in:
commit
7971daacdc
@ -515,7 +515,7 @@ class PJLink1(QTcpSocket):
|
|||||||
self.socket_timer.start()
|
self.socket_timer.start()
|
||||||
try:
|
try:
|
||||||
self.projectorNetwork.emit(S_NETWORK_SENDING)
|
self.projectorNetwork.emit(S_NETWORK_SENDING)
|
||||||
sent = self.write(out)
|
sent = self.write(out.encode('ascii'))
|
||||||
self.waitForBytesWritten(2000) # 2 seconds should be enough
|
self.waitForBytesWritten(2000) # 2 seconds should be enough
|
||||||
if sent == -1:
|
if sent == -1:
|
||||||
# Network error?
|
# Network error?
|
||||||
@ -665,7 +665,15 @@ class PJLink1(QTcpSocket):
|
|||||||
|
|
||||||
:param data: Class that projector supports.
|
:param data: Class that projector supports.
|
||||||
"""
|
"""
|
||||||
self.pjlink_class = data
|
# bug 1550891: Projector returns non-standard class response:
|
||||||
|
# : Expected: %1CLSS=1
|
||||||
|
# : Received: %1CLSS=Class 1
|
||||||
|
if len(data) > 1:
|
||||||
|
# Split non-standard information from response
|
||||||
|
clss = data.split()[-1]
|
||||||
|
else:
|
||||||
|
clss = data
|
||||||
|
self.pjlink_class = clss
|
||||||
log.debug('(%s) Setting pjlink_class for this projector to "%s"' % (self.ip, self.pjlink_class))
|
log.debug('(%s) Setting pjlink_class for this projector to "%s"' % (self.ip, self.pjlink_class))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -408,10 +408,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
|
|||||||
self.application.process_events()
|
self.application.process_events()
|
||||||
# Workaround for bug #1531319, should not be needed with PyQt 5.6.
|
# Workaround for bug #1531319, should not be needed with PyQt 5.6.
|
||||||
if is_win():
|
if is_win():
|
||||||
# Workaround for bug #1531319, should not be needed with PyQt 5.6.
|
|
||||||
fade_shake_timer.stop()
|
fade_shake_timer.stop()
|
||||||
elif is_win():
|
|
||||||
self.shake_web_view()
|
|
||||||
# Wait for the webview to update before getting the preview.
|
# Wait for the webview to update before getting the preview.
|
||||||
# Important otherwise first preview will miss the background !
|
# Important otherwise first preview will miss the background !
|
||||||
while not self.web_loaded:
|
while not self.web_loaded:
|
||||||
@ -429,6 +426,9 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
|
|||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
else:
|
else:
|
||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
|
# Workaround for bug #1531319, should not be needed with PyQt 5.6.
|
||||||
|
if is_win():
|
||||||
|
self.shake_web_view()
|
||||||
return self.grab()
|
return self.grab()
|
||||||
|
|
||||||
def build_html(self, service_item, image_path=''):
|
def build_html(self, service_item, image_path=''):
|
||||||
|
@ -601,13 +601,21 @@ class SlideController(DisplayController, RegistryProperties):
|
|||||||
def __add_actions_to_widget(self, widget):
|
def __add_actions_to_widget(self, widget):
|
||||||
"""
|
"""
|
||||||
Add actions to the widget specified by `widget`
|
Add actions to the widget specified by `widget`
|
||||||
|
This defines the controls available when Live display has stolen focus.
|
||||||
|
Examples of this happening: Clicking anything in the live window or certain single screen mode scenarios.
|
||||||
|
Needles to say, blank to modes should not be removed from here.
|
||||||
|
For some reason this required a test. It may be found in test_slidecontroller.py as
|
||||||
|
"live_stolen_focus_shortcuts_test. If you want to modify things here, you must also modify them there. (Duh)
|
||||||
|
|
||||||
:param widget: The UI widget for the actions
|
:param widget: The UI widget for the actions
|
||||||
"""
|
"""
|
||||||
widget.addActions([
|
widget.addActions([
|
||||||
self.previous_item, self.next_item,
|
self.previous_item, self.next_item,
|
||||||
self.previous_service, self.next_service,
|
self.previous_service, self.next_service,
|
||||||
self.escape_item])
|
self.escape_item,
|
||||||
|
self.desktop_screen,
|
||||||
|
self.theme_screen,
|
||||||
|
self.blank_screen])
|
||||||
|
|
||||||
def preview_size_changed(self):
|
def preview_size_changed(self):
|
||||||
"""
|
"""
|
||||||
|
@ -26,7 +26,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtWidgets
|
from PyQt5 import QtCore, QtWidgets
|
||||||
from sqlalchemy.sql import or_
|
from sqlalchemy.sql import and_, or_
|
||||||
|
|
||||||
from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
|
from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
|
||||||
from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \
|
from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \
|
||||||
@ -37,7 +37,7 @@ from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm
|
|||||||
from openlp.plugins.songs.forms.songimportform import SongImportForm
|
from openlp.plugins.songs.forms.songimportform import SongImportForm
|
||||||
from openlp.plugins.songs.forms.songexportform import SongExportForm
|
from openlp.plugins.songs.forms.songexportform import SongExportForm
|
||||||
from openlp.plugins.songs.lib import VerseType, clean_string, delete_song
|
from openlp.plugins.songs.lib import VerseType, clean_string, delete_song
|
||||||
from openlp.plugins.songs.lib.db import Author, AuthorType, Song, Book, MediaFile, SongBookEntry
|
from openlp.plugins.songs.lib.db import Author, AuthorType, Song, Book, MediaFile, SongBookEntry, Topic
|
||||||
from openlp.plugins.songs.lib.ui import SongStrings
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, SongXML
|
from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, SongXML
|
||||||
|
|
||||||
@ -52,8 +52,11 @@ class SongSearch(object):
|
|||||||
Titles = 2
|
Titles = 2
|
||||||
Lyrics = 3
|
Lyrics = 3
|
||||||
Authors = 4
|
Authors = 4
|
||||||
Books = 5
|
Topics = 5
|
||||||
Themes = 6
|
Books = 6
|
||||||
|
Themes = 7
|
||||||
|
Copyright = 8
|
||||||
|
CCLInumber = 9
|
||||||
|
|
||||||
|
|
||||||
class SongMediaItem(MediaManagerItem):
|
class SongMediaItem(MediaManagerItem):
|
||||||
@ -151,9 +154,17 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
translate('SongsPlugin.MediaItem', 'Search Lyrics...')),
|
translate('SongsPlugin.MediaItem', 'Search Lyrics...')),
|
||||||
(SongSearch.Authors, ':/songs/song_search_author.png', SongStrings.Authors,
|
(SongSearch.Authors, ':/songs/song_search_author.png', SongStrings.Authors,
|
||||||
translate('SongsPlugin.MediaItem', 'Search Authors...')),
|
translate('SongsPlugin.MediaItem', 'Search Authors...')),
|
||||||
|
(SongSearch.Topics, ':/songs/song_search_topic.png', SongStrings.Topics,
|
||||||
|
translate('SongsPlugin.MediaItem', 'Search Topics...')),
|
||||||
(SongSearch.Books, ':/songs/song_book_edit.png', SongStrings.SongBooks,
|
(SongSearch.Books, ':/songs/song_book_edit.png', SongStrings.SongBooks,
|
||||||
translate('SongsPlugin.MediaItem', 'Search Songbooks...')),
|
translate('SongsPlugin.MediaItem', 'Search Songbooks...')),
|
||||||
(SongSearch.Themes, ':/slides/slide_theme.png', UiStrings().Themes, UiStrings().SearchThemes)
|
(SongSearch.Themes, ':/slides/slide_theme.png', UiStrings().Themes, UiStrings().SearchThemes),
|
||||||
|
(SongSearch.Copyright, ':/songs/song_search_copy.png',
|
||||||
|
translate('SongsPlugin.MediaItem', 'Copyright'),
|
||||||
|
translate('SongsPlugin.MediaItem', 'Search Copyright...')),
|
||||||
|
(SongSearch.CCLInumber, ':/songs/song_search_ccli.png',
|
||||||
|
translate('SongsPlugin.MediaItem', 'CCLI number'),
|
||||||
|
translate('SongsPlugin.MediaItem', 'Search CCLI number...'))
|
||||||
])
|
])
|
||||||
self.search_text_edit.set_current_search_type(Settings().value('%s/last search type' % self.settings_section))
|
self.search_text_edit.set_current_search_type(Settings().value('%s/last search type' % self.settings_section))
|
||||||
self.config_update()
|
self.config_update()
|
||||||
@ -184,14 +195,33 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
search_results = self.plugin.manager.get_all_objects(
|
search_results = self.plugin.manager.get_all_objects(
|
||||||
Author, Author.display_name.like(search_string), Author.display_name.asc())
|
Author, Author.display_name.like(search_string), Author.display_name.asc())
|
||||||
self.display_results_author(search_results)
|
self.display_results_author(search_results)
|
||||||
|
elif search_type == SongSearch.Topics:
|
||||||
|
log.debug('Topics Search')
|
||||||
|
search_string = '%' + search_keywords + '%'
|
||||||
|
search_results = self.plugin.manager.get_all_objects(
|
||||||
|
Topic, Topic.name.like(search_string), Topic.name.asc())
|
||||||
|
self.display_results_topic(search_results)
|
||||||
elif search_type == SongSearch.Books:
|
elif search_type == SongSearch.Books:
|
||||||
log.debug('Songbook Search')
|
log.debug('Songbook Search')
|
||||||
self.display_results_book(search_keywords)
|
self.display_results_book(search_keywords)
|
||||||
elif search_type == SongSearch.Themes:
|
elif search_type == SongSearch.Themes:
|
||||||
log.debug('Theme Search')
|
log.debug('Theme Search')
|
||||||
search_string = '%' + search_keywords + '%'
|
search_string = '%' + search_keywords + '%'
|
||||||
search_results = self.plugin.manager.get_all_objects(Song, Song.theme_name.like(search_string))
|
search_results = self.plugin.manager.get_all_objects(
|
||||||
|
Song, Song.theme_name.like(search_string), Song.theme_name.asc())
|
||||||
|
self.display_results_themes(search_results)
|
||||||
|
elif search_type == SongSearch.Copyright:
|
||||||
|
log.debug('Copyright Search')
|
||||||
|
search_string = '%' + search_keywords + '%'
|
||||||
|
search_results = self.plugin.manager.get_all_objects(
|
||||||
|
Song, and_(Song.copyright.like(search_string), Song.copyright != ''))
|
||||||
self.display_results_song(search_results)
|
self.display_results_song(search_results)
|
||||||
|
elif search_type == SongSearch.CCLInumber:
|
||||||
|
log.debug('CCLI number Search')
|
||||||
|
search_string = '%' + search_keywords + '%'
|
||||||
|
search_results = self.plugin.manager.get_all_objects(
|
||||||
|
Song, and_(Song.ccli_number.like(search_string), Song.ccli_number != ''))
|
||||||
|
self.display_results_cclinumber(search_results)
|
||||||
self.check_search_result()
|
self.check_search_result()
|
||||||
|
|
||||||
def search_entire(self, search_keywords):
|
def search_entire(self, search_keywords):
|
||||||
@ -215,6 +245,12 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
log.debug('on_song_list_load - finished')
|
log.debug('on_song_list_load - finished')
|
||||||
|
|
||||||
def display_results_song(self, search_results):
|
def display_results_song(self, search_results):
|
||||||
|
"""
|
||||||
|
Display the song search results in the media manager list
|
||||||
|
|
||||||
|
:param search_results: A list of db Song objects
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
log.debug('display results Song')
|
log.debug('display results Song')
|
||||||
self.save_auto_select_id()
|
self.save_auto_select_id()
|
||||||
self.list_view.clear()
|
self.list_view.clear()
|
||||||
@ -234,6 +270,12 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.auto_select_id = -1
|
self.auto_select_id = -1
|
||||||
|
|
||||||
def display_results_author(self, search_results):
|
def display_results_author(self, search_results):
|
||||||
|
"""
|
||||||
|
Display the song search results in the media manager list, grouped by author
|
||||||
|
|
||||||
|
:param search_results: A list of db Author objects
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
log.debug('display results Author')
|
log.debug('display results Author')
|
||||||
self.list_view.clear()
|
self.list_view.clear()
|
||||||
for author in search_results:
|
for author in search_results:
|
||||||
@ -247,6 +289,13 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.list_view.addItem(song_name)
|
self.list_view.addItem(song_name)
|
||||||
|
|
||||||
def display_results_book(self, search_keywords):
|
def display_results_book(self, search_keywords):
|
||||||
|
"""
|
||||||
|
Display the song search results in the media manager list, grouped by book
|
||||||
|
|
||||||
|
:param search_keywords: A list of search keywords - book first, then number
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
log.debug('display results Book')
|
log.debug('display results Book')
|
||||||
self.list_view.clear()
|
self.list_view.clear()
|
||||||
|
|
||||||
@ -269,6 +318,64 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
song_name.setData(QtCore.Qt.UserRole, songbook_entry.song.id)
|
song_name.setData(QtCore.Qt.UserRole, songbook_entry.song.id)
|
||||||
self.list_view.addItem(song_name)
|
self.list_view.addItem(song_name)
|
||||||
|
|
||||||
|
def display_results_topic(self, search_results):
|
||||||
|
"""
|
||||||
|
Display the song search results in the media manager list, grouped by topic
|
||||||
|
|
||||||
|
:param search_results: A list of db Topic objects
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
log.debug('display results Topic')
|
||||||
|
self.list_view.clear()
|
||||||
|
search_results = sorted(search_results, key=lambda topic: self._natural_sort_key(topic.name))
|
||||||
|
for topic in search_results:
|
||||||
|
songs = sorted(topic.songs, key=lambda song: song.sort_key)
|
||||||
|
for song in songs:
|
||||||
|
# Do not display temporary songs
|
||||||
|
if song.temporary:
|
||||||
|
continue
|
||||||
|
song_detail = '%s (%s)' % (topic.name, song.title)
|
||||||
|
song_name = QtWidgets.QListWidgetItem(song_detail)
|
||||||
|
song_name.setData(QtCore.Qt.UserRole, song.id)
|
||||||
|
self.list_view.addItem(song_name)
|
||||||
|
|
||||||
|
def display_results_themes(self, search_results):
|
||||||
|
"""
|
||||||
|
Display the song search results in the media manager list, sorted by theme
|
||||||
|
|
||||||
|
:param search_results: A list of db Song objects
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
log.debug('display results Themes')
|
||||||
|
self.list_view.clear()
|
||||||
|
for song in search_results:
|
||||||
|
# Do not display temporary songs
|
||||||
|
if song.temporary:
|
||||||
|
continue
|
||||||
|
song_detail = '%s (%s)' % (song.theme_name, song.title)
|
||||||
|
song_name = QtWidgets.QListWidgetItem(song_detail)
|
||||||
|
song_name.setData(QtCore.Qt.UserRole, song.id)
|
||||||
|
self.list_view.addItem(song_name)
|
||||||
|
|
||||||
|
def display_results_cclinumber(self, search_results):
|
||||||
|
"""
|
||||||
|
Display the song search results in the media manager list, sorted by CCLI number
|
||||||
|
|
||||||
|
:param search_results: A list of db Song objects
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
log.debug('display results CCLI number')
|
||||||
|
self.list_view.clear()
|
||||||
|
songs = sorted(search_results, key=lambda song: self._natural_sort_key(song.ccli_number))
|
||||||
|
for song in songs:
|
||||||
|
# Do not display temporary songs
|
||||||
|
if song.temporary:
|
||||||
|
continue
|
||||||
|
song_detail = '%s (%s)' % (song.ccli_number, song.title)
|
||||||
|
song_name = QtWidgets.QListWidgetItem(song_detail)
|
||||||
|
song_name.setData(QtCore.Qt.UserRole, song.id)
|
||||||
|
self.list_view.addItem(song_name)
|
||||||
|
|
||||||
def on_clear_text_button_click(self):
|
def on_clear_text_button_click(self):
|
||||||
"""
|
"""
|
||||||
Clear the search text.
|
Clear the search text.
|
||||||
|
@ -3,8 +3,11 @@
|
|||||||
<file>song_search_stop.png</file>
|
<file>song_search_stop.png</file>
|
||||||
<file>song_search_all.png</file>
|
<file>song_search_all.png</file>
|
||||||
<file>song_search_author.png</file>
|
<file>song_search_author.png</file>
|
||||||
|
<file>song_search_ccli.png</file>
|
||||||
|
<file>song_search_copy.png</file>
|
||||||
<file>song_search_lyrics.png</file>
|
<file>song_search_lyrics.png</file>
|
||||||
<file>song_search_title.png</file>
|
<file>song_search_title.png</file>
|
||||||
|
<file>song_search_topic.png</file>
|
||||||
<file>topic_edit.png</file>
|
<file>topic_edit.png</file>
|
||||||
<file>author_add.png</file>
|
<file>author_add.png</file>
|
||||||
<file>author_delete.png</file>
|
<file>author_delete.png</file>
|
||||||
|
BIN
resources/images/song_search_ccli.png
Normal file
BIN
resources/images/song_search_ccli.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 403 B |
BIN
resources/images/song_search_copy.png
Normal file
BIN
resources/images/song_search_copy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 498 B |
BIN
resources/images/song_search_topic.png
Normal file
BIN
resources/images/song_search_topic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 993 B |
@ -60,3 +60,17 @@ class TestPJLink(TestCase):
|
|||||||
"Connection request should have been called with TEST_SALT"))
|
"Connection request should have been called with TEST_SALT"))
|
||||||
self.assertTrue(mock_qmd5_hash.called_with(TEST_PIN,
|
self.assertTrue(mock_qmd5_hash.called_with(TEST_PIN,
|
||||||
"Connection request should have been called with TEST_PIN"))
|
"Connection request should have been called with TEST_PIN"))
|
||||||
|
|
||||||
|
def non_standard_class_reply_test(self):
|
||||||
|
"""
|
||||||
|
bugfix 1550891 - CLSS request returns non-standard 'Class N' reply
|
||||||
|
"""
|
||||||
|
# GIVEN: Test object
|
||||||
|
pjlink = pjlink_test
|
||||||
|
|
||||||
|
# WHEN: Process non-standard reply
|
||||||
|
pjlink.process_clss('Class 1')
|
||||||
|
|
||||||
|
# THEN: Projector class should be set with proper value
|
||||||
|
self.assertEquals(pjlink.pjlink_class, '1',
|
||||||
|
'Non-standard class reply should have set proper class')
|
||||||
|
@ -685,6 +685,34 @@ class TestSlideController(TestCase):
|
|||||||
self.assertEqual('mocked_presentation_item_stop', mocked_execute.call_args_list[1][0][0],
|
self.assertEqual('mocked_presentation_item_stop', mocked_execute.call_args_list[1][0][0],
|
||||||
'The presentation should have been stopped.')
|
'The presentation should have been stopped.')
|
||||||
|
|
||||||
|
def live_stolen_focus_shortcuts_test(self):
|
||||||
|
"""
|
||||||
|
Test that all the needed shortcuts are available in scenarios where Live has stolen focus.
|
||||||
|
These are found under def __add_actions_to_widget(self, widget): in slidecontroller.py
|
||||||
|
"""
|
||||||
|
# GIVEN: A slide controller, actions needed
|
||||||
|
slide_controller = SlideController(None)
|
||||||
|
mocked_widget = MagicMock()
|
||||||
|
slide_controller.previous_item = MagicMock()
|
||||||
|
slide_controller.next_item = MagicMock()
|
||||||
|
slide_controller.previous_service = MagicMock()
|
||||||
|
slide_controller.next_service = MagicMock()
|
||||||
|
slide_controller.escape_item = MagicMock()
|
||||||
|
slide_controller.desktop_screen = MagicMock()
|
||||||
|
slide_controller.blank_screen = MagicMock()
|
||||||
|
slide_controller.theme_screen = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: __add_actions_to_widget is called
|
||||||
|
slide_controller._SlideController__add_actions_to_widget(mocked_widget)
|
||||||
|
|
||||||
|
# THEN: The call to addActions should be correct
|
||||||
|
mocked_widget.addActions.assert_called_with([
|
||||||
|
slide_controller.previous_item, slide_controller.next_item,
|
||||||
|
slide_controller.previous_service, slide_controller.next_service,
|
||||||
|
slide_controller.escape_item, slide_controller.desktop_screen,
|
||||||
|
slide_controller.theme_screen, slide_controller.blank_screen
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class TestInfoLabel(TestCase):
|
class TestInfoLabel(TestCase):
|
||||||
|
|
||||||
|
@ -48,6 +48,12 @@ class TestMediaItem(TestCase, TestMixin):
|
|||||||
with patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup'), \
|
with patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup'), \
|
||||||
patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'):
|
patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'):
|
||||||
self.media_item = SongMediaItem(None, MagicMock())
|
self.media_item = SongMediaItem(None, MagicMock())
|
||||||
|
self.media_item.save_auto_select_id = MagicMock()
|
||||||
|
self.media_item.list_view = MagicMock()
|
||||||
|
self.media_item.list_view.save_auto_select_id = MagicMock()
|
||||||
|
self.media_item.list_view.clear = MagicMock()
|
||||||
|
self.media_item.list_view.addItem = MagicMock()
|
||||||
|
self.media_item.auto_select_id = -1
|
||||||
self.media_item.display_songbook = False
|
self.media_item.display_songbook = False
|
||||||
self.media_item.display_copyright_symbol = False
|
self.media_item.display_copyright_symbol = False
|
||||||
self.setup_application()
|
self.setup_application()
|
||||||
@ -60,6 +66,151 @@ class TestMediaItem(TestCase, TestMixin):
|
|||||||
"""
|
"""
|
||||||
self.destroy_settings()
|
self.destroy_settings()
|
||||||
|
|
||||||
|
def display_results_song_test(self):
|
||||||
|
"""
|
||||||
|
Test displaying song search results with basic song
|
||||||
|
"""
|
||||||
|
# GIVEN: Search results, plus a mocked QtListWidgetItem
|
||||||
|
with patch('openlp.core.lib.QtWidgets.QListWidgetItem') as MockedQListWidgetItem, \
|
||||||
|
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
|
||||||
|
mock_search_results = []
|
||||||
|
mock_song = MagicMock()
|
||||||
|
mock_song.id = 1
|
||||||
|
mock_song.title = 'My Song'
|
||||||
|
mock_song.sort_key = 'My Song'
|
||||||
|
mock_song.authors = []
|
||||||
|
mock_author = MagicMock()
|
||||||
|
mock_author.display_name = 'My Author'
|
||||||
|
mock_song.authors.append(mock_author)
|
||||||
|
mock_song.temporary = False
|
||||||
|
mock_search_results.append(mock_song)
|
||||||
|
mock_qlist_widget = MagicMock()
|
||||||
|
MockedQListWidgetItem.return_value = mock_qlist_widget
|
||||||
|
|
||||||
|
# WHEN: I display song search results
|
||||||
|
self.media_item.display_results_song(mock_search_results)
|
||||||
|
|
||||||
|
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
|
||||||
|
self.media_item.list_view.clear.assert_called_with()
|
||||||
|
self.media_item.save_auto_select_id.assert_called_with()
|
||||||
|
MockedQListWidgetItem.assert_called_with('My Song (My Author)')
|
||||||
|
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
|
||||||
|
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
|
||||||
|
|
||||||
|
def display_results_author_test(self):
|
||||||
|
"""
|
||||||
|
Test displaying song search results grouped by author with basic song
|
||||||
|
"""
|
||||||
|
# GIVEN: Search results grouped by author, plus a mocked QtListWidgetItem
|
||||||
|
with patch('openlp.core.lib.QtWidgets.QListWidgetItem') as MockedQListWidgetItem, \
|
||||||
|
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
|
||||||
|
mock_search_results = []
|
||||||
|
mock_author = MagicMock()
|
||||||
|
mock_song = MagicMock()
|
||||||
|
mock_author.display_name = 'My Author'
|
||||||
|
mock_author.songs = []
|
||||||
|
mock_song.id = 1
|
||||||
|
mock_song.title = 'My Song'
|
||||||
|
mock_song.sort_key = 'My Song'
|
||||||
|
mock_song.temporary = False
|
||||||
|
mock_author.songs.append(mock_song)
|
||||||
|
mock_search_results.append(mock_author)
|
||||||
|
mock_qlist_widget = MagicMock()
|
||||||
|
MockedQListWidgetItem.return_value = mock_qlist_widget
|
||||||
|
|
||||||
|
# WHEN: I display song search results grouped by author
|
||||||
|
self.media_item.display_results_author(mock_search_results)
|
||||||
|
|
||||||
|
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
|
||||||
|
self.media_item.list_view.clear.assert_called_with()
|
||||||
|
MockedQListWidgetItem.assert_called_with('My Author (My Song)')
|
||||||
|
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
|
||||||
|
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
|
||||||
|
|
||||||
|
def display_results_topic_test(self):
|
||||||
|
"""
|
||||||
|
Test displaying song search results grouped by topic with basic song
|
||||||
|
"""
|
||||||
|
# GIVEN: Search results grouped by topic, plus a mocked QtListWidgetItem
|
||||||
|
with patch('openlp.core.lib.QtWidgets.QListWidgetItem') as MockedQListWidgetItem, \
|
||||||
|
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
|
||||||
|
mock_search_results = []
|
||||||
|
mock_topic = MagicMock()
|
||||||
|
mock_song = MagicMock()
|
||||||
|
mock_topic.name = 'My Topic'
|
||||||
|
mock_topic.songs = []
|
||||||
|
mock_song.id = 1
|
||||||
|
mock_song.title = 'My Song'
|
||||||
|
mock_song.sort_key = 'My Song'
|
||||||
|
mock_song.temporary = False
|
||||||
|
mock_topic.songs.append(mock_song)
|
||||||
|
mock_search_results.append(mock_topic)
|
||||||
|
mock_qlist_widget = MagicMock()
|
||||||
|
MockedQListWidgetItem.return_value = mock_qlist_widget
|
||||||
|
|
||||||
|
# WHEN: I display song search results grouped by topic
|
||||||
|
self.media_item.display_results_topic(mock_search_results)
|
||||||
|
|
||||||
|
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
|
||||||
|
self.media_item.list_view.clear.assert_called_with()
|
||||||
|
MockedQListWidgetItem.assert_called_with('My Topic (My Song)')
|
||||||
|
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
|
||||||
|
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
|
||||||
|
|
||||||
|
def display_results_themes_test(self):
|
||||||
|
"""
|
||||||
|
Test displaying song search results sorted by theme with basic song
|
||||||
|
"""
|
||||||
|
# GIVEN: Search results sorted by theme, plus a mocked QtListWidgetItem
|
||||||
|
with patch('openlp.core.lib.QtWidgets.QListWidgetItem') as MockedQListWidgetItem, \
|
||||||
|
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
|
||||||
|
mock_search_results = []
|
||||||
|
mock_song = MagicMock()
|
||||||
|
mock_song.id = 1
|
||||||
|
mock_song.title = 'My Song'
|
||||||
|
mock_song.sort_key = 'My Song'
|
||||||
|
mock_song.theme_name = 'My Theme'
|
||||||
|
mock_song.temporary = False
|
||||||
|
mock_search_results.append(mock_song)
|
||||||
|
mock_qlist_widget = MagicMock()
|
||||||
|
MockedQListWidgetItem.return_value = mock_qlist_widget
|
||||||
|
|
||||||
|
# WHEN: I display song search results sorted by theme
|
||||||
|
self.media_item.display_results_themes(mock_search_results)
|
||||||
|
|
||||||
|
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
|
||||||
|
self.media_item.list_view.clear.assert_called_with()
|
||||||
|
MockedQListWidgetItem.assert_called_with('My Theme (My Song)')
|
||||||
|
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
|
||||||
|
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
|
||||||
|
|
||||||
|
def display_results_cclinumber_test(self):
|
||||||
|
"""
|
||||||
|
Test displaying song search results sorted by CCLI number with basic song
|
||||||
|
"""
|
||||||
|
# GIVEN: Search results sorted by CCLI number, plus a mocked QtListWidgetItem
|
||||||
|
with patch('openlp.core.lib.QtWidgets.QListWidgetItem') as MockedQListWidgetItem, \
|
||||||
|
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
|
||||||
|
mock_search_results = []
|
||||||
|
mock_song = MagicMock()
|
||||||
|
mock_song.id = 1
|
||||||
|
mock_song.title = 'My Song'
|
||||||
|
mock_song.sort_key = 'My Song'
|
||||||
|
mock_song.ccli_number = '12345'
|
||||||
|
mock_song.temporary = False
|
||||||
|
mock_search_results.append(mock_song)
|
||||||
|
mock_qlist_widget = MagicMock()
|
||||||
|
MockedQListWidgetItem.return_value = mock_qlist_widget
|
||||||
|
|
||||||
|
# WHEN: I display song search results sorted by CCLI number
|
||||||
|
self.media_item.display_results_cclinumber(mock_search_results)
|
||||||
|
|
||||||
|
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
|
||||||
|
self.media_item.list_view.clear.assert_called_with()
|
||||||
|
MockedQListWidgetItem.assert_called_with('12345 (My Song)')
|
||||||
|
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
|
||||||
|
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
|
||||||
|
|
||||||
def build_song_footer_one_author_test(self):
|
def build_song_footer_one_author_test(self):
|
||||||
"""
|
"""
|
||||||
Test build songs footer with basic song and one author
|
Test build songs footer with basic song and one author
|
||||||
|
@ -81,3 +81,19 @@ class TestSongUsage(TestCase):
|
|||||||
|
|
||||||
# THEN: It should return True
|
# THEN: It should return True
|
||||||
self.assertTrue(ret)
|
self.assertTrue(ret)
|
||||||
|
|
||||||
|
@patch('openlp.plugins.songusage.songusageplugin.Manager')
|
||||||
|
def toggle_song_usage_state_test(self, MockedManager):
|
||||||
|
"""
|
||||||
|
Test that toggle_song_usage_state does toggle song_usage_state
|
||||||
|
"""
|
||||||
|
# GIVEN: A SongUsagePlugin
|
||||||
|
song_usage = SongUsagePlugin()
|
||||||
|
song_usage.set_button_state = MagicMock()
|
||||||
|
song_usage.song_usage_active = True
|
||||||
|
|
||||||
|
# WHEN: calling toggle_song_usage_state
|
||||||
|
song_usage.toggle_song_usage_state()
|
||||||
|
|
||||||
|
# THEN: song_usage_state should have been toogled
|
||||||
|
self.assertFalse(song_usage.song_usage_active)
|
||||||
|
Loading…
Reference in New Issue
Block a user