String conversions with templates part 1

- Fix string format key error in first time wizard from previous string conversion
- Fix projector pjlink1 test to use MagicMock
- Part 1 string conversions where format template is a string variable that's filled later
- Update projectordb test
- Fix a test on htmlbuilder string formatting
- Revert openlp/core/lib/htmlbuilder.py
- Convert renderer to use Template()
- Added test for renderer css
- Merge trunk and fix conflict
- Fix formatting in excep...

bzr-revno: 2687
This commit is contained in:
Ken Roberts 2016-08-10 19:31:12 +01:00 committed by Tim Bentley
commit d786382b9a
20 changed files with 341 additions and 81 deletions

View File

@ -1,5 +0,0 @@
[run]
source = openlp
[html]
directory = coverage

View File

@ -81,8 +81,8 @@ class UiStrings(object):
self.Export = translate('OpenLP.Ui', 'Export') self.Export = translate('OpenLP.Ui', 'Export')
self.File = translate('OpenLP.Ui', 'File') self.File = translate('OpenLP.Ui', 'File')
self.FileNotFound = translate('OpenLP.Ui', 'File Not Found') self.FileNotFound = translate('OpenLP.Ui', 'File Not Found')
# TODO: Check before converting to python3 string self.FileNotFoundMessage = translate('OpenLP.Ui',
self.FileNotFoundMessage = translate('OpenLP.Ui', 'File %s not found.\nPlease try selecting it individually.') 'File {name} not found.\nPlease try selecting it individually.')
self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit') self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit')
self.Help = translate('OpenLP.Ui', 'Help') self.Help = translate('OpenLP.Ui', 'Help')
self.Hours = translate('OpenLP.Ui', 'h', 'The abbreviated unit for hours') self.Hours = translate('OpenLP.Ui', 'h', 'The abbreviated unit for hours')
@ -141,8 +141,8 @@ class UiStrings(object):
self.Split = translate('OpenLP.Ui', 'Optional &Split') self.Split = translate('OpenLP.Ui', 'Optional &Split')
self.SplitToolTip = translate('OpenLP.Ui', self.SplitToolTip = translate('OpenLP.Ui',
'Split a slide into two only if it does not fit on the screen as one slide.') 'Split a slide into two only if it does not fit on the screen as one slide.')
# TODO: Check before converting to python3 string # TODO: WHERE is this used at? cannot find where it's used at in code.
self.StartTimeCode = translate('OpenLP.Ui', 'Start %s') self.StartTimeCode = translate('OpenLP.Ui', 'Start {code}')
self.StopPlaySlidesInLoop = translate('OpenLP.Ui', 'Stop Play Slides in Loop') self.StopPlaySlidesInLoop = translate('OpenLP.Ui', 'Stop Play Slides in Loop')
self.StopPlaySlidesToEnd = translate('OpenLP.Ui', 'Stop Play Slides to End') self.StopPlaySlidesToEnd = translate('OpenLP.Ui', 'Stop Play Slides to End')
self.Theme = translate('OpenLP.Ui', 'Theme', 'Singular') self.Theme = translate('OpenLP.Ui', 'Theme', 'Singular')

View File

@ -323,8 +323,7 @@ def create_separated_list(string_list):
return '' return ''
elif len(string_list) == 1: elif len(string_list) == 1:
return string_list[0] return string_list[0]
# TODO: # TODO: Verify mocking of translate() test before conversion
# Cannot convert these strings to python3 yet until I can figure out how to mock translate() with the new format
elif len(string_list) == 2: elif len(string_list) == 2:
return translate('OpenLP.core.lib', '%s and %s', return translate('OpenLP.core.lib', '%s and %s',
'Locale list separator: 2 items') % (string_list[0], string_list[1]) 'Locale list separator: 2 items') % (string_list[0], string_list[1])

View File

@ -51,9 +51,8 @@ class FileDialog(QtWidgets.QFileDialog):
file = parse.unquote(file) file = parse.unquote(file)
if not os.path.exists(file): if not os.path.exists(file):
log.error('File {text} not found.'.format(text=file)) log.error('File {text} not found.'.format(text=file))
# TODO: Test with UiStrings() before converting to python3 strings
QtWidgets.QMessageBox.information(parent, UiStrings().FileNotFound, QtWidgets.QMessageBox.information(parent, UiStrings().FileNotFound,
UiStrings().FileNotFoundMessage % file) UiStrings().FileNotFoundMessage.format(name=file))
continue continue
file_list.append(file) file_list.append(file)
return file_list return file_list

View File

@ -436,7 +436,7 @@ class PJLink1(QTcpSocket):
return return
return self.process_command(cmd, data) return self.process_command(cmd, data)
@pyqtSlot(int) @pyqtSlot(QAbstractSocket.SocketError)
def get_error(self, err): def get_error(self, err):
""" """
Process error from SocketError signal. Process error from SocketError signal.

