Merge lp:~raoul-snyman/openlp/bug-1608194-2.4 into lp:openlp/2.4

Proposed by Raoul Snyman on 2016-08-12
Status: Merged
Merged at revision: 2648
Proposed branch: lp:~raoul-snyman/openlp/bug-1608194-2.4
Merge into: lp:openlp/2.4
Diff against target: 394 lines (+115/-62)
4 files modified
openlp/plugins/songs/lib/__init__.py (+2/-3)
openlp/plugins/songs/lib/db.py (+2/-2)
openlp/plugins/songs/lib/songselect.py (+72/-36)
tests/functional/openlp_plugins/songs/test_songselect.py (+39/-21)
To merge this branch: bzr merge lp:~raoul-snyman/openlp/bug-1608194-2.4
Reviewer Review Type Date Requested Status
Tim Bentley 2016-08-12 Approve on 2016-08-12
Review via email: mp+302797@code.launchpad.net

This proposal supersedes a proposal from 2016-08-11.

To post a comment you must log in.
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Update tests count.

You wrote the rules, I am just imposing them!

Tim Bentley (trb143) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp/plugins/songs/lib/__init__.py'
--- openlp/plugins/songs/lib/__init__.py 2016-04-21 19:49:22 +0000
+++ openlp/plugins/songs/lib/__init__.py 2016-08-12 12:20:33 +0000
@@ -32,9 +32,8 @@
32from openlp.core.common import AppLocation32from openlp.core.common import AppLocation
33from openlp.core.lib import translate33from openlp.core.lib import translate
34from openlp.core.utils import CONTROL_CHARS34from openlp.core.utils import CONTROL_CHARS
35from openlp.plugins.songs.lib.db import MediaFile, Song35from openlp.plugins.songs.lib.db import Author, MediaFile, Song, Topic
36from .db import Author36from openlp.plugins.songs.lib.ui import SongStrings
37from .ui import SongStrings
3837
39log = logging.getLogger(__name__)38log = logging.getLogger(__name__)
4039
4140
=== modified file 'openlp/plugins/songs/lib/db.py'
--- openlp/plugins/songs/lib/db.py 2016-04-27 18:45:39 +0000
+++ openlp/plugins/songs/lib/db.py 2016-08-12 12:20:33 +0000
@@ -135,7 +135,7 @@
135135
136 def add_author(self, author, author_type=None):136 def add_author(self, author, author_type=None):
137 """137 """
138 Add an author to the song if it not yet exists138 Add an author to the song if it doesn't exist yet
139139
140 :param author: Author object140 :param author: Author object
141 :param author_type: AuthorType constant or None141 :param author_type: AuthorType constant or None
@@ -162,7 +162,7 @@
162162
163 def add_songbook_entry(self, songbook, entry):163 def add_songbook_entry(self, songbook, entry):
164 """164 """
165 Add a Songbook Entry to the song if it not yet exists165 Add a Songbook Entry to the song if it doesn't exist yet
166166
167 :param songbook_name: Name of the Songbook.167 :param songbook_name: Name of the Songbook.
168 :param entry: Entry in the Songbook (usually a number)168 :param entry: Entry in the Songbook (usually a number)
169169
=== modified file 'openlp/plugins/songs/lib/songselect.py'
--- openlp/plugins/songs/lib/songselect.py 2016-01-09 18:01:49 +0000
+++ openlp/plugins/songs/lib/songselect.py 2016-08-12 12:20:33 +0000
@@ -24,6 +24,8 @@
24"""24"""
25import logging25import logging
26import sys26import sys
27import random
28import re
27from http.cookiejar import CookieJar29from http.cookiejar import CookieJar
28from urllib.parse import urlencode30from urllib.parse import urlencode
29from urllib.request import HTTPCookieProcessor, URLError, build_opener31from urllib.request import HTTPCookieProcessor, URLError, build_opener
@@ -32,14 +34,21 @@
3234
33from bs4 import BeautifulSoup, NavigableString35from bs4 import BeautifulSoup, NavigableString
3436
35from openlp.plugins.songs.lib import Song, VerseType, clean_song, Author37from openlp.plugins.songs.lib import Song, Author, Topic, VerseType, clean_song
36from openlp.plugins.songs.lib.openlyricsxml import SongXML38from openlp.plugins.songs.lib.openlyricsxml import SongXML
3739
38USER_AGENT = 'Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; GT-I9000 ' \40USER_AGENTS = [
39 'Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 ' \41 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) '
40 'Mobile Safari/534.30'42 'Chrome/52.0.2743.116 Safari/537.36',
41BASE_URL = 'https://mobile.songselect.com'43 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',
42LOGIN_URL = BASE_URL + '/account/login'44 'Mozilla/5.0 (X11; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0',
45 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:46.0) Gecko/20100101 Firefox/46.0',
46 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:47.0) Gecko/20100101 Firefox/47.0'
47]
48BASE_URL = 'https://songselect.ccli.com'
49LOGIN_PAGE = 'https://profile.ccli.com/account/signin?appContext=SongSelect&returnUrl=' \
50 'https%3a%2f%2fsongselect.ccli.com%2f'
51LOGIN_URL = 'https://profile.ccli.com/'
43LOGOUT_URL = BASE_URL + '/account/logout'52LOGOUT_URL = BASE_URL + '/account/logout'
44SEARCH_URL = BASE_URL + '/search/results'53SEARCH_URL = BASE_URL + '/search/results'
4554
@@ -60,7 +69,7 @@
60 self.db_manager = db_manager69 self.db_manager = db_manager
61 self.html_parser = HTMLParser()70 self.html_parser = HTMLParser()
62 self.opener = build_opener(HTTPCookieProcessor(CookieJar()))71 self.opener = build_opener(HTTPCookieProcessor(CookieJar()))
63 self.opener.addheaders = [('User-Agent', USER_AGENT)]72 self.opener.addheaders = [('User-Agent', random.choice(USER_AGENTS))]
64 self.run_search = True73 self.run_search = True
6574
66 def login(self, username, password, callback=None):75 def login(self, username, password, callback=None):
@@ -76,27 +85,27 @@
76 if callback:85 if callback:
77 callback()86 callback()
78 try:87 try:
79 login_page = BeautifulSoup(self.opener.open(LOGIN_URL).read(), 'lxml')88 login_page = BeautifulSoup(self.opener.open(LOGIN_PAGE).read(), 'lxml')
80 except (TypeError, URLError) as e:89 except (TypeError, URLError) as error:
81 log.exception('Could not login to SongSelect, %s', e)90 log.exception('Could not login to SongSelect, %s', error)
82 return False91 return False
83 if callback:92 if callback:
84 callback()93 callback()
85 token_input = login_page.find('input', attrs={'name': '__RequestVerificationToken'})94 token_input = login_page.find('input', attrs={'name': '__RequestVerificationToken'})
86 data = urlencode({95 data = urlencode({
87 '__RequestVerificationToken': token_input['value'],96 '__RequestVerificationToken': token_input['value'],
88 'UserName': username,97 'emailAddress': username,
89 'Password': password,98 'password': password,
90 'RememberMe': 'false'99 'RememberMe': 'false'
91 })100 })
92 try:101 try:
93 posted_page = BeautifulSoup(self.opener.open(LOGIN_URL, data.encode('utf-8')).read(), 'lxml')102 posted_page = BeautifulSoup(self.opener.open(LOGIN_URL, data.encode('utf-8')).read(), 'lxml')
94 except (TypeError, URLError) as e:103 except (TypeError, URLError) as error:
95 log.exception('Could not login to SongSelect, %s', e)104 log.exception('Could not login to SongSelect, %s', error)
96 return False105 return False
97 if callback:106 if callback:
98 callback()107 callback()
99 return not posted_page.find('input', attrs={'name': '__RequestVerificationToken'})108 return posted_page.find('input', id='SearchText') is not None
100109
101 def logout(self):110 def logout(self):
102 """111 """
@@ -104,8 +113,8 @@
104 """113 """
105 try:114 try:
106 self.opener.open(LOGOUT_URL)115 self.opener.open(LOGOUT_URL)
107 except (TypeError, URLError) as e:116 except (TypeError, URLError) as error:
108 log.exception('Could not log of SongSelect, %s', e)117 log.exception('Could not log of SongSelect, %s', error)
109118
110 def search(self, search_text, max_results, callback=None):119 def search(self, search_text, max_results, callback=None):
111 """120 """
@@ -117,7 +126,15 @@
117 :return: List of songs126 :return: List of songs
118 """127 """
119 self.run_search = True128 self.run_search = True
120 params = {'allowredirect': 'false', 'SearchTerm': search_text}129 params = {
130 'SongContent': '',
131 'PrimaryLanguage': '',
132 'Keys': '',
133 'Themes': '',
134 'List': '',
135 'Sort': '',
136 'SearchText': search_text
137 }
121 current_page = 1138 current_page = 1
122 songs = []139 songs = []
123 while self.run_search:140 while self.run_search:
@@ -125,7 +142,7 @@
125 params['page'] = current_page142 params['page'] = current_page
126 try:143 try:
127 results_page = BeautifulSoup(self.opener.open(SEARCH_URL + '?' + urlencode(params)).read(), 'lxml')144 results_page = BeautifulSoup(self.opener.open(SEARCH_URL + '?' + urlencode(params)).read(), 'lxml')
128 search_results = results_page.find_all('li', 'result pane')145 search_results = results_page.find_all('div', 'song-result')
129 except (TypeError, URLError) as e:146 except (TypeError, URLError) as e:
130 log.exception('Could not search SongSelect, %s', e)147 log.exception('Could not search SongSelect, %s', e)
131 search_results = None148 search_results = None
@@ -133,9 +150,9 @@
133 break150 break
134 for result in search_results:151 for result in search_results:
135 song = {152 song = {
136 'title': unescape(result.find('h3').string),153 'title': unescape(result.find('p', 'song-result-title').find('a').string).strip(),
137 'authors': [unescape(author.string) for author in result.find_all('li')],154 'authors': unescape(result.find('p', 'song-result-subtitle').string).strip().split(', '),
138 'link': BASE_URL + result.find('a')['href']155 'link': BASE_URL + result.find('p', 'song-result-title').find('a')['href']
139 }156 }
140 if callback:157 if callback:
141 callback(song)158 callback(song)
@@ -163,27 +180,37 @@
163 if callback:180 if callback:
164 callback()181 callback()
165 try:182 try:
166 lyrics_page = BeautifulSoup(self.opener.open(song['link'] + '/lyrics').read(), 'lxml')183 lyrics_page = BeautifulSoup(self.opener.open(song['link'] + '/viewlyrics').read(), 'lxml')
167 except (TypeError, URLError):184 except (TypeError, URLError):
168 log.exception('Could not get lyrics from SongSelect')185 log.exception('Could not get lyrics from SongSelect')
169 return None186 return None
170 if callback:187 if callback:
171 callback()188 callback()
172 song['copyright'] = '/'.join([li.string for li in song_page.find('ul', 'copyright').find_all('li')])189 copyright_elements = []
173 song['copyright'] = unescape(song['copyright'])190 theme_elements = []
174 song['ccli_number'] = song_page.find('ul', 'info').find('li').string.split(':')[1].strip()191 copyrights_regex = re.compile(r'\bCopyrights\b')
192 themes_regex = re.compile(r'\bThemes\b')
193 for ul in song_page.find_all('ul', 'song-meta-list'):
194 if ul.find('li', string=copyrights_regex):
195 copyright_elements.extend(ul.find_all('li')[1:])
196 if ul.find('li', string=themes_regex):
197 theme_elements.extend(ul.find_all('li')[1:])
198 song['copyright'] = '/'.join([unescape(li.string).strip() for li in copyright_elements])
199 song['topics'] = [unescape(li.string).strip() for li in theme_elements]
200 song['ccli_number'] = song_page.find('div', 'song-content-data').find('ul').find('li')\
201 .find('strong').string.strip()
175 song['verses'] = []202 song['verses'] = []
176 verses = lyrics_page.find('section', 'lyrics').find_all('p')203 verses = lyrics_page.find('div', 'song-viewer lyrics').find_all('p')
177 verse_labels = lyrics_page.find('section', 'lyrics').find_all('h3')204 verse_labels = lyrics_page.find('div', 'song-viewer lyrics').find_all('h3')
178 for counter in range(len(verses)):205 for verse, label in zip(verses, verse_labels):
179 verse = {'label': verse_labels[counter].string, 'lyrics': ''}206 song_verse = {'label': unescape(label.string).strip(), 'lyrics': ''}
180 for v in verses[counter].contents:207 for v in verse.contents:
181 if isinstance(v, NavigableString):208 if isinstance(v, NavigableString):
182 verse['lyrics'] = verse['lyrics'] + v.string209 song_verse['lyrics'] += unescape(v.string).strip()
183 else:210 else:
184 verse['lyrics'] += '\n'211 song_verse['lyrics'] += '\n'
185 verse['lyrics'] = verse['lyrics'].strip(' \n\r\t')212 song_verse['lyrics'] = song_verse['lyrics'].strip()
186 song['verses'].append(unescape(verse))213 song['verses'].append(song_verse)
187 for counter, author in enumerate(song['authors']):214 for counter, author in enumerate(song['authors']):
188 song['authors'][counter] = unescape(author)215 song['authors'][counter] = unescape(author)
189 return song216 return song
@@ -199,7 +226,11 @@
199 song_xml = SongXML()226 song_xml = SongXML()
200 verse_order = []227 verse_order = []
201 for verse in song['verses']:228 for verse in song['verses']:
202 verse_type, verse_number = verse['label'].split(' ')[:2]229 if ' ' in verse['label']:
230 verse_type, verse_number = verse['label'].split(' ', 1)
231 else:
232 verse_type = verse['label']
233 verse_number = 1
203 verse_type = VerseType.from_loose_input(verse_type)234 verse_type = VerseType.from_loose_input(verse_type)
204 verse_number = int(verse_number)235 verse_number = int(verse_number)
205 song_xml.add_verse_to_lyrics(VerseType.tags[verse_type], verse_number, verse['lyrics'])236 song_xml.add_verse_to_lyrics(VerseType.tags[verse_type], verse_number, verse['lyrics'])
@@ -220,6 +251,11 @@
220 last_name = name_parts[1]251 last_name = name_parts[1]
221 author = Author.populate(first_name=first_name, last_name=last_name, display_name=author_name)252 author = Author.populate(first_name=first_name, last_name=last_name, display_name=author_name)
222 db_song.add_author(author)253 db_song.add_author(author)
254 for topic_name in song.get('topics', []):
255 topic = self.db_manager.get_object_filtered(Topic, Topic.name == topic_name)
256 if not topic:
257 topic = Topic.populate(name=topic_name)
258 db_song.add_topic(topic)
223 self.db_manager.save_object(db_song)259 self.db_manager.save_object(db_song)
224 return db_song260 return db_song
225261
226262
=== modified file 'tests/functional/openlp_plugins/songs/test_songselect.py'
--- tests/functional/openlp_plugins/songs/test_songselect.py 2016-01-09 18:01:49 +0000
+++ tests/functional/openlp_plugins/songs/test_songselect.py 2016-08-12 12:20:33 +0000
@@ -71,7 +71,7 @@
71 mocked_opener = MagicMock()71 mocked_opener = MagicMock()
72 mocked_build_opener.return_value = mocked_opener72 mocked_build_opener.return_value = mocked_opener
73 mocked_login_page = MagicMock()73 mocked_login_page = MagicMock()
74 mocked_login_page.find.return_value = {'value': 'blah'}74 mocked_login_page.find.side_effect = [{'value': 'blah'}, None]
75 MockedBeautifulSoup.return_value = mocked_login_page75 MockedBeautifulSoup.return_value = mocked_login_page
76 mock_callback = MagicMock()76 mock_callback = MagicMock()
77 importer = SongSelectImport(None)77 importer = SongSelectImport(None)
@@ -112,7 +112,7 @@
112 mocked_opener = MagicMock()112 mocked_opener = MagicMock()
113 mocked_build_opener.return_value = mocked_opener113 mocked_build_opener.return_value = mocked_opener
114 mocked_login_page = MagicMock()114 mocked_login_page = MagicMock()
115 mocked_login_page.find.side_effect = [{'value': 'blah'}, None]115 mocked_login_page.find.side_effect = [{'value': 'blah'}, MagicMock()]
116 MockedBeautifulSoup.return_value = mocked_login_page116 MockedBeautifulSoup.return_value = mocked_login_page
117 mock_callback = MagicMock()117 mock_callback = MagicMock()
118 importer = SongSelectImport(None)118 importer = SongSelectImport(None)
@@ -165,7 +165,7 @@
165 self.assertEqual(0, mock_callback.call_count, 'callback should not have been called')165 self.assertEqual(0, mock_callback.call_count, 'callback should not have been called')
166 self.assertEqual(1, mocked_opener.open.call_count, 'open should have been called once')166 self.assertEqual(1, mocked_opener.open.call_count, 'open should have been called once')
167 self.assertEqual(1, mocked_results_page.find_all.call_count, 'find_all should have been called once')167 self.assertEqual(1, mocked_results_page.find_all.call_count, 'find_all should have been called once')
168 mocked_results_page.find_all.assert_called_with('li', 'result pane')168 mocked_results_page.find_all.assert_called_with('div', 'song-result')
169 self.assertEqual([], results, 'The search method should have returned an empty list')169 self.assertEqual([], results, 'The search method should have returned an empty list')
170170
171 @patch('openlp.plugins.songs.lib.songselect.build_opener')171 @patch('openlp.plugins.songs.lib.songselect.build_opener')
@@ -177,12 +177,18 @@
177 # GIVEN: A bunch of mocked out stuff and an importer object177 # GIVEN: A bunch of mocked out stuff and an importer object
178 # first search result178 # first search result
179 mocked_result1 = MagicMock()179 mocked_result1 = MagicMock()
180 mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}]180 mocked_result1.find.side_effect = [
181 mocked_result1.find_all.return_value = [MagicMock(string='Author 1-1'), MagicMock(string='Author 1-2')]181 MagicMock(find=MagicMock(return_value=MagicMock(string='Title 1'))),
182 MagicMock(string='James, John'),
183 MagicMock(find=MagicMock(return_value={'href': '/url1'}))
184 ]
182 # second search result185 # second search result
183 mocked_result2 = MagicMock()186 mocked_result2 = MagicMock()
184 mocked_result2.find.side_effect = [MagicMock(string='Title 2'), {'href': '/url2'}]187 mocked_result2.find.side_effect = [
185 mocked_result2.find_all.return_value = [MagicMock(string='Author 2-1'), MagicMock(string='Author 2-2')]188 MagicMock(find=MagicMock(return_value=MagicMock(string='Title 2'))),
189 MagicMock(string='Philip'),
190 MagicMock(find=MagicMock(return_value={'href': '/url2'}))
191 ]
186 # rest of the stuff192 # rest of the stuff
187 mocked_opener = MagicMock()193 mocked_opener = MagicMock()
188 mocked_build_opener.return_value = mocked_opener194 mocked_build_opener.return_value = mocked_opener
@@ -196,13 +202,14 @@
196 results = importer.search('text', 1000, mock_callback)202 results = importer.search('text', 1000, mock_callback)
197203
198 # THEN: callback was never called, open was called once, find_all was called once, an empty list returned204 # THEN: callback was never called, open was called once, find_all was called once, an empty list returned
205 self.maxDiff = None
199 self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice')206 self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice')
200 self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice')207 self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice')
201 self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice')208 self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice')
202 mocked_results_page.find_all.assert_called_with('li', 'result pane')209 mocked_results_page.find_all.assert_called_with('div', 'song-result')
203 expected_list = [210 expected_list = [
204 {'title': 'Title 1', 'authors': ['Author 1-1', 'Author 1-2'], 'link': BASE_URL + '/url1'},211 {'title': 'Title 1', 'authors': ['James', 'John'], 'link': BASE_URL + '/url1'},
205 {'title': 'Title 2', 'authors': ['Author 2-1', 'Author 2-2'], 'link': BASE_URL + '/url2'}212 {'title': 'Title 2', 'authors': ['Philip'], 'link': BASE_URL + '/url2'}
206 ]213 ]
207 self.assertListEqual(expected_list, results, 'The search method should have returned two songs')214 self.assertListEqual(expected_list, results, 'The search method should have returned two songs')
208215
@@ -215,16 +222,25 @@
215 # GIVEN: A bunch of mocked out stuff and an importer object222 # GIVEN: A bunch of mocked out stuff and an importer object
216 # first search result223 # first search result
217 mocked_result1 = MagicMock()224 mocked_result1 = MagicMock()
218 mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}]225 mocked_result1.find.side_effect = [
219 mocked_result1.find_all.return_value = [MagicMock(string='Author 1-1'), MagicMock(string='Author 1-2')]226 MagicMock(find=MagicMock(return_value=MagicMock(string='Title 1'))),
227 MagicMock(string='James, John'),
228 MagicMock(find=MagicMock(return_value={'href': '/url1'}))
229 ]
220 # second search result230 # second search result
221 mocked_result2 = MagicMock()231 mocked_result2 = MagicMock()
222 mocked_result2.find.side_effect = [MagicMock(string='Title 2'), {'href': '/url2'}]232 mocked_result2.find.side_effect = [
223 mocked_result2.find_all.return_value = [MagicMock(string='Author 2-1'), MagicMock(string='Author 2-2')]233 MagicMock(find=MagicMock(return_value=MagicMock(string='Title 2'))),
234 MagicMock(string='Philip'),
235 MagicMock(find=MagicMock(return_value={'href': '/url2'}))
236 ]
224 # third search result237 # third search result
225 mocked_result3 = MagicMock()238 mocked_result3 = MagicMock()
226 mocked_result3.find.side_effect = [MagicMock(string='Title 3'), {'href': '/url3'}]239 mocked_result3.find.side_effect = [
227 mocked_result3.find_all.return_value = [MagicMock(string='Author 3-1'), MagicMock(string='Author 3-2')]240 MagicMock(find=MagicMock(return_value=MagicMock(string='Title 3'))),
241 MagicMock(string='Luke, Matthew'),
242 MagicMock(find=MagicMock(return_value={'href': '/url3'}))
243 ]
228 # rest of the stuff244 # rest of the stuff
229 mocked_opener = MagicMock()245 mocked_opener = MagicMock()
230 mocked_build_opener.return_value = mocked_opener246 mocked_build_opener.return_value = mocked_opener
@@ -241,9 +257,9 @@
241 self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice')257 self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice')
242 self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice')258 self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice')
243 self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice')259 self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice')
244 mocked_results_page.find_all.assert_called_with('li', 'result pane')260 mocked_results_page.find_all.assert_called_with('div', 'song-result')
245 expected_list = [{'title': 'Title 1', 'authors': ['Author 1-1', 'Author 1-2'], 'link': BASE_URL + '/url1'},261 expected_list = [{'title': 'Title 1', 'authors': ['James', 'John'], 'link': BASE_URL + '/url1'},
246 {'title': 'Title 2', 'authors': ['Author 2-1', 'Author 2-2'], 'link': BASE_URL + '/url2'}]262 {'title': 'Title 2', 'authors': ['Philip'], 'link': BASE_URL + '/url2'}]
247 self.assertListEqual(expected_list, results, 'The search method should have returned two songs')263 self.assertListEqual(expected_list, results, 'The search method should have returned two songs')
248264
249 @patch('openlp.plugins.songs.lib.songselect.build_opener')265 @patch('openlp.plugins.songs.lib.songselect.build_opener')
@@ -337,7 +353,7 @@
337 self.assertIsNotNone(result, 'The get_song() method should have returned a song dictionary')353 self.assertIsNotNone(result, 'The get_song() method should have returned a song dictionary')
338 self.assertEqual(2, mocked_lyrics_page.find.call_count, 'The find() method should have been called twice')354 self.assertEqual(2, mocked_lyrics_page.find.call_count, 'The find() method should have been called twice')
339 self.assertEqual(2, mocked_find_all.call_count, 'The find_all() method should have been called twice')355 self.assertEqual(2, mocked_find_all.call_count, 'The find_all() method should have been called twice')
340 self.assertEqual([call('section', 'lyrics'), call('section', 'lyrics')],356 self.assertEqual([call('div', 'song-viewer lyrics'), call('div', 'song-viewer lyrics')],
341 mocked_lyrics_page.find.call_args_list,357 mocked_lyrics_page.find.call_args_list,
342 'The find() method should have been called with the right arguments')358 'The find() method should have been called with the right arguments')
343 self.assertEqual([call('p'), call('h3')], mocked_find_all.call_args_list,359 self.assertEqual([call('p'), call('h3')], mocked_find_all.call_args_list,
@@ -419,8 +435,9 @@
419 self.assertEqual(1, len(result.authors_songs), 'There should only be one author')435 self.assertEqual(1, len(result.authors_songs), 'There should only be one author')
420436
421 @patch('openlp.plugins.songs.lib.songselect.clean_song')437 @patch('openlp.plugins.songs.lib.songselect.clean_song')
438 @patch('openlp.plugins.songs.lib.songselect.Topic')
422 @patch('openlp.plugins.songs.lib.songselect.Author')439 @patch('openlp.plugins.songs.lib.songselect.Author')
423 def save_song_unknown_author_test(self, MockedAuthor, mocked_clean_song):440 def save_song_unknown_author_test(self, MockedAuthor, MockedTopic, mocked_clean_song):
424 """441 """
425 Test that saving a song with an author name of only one word performs the correct actions442 Test that saving a song with an author name of only one word performs the correct actions
426 """443 """
@@ -437,6 +454,7 @@
437 'ccli_number': '123456'454 'ccli_number': '123456'
438 }455 }
439 MockedAuthor.display_name.__eq__.return_value = False456 MockedAuthor.display_name.__eq__.return_value = False
457 MockedTopic.name.__eq__.return_value = False
440 mocked_db_manager = MagicMock()458 mocked_db_manager = MagicMock()
441 mocked_db_manager.get_object_filtered.return_value = None459 mocked_db_manager.get_object_filtered.return_value = None
442 importer = SongSelectImport(mocked_db_manager)460 importer = SongSelectImport(mocked_db_manager)

Subscribers

People subscribed via source and target branches

to all changes: