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

Proposed by Phill
Status: Merged
Merged at revision: 2853
Proposed branch: lp:~phill-ridout/openlp/ftw-refactors
Merge into: lp:openlp
Diff against target: 1109 lines (+341/-229)
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/-63)
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 (+125/-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
Tomas Groth Approve
Tim Bentley Pending
Review via email: mp+364604@code.launchpad.net

This proposal supersedes 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 : 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 passed!

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

macOS tests failed, please see https://ci.openlp.io/job/MP-04-macOS-Tests/45/ 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/53/ for more details

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

Still some lint issues...

review: Needs Fixing
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/47/ for more details

Revision history for this message
Tomas Groth (tomasgroth) :
review: Approve

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-15 20:50:11 +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-15 20:50:11 +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-15 20:50:11 +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-15 20:50:11 +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-15 20:50:11 +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-15 20:50:11 +0000
452@@ -26,7 +26,6 @@
453
454 from openlp.core.common import clean_button_text, is_macosx
455 from openlp.core.common.i18n import translate
456-from openlp.core.common.settings import Settings
457 from openlp.core.lib.ui import add_welcome_page
458 from openlp.core.ui.icons import UiIcons
459
460@@ -39,14 +38,15 @@
461 An enumeration class with each of the pages of the wizard.
462 """
463 Welcome = 0
464- ScreenConfig = 1
465- Download = 2
466- NoInternet = 3
467- Plugins = 4
468- Songs = 5
469- Bibles = 6
470- Themes = 7
471- Progress = 8
472+ Plugins = 1
473+ ScreenConfig = 2
474+ SampleOption = 3
475+ Download = 4
476+ NoInternet = 5
477+ Songs = 6
478+ Bibles = 7
479+ Themes = 8
480+ Progress = 9
481
482
483 class ThemeListWidget(QtWidgets.QListWidget):
484@@ -97,20 +97,13 @@
485 first_time_wizard.resize(550, 386)
486 first_time_wizard.setModal(True)
487 first_time_wizard.setOptions(QtWidgets.QWizard.IndependentPages | QtWidgets.QWizard.NoBackButtonOnStartPage |
488- QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1 |
489- QtWidgets.QWizard.HaveCustomButton2)
490+ QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1)
491 if is_macosx(): # pragma: nocover
492 first_time_wizard.setPixmap(QtWidgets.QWizard.BackgroundPixmap,
493 QtGui.QPixmap(':/wizards/openlp-osx-wizard.png'))
494 first_time_wizard.resize(634, 386)
495 else:
496 first_time_wizard.setWizardStyle(QtWidgets.QWizard.ModernStyle)
497- self.finish_button = self.button(QtWidgets.QWizard.FinishButton)
498- self.no_internet_finish_button = self.button(QtWidgets.QWizard.CustomButton1)
499- self.cancel_button = self.button(QtWidgets.QWizard.CancelButton)
500- self.no_internet_cancel_button = self.button(QtWidgets.QWizard.CustomButton2)
501- self.next_button = self.button(QtWidgets.QWizard.NextButton)
502- self.back_button = self.button(QtWidgets.QWizard.BackButton)
503 add_welcome_page(first_time_wizard, ':/wizards/wizard_firsttime.bmp')
504 # The screen config page
505 self.screen_page = QtWidgets.QWizardPage()
506@@ -121,6 +114,18 @@
507 self.screen_selection_widget.load()
508 self.screen_page_layout.addRow(self.screen_selection_widget)
509 first_time_wizard.setPage(FirstTimePage.ScreenConfig, self.screen_page)
510+ # Download Samples page
511+ self.resource_page = QtWidgets.QWizardPage()
512+ self.resource_page.setObjectName('resource_page')
513+ self.resource_page.setFinalPage(True)
514+ self.resource_layout = QtWidgets.QVBoxLayout(self.resource_page)
515+ self.resource_layout.setContentsMargins(50, 20, 50, 20)
516+ self.resource_layout.setObjectName('resource_layout')
517+ self.resource_label = QtWidgets.QLabel(self.resource_page)
518+ self.resource_label.setObjectName('resource_label')
519+ self.resource_label.setWordWrap(True)
520+ self.resource_layout.addWidget(self.resource_label)
521+ first_time_wizard.setPage(FirstTimePage.SampleOption, self.resource_page)
522 # The download page
523 self.download_page = QtWidgets.QWizardPage()
524 self.download_page.setObjectName('download_page')
525@@ -134,6 +139,7 @@
526 # The "you don't have an internet connection" page.
527 self.no_internet_page = QtWidgets.QWizardPage()
528 self.no_internet_page.setObjectName('no_internet_page')
529+ self.no_internet_page.setFinalPage(True)
530 self.no_internet_layout = QtWidgets.QVBoxLayout(self.no_internet_page)
531 self.no_internet_layout.setContentsMargins(50, 30, 50, 40)
532 self.no_internet_layout.setObjectName('no_internet_layout')
533@@ -242,27 +248,32 @@
534 self.progress_bar.setObjectName('progress_bar')
535 self.progress_layout.addWidget(self.progress_bar)
536 first_time_wizard.setPage(FirstTimePage.Progress, self.progress_page)
537- self.retranslate_ui(first_time_wizard)
538+ self.retranslate_ui()
539
540- def retranslate_ui(self, first_time_wizard):
541+ def retranslate_ui(self):
542 """
543 Translate the UI on the fly
544
545 :param first_time_wizard: The wizard form
546 """
547- first_time_wizard.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard'))
548+ self.finish_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))
549+ back_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.BackButton))
550+ next_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.NextButton))
551+
552+ self.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard'))
553 text = translate('OpenLP.FirstTimeWizard', 'Welcome to the First Time Wizard')
554- first_time_wizard.title_label.setText('<span style="font-size:14pt; font-weight:600;">{text}'
555- '</span>'.format(text=text))
556- button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.NextButton))
557- first_time_wizard.information_label.setText(
558+ self.title_label.setText('<span style="font-size:14pt; font-weight:600;">{text}</span>'.format(text=text))
559+ self.information_label.setText(
560 translate('OpenLP.FirstTimeWizard', 'This wizard will help you to configure OpenLP for initial use. '
561- 'Click the {button} button below to start.').format(button=button))
562+ 'Click the \'{next_button}\' button below to start.'
563+ ).format(next_button=next_button_text))
564+ self.setButtonText(
565+ QtWidgets.QWizard.CustomButton1, translate('OpenLP.FirstTimeWizard', 'Internet Settings'))
566 self.download_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading Resource Index'))
567- self.download_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Please wait while the resource index is '
568- 'downloaded.'))
569- self.download_label.setText(translate('OpenLP.FirstTimeWizard', 'Please wait while OpenLP downloads the '
570- 'resource index file...'))
571+ self.download_page.setSubTitle(translate('OpenLP.FirstTimeWizard',
572+ 'Please wait while the resource index is downloaded.'))
573+ self.download_label.setText(translate('OpenLP.FirstTimeWizard',
574+ 'Please wait while OpenLP downloads the resource index file...'))
575 self.plugin_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Select parts of the program you wish to use'))
576 self.plugin_page.setSubTitle(translate('OpenLP.FirstTimeWizard',
577 'You can also change these settings after the Wizard.'))
578@@ -270,11 +281,10 @@
579 self.screen_page.setSubTitle(translate('OpenLP.FirstTimeWizard',
580 'Choose the main display screen for OpenLP.'))
581 self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs'))
582- self.custom_check_box.setText(translate('OpenLP.FirstTimeWizard',
583- 'Custom Slides – Easier to manage than songs and they have their own'
584- ' list of slides'))
585- self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard',
586- 'Bibles – Import and show Bibles'))
587+ self.custom_check_box.setText(
588+ translate('OpenLP.FirstTimeWizard',
589+ 'Custom Slides – Easier to manage than songs and they have their own list of slides'))
590+ self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Bibles – Import and show Bibles'))
591 self.image_check_box.setText(translate('OpenLP.FirstTimeWizard',
592 'Images – Show images or replace background with them'))
593 self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard',
594@@ -283,22 +293,25 @@
595 self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Song Usage Monitor'))
596 self.alert_check_box.setText(translate('OpenLP.FirstTimeWizard',
597 'Alerts – Display informative messages while showing other slides'))
598+ self.resource_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Resource Data'))
599+ self.resource_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Can OpenLP download some resource data?'))
600+ self.resource_label.setText(
601+ translate('OpenLP.FirstTimeWizard',
602+ 'OpenLP has collected some resources that we have permission to distribute.\n\n'
603+ 'If you would like to download some of these resources click the \'{next_button}\' button, '
604+ 'otherwise click the \'{finish_button}\' button.'
605+ ).format(next_button=next_button_text, finish_button=self.finish_button_text))
606 self.no_internet_page.setTitle(translate('OpenLP.FirstTimeWizard', 'No Internet Connection'))
607- self.no_internet_page.setSubTitle(
608- translate('OpenLP.FirstTimeWizard', 'Unable to detect an Internet connection.'))
609- button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.FinishButton))
610- self.no_internet_text = translate('OpenLP.FirstTimeWizard',
611- 'No Internet connection was found. The First Time Wizard needs an Internet '
612- 'connection in order to be able to download sample songs, Bibles and themes.'
613- ' Click the {button} button now to start OpenLP with initial settings and '
614- 'no sample data.\n\nTo re-run the First Time Wizard and import this sample '
615- 'data at a later time, check your Internet connection and re-run this '
616- 'wizard by selecting "Tools/Re-run First Time Wizard" from OpenLP.'
617- ).format(button=button)
618- button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.CancelButton))
619- self.cancel_wizard_text = translate('OpenLP.FirstTimeWizard',
620- '\n\nTo cancel the First Time Wizard completely (and not start OpenLP), '
621- 'click the {button} button now.').format(button=button)
622+ self.no_internet_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Cannot connect to the internet.'))
623+ self.no_internet_label.setText(
624+ translate('OpenLP.FirstTimeWizard',
625+ 'OpenLP could not connect to the internet to get information about the sample data available.\n\n'
626+ 'Please check your internet connection. If your church uses a proxy server click the '
627+ '\'Internet Settings\' button below and enter the server details there.\n\nClick the '
628+ '\'{back_button}\' button to try again.\n\nIf you click the \'{finish_button}\' '
629+ 'button you can download the data at a later time by selecting \'Re-run First Time Wizard\' '
630+ 'from the \'Tools\' menu in OpenLP.'
631+ ).format(back_button=back_button_text, finish_button=self.finish_button_text))
632 self.songs_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Songs'))
633 self.songs_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download public domain songs.'))
634 self.bibles_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Bibles'))
635@@ -310,17 +323,5 @@
636 self.themes_select_all_button.setToolTip(translate('OpenLP.FirstTimeWizard', 'Select all'))
637 self.themes_deselect_all_button.setToolTip(translate('OpenLP.FirstTimeWizard', 'Deselect all'))
638 self.progress_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading and Configuring'))
639- self.progress_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Please wait while resources are downloaded '
640- 'and OpenLP is configured.'))
641- self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Starting configuration process...'))
642- first_time_wizard.setButtonText(QtWidgets.QWizard.CustomButton1,
643- clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.FinishButton)))
644- first_time_wizard.setButtonText(QtWidgets.QWizard.CustomButton2,
645- clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.CancelButton)))
646-
647- def on_projectors_check_box_clicked(self):
648- # When clicking projectors_check box, change the visibility setting for Projectors panel.
649- if Settings().value('projector/show after wizard'):
650- Settings().setValue('projector/show after wizard', False)
651- else:
652- Settings().setValue('projector/show after wizard', True)
653+ self.progress_page.setSubTitle(
654+ translate('OpenLP.FirstTimeWizard', 'Please wait while resources are downloaded and OpenLP is configured.'))
655
656=== modified file 'openlp/core/ui/mainwindow.py'
657--- openlp/core/ui/mainwindow.py 2019-02-14 15:09:09 +0000
658+++ openlp/core/ui/mainwindow.py 2019-03-15 20:50:11 +0000
659@@ -681,8 +681,7 @@
660 return
661 first_run_wizard = FirstTimeForm(self)
662 first_run_wizard.initialize(ScreenList())
663- first_run_wizard.exec()
664- if first_run_wizard.was_cancelled:
665+ if first_run_wizard.exec() == QtWidgets.QDialog.Rejected:
666 return
667 self.application.set_busy_cursor()
668 self.first_time()
669
670=== modified file 'openlp/core/widgets/widgets.py'
671--- openlp/core/widgets/widgets.py 2019-02-14 15:09:09 +0000
672+++ openlp/core/widgets/widgets.py 2019-03-15 20:50:11 +0000
673@@ -150,6 +150,34 @@
674 settings.setValue('advanced/proxy password', self.password_edit.text())
675
676
677+class ProxyDialog(QtWidgets.QDialog):
678+ """
679+ A basic dialog to show proxy settingd
680+ """
681+ def __init__(self, *args, **kwargs):
682+ super().__init__(*args, **kwargs)
683+ self.layout = QtWidgets.QVBoxLayout(self)
684+ self.proxy_widget = ProxyWidget(self)
685+ self.layout.addWidget(self.proxy_widget)
686+ self.button_box = \
687+ QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, self)
688+ self.layout.addWidget(self.button_box)
689+ self.button_box.accepted.connect(self.accept)
690+ self.button_box.rejected.connect(self.reject)
691+
692+ def accept(self):
693+ """
694+ Reimplement the the accept slot so that the ProxyWidget settings can be saved.
695+ :rtype: None
696+ """
697+ self.proxy_widget.save()
698+ super().accept()
699+
700+ def retranslate_ui(self):
701+ self.proxy_widget.retranslate_ui()
702+ self.setWindowTitle(translate('OpenLP.ProxyDialog', 'Proxy Server Settings'))
703+
704+
705 class ScreenButton(QtWidgets.QPushButton):
706 """
707 A special button class that holds the screen information about it
708
709=== modified file 'openlp/plugins/songs/lib/importers/openlp.py'
710--- openlp/plugins/songs/lib/importers/openlp.py 2019-02-14 15:09:09 +0000
711+++ openlp/plugins/songs/lib/importers/openlp.py 2019-03-15 20:50:11 +0000
712@@ -106,7 +106,7 @@
713 pass
714
715 # Check the file type
716- if not isinstance(self.import_source, str) or not self.import_source.endswith('.sqlite'):
717+ if self.import_source.suffix != '.sqlite':
718 self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport',
719 'Not a valid OpenLP 2 song database.'))
720 return
721
722=== modified file 'openlp/plugins/songs/reporting.py'
723--- openlp/plugins/songs/reporting.py 2019-02-14 15:09:09 +0000
724+++ openlp/plugins/songs/reporting.py 2019-03-15 20:50:11 +0000
725@@ -49,13 +49,6 @@
726 Path(translate('SongPlugin.ReportSongList', 'song_extract.csv')),
727 translate('SongPlugin.ReportSongList', 'CSV format (*.csv)'))
728
729- if report_file_path is None:
730- main_window.error_message(
731- translate('SongPlugin.ReportSongList', 'Output Path Not Selected'),
732- translate('SongPlugin.ReportSongList', 'You have not set a valid output location for your report. \n'
733- 'Please select an existing path on your computer.')
734- )
735- return
736 report_file_path.with_suffix('.csv')
737 Registry().get('application').set_busy_cursor()
738 try:
739
740=== modified file 'run_openlp.py' (properties changed: +x to -x)
741=== modified file 'setup.py' (properties changed: +x to -x)
742=== modified file 'tests/functional/openlp_core/common/test_httputils.py'
743--- tests/functional/openlp_core/common/test_httputils.py 2019-02-14 15:09:09 +0000
744+++ tests/functional/openlp_core/common/test_httputils.py 2019-03-15 20:50:11 +0000
745@@ -224,7 +224,7 @@
746 file_size = get_url_file_size(fake_url)
747
748 # THEN: The correct methods are called with the correct arguments and a web page is returned
749- mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, timeout=30.0)
750+ mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, proxies=None, timeout=30.0)
751 assert file_size == 100
752
753 @patch('openlp.core.common.httputils.requests')
754@@ -249,34 +249,30 @@
755 self.addCleanup(self.destroy_settings)
756
757 @patch('openlp.core.common.httputils.Settings')
758- def test_mode_arg_specified(self, MockSettings):
759+ def test_mode_arg_specified(self, mocked_settings):
760 """
761 Test that the argument is used rather than reading the 'advanced/proxy mode' setting
762 """
763 # GIVEN: Mocked settings
764- mocked_settings = MagicMock()
765- MockSettings.return_value = mocked_settings
766
767 # WHEN: Calling `get_proxy_settings` with the mode arg specified
768 get_proxy_settings(mode=ProxyMode.NO_PROXY)
769
770 # THEN: The mode arg should have been used rather than looking it up in the settings
771- mocked_settings.value.assert_not_called()
772+ mocked_settings().value.assert_not_called()
773
774 @patch('openlp.core.common.httputils.Settings')
775- def test_mode_incorrect_arg_specified(self, MockSettings):
776+ def test_mode_incorrect_arg_specified(self, mocked_settings):
777 """
778 Test that the system settings are used when the mode arg specieied is invalid
779 """
780 # GIVEN: Mocked settings
781- mocked_settings = MagicMock()
782- MockSettings.return_value = mocked_settings
783
784 # WHEN: Calling `get_proxy_settings` with an invalid mode arg specified
785 result = get_proxy_settings(mode='qwerty')
786
787 # THEN: An None should be returned
788- mocked_settings.value.assert_not_called()
789+ mocked_settings().value.assert_not_called()
790 assert result is None
791
792 def test_no_proxy_mode(self):
793
794=== modified file 'tests/functional/openlp_core/test_threading.py'
795--- tests/functional/openlp_core/test_threading.py 2019-02-14 15:09:09 +0000
796+++ tests/functional/openlp_core/test_threading.py 2019-03-15 20:50:11 +0000
797@@ -133,15 +133,11 @@
798 # GIVEN: A mocked thread worker
799 MockRegistry.return_value.get.return_value.worker_threads = {}
800
801- try:
802- # WHEN: get_thread_worker() is called
803- get_thread_worker('test_thread')
804- assert False, 'A KeyError should have been raised'
805- except KeyError:
806- # THEN: The mocked worker is returned
807- pass
808- except Exception:
809- assert False, 'A KeyError should have been raised'
810+ # WHEN: get_thread_worker() is called
811+ result = get_thread_worker('test_thread')
812+
813+ # THEN: None should have been returned
814+ assert result is None
815
816
817 @patch('openlp.core.threading.Registry')
818
819=== modified file 'tests/functional/openlp_core/ui/test_firsttimeform.py'
820--- tests/functional/openlp_core/ui/test_firsttimeform.py 2019-02-16 08:57:11 +0000
821+++ tests/functional/openlp_core/ui/test_firsttimeform.py 2019-03-15 20:50:11 +0000
822@@ -27,6 +27,8 @@
823 from unittest import TestCase
824 from unittest.mock import MagicMock, call, patch, DEFAULT
825
826+from PyQt5 import QtWidgets
827+
828 from openlp.core.common.path import Path
829 from openlp.core.common.registry import Registry
830 from openlp.core.ui.firsttimeform import FirstTimeForm, ThemeListWidgetItem
831@@ -45,6 +47,7 @@
832 """
833 Test the :class:`ThemeListWidgetItem` class
834 """
835+
836 def setUp(self):
837 self.sample_theme_data = {'file_name': 'BlueBurst.otz', 'sha256': 'sha_256_hash',
838 'thumbnail': 'BlueBurst.png', 'title': 'Blue Burst'}
839@@ -59,7 +62,7 @@
840 """
841 Test that the theme data is loaded correctly in to a ThemeListWidgetItem object when instantiated
842 """
843- # GIVEN: A sample theme dictanary object
844+ # GIVEN: A sample theme dictionary object
845 # WHEN: Creating an instance of `ThemeListWidgetItem`
846 instance = ThemeListWidgetItem('url', self.sample_theme_data, MagicMock())
847
848@@ -74,7 +77,7 @@
849 """
850 Test that the `DownloadWorker` worker is set up correctly and that the thread is started.
851 """
852- # GIVEN: A sample theme dictanary object
853+ # GIVEN: A sample theme dictionary object
854 mocked_ftw = MagicMock(spec=FirstTimeForm)
855 mocked_ftw.thumbnail_download_threads = []
856
857@@ -120,10 +123,23 @@
858 # THEN: The screens should be set up, and the default values initialised
859 assert expected_screens == frw.screens, 'The screens should be correct'
860 assert frw.web_access is True, 'The default value of self.web_access should be True'
861- assert frw.was_cancelled is False, 'The default value of self.was_cancelled should be False'
862 assert [] == frw.thumbnail_download_threads, 'The list of threads should be empty'
863 assert frw.has_run_wizard is False, 'has_run_wizard should be False'
864
865+ @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.exec')
866+ def test_exec(self, mocked_qwizard_exec):
867+
868+ # GIVEN: An instance of FirstTimeForm
869+ frw = FirstTimeForm(None)
870+ with patch.object(frw, 'set_defaults') as mocked_set_defaults:
871+
872+ # WHEN: exec is called
873+ frw.exec()
874+
875+ # THEN: The wizard should be reset and the exec methon on the super class should have been called
876+ mocked_set_defaults.assert_called_once()
877+ mocked_qwizard_exec.assert_called_once()
878+
879 def test_set_defaults(self):
880 """
881 Test that the default values are set when set_defaults() is run
882@@ -134,8 +150,6 @@
883 mocked_settings = MagicMock()
884 mocked_settings.value.side_effect = lambda key: {'core/has run wizard': False}[key]
885 with patch.object(frw, 'restart') as mocked_restart, \
886- patch.object(frw, 'cancel_button') as mocked_cancel_button, \
887- patch.object(frw, 'no_internet_finish_button') as mocked_no_internet_finish_btn, \
888 patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \
889 patch.object(frw, 'theme_combo_box') as mocked_theme_combo_box, \
890 patch.object(frw, 'songs_check_box') as mocked_songs_check_box, \
891@@ -153,12 +167,8 @@
892 # THEN: The default values should have been set
893 mocked_restart.assert_called_once()
894 assert 'https://get.openlp.org/ftw/' == frw.web, 'The default URL should be set'
895- mocked_cancel_button.clicked.connect.assert_called_once_with(frw.on_cancel_button_clicked)
896- mocked_no_internet_finish_btn.clicked.connect.assert_called_once_with(
897- frw.on_no_internet_finish_button_clicked)
898 mocked_currentIdChanged.connect.assert_called_once_with(frw.on_current_id_changed)
899 mocked_register_function.assert_called_once_with('config_screen_changed', frw.screen_selection_widget.load)
900- mocked_no_internet_finish_btn.setVisible.assert_called_once_with(False)
901 mocked_settings.value.assert_has_calls([call('core/has run wizard')])
902 mocked_gettempdir.assert_called_once()
903 mocked_create_paths.assert_called_once_with(Path('temp', 'openlp'))
904@@ -177,8 +187,6 @@
905 mocked_settings.value.side_effect = \
906 lambda key: {'core/has run wizard': True, 'themes/global theme': 'Default Theme'}[key]
907 with patch.object(frw, 'restart') as mocked_restart, \
908- patch.object(frw, 'cancel_button') as mocked_cancel_button, \
909- patch.object(frw, 'no_internet_finish_button') as mocked_no_internet_finish_btn, \
910 patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \
911 patch.object(frw, 'theme_combo_box', **{'findText.return_value': 3}) as mocked_theme_combo_box, \
912 patch.multiple(frw, songs_check_box=DEFAULT, bible_check_box=DEFAULT, presentation_check_box=DEFAULT,
913@@ -200,12 +208,8 @@
914 # THEN: The default values should have been set
915 mocked_restart.assert_called_once()
916 assert 'https://get.openlp.org/ftw/' == frw.web, 'The default URL should be set'
917- mocked_cancel_button.clicked.connect.assert_called_once_with(frw.on_cancel_button_clicked)
918- mocked_no_internet_finish_btn.clicked.connect.assert_called_once_with(
919- frw.on_no_internet_finish_button_clicked)
920 mocked_currentIdChanged.connect.assert_called_once_with(frw.on_current_id_changed)
921 mocked_register_function.assert_called_once_with('config_screen_changed', frw.screen_selection_widget.load)
922- mocked_no_internet_finish_btn.setVisible.assert_called_once_with(False)
923 mocked_settings.value.assert_has_calls([call('core/has run wizard'), call('themes/global theme')])
924 mocked_gettempdir.assert_called_once()
925 mocked_create_paths.assert_called_once_with(Path('temp', 'openlp'))
926@@ -219,12 +223,78 @@
927 mocked_theme_combo_box.findText.assert_called_once_with('Default Theme')
928 mocked_theme_combo_box.setCurrentIndex(3)
929
930+ @patch('openlp.core.ui.firsttimeform.Settings')
931+ @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.accept')
932+ def test_accept_method(self, mocked_qwizard_accept, *args):
933+ """
934+ Test the FirstTimeForm.accept method
935+ """
936+ # GIVEN: An instance of FirstTimeForm
937+ frw = FirstTimeForm(None)
938+ with patch.object(frw, '_set_plugin_status') as mocked_set_plugin_status, \
939+ patch.multiple(frw, songs_check_box=DEFAULT, bible_check_box=DEFAULT, presentation_check_box=DEFAULT,
940+ image_check_box=DEFAULT, media_check_box=DEFAULT, custom_check_box=DEFAULT,
941+ song_usage_check_box=DEFAULT, alert_check_box=DEFAULT) as mocked_check_boxes, \
942+ patch.object(frw, 'screen_selection_widget') as mocked_screen_selection_widget:
943+
944+ # WHEN: Calling accept
945+ frw.accept()
946+
947+ # THEN: The selected plugins should be enabled, the screen selection saved and the super method called
948+ mocked_set_plugin_status.assert_has_calls([
949+ call(mocked_check_boxes['songs_check_box'], 'songs/status'),
950+ call(mocked_check_boxes['bible_check_box'], 'bibles/status'),
951+ call(mocked_check_boxes['presentation_check_box'], 'presentations/status'),
952+ call(mocked_check_boxes['image_check_box'], 'images/status'),
953+ call(mocked_check_boxes['media_check_box'], 'media/status'),
954+ call(mocked_check_boxes['custom_check_box'], 'custom/status'),
955+ call(mocked_check_boxes['song_usage_check_box'], 'songusage/status'),
956+ call(mocked_check_boxes['alert_check_box'], 'alerts/status')])
957+ mocked_screen_selection_widget.save.assert_called_once()
958+ mocked_qwizard_accept.assert_called_once()
959+
960+ @patch('openlp.core.ui.firsttimeform.Settings')
961+ def test_accept_method_theme_not_selected(self, mocked_settings):
962+ """
963+ Test the FirstTimeForm.accept method when there is no default theme selected
964+ """
965+ # GIVEN: An instance of FirstTimeForm
966+ frw = FirstTimeForm(None)
967+ with patch.object(frw, '_set_plugin_status'), patch.object(frw, 'screen_selection_widget'), \
968+ patch.object(frw, 'theme_combo_box', **{'currentIndex.return_value': -1}):
969+
970+ # WHEN: Calling accept and the currentIndex method of the theme_combo_box returns -1
971+ frw.accept()
972+
973+ # THEN: OpenLP should not try to save a theme name
974+ mocked_settings().setValue.assert_not_called()
975+
976+ @patch('openlp.core.ui.firsttimeform.Settings')
977+ def test_accept_method_theme_selected(self, mocked_settings):
978+ """
979+ Test the FirstTimeForm.accept method when a default theme is selected
980+ """
981+ # GIVEN: An instance of FirstTimeForm
982+ frw = FirstTimeForm(None)
983+ with patch.object(frw, '_set_plugin_status'), \
984+ patch.object(frw, 'screen_selection_widget'), \
985+ patch.object(
986+ frw, 'theme_combo_box', **{'currentIndex.return_value': 0, 'currentText.return_value': 'Test Item'}):
987+
988+ # WHEN: Calling accept and the currentIndex method of the theme_combo_box returns 0
989+ frw.accept()
990+
991+ # THEN: The 'currentItem' in the combobox should have been set as the default theme.
992+ mocked_settings().setValue.assert_called_once_with('themes/global theme', 'Test Item')
993+
994+ @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.reject')
995 @patch('openlp.core.ui.firsttimeform.time')
996 @patch('openlp.core.ui.firsttimeform.get_thread_worker')
997 @patch('openlp.core.ui.firsttimeform.is_thread_finished')
998- def test_on_cancel_button_clicked(self, mocked_is_thread_finished, mocked_get_thread_worker, mocked_time):
999+ def test_reject_method(
1000+ self, mocked_is_thread_finished, mocked_get_thread_worker, mocked_time, mocked_qwizard_reject):
1001 """
1002- Test that the cancel button click slot shuts down the threads correctly
1003+ Test that the reject method shuts down the threads correctly
1004 """
1005 # GIVEN: A FRW, some mocked threads and workers (that isn't quite done) and other mocked stuff
1006 mocked_worker = MagicMock()
1007@@ -235,17 +305,47 @@
1008 frw.thumbnail_download_threads = ['test_thread']
1009 with patch.object(frw.application, 'set_normal_cursor') as mocked_set_normal_cursor:
1010
1011- # WHEN: on_cancel_button_clicked() is called
1012- frw.on_cancel_button_clicked()
1013+ # WHEN: the reject method is called
1014+ frw.reject()
1015
1016 # THEN: The right things should be called in the right order
1017- assert frw.was_cancelled is True, 'The was_cancelled property should have been set to True'
1018 mocked_get_thread_worker.assert_called_once_with('test_thread')
1019 mocked_worker.cancel_download.assert_called_once()
1020 mocked_is_thread_finished.assert_called_with('test_thread')
1021 assert mocked_is_thread_finished.call_count == 2, 'isRunning() should have been called twice'
1022 mocked_time.sleep.assert_called_once_with(0.1)
1023- mocked_set_normal_cursor.assert_called_once_with()
1024+ mocked_set_normal_cursor.assert_called_once()
1025+ mocked_qwizard_reject.assert_called_once()
1026+
1027+ @patch('openlp.core.ui.firsttimeform.ProxyDialog')
1028+ def test_on_custom_button_clicked(self, mocked_proxy_dialog):
1029+ """
1030+ Test _on_custom_button when it is called whe the 'internet settings' (CustomButton1) button is not clicked.
1031+ """
1032+ # GIVEN: An instance of the FirstTimeForm
1033+ frw = FirstTimeForm(None)
1034+
1035+ # WHEN: Calling _on_custom_button_clicked with a different button to the 'internet settings button.
1036+ frw._on_custom_button_clicked(QtWidgets.QWizard.CustomButton2)
1037+
1038+ # THEN: The ProxyDialog should not be shown.
1039+ mocked_proxy_dialog.assert_not_called()
1040+
1041+ @patch('openlp.core.ui.firsttimeform.ProxyDialog')
1042+ def test_on_custom_button_clicked_internet_settings(self, mocked_proxy_dialog):
1043+ """
1044+ Test _on_custom_button when it is called when the 'internet settings' (CustomButton1) button is clicked.
1045+ """
1046+ # GIVEN: An instance of the FirstTimeForm
1047+ frw = FirstTimeForm(None)
1048+
1049+ # WHEN: Calling _on_custom_button_clicked with the constant for the 'internet settings' button (CustomButton1)
1050+ frw._on_custom_button_clicked(QtWidgets.QWizard.CustomButton1)
1051+
1052+ # THEN: The ProxyDialog should be shown.
1053+ mocked_proxy_dialog.assert_called_with(frw)
1054+ mocked_proxy_dialog().retranslate_ui.assert_called_once()
1055+ mocked_proxy_dialog().exec.assert_called_once()
1056
1057 @patch('openlp.core.ui.firsttimeform.critical_error_message_box')
1058 def test__parse_config_invalid_config(self, mocked_critical_error_message_box):
1059@@ -279,10 +379,10 @@
1060
1061 # THEN: the critical_error_message_box should have been called
1062 mocked_message_box.critical.assert_called_once_with(
1063- first_time_form, 'Network Error', 'There was a network error attempting to connect to retrieve '
1064- 'initial configuration information', 'OK')
1065+ first_time_form, 'Network Error',
1066+ 'There was a network error attempting to connect to retrieve initial configuration information', 'OK')
1067
1068- @patch('openlp.core.ui.firsttimewizard.Settings')
1069+ @patch('openlp.core.ui.firsttimeform.Settings')
1070 def test_on_projectors_check_box_checked(self, MockSettings):
1071 """
1072 Test that the projector panel is shown when the checkbox in the first time wizard is checked
1073@@ -300,7 +400,7 @@
1074 mocked_settings.value.assert_called_once_with('projector/show after wizard')
1075 mocked_settings.setValue.assert_called_once_with('projector/show after wizard', False)
1076
1077- @patch('openlp.core.ui.firsttimewizard.Settings')
1078+ @patch('openlp.core.ui.firsttimeform.Settings')
1079 def test_on_projectors_check_box_unchecked(self, MockSettings):
1080 """
1081 Test that the projector panel is shown when the checkbox in the first time wizard is checked
1082
1083=== modified file 'tests/functional/openlp_plugins/songs/test_openlpimporter.py'
1084--- tests/functional/openlp_plugins/songs/test_openlpimporter.py 2019-02-14 15:09:09 +0000
1085+++ tests/functional/openlp_plugins/songs/test_openlpimporter.py 2019-03-15 20:50:11 +0000
1086@@ -22,6 +22,7 @@
1087 """
1088 This module contains tests for the OpenLP song importer.
1089 """
1090+from pathlib import Path
1091 from unittest import TestCase
1092 from unittest.mock import MagicMock, patch
1093
1094@@ -66,10 +67,9 @@
1095 importer.stop_import_flag = True
1096
1097 # WHEN: Import source is not a list
1098- for source in ['not a list', 0]:
1099- importer.import_source = source
1100+ importer.import_source = Path()
1101
1102- # THEN: do_import should return none and the progress bar maximum should not be set.
1103- assert importer.do_import() is None, 'do_import should return None when import_source is not a list'
1104- assert mocked_import_wizard.progress_bar.setMaximum.called is False, \
1105- 'setMaximum on import_wizard.progress_bar should not have been called'
1106+ # THEN: do_import should return none and the progress bar maximum should not be set.
1107+ assert importer.do_import() is None, 'do_import should return None when import_source is not a list'
1108+ assert mocked_import_wizard.progress_bar.setMaximum.called is False, \
1109+ 'setMaximum on import_wizard.progress_bar should not have been called'