forked from openlp/openlp
HEAD
This commit is contained in:
commit
c1d4d59471
|
@ -1 +1 @@
|
|||
2.4.2
|
||||
2.4.3
|
||||
|
|
|
@ -312,10 +312,10 @@ class PJLink1(QTcpSocket):
|
|||
read = self.readLine(self.maxSize)
|
||||
dontcare = self.readLine(self.maxSize) # Clean out the trailing \r\n
|
||||
if read is None:
|
||||
log.warn('({ip}) read is None - socket error?'.format(ip=self.ip))
|
||||
log.warning('({ip}) read is None - socket error?'.format(ip=self.ip))
|
||||
return
|
||||
elif len(read) < 8:
|
||||
log.warn('({ip}) Not enough data read)'.format(ip=self.ip))
|
||||
log.warning('({ip}) Not enough data read)'.format(ip=self.ip))
|
||||
return
|
||||
data = decode(read, 'ascii')
|
||||
# Possibility of extraneous data on input when reading.
|
||||
|
@ -413,7 +413,7 @@ class PJLink1(QTcpSocket):
|
|||
self.projectorReceivedData.emit()
|
||||
return
|
||||
elif '=' not in data:
|
||||
log.warn('({ip}) get_data(): Invalid packet received'.format(ip=self.ip))
|
||||
log.warning('({ip}) get_data(): Invalid packet received'.format(ip=self.ip))
|
||||
self.send_busy = False
|
||||
self.projectorReceivedData.emit()
|
||||
return
|
||||
|
@ -421,21 +421,21 @@ class PJLink1(QTcpSocket):
|
|||
try:
|
||||
(prefix, class_, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1])
|
||||
except ValueError as e:
|
||||
log.warn('({ip}) get_data(): Invalid packet - expected header + command + data'.format(ip=self.ip))
|
||||
log.warn('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in.strip()))
|
||||
log.warning('({ip}) get_data(): Invalid packet - expected header + command + data'.format(ip=self.ip))
|
||||
log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in.strip()))
|
||||
self.change_status(E_INVALID_DATA)
|
||||
self.send_busy = False
|
||||
self.projectorReceivedData.emit()
|
||||
return
|
||||
|
||||
if not (self.pjlink_class in PJLINK_VALID_CMD and cmd in PJLINK_VALID_CMD[self.pjlink_class]):
|
||||
log.warn('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd))
|
||||
log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd))
|
||||
self.send_busy = False
|
||||
self.projectorReceivedData.emit()
|
||||
return
|
||||
return self.process_command(cmd, data)
|
||||
|
||||
@pyqtSlot(int)
|
||||
@pyqtSlot(QAbstractSocket.SocketError)
|
||||
def get_error(self, err):
|
||||
"""
|
||||
Process error from SocketError signal.
|
||||
|
@ -472,7 +472,7 @@ class PJLink1(QTcpSocket):
|
|||
:param queue: Option to force add to queue rather than sending directly
|
||||
"""
|
||||
if self.state() != self.ConnectedState:
|
||||
log.warn('({ip}) send_command(): Not connected - returning'.format(ip=self.ip))
|
||||
log.warning('({ip}) send_command(): Not connected - returning'.format(ip=self.ip))
|
||||
self.send_queue = []
|
||||
return
|
||||
self.projectorNetwork.emit(self.ip, S_NETWORK_SENDING)
|
||||
|
@ -592,7 +592,7 @@ class PJLink1(QTcpSocket):
|
|||
if cmd in self.PJLINK1_FUNC:
|
||||
self.PJLINK1_FUNC[cmd](data)
|
||||
else:
|
||||
log.warn('({ip}) Invalid command {data}'.format(ip=self.ip, data=cmd))
|
||||
log.warning('({ip}) Invalid command {data}'.format(ip=self.ip, data=cmd))
|
||||
self.send_busy = False
|
||||
self.projectorReceivedData.emit()
|
||||
|
||||
|
@ -611,7 +611,7 @@ class PJLink1(QTcpSocket):
|
|||
fill = {'Hours': int(data_dict[0]), 'On': False if data_dict[1] == '0' else True}
|
||||
except ValueError:
|
||||
# In case of invalid entry
|
||||
log.warn('({ip}) process_lamp(): Invalid data "{data}"'.format(ip=self.ip, data=data))
|
||||
log.warning('({ip}) process_lamp(): Invalid data "{data}"'.format(ip=self.ip, data=data))
|
||||
return
|
||||
lamps.append(fill)
|
||||
data_dict.pop(0) # Remove lamp hours
|
||||
|
@ -638,7 +638,7 @@ class PJLink1(QTcpSocket):
|
|||
self.send_command('INST')
|
||||
else:
|
||||
# Log unknown status response
|
||||
log.warn('({ip}) Unknown power response: {data}'.format(ip=self.ip, data=data))
|
||||
log.warning('({ip}) Unknown power response: {data}'.format(ip=self.ip, data=data))
|
||||
return
|
||||
|
||||
def process_avmt(self, data):
|
||||
|
@ -663,7 +663,7 @@ class PJLink1(QTcpSocket):
|
|||
shutter = True
|
||||
mute = True
|
||||
else:
|
||||
log.warn('({ip}) Unknown shutter response: {data}'.format(ip=self.ip, data=data))
|
||||
log.warning('({ip}) Unknown shutter response: {data}'.format(ip=self.ip, data=data))
|
||||
update_icons = shutter != self.shutter
|
||||
update_icons = update_icons or mute != self.mute
|
||||
self.shutter = shutter
|
||||
|
@ -812,7 +812,7 @@ class PJLink1(QTcpSocket):
|
|||
Initiate connection to projector.
|
||||
"""
|
||||
if self.state() == self.ConnectedState:
|
||||
log.warn('({ip}) connect_to_host(): Already connected - returning'.format(ip=self.ip))
|
||||
log.warning('({ip}) connect_to_host(): Already connected - returning'.format(ip=self.ip))
|
||||
return
|
||||
self.change_status(S_CONNECTING)
|
||||
self.connectToHost(self.ip, self.port if type(self.port) is int else int(self.port))
|
||||
|
@ -824,9 +824,9 @@ class PJLink1(QTcpSocket):
|
|||
"""
|
||||
if abort or self.state() != self.ConnectedState:
|
||||
if abort:
|
||||
log.warn('({ip}) disconnect_from_host(): Aborting connection'.format(ip=self.ip))
|
||||
log.warning('({ip}) disconnect_from_host(): Aborting connection'.format(ip=self.ip))
|
||||
else:
|
||||
log.warn('({ip}) disconnect_from_host(): Not connected - returning'.format(ip=self.ip))
|
||||
log.warning('({ip}) disconnect_from_host(): Not connected - returning'.format(ip=self.ip))
|
||||
self.reset_information()
|
||||
self.disconnectFromHost()
|
||||
try:
|
||||
|
|
|
@ -159,7 +159,6 @@ class GeneralTab(SettingsTab):
|
|||
self.startup_layout.addWidget(self.show_splash_check_box)
|
||||
self.check_for_updates_check_box = QtWidgets.QCheckBox(self.startup_group_box)
|
||||
self.check_for_updates_check_box.setObjectName('check_for_updates_check_box')
|
||||
self.check_for_updates_check_box.setVisible(False)
|
||||
self.startup_layout.addWidget(self.check_for_updates_check_box)
|
||||
self.right_layout.addWidget(self.startup_group_box)
|
||||
# Application Settings
|
||||
|
|
|
@ -100,7 +100,7 @@ class SystemPlayer(MediaPlayer):
|
|||
ext = '*%s' % extension
|
||||
if ext not in mime_type_list:
|
||||
mime_type_list.append(ext)
|
||||
log.info('MediaPlugin: %s extensions: %s' % (mimetype, ' '.join(extensions)))
|
||||
log.info('MediaPlugin: %s extensions: %s', mimetype, ' '.join(extensions))
|
||||
|
||||
def setup(self, display):
|
||||
"""
|
||||
|
@ -160,6 +160,13 @@ class SystemPlayer(MediaPlayer):
|
|||
if start_time > 0:
|
||||
self.seek(display, controller.media_info.start_time * 1000)
|
||||
self.volume(display, controller.media_info.volume)
|
||||
display.media_player.blockSignals(True)
|
||||
try:
|
||||
display.media_player.durationChanged.disconnect()
|
||||
except TypeError:
|
||||
# We get a type error if there are no slots attached to this signal, so ignore it
|
||||
pass
|
||||
display.media_player.blockSignals(False)
|
||||
display.media_player.durationChanged.connect(functools.partial(self.set_duration, controller))
|
||||
self.state = MediaState.Playing
|
||||
display.video_widget.raise_()
|
||||
|
@ -178,7 +185,11 @@ class SystemPlayer(MediaPlayer):
|
|||
Stop the current media item
|
||||
"""
|
||||
display.media_player.blockSignals(True)
|
||||
display.media_player.durationChanged.disconnect()
|
||||
try:
|
||||
display.media_player.durationChanged.disconnect()
|
||||
except TypeError:
|
||||
# We get a type error if there are no slots attached to this signal, so ignore it
|
||||
pass
|
||||
display.media_player.blockSignals(False)
|
||||
display.media_player.stop()
|
||||
self.set_visible(display, False)
|
||||
|
@ -216,6 +227,9 @@ class SystemPlayer(MediaPlayer):
|
|||
|
||||
@staticmethod
|
||||
def set_duration(controller, duration):
|
||||
"""
|
||||
Set the length of the seek slider
|
||||
"""
|
||||
controller.media_info.length = int(duration / 1000)
|
||||
controller.seek_slider.setMaximum(controller.media_info.length * 1000)
|
||||
|
||||
|
@ -261,33 +275,34 @@ class SystemPlayer(MediaPlayer):
|
|||
:return: True if file can be played otherwise False
|
||||
"""
|
||||
thread = QtCore.QThread()
|
||||
check_media_player = CheckMedia(path)
|
||||
check_media_player.setVolume(0)
|
||||
check_media_player.moveToThread(thread)
|
||||
check_media_player.finished.connect(thread.quit)
|
||||
thread.started.connect(check_media_player.play)
|
||||
check_media_worker = CheckMediaWorker(path)
|
||||
check_media_worker.setVolume(0)
|
||||
check_media_worker.moveToThread(thread)
|
||||
check_media_worker.finished.connect(thread.quit)
|
||||
thread.started.connect(check_media_worker.play)
|
||||
thread.start()
|
||||
while thread.isRunning():
|
||||
self.application.processEvents()
|
||||
return check_media_player.result
|
||||
return check_media_worker.result
|
||||
|
||||
|
||||
class CheckMedia(QtMultimedia.QMediaPlayer):
|
||||
class CheckMediaWorker(QtMultimedia.QMediaPlayer):
|
||||
"""
|
||||
Class used to check if a media file is playable
|
||||
"""
|
||||
finished = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self, path):
|
||||
super(CheckMedia, self).__init__(None, QtMultimedia.QMediaPlayer.VideoSurface)
|
||||
super(CheckMediaWorker, self).__init__(None, QtMultimedia.QMediaPlayer.VideoSurface)
|
||||
self.result = None
|
||||
|
||||
self.error.connect(functools.partial(self.signals, 'error'))
|
||||
self.mediaStatusChanged.connect(functools.partial(self.signals, 'media'))
|
||||
|
||||
self.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(path)))
|
||||
|
||||
def signals(self, origin, status):
|
||||
"""
|
||||
Exit the worker and stop the thread when either an error or a media change is encountered
|
||||
"""
|
||||
if origin == 'media' and status == self.BufferedMedia:
|
||||
self.result = True
|
||||
self.stop()
|
||||
|
|
|
@ -20,24 +20,21 @@
|
|||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
:mod: `openlp.core.ui.projector.sourceselectform` module
|
||||
:mod: `openlp.core.ui.projector.sourceselectform` module
|
||||
|
||||
Provides the dialog window for selecting video source for projector.
|
||||
Provides the dialog window for selecting video source for projector.
|
||||
"""
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
log.debug('editform loaded')
|
||||
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
from PyQt5.QtCore import pyqtSlot, QSize
|
||||
from PyQt5.QtWidgets import QDialog, QButtonGroup, QDialogButtonBox, QFormLayout, QLineEdit, QRadioButton, \
|
||||
QStyle, QStylePainter, QStyleOptionTab, QTabBar, QTabWidget, QVBoxLayout, QWidget
|
||||
|
||||
from openlp.core.common import translate, is_macosx
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.projector.db import ProjectorSource
|
||||
from openlp.core.lib.projector.constants import PJLINK_DEFAULT_SOURCES, PJLINK_DEFAULT_CODES
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def source_group(inputs, source_text):
|
||||
"""
|
||||
|
@ -104,8 +101,8 @@ def Build_Tab(group, source_key, default, projector, projectordb, edit=False):
|
|||
:param edit: If we're editing the source text
|
||||
"""
|
||||
buttonchecked = False
|
||||
widget = QWidget()
|
||||
layout = QFormLayout() if edit else QVBoxLayout()
|
||||
widget = QtWidgets.QWidget()
|
||||
layout = QtWidgets.QFormLayout() if edit else QtWidgets.QVBoxLayout()
|
||||
layout.setSpacing(10)
|
||||
widget.setLayout(layout)
|
||||
tempkey = list(source_key.keys())[0] # Should only be 1 key
|
||||
|
@ -114,7 +111,7 @@ def Build_Tab(group, source_key, default, projector, projectordb, edit=False):
|
|||
button_count = len(sourcelist)
|
||||
if edit:
|
||||
for key in sourcelist:
|
||||
item = QLineEdit()
|
||||
item = QtWidgets.QLineEdit()
|
||||
item.setObjectName('source_key_%s' % key)
|
||||
source_item = projectordb.get_source_by_code(code=key, projector_id=projector.db_item.id)
|
||||
if source_item is None:
|
||||
|
@ -130,7 +127,7 @@ def Build_Tab(group, source_key, default, projector, projectordb, edit=False):
|
|||
text = source_key[tempkey][key]
|
||||
else:
|
||||
text = source_item.text
|
||||
itemwidget = QRadioButton(text)
|
||||
itemwidget = QtWidgets.QRadioButton(text)
|
||||
itemwidget.setAutoExclusive(True)
|
||||
if default == key:
|
||||
itemwidget.setChecked(True)
|
||||
|
@ -141,30 +138,30 @@ def Build_Tab(group, source_key, default, projector, projectordb, edit=False):
|
|||
return widget, button_count, buttonchecked
|
||||
|
||||
|
||||
def set_button_tooltip(bar):
|
||||
def set_button_tooltip(button_bar):
|
||||
"""
|
||||
Set the toolip for the standard buttons used
|
||||
|
||||
:param bar: QDialogButtonBar instance to update
|
||||
"""
|
||||
for button in bar.buttons():
|
||||
if bar.standardButton(button) == QDialogButtonBox.Cancel:
|
||||
for button in button_bar.buttons():
|
||||
if button_bar.standardButton(button) == QtWidgets.QDialogButtonBox.Cancel:
|
||||
button.setToolTip(translate('OpenLP.SourceSelectForm',
|
||||
'Ignoring current changes and return to OpenLP'))
|
||||
elif bar.standardButton(button) == QDialogButtonBox.Reset:
|
||||
elif button_bar.standardButton(button) == QtWidgets.QDialogButtonBox.Reset:
|
||||
button.setToolTip(translate('OpenLP.SourceSelectForm',
|
||||
'Delete all user-defined text and revert to PJLink default text'))
|
||||
elif bar.standardButton(button) == QDialogButtonBox.Discard:
|
||||
elif button_bar.standardButton(button) == QtWidgets.QDialogButtonBox.Discard:
|
||||
button.setToolTip(translate('OpenLP.SourceSelectForm',
|
||||
'Discard changes and reset to previous user-defined text'))
|
||||
elif bar.standardButton(button) == QDialogButtonBox.Ok:
|
||||
elif button_bar.standardButton(button) == QtWidgets.QDialogButtonBox.Ok:
|
||||
button.setToolTip(translate('OpenLP.SourceSelectForm',
|
||||
'Save changes and return to OpenLP'))
|
||||
else:
|
||||
log.debug('No tooltip for button {}'.format(button.text()))
|
||||
log.debug('No tooltip for button %s', button.text())
|
||||
|
||||
|
||||
class FingerTabBarWidget(QTabBar):
|
||||
class FingerTabBarWidget(QtWidgets.QTabBar):
|
||||
"""
|
||||
Realign west -orientation tabs to left-right text rather than south-north text
|
||||
Borrowed from
|
||||
|
@ -177,8 +174,8 @@ class FingerTabBarWidget(QTabBar):
|
|||
:param width: Remove default width parameter in kwargs
|
||||
:param height: Remove default height parameter in kwargs
|
||||
"""
|
||||
self.tabSize = QSize(kwargs.pop('width', 100), kwargs.pop('height', 25))
|
||||
QTabBar.__init__(self, parent, *args, **kwargs)
|
||||
self.tabSize = QtWidgets.QSize(kwargs.pop('width', 100), kwargs.pop('height', 25))
|
||||
QtWidgets.QTabBar.__init__(self, parent, *args, **kwargs)
|
||||
|
||||
def paintEvent(self, event):
|
||||
"""
|
||||
|
@ -186,14 +183,14 @@ class FingerTabBarWidget(QTabBar):
|
|||
|
||||
:param event: Repaint event signal
|
||||
"""
|
||||
painter = QStylePainter(self)
|
||||
option = QStyleOptionTab()
|
||||
painter = QtWidgets.QStylePainter(self)
|
||||
option = QtWidgets.QStyleOptionTab()
|
||||
|
||||
for index in range(self.count()):
|
||||
self.initStyleOption(option, index)
|
||||
tabRect = self.tabRect(index)
|
||||
tabRect.moveLeft(10)
|
||||
painter.drawControl(QStyle.CE_TabBarTabShape, option)
|
||||
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabShape, option)
|
||||
painter.drawText(tabRect, QtCore.Qt.AlignVCenter |
|
||||
QtCore.Qt.TextDontClip,
|
||||
self.tabText(index))
|
||||
|
@ -209,7 +206,7 @@ class FingerTabBarWidget(QTabBar):
|
|||
return self.tabSize
|
||||
|
||||
|
||||
class FingerTabWidget(QTabWidget):
|
||||
class FingerTabWidget(QtWidgets.QTabWidget):
|
||||
"""
|
||||
A QTabWidget equivalent which uses our FingerTabBarWidget
|
||||
|
||||
|
@ -220,11 +217,11 @@ class FingerTabWidget(QTabWidget):
|
|||
"""
|
||||
Initialize FingerTabWidget instance
|
||||
"""
|
||||
QTabWidget.__init__(self, parent, *args)
|
||||
QtWidgets.QTabWidget.__init__(self, parent, *args)
|
||||
self.setTabBar(FingerTabBarWidget(self))
|
||||
|
||||
|
||||
class SourceSelectTabs(QDialog):
|
||||
class SourceSelectTabs(QtWidgets.QDialog):
|
||||
"""
|
||||
Class for handling selecting the source for the projector to use.
|
||||
Uses tabbed interface.
|
||||
|
@ -248,18 +245,18 @@ class SourceSelectTabs(QDialog):
|
|||
self.setObjectName('source_select_tabs')
|
||||
self.setWindowIcon(build_icon(':/icon/openlp-log-32x32.png'))
|
||||
self.setModal(True)
|
||||
self.layout = QVBoxLayout()
|
||||
self.layout = QtWidgets.QVBoxLayout()
|
||||
self.layout.setObjectName('source_select_tabs_layout')
|
||||
if is_macosx():
|
||||
self.tabwidget = QTabWidget(self)
|
||||
self.tabwidget = QtWidgets.QTabWidget(self)
|
||||
else:
|
||||
self.tabwidget = FingerTabWidget(self)
|
||||
self.tabwidget.setObjectName('source_select_tabs_tabwidget')
|
||||
self.tabwidget.setUsesScrollButtons(False)
|
||||
if is_macosx():
|
||||
self.tabwidget.setTabPosition(QTabWidget.North)
|
||||
self.tabwidget.setTabPosition(QtWidgets.QTabWidget.North)
|
||||
else:
|
||||
self.tabwidget.setTabPosition(QTabWidget.West)
|
||||
self.tabwidget.setTabPosition(QtWidgets.QTabWidget.West)
|
||||
self.layout.addWidget(self.tabwidget)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
|
@ -273,7 +270,7 @@ class SourceSelectTabs(QDialog):
|
|||
self.source_text = self.projectordb.get_source_list(projector=projector)
|
||||
self.source_group = source_group(projector.source_available, self.source_text)
|
||||
# self.source_group = {'4': {'41': 'Storage 1'}, '5': {"51": 'Network 1'}}
|
||||
self.button_group = [] if self.edit else QButtonGroup()
|
||||
self.button_group = [] if self.edit else QtWidgets.QButtonGroup()
|
||||
keys = list(self.source_group.keys())
|
||||
keys.sort()
|
||||
if self.edit:
|
||||
|
@ -287,10 +284,10 @@ class SourceSelectTabs(QDialog):
|
|||
thistab = self.tabwidget.addTab(tab, PJLINK_DEFAULT_SOURCES[key])
|
||||
if buttonchecked:
|
||||
self.tabwidget.setCurrentIndex(thistab)
|
||||
self.button_box = QDialogButtonBox(QtWidgets.QDialogButtonBox.Reset |
|
||||
QtWidgets.QDialogButtonBox.Discard |
|
||||
QtWidgets.QDialogButtonBox.Ok |
|
||||
QtWidgets.QDialogButtonBox.Cancel)
|
||||
self.button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Reset |
|
||||
QtWidgets.QDialogButtonBox.Discard |
|
||||
QtWidgets.QDialogButtonBox.Ok |
|
||||
QtWidgets.QDialogButtonBox.Cancel)
|
||||
else:
|
||||
for key in keys:
|
||||
(tab, button_count, buttonchecked) = Build_Tab(group=self.button_group,
|
||||
|
@ -302,15 +299,15 @@ class SourceSelectTabs(QDialog):
|
|||
thistab = self.tabwidget.addTab(tab, PJLINK_DEFAULT_SOURCES[key])
|
||||
if buttonchecked:
|
||||
self.tabwidget.setCurrentIndex(thistab)
|
||||
self.button_box = QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok |
|
||||
QtWidgets.QDialogButtonBox.Cancel)
|
||||
self.button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok |
|
||||
QtWidgets.QDialogButtonBox.Cancel)
|
||||
self.button_box.clicked.connect(self.button_clicked)
|
||||
self.layout.addWidget(self.button_box)
|
||||
set_button_tooltip(self.button_box)
|
||||
selected = super(SourceSelectTabs, self).exec()
|
||||
return selected
|
||||
|
||||
@pyqtSlot(object)
|
||||
@QtCore.pyqtSlot(QtWidgets.QAbstractButton)
|
||||
def button_clicked(self, button):
|
||||
"""
|
||||
Checks which button was clicked
|
||||
|
@ -333,6 +330,9 @@ class SourceSelectTabs(QDialog):
|
|||
return 100
|
||||
|
||||
def delete_sources(self):
|
||||
"""
|
||||
Delete the source
|
||||
"""
|
||||
msg = QtWidgets.QMessageBox()
|
||||
msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector'))
|
||||
msg.setInformativeText(translate('OpenLP.SourceSelectForm',
|
||||
|
@ -359,20 +359,20 @@ class SourceSelectTabs(QDialog):
|
|||
continue
|
||||
item = self.projectordb.get_source_by_code(code=code, projector_id=projector.id)
|
||||
if item is None:
|
||||
log.debug("(%s) Adding new source text %s: %s" % (projector.ip, code, text))
|
||||
log.debug("(%s) Adding new source text %s: %s", projector.ip, code, text)
|
||||
item = ProjectorSource(projector_id=projector.id, code=code, text=text)
|
||||
else:
|
||||
item.text = text
|
||||
log.debug('(%s) Updating source code %s with text="%s"' % (projector.ip, item.code, item.text))
|
||||
log.debug('(%s) Updating source code %s with text="%s"', projector.ip, item.code, item.text)
|
||||
self.projectordb.add_source(item)
|
||||
selected = 0
|
||||
else:
|
||||
selected = self.button_group.checkedId()
|
||||
log.debug('SourceSelectTabs().accepted() Setting source to %s' % selected)
|
||||
log.debug('SourceSelectTabs().accepted() Setting source to %s', selected)
|
||||
self.done(selected)
|
||||
|
||||
|
||||
class SourceSelectSingle(QDialog):
|
||||
class SourceSelectSingle(QtWidgets.QDialog):
|
||||
"""
|
||||
Class for handling selecting the source for the projector to use.
|
||||
Uses single dialog interface.
|
||||
|
@ -393,6 +393,7 @@ class SourceSelectSingle(QDialog):
|
|||
title = translate('OpenLP.SourceSelectForm', 'Select Projector Source')
|
||||
self.setObjectName('source_select_single')
|
||||
self.setWindowIcon(build_icon(':/icon/openlp-log-32x32.png'))
|
||||
self.setWindowTitle(title)
|
||||
self.setModal(True)
|
||||
self.edit = edit
|
||||
|
||||
|
@ -403,12 +404,12 @@ class SourceSelectSingle(QDialog):
|
|||
:param projector: Projector instance to build source list from
|
||||
"""
|
||||
self.projector = projector
|
||||
self.layout = QFormLayout() if self.edit else QVBoxLayout()
|
||||
self.layout = QtWidgets.QFormLayout() if self.edit else QtWidgets.QVBoxLayout()
|
||||
self.layout.setObjectName('source_select_tabs_layout')
|
||||
self.layout.setSpacing(10)
|
||||
self.setLayout(self.layout)
|
||||
self.setMinimumWidth(350)
|
||||
self.button_group = [] if self.edit else QButtonGroup()
|
||||
self.button_group = [] if self.edit else QtWidgets.QButtonGroup()
|
||||
self.source_text = self.projectordb.get_source_list(projector=projector)
|
||||
keys = list(self.source_text.keys())
|
||||
keys.sort()
|
||||
|
@ -416,7 +417,7 @@ class SourceSelectSingle(QDialog):
|
|||
button_list = []
|
||||
if self.edit:
|
||||
for key in keys:
|
||||
item = QLineEdit()
|
||||
item = QtWidgets.QLineEdit()
|
||||
item.setObjectName('source_key_%s' % key)
|
||||
source_item = self.projectordb.get_source_by_code(code=key, projector_id=self.projector.db_item.id)
|
||||
if source_item is None:
|
||||
|
@ -426,10 +427,10 @@ class SourceSelectSingle(QDialog):
|
|||
item.setText(source_item.text)
|
||||
self.layout.addRow(PJLINK_DEFAULT_CODES[key], item)
|
||||
self.button_group.append(item)
|
||||
self.button_box = QDialogButtonBox(QtWidgets.QDialogButtonBox.Reset |
|
||||
QtWidgets.QDialogButtonBox.Discard |
|
||||
QtWidgets.QDialogButtonBox.Ok |
|
||||
QtWidgets.QDialogButtonBox.Cancel)
|
||||
self.button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Reset |
|
||||
QtWidgets.QDialogButtonBox.Discard |
|
||||
QtWidgets.QDialogButtonBox.Ok |
|
||||
QtWidgets.QDialogButtonBox.Cancel)
|
||||
else:
|
||||
for key in keys:
|
||||
source_text = self.projectordb.get_source_by_code(code=key, projector_id=self.projector.db_item.id)
|
||||
|
@ -439,8 +440,8 @@ class SourceSelectSingle(QDialog):
|
|||
self.layout.addWidget(button)
|
||||
self.button_group.addButton(button, int(key))
|
||||
button_list.append(key)
|
||||
self.button_box = QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok |
|
||||
QtWidgets.QDialogButtonBox.Cancel)
|
||||
self.button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok |
|
||||
QtWidgets.QDialogButtonBox.Cancel)
|
||||
self.button_box.clicked.connect(self.button_clicked)
|
||||
self.layout.addWidget(self.button_box)
|
||||
self.setMinimumHeight(key_count * 25)
|
||||
|
@ -448,7 +449,7 @@ class SourceSelectSingle(QDialog):
|
|||
selected = super(SourceSelectSingle, self).exec()
|
||||
return selected
|
||||
|
||||
@pyqtSlot(object)
|
||||
@QtCore.pyqtSlot(QtWidgets.QAbstractButton)
|
||||
def button_clicked(self, button):
|
||||
"""
|
||||
Checks which button was clicked
|
||||
|
@ -471,6 +472,9 @@ class SourceSelectSingle(QDialog):
|
|||
return 100
|
||||
|
||||
def delete_sources(self):
|
||||
"""
|
||||
Delete all the entries for this projector
|
||||
"""
|
||||
msg = QtWidgets.QMessageBox()
|
||||
msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector'))
|
||||
msg.setInformativeText(translate('OpenLP.SourceSelectForm',
|
||||
|
@ -484,7 +488,7 @@ class SourceSelectSingle(QDialog):
|
|||
self.projectordb.delete_all_objects(ProjectorSource, ProjectorSource.projector_id == self.projector.db_item.id)
|
||||
self.done(100)
|
||||
|
||||
@pyqtSlot()
|
||||
@QtCore.pyqtSlot()
|
||||
def accept_me(self):
|
||||
"""
|
||||
Slot to accept 'OK' button
|
||||
|
@ -498,14 +502,14 @@ class SourceSelectSingle(QDialog):
|
|||
continue
|
||||
item = self.projectordb.get_source_by_code(code=code, projector_id=projector.id)
|
||||
if item is None:
|
||||
log.debug("(%s) Adding new source text %s: %s" % (projector.ip, code, text))
|
||||
log.debug('(%s) Adding new source text %s: %s', projector.ip, code, text)
|
||||
item = ProjectorSource(projector_id=projector.id, code=code, text=text)
|
||||
else:
|
||||
item.text = text
|
||||
log.debug('(%s) Updating source code %s with text="%s"' % (projector.ip, item.code, item.text))
|
||||
log.debug('(%s) Updating source code %s with text="%s"', projector.ip, item.code, item.text)
|
||||
self.projectordb.add_source(item)
|
||||
selected = 0
|
||||
else:
|
||||
selected = self.button_group.checkedId()
|
||||
log.debug('SourceSelectDialog().accepted() Setting source to %s' % selected)
|
||||
log.debug('SourceSelectDialog().accepted() Setting source to %s', selected)
|
||||
self.done(selected)
|
||||
|
|
|
@ -594,7 +594,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
|
|||
if success:
|
||||
try:
|
||||
shutil.copy(temp_file_name, path_file_name)
|
||||
except shutil.Error:
|
||||
except (shutil.Error, PermissionError):
|
||||
return self.save_file_as()
|
||||
except OSError as ose:
|
||||
QtWidgets.QMessageBox.critical(self, translate('OpenLP.ServiceManager', 'Error Saving File'),
|
||||
|
@ -655,7 +655,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
|
|||
if success:
|
||||
try:
|
||||
shutil.copy(temp_file_name, path_file_name)
|
||||
except shutil.Error:
|
||||
except (shutil.Error, PermissionError):
|
||||
return self.save_file_as()
|
||||
self.main_window.add_recent_file(path_file_name)
|
||||
self.set_modified(False)
|
||||
|
@ -1321,7 +1321,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
|
|||
"""
|
||||
The theme may have changed in the settings dialog so make sure the theme combo box is in the correct state.
|
||||
"""
|
||||
visible = self.renderer.theme_level == ThemeLevel.Global
|
||||
visible = self.renderer.theme_level != ThemeLevel.Global
|
||||
self.toolbar.actions['theme_combo_box'].setVisible(visible)
|
||||
self.toolbar.actions['theme_label'].setVisible(visible)
|
||||
self.regenerate_service_items()
|
||||
|
|
|
@ -114,7 +114,7 @@ class CategoryActionList(object):
|
|||
if item[1] == action:
|
||||
self.actions.remove(item)
|
||||
return
|
||||
raise ValueError('Action "%s" does not exist.' % action)
|
||||
raise ValueError('Action "%s" does not exist.' % action[0])
|
||||
|
||||
|
||||
class CategoryList(object):
|
||||
|
|
|
@ -248,7 +248,7 @@ class BGExtract(RegistryProperties):
|
|||
url_book_name = urllib.parse.quote(book_name.encode("utf-8"))
|
||||
url_params = 'search=%s+%s&version=%s' % (url_book_name, chapter, version)
|
||||
soup = get_soup_for_bible_ref(
|
||||
'http://legacy.biblegateway.com/passage/?%s' % url_params,
|
||||
'http://biblegateway.com/passage/?%s' % url_params,
|
||||
pre_parse_regex=r'<meta name.*?/>', pre_parse_substitute='')
|
||||
if not soup:
|
||||
return None
|
||||
|
@ -277,7 +277,7 @@ class BGExtract(RegistryProperties):
|
|||
"""
|
||||
log.debug('BGExtract.get_books_from_http("%s")', version)
|
||||
url_params = urllib.parse.urlencode({'action': 'getVersionInfo', 'vid': '%s' % version})
|
||||
reference_url = 'http://legacy.biblegateway.com/versions/?%s#books' % url_params
|
||||
reference_url = 'http://biblegateway.com/versions/?%s#books' % url_params
|
||||
page = get_web_page(reference_url)
|
||||
if not page:
|
||||
send_error_message('download')
|
||||
|
@ -308,7 +308,7 @@ class BGExtract(RegistryProperties):
|
|||
for book in content:
|
||||
book = book.find('td')
|
||||
if book:
|
||||
books.append(book.contents[0])
|
||||
books.append(book.contents[1])
|
||||
return books
|
||||
|
||||
def get_bibles_from_http(self):
|
||||
|
@ -318,11 +318,11 @@ class BGExtract(RegistryProperties):
|
|||
returns a list in the form [(biblename, biblekey, language_code)]
|
||||
"""
|
||||
log.debug('BGExtract.get_bibles_from_http')
|
||||
bible_url = 'https://legacy.biblegateway.com/versions/'
|
||||
bible_url = 'https://biblegateway.com/versions/'
|
||||
soup = get_soup_for_bible_ref(bible_url)
|
||||
if not soup:
|
||||
return None
|
||||
bible_select = soup.find('select', {'class': 'translation-dropdown'})
|
||||
bible_select = soup.find('select', {'class': 'search-translation-select'})
|
||||
if not bible_select:
|
||||
log.debug('No select tags found - did site change?')
|
||||
return None
|
||||
|
@ -480,7 +480,7 @@ class CWExtract(RegistryProperties):
|
|||
for verse in verses_div:
|
||||
self.application.process_events()
|
||||
verse_number = int(verse.find('strong').contents[0])
|
||||
verse_span = verse.find('span')
|
||||
verse_span = verse.find('span', class_='verse-%d' % verse_number)
|
||||
tags_to_remove = verse_span.find_all(['a', 'sup'])
|
||||
for tag in tags_to_remove:
|
||||
tag.decompose()
|
||||
|
@ -520,28 +520,26 @@ class CWExtract(RegistryProperties):
|
|||
returns a list in the form [(biblename, biblekey, language_code)]
|
||||
"""
|
||||
log.debug('CWExtract.get_bibles_from_http')
|
||||
bible_url = 'http://www.biblestudytools.com/'
|
||||
bible_url = 'http://www.biblestudytools.com/bible-versions/'
|
||||
soup = get_soup_for_bible_ref(bible_url)
|
||||
if not soup:
|
||||
return None
|
||||
bible_select = soup.find('select')
|
||||
if not bible_select:
|
||||
log.debug('No select tags found - did site change?')
|
||||
return None
|
||||
option_tags = bible_select.find_all('option', {'class': 'log-translation'})
|
||||
if not option_tags:
|
||||
log.debug('No option tags found - did site change?')
|
||||
h4_tags = soup.find_all('h4', {'class': 'small-header'})
|
||||
if not h4_tags:
|
||||
log.debug('No h4 tags found - did site change?')
|
||||
return None
|
||||
bibles = []
|
||||
for ot in option_tags:
|
||||
tag_text = ot.get_text().strip()
|
||||
try:
|
||||
tag_value = ot['value']
|
||||
except KeyError:
|
||||
log.exception('No value attribute found - did site change?')
|
||||
for h4t in h4_tags:
|
||||
short_name = None
|
||||
if h4t.span:
|
||||
short_name = h4t.span.get_text().strip().lower()
|
||||
else:
|
||||
log.error('No span tag found - did site change?')
|
||||
return None
|
||||
if not tag_value:
|
||||
if not short_name:
|
||||
continue
|
||||
h4t.span.extract()
|
||||
tag_text = h4t.get_text().strip()
|
||||
# The names of non-english bibles has their language in parentheses at the end
|
||||
if tag_text.endswith(')'):
|
||||
language = tag_text[tag_text.rfind('(') + 1:-1]
|
||||
|
@ -549,12 +547,20 @@ class CWExtract(RegistryProperties):
|
|||
language_code = CROSSWALK_LANGUAGES[language]
|
||||
else:
|
||||
language_code = ''
|
||||
# ... except for the latin vulgate
|
||||
# ... except for those that don't...
|
||||
elif 'latin' in tag_text.lower():
|
||||
language_code = 'la'
|
||||
elif 'la biblia' in tag_text.lower() or 'nueva' in tag_text.lower():
|
||||
language_code = 'es'
|
||||
elif 'chinese' in tag_text.lower():
|
||||
language_code = 'zh'
|
||||
elif 'greek' in tag_text.lower():
|
||||
language_code = 'el'
|
||||
elif 'nova' in tag_text.lower():
|
||||
language_code = 'pt'
|
||||
else:
|
||||
language_code = 'en'
|
||||
bibles.append((tag_text, tag_value, language_code))
|
||||
bibles.append((tag_text, short_name, language_code))
|
||||
return bibles
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -32,9 +32,8 @@ from PyQt5 import QtWidgets
|
|||
from openlp.core.common import AppLocation
|
||||
from openlp.core.lib import translate
|
||||
from openlp.core.utils import CONTROL_CHARS
|
||||
from openlp.plugins.songs.lib.db import MediaFile, Song
|
||||
from .db import Author
|
||||
from .ui import SongStrings
|
||||
from openlp.plugins.songs.lib.db import Author, MediaFile, Song, Topic
|
||||
from openlp.plugins.songs.lib.ui import SongStrings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ class Song(BaseModel):
|
|||
|
||||
def add_author(self, author, author_type=None):
|
||||
"""
|
||||
Add an author to the song if it not yet exists
|
||||
Add an author to the song if it doesn't exist yet
|
||||
|
||||
:param author: Author object
|
||||
:param author_type: AuthorType constant or None
|
||||
|
@ -162,7 +162,7 @@ class Song(BaseModel):
|
|||
|
||||
def add_songbook_entry(self, songbook, entry):
|
||||
"""
|
||||
Add a Songbook Entry to the song if it not yet exists
|
||||
Add a Songbook Entry to the song if it doesn't exist yet
|
||||
|
||||
:param songbook_name: Name of the Songbook.
|
||||
:param entry: Entry in the Songbook (usually a number)
|
||||
|
|
|
@ -93,7 +93,7 @@ class MediaShoutImport(SongImport):
|
|||
self.song_book_name = song.SongID
|
||||
for verse in verses:
|
||||
tag = VERSE_TAGS[verse.Type] + str(verse.Number) if verse.Type < len(VERSE_TAGS) else 'O'
|
||||
self.add_verse(verse.Text, tag)
|
||||
self.add_verse(self.tidy_text(verse.Text), tag)
|
||||
for order in verse_order:
|
||||
if order.Type < len(VERSE_TAGS):
|
||||
self.verse_order_list.append(VERSE_TAGS[order.Type] + str(order.Number))
|
||||
|
|
|
@ -140,10 +140,13 @@ class SongImport(QtCore.QObject):
|
|||
text = text.replace('\u2026', '...')
|
||||
text = text.replace('\u2013', '-')
|
||||
text = text.replace('\u2014', '-')
|
||||
# Replace vertical tab with 2 linebreaks
|
||||
text = text.replace('\v', '\n\n')
|
||||
# Replace form feed (page break) with 2 linebreaks
|
||||
text = text.replace('\f', '\n\n')
|
||||
# Remove surplus blank lines, spaces, trailing/leading spaces
|
||||
text = re.sub(r'[ \t\v]+', ' ', text)
|
||||
text = re.sub(r'[ \t]+', ' ', text)
|
||||
text = re.sub(r' ?(\r\n?|\n) ?', '\n', text)
|
||||
text = re.sub(r' ?(\n{5}|\f)+ ?', '\f', text)
|
||||
return text
|
||||
|
||||
def process_song_text(self, text):
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
The :mod:`~openlp.plugins.songs.lib.songselect` module contains the SongSelect importer itself.
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
import random
|
||||
import re
|
||||
from http.cookiejar import CookieJar
|
||||
from urllib.parse import urlencode
|
||||
from urllib.request import HTTPCookieProcessor, URLError, build_opener
|
||||
|
@ -32,14 +33,21 @@ from html import unescape
|
|||
|
||||
from bs4 import BeautifulSoup, NavigableString
|
||||
|
||||
from openlp.plugins.songs.lib import Song, VerseType, clean_song, Author
|
||||
from openlp.plugins.songs.lib import Song, Author, Topic, VerseType, clean_song
|
||||
from openlp.plugins.songs.lib.openlyricsxml import SongXML
|
||||
|
||||
USER_AGENT = 'Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; GT-I9000 ' \
|
||||
'Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 ' \
|
||||
'Mobile Safari/534.30'
|
||||
BASE_URL = 'https://mobile.songselect.com'
|
||||
LOGIN_URL = BASE_URL + '/account/login'
|
||||
USER_AGENTS = [
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) '
|
||||
'Chrome/52.0.2743.116 Safari/537.36',
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',
|
||||
'Mozilla/5.0 (X11; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0',
|
||||
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:46.0) Gecko/20100101 Firefox/46.0',
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:47.0) Gecko/20100101 Firefox/47.0'
|
||||
]
|
||||
BASE_URL = 'https://songselect.ccli.com'
|
||||
LOGIN_PAGE = 'https://profile.ccli.com/account/signin?appContext=SongSelect&returnUrl=' \
|
||||
'https%3a%2f%2fsongselect.ccli.com%2f'
|
||||
LOGIN_URL = 'https://profile.ccli.com/'
|
||||
LOGOUT_URL = BASE_URL + '/account/logout'
|
||||
SEARCH_URL = BASE_URL + '/search/results'
|
||||
|
||||
|
@ -60,7 +68,7 @@ class SongSelectImport(object):
|
|||
self.db_manager = db_manager
|
||||
self.html_parser = HTMLParser()
|
||||
self.opener = build_opener(HTTPCookieProcessor(CookieJar()))
|
||||
self.opener.addheaders = [('User-Agent', USER_AGENT)]
|
||||
self.opener.addheaders = [('User-Agent', random.choice(USER_AGENTS))]
|
||||
self.run_search = True
|
||||
|
||||
def login(self, username, password, callback=None):
|
||||
|
@ -76,27 +84,27 @@ class SongSelectImport(object):
|
|||
if callback:
|
||||
callback()
|
||||
try:
|
||||
login_page = BeautifulSoup(self.opener.open(LOGIN_URL).read(), 'lxml')
|
||||
except (TypeError, URLError) as e:
|
||||
log.exception('Could not login to SongSelect, %s', e)
|
||||
login_page = BeautifulSoup(self.opener.open(LOGIN_PAGE).read(), 'lxml')
|
||||
except (TypeError, URLError) as error:
|
||||
log.exception('Could not login to SongSelect, %s', error)
|
||||
return False
|
||||
if callback:
|
||||
callback()
|
||||
token_input = login_page.find('input', attrs={'name': '__RequestVerificationToken'})
|
||||
data = urlencode({
|
||||
'__RequestVerificationToken': token_input['value'],
|
||||
'UserName': username,
|
||||
'Password': password,
|
||||
'emailAddress': username,
|
||||
'password': password,
|
||||
'RememberMe': 'false'
|
||||
})
|
||||
try:
|
||||
posted_page = BeautifulSoup(self.opener.open(LOGIN_URL, data.encode('utf-8')).read(), 'lxml')
|
||||
except (TypeError, URLError) as e:
|
||||
log.exception('Could not login to SongSelect, %s', e)
|
||||
except (TypeError, URLError) as error:
|
||||
log.exception('Could not login to SongSelect, %s', error)
|
||||
return False
|
||||
if callback:
|
||||
callback()
|
||||
return not posted_page.find('input', attrs={'name': '__RequestVerificationToken'})
|
||||
return posted_page.find('input', id='SearchText') is not None
|
||||
|
||||
def logout(self):
|
||||
"""
|
||||
|
@ -104,8 +112,8 @@ class SongSelectImport(object):
|
|||
"""
|
||||
try:
|
||||
self.opener.open(LOGOUT_URL)
|
||||
except (TypeError, URLError) as e:
|
||||
log.exception('Could not log of SongSelect, %s', e)
|
||||
except (TypeError, URLError) as error:
|
||||
log.exception('Could not log out of SongSelect, %s', error)
|
||||
|
||||
def search(self, search_text, max_results, callback=None):
|
||||
"""
|
||||
|
@ -117,7 +125,15 @@ class SongSelectImport(object):
|
|||
:return: List of songs
|
||||
"""
|
||||
self.run_search = True
|
||||
params = {'allowredirect': 'false', 'SearchTerm': search_text}
|
||||
params = {
|
||||
'SongContent': '',
|
||||
'PrimaryLanguage': '',
|
||||
'Keys': '',
|
||||
'Themes': '',
|
||||
'List': '',
|
||||
'Sort': '',
|
||||
'SearchText': search_text
|
||||
}
|
||||
current_page = 1
|
||||
songs = []
|
||||
while self.run_search:
|
||||
|
@ -125,7 +141,7 @@ class SongSelectImport(object):
|
|||
params['page'] = current_page
|
||||
try:
|
||||
results_page = BeautifulSoup(self.opener.open(SEARCH_URL + '?' + urlencode(params)).read(), 'lxml')
|
||||
search_results = results_page.find_all('li', 'result pane')
|
||||
search_results = results_page.find_all('div', 'song-result')
|
||||
except (TypeError, URLError) as e:
|
||||
log.exception('Could not search SongSelect, %s', e)
|
||||
search_results = None
|
||||
|
@ -133,9 +149,9 @@ class SongSelectImport(object):
|
|||
break
|
||||
for result in search_results:
|
||||
song = {
|
||||
'title': unescape(result.find('h3').string),
|
||||
'authors': [unescape(author.string) for author in result.find_all('li')],
|
||||
'link': BASE_URL + result.find('a')['href']
|
||||
'title': unescape(result.find('p', 'song-result-title').find('a').string).strip(),
|
||||
'authors': unescape(result.find('p', 'song-result-subtitle').string).strip().split(', '),
|
||||
'link': BASE_URL + result.find('p', 'song-result-title').find('a')['href']
|
||||
}
|
||||
if callback:
|
||||
callback(song)
|
||||
|
@ -163,27 +179,37 @@ class SongSelectImport(object):
|
|||
if callback:
|
||||
callback()
|
||||
try:
|
||||
lyrics_page = BeautifulSoup(self.opener.open(song['link'] + '/lyrics').read(), 'lxml')
|
||||
lyrics_page = BeautifulSoup(self.opener.open(song['link'] + '/viewlyrics').read(), 'lxml')
|
||||
except (TypeError, URLError):
|
||||
log.exception('Could not get lyrics from SongSelect')
|
||||
return None
|
||||
if callback:
|
||||
callback()
|
||||
song['copyright'] = '/'.join([li.string for li in song_page.find('ul', 'copyright').find_all('li')])
|
||||
song['copyright'] = unescape(song['copyright'])
|
||||
song['ccli_number'] = song_page.find('ul', 'info').find('li').string.split(':')[1].strip()
|
||||
copyright_elements = []
|
||||
theme_elements = []
|
||||
copyrights_regex = re.compile(r'\bCopyrights\b')
|
||||
themes_regex = re.compile(r'\bThemes\b')
|
||||
for ul in song_page.find_all('ul', 'song-meta-list'):
|
||||
if ul.find('li', string=copyrights_regex):
|
||||
copyright_elements.extend(ul.find_all('li')[1:])
|
||||
if ul.find('li', string=themes_regex):
|
||||
theme_elements.extend(ul.find_all('li')[1:])
|
||||
song['copyright'] = '/'.join([unescape(li.string).strip() for li in copyright_elements])
|
||||
song['topics'] = [unescape(li.string).strip() for li in theme_elements]
|
||||
song['ccli_number'] = song_page.find('div', 'song-content-data').find('ul').find('li')\
|
||||
.find('strong').string.strip()
|
||||
song['verses'] = []
|
||||
verses = lyrics_page.find('section', 'lyrics').find_all('p')
|
||||
verse_labels = lyrics_page.find('section', 'lyrics').find_all('h3')
|
||||
for counter in range(len(verses)):
|
||||
verse = {'label': verse_labels[counter].string, 'lyrics': ''}
|
||||
for v in verses[counter].contents:
|
||||
verses = lyrics_page.find('div', 'song-viewer lyrics').find_all('p')
|
||||
verse_labels = lyrics_page.find('div', 'song-viewer lyrics').find_all('h3')
|
||||
for verse, label in zip(verses, verse_labels):
|
||||
song_verse = {'label': unescape(label.string).strip(), 'lyrics': ''}
|
||||
for v in verse.contents:
|
||||
if isinstance(v, NavigableString):
|
||||
verse['lyrics'] = verse['lyrics'] + v.string
|
||||
song_verse['lyrics'] += unescape(v.string).strip()
|
||||
else:
|
||||
verse['lyrics'] += '\n'
|
||||
verse['lyrics'] = verse['lyrics'].strip(' \n\r\t')
|
||||
song['verses'].append(unescape(verse))
|
||||
song_verse['lyrics'] += '\n'
|
||||
song_verse['lyrics'] = song_verse['lyrics'].strip()
|
||||
song['verses'].append(song_verse)
|
||||
for counter, author in enumerate(song['authors']):
|
||||
song['authors'][counter] = unescape(author)
|
||||
return song
|
||||
|
@ -199,7 +225,11 @@ class SongSelectImport(object):
|
|||
song_xml = SongXML()
|
||||
verse_order = []
|
||||
for verse in song['verses']:
|
||||
verse_type, verse_number = verse['label'].split(' ')[:2]
|
||||
if ' ' in verse['label']:
|
||||
verse_type, verse_number = verse['label'].split(' ', 1)
|
||||
else:
|
||||
verse_type = verse['label']
|
||||
verse_number = 1
|
||||
verse_type = VerseType.from_loose_input(verse_type)
|
||||
verse_number = int(verse_number)
|
||||
song_xml.add_verse_to_lyrics(VerseType.tags[verse_type], verse_number, verse['lyrics'])
|
||||
|
@ -220,6 +250,12 @@ class SongSelectImport(object):
|
|||
last_name = name_parts[1]
|
||||
author = Author.populate(first_name=first_name, last_name=last_name, display_name=author_name)
|
||||
db_song.add_author(author)
|
||||
db_song.topics = []
|
||||
for topic_name in song.get('topics', []):
|
||||
topic = self.db_manager.get_object_filtered(Topic, Topic.name == topic_name)
|
||||
if not topic:
|
||||
topic = Topic.populate(name=topic_name)
|
||||
db_song.topics.append(topic)
|
||||
self.db_manager.save_object(db_song)
|
||||
return db_song
|
||||
|
||||
|
|
|
@ -7860,12 +7860,12 @@ Probeer asseblief om dit individueel te kies.</translation>
|
|||
<message>
|
||||
<location filename="../../openlp/plugins/remotes/lib/httprouter.py" line="314"/>
|
||||
<source>Stage View</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Verhoog skerm</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../openlp/plugins/remotes/lib/httprouter.py" line="315"/>
|
||||
<source>Live View</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Lewendige Kykskerm</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7992,12 +7992,12 @@ Silakan coba memilih secara individu.</translation>
|
|||
<message>
|
||||
<location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="198"/>
|
||||
<source>iOS App</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Aplikasi iOS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="199"/>
|
||||
<source>Scan the QR code or click <a href="%s">download</a> to install the iOS app from the App Store.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Pindai QR Code atau klik <a href="%s">download</a> untuk meng-install aplikasi iOS dari App Store.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -8787,7 +8787,9 @@ Silakan masukkan bait-bait tersebut dan pisahkan dengan spasi.</translation>
|
|||
<source>Unable to find the following file:
|
||||
%s
|
||||
Do you want to remove the entry from the song?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Tidak dapat menemukan file berikut:
|
||||
%s
|
||||
Apa Anda ingin membuang entry ini dari lagu?</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
|
|
@ -2405,7 +2405,7 @@ Ar OpenLP turėtų naujinti dabar?</translation>
|
|||
<message>
|
||||
<location filename="../../openlp/core/ui/aboutdialog.py" line="317"/>
|
||||
<source>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 below for more details.</source>
|
||||
<translation>Ši programa platinama, tikintis, kad ji bus naudinga, tačiau BE JOKIŲ GARANTIJŲ; netgi be numanomos PARDAVIMO ar TINKAMUMO TAM TIKRAM TIKSLUI garantijos. Išsamiau apie tai, žiūrėkite žemiau.</translation>
|
||||
<translation>Ši programa yra platinama, tikintis, kad ji bus naudinga, tačiau BE JOKIŲ GARANTIJŲ; netgi be numanomos PARDAVIMO ar TINKAMUMO TAM TIKRAM TIKSLUI garantijos. Išsamiau apie tai, žiūrėkite žemiau.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../openlp/core/ui/aboutdialog.py" line="92"/>
|
||||
|
|
|
@ -7986,12 +7986,12 @@ Probeer elk bestand individueel te selecteren.</translation>
|
|||
<message>
|
||||
<location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="198"/>
|
||||
<source>iOS App</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>iOS App</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="199"/>
|
||||
<source>Scan the QR code or click <a href="%s">download</a> to install the iOS app from the App Store.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Scan de QR-code of klik op <a href="%s">download</a> om de IOS-app te installeren via App Store.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -8780,7 +8780,9 @@ Geef de verzen op gescheiden door spaties.</translation>
|
|||
<source>Unable to find the following file:
|
||||
%s
|
||||
Do you want to remove the entry from the song?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Kon het volgende bestand niet vonden:
|
||||
%s
|
||||
Wilt u het lied verwijderen uit het overzicht?</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
|
|
@ -8816,7 +8816,7 @@ Chceš vymazať tento záznam z pesničiek?</translation>
|
|||
<message>
|
||||
<location filename="../../openlp/plugins/songs/forms/songexportform.py" line="155"/>
|
||||
<source>Uncheck All</source>
|
||||
<translation>Odoznačiť všetko</translation>
|
||||
<translation>Odznačiť všetko</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../openlp/plugins/songs/forms/songexportform.py" line="156"/>
|
||||
|
@ -9689,7 +9689,7 @@ Chceš vymazať tento záznam z pesničiek?</translation>
|
|||
<message>
|
||||
<location filename="../../openlp/plugins/songs/lib/__init__.py" line="154"/>
|
||||
<source>Intro</source>
|
||||
<translation>Intro</translation>
|
||||
<translation>Predohra</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../openlp/plugins/songs/lib/__init__.py" line="155"/>
|
||||
|
|
|
@ -25,7 +25,8 @@ Test the registry properties
|
|||
from unittest import TestCase
|
||||
|
||||
from openlp.core.common import Registry, RegistryProperties
|
||||
from tests.functional import MagicMock
|
||||
|
||||
from tests.functional import MagicMock, patch
|
||||
|
||||
|
||||
class TestRegistryProperties(TestCase, RegistryProperties):
|
||||
|
@ -36,7 +37,7 @@ class TestRegistryProperties(TestCase, RegistryProperties):
|
|||
"""
|
||||
Create the Register
|
||||
"""
|
||||
Registry.create()
|
||||
self.registry = Registry.create()
|
||||
|
||||
def no_application_test(self):
|
||||
"""
|
||||
|
@ -53,7 +54,30 @@ class TestRegistryProperties(TestCase, RegistryProperties):
|
|||
"""
|
||||
# GIVEN an Empty Registry
|
||||
application = MagicMock()
|
||||
|
||||
# WHEN the application is registered
|
||||
Registry().register('application', application)
|
||||
|
||||
# THEN the application should be none
|
||||
self.assertEqual(self.application, application, 'The application value should match')
|
||||
|
||||
@patch('openlp.core.common.registryproperties.is_win')
|
||||
def test_get_application_on_windows(self, mocked_is_win):
|
||||
"""
|
||||
Set that getting the application object on Windows happens dynamically
|
||||
"""
|
||||
# GIVEN an Empty Registry and we're on Windows
|
||||
mocked_is_win.return_value = True
|
||||
mock_application = MagicMock()
|
||||
reg_props = RegistryProperties()
|
||||
registry = Registry()
|
||||
|
||||
# WHEN the application is accessed
|
||||
with patch.object(registry, 'get') as mocked_get:
|
||||
mocked_get.return_value = mock_application
|
||||
actual_application = reg_props.application
|
||||
|
||||
# THEN the application should be the mock object, and the correct function should have been called
|
||||
self.assertEqual(mock_application, actual_application, 'The application value should match')
|
||||
mocked_is_win.assert_called_with()
|
||||
mocked_get.assert_called_with('application')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
# pylint: disable=protected-access
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
@ -22,16 +22,21 @@
|
|||
"""
|
||||
Package to test the openlp.core.ui.slidecontroller package.
|
||||
"""
|
||||
import os
|
||||
from unittest import TestCase
|
||||
|
||||
from openlp.core.common import Registry, ThemeLevel
|
||||
from openlp.core.lib import ServiceItem, ServiceItemType, ItemCapabilities
|
||||
from openlp.core.lib.toolbar import OpenLPToolbar
|
||||
from openlp.core.ui import ServiceManager
|
||||
|
||||
from tests.functional import MagicMock
|
||||
from tests.functional import MagicMock, patch
|
||||
|
||||
|
||||
class TestServiceManager(TestCase):
|
||||
"""
|
||||
Test the service manager
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
|
@ -107,20 +112,20 @@ class TestServiceManager(TestCase):
|
|||
# WHEN I define a context menu
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following calls should have occurred.
|
||||
self.assertEquals(service_manager.edit_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEquals(service_manager.rename_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEquals(service_manager.maintain_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have been called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have been called once')
|
||||
self.assertEqual(service_manager.edit_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEqual(service_manager.rename_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEqual(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEqual(service_manager.maintain_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEqual(service_manager.notes_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEqual(service_manager.time_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEqual(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have been called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have been called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have been called once')
|
||||
|
||||
def build_song_context_menu_test(self):
|
||||
"""
|
||||
|
@ -138,12 +143,9 @@ class TestServiceManager(TestCase):
|
|||
service_manager.service_manager_list = MagicMock()
|
||||
service_manager.service_manager_list.itemAt.return_value = item
|
||||
service_item = ServiceItem(None)
|
||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||
service_item.add_capability(ItemCapabilities.OnLoadUpdate)
|
||||
service_item.add_capability(ItemCapabilities.AddIfNewItem)
|
||||
service_item.add_capability(ItemCapabilities.CanSoftBreak)
|
||||
for capability in [ItemCapabilities.CanEdit, ItemCapabilities.CanPreview, ItemCapabilities.CanLoop,
|
||||
ItemCapabilities.OnLoadUpdate, ItemCapabilities.AddIfNewItem, ItemCapabilities.CanSoftBreak]:
|
||||
service_item.add_capability(capability)
|
||||
service_item.service_item_type = ServiceItemType.Text
|
||||
service_item.edit_id = 1
|
||||
service_item._display_frames.append(MagicMock())
|
||||
|
@ -164,29 +166,29 @@ class TestServiceManager(TestCase):
|
|||
# WHEN I define a context menu
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following calls should have occurred.
|
||||
self.assertEquals(service_manager.edit_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEquals(service_manager.rename_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
self.assertEqual(service_manager.edit_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEqual(service_manager.rename_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.theme_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
# THEN we add a 2nd display frame
|
||||
service_item._display_frames.append(MagicMock())
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following additional calls should have occurred.
|
||||
self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
self.assertEqual(service_manager.auto_play_slides_once.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_loop.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.timed_slide_interval.setChecked.call_count, 1, 'Should have be called once')
|
||||
|
||||
def build_bible_context_menu_test(self):
|
||||
"""
|
||||
|
@ -204,11 +206,9 @@ class TestServiceManager(TestCase):
|
|||
service_manager.service_manager_list = MagicMock()
|
||||
service_manager.service_manager_list.itemAt.return_value = item
|
||||
service_item = ServiceItem(None)
|
||||
service_item.add_capability(ItemCapabilities.NoLineBreaks)
|
||||
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||
service_item.add_capability(ItemCapabilities.CanWordSplit)
|
||||
service_item.add_capability(ItemCapabilities.CanEditTitle)
|
||||
for capability in [ItemCapabilities.NoLineBreaks, ItemCapabilities.CanPreview, ItemCapabilities.CanLoop,
|
||||
ItemCapabilities.CanWordSplit, ItemCapabilities.CanEditTitle]:
|
||||
service_item.add_capability(capability)
|
||||
service_item.service_item_type = ServiceItemType.Text
|
||||
service_item.edit_id = 1
|
||||
service_item._display_frames.append(MagicMock())
|
||||
|
@ -229,29 +229,29 @@ class TestServiceManager(TestCase):
|
|||
# WHEN I define a context menu
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following calls should have occurred.
|
||||
self.assertEquals(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.rename_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
self.assertEqual(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.rename_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEqual(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.theme_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
# THEN we add a 2nd display frame
|
||||
service_item._display_frames.append(MagicMock())
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following additional calls should have occurred.
|
||||
self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
self.assertEqual(service_manager.auto_play_slides_once.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_loop.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.timed_slide_interval.setChecked.call_count, 1, 'Should have be called once')
|
||||
|
||||
def build_custom_context_menu_test(self):
|
||||
"""
|
||||
|
@ -269,11 +269,9 @@ class TestServiceManager(TestCase):
|
|||
service_manager.service_manager_list = MagicMock()
|
||||
service_manager.service_manager_list.itemAt.return_value = item
|
||||
service_item = ServiceItem(None)
|
||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||
service_item.add_capability(ItemCapabilities.CanSoftBreak)
|
||||
service_item.add_capability(ItemCapabilities.OnLoadUpdate)
|
||||
for capability in [ItemCapabilities.CanEdit, ItemCapabilities.CanPreview, ItemCapabilities.CanLoop,
|
||||
ItemCapabilities.CanSoftBreak, ItemCapabilities.OnLoadUpdate]:
|
||||
service_item.add_capability(capability)
|
||||
service_item.service_item_type = ServiceItemType.Text
|
||||
service_item.edit_id = 1
|
||||
service_item._display_frames.append(MagicMock())
|
||||
|
@ -294,29 +292,29 @@ class TestServiceManager(TestCase):
|
|||
# WHEN I define a context menu
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following calls should have occurred.
|
||||
self.assertEquals(service_manager.edit_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEquals(service_manager.rename_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
self.assertEqual(service_manager.edit_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEqual(service_manager.rename_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.theme_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
# THEN we add a 2nd display frame
|
||||
service_item._display_frames.append(MagicMock())
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following additional calls should have occurred.
|
||||
self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
self.assertEqual(service_manager.auto_play_slides_once.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_loop.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.timed_slide_interval.setChecked.call_count, 1, 'Should have be called once')
|
||||
|
||||
def build_image_context_menu_test(self):
|
||||
"""
|
||||
|
@ -332,11 +330,9 @@ class TestServiceManager(TestCase):
|
|||
service_manager.service_manager_list = MagicMock()
|
||||
service_manager.service_manager_list.itemAt.return_value = item
|
||||
service_item = ServiceItem(None)
|
||||
service_item.add_capability(ItemCapabilities.CanMaintain)
|
||||
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||
service_item.add_capability(ItemCapabilities.CanAppend)
|
||||
service_item.add_capability(ItemCapabilities.CanEditTitle)
|
||||
for capability in [ItemCapabilities.CanMaintain, ItemCapabilities.CanPreview, ItemCapabilities.CanLoop,
|
||||
ItemCapabilities.CanAppend, ItemCapabilities.CanEditTitle]:
|
||||
service_item.add_capability(capability)
|
||||
service_item.service_item_type = ServiceItemType.Image
|
||||
service_item.edit_id = 1
|
||||
service_item._raw_frames.append(MagicMock())
|
||||
|
@ -357,29 +353,29 @@ class TestServiceManager(TestCase):
|
|||
# WHEN I define a context menu
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following calls should have occurred.
|
||||
self.assertEquals(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.rename_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.maintain_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEqual(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.rename_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEqual(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.maintain_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEqual(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
# THEN we add a 2nd display frame and regenerate the menu.
|
||||
service_item._raw_frames.append(MagicMock())
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following additional calls should have occurred.
|
||||
self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 2,
|
||||
'Should have be called twice')
|
||||
self.assertEqual(service_manager.auto_play_slides_once.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_loop.setChecked.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.timed_slide_interval.setChecked.call_count, 1, 'Should have be called once')
|
||||
|
||||
def build_media_context_menu_test(self):
|
||||
"""
|
||||
|
@ -418,27 +414,27 @@ class TestServiceManager(TestCase):
|
|||
# WHEN I define a context menu
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following calls should have occurred.
|
||||
self.assertEquals(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.rename_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEqual(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.rename_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEqual(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_start_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEqual(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
# THEN I change the length of the media and regenerate the menu.
|
||||
service_item.set_media_length(5)
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following additional calls should have occurred.
|
||||
self.assertEquals(service_manager.time_action.setVisible.call_count, 3, 'Should have be called three times')
|
||||
self.assertEqual(service_manager.time_action.setVisible.call_count, 3, 'Should have be called three times')
|
||||
|
||||
def build_presentation_pdf_context_menu_test(self):
|
||||
def build_pdf_context_menu_test(self):
|
||||
"""
|
||||
Test the creation of a context menu from service item of type Command with PDF from Presentation.
|
||||
"""
|
||||
|
@ -476,22 +472,22 @@ class TestServiceManager(TestCase):
|
|||
# WHEN I define a context menu
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following calls should have occurred.
|
||||
self.assertEquals(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.rename_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.maintain_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEqual(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.rename_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.maintain_action.setVisible.call_count, 2, 'Should have be called twice')
|
||||
self.assertEqual(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
|
||||
def build_presentation_non_pdf_context_menu_test(self):
|
||||
def build_pres_context_menu_test(self):
|
||||
"""
|
||||
Test the creation of a context menu from service item of type Command with Impress from Presentation.
|
||||
"""
|
||||
|
@ -526,17 +522,112 @@ class TestServiceManager(TestCase):
|
|||
# WHEN I define a context menu
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following calls should have occurred.
|
||||
self.assertEquals(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.rename_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEqual(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.rename_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
self.assertEqual(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
|
||||
self.assertEqual(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
|
||||
'Should have be called once')
|
||||
|
||||
@patch(u'openlp.core.ui.servicemanager.shutil.copy')
|
||||
@patch(u'openlp.core.ui.servicemanager.zipfile')
|
||||
@patch(u'openlp.core.ui.servicemanager.ServiceManager.save_file_as')
|
||||
def test_save_file_raises_permission_error(self, mocked_save_file_as, mocked_zipfile, mocked_shutil_copy):
|
||||
"""
|
||||
Test that if the save_file() method throws a PermissionError, it is handled correctly
|
||||
"""
|
||||
# GIVEN: A service manager, a service to save
|
||||
mocked_main_window = MagicMock()
|
||||
mocked_main_window.service_manager_settings_section = 'servicemanager'
|
||||
Registry().register('main_window', mocked_main_window)
|
||||
Registry().register('application', MagicMock())
|
||||
service_manager = ServiceManager(None)
|
||||
service_manager._file_name = os.path.join('temp', 'filename.osz')
|
||||
service_manager._save_lite = False
|
||||
service_manager.service_items = []
|
||||
service_manager.service_theme = 'Default'
|
||||
service_manager.service_manager_list = MagicMock()
|
||||
mocked_save_file_as.return_value = True
|
||||
mocked_zipfile.ZipFile.return_value = MagicMock()
|
||||
mocked_shutil_copy.side_effect = PermissionError
|
||||
|
||||
# WHEN: The service is saved and a PermissionError is thrown
|
||||
result = service_manager.save_file()
|
||||
|
||||
# THEN: The "save_as" method is called to save the service
|
||||
self.assertTrue(result)
|
||||
mocked_save_file_as.assert_called_with()
|
||||
|
||||
@patch(u'openlp.core.ui.servicemanager.shutil.copy')
|
||||
@patch(u'openlp.core.ui.servicemanager.zipfile')
|
||||
@patch(u'openlp.core.ui.servicemanager.ServiceManager.save_file_as')
|
||||
def test_save_local_file_raises_permission_error(self, mocked_save_file_as, mocked_zipfile, mocked_shutil_copy):
|
||||
"""
|
||||
Test that when a PermissionError is raised when trying to save a local file, it is handled correctly
|
||||
"""
|
||||
# GIVEN: A service manager, a service to save
|
||||
mocked_main_window = MagicMock()
|
||||
mocked_main_window.service_manager_settings_section = 'servicemanager'
|
||||
Registry().register('main_window', mocked_main_window)
|
||||
Registry().register('application', MagicMock())
|
||||
service_manager = ServiceManager(None)
|
||||
service_manager._file_name = os.path.join('temp', 'filename.osz')
|
||||
service_manager._save_lite = False
|
||||
service_manager.service_items = []
|
||||
service_manager.service_theme = 'Default'
|
||||
mocked_save_file_as.return_value = True
|
||||
mocked_zipfile.ZipFile.return_value = MagicMock()
|
||||
mocked_shutil_copy.side_effect = PermissionError
|
||||
|
||||
# WHEN: The service is saved and a PermissionError is thrown
|
||||
result = service_manager.save_local_file()
|
||||
|
||||
# THEN: The "save_as" method is called to save the service
|
||||
self.assertTrue(result)
|
||||
mocked_save_file_as.assert_called_with()
|
||||
|
||||
@patch(u'openlp.core.ui.servicemanager.ServiceManager.regenerate_service_items')
|
||||
def test_theme_change(self, mocked_regenerate_service_items):
|
||||
"""
|
||||
Test that when a Toolbar theme combobox displays correctly
|
||||
"""
|
||||
# GIVEN: A service manager, a service to save with a theme level of the renderer
|
||||
mocked_renderer = MagicMock()
|
||||
service_manager = ServiceManager(None)
|
||||
Registry().register('renderer', mocked_renderer)
|
||||
service_manager.toolbar = OpenLPToolbar(None)
|
||||
|
||||
service_manager.toolbar.add_toolbar_action('theme_combo_box', triggers=MagicMock())
|
||||
service_manager.toolbar.add_toolbar_action('theme_label', triggers=MagicMock())
|
||||
|
||||
# WHEN: The service manager has a Global theme
|
||||
mocked_renderer.theme_level = ThemeLevel.Global
|
||||
result = service_manager.theme_change()
|
||||
|
||||
# THEN: The the theme toolbar should not be visible
|
||||
self.assertFalse(service_manager.toolbar.actions['theme_combo_box'].isVisible(),
|
||||
'The visibility should be False')
|
||||
|
||||
# WHEN: The service manager has a Service theme
|
||||
mocked_renderer.theme_level = ThemeLevel.Service
|
||||
result = service_manager.theme_change()
|
||||
|
||||
# THEN: The the theme toolbar should not be visible
|
||||
self.assertTrue(service_manager.toolbar.actions['theme_combo_box'].isVisible(),
|
||||
'The visibility should be True')
|
||||
|
||||
# WHEN: The service manager has a Song theme
|
||||
mocked_renderer.theme_level = ThemeLevel.Song
|
||||
result = service_manager.theme_change()
|
||||
|
||||
# THEN: The the theme toolbar should not be visible
|
||||
self.assertTrue(service_manager.toolbar.actions['theme_combo_box'].isVisible(),
|
||||
'The visibility should be true')
|
||||
|
|
|
@ -0,0 +1,499 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2016 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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; version 2 of the License. #
|
||||
# #
|
||||
# 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, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
Package to test the openlp.core.ui.media.systemplayer package.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
|
||||
from PyQt5 import QtCore, QtMultimedia
|
||||
|
||||
from openlp.core.common import Registry
|
||||
from openlp.core.ui.media import MediaState
|
||||
from openlp.core.ui.media.systemplayer import SystemPlayer, CheckMediaWorker, ADDITIONAL_EXT
|
||||
|
||||
from tests.functional import MagicMock, call, patch
|
||||
|
||||
|
||||
class TestSystemPlayer(TestCase):
|
||||
"""
|
||||
Test the system media player
|
||||
"""
|
||||
@patch('openlp.core.ui.media.systemplayer.mimetypes')
|
||||
@patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
|
||||
def test_constructor(self, MockQMediaPlayer, mocked_mimetypes):
|
||||
"""
|
||||
Test the SystemPlayer constructor
|
||||
"""
|
||||
# GIVEN: The SystemPlayer class and a mockedQMediaPlayer
|
||||
mocked_media_player = MagicMock()
|
||||
mocked_media_player.supportedMimeTypes.return_value = [
|
||||
'application/postscript',
|
||||
'audio/aiff',
|
||||
'audio/x-aiff',
|
||||
'text/html',
|
||||
'video/animaflex',
|
||||
'video/x-ms-asf'
|
||||
]
|
||||
mocked_mimetypes.guess_all_extensions.side_effect = [
|
||||
['.aiff'],
|
||||
['.aiff'],
|
||||
['.afl'],
|
||||
['.asf']
|
||||
]
|
||||
MockQMediaPlayer.return_value = mocked_media_player
|
||||
|
||||
# WHEN: An object is created from it
|
||||
player = SystemPlayer(self)
|
||||
|
||||
# THEN: The correct initial values should be set up
|
||||
self.assertEqual('system', player.name)
|
||||
self.assertEqual('System', player.original_name)
|
||||
self.assertEqual('&System', player.display_name)
|
||||
self.assertEqual(self, player.parent)
|
||||
self.assertEqual(ADDITIONAL_EXT, player.additional_extensions)
|
||||
MockQMediaPlayer.assert_called_once_with(None, QtMultimedia.QMediaPlayer.VideoSurface)
|
||||
mocked_mimetypes.init.assert_called_once_with()
|
||||
mocked_media_player.service.assert_called_once_with()
|
||||
mocked_media_player.supportedMimeTypes.assert_called_once_with()
|
||||
self.assertEqual(['*.aiff'], player.audio_extensions_list)
|
||||
self.assertEqual(['*.afl', '*.asf'], player.video_extensions_list)
|
||||
|
||||
@patch('openlp.core.ui.media.systemplayer.QtMultimediaWidgets.QVideoWidget')
|
||||
@patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
|
||||
def test_setup(self, MockQMediaPlayer, MockQVideoWidget):
|
||||
"""
|
||||
Test the setup() method of SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance and a mock display
|
||||
player = SystemPlayer(self)
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.size.return_value = [1, 2, 3, 4]
|
||||
mocked_video_widget = MagicMock()
|
||||
mocked_media_player = MagicMock()
|
||||
MockQVideoWidget.return_value = mocked_video_widget
|
||||
MockQMediaPlayer.return_value = mocked_media_player
|
||||
|
||||
# WHEN: setup() is run
|
||||
player.setup(mocked_display)
|
||||
|
||||
# THEN: The player should have a display widget
|
||||
MockQVideoWidget.assert_called_once_with(mocked_display)
|
||||
self.assertEqual(mocked_video_widget, mocked_display.video_widget)
|
||||
mocked_display.size.assert_called_once_with()
|
||||
mocked_video_widget.resize.assert_called_once_with([1, 2, 3, 4])
|
||||
MockQMediaPlayer.assert_called_with(mocked_display)
|
||||
self.assertEqual(mocked_media_player, mocked_display.media_player)
|
||||
mocked_media_player.setVideoOutput.assert_called_once_with(mocked_video_widget)
|
||||
mocked_video_widget.raise_.assert_called_once_with()
|
||||
mocked_video_widget.hide.assert_called_once_with()
|
||||
self.assertTrue(player.has_own_widget)
|
||||
|
||||
def test_check_available(self):
|
||||
"""
|
||||
Test the check_available() method on SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance
|
||||
player = SystemPlayer(self)
|
||||
|
||||
# WHEN: check_available is run
|
||||
result = player.check_available()
|
||||
|
||||
# THEN: it should be available
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_load_valid_media(self):
|
||||
"""
|
||||
Test the load() method of SystemPlayer with a valid media file
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance and a mocked display
|
||||
player = SystemPlayer(self)
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.controller.media_info.volume = 1
|
||||
mocked_display.controller.media_info.file_info.absoluteFilePath.return_value = '/path/to/file'
|
||||
|
||||
# WHEN: The load() method is run
|
||||
with patch.object(player, 'check_media') as mocked_check_media, \
|
||||
patch.object(player, 'volume') as mocked_volume:
|
||||
mocked_check_media.return_value = True
|
||||
result = player.load(mocked_display)
|
||||
|
||||
# THEN: the file is sent to the video widget
|
||||
mocked_display.controller.media_info.file_info.absoluteFilePath.assert_called_once_with()
|
||||
mocked_check_media.assert_called_once_with('/path/to/file')
|
||||
mocked_display.media_player.setMedia.assert_called_once_with(
|
||||
QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile('/path/to/file')))
|
||||
mocked_volume.assert_called_once_with(mocked_display, 1)
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_load_invalid_media(self):
|
||||
"""
|
||||
Test the load() method of SystemPlayer with an invalid media file
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance and a mocked display
|
||||
player = SystemPlayer(self)
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.controller.media_info.volume = 1
|
||||
mocked_display.controller.media_info.file_info.absoluteFilePath.return_value = '/path/to/file'
|
||||
|
||||
# WHEN: The load() method is run
|
||||
with patch.object(player, 'check_media') as mocked_check_media, \
|
||||
patch.object(player, 'volume') as mocked_volume:
|
||||
mocked_check_media.return_value = False
|
||||
result = player.load(mocked_display)
|
||||
|
||||
# THEN: stuff
|
||||
mocked_display.controller.media_info.file_info.absoluteFilePath.assert_called_once_with()
|
||||
mocked_check_media.assert_called_once_with('/path/to/file')
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_resize(self):
|
||||
"""
|
||||
Test the resize() method of the SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance and a mocked display
|
||||
player = SystemPlayer(self)
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.size.return_value = [1, 2, 3, 4]
|
||||
|
||||
# WHEN: The resize() method is called
|
||||
player.resize(mocked_display)
|
||||
|
||||
# THEN: The player is resized
|
||||
mocked_display.size.assert_called_once_with()
|
||||
mocked_display.video_widget.resize.assert_called_once_with([1, 2, 3, 4])
|
||||
|
||||
@patch('openlp.core.ui.media.systemplayer.functools')
|
||||
def test_play(self, mocked_functools):
|
||||
"""
|
||||
Test the play() method of the SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance and a mocked display
|
||||
mocked_functools.partial.return_value = 'function'
|
||||
player = SystemPlayer(self)
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.controller.media_info.start_time = 1
|
||||
mocked_display.controller.media_info.volume = 1
|
||||
mocked_display.media_player.state.return_value = QtMultimedia.QMediaPlayer.PlayingState
|
||||
mocked_display.media_player.durationChanged.disconnect.side_effect = \
|
||||
TypeError('disconnect() failed between \'durationChanged\' and all its connections')
|
||||
|
||||
# WHEN: play() is called
|
||||
with patch.object(player, 'seek') as mocked_seek, \
|
||||
patch.object(player, 'volume') as mocked_volume:
|
||||
result = player.play(mocked_display)
|
||||
|
||||
# THEN: the media file is played
|
||||
mocked_display.media_player.state.assert_called_once_with()
|
||||
mocked_display.media_player.play.assert_called_once_with()
|
||||
mocked_seek.assert_called_once_with(mocked_display, 1000)
|
||||
mocked_volume.assert_called_once_with(mocked_display, 1)
|
||||
expected_block_signals_calls = [call(True), call(False)]
|
||||
self.assertEqual(expected_block_signals_calls, mocked_display.media_player.blockSignals.call_args_list)
|
||||
mocked_display.media_player.durationChanged.disconnect.assert_called_once_with()
|
||||
mocked_display.media_player.durationChanged.connect.assert_called_once_with('function')
|
||||
self.assertEqual(MediaState.Playing, player.state)
|
||||
mocked_display.video_widget.raise_.assert_called_once_with()
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_pause(self):
|
||||
"""
|
||||
Test the pause() method of the SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance
|
||||
player = SystemPlayer(self)
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.media_player.state.return_value = QtMultimedia.QMediaPlayer.PausedState
|
||||
|
||||
# WHEN: The pause method is called
|
||||
player.pause(mocked_display)
|
||||
|
||||
# THEN: The video is paused
|
||||
mocked_display.media_player.pause.assert_called_once_with()
|
||||
mocked_display.media_player.state.assert_called_once_with()
|
||||
self.assertEqual(MediaState.Paused, player.state)
|
||||
|
||||
def test_stop(self):
|
||||
"""
|
||||
Test the stop() method of the SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance
|
||||
player = SystemPlayer(self)
|
||||
mocked_display = MagicMock()
|
||||
|
||||
# WHEN: The stop method is called
|
||||
with patch.object(player, 'set_visible') as mocked_set_visible:
|
||||
player.stop(mocked_display)
|
||||
|
||||
# THEN: The video is stopped
|
||||
expected_block_signals_calls = [call(True), call(False)]
|
||||
self.assertEqual(expected_block_signals_calls, mocked_display.media_player.blockSignals.call_args_list)
|
||||
mocked_display.media_player.durationChanged.disconnect.assert_called_once_with()
|
||||
mocked_display.media_player.stop.assert_called_once_with()
|
||||
mocked_set_visible.assert_called_once_with(mocked_display, False)
|
||||
self.assertEqual(MediaState.Stopped, player.state)
|
||||
|
||||
def test_stop_throws_exception(self):
|
||||
"""
|
||||
Test the stop() method of the SystemPlayer when disconnect() throws an exception
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance
|
||||
player = SystemPlayer(self)
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.media_player.durationChanged.disconnect.side_effect = TypeError('oops')
|
||||
|
||||
# WHEN: The stop method is called
|
||||
with patch.object(player, 'set_visible') as mocked_set_visible:
|
||||
player.stop(mocked_display)
|
||||
|
||||
# THEN: The video is stopped
|
||||
expected_block_signals_calls = [call(True), call(False)]
|
||||
self.assertEqual(expected_block_signals_calls, mocked_display.media_player.blockSignals.call_args_list)
|
||||
mocked_display.media_player.durationChanged.disconnect.assert_called_once_with()
|
||||
mocked_display.media_player.stop.assert_called_once_with()
|
||||
mocked_set_visible.assert_called_once_with(mocked_display, False)
|
||||
self.assertEqual(MediaState.Stopped, player.state)
|
||||
|
||||
def test_volume(self):
|
||||
"""
|
||||
Test the volume() method of the SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance
|
||||
player = SystemPlayer(self)
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.has_audio = True
|
||||
|
||||
# WHEN: The stop method is called
|
||||
player.volume(mocked_display, 2)
|
||||
|
||||
# THEN: The video is stopped
|
||||
mocked_display.media_player.setVolume.assert_called_once_with(2)
|
||||
|
||||
def test_seek(self):
|
||||
"""
|
||||
Test the seek() method of the SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance
|
||||
player = SystemPlayer(self)
|
||||
mocked_display = MagicMock()
|
||||
|
||||
# WHEN: The stop method is called
|
||||
player.seek(mocked_display, 2)
|
||||
|
||||
# THEN: The video is stopped
|
||||
mocked_display.media_player.setPosition.assert_called_once_with(2)
|
||||
|
||||
def test_reset(self):
|
||||
"""
|
||||
Test the reset() method of the SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance
|
||||
player = SystemPlayer(self)
|
||||
mocked_display = MagicMock()
|
||||
|
||||
# WHEN: reset() is called
|
||||
with patch.object(player, 'set_visible') as mocked_set_visible:
|
||||
player.reset(mocked_display)
|
||||
|
||||
# THEN: The media player is reset
|
||||
mocked_display.media_player.stop()
|
||||
mocked_display.media_player.setMedia.assert_called_once_with(QtMultimedia.QMediaContent())
|
||||
mocked_set_visible.assert_called_once_with(mocked_display, False)
|
||||
mocked_display.video_widget.setVisible.assert_called_once_with(False)
|
||||
self.assertEqual(MediaState.Off, player.state)
|
||||
|
||||
def test_set_visible(self):
|
||||
"""
|
||||
Test the set_visible() method on the SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance and a mocked display
|
||||
player = SystemPlayer(self)
|
||||
player.has_own_widget = True
|
||||
mocked_display = MagicMock()
|
||||
|
||||
# WHEN: set_visible() is called
|
||||
player.set_visible(mocked_display, True)
|
||||
|
||||
# THEN: The widget should be visible
|
||||
mocked_display.video_widget.setVisible.assert_called_once_with(True)
|
||||
|
||||
def test_set_duration(self):
|
||||
"""
|
||||
Test the set_duration() method of the SystemPlayer
|
||||
"""
|
||||
# GIVEN: a mocked controller
|
||||
mocked_controller = MagicMock()
|
||||
|
||||
# WHEN: The set_duration() is called. NB: the 10 here is ignored by the code
|
||||
SystemPlayer.set_duration(mocked_controller, 1000)
|
||||
|
||||
# THEN: The maximum length of the slider should be set
|
||||
self.assertEqual(1, mocked_controller.media_info.length)
|
||||
mocked_controller.seek_slider.setMaximum.assert_called_once_with(1000)
|
||||
|
||||
def test_update_ui(self):
|
||||
"""
|
||||
Test the update_ui() method on the SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance
|
||||
player = SystemPlayer(self)
|
||||
player.state = MediaState.Playing
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.media_player.state.return_value = QtMultimedia.QMediaPlayer.PausedState
|
||||
mocked_display.controller.media_info.end_time = 1
|
||||
mocked_display.media_player.position.return_value = 2000
|
||||
mocked_display.controller.seek_slider.isSliderDown.return_value = False
|
||||
|
||||
# WHEN: update_ui() is called
|
||||
with patch.object(player, 'stop') as mocked_stop, \
|
||||
patch.object(player, 'set_visible') as mocked_set_visible:
|
||||
player.update_ui(mocked_display)
|
||||
|
||||
# THEN: The UI is updated
|
||||
expected_stop_calls = [call(mocked_display), call(mocked_display)]
|
||||
expected_position_calls = [call(), call()]
|
||||
expected_block_signals_calls = [call(True), call(False)]
|
||||
mocked_display.media_player.state.assert_called_once_with()
|
||||
self.assertEqual(2, mocked_stop.call_count)
|
||||
self.assertEqual(expected_stop_calls, mocked_stop.call_args_list)
|
||||
self.assertEqual(2, mocked_display.media_player.position.call_count)
|
||||
self.assertEqual(expected_position_calls, mocked_display.media_player.position.call_args_list)
|
||||
mocked_set_visible.assert_called_once_with(mocked_display, False)
|
||||
mocked_display.controller.seek_slider.isSliderDown.assert_called_once_with()
|
||||
self.assertEqual(expected_block_signals_calls,
|
||||
mocked_display.controller.seek_slider.blockSignals.call_args_list)
|
||||
mocked_display.controller.seek_slider.setSliderPosition.assert_called_once_with(2000)
|
||||
|
||||
def test_get_media_display_css(self):
|
||||
"""
|
||||
Test the get_media_display_css() method of the SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance
|
||||
player = SystemPlayer(self)
|
||||
|
||||
# WHEN: get_media_display_css() is called
|
||||
result = player.get_media_display_css()
|
||||
|
||||
# THEN: The css should be empty
|
||||
self.assertEqual('', result)
|
||||
|
||||
def test_get_info(self):
|
||||
"""
|
||||
Test the get_info() method of the SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance
|
||||
player = SystemPlayer(self)
|
||||
|
||||
# WHEN: get_info() is called
|
||||
result = player.get_info()
|
||||
|
||||
# THEN: The info should be correct
|
||||
expected_info = 'This media player uses your operating system to provide media capabilities.<br/> ' \
|
||||
'<strong>Audio</strong><br/>[]<br/><strong>Video</strong><br/>[]<br/>'
|
||||
self.assertEqual(expected_info, result)
|
||||
|
||||
@patch('openlp.core.ui.media.systemplayer.CheckMediaWorker')
|
||||
@patch('openlp.core.ui.media.systemplayer.QtCore.QThread')
|
||||
def test_check_media(self, MockQThread, MockCheckMediaWorker):
|
||||
"""
|
||||
Test the check_media() method of the SystemPlayer
|
||||
"""
|
||||
# GIVEN: A SystemPlayer instance and a mocked thread
|
||||
valid_file = '/path/to/video.ogv'
|
||||
mocked_application = MagicMock()
|
||||
Registry().create()
|
||||
Registry().register('application', mocked_application)
|
||||
player = SystemPlayer(self)
|
||||
mocked_thread = MagicMock()
|
||||
mocked_thread.isRunning.side_effect = [True, False]
|
||||
mocked_thread.quit = 'quit' # actually supposed to be a slot, but it's all mocked out anyway
|
||||
MockQThread.return_value = mocked_thread
|
||||
mocked_check_media_worker = MagicMock()
|
||||
mocked_check_media_worker.play = 'play'
|
||||
mocked_check_media_worker.result = True
|
||||
MockCheckMediaWorker.return_value = mocked_check_media_worker
|
||||
|
||||
# WHEN: check_media() is called with a valid media file
|
||||
result = player.check_media(valid_file)
|
||||
|
||||
# THEN: It should return True
|
||||
MockQThread.assert_called_once_with()
|
||||
MockCheckMediaWorker.assert_called_once_with(valid_file)
|
||||
mocked_check_media_worker.setVolume.assert_called_once_with(0)
|
||||
mocked_check_media_worker.moveToThread.assert_called_once_with(mocked_thread)
|
||||
mocked_check_media_worker.finished.connect.assert_called_once_with('quit')
|
||||
mocked_thread.started.connect.assert_called_once_with('play')
|
||||
mocked_thread.start.assert_called_once_with()
|
||||
self.assertEqual(2, mocked_thread.isRunning.call_count)
|
||||
mocked_application.processEvents.assert_called_once_with()
|
||||
self.assertTrue(result)
|
||||
|
||||
|
||||
class TestCheckMediaWorker(TestCase):
|
||||
"""
|
||||
Test the CheckMediaWorker class
|
||||
"""
|
||||
def test_constructor(self):
|
||||
"""
|
||||
Test the constructor of the CheckMediaWorker class
|
||||
"""
|
||||
# GIVEN: A file path
|
||||
path = 'file.ogv'
|
||||
|
||||
# WHEN: The CheckMediaWorker object is instantiated
|
||||
worker = CheckMediaWorker(path)
|
||||
|
||||
# THEN: The correct values should be set up
|
||||
self.assertIsNotNone(worker)
|
||||
|
||||
def test_signals_media(self):
|
||||
"""
|
||||
Test the signals() signal of the CheckMediaWorker class with a "media" origin
|
||||
"""
|
||||
# GIVEN: A CheckMediaWorker instance
|
||||
worker = CheckMediaWorker('file.ogv')
|
||||
|
||||
# WHEN: signals() is called with media and BufferedMedia
|
||||
with patch.object(worker, 'stop') as mocked_stop, \
|
||||
patch.object(worker, 'finished') as mocked_finished:
|
||||
worker.signals('media', worker.BufferedMedia)
|
||||
|
||||
# THEN: The worker should exit and the result should be True
|
||||
mocked_stop.assert_called_once_with()
|
||||
mocked_finished.emit.assert_called_once_with()
|
||||
self.assertTrue(worker.result)
|
||||
|
||||
def test_signals_error(self):
|
||||
"""
|
||||
Test the signals() signal of the CheckMediaWorker class with a "error" origin
|
||||
"""
|
||||
# GIVEN: A CheckMediaWorker instance
|
||||
worker = CheckMediaWorker('file.ogv')
|
||||
|
||||
# WHEN: signals() is called with error and BufferedMedia
|
||||
with patch.object(worker, 'stop') as mocked_stop, \
|
||||
patch.object(worker, 'finished') as mocked_finished:
|
||||
worker.signals('error', None)
|
||||
|
||||
# THEN: The worker should exit and the result should be True
|
||||
mocked_stop.assert_called_once_with()
|
||||
mocked_finished.emit.assert_called_once_with()
|
||||
self.assertFalse(worker.result)
|
|
@ -26,16 +26,16 @@ import os
|
|||
from unittest import TestCase
|
||||
from urllib.error import URLError
|
||||
|
||||
from bs4 import NavigableString
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from tests.helpers.songfileimport import SongImportTestHelper
|
||||
from openlp.core import Registry
|
||||
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.songfileimport import SongImportTestHelper
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
TEST_PATH = os.path.abspath(
|
||||
|
@ -61,6 +61,30 @@ 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')
|
||||
|
||||
@patch('openlp.plugins.songs.lib.songselect.build_opener')
|
||||
@patch('openlp.plugins.songs.lib.songselect.BeautifulSoup')
|
||||
def login_fails_type_error_test(self, MockedBeautifulSoup, mocked_build_opener):
|
||||
"""
|
||||
Test that when logging in to SongSelect fails due to a TypeError, the login method returns False
|
||||
"""
|
||||
# GIVEN: A bunch of mocked out stuff and an importer object
|
||||
mocked_opener = MagicMock()
|
||||
mocked_build_opener.return_value = mocked_opener
|
||||
mocked_login_page = MagicMock()
|
||||
mocked_login_page.find.side_effect = [{'value': 'blah'}, None]
|
||||
MockedBeautifulSoup.side_effect = [mocked_login_page, TypeError('wrong type')]
|
||||
mock_callback = MagicMock()
|
||||
importer = SongSelectImport(None)
|
||||
|
||||
# WHEN: The login method is called after being rigged to fail
|
||||
result = importer.login('username', 'password', mock_callback)
|
||||
|
||||
# THEN: callback was called 3 times, open was called twice, find was called twice, and False was returned
|
||||
self.assertEqual(2, mock_callback.call_count, 'callback should have been called 3 times')
|
||||
self.assertEqual(1, mocked_login_page.find.call_count, 'find should have been called twice')
|
||||
self.assertEqual(2, mocked_opener.open.call_count, 'opener should have been called twice')
|
||||
self.assertFalse(result, 'The login method should have returned False')
|
||||
|
||||
@patch('openlp.plugins.songs.lib.songselect.build_opener')
|
||||
@patch('openlp.plugins.songs.lib.songselect.BeautifulSoup')
|
||||
def login_fails_test(self, MockedBeautifulSoup, mocked_build_opener):
|
||||
|
@ -71,7 +95,7 @@ class TestSongSelectImport(TestCase, TestMixin):
|
|||
mocked_opener = MagicMock()
|
||||
mocked_build_opener.return_value = mocked_opener
|
||||
mocked_login_page = MagicMock()
|
||||
mocked_login_page.find.return_value = {'value': 'blah'}
|
||||
mocked_login_page.find.side_effect = [{'value': 'blah'}, None]
|
||||
MockedBeautifulSoup.return_value = mocked_login_page
|
||||
mock_callback = MagicMock()
|
||||
importer = SongSelectImport(None)
|
||||
|
@ -112,7 +136,7 @@ class TestSongSelectImport(TestCase, TestMixin):
|
|||
mocked_opener = MagicMock()
|
||||
mocked_build_opener.return_value = mocked_opener
|
||||
mocked_login_page = MagicMock()
|
||||
mocked_login_page.find.side_effect = [{'value': 'blah'}, None]
|
||||
mocked_login_page.find.side_effect = [{'value': 'blah'}, MagicMock()]
|
||||
MockedBeautifulSoup.return_value = mocked_login_page
|
||||
mock_callback = MagicMock()
|
||||
importer = SongSelectImport(None)
|
||||
|
@ -143,6 +167,27 @@ 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)
|
||||
|
||||
@patch('openlp.plugins.songs.lib.songselect.build_opener')
|
||||
@patch('openlp.plugins.songs.lib.songselect.log')
|
||||
def logout_fails_test(self, mocked_log, 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
|
||||
type_error = TypeError('wrong type')
|
||||
mocked_opener = MagicMock()
|
||||
mocked_opener.open.side_effect = type_error
|
||||
mocked_build_opener.return_value = mocked_opener
|
||||
importer = SongSelectImport(None)
|
||||
|
||||
# WHEN: The login method is called after being rigged to fail
|
||||
importer.logout()
|
||||
|
||||
# THEN: The opener is called once with the logout url
|
||||
self.assertEqual(1, mocked_opener.open.call_count, 'opener should have been called once')
|
||||
mocked_opener.open.assert_called_with(LOGOUT_URL)
|
||||
mocked_log.exception.assert_called_once_with('Could not log out of SongSelect, %s', type_error)
|
||||
|
||||
@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):
|
||||
|
@ -165,7 +210,31 @@ class TestSongSelectImport(TestCase, TestMixin):
|
|||
self.assertEqual(0, mock_callback.call_count, 'callback should not have been called')
|
||||
self.assertEqual(1, mocked_opener.open.call_count, 'open should have been called once')
|
||||
self.assertEqual(1, mocked_results_page.find_all.call_count, 'find_all should have been called once')
|
||||
mocked_results_page.find_all.assert_called_with('li', 'result pane')
|
||||
mocked_results_page.find_all.assert_called_with('div', 'song-result')
|
||||
self.assertEqual([], results, 'The search method should have returned an empty list')
|
||||
|
||||
@patch('openlp.plugins.songs.lib.songselect.build_opener')
|
||||
@patch('openlp.plugins.songs.lib.songselect.BeautifulSoup')
|
||||
def search_returns_no_results_after_exception_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
|
||||
mocked_opener = MagicMock()
|
||||
mocked_build_opener.return_value = mocked_opener
|
||||
mocked_results_page = MagicMock()
|
||||
mocked_results_page.find_all.return_value = []
|
||||
MockedBeautifulSoup.side_effect = TypeError('wrong type')
|
||||
mock_callback = MagicMock()
|
||||
importer = SongSelectImport(None)
|
||||
|
||||
# WHEN: The login method is called after being rigged to fail
|
||||
results = importer.search('text', 1000, mock_callback)
|
||||
|
||||
# THEN: callback was never called, open was called once, find_all was called once, an empty list returned
|
||||
self.assertEqual(0, mock_callback.call_count, 'callback should not have been called')
|
||||
self.assertEqual(1, mocked_opener.open.call_count, 'open should have been called once')
|
||||
self.assertEqual(0, mocked_results_page.find_all.call_count, 'find_all should not have been called')
|
||||
self.assertEqual([], results, 'The search method should have returned an empty list')
|
||||
|
||||
@patch('openlp.plugins.songs.lib.songselect.build_opener')
|
||||
|
@ -177,12 +246,18 @@ class TestSongSelectImport(TestCase, TestMixin):
|
|||
# GIVEN: A bunch of mocked out stuff and an importer object
|
||||
# first search result
|
||||
mocked_result1 = MagicMock()
|
||||
mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}]
|
||||
mocked_result1.find_all.return_value = [MagicMock(string='Author 1-1'), MagicMock(string='Author 1-2')]
|
||||
mocked_result1.find.side_effect = [
|
||||
MagicMock(find=MagicMock(return_value=MagicMock(string='Title 1'))),
|
||||
MagicMock(string='James, John'),
|
||||
MagicMock(find=MagicMock(return_value={'href': '/url1'}))
|
||||
]
|
||||
# second search result
|
||||
mocked_result2 = MagicMock()
|
||||
mocked_result2.find.side_effect = [MagicMock(string='Title 2'), {'href': '/url2'}]
|
||||
mocked_result2.find_all.return_value = [MagicMock(string='Author 2-1'), MagicMock(string='Author 2-2')]
|
||||
mocked_result2.find.side_effect = [
|
||||
MagicMock(find=MagicMock(return_value=MagicMock(string='Title 2'))),
|
||||
MagicMock(string='Philip'),
|
||||
MagicMock(find=MagicMock(return_value={'href': '/url2'}))
|
||||
]
|
||||
# rest of the stuff
|
||||
mocked_opener = MagicMock()
|
||||
mocked_build_opener.return_value = mocked_opener
|
||||
|
@ -196,13 +271,14 @@ class TestSongSelectImport(TestCase, TestMixin):
|
|||
results = importer.search('text', 1000, mock_callback)
|
||||
|
||||
# THEN: callback was never called, open was called once, find_all was called once, an empty list returned
|
||||
self.maxDiff = None
|
||||
self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice')
|
||||
self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice')
|
||||
self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice')
|
||||
mocked_results_page.find_all.assert_called_with('li', 'result pane')
|
||||
mocked_results_page.find_all.assert_called_with('div', 'song-result')
|
||||
expected_list = [
|
||||
{'title': 'Title 1', 'authors': ['Author 1-1', 'Author 1-2'], 'link': BASE_URL + '/url1'},
|
||||
{'title': 'Title 2', 'authors': ['Author 2-1', 'Author 2-2'], 'link': BASE_URL + '/url2'}
|
||||
{'title': 'Title 1', 'authors': ['James', 'John'], 'link': BASE_URL + '/url1'},
|
||||
{'title': 'Title 2', 'authors': ['Philip'], 'link': BASE_URL + '/url2'}
|
||||
]
|
||||
self.assertListEqual(expected_list, results, 'The search method should have returned two songs')
|
||||
|
||||
|
@ -215,16 +291,25 @@ class TestSongSelectImport(TestCase, TestMixin):
|
|||
# GIVEN: A bunch of mocked out stuff and an importer object
|
||||
# first search result
|
||||
mocked_result1 = MagicMock()
|
||||
mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}]
|
||||
mocked_result1.find_all.return_value = [MagicMock(string='Author 1-1'), MagicMock(string='Author 1-2')]
|
||||
mocked_result1.find.side_effect = [
|
||||
MagicMock(find=MagicMock(return_value=MagicMock(string='Title 1'))),
|
||||
MagicMock(string='James, John'),
|
||||
MagicMock(find=MagicMock(return_value={'href': '/url1'}))
|
||||
]
|
||||
# second search result
|
||||
mocked_result2 = MagicMock()
|
||||
mocked_result2.find.side_effect = [MagicMock(string='Title 2'), {'href': '/url2'}]
|
||||
mocked_result2.find_all.return_value = [MagicMock(string='Author 2-1'), MagicMock(string='Author 2-2')]
|
||||
mocked_result2.find.side_effect = [
|
||||
MagicMock(find=MagicMock(return_value=MagicMock(string='Title 2'))),
|
||||
MagicMock(string='Philip'),
|
||||
MagicMock(find=MagicMock(return_value={'href': '/url2'}))
|
||||
]
|
||||
# third search result
|
||||
mocked_result3 = MagicMock()
|
||||
mocked_result3.find.side_effect = [MagicMock(string='Title 3'), {'href': '/url3'}]
|
||||
mocked_result3.find_all.return_value = [MagicMock(string='Author 3-1'), MagicMock(string='Author 3-2')]
|
||||
mocked_result3.find.side_effect = [
|
||||
MagicMock(find=MagicMock(return_value=MagicMock(string='Title 3'))),
|
||||
MagicMock(string='Luke, Matthew'),
|
||||
MagicMock(find=MagicMock(return_value={'href': '/url3'}))
|
||||
]
|
||||
# rest of the stuff
|
||||
mocked_opener = MagicMock()
|
||||
mocked_build_opener.return_value = mocked_opener
|
||||
|
@ -241,9 +326,9 @@ class TestSongSelectImport(TestCase, TestMixin):
|
|||
self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice')
|
||||
self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice')
|
||||
self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice')
|
||||
mocked_results_page.find_all.assert_called_with('li', 'result pane')
|
||||
expected_list = [{'title': 'Title 1', 'authors': ['Author 1-1', 'Author 1-2'], 'link': BASE_URL + '/url1'},
|
||||
{'title': 'Title 2', 'authors': ['Author 2-1', 'Author 2-2'], 'link': BASE_URL + '/url2'}]
|
||||
mocked_results_page.find_all.assert_called_with('div', 'song-result')
|
||||
expected_list = [{'title': 'Title 1', 'authors': ['James', 'John'], 'link': BASE_URL + '/url1'},
|
||||
{'title': 'Title 2', 'authors': ['Philip'], 'link': BASE_URL + '/url2'}]
|
||||
self.assertListEqual(expected_list, results, 'The search method should have returned two songs')
|
||||
|
||||
@patch('openlp.plugins.songs.lib.songselect.build_opener')
|
||||
|
@ -306,22 +391,47 @@ class TestSongSelectImport(TestCase, TestMixin):
|
|||
Test that the get_song() method returns the correct song details
|
||||
"""
|
||||
# GIVEN: A bunch of mocked out stuff and an importer object
|
||||
mocked_song_page = MagicMock()
|
||||
mocked_copyright = MagicMock()
|
||||
mocked_copyright.find_all.return_value = [MagicMock(string='Copyright 1'), MagicMock(string='Copyright 2')]
|
||||
mocked_song_page.find.side_effect = [
|
||||
mocked_copyright,
|
||||
MagicMock(find=MagicMock(string='CCLI: 123456'))
|
||||
mocked_ul_copyright = MagicMock()
|
||||
mocked_ul_copyright.find.side_effect = [True, False]
|
||||
mocked_ul_copyright.find_all.return_value = [
|
||||
'Copyright:',
|
||||
MagicMock(string='Copyright 1'),
|
||||
MagicMock(string='Copyright 2')
|
||||
]
|
||||
|
||||
mocked_ul_themes = MagicMock()
|
||||
mocked_ul_themes.find.side_effect = [False, True]
|
||||
mocked_ul_themes.find_all.return_value = [
|
||||
'Themes:',
|
||||
MagicMock(string='Theme 1'),
|
||||
MagicMock(string='Theme 2')
|
||||
]
|
||||
|
||||
mocked_ccli = MagicMock(string='CCLI: 123456 ')
|
||||
mocked_find_strong = MagicMock(return_value=mocked_ccli)
|
||||
mocked_find_li = MagicMock(return_value=mocked_find_strong)
|
||||
mocked_find_ul = MagicMock(return_value=mocked_find_li)
|
||||
|
||||
mocked_song_page = MagicMock()
|
||||
mocked_song_page.find_all.return_value = [
|
||||
mocked_ul_copyright,
|
||||
mocked_ul_themes
|
||||
]
|
||||
mocked_song_page.find.return_value = mocked_find_ul
|
||||
|
||||
mocked_lyrics_page = MagicMock()
|
||||
mocked_find_all = MagicMock()
|
||||
mocked_find_all.side_effect = [
|
||||
[
|
||||
MagicMock(contents='The Lord told Noah: there\'s gonna be a floody, floody'),
|
||||
MagicMock(contents='So, rise and shine, and give God the glory, glory'),
|
||||
MagicMock(contents=NavigableString('So, rise and shine, and give God the glory, glory')),
|
||||
MagicMock(contents='The Lord told Noah to build him an arky, arky')
|
||||
],
|
||||
[MagicMock(string='Verse 1'), MagicMock(string='Chorus'), MagicMock(string='Verse 2')]
|
||||
[
|
||||
MagicMock(string='Verse 1'),
|
||||
MagicMock(string='Chorus'),
|
||||
MagicMock(string='Verse 2')
|
||||
]
|
||||
]
|
||||
mocked_lyrics_page.find.return_value = MagicMock(find_all=mocked_find_all)
|
||||
MockedBeautifulSoup.side_effect = [mocked_song_page, mocked_lyrics_page]
|
||||
|
@ -333,17 +443,24 @@ class TestSongSelectImport(TestCase, TestMixin):
|
|||
result = importer.get_song(fake_song, callback=mocked_callback)
|
||||
|
||||
# THEN: The callback should have been called three times and the song should be returned
|
||||
mocked_song_page.find_all.assert_called_once_with('ul', 'song-meta-list')
|
||||
self.assertEqual(2, mocked_ul_copyright.find.call_count)
|
||||
self.assertEqual(1, mocked_ul_copyright.find_all.call_count)
|
||||
self.assertEqual(2, mocked_ul_themes.find.call_count)
|
||||
self.assertEqual(1, mocked_ul_themes.find_all.call_count)
|
||||
self.assertEqual(3, mocked_callback.call_count, 'The callback should have been called twice')
|
||||
self.assertIsNotNone(result, 'The get_song() method should have returned a song dictionary')
|
||||
self.assertIsInstance(result, dict, 'The get_song() method should have returned a song dictionary')
|
||||
self.assertEqual(2, mocked_lyrics_page.find.call_count, 'The find() method should have been called twice')
|
||||
self.assertEqual(2, mocked_find_all.call_count, 'The find_all() method should have been called twice')
|
||||
self.assertEqual([call('section', 'lyrics'), call('section', 'lyrics')],
|
||||
self.assertEqual([call('div', 'song-viewer lyrics'), call('div', 'song-viewer lyrics')],
|
||||
mocked_lyrics_page.find.call_args_list,
|
||||
'The find() method should have been called with the right arguments')
|
||||
self.assertEqual([call('p'), call('h3')], mocked_find_all.call_args_list,
|
||||
'The find_all() method should have been called with the right arguments')
|
||||
self.assertIn('copyright', result, 'The returned song should have a copyright')
|
||||
self.assertEqual('Copyright 1/Copyright 2', result['copyright'])
|
||||
self.assertIn('ccli_number', result, 'The returned song should have a CCLI number')
|
||||
# self.assertEqual('123456', result['ccli_number'], result['ccli_number'])
|
||||
self.assertIn('verses', result, 'The returned song should have verses')
|
||||
self.assertEqual(3, len(result['verses']), 'Three verses should have been returned')
|
||||
|
||||
|
@ -359,7 +476,7 @@ class TestSongSelectImport(TestCase, TestMixin):
|
|||
'authors': ['Public Domain'],
|
||||
'verses': [
|
||||
{'label': 'Verse 1', 'lyrics': 'The Lord told Noah: there\'s gonna be a floody, floody'},
|
||||
{'label': 'Chorus 1', 'lyrics': 'So, rise and shine, and give God the glory, glory'},
|
||||
{'label': 'Chorus', 'lyrics': 'So, rise and shine, and give God the glory, glory'},
|
||||
{'label': 'Verse 2', 'lyrics': 'The Lord told Noah to build him an arky, arky'}
|
||||
],
|
||||
'copyright': 'Public Domain',
|
||||
|
@ -453,6 +570,45 @@ class TestSongSelectImport(TestCase, TestMixin):
|
|||
MockedAuthor.populate.assert_called_with(first_name='Unknown', last_name='',
|
||||
display_name='Unknown')
|
||||
self.assertEqual(1, len(result.authors_songs), 'There should only be one author')
|
||||
# self.assertEqual(2, len(result.topics), 'There should only be two topics')
|
||||
|
||||
@patch('openlp.plugins.songs.lib.songselect.clean_song')
|
||||
@patch('openlp.plugins.songs.lib.songselect.Topic')
|
||||
@patch('openlp.plugins.songs.lib.songselect.Author')
|
||||
def save_song_topics_test(self, MockedAuthor, MockedTopic, mocked_clean_song):
|
||||
"""
|
||||
Test that saving a song with topics performs the correct actions
|
||||
"""
|
||||
# GIVEN: A song to save, and some mocked out objects
|
||||
song_dict = {
|
||||
'title': 'Arky Arky',
|
||||
'authors': ['Public Domain'],
|
||||
'verses': [
|
||||
{'label': 'Verse 1', 'lyrics': 'The Lord told Noah: there\'s gonna be a floody, floody'},
|
||||
{'label': 'Chorus', 'lyrics': 'So, rise and shine, and give God the glory, glory'},
|
||||
{'label': 'Verse 2', 'lyrics': 'The Lord told Noah to build him an arky, arky'}
|
||||
],
|
||||
'copyright': 'Public Domain',
|
||||
'ccli_number': '123456',
|
||||
'topics': ['Grace', 'Love']
|
||||
}
|
||||
MockedAuthor.display_name.__eq__.return_value = False
|
||||
MockedTopic.name.__eq__.return_value = False
|
||||
mocked_db_manager = MagicMock()
|
||||
mocked_db_manager.get_object_filtered.return_value = None
|
||||
importer = SongSelectImport(mocked_db_manager)
|
||||
|
||||
# WHEN: The song is saved to the database
|
||||
result = importer.save_song(song_dict)
|
||||
|
||||
# THEN: The return value should be a Song class and the mocked_db_manager should have been called
|
||||
self.assertIsInstance(result, Song, 'The returned value should be a Song object')
|
||||
mocked_clean_song.assert_called_with(mocked_db_manager, result)
|
||||
self.assertEqual(2, mocked_db_manager.save_object.call_count,
|
||||
'The save_object() method should have been called twice')
|
||||
mocked_db_manager.get_object_filtered.assert_called_with(MockedTopic, False)
|
||||
self.assertEqual([call(name='Grace'), call(name='Love')], MockedTopic.populate.call_args_list)
|
||||
self.assertEqual(2, len(result.topics), 'There should be two topics')
|
||||
|
||||
|
||||
class TestSongSelectForm(TestCase, TestMixin):
|
||||
|
|
|
@ -50,7 +50,8 @@ class TestBibleHTTP(TestCase):
|
|||
books = handler.get_books_from_http('NIV')
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert len(books) == 66, 'The bible should not have had any books added or removed'
|
||||
self.assertEqual(len(books), 66, 'The bible should not have had any books added or removed')
|
||||
self.assertEqual(books[0], 'Genesis', 'The first bible book should be Genesis')
|
||||
|
||||
def bible_gateway_extract_books_support_redirect_test(self):
|
||||
"""
|
||||
|
@ -63,7 +64,7 @@ class TestBibleHTTP(TestCase):
|
|||
books = handler.get_books_from_http('DN1933')
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert len(books) == 66, 'This bible should have 66 books'
|
||||
self.assertEqual(len(books), 66, 'This bible should have 66 books')
|
||||
|
||||
def bible_gateway_extract_verse_test(self):
|
||||
"""
|
||||
|
@ -76,7 +77,8 @@ class TestBibleHTTP(TestCase):
|
|||
results = handler.get_bible_chapter('NIV', 'John', 3)
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed'
|
||||
self.assertEqual(len(results.verse_list), 36,
|
||||
'The book of John should not have had any verses added or removed')
|
||||
|
||||
def bible_gateway_extract_verse_nkjv_test(self):
|
||||
"""
|
||||
|
@ -89,7 +91,8 @@ class TestBibleHTTP(TestCase):
|
|||
results = handler.get_bible_chapter('NKJV', 'John', 3)
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed'
|
||||
self.assertEqual(len(results.verse_list), 36,
|
||||
'The book of John should not have had any verses added or removed')
|
||||
|
||||
def crosswalk_extract_books_test(self):
|
||||
"""
|
||||
|
@ -102,7 +105,7 @@ class TestBibleHTTP(TestCase):
|
|||
books = handler.get_books_from_http('niv')
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert len(books) == 66, 'The bible should not have had any books added or removed'
|
||||
self.assertEqual(len(books), 66, 'The bible should not have had any books added or removed')
|
||||
|
||||
def crosswalk_extract_verse_test(self):
|
||||
"""
|
||||
|
@ -115,7 +118,8 @@ class TestBibleHTTP(TestCase):
|
|||
results = handler.get_bible_chapter('niv', 'john', 3)
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed'
|
||||
self.assertEqual(len(results.verse_list), 36,
|
||||
'The book of John should not have had any verses added or removed')
|
||||
|
||||
def bibleserver_get_bibles_test(self):
|
||||
"""
|
||||
|
@ -144,7 +148,7 @@ class TestBibleHTTP(TestCase):
|
|||
|
||||
# THEN: The list should not be None, and some known bibles should be there
|
||||
self.assertIsNotNone(bibles)
|
||||
self.assertIn(('Holman Christian Standard Bible', 'HCSB', 'en'), bibles)
|
||||
self.assertIn(('Holman Christian Standard Bible (HCSB)', 'HCSB', 'en'), bibles)
|
||||
|
||||
def crosswalk_get_bibles_test(self):
|
||||
"""
|
||||
|
|
|
@ -29,7 +29,7 @@ from subprocess import Popen, PIPE
|
|||
|
||||
TAGS1 = {'1.9.0', '1.9.1', '1.9.2', '1.9.3', '1.9.4', '1.9.5', '1.9.6', '1.9.7', '1.9.8', '1.9.9', '1.9.10',
|
||||
'1.9.11', '1.9.12', '2.0', '2.1.0', '2.1.1', '2.1.2', '2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.2',
|
||||
'2.3.1', '2.3.2', '2.3.3', '2.4', '2.4.1', '2.4.2'}
|
||||
'2.3.1', '2.3.2', '2.3.3', '2.4', '2.4.1', '2.4.2', '2.4.3'}
|
||||
|
||||
|
||||
class TestBzrTags(TestCase):
|
||||
|
|
Loading…
Reference in New Issue