View File

@ -22,6 +22,7 @@
import re import re
from string import Template
from PyQt5 import QtGui, QtCore, QtWebKitWidgets from PyQt5 import QtGui, QtCore, QtWebKitWidgets
from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, RegistryMixin, Settings from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, RegistryMixin, Settings
@ -370,8 +371,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
self.web.resize(self.page_width, self.page_height) self.web.resize(self.page_width, self.page_height)
self.web_frame = self.web.page().mainFrame() self.web_frame = self.web.page().mainFrame()
# Adjust width and height to account for shadow. outline done in css. # Adjust width and height to account for shadow. outline done in css.
# TODO: Verify before converting to python3 strings html = Template("""<!DOCTYPE html><html><head><script>
html = """<!DOCTYPE html><html><head><script>
function show_text(newtext) { function show_text(newtext) {
var main = document.getElementById('main'); var main = document.getElementById('main');
main.innerHTML = newtext; main.innerHTML = newtext;
@ -380,12 +380,16 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
// returned value). // returned value).
return main.offsetHeight; return main.offsetHeight;
} }
</script><style>*{margin: 0; padding: 0; border: 0;} </script>
#main {position: absolute; top: 0px; %s %s}</style></head><body> <style>
<div id="main"></div></body></html>""" % \ *{margin: 0; padding: 0; border: 0;}
(build_lyrics_format_css(theme_data, self.page_width, self.page_height), #main {position: absolute; top: 0px; ${format_css} ${outline_css}}
build_lyrics_outline_css(theme_data)) </style></head>
self.web.setHtml(html) <body><div id="main"></div></body></html>""")
self.web.setHtml(html.substitute(format_css=build_lyrics_format_css(theme_data,
self.page_width,
self.page_height),
outline_css=build_lyrics_outline_css(theme_data)))
self.empty_height = self.web_frame.contentsSize().height() self.empty_height = self.web_frame.contentsSize().height()
def _paginate_slide(self, lines, line_end): def _paginate_slide(self, lines, line_end):

View File

@ -514,8 +514,8 @@ class ThemeXML(object):
theme_strings = [] theme_strings = []
for key in dir(self): for key in dir(self):
if key[0:1] != '_': if key[0:1] != '_':
# TODO: Verify spacing format before converting to python3 string # TODO: Due to bound methods returned, I don't know how to write a proper test
theme_strings.append('%30s: %s' % (key, getattr(self, key))) theme_strings.append('{key:>30}: {value}'.format(key=key, value=getattr(self, key)))
return '\n'.join(theme_strings) return '\n'.join(theme_strings)
def _build_xml_from_attrs(self): def _build_xml_from_attrs(self):

View File

@ -90,13 +90,12 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
super(ExceptionForm, self).__init__(None, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) super(ExceptionForm, self).__init__(None, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
self.settings_section = 'crashreport' self.settings_section = 'crashreport'
# TODO: Need to see how to format strings when string with tags is actually a variable
self.report_text = '**OpenLP Bug Report**\n' \ self.report_text = '**OpenLP Bug Report**\n' \
'Version: %s\n\n' \ 'Version: {version}\n\n' \
'--- Details of the Exception. ---\n\n%s\n\n ' \ '--- Details of the Exception. ---\n\n{description}\n\n ' \
'--- Exception Traceback ---\n%s\n' \ '--- Exception Traceback ---\n{traceback}\n' \
'--- System information ---\n%s\n' \ '--- System information ---\n{system}\n' \
'--- Library Versions ---\n%s\n' '--- Library Versions ---\n{libs}\n'
def exec(self): def exec(self):
""" """
@ -132,7 +131,9 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
system += 'Desktop: GNOME\n' system += 'Desktop: GNOME\n'
elif os.environ.get('DESKTOP_SESSION') == 'xfce': elif os.environ.get('DESKTOP_SESSION') == 'xfce':
system += 'Desktop: Xfce\n' system += 'Desktop: Xfce\n'
return openlp_version, description, traceback, system, libraries # NOTE: Keys match the expected input for self.report_text.format()
return {'version': openlp_version, 'description': description, 'traceback': traceback,
'system': system, 'libs': libraries}
def on_save_report_button_clicked(self): def on_save_report_button_clicked(self):
""" """
@ -146,7 +147,9 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
if filename: if filename:
filename = str(filename).replace('/', os.path.sep) filename = str(filename).replace('/', os.path.sep)
Settings().setValue(self.settings_section + '/last directory', os.path.dirname(filename)) Settings().setValue(self.settings_section + '/last directory', os.path.dirname(filename))
report_text = self.report_text % self._create_report() opts = self._create_report()
report_text = self.report_text.format(version=opts['version'], description=opts['description'],
traceback=opts['traceback'], libs=opts['libs'], system=opts['system'])
try: try:
report_file = open(filename, 'w') report_file = open(filename, 'w')
try: try:
@ -169,7 +172,7 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
content = self._create_report() content = self._create_report()
source = '' source = ''
exception = '' exception = ''
for line in content[2].split('\n'): for line in content['traceback'].split('\n'):
if re.search(r'[/\\]openlp[/\\]', line): if re.search(r'[/\\]openlp[/\\]', line):
source = re.sub(r'.*[/\\]openlp[/\\](.*)".*', r'\1', line) source = re.sub(r'.*[/\\]openlp[/\\](.*)".*', r'\1', line)
if ':' in line: if ':' in line:
@ -177,8 +180,11 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
subject = 'Bug report: {error} in {source}'.format(error=exception, source=source) subject = 'Bug report: {error} in {source}'.format(error=exception, source=source)
mail_urlquery = QtCore.QUrlQuery() mail_urlquery = QtCore.QUrlQuery()
mail_urlquery.addQueryItem('subject', subject) mail_urlquery.addQueryItem('subject', subject)
# TODO: Find out how to format() text that is in a variable mail_urlquery.addQueryItem('body', self.report_text.format(version=content['version'],
mail_urlquery.addQueryItem('body', self.report_text % content) description=content['description'],
traceback=content['traceback'],
system=content['system'],
libs=content['libs']))
if self.file_attachment: if self.file_attachment:
mail_urlquery.addQueryItem('attach', self.file_attachment) mail_urlquery.addQueryItem('attach', self.file_attachment)
mail_to_url = QtCore.QUrl('mailto:bugs@openlp.org') mail_to_url = QtCore.QUrl('mailto:bugs@openlp.org')
@ -208,7 +214,7 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
Settings().value(self.settings_section + Settings().value(self.settings_section +
'/last directory'), '/last directory'),
'{text} (*)'.format(text=UiStrings().AllFiles)) '{text} (*)'.format(text=UiStrings().AllFiles))
log.info('New files(s) %s', str(files)) log.info('New files(s) {files}'.format(files=str(files)))
if files: if files:
self.file_attachment = str(files) self.file_attachment = str(files)

View File

@ -207,8 +207,8 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
trace_error_handler(log) trace_error_handler(log)
self.update_screen_list_combo() self.update_screen_list_combo()
self.application.process_events() self.application.process_events()
# TODO: Figure out how to use a variable with format() # TODO: Tested at home
self.downloading = translate('OpenLP.FirstTimeWizard', 'Downloading %s...') self.downloading = translate('OpenLP.FirstTimeWizard', 'Downloading {name}...')
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())
self.bible_check_box.setChecked(self.plugin_manager.get_plugin_by_name('bibles').is_active()) self.bible_check_box.setChecked(self.plugin_manager.get_plugin_by_name('bibles').is_active())
@ -632,7 +632,8 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
item = self.songs_list_widget.item(i) item = self.songs_list_widget.item(i)
if item.checkState() == QtCore.Qt.Checked: if item.checkState() == QtCore.Qt.Checked:
filename, sha256 = item.data(QtCore.Qt.UserRole) filename, sha256 = item.data(QtCore.Qt.UserRole)
self._increment_progress_bar(self.downloading % filename, 0) # TODO: Tested at home
self._increment_progress_bar(self.downloading.format(name=filename), 0)
self.previous_size = 0 self.previous_size = 0
destination = os.path.join(songs_destination, str(filename)) destination = os.path.join(songs_destination, str(filename))
if not self.url_get_file('{path}{name}'.format(path=self.songs_url, name=filename), if not self.url_get_file('{path}{name}'.format(path=self.songs_url, name=filename),
@ -644,7 +645,8 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
item = bibles_iterator.value() item = bibles_iterator.value()
if item.parent() and item.checkState(0) == QtCore.Qt.Checked: if item.parent() and item.checkState(0) == QtCore.Qt.Checked:
bible, sha256 = item.data(0, QtCore.Qt.UserRole) bible, sha256 = item.data(0, QtCore.Qt.UserRole)
self._increment_progress_bar(self.downloading % bible, 0) # TODO: Tested at home
self._increment_progress_bar(self.downloading.format(name=bible), 0)
self.previous_size = 0 self.previous_size = 0
if not self.url_get_file('{path}{name}'.format(path=self.bibles_url, name=bible), if not self.url_get_file('{path}{name}'.format(path=self.bibles_url, name=bible),
os.path.join(bibles_destination, bible), os.path.join(bibles_destination, bible),
@ -656,8 +658,8 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
item = self.themes_list_widget.item(i) item = self.themes_list_widget.item(i)
if item.checkState() == QtCore.Qt.Checked: if item.checkState() == QtCore.Qt.Checked:
theme, sha256 = item.data(QtCore.Qt.UserRole) theme, sha256 = item.data(QtCore.Qt.UserRole)
# TODO: Verify how to use format() with strings in a variable # TODO: Tested at home
self._increment_progress_bar(self.downloading % theme, 0) self._increment_progress_bar(self.downloading.format(name=theme), 0)
self.previous_size = 0 self.previous_size = 0
if not self.url_get_file('{path}{name}'.format(path=self.themes_url, name=theme), if not self.url_get_file('{path}{name}'.format(path=self.themes_url, name=theme),
os.path.join(themes_destination, theme), os.path.join(themes_destination, theme),

View File

@ -1333,7 +1333,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
self.recent_files_menu.clear() self.recent_files_menu.clear()
for file_id, filename in enumerate(recent_files_to_display): for file_id, filename in enumerate(recent_files_to_display):
log.debug('Recent file name: {name}'.format(name=filename)) log.debug('Recent file name: {name}'.format(name=filename))
# TODO: Verify ''.format() before committing # TODO: Should be good
action = create_action(self, '', action = create_action(self, '',
text='&{n} {name}'.format(n=file_id + 1, text='&{n} {name}'.format(n=file_id + 1,
name=os.path.splitext(os.path.basename(str(filename)))[0]), name=os.path.splitext(os.path.basename(str(filename)))[0]),

View File

@ -60,7 +60,7 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
self._clear_details() self._clear_details()
self.programatic_change = True self.programatic_change = True
plugin_list_width = 0 plugin_list_width = 0
# TODO: See how to use format() with variables # TODO: Tested at home
for plugin in self.plugin_manager.plugins: for plugin in self.plugin_manager.plugins:
item = QtWidgets.QListWidgetItem(self.plugin_list_widget) item = QtWidgets.QListWidgetItem(self.plugin_list_widget)
# We do this just to make 100% sure the status is an integer as # We do this just to make 100% sure the status is an integer as
@ -68,19 +68,19 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
plugin.status = int(plugin.status) plugin.status = int(plugin.status)
# Set the little status text in brackets next to the plugin name. # Set the little status text in brackets next to the plugin name.
if plugin.status == PluginStatus.Disabled: if plugin.status == PluginStatus.Disabled:
status_text = translate('OpenLP.PluginForm', '%s (Disabled)') status_text = translate('OpenLP.PluginForm', '{name} (Disabled)')
elif plugin.status == PluginStatus.Active: elif plugin.status == PluginStatus.Active:
status_text = translate('OpenLP.PluginForm', '%s (Active)') status_text = translate('OpenLP.PluginForm', '{name} (Active)')
else: else:
# PluginStatus.Inactive # PluginStatus.Inactive
status_text = translate('OpenLP.PluginForm', '%s (Inactive)') status_text = translate('OpenLP.PluginForm', '{name} (Inactive)')
item.setText(status_text % plugin.name_strings['singular']) item.setText(status_text.format(name=plugin.name_strings['singular']))
# If the plugin has an icon, set it! # If the plugin has an icon, set it!
if plugin.icon: if plugin.icon:
item.setIcon(plugin.icon) item.setIcon(plugin.icon)
self.plugin_list_widget.addItem(item) self.plugin_list_widget.addItem(item)
plugin_list_width = max(plugin_list_width, self.fontMetrics().width( plugin_list_width = max(plugin_list_width, self.fontMetrics().width(
translate('OpenLP.PluginForm', '%s (Inactive)') % plugin.name_strings['singular'])) translate('OpenLP.PluginForm', '{name} (Inactive)').format(name=plugin.name_strings['singular'])))
self.plugin_list_widget.setFixedWidth(plugin_list_width + self.plugin_list_widget.iconSize().width() + 48) self.plugin_list_widget.setFixedWidth(plugin_list_width + self.plugin_list_widget.iconSize().width() + 48)
def _clear_details(self): def _clear_details(self):
@ -137,13 +137,13 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
self.active_plugin.app_startup() self.active_plugin.app_startup()
else: else:
self.active_plugin.toggle_status(PluginStatus.Inactive) self.active_plugin.toggle_status(PluginStatus.Inactive)
# TODO: Verify using format() with a variable # TODO: Tested at home
status_text = translate('OpenLP.PluginForm', '%s (Inactive)') status_text = translate('OpenLP.PluginForm', '{name} (Inactive)')
if self.active_plugin.status == PluginStatus.Active: if self.active_plugin.status == PluginStatus.Active:
status_text = translate('OpenLP.PluginForm', '%s (Active)') status_text = translate('OpenLP.PluginForm', '{name} (Active)')
elif self.active_plugin.status == PluginStatus.Inactive: elif self.active_plugin.status == PluginStatus.Inactive:
status_text = translate('OpenLP.PluginForm', '%s (Inactive)') status_text = translate('OpenLP.PluginForm', '{name} (Inactive)')
elif self.active_plugin.status == PluginStatus.Disabled: elif self.active_plugin.status == PluginStatus.Disabled:
status_text = translate('OpenLP.PluginForm', '%s (Disabled)') status_text = translate('OpenLP.PluginForm', '{name} (Disabled)')
self.plugin_list_widget.currentItem().setText( self.plugin_list_widget.currentItem().setText(
status_text % self.active_plugin.name_strings['singular']) status_text.format(name=self.active_plugin.name_strings['singular']))

View File

@ -30,8 +30,8 @@ log.debug('editform loaded')
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import pyqtSlot, QSize from PyQt5.QtCore import pyqtSlot, QSize
from PyQt5.QtWidgets import QDialog, QButtonGroup, QDialogButtonBox, QFormLayout, QLineEdit, QRadioButton, \ from PyQt5.QtWidgets import QAbstractButton, QDialog, QButtonGroup, QDialogButtonBox, QFormLayout, QLineEdit, \
QStyle, QStylePainter, QStyleOptionTab, QTabBar, QTabWidget, QVBoxLayout, QWidget QRadioButton, QStyle, QStylePainter, QStyleOptionTab, QTabBar, QTabWidget, QVBoxLayout, QWidget
from openlp.core.common import translate, is_macosx from openlp.core.common import translate, is_macosx
from openlp.core.lib import build_icon from openlp.core.lib import build_icon
@ -452,7 +452,7 @@ class SourceSelectSingle(QDialog):
selected = super(SourceSelectSingle, self).exec() selected = super(SourceSelectSingle, self).exec()
return selected return selected
@pyqtSlot(object) @pyqtSlot(QAbstractButton)
def button_clicked(self, button): def button_clicked(self, button):
""" """
Checks which button was clicked Checks which button was clicked

View File

@ -464,9 +464,9 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
""" """
Background video button pushed. Background video button pushed.
""" """
# TODO: Check this before converting # TODO: Should work
visible_formats = '(%s)' % '; '.join(VIDEO_EXT) visible_formats = '({name})'.format(name='; '.join(VIDEO_EXT))
actual_formats = '(%s)' % ' '.join(VIDEO_EXT) actual_formats = '({name})'.format(name=' '.join(VIDEO_EXT))
video_filter = '{trans} {visible} {actual}'.format(trans=translate('OpenLP', 'Video Files'), video_filter = '{trans} {visible} {actual}'.format(trans=translate('OpenLP', 'Video Files'),
visible=visible_formats, actual=actual_formats) visible=visible_formats, actual=actual_formats)
video_filter = '{video};;{ui} (*.*)'.format(video=video_filter, ui=UiStrings().AllFiles) video_filter = '{video};;{ui} (*.*)'.format(video=video_filter, ui=UiStrings().AllFiles)

View File

@ -769,7 +769,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
'{count} time(s) by {plugin}' '{count} time(s) by {plugin}'
).format(name=used_count, ).format(name=used_count,
plugin=plugin.name))) plugin=plugin.name)))
plugin_usage = "%s\n" % plugin_usage plugin_usage = "{text}\n".format(text=plugin_usage)
if plugin_usage: if plugin_usage:
critical_error_message_box(translate('OpenLP.ThemeManager', 'Unable to delete theme'), critical_error_message_box(translate('OpenLP.ThemeManager', 'Unable to delete theme'),
translate('OpenLP.ThemeManager', translate('OpenLP.ThemeManager',

View File

@ -643,7 +643,7 @@ class OpenLyrics(object):
# Append text from tail and add formatting end tag. # Append text from tail and add formatting end tag.
# TODO: Verify format() with template variables # TODO: Verify format() with template variables
if element.tag == NSMAP % 'tag' and use_endtag: if element.tag == NSMAP % 'tag' and use_endtag:
text += '{{{name}}}'.format(name=element.get('name')) text += '{{/{name}}}'.format(name=element.get('name'))
# Append text from tail. # Append text from tail.
if element.tail: if element.tail:
text += element.tail text += element.tail

View File

@ -60,7 +60,7 @@ class TestFileDialog(TestCase):
self.mocked_os.path.exists.side_effect = lambda file_name: file_name in [ self.mocked_os.path.exists.side_effect = lambda file_name: file_name in [
'/Valid File', '/url encoded file #1'] '/Valid File', '/url encoded file #1']
self.mocked_ui_strings().FileNotFound = 'File Not Found' self.mocked_ui_strings().FileNotFound = 'File Not Found'
self.mocked_ui_strings().FileNotFoundMessage = 'File %s not found.\nPlease try selecting it individually.' self.mocked_ui_strings().FileNotFoundMessage = 'File {name} not found.\nPlease try selecting it individually.'
# WHEN: FileDialog.getOpenFileNames is called # WHEN: FileDialog.getOpenFileNames is called
result = FileDialog.getOpenFileNames(self.mocked_parent) result = FileDialog.getOpenFileNames(self.mocked_parent)

View File

@ -29,26 +29,12 @@ from openlp.core.lib.projector.pjlink1 import PJLink1
from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_OFF, S_STANDBY, S_WARMUP, S_ON, \ from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_OFF, S_STANDBY, S_WARMUP, S_ON, \
S_COOLDOWN, PJLINK_POWR_STATUS S_COOLDOWN, PJLINK_POWR_STATUS
from tests.functional import patch from tests.functional import patch, MagicMock
from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE, TEST_HASH from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE, TEST_HASH
pjlink_test = PJLink1(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True) pjlink_test = PJLink1(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True)
class DummyTimer(object):
'''
Dummy class to fake timers
'''
def __init__(self, *args, **kwargs):
pass
def start(self, *args, **kwargs):
pass
def stop(self, *args, **kwargs):
pass
class TestPJLink(TestCase): class TestPJLink(TestCase):
""" """
Tests for the PJLink module Tests for the PJLink module
@ -308,8 +294,8 @@ class TestPJLink(TestCase):
pjlink.other_info = 'ANOTHER TEST' pjlink.other_info = 'ANOTHER TEST'
pjlink.send_queue = True pjlink.send_queue = True
pjlink.send_busy = True pjlink.send_busy = True
pjlink.timer = DummyTimer() pjlink.timer = MagicMock()
pjlink.socket_timer = DummyTimer() pjlink.socket_timer = MagicMock()
# WHEN: reset_information() is called # WHEN: reset_information() is called
with patch.object(pjlink.timer, 'stop') as mock_timer: with patch.object(pjlink.timer, 'stop') as mock_timer:

View File

@ -284,3 +284,16 @@ class TestProjectorDB(TestCase):
self.assertEqual(str(source), self.assertEqual(str(source),
'<ProjectorSource(id="1", code="11", text="First RGB source", projector_id="1")>', '<ProjectorSource(id="1", code="11", text="First RGB source", projector_id="1")>',
'ProjectorSource.__repr__)_ should have returned a proper representation string') 'ProjectorSource.__repr__)_ should have returned a proper representation string')
def test_get_projector_by_id_none(self):
"""
Test get_projector_by_id returns None if no db entry
"""
# GIVEN: Test object and data
projector = self.projector
# WHEN: DB search for entry not saved
results = projector.get_projector_by_id(dbid=123134556409824506)
# THEN: Verify return was None
self.assertEqual(results, None, 'Returned results should have equaled None')

View File

@ -29,6 +29,7 @@ from PyQt5 import QtCore
from openlp.core.common import Registry from openlp.core.common import Registry
from openlp.core.lib import Renderer, ScreenList, ServiceItem, FormattingTags from openlp.core.lib import Renderer, ScreenList, ServiceItem, FormattingTags
from openlp.core.lib.renderer import words_split, get_start_tags from openlp.core.lib.renderer import words_split, get_start_tags
from openlp.core.lib.theme import ThemeXML
from tests.functional import MagicMock, patch from tests.functional import MagicMock, patch
@ -39,6 +40,24 @@ SCREEN = {
} }
# WARNING: Leave formatting alone - this is how it's returned in renderer.py
CSS_TEST_ONE = """<!DOCTYPE html><html><head><script>
function show_text(newtext) {
var main = document.getElementById('main');
main.innerHTML = newtext;
// We need to be sure that the page is loaded, that is why we
// return the element's height (even though we do not use the
// returned value).
return main.offsetHeight;
}
</script>
<style>
*{margin: 0; padding: 0; border: 0;}
#main {position: absolute; top: 0px; FORMAT CSS; OUTLINE CSS; }
</style></head>
<body><div id="main"></div></body></html>'"""
class TestRenderer(TestCase): class TestRenderer(TestCase):
def setUp(self): def setUp(self):
@ -159,3 +178,28 @@ class TestRenderer(TestCase):
# THEN: The blanks have been removed. # THEN: The blanks have been removed.
self.assertListEqual(result_words, expected_words) self.assertListEqual(result_words, expected_words)
@patch('openlp.core.lib.renderer.QtWebKitWidgets.QWebView')
@patch('openlp.core.lib.renderer.build_lyrics_format_css')
@patch('openlp.core.lib.renderer.build_lyrics_outline_css')
def test_set_text_rectangle(self, mock_outline_css, mock_lyrics_css, mock_webview):
"""
Test set_text_rectangle returns a proper html string
"""
# GIVEN: test object and data
mock_lyrics_css.return_value = ' FORMAT CSS; '
mock_outline_css.return_value = ' OUTLINE CSS; '
theme_data = ThemeXML()
theme_data.font_main_name = 'Arial'
theme_data.font_main_size = 20
theme_data.font_main_color = '#FFFFFF'
theme_data.font_main_outline_color = '#FFFFFF'
main = QtCore.QRect(10, 10, 1280, 900)
foot = QtCore.QRect(10, 1000, 1260, 24)
renderer = Renderer()
# WHEN: Calling method
renderer._set_text_rectangle(theme_data=theme_data, rect_main=main, rect_footer=foot)
# THEN: QtWebKitWidgets should be called with the proper string
mock_webview.setHtml.called_with(CSS_TEST_ONE, 'Should be the same')

