Merge lp:~phill-ridout/openlp/ftw-refactors into lp:openlp

Proposed by Phill
Status: Superseded
Proposed branch: lp:~phill-ridout/openlp/ftw-refactors
Merge into: lp:openlp
Diff against target: 1102 lines (+342/-228)
14 files modified
openlp/core/app.py (+1/-1)
openlp/core/common/httputils.py (+16/-8)
openlp/core/display/render.py (+2/-2)
openlp/core/threading.py (+2/-2)
openlp/core/ui/firsttimeform.py (+85/-94)
openlp/core/ui/firsttimewizard.py (+64/-62)
openlp/core/ui/mainwindow.py (+1/-2)
openlp/core/widgets/widgets.py (+28/-0)
openlp/plugins/songs/lib/importers/openlp.py (+1/-1)
openlp/plugins/songs/reporting.py (+0/-7)
tests/functional/openlp_core/common/test_httputils.py (+5/-9)
tests/functional/openlp_core/test_threading.py (+5/-9)
tests/functional/openlp_core/ui/test_firsttimeform.py (+126/-25)
tests/functional/openlp_plugins/songs/test_openlpimporter.py (+6/-6)
To merge this branch: bzr merge lp:~phill-ridout/openlp/ftw-refactors
Reviewer Review Type Date Requested Status
Tim Bentley Pending
Tomas Groth Pending
Review via email: mp+364186@code.launchpad.net

This proposal supersedes a proposal from 2019-03-08.

This proposal has been superseded by a proposal from 2019-03-10.

Commit message

Add proxy settings to ftw. Option to skip sample data

Description of the change

Add option to skip downloading.
Add dialog for proxy settings to FTW
FTW refactors.
General fixes.

To post a comment you must log in.
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests failed, please see https://ci.openlp.io/job/MP-02-Linux_Tests/89/ for more details

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests failed, please see https://ci.openlp.io/job/MP-02-Linux_Tests/90/ for more details

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linting failed, please see https://ci.openlp.io/job/MP-03-Linting/41/ for more details

Revision history for this message
Tomas Groth (tomasgroth) wrote : Posted in a previous version of this proposal

Some code style needs fixing - see link above

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linting failed, please see https://ci.openlp.io/job/MP-03-Linting/49/ for more details

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linting failed, please see https://ci.openlp.io/job/MP-03-Linting/50/ for more details

Revision history for this message
Tim Bentley (trb143) : Posted in a previous version of this proposal
review: Approve
Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Linux tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Linting passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

macOS tests failed, please see https://ci.openlp.io/job/MP-04-macOS-Tests/45/ for more details

2851. By Phill

mac test fix?

2852. By Phill

Test fixes

2853. By Phill

HEAD

2854. By Phill

Test fix

2855. By Phill

