Fix bug #1423699 so that you're not logged out when you want to import more songs.

Fixes: https://launchpad.net/bugs/1423699
This commit is contained in:
Raoul Snyman 2015-02-28 00:28:24 +02:00
parent b65e592df9
commit 7d7fec194e
3 changed files with 575 additions and 341 deletions

View File

@ -40,3 +40,5 @@ __pycache__
# Rejected diff's
*.rej
*.~\?~
.coverage
cover

View File

@ -192,7 +192,7 @@ class SongSelectForm(QtGui.QDialog, Ui_SongSelectDialog):
self, translate('SongsPlugin.SongSelectForm', 'Incomplete song'),
translate('SongsPlugin.SongSelectForm', 'This song is missing some information, like the lyrics, '
'and cannot be imported.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok), QtGui.QMessageBox.OK)
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok), QtGui.QMessageBox.Ok)
self.stacked_widget.setCurrentIndex(1)
return
# Update the UI
@ -367,15 +367,11 @@ class SongSelectForm(QtGui.QDialog, Ui_SongSelectDialog):
Import a song from SongSelect.
"""
self.song_select_importer.save_song(self.song)
question_dialog = QtGui.QMessageBox()
question_dialog.setWindowTitle(translate('SongsPlugin.SongSelectForm', 'Song Imported'))
question_dialog.setText(translate('SongsPlugin.SongSelectForm', 'Your song has been imported, would you like '
'to exit now, or import more songs?'))
question_dialog.addButton(QtGui.QPushButton(translate('SongsPlugin.SongSelectForm', 'Import More Songs')),
QtGui.QMessageBox.YesRole)
question_dialog.addButton(QtGui.QPushButton(translate('SongsPlugin.SongSelectForm', 'Exit Now')),
QtGui.QMessageBox.NoRole)
if question_dialog.exec_() == QtGui.QMessageBox.Yes:
if QtGui.QMessageBox.question(self, translate('SongsPlugin.SongSelectForm', 'Song Imported'),
translate('SongsPlugin.SongSelectForm', 'Your song has been imported, would you '
'like to import more songs?'),
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes:
self.on_back_button_clicked()
else:
self.application.process_events()

View File

@ -23,29 +23,31 @@
This module contains tests for the CCLI SongSelect importer.
"""
import os
from unittest import TestCase
from urllib.error import URLError
from tests.functional import MagicMock, patch, call
from tests.helpers.testmixin import TestMixin
from PyQt4 import QtGui
from openlp.core import Registry
from openlp.plugins.songs.forms.songselectform import SongSelectForm
from openlp.plugins.songs.lib import Author, Song, VerseType
from openlp.plugins.songs.forms.songselectform import SongSelectForm, SearchWorker
from openlp.plugins.songs.lib import Song
from openlp.plugins.songs.lib.songselect import SongSelectImport, LOGOUT_URL, BASE_URL
from openlp.plugins.songs.lib.importers.cclifile import CCLIFileImport
from tests.functional import MagicMock, patch, call
from tests.helpers.testmixin import TestMixin
class TestSongSelectImport(TestCase, TestMixin):
"""
Test the :class:`~openlp.plugins.songs.lib.songselect.SongSelectImport` class
"""
def constructor_test(self):
@patch('openlp.plugins.songs.lib.songselect.build_opener')
def constructor_test(self, mocked_build_opener):
"""
Test that constructing a basic SongSelectImport object works correctly
"""
# GIVEN: The SongSelectImporter class and a mocked out build_opener
with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener:
# WHEN: An object is instantiated
importer = SongSelectImport(None)
@ -55,13 +57,13 @@ class TestSongSelectImport(TestCase, TestMixin):
self.assertIsNotNone(importer.opener, 'There should be a valid opener object')
self.assertEqual(1, mocked_build_opener.call_count, 'The build_opener method should have been called once')
def login_fails_test(self):
@patch('openlp.plugins.songs.lib.songselect.build_opener')
@patch('openlp.plugins.songs.lib.songselect.BeautifulSoup')
def login_fails_test(self, MockedBeautifulSoup, mocked_build_opener):
"""
Test that when logging in to SongSelect fails, the login method returns False
"""
# GIVEN: A bunch of mocked out stuff and an importer object
with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener, \
patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup:
mocked_opener = MagicMock()
mocked_build_opener.return_value = mocked_opener
mocked_login_page = MagicMock()
@ -79,13 +81,13 @@ class TestSongSelectImport(TestCase, TestMixin):
self.assertEqual(2, mocked_opener.open.call_count, 'opener should have been called twice')
self.assertFalse(result, 'The login method should have returned False')
def login_succeeds_test(self):
@patch('openlp.plugins.songs.lib.songselect.build_opener')
@patch('openlp.plugins.songs.lib.songselect.BeautifulSoup')
def login_succeeds_test(self, MockedBeautifulSoup, mocked_build_opener):
"""
Test that when logging in to SongSelect succeeds, the login method returns True
"""
# GIVEN: A bunch of mocked out stuff and an importer object
with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener, \
patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup:
mocked_opener = MagicMock()
mocked_build_opener.return_value = mocked_opener
mocked_login_page = MagicMock()
@ -103,12 +105,12 @@ class TestSongSelectImport(TestCase, TestMixin):
self.assertEqual(2, mocked_opener.open.call_count, 'opener should have been called twice')
self.assertTrue(result, 'The login method should have returned True')
def logout_test(self):
@patch('openlp.plugins.songs.lib.songselect.build_opener')
def logout_test(self, mocked_build_opener):
"""
Test that when the logout method is called, it logs the user out of SongSelect
"""
# GIVEN: A bunch of mocked out stuff and an importer object
with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener:
mocked_opener = MagicMock()
mocked_build_opener.return_value = mocked_opener
importer = SongSelectImport(None)
@ -120,13 +122,13 @@ class TestSongSelectImport(TestCase, TestMixin):
self.assertEqual(1, mocked_opener.open.call_count, 'opener should have been called once')
mocked_opener.open.assert_called_with(LOGOUT_URL)
def search_returns_no_results_test(self):
@patch('openlp.plugins.songs.lib.songselect.build_opener')
@patch('openlp.plugins.songs.lib.songselect.BeautifulSoup')
def search_returns_no_results_test(self, MockedBeautifulSoup, mocked_build_opener):
"""
Test that when the search finds no results, it simply returns an empty list
"""
# GIVEN: A bunch of mocked out stuff and an importer object
with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener, \
patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup:
mocked_opener = MagicMock()
mocked_build_opener.return_value = mocked_opener
mocked_results_page = MagicMock()
@ -145,13 +147,13 @@ class TestSongSelectImport(TestCase, TestMixin):
mocked_results_page.find_all.assert_called_with('li', 'result pane')
self.assertEqual([], results, 'The search method should have returned an empty list')
def search_returns_two_results_test(self):
@patch('openlp.plugins.songs.lib.songselect.build_opener')
@patch('openlp.plugins.songs.lib.songselect.BeautifulSoup')
def search_returns_two_results_test(self, MockedBeautifulSoup, mocked_build_opener):
"""
Test that when the search finds 2 results, it simply returns a list with 2 results
"""
# GIVEN: A bunch of mocked out stuff and an importer object
with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener, \
patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup:
# first search result
mocked_result1 = MagicMock()
mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}]
@ -183,13 +185,13 @@ class TestSongSelectImport(TestCase, TestMixin):
]
self.assertListEqual(expected_list, results, 'The search method should have returned two songs')
def search_reaches_max_results_test(self):
@patch('openlp.plugins.songs.lib.songselect.build_opener')
@patch('openlp.plugins.songs.lib.songselect.BeautifulSoup')
def search_reaches_max_results_test(self, MockedBeautifulSoup, mocked_build_opener):
"""
Test that when the search finds MAX (2) results, it simply returns a list with those (2)
"""
# GIVEN: A bunch of mocked out stuff and an importer object
with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener, \
patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup:
# first search result
mocked_result1 = MagicMock()
mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}]
@ -223,12 +225,12 @@ class TestSongSelectImport(TestCase, TestMixin):
{'title': 'Title 2', 'authors': ['Author 2-1', 'Author 2-2'], 'link': BASE_URL + '/url2'}]
self.assertListEqual(expected_list, results, 'The search method should have returned two songs')
def get_song_page_raises_exception_test(self):
@patch('openlp.plugins.songs.lib.songselect.build_opener')
def get_song_page_raises_exception_test(self, mocked_build_opener):
"""
Test that when BeautifulSoup gets a bad song page the get_song() method returns None
"""
# GIVEN: A bunch of mocked out stuff and an importer object
with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener:
mocked_opener = MagicMock()
mocked_build_opener.return_value = mocked_opener
mocked_opener.open.read.side_effect = URLError('[Errno -2] Name or service not known')
@ -242,13 +244,13 @@ class TestSongSelectImport(TestCase, TestMixin):
mocked_callback.assert_called_with()
self.assertIsNone(result, 'The get_song() method should have returned None')
def get_song_lyrics_raise_exception_test(self):
@patch('openlp.plugins.songs.lib.songselect.build_opener')
@patch('openlp.plugins.songs.lib.songselect.BeautifulSoup')
def get_song_lyrics_raise_exception_test(self, MockedBeautifulSoup, mocked_build_opener):
"""
Test that when BeautifulSoup gets a bad lyrics page the get_song() method returns None
"""
# GIVEN: A bunch of mocked out stuff and an importer object
with patch('openlp.plugins.songs.lib.songselect.build_opener'), \
patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup:
MockedBeautifulSoup.side_effect = [None, TypeError('Test Error')]
mocked_callback = MagicMock()
importer = SongSelectImport(None)
@ -260,13 +262,13 @@ class TestSongSelectImport(TestCase, TestMixin):
self.assertEqual(2, mocked_callback.call_count, 'The callback should have been called twice')
self.assertIsNone(result, 'The get_song() method should have returned None')
def get_song_test(self):
@patch('openlp.plugins.songs.lib.songselect.build_opener')
@patch('openlp.plugins.songs.lib.songselect.BeautifulSoup')
def get_song_test(self, MockedBeautifulSoup, mocked_build_opener):
"""
Test that the get_song() method returns the correct song details
"""
# GIVEN: A bunch of mocked out stuff and an importer object
with patch('openlp.plugins.songs.lib.songselect.build_opener'), \
patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup:
mocked_song_page = MagicMock()
mocked_copyright = MagicMock()
mocked_copyright.find_all.return_value = [MagicMock(string='Copyright 1'), MagicMock(string='Copyright 2')]
@ -308,13 +310,13 @@ class TestSongSelectImport(TestCase, TestMixin):
self.assertIn('verses', result, 'The returned song should have verses')
self.assertEqual(3, len(result['verses']), 'Three verses should have been returned')
def save_song_new_author_test(self):
@patch('openlp.plugins.songs.lib.songselect.clean_song')
@patch('openlp.plugins.songs.lib.songselect.Author')
def save_song_new_author_test(self, MockedAuthor, mocked_clean_song):
"""
Test that saving a song with a new author performs the correct actions
"""
# GIVEN: A song to save, and some mocked out objects
with patch('openlp.plugins.songs.lib.songselect.clean_song') as mocked_clean_song, \
patch('openlp.plugins.songs.lib.songselect.Author') as MockedAuthor:
song_dict = {
'title': 'Arky Arky',
'authors': ['Public Domain'],
@ -344,13 +346,13 @@ class TestSongSelectImport(TestCase, TestMixin):
display_name='Public Domain')
self.assertEqual(1, len(result.authors_songs), 'There should only be one author')
def save_song_existing_author_test(self):
@patch('openlp.plugins.songs.lib.songselect.clean_song')
@patch('openlp.plugins.songs.lib.songselect.Author')
def save_song_existing_author_test(self, MockedAuthor, mocked_clean_song):
"""
Test that saving a song with an existing author performs the correct actions
"""
# GIVEN: A song to save, and some mocked out objects
with patch('openlp.plugins.songs.lib.songselect.clean_song') as mocked_clean_song, \
patch('openlp.plugins.songs.lib.songselect.Author') as MockedAuthor:
song_dict = {
'title': 'Arky Arky',
'authors': ['Public Domain'],
@ -409,14 +411,14 @@ class TestSongSelectForm(TestCase, TestMixin):
self.assertEqual(mocked_plugin, ssform.plugin, 'The correct plugin should have been assigned')
self.assertEqual(mocked_db_manager, ssform.db_manager, 'The correct db_manager should have been assigned')
def login_fails_test(self):
@patch('openlp.plugins.songs.forms.songselectform.SongSelectImport')
@patch('openlp.plugins.songs.forms.songselectform.QtGui.QMessageBox.critical')
@patch('openlp.plugins.songs.forms.songselectform.translate')
def login_fails_test(self, mocked_translate, mocked_critical, MockedSongSelectImport):
"""
Test that when the login fails, the form returns to the correct state
"""
# GIVEN: A valid SongSelectForm with a mocked out SongSelectImport, and a bunch of mocked out controls
with patch('openlp.plugins.songs.forms.songselectform.SongSelectImport') as MockedSongSelectImport, \
patch('openlp.plugins.songs.forms.songselectform.QtGui.QMessageBox.critical') as mocked_critical, \
patch('openlp.plugins.songs.forms.songselectform.translate') as mocked_translate:
mocked_song_select_import = MagicMock()
mocked_song_select_import.login.return_value = False
MockedSongSelectImport.return_value = mocked_song_select_import
@ -463,6 +465,166 @@ class TestSongSelectForm(TestCase, TestMixin):
'perhaps your username or password is '
'incorrect?')
@patch('openlp.plugins.songs.forms.songselectform.QtGui.QMessageBox.question')
@patch('openlp.plugins.songs.forms.songselectform.translate')
def on_import_yes_clicked_test(self, mocked_translate, mocked_question):
"""
Test that when a song is imported and the user clicks the "yes" button, the UI goes back to the previous page
"""
# GIVEN: A valid SongSelectForm with a mocked out QMessageBox.question() method
mocked_translate.side_effect = lambda *args: args[1]
mocked_question.return_value = QtGui.QMessageBox.Yes
ssform = SongSelectForm(None, MagicMock(), MagicMock())
mocked_song_select_importer = MagicMock()
ssform.song_select_importer = mocked_song_select_importer
ssform.song = None
# WHEN: The import button is clicked, and the user clicks Yes
with patch.object(ssform, 'on_back_button_clicked') as mocked_on_back_button_clicked:
ssform.on_import_button_clicked()
# THEN: The on_back_button_clicked() method should have been called
mocked_song_select_importer.save_song.assert_called_with(None)
mocked_question.assert_called_with(ssform, 'Song Imported',
'Your song has been imported, would you like to import more songs?',
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.Yes)
mocked_on_back_button_clicked.assert_called_with()
@patch('openlp.plugins.songs.forms.songselectform.QtGui.QMessageBox.question')
@patch('openlp.plugins.songs.forms.songselectform.translate')
def on_import_no_clicked_test(self, mocked_translate, mocked_question):
"""
Test that when a song is imported and the user clicks the "no" button, the UI exits
"""
# GIVEN: A valid SongSelectForm with a mocked out QMessageBox.question() method
mocked_translate.side_effect = lambda *args: args[1]
mocked_question.return_value = QtGui.QMessageBox.No
ssform = SongSelectForm(None, MagicMock(), MagicMock())
mocked_song_select_importer = MagicMock()
ssform.song_select_importer = mocked_song_select_importer
ssform.song = None
# WHEN: The import button is clicked, and the user clicks Yes
with patch.object(ssform, 'done') as mocked_done:
ssform.on_import_button_clicked()
# THEN: The on_back_button_clicked() method should have been called
mocked_song_select_importer.save_song.assert_called_with(None)
mocked_question.assert_called_with(ssform, 'Song Imported',
'Your song has been imported, would you like to import more songs?',
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.Yes)
mocked_done.assert_called_with(QtGui.QDialog.Accepted)
def on_back_button_clicked_test(self):
"""
Test that when the back button is clicked, the stacked widget is set back one page
"""
# GIVEN: A SongSelect form
ssform = SongSelectForm(None, MagicMock(), MagicMock())
# WHEN: The back button is clicked
with patch.object(ssform, 'stacked_widget') as mocked_stacked_widget, \
patch.object(ssform, 'search_combobox') as mocked_search_combobox:
ssform.on_back_button_clicked()
# THEN: The stacked widget should be set back one page
mocked_stacked_widget.setCurrentIndex.assert_called_with(1)
mocked_search_combobox.setFocus.assert_called_with()
@patch('openlp.plugins.songs.forms.songselectform.QtGui.QMessageBox.information')
def on_search_show_info_test(self, mocked_information):
"""
Test that when the search_show_info signal is emitted, the on_search_show_info() method shows a dialog
"""
# GIVEN: A SongSelect form
ssform = SongSelectForm(None, MagicMock(), MagicMock())
expected_title = 'Test Title'
expected_text = 'This is a test'
# WHEN: on_search_show_info is called
ssform.on_search_show_info(expected_title, expected_text)
# THEN: An information dialog should be shown
mocked_information.assert_called_with(ssform, expected_title, expected_text)
def update_login_progress_test(self):
"""
Test the _update_login_progress() method
"""
# GIVEN: A SongSelect form
ssform = SongSelectForm(None, MagicMock(), MagicMock())
# WHEN: _update_login_progress() is called
with patch.object(ssform, 'login_progress_bar') as mocked_login_progress_bar:
mocked_login_progress_bar.value.return_value = 3
ssform._update_login_progress()
# THEN: The login progress bar should be updated
mocked_login_progress_bar.setValue.assert_called_with(4)
def update_song_progress_test(self):
"""
Test the _update_song_progress() method
"""
# GIVEN: A SongSelect form
ssform = SongSelectForm(None, MagicMock(), MagicMock())
# WHEN: _update_song_progress() is called
with patch.object(ssform, 'song_progress_bar') as mocked_song_progress_bar:
mocked_song_progress_bar.value.return_value = 2
ssform._update_song_progress()
# THEN: The song progress bar should be updated
mocked_song_progress_bar.setValue.assert_called_with(3)
def on_search_results_widget_double_clicked_test(self):
"""
Test that a song is retrieved when a song in the results list is double-clicked
"""
# GIVEN: A SongSelect form
ssform = SongSelectForm(None, MagicMock(), MagicMock())
expected_song = {'title': 'Amazing Grace'}
# WHEN: A song result is double-clicked
with patch.object(ssform, '_view_song') as mocked_view_song:
ssform.on_search_results_widget_double_clicked(expected_song)
# THEN: The song is fetched and shown to the user
mocked_view_song.assert_called_with(expected_song)
def on_view_button_clicked_test(self):
"""
Test that a song is retrieved when the view button is clicked
"""
# GIVEN: A SongSelect form
ssform = SongSelectForm(None, MagicMock(), MagicMock())
expected_song = {'title': 'Amazing Grace'}
# WHEN: A song result is double-clicked
with patch.object(ssform, '_view_song') as mocked_view_song, \
patch.object(ssform, 'search_results_widget') as mocked_search_results_widget:
mocked_search_results_widget.currentItem.return_value = expected_song
ssform.on_view_button_clicked()
# THEN: The song is fetched and shown to the user
mocked_view_song.assert_called_with(expected_song)
def on_search_results_widget_selection_changed_test(self):
"""
Test that the view button is updated when the search results list is changed
"""
# GIVEN: A SongSelect form
ssform = SongSelectForm(None, MagicMock(), MagicMock())
# WHEN: There is at least 1 item selected
with patch.object(ssform, 'search_results_widget') as mocked_search_results_widget, \
patch.object(ssform, 'view_button') as mocked_view_button:
mocked_search_results_widget.selectedItems.return_value = [1]
ssform.on_search_results_widget_selection_changed()
# THEN: The view button should be enabled
mocked_view_button.setEnabled.assert_called_with(True)
class TestSongSelectFileImport(TestCase, TestMixin):
"""
@ -481,53 +643,45 @@ class TestSongSelectFileImport(TestCase, TestMixin):
self.authors = ['Author One', 'Author Two']
self.topics = ['Adoration', 'Praise']
def tearDown(self):
def songselect_import_bin_file_test(self):
"""
Test cleanups
:return:
"""
pass
def songselect_import_usr_file_test(self):
"""
Verify import SongSelect USR file parses file properly
Verify import SongSelect BIN file parses file properly
:return:
"""
# GIVEN: Text file to import and mocks
copyright = '2011 OpenLP Programmer One (Admin. by OpenLP One) | ' \
copyright_bin = '2011 OpenLP Programmer One (Admin. by OpenLP One) | ' \
'Openlp Programmer Two (Admin. by OpenLP Two)'
verses = [
verses_bin = [
['v1', 'Line One Verse One\nLine Two Verse One\nLine Three Verse One\nLine Four Verse One', None],
['v2', 'Line One Verse Two\nLine Two Verse Two\nLine Three Verse Two\nLine Four Verse Two', None]
]
song_import = CCLIFileImport(manager=None, filename=['{}.bin'.format(self.file_name)])
song_import = CCLIFileImport(manager=None, filename=['{}.bin'.format(self.file_name)], )
with patch.object(song_import, 'import_wizard'), patch.object(song_import, 'finish'):
# WHEN: We call the song importer
song_import.do_import()
# THEN: Song values should be equal to test values in setUp
self.assertEquals(song_import.title, self.title, 'Song title should match')
self.assertEquals(song_import.ccli_number, self.ccli_number, 'CCLI Song Number should match')
self.assertEquals(song_import.authors, self.authors, 'Author(s) should match')
self.assertEquals(song_import.copyright, self.copyright_usr, 'Copyright should match')
self.assertEquals(song_import.copyright, copyright_bin, 'Copyright should match')
self.assertEquals(song_import.topics, self.topics, 'Theme(s) should match')
self.assertEquals(song_import.verses, self.verses, 'Verses should match with test verses')
self.assertEquals(song_import.verses, verses_bin, 'Verses should match with test verses')
def songselect_import_usr_file_test(self):
def songselect_import_txt_file_test(self):
"""
Verify import SongSelect USR file parses file properly
Verify import SongSelect TXT file parses file properly
:return:
"""
# GIVEN: Text file to import and mocks
copyright = '© 2011 OpenLP Programmer One (Admin. by OpenLP One)'
verses = [
copyright_txt = '© 2011 OpenLP Programmer One (Admin. by OpenLP One)'
verses_txt = [
['v1', 'Line One Verse One\r\nLine Two Verse One\r\nLine Three Verse One\r\nLine Four Verse One', None],
['v2', 'Line One Verse Two\r\nLine Two Verse Two\r\nLine Three Verse Two\r\nLine Four Verse Two', None]
]
song_import = CCLIFileImport(manager=None, filename=['{}.txt'.format(self.file_name)], )
with patch.object(song_import, 'import_wizard'), patch.object(song_import, 'finish'):
song_import = CCLIFileImport(manager=None, filename=['{}.txt'.format(self.file_name)])
with patch.object(song_import, 'import_wizard'), patch.object(song_import, 'finish'):
# WHEN: We call the song importer
song_import.do_import()
@ -535,5 +689,87 @@ class TestSongSelectFileImport(TestCase, TestMixin):
self.assertEquals(song_import.title, self.title, 'Song title should match')
self.assertEquals(song_import.ccli_number, self.ccli_number, 'CCLI Song Number should match')
self.assertEquals(song_import.authors, self.authors, 'Author(s) should match')
self.assertEquals(song_import.copyright, copyright, 'Copyright should match')
self.assertEquals(song_import.verses, verses, 'Verses should match with test verses')
self.assertEquals(song_import.copyright, copyright_txt, 'Copyright should match')
self.assertEquals(song_import.verses, verses_txt, 'Verses should match with test verses')
class TestSearchWorker(TestCase, TestMixin):
"""
Test the SearchWorker class
"""
def constructor_test(self):
"""
Test the SearchWorker constructor
"""
# GIVEN: An importer mock object and some search text
importer = MagicMock()
search_text = 'Jesus'
# WHEN: The search worker is created
worker = SearchWorker(importer, search_text)
# THEN: The correct values should be set
self.assertIs(importer, worker.importer, 'The importer should be the right object')
self.assertEqual(search_text, worker.search_text, 'The search text should be correct')
def start_test(self):
"""
Test the start() method of the SearchWorker class
"""
# GIVEN: An importer mock object, some search text and an initialised SearchWorker
importer = MagicMock()
importer.search.return_value = ['song1', 'song2']
search_text = 'Jesus'
worker = SearchWorker(importer, search_text)
# WHEN: The start() method is called
with patch.object(worker, 'finished') as mocked_finished, patch.object(worker, 'quit') as mocked_quit:
worker.start()
# THEN: The "finished" and "quit" signals should be emitted
importer.search.assert_called_with(search_text, 1000, worker._found_song_callback)
mocked_finished.emit.assert_called_with()
mocked_quit.emit.assert_called_with()
@patch('openlp.plugins.songs.forms.songselectform.translate')
def start_over_1000_songs_test(self, mocked_translate):
"""
Test the start() method of the SearchWorker class when it finds over 1000 songs
"""
# GIVEN: An importer mock object, some search text and an initialised SearchWorker
mocked_translate.side_effect = lambda x, y: y
importer = MagicMock()
importer.search.return_value = ['song%s' % num for num in range(1050)]
search_text = 'Jesus'
worker = SearchWorker(importer, search_text)
# WHEN: The start() method is called
with patch.object(worker, 'finished') as mocked_finished, patch.object(worker, 'quit') as mocked_quit, \
patch.object(worker, 'show_info') as mocked_show_info:
worker.start()
# THEN: The "finished" and "quit" signals should be emitted
importer.search.assert_called_with(search_text, 1000, worker._found_song_callback)
mocked_show_info.emit.assert_called_with('More than 1000 results', 'Your search has returned more than 1000 '
'results, it has been stopped. Please '
'refine your search to fetch better '
'results.')
mocked_finished.emit.assert_called_with()
mocked_quit.emit.assert_called_with()
def found_song_callback_test(self):
"""
Test that when the _found_song_callback() function is called, the "found_song" signal is emitted
"""
# GIVEN: An importer mock object, some search text and an initialised SearchWorker
importer = MagicMock()
search_text = 'Jesus'
song = {'title': 'Amazing Grace'}
worker = SearchWorker(importer, search_text)
# WHEN: The start() method is called
with patch.object(worker, 'found_song') as mocked_found_song:
worker._found_song_callback(song)
# THEN: The "found_song" signal should have been emitted
mocked_found_song.emit.assert_called_with(song)