openlp/tests/openlp_plugins/songs/test_mediaitem.py

622 lines
25 KiB
Python
Raw Normal View History

2014-12-19 22:01:15 +00:00
# -*- coding: utf-8 -*-
2019-04-13 13:00:22 +00:00
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
2020-12-30 21:42:49 +00:00
# Copyright (c) 2008-2021 OpenLP Developers #
2019-04-13 13:00:22 +00:00
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
2013-04-23 19:08:07 +00:00
"""
This module contains tests for the lib submodule of the Songs plugin.
"""
import pytest
2018-10-27 11:05:41 +00:00
from unittest.mock import MagicMock, patch
2013-04-23 19:08:07 +00:00
2015-11-07 00:49:40 +00:00
from PyQt5 import QtCore
2013-04-23 19:08:07 +00:00
2017-10-07 07:05:07 +00:00
from openlp.core.common.registry import Registry
from openlp.core.lib.serviceitem import ServiceItem
2016-01-04 19:20:21 +00:00
from openlp.plugins.songs.lib.db import AuthorType, Song
from openlp.plugins.songs.lib.mediaitem import SongMediaItem
2013-04-23 19:08:07 +00:00
__default_settings__ = {
2017-06-12 18:04:17 +00:00
'songs/footer template': """
${title}<br/>
%if authors_none:
<%
2017-06-12 18:09:50 +00:00
authors = ", ".join(authors_none)
2017-06-12 18:04:17 +00:00
%>
${authors_none_label}:&nbsp;${authors}<br/>
%endif
%if authors_words_music:
<%
2017-06-12 18:09:50 +00:00
authors = ", ".join(authors_words_music)
2017-06-12 18:04:17 +00:00
%>
${authors_words_music_label}:&nbsp;${authors}<br/>
%endif
%if authors_words:
<%
2017-06-12 18:09:50 +00:00
authors = ", ".join(authors_words)
2017-06-12 18:04:17 +00:00
%>
${authors_words_label}:&nbsp;${authors}<br/>
%endif
%if authors_music:
<%
2017-06-12 18:09:50 +00:00
authors = ", ".join(authors_music)
2017-06-12 18:04:17 +00:00
%>
${authors_music_label}:&nbsp;${authors}<br/>
%endif
%if authors_translation:
<%
2017-06-12 18:09:50 +00:00
authors = ", ".join(authors_translation)
2017-06-12 18:04:17 +00:00
%>
${authors_translation_label}:&nbsp;${authors}<br/>
%endif
%if copyright:
&copy;&nbsp;${copyright}<br/>
%endif
%if songbook_entries:
<%
entries = ", ".join(songbook_entries)
%>
${entries}<br/>
%endif
%if ccli_license:
${ccli_license_label}&nbsp;${ccli_license}<br/>
%endif
"""
}
2013-04-23 19:08:07 +00:00
@pytest.fixture
2020-03-08 21:45:42 +00:00
def media_item(settings):
Registry().register('service_list', MagicMock())
Registry().register('main_window', MagicMock())
mocked_plugin = MagicMock()
with patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup'), \
patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'):
media_item = SongMediaItem(None, mocked_plugin)
media_item.save_auto_select_id = MagicMock()
media_item.list_view = MagicMock()
media_item.list_view.save_auto_select_id = MagicMock()
media_item.list_view.clear = MagicMock()
media_item.list_view.addItem = MagicMock()
media_item.list_view.setCurrentItem = MagicMock()
media_item.auto_select_id = -1
media_item.display_songbook = False
media_item.display_copyright_symbol = False
settings = Registry().get('settings')
settings.extend_default_settings(__default_settings__)
QtCore.QLocale.setDefault(QtCore.QLocale('en_GB'))
yield media_item
2020-03-08 21:45:42 +00:00
def test_display_results_song(media_item):
"""
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 = []
2013-04-23 19:08:07 +00:00
mock_song = MagicMock()
2020-03-08 21:45:42 +00:00
mock_song.id = 1
2013-08-31 18:17:38 +00:00
mock_song.title = 'My Song'
2020-03-08 21:45:42 +00:00
mock_song.sort_key = 'My Song'
mock_song.authors = []
mock_song_temp = MagicMock()
mock_song_temp.id = 2
mock_song_temp.title = 'My Temporary'
mock_song_temp.sort_key = 'My Temporary'
mock_song_temp.authors = []
2013-04-23 19:08:07 +00:00
mock_author = MagicMock()
2020-03-08 21:45:42 +00:00
mock_author.display_name = 'My Author'
mock_song.authors.append(mock_author)
mock_song_temp.authors.append(mock_author)
mock_song.temporary = False
mock_song_temp.temporary = True
mock_search_results.append(mock_song)
mock_search_results.append(mock_song_temp)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
media_item.auto_select_id = 1
# WHEN: I display song search results
media_item.display_results_song(mock_search_results)
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
media_item.list_view.clear.assert_called_with()
media_item.save_auto_select_id.assert_called_with()
MockedQListWidgetItem.assert_called_once_with('My Song (My Author)')
mock_qlist_widget.setData.assert_called_once_with(MockedUserRole, mock_song.id)
media_item.list_view.addItem.assert_called_once_with(mock_qlist_widget)
media_item.list_view.setCurrentItem.assert_called_with(mock_qlist_widget)
def test_display_results_author(media_item):
"""
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 = []
2014-03-30 18:01:03 +00:00
mock_author = MagicMock()
2013-04-23 19:08:07 +00:00
mock_song = MagicMock()
2020-03-08 21:45:42 +00:00
mock_song_temp = MagicMock()
mock_author.display_name = 'My Author'
mock_author.songs = []
mock_song.id = 1
2013-08-31 18:17:38 +00:00
mock_song.title = 'My Song'
2020-03-08 21:45:42 +00:00
mock_song.sort_key = 'My Song'
mock_song.temporary = False
mock_song_temp.id = 2
mock_song_temp.title = 'My Temporary'
mock_song_temp.sort_key = 'My Temporary'
mock_song_temp.temporary = True
mock_author.songs.append(mock_song)
mock_author.songs.append(mock_song_temp)
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
media_item.display_results_author(mock_search_results)
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_once_with('My Author (My Song)')
mock_qlist_widget.setData.assert_called_once_with(MockedUserRole, mock_song.id)
media_item.list_view.addItem.assert_called_once_with(mock_qlist_widget)
def test_display_results_book(media_item):
"""
Test displaying song search results grouped by book and entry with basic song
"""
# GIVEN: Search results grouped by book and entry, 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 = [('1', 'My Book', 'My Song', 1)]
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
2013-04-23 19:08:07 +00:00
2020-03-08 21:45:42 +00:00
# WHEN: I display song search results grouped by book
media_item.display_results_book(mock_search_results)
2014-05-03 10:42:18 +00:00
2020-03-08 21:45:42 +00:00
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_once_with('My Book #1: My Song')
mock_qlist_widget.setData.assert_called_once_with(MockedUserRole, 1)
media_item.list_view.addItem.assert_called_once_with(mock_qlist_widget)
2014-05-03 10:42:18 +00:00
2014-05-13 09:53:02 +00:00
2020-03-08 21:45:42 +00:00
def test_songbook_natural_sorting(media_item):
"""
Test that songbooks are sorted naturally
"""
# GIVEN: Search results grouped by book and entry
search_results = [('2', 'Thy Book', 'Thy Song', 50),
('2', 'My Book', 'Your Song', 7),
('10', 'My Book', 'Our Song', 12),
('1', 'My Book', 'My Song', 1),
('2', 'Thy Book', 'A Song', 8)]
# WHEN: I display song search results grouped by book
media_item.display_results_book(search_results)
# THEN: The songbooks are sorted inplace in the right (natural) order,
# grouped first by book, then by number, then by song title
assert search_results == [('1', 'My Book', 'My Song', 1),
('2', 'My Book', 'Your Song', 7),
('10', 'My Book', 'Our Song', 12),
('2', 'Thy Book', 'A Song', 8),
('2', 'Thy Book', 'Thy Song', 50)]
def test_display_results_topic(media_item):
"""
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()
2014-07-09 12:41:55 +00:00
mock_song = MagicMock()
2020-03-08 21:45:42 +00:00
mock_song_temp = MagicMock()
mock_topic.name = 'My Topic'
mock_topic.songs = []
mock_song.id = 1
2014-07-09 12:41:55 +00:00
mock_song.title = 'My Song'
2020-03-08 21:45:42 +00:00
mock_song.sort_key = 'My Song'
mock_song.temporary = False
mock_song_temp.id = 2
mock_song_temp.title = 'My Temporary'
mock_song_temp.sort_key = 'My Temporary'
mock_song_temp.temporary = True
mock_topic.songs.append(mock_song)
mock_topic.songs.append(mock_song_temp)
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
media_item.display_results_topic(mock_search_results)
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_once_with('My Topic (My Song)')
mock_qlist_widget.setData.assert_called_once_with(MockedUserRole, mock_song.id)
media_item.list_view.addItem.assert_called_once_with(mock_qlist_widget)
def test_display_results_themes(media_item):
"""
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 = []
2014-07-12 20:00:58 +00:00
mock_song = MagicMock()
2020-03-08 21:45:42 +00:00
mock_song_temp = MagicMock()
mock_song.id = 1
2014-07-12 20:00:58 +00:00
mock_song.title = 'My Song'
2020-03-08 21:45:42 +00:00
mock_song.sort_key = 'My Song'
mock_song.theme_name = 'My Theme'
mock_song.temporary = False
mock_song_temp.id = 2
mock_song_temp.title = 'My Temporary'
mock_song_temp.sort_key = 'My Temporary'
mock_song_temp.theme_name = 'My Theme'
mock_song_temp.temporary = True
mock_search_results.append(mock_song)
mock_search_results.append(mock_song_temp)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
# WHEN: I display song search results sorted by theme
media_item.display_results_themes(mock_search_results)
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_once_with('My Theme (My Song)')
mock_qlist_widget.setData.assert_called_once_with(MockedUserRole, mock_song.id)
media_item.list_view.addItem.assert_called_once_with(mock_qlist_widget)
def test_display_results_cclinumber(media_item):
"""
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 = []
2016-01-05 19:32:12 +00:00
mock_song = MagicMock()
2020-03-08 21:45:42 +00:00
mock_song_temp = MagicMock()
mock_song.id = 1
2016-01-05 19:32:12 +00:00
mock_song.title = 'My Song'
2020-03-08 21:45:42 +00:00
mock_song.sort_key = 'My Song'
mock_song.ccli_number = '12345'
mock_song.temporary = False
mock_song_temp.id = 2
mock_song_temp.title = 'My Temporary'
mock_song_temp.sort_key = 'My Temporary'
mock_song_temp.ccli_number = '12346'
mock_song_temp.temporary = True
mock_search_results.append(mock_song)
mock_search_results.append(mock_song_temp)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
# WHEN: I display song search results sorted by CCLI number
media_item.display_results_cclinumber(mock_search_results)
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_once_with('12345 (My Song)')
mock_qlist_widget.setData.assert_called_once_with(MockedUserRole, mock_song.id)
media_item.list_view.addItem.assert_called_once_with(mock_qlist_widget)
def test_build_song_footer_two_authors(media_item):
"""
2020-03-08 21:45:42 +00:00
Test build songs footer with basic song and two authors
"""
2020-03-08 21:45:42 +00:00
# GIVEN: A Song and a Service Item
mock_song = MagicMock()
mock_song.title = 'My Song'
mock_song.authors_songs = []
mock_author = MagicMock()
mock_author.display_name = 'my author'
mock_author_song = MagicMock()
mock_author_song.author = mock_author
mock_author_song.author_type = AuthorType.Music
mock_song.authors_songs.append(mock_author_song)
mock_author = MagicMock()
mock_author.display_name = 'another author'
mock_author_song = MagicMock()
mock_author_song.author = mock_author
mock_author_song.author_type = AuthorType.Words
mock_song.authors_songs.append(mock_author_song)
mock_author = MagicMock()
mock_author.display_name = 'translator'
mock_author_song = MagicMock()
mock_author_song.author = mock_author
mock_author_song.author_type = AuthorType.Translation
mock_song.authors_songs.append(mock_author_song)
mock_song.copyright = 'My copyright'
mock_song.songbook_entries = []
service_item = ServiceItem(None)
# WHEN: I generate the Footer with default settings
author_list = media_item.generate_footer(service_item, mock_song)
# THEN: I get the following Array returned
assert service_item.raw_footer == ['My Song', 'Words: another author', 'Music: my author',
'Translation: translator', '© My copyright'], \
'The array should be returned correctly with a song, two authors and copyright'
assert author_list == ['another author', 'my author', 'translator'], \
'The author list should be returned correctly with two authors'
def test_build_song_footer_base_ccli(media_item):
"""
Test build songs footer with basic song and a CCLI number
"""
# GIVEN: A Song and a Service Item and a configured CCLI license
mock_song = MagicMock()
mock_song.title = 'My Song'
mock_song.copyright = 'My copyright'
mock_song.songbook_entries = []
service_item = ServiceItem(None)
media_item.settings.setValue('core/ccli number', '1234')
# WHEN: I generate the Footer with default settings
media_item.generate_footer(service_item, mock_song)
# THEN: I get the following Array returned
assert service_item.raw_footer == ['My Song', '© My copyright', 'CCLI License: 1234'], \
'The array should be returned correctly with a song, an author, copyright and ccli'
# WHEN: I amend the CCLI value
media_item.settings.setValue('core/ccli number', '4321')
media_item.generate_footer(service_item, mock_song)
# THEN: I would get an amended footer string
assert service_item.raw_footer == ['My Song', '© My copyright', 'CCLI License: 4321'], \
'The array should be returned correctly with a song, an author, copyright and amended ccli'
def test_build_song_footer_base_songbook(media_item):
"""
Test build songs footer with basic song and multiple songbooks
"""
# GIVEN: A Song and a Service Item
song = Song()
song.title = 'My Song'
song.alternate_title = ''
song.copyright = 'My copyright'
song.authors_songs = []
song.songbook_entries = []
song.alternate_title = ''
song.topics = []
song.ccli_number = ''
book1 = MagicMock()
book1.name = 'My songbook'
book2 = MagicMock()
book2.name = 'Thy songbook'
song.songbookentries = []
song.add_songbook_entry(book1, '12')
song.add_songbook_entry(book2, '502A')
service_item = ServiceItem(None)
# WHEN: I generate the Footer with default settings
media_item.generate_footer(service_item, song)
# THEN: The songbook should be in the footer
assert service_item.raw_footer == ['My Song', '© My copyright', 'My songbook #12, Thy songbook #502A']
def test_build_song_footer_copyright_enabled(media_item):
"""
Test building song footer with displaying the copyright symbol
"""
# GIVEN: A Song and a Service Item; displaying the copyright symbol is enabled
media_item.display_copyright_symbol = True
mock_song = MagicMock()
mock_song.title = 'My Song'
mock_song.copyright = 'My copyright'
mock_song.songbook_entries = []
service_item = ServiceItem(None)
# WHEN: I generate the Footer with default settings
media_item.generate_footer(service_item, mock_song)
# THEN: The copyright symbol should be in the footer
assert service_item.raw_footer == ['My Song', '© My copyright']
def test_build_song_footer_copyright_disabled(media_item):
"""
Test building song footer without displaying the copyright symbol
"""
# GIVEN: A Song and a Service Item; displaying the copyright symbol should be disabled by default
mock_song = MagicMock()
mock_song.title = 'My Song'
mock_song.copyright = 'My copyright'
mock_song.songbook_entries = []
service_item = ServiceItem(None)
# WHEN: I generate the Footer with default settings
media_item.generate_footer(service_item, mock_song)
# THEN: The copyright symbol should not be in the footer
assert service_item.raw_footer == ['My Song', '© My copyright']
2020-03-08 21:45:42 +00:00
def test_authors_match(media_item):
"""
Test the author matching when importing a song from a service
"""
# GIVEN: A song and a string with authors
song = MagicMock()
song.authors = []
author = MagicMock()
author.display_name = "Hans Wurst"
song.authors.append(author)
author2 = MagicMock()
author2.display_name = "Max Mustermann"
song.authors.append(author2)
# There are occasions where an author appears twice in a song (with different types).
# We need to make sure that this case works (lp#1313538)
author3 = MagicMock()
author3.display_name = "Max Mustermann"
song.authors.append(author3)
authors_str = "Hans Wurst, Max Mustermann, Max Mustermann"
# WHEN: Checking for matching
result = media_item._authors_match(song, authors_str)
# THEN: They should match
assert result is True, "Authors should match"
def test_authors_dont_match(media_item):
# GIVEN: A song and a string with authors
song = MagicMock()
song.authors = []
author = MagicMock()
author.display_name = "Hans Wurst"
song.authors.append(author)
author2 = MagicMock()
author2.display_name = "Max Mustermann"
song.authors.append(author2)
# There are occasions where an author appears twice in a song (with different types).
# We need to make sure that this case works (lp#1313538)
author3 = MagicMock()
author3.display_name = "Max Mustermann"
song.authors.append(author3)
# WHEN: An author is missing in the string
authors_str = "Hans Wurst, Max Mustermann"
result = media_item._authors_match(song, authors_str)
# THEN: They should not match
assert result is False, "Authors should not match"
def test_build_remote_search(media_item):
"""
Test results for the remote search api
"""
# GIVEN: A Song and a search a JSON array should be returned.
mock_song = MagicMock()
mock_song.id = 123
mock_song.title = 'My Song'
mock_song.search_title = 'My Song'
mock_song.alternate_title = 'My alternative'
media_item.search_entire = MagicMock()
media_item.search_entire.return_value = [mock_song]
# WHEN: I process a search
search_results = media_item.search('My Song', False)
# THEN: The correct formatted results are returned
assert search_results == [[123, 'My Song', 'My alternative']]
@patch('openlp.plugins.songs.lib.mediaitem.Book')
@patch('openlp.plugins.songs.lib.mediaitem.SongBookEntry')
@patch('openlp.plugins.songs.lib.mediaitem.Song')
@patch('openlp.plugins.songs.lib.mediaitem.or_')
def test_entire_song_search(mocked_or, MockedSong, MockedSongBookEntry, MockedBook, media_item):
"""
Test that searching the entire song does the right queries
"""
# GIVEN: A song media item, a keyword and some mocks
keyword = 'Jesus'
mocked_or.side_effect = lambda a, b, c, d, e: ' '.join([a, b, c, d, e])
MockedSong.search_title.like.side_effect = lambda a: a
MockedSong.search_lyrics.like.side_effect = lambda a: a
MockedSong.comments.like.side_effect = lambda a: a
MockedSongBookEntry.entry.like.side_effect = lambda a: a
MockedBook.name.like.side_effect = lambda a: a
# WHEN: search_entire_song() is called with the keyword
media_item.search_entire(keyword)
# THEN: The correct calls were made
MockedSong.search_title.like.assert_called_once_with('%jesus%')
MockedSong.search_lyrics.like.assert_called_once_with('%jesus%')
MockedSong.comments.like.assert_called_once_with('%jesus%')
MockedSongBookEntry.entry.like.assert_called_once_with('%jesus%')
MockedBook.name.like.assert_called_once_with('%jesus%')
mocked_or.assert_called_once_with('%jesus%', '%jesus%', '%jesus%', '%jesus%', '%jesus%')
media_item.plugin.manager.session.query.assert_called_once_with(MockedSong)
assert media_item.plugin.manager.session.query.mock_calls[4][0] == '().join().join().filter().all'
def test_build_song_footer_one_author_show_written_by(media_item):
"""
Test build songs footer with basic song and one author
"""
# GIVEN: A Song and a Service Item, mocked settings
media_item.settings.setValue('core/ccli number', "0")
media_item.settings.setValue('songs/footer template', "")
with patch('mako.template.Template.render_unicode') as MockedRenderer:
mock_song = MagicMock()
mock_song.title = 'My Song'
mock_song.alternate_title = ''
mock_song.ccli_number = ''
mock_song.authors_songs = []
mock_author = MagicMock()
mock_author.display_name = 'my author'
mock_author_song = MagicMock()
mock_author_song.author = mock_author
mock_song.authors_songs.append(mock_author_song)
mock_song.copyright = 'My copyright'
mock_song.songbook_entries = []
service_item = ServiceItem(None)
# WHEN: I generate the Footer with default settings
2020-03-08 21:45:42 +00:00
author_list = media_item.generate_footer(service_item, mock_song)
# THEN: The mako function was called with the following arguments
args = {'authors_translation': [], 'authors_music_label': 'Music',
'copyright': 'My copyright', 'songbook_entries': [],
'alternate_title': '', 'topics': [], 'authors_music_all': [],
'authors_words_label': 'Words', 'authors_music': [],
'authors_words_music': [], 'ccli_number': '',
'authors_none_label': 'Written by', 'title': 'My Song',
'authors_words_music_label': 'Words and Music',
'authors_none': ['my author'],
'ccli_license_label': 'CCLI License', 'authors_words': [],
'ccli_license': '0', 'authors_translation_label': 'Translation',
'authors_words_all': []}
MockedRenderer.assert_called_once_with(**args)
assert author_list == ['my author'], 'The author list should be returned correctly with one author'