PEP8

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/app.py'
2--- openlp/core/app.py 2019-02-14 15:09:09 +0000
3+++ openlp/core/app.py 2019-03-09 07:06:25 +0000
4@@ -101,7 +101,7 @@
5 ftw.initialize(screens)
6 if ftw.exec() == QtWidgets.QDialog.Accepted:
7 Settings().setValue('core/has run wizard', True)
8- elif ftw.was_cancelled:
9+ else:
10 QtCore.QCoreApplication.exit()
11 sys.exit()
12 # Correct stylesheet bugs
13
14=== modified file 'openlp/core/common/httputils.py'
15--- openlp/core/common/httputils.py 2019-02-21 21:29:00 +0000
16+++ openlp/core/common/httputils.py 2019-03-09 07:06:25 +0000
17@@ -158,16 +158,20 @@
18 return response.text
19
20
21-def get_url_file_size(url):
22+def get_url_file_size(url, proxy=None):
23 """
24 Get the size of a file.
25
26 :param url: The URL of the file we want to download.
27+ :param dict | ProxyMode | None proxy: ProxyMode enum or a dictionary containing the proxy servers, with their types
28+ as the key e.g. {'http': 'http://proxyserver:port', 'https': 'https://proxyserver:port'}
29 """
30 retries = 0
31+ if not isinstance(proxy, dict):
32+ proxy = get_proxy_settings(mode=proxy)
33 while True:
34 try:
35- response = requests.head(url, timeout=float(CONNECTION_TIMEOUT), allow_redirects=True)
36+ response = requests.head(url, proxies=proxy, timeout=float(CONNECTION_TIMEOUT), allow_redirects=True)
37 return int(response.headers['Content-Length'])
38 except OSError:
39 if retries > CONNECTION_RETRIES:
40@@ -178,7 +182,7 @@
41 continue
42
43
44-def download_file(update_object, url, file_path, sha256=None):
45+def download_file(update_object, url, file_path, sha256=None, proxy=None):
46 """"
47 Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any
48 point. Returns False on download error.
49@@ -187,15 +191,19 @@
50 :param url: URL to download
51 :param file_path: Destination file
52 :param sha256: The check sum value to be checked against the download value
53+ :param dict | ProxyMode | None proxy: ProxyMode enum or a dictionary containing the proxy servers, with their types
54+ as the key e.g. {'http': 'http://proxyserver:port', 'https': 'https://proxyserver:port'}
55 """
56 block_count = 0
57 block_size = 4096
58 retries = 0
59+ if not isinstance(proxy, dict):
60+ proxy = get_proxy_settings(mode=proxy)
61 log.debug('url_get_file: %s', url)
62 while retries < CONNECTION_RETRIES:
63 try:
64 with file_path.open('wb') as saved_file:
65- response = requests.get(url, timeout=float(CONNECTION_TIMEOUT), stream=True)
66+ response = requests.get(url, proxies=proxy, timeout=float(CONNECTION_TIMEOUT), stream=True)
67 if sha256:
68 hasher = hashlib.sha256()
69 # Download until finished or canceled.
70@@ -244,21 +252,21 @@
71 """
72 self._base_url = base_url
73 self._file_name = file_name
74- self._download_cancelled = False
75+ self.was_cancelled = False
76 super().__init__()
77
78 def start(self):
79 """
80 Download the url to the temporary directory
81 """
82- if self._download_cancelled:
83+ if self.was_cancelled:
84 self.quit.emit()
85 return
86 try:
87 dest_path = Path(gettempdir()) / 'openlp' / self._file_name
88 url = '{url}{name}'.format(url=self._base_url, name=self._file_name)
89 is_success = download_file(self, url, dest_path)
90- if is_success and not self._download_cancelled:
91+ if is_success and not self.was_cancelled:
92 self.download_succeeded.emit(dest_path)
93 else:
94 self.download_failed.emit()
95@@ -273,4 +281,4 @@
96 """
97 A slot to allow the download to be cancelled from outside of the thread
98 """
99- self._download_cancelled = True
100+ self.was_cancelled = True
101
102=== modified file 'openlp/core/display/render.py'
103--- openlp/core/display/render.py 2019-02-19 21:38:44 +0000
104+++ openlp/core/display/render.py 2019-03-09 07:06:25 +0000
105@@ -43,8 +43,8 @@
106 log = logging.getLogger(__name__)
107
108 SLIM_CHARS = 'fiíIÍjlĺľrtť.,;/ ()|"\'!:\\'
109-CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)' # noqa
110- '([\u0080-\uFFFF,\w,\s,\.,\,,\!,\?,\;,\:,\|,\",\',\-,\_]*)(\Z)?')
111+CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)'
112+ r'([\u0080-\uFFFF,\w,\s,\.,\,,\!,\?,\;,\:,\|,\",\',\-,\_]*)(\Z)?')
113 CHORD_TEMPLATE = '<span class="chordline">{chord}</span>'
114 FIRST_CHORD_TEMPLATE = '<span class="chordline firstchordline">{chord}</span>'
115 CHORD_LINE_TEMPLATE = '<span class="chord"><span><strong>{chord}</strong></span></span>{tail}{whitespace}{remainder}'
116
117=== modified file 'openlp/core/threading.py'
118--- openlp/core/threading.py 2019-02-14 15:09:09 +0000
119+++ openlp/core/threading.py 2019-03-09 07:06:25 +0000
120@@ -76,11 +76,11 @@
121 Get the worker by the thread name
122
123 :param str thread_name: The name of the thread
124- :returns: The worker for this thread name
125+ :returns: The worker for this thread name, or None
126 """
127 thread_info = Registry().get('application').worker_threads.get(thread_name)
128 if not thread_info:
129- raise KeyError('No thread named "{}" exists'.format(thread_name))
130+ return
131 return thread_info.get('worker')
132
133
134
135=== modified file 'openlp/core/ui/firsttimeform.py'
136--- openlp/core/ui/firsttimeform.py 2019-02-21 21:29:00 +0000
137+++ openlp/core/ui/firsttimeform.py 2019-03-09 07:06:25 +0000
138@@ -32,7 +32,7 @@
139
140 from PyQt5 import QtCore, QtWidgets
141
142-from openlp.core.common import clean_button_text, trace_error_handler
143+from openlp.core.common import trace_error_handler
144 from openlp.core.common.applocation import AppLocation
145 from openlp.core.common.httputils import DownloadWorker, download_file, get_url_file_size, get_web_page
146 from openlp.core.common.i18n import translate
147@@ -46,6 +46,7 @@
148 from openlp.core.threading import get_thread_worker, is_thread_finished, run_thread
149 from openlp.core.ui.firsttimewizard import FirstTimePage, UiFirstTimeWizard
150 from openlp.core.ui.icons import UiIcons
151+from openlp.core.widgets.widgets import ProxyDialog
152
153
154 log = logging.getLogger(__name__)
155@@ -91,7 +92,7 @@
156
157 class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
158 """
159- This is the Theme Import Wizard, which allows easy creation and editing of OpenLP themes.
160+ This is the FirstTimeWizard, designed to help new users to get up and running quickly.
161 """
162 log.info('ThemeWizardForm loaded')
163
164@@ -103,6 +104,7 @@
165 self.web_access = True
166 self.web = ''
167 self.setup_ui(self)
168+ self.customButtonClicked.connect(self._on_custom_button_clicked)
169 self.themes_list_widget.itemSelectionChanged.connect(self.on_themes_list_widget_selection_changed)
170 self.themes_deselect_all_button.clicked.connect(self.themes_list_widget.clearSelection)
171 self.themes_select_all_button.clicked.connect(self.themes_list_widget.selectAll)
172@@ -111,13 +113,13 @@
173 """
174 Returns the id of the next FirstTimePage to go to based on enabled plugins
175 """
176- if FirstTimePage.ScreenConfig < self.currentId() < FirstTimePage.Songs and self.songs_check_box.isChecked():
177+ if FirstTimePage.Download < self.currentId() < FirstTimePage.Songs and self.songs_check_box.isChecked():
178 # If the songs plugin is enabled then go to the songs page
179 return FirstTimePage.Songs
180- elif FirstTimePage.ScreenConfig < self.currentId() < FirstTimePage.Bibles and self.bible_check_box.isChecked():
181+ elif FirstTimePage.Download < self.currentId() < FirstTimePage.Bibles and self.bible_check_box.isChecked():
182 # Otherwise, if the Bibles plugin is enabled then go to the Bibles page
183 return FirstTimePage.Bibles
184- elif FirstTimePage.ScreenConfig < self.currentId() < FirstTimePage.Themes:
185+ elif FirstTimePage.Download < self.currentId() < FirstTimePage.Themes:
186 # Otherwise, if the current page is somewhere between the Welcome and the Themes pages, go to the themes
187 return FirstTimePage.Themes
188 else:
189@@ -133,9 +135,7 @@
190 if not self.web_access:
191 return FirstTimePage.NoInternet
192 else:
193- return FirstTimePage.Plugins
194- elif self.currentId() == FirstTimePage.Plugins:
195- return self.get_next_page_id()
196+ return FirstTimePage.Songs
197 elif self.currentId() == FirstTimePage.Progress:
198 return -1
199 elif self.currentId() == FirstTimePage.NoInternet:
200@@ -147,7 +147,7 @@
201 Run the wizard.
202 """
203 self.set_defaults()
204- return QtWidgets.QWizard.exec(self)
205+ return super().exec()
206
207 def initialize(self, screens):
208 """
209@@ -227,17 +227,13 @@
210 """
211 self.restart()
212 self.web = 'https://get.openlp.org/ftw/'
213- self.cancel_button.clicked.connect(self.on_cancel_button_clicked)
214- self.no_internet_finish_button.clicked.connect(self.on_no_internet_finish_button_clicked)
215- self.no_internet_cancel_button.clicked.connect(self.on_no_internet_cancel_button_clicked)
216 self.currentIdChanged.connect(self.on_current_id_changed)
217 Registry().register_function('config_screen_changed', self.screen_selection_widget.load)
218- self.no_internet_finish_button.setVisible(False)
219- self.no_internet_cancel_button.setVisible(False)
220 # Check if this is a re-run of the wizard.
221 self.has_run_wizard = Settings().value('core/has run wizard')
222 create_paths(Path(gettempdir(), 'openlp'))
223 self.theme_combo_box.clear()
224+ self.button(QtWidgets.QWizard.CustomButton1).setVisible(False)
225 if self.has_run_wizard:
226 self.songs_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songs').is_active())
227 self.bible_check_box.setChecked(self.plugin_manager.get_plugin_by_name('bibles').is_active())
228@@ -260,57 +256,92 @@
229 """
230 Detects Page changes and updates as appropriate.
231 """
232- # Keep track of the page we are at. Triggering "Cancel" causes page_id to be a -1.
233+ back_button = self.button(QtWidgets.QWizard.BackButton)
234+ cancel_button = self.button(QtWidgets.QWizard.CancelButton)
235+ internet_settings_button = self.button(QtWidgets.QWizard.CustomButton1)
236+ next_button = self.button(QtWidgets.QWizard.NextButton)
237+ back_button.setVisible(True)
238+ next_button.setVisible(True)
239+ internet_settings_button.setVisible(False)
240 self.application.process_events()
241- if page_id != -1:
242- self.last_id = page_id
243- if page_id == FirstTimePage.Download:
244- self.back_button.setVisible(False)
245- self.next_button.setVisible(False)
246- # Set the no internet page text.
247- if self.has_run_wizard:
248- self.no_internet_label.setText(self.no_internet_text)
249- else:
250- self.no_internet_label.setText(self.no_internet_text + self.cancel_wizard_text)
251+ if page_id == FirstTimePage.SampleOption:
252+ internet_settings_button.setVisible(True)
253+ elif page_id == FirstTimePage.Download:
254+ back_button.setVisible(False)
255+ next_button.setVisible(False)
256 self.application.set_busy_cursor()
257 self._download_index()
258 self.application.set_normal_cursor()
259- self.back_button.setVisible(False)
260- self.next_button.setVisible(True)
261 self.next()
262 elif page_id == FirstTimePage.NoInternet:
263- self.back_button.setVisible(False)
264- self.next_button.setVisible(False)
265- self.cancel_button.setVisible(False)
266- self.no_internet_finish_button.setVisible(True)
267- if self.has_run_wizard:
268- self.no_internet_cancel_button.setVisible(False)
269- else:
270- self.no_internet_cancel_button.setVisible(True)
271- elif page_id == FirstTimePage.Plugins:
272- self.back_button.setVisible(False)
273+ next_button.setVisible(False)
274+ cancel_button.setVisible(False)
275+ internet_settings_button.setVisible(True)
276 elif page_id == FirstTimePage.Progress:
277+ back_button.setVisible(False)
278+ next_button.setVisible(False)
279 self.application.set_busy_cursor()
280 self._pre_wizard()
281 self._perform_wizard()
282 self._post_wizard()
283 self.application.set_normal_cursor()
284
285- def on_cancel_button_clicked(self):
286- """
287- Process the triggering of the cancel button.
288+ def accept(self):
289+ """
290+ Called when the user clicks 'Finish'. Reimplement it to to save the plugin status
291+
292+ :rtype: None
293+ """
294+ self._set_plugin_status(self.songs_check_box, 'songs/status')
295+ self._set_plugin_status(self.bible_check_box, 'bibles/status')
296+ self._set_plugin_status(self.presentation_check_box, 'presentations/status')
297+ self._set_plugin_status(self.image_check_box, 'images/status')
298+ self._set_plugin_status(self.media_check_box, 'media/status')
299+ self._set_plugin_status(self.custom_check_box, 'custom/status')
300+ self._set_plugin_status(self.song_usage_check_box, 'songusage/status')
301+ self._set_plugin_status(self.alert_check_box, 'alerts/status')
302+ self.screen_selection_widget.save()
303+ if self.theme_combo_box.currentIndex() != -1:
304+ Settings().setValue('themes/global theme', self.theme_combo_box.currentText())
305+ super().accept()
306+
307+ def reject(self):
308+ """
309+ Called when the user clicks the cancel button. Reimplement it to clean up the threads.
310+
311+ :rtype: None
312 """
313 self.was_cancelled = True
314- if self.thumbnail_download_threads: # TODO: Use main thread list
315- for thread_name in self.thumbnail_download_threads:
316- worker = get_thread_worker(thread_name)
317- if worker:
318- worker.cancel_download()
319+ for thread_name in self.thumbnail_download_threads:
320+ worker = get_thread_worker(thread_name)
321+ if worker:
322+ worker.cancel_download()
323 # Was the thread created.
324 if self.thumbnail_download_threads:
325 while any([not is_thread_finished(thread_name) for thread_name in self.thumbnail_download_threads]):
326 time.sleep(0.1)
327 self.application.set_normal_cursor()
328+ super().reject()
329+
330+ def _on_custom_button_clicked(self, which):
331+ """
332+ Slot to handle the a click on one of the wizards custom buttons.
333+
334+ :param int QtWidgets.QWizard which: The button pressed
335+ :rtype: None
336+ """
337+ # Internet settings button
338+ if which == QtWidgets.QWizard.CustomButton1:
339+ proxy_dialog = ProxyDialog(self)
340+ proxy_dialog.retranslate_ui()
341+ proxy_dialog.exec()
342+
343+ def on_projectors_check_box_clicked(self):
344+ # When clicking projectors_check box, change the visibility setting for Projectors panel.
345+ if Settings().value('projector/show after wizard'):
346+ Settings().setValue('projector/show after wizard', False)
347+ else:
348+ Settings().setValue('projector/show after wizard', True)
349
350 def on_themes_list_widget_selection_changed(self):
351 """
352@@ -330,23 +361,6 @@
353 elif not item.isSelected() and cbox_index != -1:
354 self.theme_combo_box.removeItem(cbox_index)
355
356- def on_no_internet_finish_button_clicked(self):
357- """
358- Process the triggering of the "Finish" button on the No Internet page.
359- """
360- self.application.set_busy_cursor()
361- self._perform_wizard()
362- self.application.set_normal_cursor()
363- Settings().setValue('core/has run wizard', True)
364- self.close()
365-
366- def on_no_internet_cancel_button_clicked(self):
367- """
368- Process the triggering of the "Cancel" button on the No Internet page.
369- """
370- self.was_cancelled = True
371- self.close()
372-
373 def update_progress(self, count, block_size):
374 """
375 Calculate and display the download progress. This method is called by download_file().
376@@ -373,7 +387,7 @@
377 Prepare the UI for the process.
378 """
379 self.max_progress = 0
380- self.finish_button.setVisible(False)
381+ self.button(QtWidgets.QWizard.FinishButton).setEnabled(False)
382 self.application.process_events()
383 try:
384 # Loop through the songs list and increase for each selected item
385@@ -432,54 +446,31 @@
386 self.progress_bar.setValue(self.progress_bar.maximum())
387 if self.has_run_wizard:
388 text = translate('OpenLP.FirstTimeWizard',
389- 'Download complete. Click the {button} button to return to OpenLP.'
390- ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
391- self.progress_label.setText(text)
392+ 'Download complete. Click the \'{finish_button}\' button to return to OpenLP.')
393 else:
394 text = translate('OpenLP.FirstTimeWizard',
395- 'Download complete. Click the {button} button to start OpenLP.'
396- ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
397- self.progress_label.setText(text)
398+ 'Download complete. Click the \'{finish_button}\' button to start OpenLP.')
399 else:
400 if self.has_run_wizard:
401- text = translate('OpenLP.FirstTimeWizard',
402- 'Click the {button} button to return to OpenLP.'
403- ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
404- self.progress_label.setText(text)
405+ text = translate('OpenLP.FirstTimeWizard', 'Click the \'{finish_button}\' button to return to OpenLP.')
406 else:
407- text = translate('OpenLP.FirstTimeWizard',
408- 'Click the {button} button to start OpenLP.'
409- ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
410- self.progress_label.setText(text)
411- self.finish_button.setVisible(True)
412- self.finish_button.setEnabled(True)
413- self.cancel_button.setVisible(False)
414- self.next_button.setVisible(False)
415+ text = translate('OpenLP.FirstTimeWizard', 'Click the \'{finish_button}\' button to start OpenLP.')
416+ self.progress_label.setText(text.format(finish_button=self.finish_button_text))
417+ self.button(QtWidgets.QWizard.FinishButton).setEnabled(True)
418+ self.button(QtWidgets.QWizard.CancelButton).setVisible(False)
419 self.application.process_events()
420
421 def _perform_wizard(self):
422 """
423 Run the tasks in the wizard.
424 """
425- # Set plugin states
426- self._increment_progress_bar(translate('OpenLP.FirstTimeWizard', 'Enabling selected plugins...'))
427- self._set_plugin_status(self.songs_check_box, 'songs/status')
428- self._set_plugin_status(self.bible_check_box, 'bibles/status')
429- self._set_plugin_status(self.presentation_check_box, 'presentations/status')
430- self._set_plugin_status(self.image_check_box, 'images/status')
431- self._set_plugin_status(self.media_check_box, 'media/status')
432- self._set_plugin_status(self.custom_check_box, 'custom/status')
433- self._set_plugin_status(self.song_usage_check_box, 'songusage/status')
434- self._set_plugin_status(self.alert_check_box, 'alerts/status')
435+
436 if self.web_access:
437 if not self._download_selected():
438 critical_error_message_box(translate('OpenLP.FirstTimeWizard', 'Download Error'),
439 translate('OpenLP.FirstTimeWizard', 'There was a connection problem while '
440 'downloading, so further downloads will be skipped. Try to re-run '
441 'the First Time Wizard later.'))
442- self.screen_selection_widget.save()
443- if self.theme_combo_box.currentIndex() != -1:
444- Settings().setValue('themes/global theme', self.theme_combo_box.currentText())
445
446 def _download_selected(self):
447 """
448
449=== modified file 'openlp/core/ui/firsttimewizard.py'
450--- openlp/core/ui/firsttimewizard.py 2019-02-15 22:34:53 +0000
451+++ openlp/core/ui/firsttimewizard.py 2019-03-09 07:06:25 +0000
452@@ -39,14 +39,15 @@
453 An enumeration class with each of the pages of the wizard.
454 """
455 Welcome = 0
456- ScreenConfig = 1
457- Download = 2
458- NoInternet = 3
459- Plugins = 4
460- Songs = 5
461- Bibles = 6
462- Themes = 7
463- Progress = 8
464+ Plugins = 1
465+ ScreenConfig = 2
466+ SampleOption = 3
467+ Download = 4
468+ NoInternet = 5
469+ Songs = 6
470+ Bibles = 7
471+ Themes = 8
472+ Progress = 9
473
474
475 class ThemeListWidget(QtWidgets.QListWidget):
476@@ -97,20 +98,13 @@
477 first_time_wizard.resize(550, 386)
478 first_time_wizard.setModal(True)
479 first_time_wizard.setOptions(QtWidgets.QWizard.IndependentPages | QtWidgets.QWizard.NoBackButtonOnStartPage |
480- QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1 |
481- QtWidgets.QWizard.HaveCustomButton2)
482+ QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1)
483 if is_macosx(): # pragma: nocover
484 first_time_wizard.setPixmap(QtWidgets.QWizard.BackgroundPixmap,
485 QtGui.QPixmap(':/wizards/openlp-osx-wizard.png'))
486 first_time_wizard.resize(634, 386)
487 else:
488 first_time_wizard.setWizardStyle(QtWidgets.QWizard.ModernStyle)
489- self.finish_button = self.button(QtWidgets.QWizard.FinishButton)
490- self.no_internet_finish_button = self.button(QtWidgets.QWizard.CustomButton1)
491- self.cancel_button = self.button(QtWidgets.QWizard.CancelButton)
492- self.no_internet_cancel_button = self.button(QtWidgets.QWizard.CustomButton2)
493- self.next_button = self.button(QtWidgets.QWizard.NextButton)
494- self.back_button = self.button(QtWidgets.QWizard.BackButton)
495 add_welcome_page(first_time_wizard, ':/wizards/wizard_firsttime.bmp')
496 # The screen config page
497 self.screen_page = QtWidgets.QWizardPage()
498@@ -121,6 +115,18 @@
499 self.screen_selection_widget.load()
500 self.screen_page_layout.addRow(self.screen_selection_widget)
501 first_time_wizard.setPage(FirstTimePage.ScreenConfig, self.screen_page)
502+ # Download Samples page
503+ self.resource_page = QtWidgets.QWizardPage()
504+ self.resource_page.setObjectName('resource_page')
505+ self.resource_page.setFinalPage(True)
506+ self.resource_layout = QtWidgets.QVBoxLayout(self.resource_page)
507+ self.resource_layout.setContentsMargins(50, 20, 50, 20)
508+ self.resource_layout.setObjectName('resource_layout')
509+ self.resource_label = QtWidgets.QLabel(self.resource_page)
510+ self.resource_label.setObjectName('resource_label')
511+ self.resource_label.setWordWrap(True)
512+ self.resource_layout.addWidget(self.resource_label)
513+ first_time_wizard.setPage(FirstTimePage.SampleOption, self.resource_page)
514 # The download page
515 self.download_page = QtWidgets.QWizardPage()
516 self.download_page.setObjectName('download_page')
517@@ -134,6 +140,7 @@
518 # The "you don't have an internet connection" page.
519 self.no_internet_page = QtWidgets.QWizardPage()
520 self.no_internet_page.setObjectName('no_internet_page')
521+ self.no_internet_page.setFinalPage(True)
522 self.no_internet_layout = QtWidgets.QVBoxLayout(self.no_internet_page)
523 self.no_internet_layout.setContentsMargins(50, 30, 50, 40)
524 self.no_internet_layout.setObjectName('no_internet_layout')
525@@ -242,27 +249,32 @@
526 self.progress_bar.setObjectName('progress_bar')
527 self.progress_layout.addWidget(self.progress_bar)
528 first_time_wizard.setPage(FirstTimePage.Progress, self.progress_page)
529- self.retranslate_ui(first_time_wizard)
530+ self.retranslate_ui()
531
532- def retranslate_ui(self, first_time_wizard):
533+ def retranslate_ui(self):
534 """
535 Translate the UI on the fly
536
537 :param first_time_wizard: The wizard form
538 """
539- first_time_wizard.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard'))
540+ self.finish_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))
541+ back_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.BackButton))
542+ next_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.NextButton))
543+
544+ self.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard'))
545 text = translate('OpenLP.FirstTimeWizard', 'Welcome to the First Time Wizard')
546- first_time_wizard.title_label.setText('<span style="font-size:14pt; font-weight:600;">{text}'
547- '</span>'.format(text=text))
548- button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.NextButton))
549- first_time_wizard.information_label.setText(
550+ self.title_label.setText('<span style="font-size:14pt; font-weight:600;">{text}</span>'.format(text=text))
551+ self.information_label.setText(
552 translate('OpenLP.FirstTimeWizard', 'This wizard will help you to configure OpenLP for initial use. '
553- 'Click the {button} button below to start.').format(button=button))
554+ 'Click the \'{next_button}\' button below to start.'
555+ ).format(next_button=next_button_text))
556+ self.setButtonText(
557+ QtWidgets.QWizard.CustomButton1, translate('OpenLP.FirstTimeWizard', 'Internet Settings'))
558 self.download_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading Resource Index'))
559- self.download_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Please wait while the resource index is '
560- 'downloaded.'))
561- self.download_label.setText(translate('OpenLP.FirstTimeWizard', 'Please wait while OpenLP downloads the '
562- 'resource index file...'))
563+ self.download_page.setSubTitle(translate('OpenLP.FirstTimeWizard',
564+ 'Please wait while the resource index is downloaded.'))
565+ self.download_label.setText(translate('OpenLP.FirstTimeWizard',
566+ 'Please wait while OpenLP downloads the resource index file...'))
567 self.plugin_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Select parts of the program you wish to use'))
568 self.plugin_page.setSubTitle(translate('OpenLP.FirstTimeWizard',
569 'You can also change these settings after the Wizard.'))
570@@ -270,11 +282,10 @@
571 self.screen_page.setSubTitle(translate('OpenLP.FirstTimeWizard',
572 'Choose the main display screen for OpenLP.'))
573 self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs'))
574- self.custom_check_box.setText(translate('OpenLP.FirstTimeWizard',
575- 'Custom Slides – Easier to manage than songs and they have their own'
576- ' list of slides'))
577- self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard',
578- 'Bibles – Import and show Bibles'))
579+ self.custom_check_box.setText(
580+ translate('OpenLP.FirstTimeWizard',
581+ 'Custom Slides – Easier to manage than songs and they have their own list of slides'))
582+ self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Bibles – Import and show Bibles'))
583 self.image_check_box.setText(translate('OpenLP.FirstTimeWizard',
584 'Images – Show images or replace background with them'))
585 self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard',
586@@ -283,22 +294,25 @@
587 self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Song Usage Monitor'))
588 self.alert_check_box.setText(translate('OpenLP.FirstTimeWizard',
589 'Alerts – Display informative messages while showing other slides'))
590+ self.resource_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Resource Data'))
591+ self.resource_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Can OpenLP download some resource data?'))
592+ self.resource_label.setText(
593+ translate('OpenLP.FirstTimeWizard',
594+ 'OpenLP has collected some resources that we have permission to distribute.\n\n'
595+ 'If you would like to download some of these resources click the \'{next_button}\' button, '
596+ 'otherwise click the \'{finish_button}\' button.'
597+ ).format(next_button=next_button_text, finish_button=self.finish_button_text))
598 self.no_internet_page.setTitle(translate('OpenLP.FirstTimeWizard', 'No Internet Connection'))
599- self.no_internet_page.setSubTitle(
600- translate('OpenLP.FirstTimeWizard', 'Unable to detect an Internet connection.'))
601- button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.FinishButton))
602- self.no_internet_text = translate('OpenLP.FirstTimeWizard',
603- 'No Internet connection was found. The First Time Wizard needs an Internet '
604- 'connection in order to be able to download sample songs, Bibles and themes.'
605- ' Click the {button} button now to start OpenLP with initial settings and '
606- 'no sample data.\n\nTo re-run the First Time Wizard and import this sample '
607- 'data at a later time, check your Internet connection and re-run this '
608- 'wizard by selecting "Tools/Re-run First Time Wizard" from OpenLP.'
609- ).format(button=button)
610- button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.CancelButton))
611- self.cancel_wizard_text = translate('OpenLP.FirstTimeWizard',
612- '\n\nTo cancel the First Time Wizard completely (and not start OpenLP), '
613- 'click the {button} button now.').format(button=button)
614+ self.no_internet_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Cannot connect to the internet.'))
615+ self.no_internet_label.setText(
616+ translate('OpenLP.FirstTimeWizard',
617+ 'OpenLP could not connect to the internet to get information about the sample data available.\n\n'
618+ 'Please check your internet connection. If your church uses a proxy server click the '
619+ '\'Internet Settings\' button below and enter the server details there.\n\nClick the '
620+ '\'{back_button}\' button to try again.\n\nIf you click the \'{finish_button}\' '
621+ 'button you can download the data at a later time by selecting \'Re-run First Time Wizard\' '
622+ 'from the \'Tools\' menu in OpenLP.'
623+ ).format(back_button=back_button_text, finish_button=self.finish_button_text))
624 self.songs_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Songs'))
625 self.songs_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download public domain songs.'))
626 self.bibles_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Bibles'))
627@@ -310,17 +324,5 @@
628 self.themes_select_all_button.setToolTip(translate('OpenLP.FirstTimeWizard', 'Select all'))
629 self.themes_deselect_all_button.setToolTip(translate('OpenLP.FirstTimeWizard', 'Deselect all'))
630 self.progress_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading and Configuring'))
631- self.progress_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Please wait while resources are downloaded '
632- 'and OpenLP is configured.'))
633- self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Starting configuration process...'))
634- first_time_wizard.setButtonText(QtWidgets.QWizard.CustomButton1,
635- clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.FinishButton)))
636- first_time_wizard.setButtonText(QtWidgets.QWizard.CustomButton2,
637- clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.CancelButton)))
638-
639- def on_projectors_check_box_clicked(self):
640- # When clicking projectors_check box, change the visibility setting for Projectors panel.
641- if Settings().value('projector/show after wizard'):
642- Settings().setValue('projector/show after wizard', False)
643- else:
644- Settings().setValue('projector/show after wizard', True)
645+ self.progress_page.setSubTitle(
646+ translate('OpenLP.FirstTimeWizard', 'Please wait while resources are downloaded and OpenLP is configured.'))
647
648=== modified file 'openlp/core/ui/mainwindow.py'
649--- openlp/core/ui/mainwindow.py 2019-02-14 15:09:09 +0000
650+++ openlp/core/ui/mainwindow.py 2019-03-09 07:06:25 +0000
651@@ -681,8 +681,7 @@
652 return
653 first_run_wizard = FirstTimeForm(self)
654 first_run_wizard.initialize(ScreenList())
655- first_run_wizard.exec()
656- if first_run_wizard.was_cancelled:
657+ if first_run_wizard.exec() == QtWidgets.QDialog.Rejected:
658 return
659 self.application.set_busy_cursor()
660 self.first_time()
661
662=== modified file 'openlp/core/widgets/widgets.py'
663--- openlp/core/widgets/widgets.py 2019-02-14 15:09:09 +0000
664+++ openlp/core/widgets/widgets.py 2019-03-09 07:06:25 +0000
665@@ -150,6 +150,34 @@
666 settings.setValue('advanced/proxy password', self.password_edit.text())
667
668
669+class ProxyDialog(QtWidgets.QDialog):
670+ """
671+ A basic dialog to show proxy settingd
672+ """
673+ def __init__(self, *args, **kwargs):
674+ super().__init__(*args, **kwargs)
675+ self.layout = QtWidgets.QVBoxLayout(self)
676+ self.proxy_widget = ProxyWidget(self)
677+ self.layout.addWidget(self.proxy_widget)
678+ self.button_box = \
679+ QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, self)
680+ self.layout.addWidget(self.button_box)
681+ self.button_box.accepted.connect(self.accept)
682+ self.button_box.rejected.connect(self.reject)
683+
684+ def accept(self):
685+ """
686+ Reimplement the the accept slot so that the ProxyWidget settings can be saved.
687+ :rtype: None
688+ """
689+ self.proxy_widget.save()
690+ super().accept()
691+
692+ def retranslate_ui(self):
693+ self.proxy_widget.retranslate_ui()
694+ self.setWindowTitle(translate('OpenLP.ProxyDialog', 'Proxy Server Settings'))
695+
696+
697 class ScreenButton(QtWidgets.QPushButton):
698 """
699 A special button class that holds the screen information about it
700
701=== modified file 'openlp/plugins/songs/lib/importers/openlp.py'
702--- openlp/plugins/songs/lib/importers/openlp.py 2019-02-14 15:09:09 +0000
703+++ openlp/plugins/songs/lib/importers/openlp.py 2019-03-09 07:06:25 +0000
704@@ -106,7 +106,7 @@
705 pass
706
707 # Check the file type
708- if not isinstance(self.import_source, str) or not self.import_source.endswith('.sqlite'):
709+ if self.import_source.suffix != '.sqlite':
710 self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport',
711 'Not a valid OpenLP 2 song database.'))
712 return
713
714=== modified file 'openlp/plugins/songs/reporting.py'
715--- openlp/plugins/songs/reporting.py 2019-02-14 15:09:09 +0000
716+++ openlp/plugins/songs/reporting.py 2019-03-09 07:06:25 +0000
717@@ -49,13 +49,6 @@
718 Path(translate('SongPlugin.ReportSongList', 'song_extract.csv')),
719 translate('SongPlugin.ReportSongList', 'CSV format (*.csv)'))
720
721- if report_file_path is None:
722- main_window.error_message(
723- translate('SongPlugin.ReportSongList', 'Output Path Not Selected'),
724- translate('SongPlugin.ReportSongList', 'You have not set a valid output location for your report. \n'
725- 'Please select an existing path on your computer.')
726- )
727- return
728 report_file_path.with_suffix('.csv')
729 Registry().get('application').set_busy_cursor()
730 try:
731
732=== modified file 'run_openlp.py' (properties changed: +x to -x)
733=== modified file 'setup.py' (properties changed: +x to -x)
734=== modified file 'tests/functional/openlp_core/common/test_httputils.py'
735--- tests/functional/openlp_core/common/test_httputils.py 2019-02-14 15:09:09 +0000
736+++ tests/functional/openlp_core/common/test_httputils.py 2019-03-09 07:06:25 +0000
737@@ -224,7 +224,7 @@
738 file_size = get_url_file_size(fake_url)
739
740 # THEN: The correct methods are called with the correct arguments and a web page is returned
741- mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, timeout=30.0)
742+ mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, proxies=None, timeout=30.0)
743 assert file_size == 100
744
745 @patch('openlp.core.common.httputils.requests')
746@@ -249,34 +249,30 @@
747 self.addCleanup(self.destroy_settings)
748
749 @patch('openlp.core.common.httputils.Settings')
750- def test_mode_arg_specified(self, MockSettings):
751+ def test_mode_arg_specified(self, mocked_settings):
752 """
753 Test that the argument is used rather than reading the 'advanced/proxy mode' setting
754 """
755 # GIVEN: Mocked settings
756- mocked_settings = MagicMock()
757- MockSettings.return_value = mocked_settings
758
759 # WHEN: Calling `get_proxy_settings` with the mode arg specified
760 get_proxy_settings(mode=ProxyMode.NO_PROXY)
761
762 # THEN: The mode arg should have been used rather than looking it up in the settings
763- mocked_settings.value.assert_not_called()
764+ mocked_settings().value.assert_not_called()
765
766 @patch('openlp.core.common.httputils.Settings')
767- def test_mode_incorrect_arg_specified(self, MockSettings):
768+ def test_mode_incorrect_arg_specified(self, mocked_settings):
769 """
770 Test that the system settings are used when the mode arg specieied is invalid
771 """
772 # GIVEN: Mocked settings
773- mocked_settings = MagicMock()
774- MockSettings.return_value = mocked_settings
775
776 # WHEN: Calling `get_proxy_settings` with an invalid mode arg specified
777 result = get_proxy_settings(mode='qwerty')
778
779 # THEN: An None should be returned
780- mocked_settings.value.assert_not_called()
781+ mocked_settings().value.assert_not_called()
782 assert result is None
783
784 def test_no_proxy_mode(self):
785
786=== modified file 'tests/functional/openlp_core/test_threading.py'
787--- tests/functional/openlp_core/test_threading.py 2019-02-14 15:09:09 +0000
788+++ tests/functional/openlp_core/test_threading.py 2019-03-09 07:06:25 +0000
789@@ -133,15 +133,11 @@
790 # GIVEN: A mocked thread worker
791 MockRegistry.return_value.get.return_value.worker_threads = {}
792
793- try:
794- # WHEN: get_thread_worker() is called
795- get_thread_worker('test_thread')
796- assert False, 'A KeyError should have been raised'
797- except KeyError:
798- # THEN: The mocked worker is returned
799- pass
800- except Exception:
801- assert False, 'A KeyError should have been raised'
802+ # WHEN: get_thread_worker() is called
803+ result = get_thread_worker('test_thread')
804+
805+ # THEN: None should have been returned
806+ assert result is None
807
808
809 @patch('openlp.core.threading.Registry')
810
811=== modified file 'tests/functional/openlp_core/ui/test_firsttimeform.py'
812--- tests/functional/openlp_core/ui/test_firsttimeform.py 2019-02-16 08:57:11 +0000
813+++ tests/functional/openlp_core/ui/test_firsttimeform.py 2019-03-09 07:06:25 +0000
814@@ -27,6 +27,8 @@
815 from unittest import TestCase
816 from unittest.mock import MagicMock, call, patch, DEFAULT
817
818+from PyQt5 import QtWidgets
819+
820 from openlp.core.common.path import Path
821 from openlp.core.common.registry import Registry
822 from openlp.core.ui.firsttimeform import FirstTimeForm, ThemeListWidgetItem
823@@ -45,6 +47,7 @@
824 """
825 Test the :class:`ThemeListWidgetItem` class
826 """
827+
828 def setUp(self):
829 self.sample_theme_data = {'file_name': 'BlueBurst.otz', 'sha256': 'sha_256_hash',
830 'thumbnail': 'BlueBurst.png', 'title': 'Blue Burst'}
831@@ -59,7 +62,7 @@
832 """
833 Test that the theme data is loaded correctly in to a ThemeListWidgetItem object when instantiated
834 """
835- # GIVEN: A sample theme dictanary object
836+ # GIVEN: A sample theme dictionary object
837 # WHEN: Creating an instance of `ThemeListWidgetItem`
838 instance = ThemeListWidgetItem('url', self.sample_theme_data, MagicMock())
839
840@@ -74,7 +77,7 @@
841 """
842 Test that the `DownloadWorker` worker is set up correctly and that the thread is started.
843 """
844- # GIVEN: A sample theme dictanary object
845+ # GIVEN: A sample theme dictionary object
846 mocked_ftw = MagicMock(spec=FirstTimeForm)
847 mocked_ftw.thumbnail_download_threads = []
848
849@@ -120,10 +123,23 @@
850 # THEN: The screens should be set up, and the default values initialised
851 assert expected_screens == frw.screens, 'The screens should be correct'
852 assert frw.web_access is True, 'The default value of self.web_access should be True'
853- assert frw.was_cancelled is False, 'The default value of self.was_cancelled should be False'
854 assert [] == frw.thumbnail_download_threads, 'The list of threads should be empty'
855 assert frw.has_run_wizard is False, 'has_run_wizard should be False'
856
857+ @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.exec')
858+ def test_exec(self, mocked_qwizard_exec):
859+
860+ # GIVEN: An instance of FirstTimeForm
861+ frw = FirstTimeForm(None)
862+ with patch.object(frw, 'set_defaults') as mocked_set_defaults:
863+
864+ # WHEN: exec is called
865+ frw.exec()
866+
867+ # THEN: The wizard should be reset and the exec methon on the super class should have been called
868+ mocked_set_defaults.assert_called_once()
869+ mocked_qwizard_exec.assert_called_once()
870+
871 def test_set_defaults(self):
872 """
873 Test that the default values are set when set_defaults() is run
874@@ -134,8 +150,6 @@
875 mocked_settings = MagicMock()
876 mocked_settings.value.side_effect = lambda key: {'core/has run wizard': False}[key]
877 with patch.object(frw, 'restart') as mocked_restart, \
878- patch.object(frw, 'cancel_button') as mocked_cancel_button, \
879- patch.object(frw, 'no_internet_finish_button') as mocked_no_internet_finish_btn, \
880 patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \
881 patch.object(frw, 'theme_combo_box') as mocked_theme_combo_box, \
882 patch.object(frw, 'songs_check_box') as mocked_songs_check_box, \
883@@ -153,12 +167,8 @@
884 # THEN: The default values should have been set
885 mocked_restart.assert_called_once()
886 assert 'https://get.openlp.org/ftw/' == frw.web, 'The default URL should be set'
887- mocked_cancel_button.clicked.connect.assert_called_once_with(frw.on_cancel_button_clicked)
888- mocked_no_internet_finish_btn.clicked.connect.assert_called_once_with(
889- frw.on_no_internet_finish_button_clicked)
890 mocked_currentIdChanged.connect.assert_called_once_with(frw.on_current_id_changed)
891 mocked_register_function.assert_called_once_with('config_screen_changed', frw.screen_selection_widget.load)
892- mocked_no_internet_finish_btn.setVisible.assert_called_once_with(False)
893 mocked_settings.value.assert_has_calls([call('core/has run wizard')])
894 mocked_gettempdir.assert_called_once()
895 mocked_create_paths.assert_called_once_with(Path('temp', 'openlp'))
896@@ -177,8 +187,6 @@
897 mocked_settings.value.side_effect = \
898 lambda key: {'core/has run wizard': True, 'themes/global theme': 'Default Theme'}[key]
899 with patch.object(frw, 'restart') as mocked_restart, \
900- patch.object(frw, 'cancel_button') as mocked_cancel_button, \
901- patch.object(frw, 'no_internet_finish_button') as mocked_no_internet_finish_btn, \
902 patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \
903 patch.object(frw, 'theme_combo_box', **{'findText.return_value': 3}) as mocked_theme_combo_box, \
904 patch.multiple(frw, songs_check_box=DEFAULT, bible_check_box=DEFAULT, presentation_check_box=DEFAULT,
905@@ -200,12 +208,8 @@
906 # THEN: The default values should have been set
907 mocked_restart.assert_called_once()
908 assert 'https://get.openlp.org/ftw/' == frw.web, 'The default URL should be set'
909- mocked_cancel_button.clicked.connect.assert_called_once_with(frw.on_cancel_button_clicked)
910- mocked_no_internet_finish_btn.clicked.connect.assert_called_once_with(
911- frw.on_no_internet_finish_button_clicked)
912 mocked_currentIdChanged.connect.assert_called_once_with(frw.on_current_id_changed)
913 mocked_register_function.assert_called_once_with('config_screen_changed', frw.screen_selection_widget.load)
914- mocked_no_internet_finish_btn.setVisible.assert_called_once_with(False)
915 mocked_settings.value.assert_has_calls([call('core/has run wizard'), call('themes/global theme')])
916 mocked_gettempdir.assert_called_once()
917 mocked_create_paths.assert_called_once_with(Path('temp', 'openlp'))
918@@ -219,12 +223,79 @@
919 mocked_theme_combo_box.findText.assert_called_once_with('Default Theme')
920 mocked_theme_combo_box.setCurrentIndex(3)
921
922+
923+ @patch('openlp.core.ui.firsttimeform.Settings')
924+ @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.accept')
925+ def test_accept_method(self, mocked_qwizard_accept, *args):
926+ """
927+ Test the FirstTimeForm.accept method
928+ """
929+ # GIVEN: An instance of FirstTimeForm
930+ frw = FirstTimeForm(None)
931+ with patch.object(frw, '_set_plugin_status') as mocked_set_plugin_status, \
932+ patch.multiple(frw, songs_check_box=DEFAULT, bible_check_box=DEFAULT, presentation_check_box=DEFAULT,
933+ image_check_box=DEFAULT, media_check_box=DEFAULT, custom_check_box=DEFAULT,
934+ song_usage_check_box=DEFAULT, alert_check_box=DEFAULT) as mocked_check_boxes, \
935+ patch.object(frw, 'screen_selection_widget') as mocked_screen_selection_widget:
936+
937+ # WHEN: Calling accept
938+ frw.accept()
939+
940+ # THEN: The selected plugins should be enabled, the screen selection saved and the super method called
941+ mocked_set_plugin_status.assert_has_calls([
942+ call(mocked_check_boxes['songs_check_box'], 'songs/status'),
943+ call(mocked_check_boxes['bible_check_box'], 'bibles/status'),
944+ call(mocked_check_boxes['presentation_check_box'], 'presentations/status'),
945+ call(mocked_check_boxes['image_check_box'], 'images/status'),
946+ call(mocked_check_boxes['media_check_box'], 'media/status'),
947+ call(mocked_check_boxes['custom_check_box'], 'custom/status'),
948+ call(mocked_check_boxes['song_usage_check_box'], 'songusage/status'),
949+ call(mocked_check_boxes['alert_check_box'], 'alerts/status')])
950+ mocked_screen_selection_widget.save.assert_called_once()
951+ mocked_qwizard_accept.assert_called_once()
952+
953+ @patch('openlp.core.ui.firsttimeform.Settings')
954+ def test_accept_method_theme_not_selected(self, mocked_settings):
955+ """
956+ Test the FirstTimeForm.accept method when there is no default theme selected
957+ """
958+ # GIVEN: An instance of FirstTimeForm
959+ frw = FirstTimeForm(None)
960+ with patch.object(frw, '_set_plugin_status'), patch.object(frw, 'screen_selection_widget'), \
961+ patch.object(frw, 'theme_combo_box', **{'currentIndex.return_value': -1}):
962+
963+ # WHEN: Calling accept and the currentIndex method of the theme_combo_box returns -1
964+ frw.accept()
965+
966+ # THEN: OpenLP should not try to save a theme name
967+ mocked_settings().setValue.assert_not_called()
968+
969+ @patch('openlp.core.ui.firsttimeform.Settings')
970+ def test_accept_method_theme_selected(self, mocked_settings):
971+ """
972+ Test the FirstTimeForm.accept method when a default theme is selected
973+ """
974+ # GIVEN: An instance of FirstTimeForm
975+ frw = FirstTimeForm(None)
976+ with patch.object(frw, '_set_plugin_status'), \
977+ patch.object(frw, 'screen_selection_widget'), \
978+ patch.object(
979+ frw, 'theme_combo_box', **{'currentIndex.return_value': 0, 'currentText.return_value': 'Test Item'}):
980+
981+ # WHEN: Calling accept and the currentIndex method of the theme_combo_box returns 0
982+ frw.accept()
983+
984+ # THEN: The 'currentItem' in the combobox should have been set as the default theme.
985+ mocked_settings().setValue.assert_called_once_with('themes/global theme', 'Test Item')
986+
987+ @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.reject')
988 @patch('openlp.core.ui.firsttimeform.time')
989 @patch('openlp.core.ui.firsttimeform.get_thread_worker')
990 @patch('openlp.core.ui.firsttimeform.is_thread_finished')
991- def test_on_cancel_button_clicked(self, mocked_is_thread_finished, mocked_get_thread_worker, mocked_time):
992+ def test_reject_method(
993+ self, mocked_is_thread_finished, mocked_get_thread_worker, mocked_time, mocked_qwizard_reject):
994 """
995- Test that the cancel button click slot shuts down the threads correctly
996+ Test that the reject method shuts down the threads correctly
997 """
998 # GIVEN: A FRW, some mocked threads and workers (that isn't quite done) and other mocked stuff
999 mocked_worker = MagicMock()
1000@@ -235,17 +306,47 @@
1001 frw.thumbnail_download_threads = ['test_thread']
1002 with patch.object(frw.application, 'set_normal_cursor') as mocked_set_normal_cursor:
1003
1004- # WHEN: on_cancel_button_clicked() is called
1005- frw.on_cancel_button_clicked()
1006+ # WHEN: the reject method is called
1007+ frw.reject()
1008
1009 # THEN: The right things should be called in the right order
1010- assert frw.was_cancelled is True, 'The was_cancelled property should have been set to True'
1011 mocked_get_thread_worker.assert_called_once_with('test_thread')
1012 mocked_worker.cancel_download.assert_called_once()
1013 mocked_is_thread_finished.assert_called_with('test_thread')
1014 assert mocked_is_thread_finished.call_count == 2, 'isRunning() should have been called twice'
1015 mocked_time.sleep.assert_called_once_with(0.1)
1016- mocked_set_normal_cursor.assert_called_once_with()
1017+ mocked_set_normal_cursor.assert_called_once()
1018+ mocked_qwizard_reject.assert_called_once()
1019+
1020+ @patch('openlp.core.ui.firsttimeform.ProxyDialog')
1021+ def test_on_custom_button_clicked(self, mocked_proxy_dialog):
1022+ """
1023+ Test _on_custom_button when it is called whe the 'internet settings' (CustomButton1) button is not clicked.
1024+ """
1025+ # GIVEN: An instance of the FirstTimeForm
1026+ frw = FirstTimeForm(None)
1027+
1028+ # WHEN: Calling _on_custom_button_clicked with a different button to the 'internet settings button.
1029+ frw._on_custom_button_clicked(QtWidgets.QWizard.CustomButton2)
1030+
1031+ # THEN: The ProxyDialog should not be shown.
1032+ mocked_proxy_dialog.assert_not_called()
1033+
1034+ @patch('openlp.core.ui.firsttimeform.ProxyDialog')
1035+ def test_on_custom_button_clicked_internet_settings(self, mocked_proxy_dialog):
1036+ """
1037+ Test _on_custom_button when it is called when the 'internet settings' (CustomButton1) button is clicked.
1038+ """
1039+ # GIVEN: An instance of the FirstTimeForm
1040+ frw = FirstTimeForm(None)
1041+
1042+ # WHEN: Calling _on_custom_button_clicked with the constant for the 'internet settings' button (CustomButton1)
1043+ frw._on_custom_button_clicked(QtWidgets.QWizard.CustomButton1)
1044+
1045+ # THEN: The ProxyDialog should be shown.
1046+ mocked_proxy_dialog.assert_called_with(frw)
1047+ mocked_proxy_dialog().retranslate_ui.assert_called_once()
1048+ mocked_proxy_dialog().exec.assert_called_once()
1049
1050 @patch('openlp.core.ui.firsttimeform.critical_error_message_box')
1051 def test__parse_config_invalid_config(self, mocked_critical_error_message_box):
1052@@ -279,10 +380,10 @@
1053
1054 # THEN: the critical_error_message_box should have been called
1055 mocked_message_box.critical.assert_called_once_with(
1056- first_time_form, 'Network Error', 'There was a network error attempting to connect to retrieve '
1057- 'initial configuration information', 'OK')
1058+ first_time_form, 'Network Error',
1059+ 'There was a network error attempting to connect to retrieve initial configuration information', 'OK')
1060
1061- @patch('openlp.core.ui.firsttimewizard.Settings')
1062+ @patch('openlp.core.ui.firsttimeform.Settings')
1063 def test_on_projectors_check_box_checked(self, MockSettings):
1064 """
1065 Test that the projector panel is shown when the checkbox in the first time wizard is checked
1066@@ -300,7 +401,7 @@
1067 mocked_settings.value.assert_called_once_with('projector/show after wizard')
1068 mocked_settings.setValue.assert_called_once_with('projector/show after wizard', False)
1069
1070- @patch('openlp.core.ui.firsttimewizard.Settings')
1071+ @patch('openlp.core.ui.firsttimeform.Settings')
1072 def test_on_projectors_check_box_unchecked(self, MockSettings):
1073 """
1074 Test that the projector panel is shown when the checkbox in the first time wizard is checked
1075
1076=== modified file 'tests/functional/openlp_plugins/songs/test_openlpimporter.py'
1077--- tests/functional/openlp_plugins/songs/test_openlpimporter.py 2019-02-14 15:09:09 +0000
1078+++ tests/functional/openlp_plugins/songs/test_openlpimporter.py 2019-03-09 07:06:25 +0000
1079@@ -22,6 +22,7 @@
1080 """
1081 This module contains tests for the OpenLP song importer.
1082 """
1083+from pathlib import Path
1084 from unittest import TestCase
1085 from unittest.mock import MagicMock, patch
1086
1087@@ -66,10 +67,9 @@
1088 importer.stop_import_flag = True
1089
1090 # WHEN: Import source is not a list
1091- for source in ['not a list', 0]:
1092- importer.import_source = source
1093+ importer.import_source = Path()
1094
1095- # THEN: do_import should return none and the progress bar maximum should not be set.
1096- assert importer.do_import() is None, 'do_import should return None when import_source is not a list'
1097- assert mocked_import_wizard.progress_bar.setMaximum.called is False, \
1098- 'setMaximum on import_wizard.progress_bar should not have been called'
1099+ # THEN: do_import should return none and the progress bar maximum should not be set.
1100+ assert importer.do_import() is None, 'do_import should return None when import_source is not a list'
1101+ assert mocked_import_wizard.progress_bar.setMaximum.called is False, \
1102+ 'setMaximum on import_wizard.progress_bar should not have been called'