forked from openlp/openlp
Merge branch 'ftw-remote-download' into 'master'
Web Remote Version Checking and Downloads; Other Fixes See merge request openlp/openlp!159
This commit is contained in:
commit
92d67468e2
@ -23,16 +23,64 @@ Download and "install" the remote web client
|
|||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
from datetime import date
|
||||||
|
from distutils.version import LooseVersion
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
|
||||||
from openlp.core.common.applocation import AppLocation
|
from openlp.core.common.applocation import AppLocation
|
||||||
from openlp.core.common.httputils import download_file, get_web_page, get_openlp_user_agent
|
from openlp.core.common.httputils import download_file, get_web_page, get_openlp_user_agent
|
||||||
|
from openlp.core.common.registry import Registry
|
||||||
|
from openlp.core.threading import ThreadWorker, run_thread
|
||||||
|
|
||||||
REMOTE_URL = 'https://get.openlp.org/remote/'
|
REMOTE_URL = 'https://get.openlp.org/remote/'
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteVersionWorker(ThreadWorker):
|
||||||
|
"""
|
||||||
|
A worker class to fetch the version of the web remote. This is run from within a thread so that it
|
||||||
|
doesn't affect the loading time of OpenLP.
|
||||||
|
"""
|
||||||
|
new_version = QtCore.pyqtSignal(str)
|
||||||
|
no_internet = QtCore.pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, current_version):
|
||||||
|
"""
|
||||||
|
Constructor for the version check worker.
|
||||||
|
|
||||||
|
:param string current_version: The current version of the web remote
|
||||||
|
"""
|
||||||
|
log.debug('VersionWorker - Initialise')
|
||||||
|
super().__init__(None)
|
||||||
|
self.current_version = current_version or '0.0'
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""
|
||||||
|
Check the latest version of the web remote against the version file on the OpenLP server.
|
||||||
|
"""
|
||||||
|
log.debug('RemoteVersionWorker - Start')
|
||||||
|
version_info = None
|
||||||
|
retries = 0
|
||||||
|
while retries < 3:
|
||||||
|
try:
|
||||||
|
version_info = download_version_info()
|
||||||
|
log.debug('New version found: %s', version_info['latest']['version'])
|
||||||
|
break
|
||||||
|
except OSError:
|
||||||
|
log.exception('Unable to connect to OpenLP server to download version file')
|
||||||
|
retries += 1
|
||||||
|
else:
|
||||||
|
self.no_internet.emit()
|
||||||
|
if version_info and LooseVersion(version_info['latest']['version']) > LooseVersion(self.current_version):
|
||||||
|
Registry().get('settings').setValue('api/last version test', date.today().strftime('%Y-%m-%d'))
|
||||||
|
Registry().get('settings_form').api_tab.master_version = version_info['latest']['version']
|
||||||
|
self.new_version.emit(version_info['latest']['version'])
|
||||||
|
self.quit.emit()
|
||||||
|
|
||||||
|
|
||||||
def deploy_zipfile(app_root_path, zip_name):
|
def deploy_zipfile(app_root_path, zip_name):
|
||||||
"""
|
"""
|
||||||
Process the downloaded zip file and add to the correct directory
|
Process the downloaded zip file and add to the correct directory
|
||||||
@ -60,7 +108,18 @@ def download_version_info():
|
|||||||
return json.loads(file_contents)
|
return json.loads(file_contents)
|
||||||
|
|
||||||
|
|
||||||
def download_and_check(callback=None):
|
def get_latest_size():
|
||||||
|
"""
|
||||||
|
Download the version info file and get the size of the latest file
|
||||||
|
"""
|
||||||
|
version_info = download_version_info()
|
||||||
|
if not version_info:
|
||||||
|
log.warning('Unable to access the version information, abandoning download')
|
||||||
|
return 0
|
||||||
|
return version_info['latest']['size']
|
||||||
|
|
||||||
|
|
||||||
|
def download_and_check(callback=None, can_update_range=True):
|
||||||
"""
|
"""
|
||||||
Download the web site and deploy it.
|
Download the web site and deploy it.
|
||||||
"""
|
"""
|
||||||
@ -70,9 +129,27 @@ def download_and_check(callback=None):
|
|||||||
# Show the user an error message
|
# Show the user an error message
|
||||||
return None
|
return None
|
||||||
file_size = version_info['latest']['size']
|
file_size = version_info['latest']['size']
|
||||||
callback.setRange(0, file_size)
|
if can_update_range:
|
||||||
|
callback.setRange(0, file_size)
|
||||||
if download_file(callback, REMOTE_URL + '{version}/{filename}'.format(**version_info['latest']),
|
if download_file(callback, REMOTE_URL + '{version}/{filename}'.format(**version_info['latest']),
|
||||||
AppLocation.get_section_data_path('remotes') / 'remote.zip'):
|
AppLocation.get_section_data_path('remotes') / 'remote.zip'):
|
||||||
deploy_zipfile(AppLocation.get_section_data_path('remotes'), 'remote.zip')
|
deploy_zipfile(AppLocation.get_section_data_path('remotes'), 'remote.zip')
|
||||||
return version_info['latest']['version']
|
return version_info['latest']['version']
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def check_for_remote_update(main_window):
|
||||||
|
"""
|
||||||
|
Run a thread to download and check the version of OpenLP
|
||||||
|
|
||||||
|
:param MainWindow main_window: The OpenLP main window.
|
||||||
|
"""
|
||||||
|
last_check_date = Registry().get('settings').value('api/last version test')
|
||||||
|
if date.today().strftime('%Y-%m-%d') <= last_check_date:
|
||||||
|
log.debug('Version check skipped, last checked today')
|
||||||
|
return
|
||||||
|
worker = RemoteVersionWorker(Registry().get('settings').value('api/download version'))
|
||||||
|
worker.new_version.connect(main_window.on_new_remote_version)
|
||||||
|
# TODO: Use this to figure out if there's an Internet connection?
|
||||||
|
# worker.no_internet.connect(main_window.on_no_internet)
|
||||||
|
run_thread(worker, 'remote-version')
|
||||||
|
@ -44,7 +44,7 @@ class ApiTab(SettingsTab):
|
|||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
self.icon_path = UiIcons().remote
|
self.icon_path = UiIcons().remote
|
||||||
advanced_translated = translate('OpenLP.APITab', 'API')
|
advanced_translated = translate('OpenLP.APITab', 'API')
|
||||||
self.master_version = None
|
self._master_version = None
|
||||||
super(ApiTab, self).__init__(parent, 'api', advanced_translated)
|
super(ApiTab, self).__init__(parent, 'api', advanced_translated)
|
||||||
|
|
||||||
def setup_ui(self):
|
def setup_ui(self):
|
||||||
@ -192,7 +192,37 @@ class ApiTab(SettingsTab):
|
|||||||
self.password_label.setText(translate('RemotePlugin.RemoteTab', 'Password:'))
|
self.password_label.setText(translate('RemotePlugin.RemoteTab', 'Password:'))
|
||||||
self.current_version_label.setText(translate('RemotePlugin.RemoteTab', 'Current version:'))
|
self.current_version_label.setText(translate('RemotePlugin.RemoteTab', 'Current version:'))
|
||||||
self.master_version_label.setText(translate('RemotePlugin.RemoteTab', 'Latest version:'))
|
self.master_version_label.setText(translate('RemotePlugin.RemoteTab', 'Latest version:'))
|
||||||
self.unknown_version = translate('RemotePlugin.RemoteTab', '(unknown)')
|
self._unknown_version = translate('RemotePlugin.RemoteTab', '(unknown)')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def master_version(self):
|
||||||
|
"""
|
||||||
|
Property getter for the remote master version
|
||||||
|
"""
|
||||||
|
return self._master_version
|
||||||
|
|
||||||
|
@master_version.setter
|
||||||
|
def master_version(self, value):
|
||||||
|
"""
|
||||||
|
Property setter for the remote master version
|
||||||
|
"""
|
||||||
|
self._master_version = value
|
||||||
|
self.master_version_value.setText(self._master_version or self._unknown_version)
|
||||||
|
self.upgrade_button.setEnabled(self.can_enable_upgrade_button())
|
||||||
|
|
||||||
|
def can_enable_upgrade_button(self):
|
||||||
|
"""
|
||||||
|
Do a couple checks to set the upgrade button state
|
||||||
|
"""
|
||||||
|
return self.master_version_value.text() != self._unknown_version and \
|
||||||
|
self.master_version_value.text() != self.current_version_value.text()
|
||||||
|
|
||||||
|
def set_master_version(self):
|
||||||
|
"""
|
||||||
|
Check if the master version is not set, and set it to None to invoke the "unknown version" label
|
||||||
|
"""
|
||||||
|
if not self._master_version:
|
||||||
|
self.master_version = None
|
||||||
|
|
||||||
def set_urls(self):
|
def set_urls(self):
|
||||||
"""
|
"""
|
||||||
@ -222,13 +252,6 @@ class ApiTab(SettingsTab):
|
|||||||
break
|
break
|
||||||
return ip_address
|
return ip_address
|
||||||
|
|
||||||
def can_enable_upgrade_button(self):
|
|
||||||
"""
|
|
||||||
Do a couple checks to set the upgrade button state
|
|
||||||
"""
|
|
||||||
return self.master_version_value.text() != self.unknown_version and \
|
|
||||||
self.master_version_value.text() != self.current_version_value.text()
|
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
"""
|
"""
|
||||||
Load the configuration and update the server configuration if necessary
|
Load the configuration and update the server configuration if necessary
|
||||||
@ -243,8 +266,7 @@ class ApiTab(SettingsTab):
|
|||||||
self.user_id.setText(self.settings.value(self.settings_section + '/user id'))
|
self.user_id.setText(self.settings.value(self.settings_section + '/user id'))
|
||||||
self.password.setText(self.settings.value(self.settings_section + '/password'))
|
self.password.setText(self.settings.value(self.settings_section + '/password'))
|
||||||
self.current_version_value.setText(self.settings.value(self.settings_section + '/download version'))
|
self.current_version_value.setText(self.settings.value(self.settings_section + '/download version'))
|
||||||
self.master_version_value.setText(self.master_version or self.unknown_version)
|
self.set_master_version()
|
||||||
self.upgrade_button.setEnabled(self.can_enable_upgrade_button())
|
|
||||||
self.set_urls()
|
self.set_urls()
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
@ -287,8 +309,7 @@ class ApiTab(SettingsTab):
|
|||||||
app.process_events()
|
app.process_events()
|
||||||
version_info = download_version_info()
|
version_info = download_version_info()
|
||||||
app.process_events()
|
app.process_events()
|
||||||
self.master_version_value.setText(version_info['latest']['version'])
|
self.master_version = version_info['latest']['version']
|
||||||
self.upgrade_button.setEnabled(self.can_enable_upgrade_button())
|
|
||||||
app.process_events()
|
app.process_events()
|
||||||
app.set_normal_cursor()
|
app.set_normal_cursor()
|
||||||
app.process_events()
|
app.process_events()
|
||||||
|
@ -37,18 +37,19 @@ from traceback import format_exception
|
|||||||
|
|
||||||
from PyQt5 import QtCore, QtWebEngineWidgets, QtWidgets # noqa
|
from PyQt5 import QtCore, QtWebEngineWidgets, QtWidgets # noqa
|
||||||
|
|
||||||
from openlp.core.state import State
|
from openlp.core.api.deploy import check_for_remote_update
|
||||||
from openlp.core.common import is_macosx, is_win
|
from openlp.core.common import is_macosx, is_win
|
||||||
from openlp.core.common.applocation import AppLocation
|
from openlp.core.common.applocation import AppLocation
|
||||||
from openlp.core.common.mixins import LogMixin
|
|
||||||
from openlp.core.loader import loader
|
|
||||||
from openlp.core.common.i18n import LanguageManager, UiStrings, translate
|
from openlp.core.common.i18n import LanguageManager, UiStrings, translate
|
||||||
|
from openlp.core.common.mixins import LogMixin
|
||||||
from openlp.core.common.path import create_paths
|
from openlp.core.common.path import create_paths
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.common.settings import Settings
|
from openlp.core.common.settings import Settings
|
||||||
from openlp.core.display.screens import ScreenList
|
from openlp.core.display.screens import ScreenList
|
||||||
|
from openlp.core.loader import loader
|
||||||
from openlp.core.resources import qInitResources
|
from openlp.core.resources import qInitResources
|
||||||
from openlp.core.server import Server
|
from openlp.core.server import Server
|
||||||
|
from openlp.core.state import State
|
||||||
from openlp.core.ui.exceptionform import ExceptionForm
|
from openlp.core.ui.exceptionform import ExceptionForm
|
||||||
from openlp.core.ui.firsttimeform import FirstTimeForm
|
from openlp.core.ui.firsttimeform import FirstTimeForm
|
||||||
from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm
|
from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm
|
||||||
@ -140,6 +141,8 @@ class OpenLP(QtCore.QObject, LogMixin):
|
|||||||
self.main_window.first_time()
|
self.main_window.first_time()
|
||||||
if self.settings.value('core/update check'):
|
if self.settings.value('core/update check'):
|
||||||
check_for_update(self.main_window)
|
check_for_update(self.main_window)
|
||||||
|
if self.settings.value('api/update check'):
|
||||||
|
check_for_remote_update(self.main_window)
|
||||||
self.main_window.is_display_blank()
|
self.main_window.is_display_blank()
|
||||||
Registry().execute('bootstrap_completion')
|
Registry().execute('bootstrap_completion')
|
||||||
return self.exec()
|
return self.exec()
|
||||||
|
@ -196,6 +196,8 @@ class Settings(QtCore.QSettings):
|
|||||||
'api/ip address': '0.0.0.0',
|
'api/ip address': '0.0.0.0',
|
||||||
'api/thumbnails': True,
|
'api/thumbnails': True,
|
||||||
'api/download version': '0.0',
|
'api/download version': '0.0',
|
||||||
|
'api/last version test': '',
|
||||||
|
'api/update check': True,
|
||||||
'bibles/db type': 'sqlite',
|
'bibles/db type': 'sqlite',
|
||||||
'bibles/db username': '',
|
'bibles/db username': '',
|
||||||
'bibles/db password': '',
|
'bibles/db password': '',
|
||||||
|
@ -32,6 +32,7 @@ from tempfile import gettempdir
|
|||||||
|
|
||||||
from PyQt5 import QtCore, QtWidgets
|
from PyQt5 import QtCore, QtWidgets
|
||||||
|
|
||||||
|
from openlp.core.api.deploy import get_latest_size, download_and_check
|
||||||
from openlp.core.common import trace_error_handler
|
from openlp.core.common import trace_error_handler
|
||||||
from openlp.core.common.applocation import AppLocation
|
from openlp.core.common.applocation import AppLocation
|
||||||
from openlp.core.common.httputils import DownloadWorker, download_file, get_url_file_size, get_web_page
|
from openlp.core.common.httputils import DownloadWorker, download_file, get_url_file_size, get_web_page
|
||||||
@ -113,13 +114,13 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Returns the id of the next FirstTimePage to go to based on enabled plugins
|
Returns the id of the next FirstTimePage to go to based on enabled plugins
|
||||||
"""
|
"""
|
||||||
if FirstTimePage.Download < self.currentId() < FirstTimePage.Songs and self.songs_check_box.isChecked():
|
if FirstTimePage.Remote < self.currentId() < FirstTimePage.Songs and self.songs_check_box.isChecked():
|
||||||
# If the songs plugin is enabled then go to the songs page
|
# If the songs plugin is enabled then go to the songs page
|
||||||
return FirstTimePage.Songs
|
return FirstTimePage.Songs
|
||||||
elif FirstTimePage.Download < self.currentId() < FirstTimePage.Bibles and self.bible_check_box.isChecked():
|
elif FirstTimePage.Remote < self.currentId() < FirstTimePage.Bibles and self.bible_check_box.isChecked():
|
||||||
# Otherwise, if the Bibles plugin is enabled then go to the Bibles page
|
# Otherwise, if the Bibles plugin is enabled then go to the Bibles page
|
||||||
return FirstTimePage.Bibles
|
return FirstTimePage.Bibles
|
||||||
elif FirstTimePage.Download < self.currentId() < FirstTimePage.Themes:
|
elif FirstTimePage.Remote < self.currentId() < FirstTimePage.Themes:
|
||||||
# Otherwise, if the current page is somewhere between the Welcome and the Themes pages, go to the themes
|
# Otherwise, if the current page is somewhere between the Welcome and the Themes pages, go to the themes
|
||||||
return FirstTimePage.Themes
|
return FirstTimePage.Themes
|
||||||
else:
|
else:
|
||||||
@ -135,7 +136,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
if not self.has_web_access:
|
if not self.has_web_access:
|
||||||
return FirstTimePage.NoInternet
|
return FirstTimePage.NoInternet
|
||||||
else:
|
else:
|
||||||
return FirstTimePage.Songs
|
return FirstTimePage.Remote
|
||||||
elif self.currentId() == FirstTimePage.Progress:
|
elif self.currentId() == FirstTimePage.Progress:
|
||||||
return -1
|
return -1
|
||||||
elif self.currentId() == FirstTimePage.NoInternet:
|
elif self.currentId() == FirstTimePage.NoInternet:
|
||||||
@ -237,6 +238,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
self.has_run_wizard = self.settings.value('core/has run wizard')
|
self.has_run_wizard = self.settings.value('core/has run wizard')
|
||||||
create_paths(Path(gettempdir(), 'openlp'))
|
create_paths(Path(gettempdir(), 'openlp'))
|
||||||
self.theme_combo_box.clear()
|
self.theme_combo_box.clear()
|
||||||
|
self.remote_page.can_download_remote = False
|
||||||
self.button(QtWidgets.QWizard.CustomButton1).setVisible(False)
|
self.button(QtWidgets.QWizard.CustomButton1).setVisible(False)
|
||||||
if self.has_run_wizard:
|
if self.has_run_wizard:
|
||||||
self.songs_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songs').is_active())
|
self.songs_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songs').is_active())
|
||||||
@ -418,6 +420,9 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
for item in self.themes_list_widget.selectedItems():
|
for item in self.themes_list_widget.selectedItems():
|
||||||
size = get_url_file_size('{url}{file}'.format(url=self.themes_url, file=item.file_name))
|
size = get_url_file_size('{url}{file}'.format(url=self.themes_url, file=item.file_name))
|
||||||
self.max_progress += size
|
self.max_progress += size
|
||||||
|
# If we're downloading the remote, add it in here too
|
||||||
|
if self.remote_page.can_download_remote:
|
||||||
|
self.max_progress += get_latest_size()
|
||||||
except urllib.error.URLError:
|
except urllib.error.URLError:
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
critical_error_message_box(translate('OpenLP.FirstTimeWizard', 'Download Error'),
|
critical_error_message_box(translate('OpenLP.FirstTimeWizard', 'Download Error'),
|
||||||
@ -517,6 +522,15 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
if not download_file(self, '{url}{file}'.format(url=self.themes_url, file=item.file_name),
|
if not download_file(self, '{url}{file}'.format(url=self.themes_url, file=item.file_name),
|
||||||
themes_destination_path / item.file_name, item.sha256):
|
themes_destination_path / item.file_name, item.sha256):
|
||||||
missed_files.append('Theme: name'.format(name=item.file_name))
|
missed_files.append('Theme: name'.format(name=item.file_name))
|
||||||
|
# Remote
|
||||||
|
if self.remote_page.can_download_remote:
|
||||||
|
self._increment_progress_bar(self.downloading.format(name='Web Remote'), 0)
|
||||||
|
self.previous_size = 0
|
||||||
|
remote_version = download_and_check(self, can_update_range=False)
|
||||||
|
if remote_version:
|
||||||
|
self.settings.setValue('api/download version', remote_version)
|
||||||
|
else:
|
||||||
|
missed_files.append('Web Remote')
|
||||||
if missed_files:
|
if missed_files:
|
||||||
file_list = ''
|
file_list = ''
|
||||||
for entry in missed_files:
|
for entry in missed_files:
|
||||||
|
@ -29,6 +29,7 @@ from openlp.core.lib.ui import add_welcome_page
|
|||||||
from openlp.core.ui.icons import UiIcons
|
from openlp.core.ui.icons import UiIcons
|
||||||
|
|
||||||
from openlp.core.display.screens import ScreenList
|
from openlp.core.display.screens import ScreenList
|
||||||
|
from openlp.core.pages import GridLayoutPage
|
||||||
from openlp.core.widgets.widgets import ScreenSelectionWidget
|
from openlp.core.widgets.widgets import ScreenSelectionWidget
|
||||||
|
|
||||||
|
|
||||||
@ -42,10 +43,53 @@ class FirstTimePage(object):
|
|||||||
SampleOption = 3
|
SampleOption = 3
|
||||||
Download = 4
|
Download = 4
|
||||||
NoInternet = 5
|
NoInternet = 5
|
||||||
Songs = 6
|
Remote = 6
|
||||||
Bibles = 7
|
Songs = 7
|
||||||
Themes = 8
|
Bibles = 8
|
||||||
Progress = 9
|
Themes = 9
|
||||||
|
Progress = 10
|
||||||
|
|
||||||
|
|
||||||
|
class RemotePage(GridLayoutPage):
|
||||||
|
"""
|
||||||
|
A page for the web remote
|
||||||
|
"""
|
||||||
|
def setup_ui(self):
|
||||||
|
"""
|
||||||
|
Set up the page
|
||||||
|
"""
|
||||||
|
self.remote_label = QtWidgets.QLabel(self)
|
||||||
|
self.remote_label.setWordWrap(True)
|
||||||
|
self.remote_label.setObjectName('remote_label')
|
||||||
|
self.layout.addWidget(self.remote_label, 0, 0, 1, 4)
|
||||||
|
self.download_checkbox = QtWidgets.QCheckBox(self)
|
||||||
|
self.setObjectName('download_checkbox')
|
||||||
|
self.layout.addWidget(self.download_checkbox, 1, 1, 1, 3)
|
||||||
|
|
||||||
|
def retranslate_ui(self):
|
||||||
|
"""
|
||||||
|
Translate the interface
|
||||||
|
"""
|
||||||
|
self.remote_label.setText(translate('OpenLP.FirstTimeWizard', 'OpenLP has a web remote, which enables you to '
|
||||||
|
'control OpenLP from another computer, phone or tablet on the same network '
|
||||||
|
'as the OpenLP computer. OpenLP can download this web remote for you now, '
|
||||||
|
'or you can download it later via the remote settings.'))
|
||||||
|
self.download_checkbox.setText(translate('OpenLP.FirstTimeWizard', 'Yes, download the remote now'))
|
||||||
|
self.setTitle(translate('OpenLP.FirstTimeWizard', 'Web-based Remote Interface'))
|
||||||
|
self.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Please confirm if you want to download the web remote.'))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def can_download_remote(self):
|
||||||
|
"""
|
||||||
|
The get method of a property to determine if the user selected the "Download remote now" checkbox
|
||||||
|
"""
|
||||||
|
return self.download_checkbox.isChecked()
|
||||||
|
|
||||||
|
@can_download_remote.setter
|
||||||
|
def can_download_remote(self, value):
|
||||||
|
if not isinstance(value, bool):
|
||||||
|
raise TypeError('Must be a bool')
|
||||||
|
self.download_checkbox.setChecked(value)
|
||||||
|
|
||||||
|
|
||||||
class ThemeListWidget(QtWidgets.QListWidget):
|
class ThemeListWidget(QtWidgets.QListWidget):
|
||||||
@ -187,6 +231,9 @@ class UiFirstTimeWizard(object):
|
|||||||
self.alert_check_box.setObjectName('alert_check_box')
|
self.alert_check_box.setObjectName('alert_check_box')
|
||||||
self.plugin_layout.addWidget(self.alert_check_box)
|
self.plugin_layout.addWidget(self.alert_check_box)
|
||||||
first_time_wizard.setPage(FirstTimePage.Plugins, self.plugin_page)
|
first_time_wizard.setPage(FirstTimePage.Plugins, self.plugin_page)
|
||||||
|
# Web Remote page
|
||||||
|
self.remote_page = RemotePage(self)
|
||||||
|
first_time_wizard.setPage(FirstTimePage.Remote, self.remote_page)
|
||||||
# The song samples page
|
# The song samples page
|
||||||
self.songs_page = QtWidgets.QWizardPage()
|
self.songs_page = QtWidgets.QWizardPage()
|
||||||
self.songs_page.setObjectName('songs_page')
|
self.songs_page.setObjectName('songs_page')
|
||||||
|
@ -622,8 +622,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
|
|||||||
|
|
||||||
def on_new_version(self, version):
|
def on_new_version(self, version):
|
||||||
"""
|
"""
|
||||||
Notifies the user that a newer version of OpenLP is available.
|
Notifies the user that a newer version of OpenLP is available. Triggered by delay thread and cannot display
|
||||||
Triggered by delay thread and cannot display popup.
|
popup.
|
||||||
|
|
||||||
:param version: The Version to be displayed.
|
:param version: The Version to be displayed.
|
||||||
"""
|
"""
|
||||||
@ -632,6 +632,18 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
|
|||||||
'https://openlp.org/.').format(new=version, current=get_version()[u'full'])
|
'https://openlp.org/.').format(new=version, current=get_version()[u'full'])
|
||||||
QtWidgets.QMessageBox.question(self, translate('OpenLP.MainWindow', 'OpenLP Version Updated'), version_text)
|
QtWidgets.QMessageBox.question(self, translate('OpenLP.MainWindow', 'OpenLP Version Updated'), version_text)
|
||||||
|
|
||||||
|
def on_new_remote_version(self, version):
|
||||||
|
"""
|
||||||
|
Notifies the user that a newer version of the web remote is available. Triggered by delay thread and cannot
|
||||||
|
display popup.
|
||||||
|
|
||||||
|
:param version: The Version to be displayed.
|
||||||
|
"""
|
||||||
|
version_text = translate('OpenLP.MainWindow', 'Version {version} of the web remote is now available for '
|
||||||
|
'download.\nTo download this version, go to the Remote settings and click the Upgrade '
|
||||||
|
'button.').format(version=version)
|
||||||
|
self.information_message(translate('OpenLP.MainWindow', 'New Web Remote Version Available'), version_text)
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
"""
|
"""
|
||||||
Show the main form, as well as the display form
|
Show the main form, as well as the display form
|
||||||
|
@ -61,7 +61,7 @@ class CustomPlugin(Plugin):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def about():
|
def about():
|
||||||
about_text = translate('CustomPlugin', '<strong>Custom Slide Plugin </strong><br />The custom slide plugin '
|
about_text = translate('CustomPlugin', '<strong>Custom Slide Plugin</strong><br />The custom slide plugin '
|
||||||
'provides the ability to set up custom text slides that can be displayed on the screen '
|
'provides the ability to set up custom text slides that can be displayed on the screen '
|
||||||
'the same way songs are. This plugin provides greater freedom over the songs plugin.')
|
'the same way songs are. This plugin provides greater freedom over the songs plugin.')
|
||||||
return about_text
|
return about_text
|
||||||
|
@ -70,7 +70,9 @@ class OpenLyricsExport(RegistryProperties):
|
|||||||
xml = open_lyrics.song_to_xml(song)
|
xml = open_lyrics.song_to_xml(song)
|
||||||
tree = etree.ElementTree(etree.fromstring(xml.encode()))
|
tree = etree.ElementTree(etree.fromstring(xml.encode()))
|
||||||
filename = '{title} ({author})'.format(title=song.title,
|
filename = '{title} ({author})'.format(title=song.title,
|
||||||
author=', '.join([author.display_name for author in song.authors]))
|
author=', '.join([author.display_name for author in
|
||||||
|
sorted(song.authors,
|
||||||
|
key=lambda a: a.display_name)]))
|
||||||
filename = clean_filename(filename)
|
filename = clean_filename(filename)
|
||||||
# Ensure the filename isn't too long for some filesystems
|
# Ensure the filename isn't too long for some filesystems
|
||||||
path_length = len(str(self.save_path))
|
path_length = len(str(self.save_path))
|
||||||
|
@ -187,7 +187,22 @@ class TestOpenLPJSONDecoder(TestCase):
|
|||||||
"""
|
"""
|
||||||
# GIVEN: A JSON encoded string
|
# GIVEN: A JSON encoded string
|
||||||
json_string = '[{"parts": ["test", "path1"], "json_meta": {"class": "Path", "version": 1}}, ' \
|
json_string = '[{"parts": ["test", "path1"], "json_meta": {"class": "Path", "version": 1}}, ' \
|
||||||
'{"parts": ["test", "path2"], "json_meta": {"class": "Path", "version": 1}}]'
|
'{"parts": ["test", "path2"], "json_meta": {"class": "Path", "version": 1}}, ' \
|
||||||
|
'{"key": "value", "json_meta": {"class": "Object"}}]'
|
||||||
|
|
||||||
|
# WHEN: Decoding the string using the OpenLPJsonDecoder class
|
||||||
|
obj = json.loads(json_string, cls=OpenLPJSONDecoder)
|
||||||
|
|
||||||
|
# THEN: The object returned should be a python version of the JSON string
|
||||||
|
assert obj == [Path('test', 'path1'), Path('test', 'path2'), {'key': 'value', 'json_meta': {'class': 'Object'}}]
|
||||||
|
|
||||||
|
def test_json_decode_old_style(self):
|
||||||
|
"""
|
||||||
|
Test the OpenLPJsonDecoder when decoding a JSON string with an old-style Path object
|
||||||
|
"""
|
||||||
|
# GIVEN: A JSON encoded string
|
||||||
|
json_string = '[{"__Path__": ["test", "path1"]}, ' \
|
||||||
|
'{"__Path__": ["test", "path2"]}]'
|
||||||
|
|
||||||
# WHEN: Decoding the string using the OpenLPJsonDecoder class
|
# WHEN: Decoding the string using the OpenLPJsonDecoder class
|
||||||
obj = json.loads(json_string, cls=OpenLPJSONDecoder)
|
obj = json.loads(json_string, cls=OpenLPJSONDecoder)
|
||||||
@ -284,6 +299,19 @@ class TestPathSerializer(TestCase):
|
|||||||
# THEN: A JSON decodeable object should have been returned.
|
# THEN: A JSON decodeable object should have been returned.
|
||||||
assert obj == {'parts': (os.sep, 'base', 'path', 'to', 'fi.le'), "json_meta": {"class": "Path", "version": 1}}
|
assert obj == {'parts': (os.sep, 'base', 'path', 'to', 'fi.le'), "json_meta": {"class": "Path", "version": 1}}
|
||||||
|
|
||||||
|
def test_path_json_object_is_js(self):
|
||||||
|
"""
|
||||||
|
Test that `Path.json_object` creates a JSON decode-able object from a Path object
|
||||||
|
"""
|
||||||
|
# GIVEN: A Path object from openlp.core.common.path
|
||||||
|
path = Path('/base', 'path', 'to', 'fi.le')
|
||||||
|
|
||||||
|
# WHEN: Calling json_object
|
||||||
|
obj = PathSerializer().json_object(path, is_js=True, extra=1, args=2)
|
||||||
|
|
||||||
|
# THEN: A URI should be returned
|
||||||
|
assert obj == 'file:///base/path/to/fi.le'
|
||||||
|
|
||||||
def test_path_json_object_base_path(self):
|
def test_path_json_object_base_path(self):
|
||||||
"""
|
"""
|
||||||
Test that `Path.json_object` creates a JSON decode-able object from a Path object, that is relative to the
|
Test that `Path.json_object` creates a JSON decode-able object from a Path object, that is relative to the
|
||||||
|
@ -29,7 +29,7 @@ from PyQt5 import QtCore, QtWidgets
|
|||||||
|
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.ui.firsttimeform import FirstTimeForm, ThemeListWidgetItem
|
from openlp.core.ui.firsttimeform import FirstTimeForm, ThemeListWidgetItem
|
||||||
from openlp.core.ui.firsttimewizard import ThemeListWidget
|
from openlp.core.ui.firsttimewizard import RemotePage, ThemeListWidget
|
||||||
|
|
||||||
|
|
||||||
INVALID_CONFIG = """
|
INVALID_CONFIG = """
|
||||||
@ -367,6 +367,50 @@ def test_on_projectors_check_box_unchecked(mock_settings):
|
|||||||
mock_settings.setValue.assert_called_once_with('projector/show after wizard', True)
|
mock_settings.setValue.assert_called_once_with('projector/show after wizard', True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_remote_page_get_can_download_remote(ftf_app):
|
||||||
|
"""
|
||||||
|
Test that the `can_download_remote` property returns the correct value
|
||||||
|
"""
|
||||||
|
# GIVEN: A RemotePage object with a mocked out download_checkbox
|
||||||
|
remote_page = RemotePage(None)
|
||||||
|
remote_page.download_checkbox = MagicMock(**{"isChecked.return_value": True})
|
||||||
|
|
||||||
|
# WHEN: The can_download_remote property is accessed
|
||||||
|
result = remote_page.can_download_remote
|
||||||
|
|
||||||
|
# THEN: The result should be True
|
||||||
|
assert result is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_remote_page_set_can_download_remote(ftf_app):
|
||||||
|
"""
|
||||||
|
Test that the `can_download_remote` property sets the correct value
|
||||||
|
"""
|
||||||
|
# GIVEN: A RemotePage object with a mocked out download_checkbox
|
||||||
|
remote_page = RemotePage(None)
|
||||||
|
remote_page.download_checkbox = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: The can_download_remote property is set
|
||||||
|
remote_page.can_download_remote = False
|
||||||
|
|
||||||
|
# THEN: The result should be True
|
||||||
|
remote_page.download_checkbox.setChecked.assert_called_once_with(False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_remote_page_set_can_download_remote_not_bool(ftf_app):
|
||||||
|
"""
|
||||||
|
Test that the `can_download_remote` property throws an exception when the value is not a boolean
|
||||||
|
"""
|
||||||
|
# GIVEN: A RemotePage object with a mocked out download_checkbox
|
||||||
|
remote_page = RemotePage(None)
|
||||||
|
remote_page.download_checkbox = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: The can_download_remote property is set
|
||||||
|
# THEN: An exception is thrown
|
||||||
|
with pytest.raises(TypeError, match='Must be a bool'):
|
||||||
|
remote_page.can_download_remote = 'not a bool'
|
||||||
|
|
||||||
|
|
||||||
def test_theme_list_widget_resize(ftf_app):
|
def test_theme_list_widget_resize(ftf_app):
|
||||||
"""
|
"""
|
||||||
Test that the resizeEvent() method in the ThemeListWidget works correctly
|
Test that the resizeEvent() method in the ThemeListWidget works correctly
|
||||||
@ -382,5 +426,3 @@ def test_theme_list_widget_resize(ftf_app):
|
|||||||
|
|
||||||
# THEN: Check that the correct calculations were done
|
# THEN: Check that the correct calculations were done
|
||||||
mocked_setGridSize.assert_called_once_with(QtCore.QSize(149, 140))
|
mocked_setGridSize.assert_called_once_with(QtCore.QSize(149, 140))
|
||||||
|
|
||||||
# THEN: everything resizes correctly
|
|
||||||
|
33
tests/functional/openlp_plugins/alerts/test_plugin.py
Normal file
33
tests/functional/openlp_plugins/alerts/test_plugin.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2020 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, either version 3 of the License, or #
|
||||||
|
# (at your option) any later version. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, #
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||||
|
# GNU General Public License for more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License #
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||||
|
##########################################################################
|
||||||
|
"""
|
||||||
|
This module contains tests for the plugin class Alerts plugin.
|
||||||
|
"""
|
||||||
|
from openlp.plugins.alerts.alertsplugin import AlertsPlugin
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_about():
|
||||||
|
result = AlertsPlugin.about()
|
||||||
|
|
||||||
|
assert result == (
|
||||||
|
'<strong>Alerts Plugin</strong>'
|
||||||
|
'<br />The alert plugin controls the displaying of alerts on the display screen.'
|
||||||
|
)
|
0
tests/functional/openlp_plugins/bibles/test_mediaitem.py
Executable file → Normal file
0
tests/functional/openlp_plugins/bibles/test_mediaitem.py
Executable file → Normal file
34
tests/functional/openlp_plugins/bibles/test_plugin.py
Normal file
34
tests/functional/openlp_plugins/bibles/test_plugin.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2020 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, either version 3 of the License, or #
|
||||||
|
# (at your option) any later version. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, #
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||||
|
# GNU General Public License for more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License #
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||||
|
##########################################################################
|
||||||
|
"""
|
||||||
|
This module contains tests for the plugin class Bibles plugin.
|
||||||
|
"""
|
||||||
|
from openlp.plugins.bibles.bibleplugin import BiblePlugin
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_about():
|
||||||
|
result = BiblePlugin.about()
|
||||||
|
|
||||||
|
assert result == (
|
||||||
|
'<strong>Bible Plugin</strong>'
|
||||||
|
'<br />The Bible plugin provides the ability to display Bible '
|
||||||
|
'verses from different sources during the service.'
|
||||||
|
)
|
34
tests/functional/openlp_plugins/custom/test_plugin.py
Normal file
34
tests/functional/openlp_plugins/custom/test_plugin.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2020 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, either version 3 of the License, or #
|
||||||
|
# (at your option) any later version. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, #
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||||
|
# GNU General Public License for more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License #
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||||
|
##########################################################################
|
||||||
|
"""
|
||||||
|
This module contains tests for the plugin class Custom plugin.
|
||||||
|
"""
|
||||||
|
from openlp.plugins.custom.customplugin import CustomPlugin
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_about():
|
||||||
|
result = CustomPlugin.about()
|
||||||
|
|
||||||
|
assert result == (
|
||||||
|
'<strong>Custom Slide Plugin</strong><br />The custom slide plugin '
|
||||||
|
'provides the ability to set up custom text slides that can be displayed on the screen '
|
||||||
|
'the same way songs are. This plugin provides greater freedom over the songs plugin.'
|
||||||
|
)
|
42
tests/functional/openlp_plugins/images/test_plugin.py
Normal file
42
tests/functional/openlp_plugins/images/test_plugin.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2020 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, either version 3 of the License, or #
|
||||||
|
# (at your option) any later version. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, #
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||||
|
# GNU General Public License for more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License #
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||||
|
##########################################################################
|
||||||
|
"""
|
||||||
|
This module contains tests for the plugin class Images plugin.
|
||||||
|
"""
|
||||||
|
from openlp.plugins.images.imageplugin import ImagePlugin
|
||||||
|
|
||||||
|
|
||||||
|
def test_image_plugin_about():
|
||||||
|
result = ImagePlugin.about()
|
||||||
|
|
||||||
|
assert result == (
|
||||||
|
'<strong>Image Plugin</strong>'
|
||||||
|
'<br />The image plugin provides displaying of images.<br />One '
|
||||||
|
'of the distinguishing features of this plugin is the ability to '
|
||||||
|
'group a number of images together in the service manager, making '
|
||||||
|
'the displaying of multiple images easier. This plugin can also '
|
||||||
|
'make use of OpenLP\'s "timed looping" feature to create a slide '
|
||||||
|
'show that runs automatically. In addition to this, images from '
|
||||||
|
'the plugin can be used to override the current theme\'s '
|
||||||
|
'background, which renders text-based items like songs with the '
|
||||||
|
'selected image as a background instead of the background '
|
||||||
|
'provided by the theme.'
|
||||||
|
)
|
33
tests/functional/openlp_plugins/media/test_plugin.py
Normal file
33
tests/functional/openlp_plugins/media/test_plugin.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2020 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, either version 3 of the License, or #
|
||||||
|
# (at your option) any later version. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, #
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||||
|
# GNU General Public License for more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License #
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||||
|
##########################################################################
|
||||||
|
"""
|
||||||
|
This module contains tests for the plugin class Media plugin.
|
||||||
|
"""
|
||||||
|
from openlp.plugins.media.mediaplugin import MediaPlugin
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_about():
|
||||||
|
result = MediaPlugin.about()
|
||||||
|
|
||||||
|
assert result == (
|
||||||
|
'<strong>Media Plugin</strong>'
|
||||||
|
'<br />The media plugin provides playback of audio and video.'
|
||||||
|
)
|
36
tests/functional/openlp_plugins/presentations/test_plugin.py
Normal file
36
tests/functional/openlp_plugins/presentations/test_plugin.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2020 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, either version 3 of the License, or #
|
||||||
|
# (at your option) any later version. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, #
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||||
|
# GNU General Public License for more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License #
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||||
|
##########################################################################
|
||||||
|
"""
|
||||||
|
This module contains tests for the plugin class Presentation plugin.
|
||||||
|
"""
|
||||||
|
from openlp.plugins.presentations.presentationplugin import PresentationPlugin
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_about():
|
||||||
|
result = PresentationPlugin.about()
|
||||||
|
|
||||||
|
assert result == (
|
||||||
|
'<strong>Presentation '
|
||||||
|
'Plugin</strong><br />The presentation plugin provides the '
|
||||||
|
'ability to show presentations using a number of different '
|
||||||
|
'programs. The choice of available presentation programs is '
|
||||||
|
'available to the user in a drop down box.'
|
||||||
|
)
|
@ -24,93 +24,108 @@ This module contains tests for the OpenLyrics song importer.
|
|||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
from unittest import TestCase
|
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from openlp.core.common.registry import Registry
|
import pytest
|
||||||
from openlp.core.common.settings import Settings
|
|
||||||
|
# from openlp.core.common.registry import Registry
|
||||||
from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport
|
from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport
|
||||||
from tests.helpers.testmixin import TestMixin
|
|
||||||
|
|
||||||
|
|
||||||
class TestOpenLyricsExport(TestCase, TestMixin):
|
@pytest.yield_fixture
|
||||||
|
def temp_folder():
|
||||||
|
temp_path = Path(mkdtemp())
|
||||||
|
yield temp_path
|
||||||
|
shutil.rmtree(temp_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_same_filename(registry, settings, temp_folder):
|
||||||
"""
|
"""
|
||||||
Test the functions in the :mod:`openlyricsexport` module.
|
Test that files is not overwritten if songs has same title and author
|
||||||
"""
|
"""
|
||||||
def setUp(self):
|
# GIVEN: A mocked song_to_xml, 2 mocked songs, a mocked application and an OpenLyricsExport instance
|
||||||
"""
|
with patch('openlp.plugins.songs.lib.openlyricsexport.OpenLyrics.song_to_xml') as mocked_song_to_xml:
|
||||||
Create the registry
|
mocked_song_to_xml.return_value = '<?xml version="1.0" encoding="UTF-8"?>\n<empty/>'
|
||||||
"""
|
author = MagicMock()
|
||||||
Registry.create()
|
author.display_name = 'Test Author'
|
||||||
Registry().register('settings', Settings())
|
song = MagicMock()
|
||||||
self.temp_folder = Path(mkdtemp())
|
song.authors = [author]
|
||||||
|
song.title = 'Test Title'
|
||||||
|
parent = MagicMock()
|
||||||
|
parent.stop_export_flag = False
|
||||||
|
# mocked_application_object = MagicMock()
|
||||||
|
# Registry().register('application', mocked_application_object)
|
||||||
|
ol_export = OpenLyricsExport(parent, [song, song], temp_folder)
|
||||||
|
|
||||||
def tearDown(self):
|
# WHEN: Doing the export
|
||||||
"""
|
ol_export.do_export()
|
||||||
Cleanup
|
|
||||||
"""
|
|
||||||
shutil.rmtree(self.temp_folder)
|
|
||||||
|
|
||||||
def test_export_same_filename(self):
|
# THEN: The exporter should have created 2 files
|
||||||
"""
|
assert (temp_folder / '{title} ({display_name}).xml'.format(
|
||||||
Test that files is not overwritten if songs has same title and author
|
title=song.title, display_name=author.display_name)).exists() is True
|
||||||
"""
|
assert (temp_folder / '{title} ({display_name})-1.xml'.format(
|
||||||
# GIVEN: A mocked song_to_xml, 2 mocked songs, a mocked application and an OpenLyricsExport instance
|
title=song.title, display_name=author.display_name)).exists() is True
|
||||||
with patch('openlp.plugins.songs.lib.openlyricsexport.OpenLyrics.song_to_xml') as mocked_song_to_xml:
|
|
||||||
mocked_song_to_xml.return_value = '<?xml version="1.0" encoding="UTF-8"?>\n<empty/>'
|
|
||||||
author = MagicMock()
|
|
||||||
author.display_name = 'Test Author'
|
|
||||||
song = MagicMock()
|
|
||||||
song.authors = [author]
|
|
||||||
song.title = 'Test Title'
|
|
||||||
parent = MagicMock()
|
|
||||||
parent.stop_export_flag = False
|
|
||||||
mocked_application_object = MagicMock()
|
|
||||||
Registry().register('application', mocked_application_object)
|
|
||||||
ol_export = OpenLyricsExport(parent, [song, song], self.temp_folder)
|
|
||||||
|
|
||||||
# WHEN: Doing the export
|
|
||||||
ol_export.do_export()
|
|
||||||
|
|
||||||
# THEN: The exporter should have created 2 files
|
def test_export_sort_of_authers_filename(registry, settings, temp_folder):
|
||||||
assert (self.temp_folder / '{title} ({display_name}).xml'.format(
|
"""
|
||||||
title=song.title, display_name=author.display_name)).exists() is True
|
Test that files is not overwritten if songs has same title and author
|
||||||
assert (self.temp_folder / '{title} ({display_name})-1.xml'.format(
|
"""
|
||||||
title=song.title, display_name=author.display_name)).exists() is True
|
# GIVEN: A mocked song_to_xml, 1 mocked songs, a mocked application and an OpenLyricsExport instance
|
||||||
|
with patch('openlp.plugins.songs.lib.openlyricsexport.OpenLyrics.song_to_xml') as mocked_song_to_xml:
|
||||||
|
mocked_song_to_xml.return_value = '<?xml version="1.0" encoding="UTF-8"?>\n<empty/>'
|
||||||
|
authorA = MagicMock()
|
||||||
|
authorA.display_name = 'a Author'
|
||||||
|
authorB = MagicMock()
|
||||||
|
authorB.display_name = 'b Author'
|
||||||
|
songA = MagicMock()
|
||||||
|
songA.authors = [authorA, authorB]
|
||||||
|
songA.title = 'Test Title'
|
||||||
|
songB = MagicMock()
|
||||||
|
songB.authors = [authorB, authorA]
|
||||||
|
songB.title = 'Test Title'
|
||||||
|
|
||||||
def test_export_sort_of_authers_filename(self):
|
parent = MagicMock()
|
||||||
"""
|
parent.stop_export_flag = False
|
||||||
Test that files is not overwritten if songs has same title and author
|
# mocked_application_object = MagicMock()
|
||||||
"""
|
# Registry().register('application', mocked_application_object)
|
||||||
# GIVEN: A mocked song_to_xml, 1 mocked songs, a mocked application and an OpenLyricsExport instance
|
ol_export = OpenLyricsExport(parent, [songA, songB], temp_folder)
|
||||||
with patch('openlp.plugins.songs.lib.openlyricsexport.OpenLyrics.song_to_xml') as mocked_song_to_xml:
|
|
||||||
mocked_song_to_xml.return_value = '<?xml version="1.0" encoding="UTF-8"?>\n<empty/>'
|
|
||||||
authorA = MagicMock()
|
|
||||||
authorA.display_name = 'a Author'
|
|
||||||
authorB = MagicMock()
|
|
||||||
authorB.display_name = 'b Author'
|
|
||||||
songA = MagicMock()
|
|
||||||
songA.authors = [authorA, authorB]
|
|
||||||
songA.title = 'Test Title'
|
|
||||||
songB = MagicMock()
|
|
||||||
songB.authors = [authorB, authorA]
|
|
||||||
songB.title = 'Test Title'
|
|
||||||
|
|
||||||
parent = MagicMock()
|
# WHEN: Doing the export
|
||||||
parent.stop_export_flag = False
|
ol_export.do_export()
|
||||||
mocked_application_object = MagicMock()
|
|
||||||
Registry().register('application', mocked_application_object)
|
|
||||||
ol_export = OpenLyricsExport(parent, [songA, songB], self.temp_folder)
|
|
||||||
|
|
||||||
# WHEN: Doing the export
|
# THEN: The exporter orders authers
|
||||||
ol_export.do_export()
|
assert (temp_folder / '{title} ({display_name}).xml'.format(
|
||||||
|
title=songA.title,
|
||||||
|
display_name=", ".join([authorA.display_name, authorB.display_name])
|
||||||
|
)).exists() is True
|
||||||
|
assert (temp_folder / '{title} ({display_name})-1.xml'.format(
|
||||||
|
title=songB.title,
|
||||||
|
display_name=", ".join([authorA.display_name, authorB.display_name])
|
||||||
|
)).exists() is True
|
||||||
|
|
||||||
# THEN: The exporter orders authers
|
|
||||||
assert (self.temp_folder / '{title} ({display_name}).xml'.format(
|
def test_export_is_stopped(registry, settings, temp_folder):
|
||||||
title=song.title,
|
"""
|
||||||
display_name=", ".join([authorA.display_name, authorB.display_name])
|
Test that the exporter stops when the flag is set
|
||||||
)).exists() is True
|
"""
|
||||||
assert (self.temp_folder / '{title} ({display_name})-1.xml'.format(
|
# GIVEN: A mocked song_to_xml, a mocked song, a mocked application and an OpenLyricsExport instance
|
||||||
title=song.title,
|
with patch('openlp.plugins.songs.lib.openlyricsexport.OpenLyrics.song_to_xml') as mocked_song_to_xml:
|
||||||
display_name=", ".join([authorA.display_name, authorB.display_name])
|
mocked_song_to_xml.return_value = '<?xml version="1.0" encoding="UTF-8"?>\n<empty/>'
|
||||||
)).exists() is True
|
author = MagicMock()
|
||||||
|
author.display_name = 'Test Author'
|
||||||
|
song = MagicMock()
|
||||||
|
song.authors = [author]
|
||||||
|
song.title = 'Test Title'
|
||||||
|
parent = MagicMock()
|
||||||
|
parent.stop_export_flag = True
|
||||||
|
# mocked_application_object = MagicMock()
|
||||||
|
# Registry().register('application', mocked_application_object)
|
||||||
|
ol_export = OpenLyricsExport(parent, [song, song], temp_folder)
|
||||||
|
|
||||||
|
# WHEN: Doing the export
|
||||||
|
ol_export.do_export()
|
||||||
|
|
||||||
|
# THEN: The exporter should not have created any files
|
||||||
|
assert (temp_folder / '{title} ({display_name}).xml'.format(
|
||||||
|
title=song.title, display_name=author.display_name)).exists() is False
|
||||||
|
33
tests/functional/openlp_plugins/songs/test_plugin.py
Normal file
33
tests/functional/openlp_plugins/songs/test_plugin.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2020 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, either version 3 of the License, or #
|
||||||
|
# (at your option) any later version. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, #
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||||
|
# GNU General Public License for more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License #
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||||
|
##########################################################################
|
||||||
|
"""
|
||||||
|
This module contains tests for the plugin class Song plugin.
|
||||||
|
"""
|
||||||
|
from openlp.plugins.songs.songsplugin import SongsPlugin
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_about():
|
||||||
|
result = SongsPlugin.about()
|
||||||
|
|
||||||
|
assert result == (
|
||||||
|
'<strong>Songs Plugin</strong>'
|
||||||
|
'<br />The songs plugin provides the ability to display and manage songs.'
|
||||||
|
)
|
@ -22,7 +22,7 @@
|
|||||||
Package to test the openlp.plugins.planningcenter.planningcenterplugin package.
|
Package to test the openlp.plugins.planningcenter.planningcenterplugin package.
|
||||||
"""
|
"""
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from unittest.mock import patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
|
|
||||||
@ -152,3 +152,16 @@ class TestPlanningCenterPlugin(TestCase, TestMixin):
|
|||||||
return_value = self.plugin.about()
|
return_value = self.plugin.about()
|
||||||
# THEN:
|
# THEN:
|
||||||
self.assertGreater(len(return_value), 0, "About function returned some text")
|
self.assertGreater(len(return_value), 0, "About function returned some text")
|
||||||
|
|
||||||
|
def test_finalise(self):
|
||||||
|
"""
|
||||||
|
Test that the finalise function cleans up after the plugin
|
||||||
|
"""
|
||||||
|
# GIVEN: A PlanningcenterPlugin Class with a bunch of mocks
|
||||||
|
self.plugin.import_planning_center = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: finalise has been called on the class
|
||||||
|
self.plugin.finalise()
|
||||||
|
|
||||||
|
# THEN: it cleans up after itself
|
||||||
|
self.plugin.import_planning_center.setVisible.assert_called_once_with(False)
|
||||||
|
Loading…
Reference in New Issue
Block a user