View File

@ -0,0 +1,212 @@
# -*- 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.exeptionform package.
"""
import os
import socket
import tempfile
import urllib
from unittest import TestCase
from unittest.mock import mock_open
from PyQt5.QtCore import QUrlQuery
from openlp.core.common import Registry
from openlp.core.ui.firsttimeform import FirstTimeForm
from tests.functional import MagicMock, patch
from tests.helpers.testmixin import TestMixin
from openlp.core.ui import exceptionform
exceptionform.WEBKIT_VERSION = 'Webkit Test'
exceptionform.MIGRATE_VERSION = 'Migrate Test'
exceptionform.CHARDET_VERSION = 'CHARDET Test'
exceptionform.ENCHANT_VERSION = 'Enchant Test'
exceptionform.MAKO_VERSION = 'Mako Test'
exceptionform.ICU_VERSION = 'ICU Test'
exceptionform.VLC_VERSION = 'VLC Test'
MAIL_ITEM_TEXT = ('**OpenLP Bug Report**\nVersion: Trunk Test\n\n--- Details of the Exception. ---\n\n'
'Description Test\n\n --- Exception Traceback ---\nopenlp: Traceback Test\n'
'--- System information ---\nPlatform: Nose Test\n\n--- Library Versions ---\n'
'Python: Python Test\nQt5: Qt5 test\nPyQt5: PyQt5 Test\nQtWebkit: Webkit Test\n'
'SQLAlchemy: SqlAlchemy Test\nSQLAlchemy Migrate: Migrate Test\nBeautifulSoup: BeautifulSoup Test\n'
'lxml: ETree Test\nChardet: CHARDET Test\nPyEnchant: Enchant Test\nMako: Mako Test\n'
'pyICU: ICU Test\npyUNO bridge: UNO Bridge Test\nVLC: VLC Test\n\n')
@patch("openlp.core.ui.exceptionform.Qt.qVersion")
@patch("openlp.core.ui.exceptionform.QtGui.QDesktopServices.openUrl")
@patch("openlp.core.ui.exceptionform.get_application_version")
@patch("openlp.core.ui.exceptionform.sqlalchemy")
@patch("openlp.core.ui.exceptionform.bs4")
@patch("openlp.core.ui.exceptionform.etree")
@patch("openlp.core.ui.exceptionform.is_linux")
@patch("openlp.core.ui.exceptionform.platform.platform")
@patch("openlp.core.ui.exceptionform.platform.python_version")
class TestExceptionForm(TestMixin, TestCase):
"""
Test functionality of exception form functions
"""
def __method_template_for_class_patches(self,
__PLACEHOLDER_FOR_LOCAL_METHOD_PATCH_DECORATORS_GO_HERE__,
mocked_python_version,
mocked_platform,
mocked_is_linux,
mocked_etree,
mocked_bs4,
mocked_sqlalchemy,
mocked_application_version,
mocked_openlurl,
mocked_qversion,
):
"""
Template so you don't have to remember the layout of class mock options for methods
"""
mocked_etree.__version__ = 'ETree Test'
mocked_bs4.__version__ = 'BeautifulSoup Test'
mocked_sqlalchemy.__version__ = 'SqlAlchemy Test'
mocked_python_version.return_value = 'Python Test'
mocked_platform.return_value = 'Nose Test'
mocked_qversion.return_value = 'Qt5 test'
mocked_is_linux.return_value = False
mocked_application_version.return_value = 'Trunk Test'
def setUp(self):
self.setup_application()
self.app.setApplicationVersion('0.0')
# Set up a fake "set_normal_cursor" method since we're not dealing with an actual OpenLP application object
self.app.set_normal_cursor = lambda: None
self.app.process_events = lambda: None
Registry.create()
Registry().register('application', self.app)
self.tempfile = os.path.join(tempfile.gettempdir(), 'testfile')
def tearDown(self):
if os.path.isfile(self.tempfile):
os.remove(self.tempfile)
@patch("openlp.core.ui.exceptionform.Ui_ExceptionDialog")
@patch("openlp.core.ui.exceptionform.QtWidgets.QFileDialog")
@patch("openlp.core.ui.exceptionform.QtCore.QUrl")
@patch("openlp.core.ui.exceptionform.QtCore.QUrlQuery.addQueryItem")
@patch("openlp.core.ui.exceptionform.Qt")
def test_on_send_report_button_clicked(self,
mocked_qt,
mocked_add_query_item,
mocked_qurl,
mocked_file_dialog,
mocked_ui_exception_dialog,
mocked_python_version,
mocked_platform,
mocked_is_linux,
mocked_etree,
mocked_bs4,
mocked_sqlalchemy,
mocked_application_version,
mocked_openlurl,
mocked_qversion,
):
"""
Test send report creates the proper system information text
"""
# GIVEN: Test environment
mocked_etree.__version__ = 'ETree Test'
mocked_bs4.__version__ = 'BeautifulSoup Test'
mocked_sqlalchemy.__version__ = 'SqlAlchemy Test'
mocked_python_version.return_value = 'Python Test'
mocked_platform.return_value = 'Nose Test'
mocked_qversion.return_value = 'Qt5 test'
mocked_is_linux.return_value = False
mocked_application_version.return_value = 'Trunk Test'
mocked_qt.PYQT_VERSION_STR = 'PyQt5 Test'
mocked_is_linux.return_value = False
mocked_application_version.return_value = 'Trunk Test'
test_form = exceptionform.ExceptionForm()
test_form.file_attachment = None
with patch.object(test_form, '_pyuno_import') as mock_pyuno:
with patch.object(test_form.exception_text_edit, 'toPlainText') as mock_traceback:
with patch.object(test_form.description_text_edit, 'toPlainText') as mock_description:
mock_pyuno.return_value = 'UNO Bridge Test'
mock_traceback.return_value = 'openlp: Traceback Test'
mock_description.return_value = 'Description Test'
# WHEN: on_save_report_button_clicked called
test_form.on_send_report_button_clicked()
# THEN: Verify strings were formatted properly
mocked_add_query_item.assert_called_with('body', MAIL_ITEM_TEXT)
@patch("openlp.core.ui.exceptionform.QtWidgets.QFileDialog.getSaveFileName")
@patch("openlp.core.ui.exceptionform.Qt")
def test_on_save_report_button_clicked(self,
mocked_qt,
mocked_save_filename,
mocked_python_version,
mocked_platform,
mocked_is_linux,
mocked_etree,
mocked_bs4,
mocked_sqlalchemy,
mocked_application_version,
mocked_openlurl,
mocked_qversion,
):
"""
Test save report saves the correct information to a file
"""
mocked_etree.__version__ = 'ETree Test'
mocked_bs4.__version__ = 'BeautifulSoup Test'
mocked_sqlalchemy.__version__ = 'SqlAlchemy Test'
mocked_python_version.return_value = 'Python Test'
mocked_platform.return_value = 'Nose Test'
mocked_qversion.return_value = 'Qt5 test'
mocked_qt.PYQT_VERSION_STR = 'PyQt5 Test'
mocked_is_linux.return_value = False
mocked_application_version.return_value = 'Trunk Test'
mocked_save_filename.return_value = ['testfile.txt', ]
test_form = exceptionform.ExceptionForm()
test_form.file_attachment = None
with patch.object(test_form, '_pyuno_import') as mock_pyuno:
with patch.object(test_form.exception_text_edit, 'toPlainText') as mock_traceback:
with patch.object(test_form.description_text_edit, 'toPlainText') as mock_description:
with patch("openlp.core.ui.exceptionform.open", mock_open(), create=True) as mocked_open:
mock_pyuno.return_value = 'UNO Bridge Test'
mock_traceback.return_value = 'openlp: Traceback Test'
mock_description.return_value = 'Description Test'
# WHEN: on_save_report_button_clicked called
test_form.on_save_report_button_clicked()
# THEN: Verify proper calls to save file
# self.maxDiff = None
check_text = "call().write({text})".format(text=MAIL_ITEM_TEXT.__repr__())
write_text = "{text}".format(text=mocked_open.mock_calls[1])
mocked_open.assert_called_with('testfile.txt', 'w')
self.assertEquals(check_text, write_text, "Saved information should match test text")