Add cetralised proxy support

bzr-revno: 2823
This commit is contained in:
Phill Ridout 2018-07-12 21:10:35 +01:00
commit 6a8bf83d27
6 changed files with 507 additions and 35 deletions

View File

@ -32,6 +32,7 @@ import requests
from openlp.core.common import trace_error_handler
from openlp.core.common.registry import Registry
from openlp.core.common.settings import ProxyMode, Settings
log = logging.getLogger(__name__ + '.__init__')
@ -64,6 +65,39 @@ CONNECTION_TIMEOUT = 30
CONNECTION_RETRIES = 2
def get_proxy_settings(mode=None):
"""
Create a dictionary containing the proxy settings.
:param ProxyMode | None mode: Specify the source of the proxy settings
:return: A dict using the format expected by the requests library.
:rtype: dict | None
"""
settings = Settings()
if mode is None:
mode = settings.value('advanced/proxy mode')
if mode == ProxyMode.NO_PROXY:
return {'http': None, 'https': None}
elif mode == ProxyMode.SYSTEM_PROXY:
# The requests library defaults to using the proxy settings in the environment variables
return
elif mode == ProxyMode.MANUAL_PROXY:
http_addr = settings.value('advanced/proxy http')
https_addr = settings.value('advanced/proxy https')
username = settings.value('advanced/proxy username')
password = settings.value('advanced/proxy password')
basic_auth = ''
if username:
basic_auth = '{username}:{password}@'.format(username=username, password=password)
http_value = None
https_value = None
if http_addr:
http_value = 'http://{basic_auth}{http_addr}'.format(basic_auth=basic_auth, http_addr=http_addr)
if https_addr:
https_value = 'https://{basic_auth}{https_addr}'.format(basic_auth=basic_auth, https_addr=https_addr)
return {'http': http_value, 'https': https_value}
def get_user_agent():
"""
Return a user agent customised for the platform the user is on.
@ -75,14 +109,15 @@ def get_user_agent():
return browser_list[random_index]
def get_web_page(url, headers=None, update_openlp=False, proxies=None):
def get_web_page(url, headers=None, update_openlp=False, proxy=None):
"""
Attempts to download the webpage at url and returns that page or None.
:param url: The URL to be downloaded.
:param header: An optional HTTP header to pass in the request to the web server.
:param update_openlp: Tells OpenLP to update itself if the page is successfully downloaded.
Defaults to False.
:param dict | None headers: An optional HTTP header to pass in the request to the web server.
:param update_openlp: Tells OpenLP to update itself if the page is successfully downloaded. Defaults to False.
:param dict | ProxyMode | None proxy: ProxyMode enum or a dictionary containing the proxy servers, with their types
as the key e.g. {'http': 'http://proxyserver:port', 'https': 'https://proxyserver:port'}
"""
if not url:
return None
@ -90,11 +125,13 @@ def get_web_page(url, headers=None, update_openlp=False, proxies=None):
headers = {}
if 'user-agent' not in [key.lower() for key in headers.keys()]:
headers['User-Agent'] = get_user_agent()
if not isinstance(proxy, dict):
proxy = get_proxy_settings(mode=proxy)
log.debug('Downloading URL = %s' % url)
retries = 0
while retries < CONNECTION_RETRIES:
try:
response = requests.get(url, headers=headers, proxies=proxies, timeout=float(CONNECTION_TIMEOUT))
response = requests.get(url, headers=headers, proxies=proxy, timeout=float(CONNECTION_TIMEOUT))
log.debug('Downloaded page {url}'.format(url=response.url))
break
except OSError:

View File

@ -26,6 +26,7 @@ import datetime
import json
import logging
import os
from enum import IntEnum
from tempfile import gettempdir
from PyQt5 import QtCore, QtGui
@ -38,6 +39,13 @@ log = logging.getLogger(__name__)
__version__ = 2
class ProxyMode(IntEnum):
NO_PROXY = 1
SYSTEM_PROXY = 2
MANUAL_PROXY = 3
# Fix for bug #1014422.
X11_BYPASS_DEFAULT = True
if is_linux(): # pragma: no cover
@ -116,6 +124,11 @@ class Settings(QtCore.QSettings):
'advanced/print file meta data': False,
'advanced/print notes': False,
'advanced/print slide text': False,
'advanced/proxy mode': ProxyMode.SYSTEM_PROXY,
'advanced/proxy http': '',
'advanced/proxy https': '',
'advanced/proxy username': '',
'advanced/proxy password': '',
'advanced/recent file count': 4,
'advanced/save current plugin': False,
'advanced/slide limits': SlideLimits.End,

View File

@ -35,6 +35,7 @@ from openlp.core.lib import SettingsTab, build_icon
from openlp.core.ui.style import HAS_DARK_STYLE
from openlp.core.widgets.edits import PathEdit
from openlp.core.widgets.enums import PathEditType
from openlp.core.widgets.widgets import ProxyWidget
log = logging.getLogger(__name__)
@ -76,6 +77,9 @@ class AdvancedTab(SettingsTab):
self.media_plugin_check_box = QtWidgets.QCheckBox(self.ui_group_box)
self.media_plugin_check_box.setObjectName('media_plugin_check_box')
self.ui_layout.addRow(self.media_plugin_check_box)
self.hide_mouse_check_box = QtWidgets.QCheckBox(self.ui_group_box)
self.hide_mouse_check_box.setObjectName('hide_mouse_check_box')
self.ui_layout.addWidget(self.hide_mouse_check_box)
self.double_click_live_check_box = QtWidgets.QCheckBox(self.ui_group_box)
self.double_click_live_check_box.setObjectName('double_click_live_check_box')
self.ui_layout.addRow(self.double_click_live_check_box)
@ -116,6 +120,24 @@ class AdvancedTab(SettingsTab):
self.use_dark_style_checkbox = QtWidgets.QCheckBox(self.ui_group_box)
self.use_dark_style_checkbox.setObjectName('use_dark_style_checkbox')
self.ui_layout.addRow(self.use_dark_style_checkbox)
# Service Item Slide Limits
self.slide_group_box = QtWidgets.QGroupBox(self.left_column)
self.slide_group_box.setObjectName('slide_group_box')
self.slide_layout = QtWidgets.QVBoxLayout(self.slide_group_box)
self.slide_layout.setObjectName('slide_layout')
self.slide_label = QtWidgets.QLabel(self.slide_group_box)
self.slide_label.setWordWrap(True)
self.slide_layout.addWidget(self.slide_label)
self.end_slide_radio_button = QtWidgets.QRadioButton(self.slide_group_box)
self.end_slide_radio_button.setObjectName('end_slide_radio_button')
self.slide_layout.addWidget(self.end_slide_radio_button)
self.wrap_slide_radio_button = QtWidgets.QRadioButton(self.slide_group_box)
self.wrap_slide_radio_button.setObjectName('wrap_slide_radio_button')
self.slide_layout.addWidget(self.wrap_slide_radio_button)
self.next_item_radio_button = QtWidgets.QRadioButton(self.slide_group_box)
self.next_item_radio_button.setObjectName('next_item_radio_button')
self.slide_layout.addWidget(self.next_item_radio_button)
self.left_layout.addWidget(self.slide_group_box)
# Data Directory
self.data_directory_group_box = QtWidgets.QGroupBox(self.left_column)
self.data_directory_group_box.setObjectName('data_directory_group_box')
@ -142,33 +164,6 @@ class AdvancedTab(SettingsTab):
self.data_directory_layout.addRow(self.data_directory_copy_check_layout)
self.data_directory_layout.addRow(self.new_data_directory_has_files_label)
self.left_layout.addWidget(self.data_directory_group_box)
# Hide mouse
self.hide_mouse_group_box = QtWidgets.QGroupBox(self.right_column)
self.hide_mouse_group_box.setObjectName('hide_mouse_group_box')
self.hide_mouse_layout = QtWidgets.QVBoxLayout(self.hide_mouse_group_box)
self.hide_mouse_layout.setObjectName('hide_mouse_layout')
self.hide_mouse_check_box = QtWidgets.QCheckBox(self.hide_mouse_group_box)
self.hide_mouse_check_box.setObjectName('hide_mouse_check_box')
self.hide_mouse_layout.addWidget(self.hide_mouse_check_box)
self.right_layout.addWidget(self.hide_mouse_group_box)
# Service Item Slide Limits
self.slide_group_box = QtWidgets.QGroupBox(self.right_column)
self.slide_group_box.setObjectName('slide_group_box')
self.slide_layout = QtWidgets.QVBoxLayout(self.slide_group_box)
self.slide_layout.setObjectName('slide_layout')
self.slide_label = QtWidgets.QLabel(self.slide_group_box)
self.slide_label.setWordWrap(True)
self.slide_layout.addWidget(self.slide_label)
self.end_slide_radio_button = QtWidgets.QRadioButton(self.slide_group_box)
self.end_slide_radio_button.setObjectName('end_slide_radio_button')
self.slide_layout.addWidget(self.end_slide_radio_button)
self.wrap_slide_radio_button = QtWidgets.QRadioButton(self.slide_group_box)
self.wrap_slide_radio_button.setObjectName('wrap_slide_radio_button')
self.slide_layout.addWidget(self.wrap_slide_radio_button)
self.next_item_radio_button = QtWidgets.QRadioButton(self.slide_group_box)
self.next_item_radio_button.setObjectName('next_item_radio_button')
self.slide_layout.addWidget(self.next_item_radio_button)
self.right_layout.addWidget(self.slide_group_box)
# Display Workarounds
self.display_workaround_group_box = QtWidgets.QGroupBox(self.right_column)
self.display_workaround_group_box.setObjectName('display_workaround_group_box')
@ -223,6 +218,9 @@ class AdvancedTab(SettingsTab):
self.service_name_example.setObjectName('service_name_example')
self.service_name_layout.addRow(self.service_name_example_label, self.service_name_example)
self.right_layout.addWidget(self.service_name_group_box)
# Proxies
self.proxy_widget = ProxyWidget(self.right_column)
self.right_layout.addWidget(self.proxy_widget)
# After the last item on each side, add some spacing
self.left_layout.addStretch()
self.right_layout.addStretch()
@ -311,7 +309,6 @@ class AdvancedTab(SettingsTab):
translate('OpenLP.AdvancedTab',
'Revert to the default service name "{name}".').format(name=UiStrings().DefaultServiceName))
self.service_name_example_label.setText(translate('OpenLP.AdvancedTab', 'Example:'))
self.hide_mouse_group_box.setTitle(translate('OpenLP.AdvancedTab', 'Mouse Cursor'))
self.hide_mouse_check_box.setText(translate('OpenLP.AdvancedTab', 'Hide mouse cursor when over display window'))
self.data_directory_new_label.setText(translate('OpenLP.AdvancedTab', 'Path:'))
self.data_directory_cancel_button.setText(translate('OpenLP.AdvancedTab', 'Cancel'))
@ -334,6 +331,7 @@ class AdvancedTab(SettingsTab):
self.wrap_slide_radio_button.setText(translate('OpenLP.GeneralTab', '&Wrap around'))
self.next_item_radio_button.setText(translate('OpenLP.GeneralTab', '&Move to next/previous service item'))
self.search_as_type_check_box.setText(translate('SongsPlugin.GeneralTab', 'Enable search as you type'))
self.proxy_widget.retranslate_ui()
def load(self):
"""
@ -436,6 +434,7 @@ class AdvancedTab(SettingsTab):
if HAS_DARK_STYLE:
settings.setValue('use_dark_style', self.use_dark_style_checkbox.isChecked())
settings.endGroup()
self.proxy_widget.save()
def on_search_as_type_check_box_changed(self, check_state):
self.is_search_as_you_type_enabled = (check_state == QtCore.Qt.Checked)

View File

@ -0,0 +1,132 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`~openlp.core.widgets.widgets` module contains custom widgets used in OpenLP
"""
from PyQt5 import QtWidgets
from openlp.core.common.i18n import translate
from openlp.core.common.settings import ProxyMode, Settings
class ProxyWidget(QtWidgets.QGroupBox):
"""
A proxy settings widget that implements loading and saving its settings.
"""
def __init__(self, parent=None):
"""
Initialise the widget.
:param QtWidgets.QWidget | None parent: The widgets parent
"""
super().__init__(parent)
self._setup()
def _setup(self):
"""
A setup method seperate from __init__ to allow easier testing
"""
self.setup_ui()
self.load()
def setup_ui(self):
"""
Create the widget layout and sub widgets
"""
self.layout = QtWidgets.QFormLayout(self)
self.radio_group = QtWidgets.QButtonGroup(self)
self.no_proxy_radio = QtWidgets.QRadioButton('', self)
self.radio_group.addButton(self.no_proxy_radio, ProxyMode.NO_PROXY)
self.layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.no_proxy_radio)
self.use_sysem_proxy_radio = QtWidgets.QRadioButton('', self)
self.radio_group.addButton(self.use_sysem_proxy_radio, ProxyMode.SYSTEM_PROXY)
self.layout.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.use_sysem_proxy_radio)
self.manual_proxy_radio = QtWidgets.QRadioButton('', self)
self.radio_group.addButton(self.manual_proxy_radio, ProxyMode.MANUAL_PROXY)
self.layout.setWidget(2, QtWidgets.QFormLayout.SpanningRole, self.manual_proxy_radio)
self.http_edit = QtWidgets.QLineEdit(self)
self.layout.addRow('HTTP:', self.http_edit)
self.https_edit = QtWidgets.QLineEdit(self)
self.layout.addRow('HTTPS:', self.https_edit)
self.username_edit = QtWidgets.QLineEdit(self)
self.layout.addRow('Username:', self.username_edit)
self.password_edit = QtWidgets.QLineEdit(self)
self.password_edit.setEchoMode(QtWidgets.QLineEdit.Password)
self.layout.addRow('Password:', self.password_edit)
# Signal / Slots
self.radio_group.buttonToggled.connect(self.on_radio_group_button_toggled)
def on_radio_group_button_toggled(self, button, checked):
"""
Handles the toggled signal on the radio buttons. The signal is emitted twice if a radio butting being toggled on
causes another radio button in the group to be toggled off.
En/Disables the `Manual Proxy` line edits depending on the currently selected radio button
:param QtWidgets.QRadioButton button: The button that has toggled
:param bool checked: The buttons new state
"""
id = self.radio_group.id(button) # The work around (see above comment)
enable_manual_edits = id == ProxyMode.MANUAL_PROXY and checked
self.http_edit.setEnabled(enable_manual_edits)
self.https_edit.setEnabled(enable_manual_edits)
self.username_edit.setEnabled(enable_manual_edits)
self.password_edit.setEnabled(enable_manual_edits)
def retranslate_ui(self):
"""
Translate the Ui
"""
self.setTitle(translate('OpenLP.ProxyWidget', 'Proxy Server Settings'))
self.no_proxy_radio.setText(translate('OpenLP.ProxyWidget', 'No prox&y'))
self.use_sysem_proxy_radio.setText(translate('OpenLP.ProxyWidget', '&Use system proxy'))
self.manual_proxy_radio.setText(translate('OpenLP.ProxyWidget', '&Manual proxy configuration'))
proxy_example = translate('OpenLP.ProxyWidget', 'e.g. proxy_server_address:port_no')
self.layout.labelForField(self.http_edit).setText(translate('OpenLP.ProxyWidget', 'HTTP:'))
self.http_edit.setPlaceholderText(proxy_example)
self.layout.labelForField(self.https_edit).setText(translate('OpenLP.ProxyWidget', 'HTTPS:'))
self.https_edit.setPlaceholderText(proxy_example)
self.layout.labelForField(self.username_edit).setText(translate('OpenLP.ProxyWidget', 'Username:'))
self.layout.labelForField(self.password_edit).setText(translate('OpenLP.ProxyWidget', 'Password:'))
def load(self):
"""
Load the data from the settings to the widget.
"""
settings = Settings()
checked_radio = self.radio_group.button(settings.value('advanced/proxy mode'))
checked_radio.setChecked(True)
self.http_edit.setText(settings.value('advanced/proxy http'))
self.https_edit.setText(settings.value('advanced/proxy https'))
self.username_edit.setText(settings.value('advanced/proxy username'))
self.password_edit.setText(settings.value('advanced/proxy password'))
def save(self):
"""
Save the widget data to the settings
"""
settings = Settings() # TODO: Migrate from old system
settings.setValue('advanced/proxy mode', self.radio_group.checkedId())
settings.setValue('advanced/proxy http', self.http_edit.text())
settings.setValue('advanced/proxy https', self.https_edit.text())
settings.setValue('advanced/proxy username', self.username_edit.text())
settings.setValue('advanced/proxy password', self.password_edit.text())

View File

@ -27,13 +27,14 @@ import tempfile
from unittest import TestCase
from unittest.mock import MagicMock, patch
from openlp.core.common.httputils import get_user_agent, get_web_page, get_url_file_size, download_file
from openlp.core.common.httputils import ProxyMode, download_file, get_proxy_settings, get_url_file_size, \
get_user_agent, get_web_page
from openlp.core.common.path import Path
from openlp.core.common.settings import Settings
from tests.helpers.testmixin import TestMixin
class TestHttpUtils(TestCase, TestMixin):
"""
A test suite to test out various http helper functions.
"""
@ -240,3 +241,119 @@ class TestHttpUtils(TestCase, TestMixin):
# THEN: socket.timeout should have been caught
# NOTE: Test is if $tmpdir/tempfile is still there, then test fails since ftw deletes bad downloaded files
assert os.path.exists(self.tempfile) is False, 'tempfile should have been deleted'
class TestGetProxySettings(TestCase, TestMixin):
def setUp(self):
self.build_settings()
self.addCleanup(self.destroy_settings)
@patch('openlp.core.common.httputils.Settings')
def test_mode_arg_specified(self, MockSettings):
"""
Test that the argument is used rather than reading the 'advanced/proxy mode' setting
"""
# GIVEN: Mocked settings
mocked_settings = MagicMock()
MockSettings.return_value = mocked_settings
# WHEN: Calling `get_proxy_settings` with the mode arg specified
get_proxy_settings(mode=ProxyMode.NO_PROXY)
# THEN: The mode arg should have been used rather than looking it up in the settings
mocked_settings.value.assert_not_called()
@patch('openlp.core.common.httputils.Settings')
def test_mode_incorrect_arg_specified(self, MockSettings):
"""
Test that the system settings are used when the mode arg specieied is invalid
"""
# GIVEN: Mocked settings
mocked_settings = MagicMock()
MockSettings.return_value = mocked_settings
# WHEN: Calling `get_proxy_settings` with an invalid mode arg specified
result = get_proxy_settings(mode='qwerty')
# THEN: An None should be returned
mocked_settings.value.assert_not_called()
assert result is None
def test_no_proxy_mode(self):
"""
Test that a dictionary with http and https values are set to None is returned, when `NO_PROXY` mode is specified
"""
# GIVEN: A `proxy mode` setting of NO_PROXY
Settings().setValue('advanced/proxy mode', ProxyMode.NO_PROXY)
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# THEN: The returned value should be a dictionary with http and https values set to None
assert result == {'http': None, 'https': None}
def test_system_proxy_mode(self):
"""
Test that None is returned, when `SYSTEM_PROXY` mode is specified
"""
# GIVEN: A `proxy mode` setting of SYSTEM_PROXY
Settings().setValue('advanced/proxy mode', ProxyMode.SYSTEM_PROXY)
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# THEN: The returned value should be None
assert result is None
def test_manual_proxy_mode_no_auth(self):
"""
Test that the correct proxy addresses are returned when basic authentication is not used
"""
# GIVEN: A `proxy mode` setting of MANUAL_PROXY with proxy servers, but no auth credentials are supplied
Settings().setValue('advanced/proxy mode', ProxyMode.MANUAL_PROXY)
Settings().setValue('advanced/proxy http', 'testhttp.server:port')
Settings().setValue('advanced/proxy https', 'testhttps.server:port')
Settings().setValue('advanced/proxy username', '')
Settings().setValue('advanced/proxy password', '')
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# THEN: The returned value should be the proxy servers without authentication
assert result == {'http': 'http://testhttp.server:port', 'https': 'https://testhttps.server:port'}
def test_manual_proxy_mode_auth(self):
"""
Test that the correct proxy addresses are returned when basic authentication is used
"""
# GIVEN: A `proxy mode` setting of MANUAL_PROXY with proxy servers and auth credentials supplied
Settings().setValue('advanced/proxy mode', ProxyMode.MANUAL_PROXY)
Settings().setValue('advanced/proxy http', 'testhttp.server:port')
Settings().setValue('advanced/proxy https', 'testhttps.server:port')
Settings().setValue('advanced/proxy username', 'user')
Settings().setValue('advanced/proxy password', 'pass')
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# THEN: The returned value should be the proxy servers with the authentication credentials
assert result == {'http': 'http://user:pass@testhttp.server:port',
'https': 'https://user:pass@testhttps.server:port'}
def test_manual_proxy_mode_no_servers(self):
"""
Test that the system proxies are overidden when the MANUAL_PROXY mode is specified, but no server addresses are
supplied
"""
# GIVEN: A `proxy mode` setting of MANUAL_PROXY with no servers specified
Settings().setValue('advanced/proxy mode', ProxyMode.MANUAL_PROXY)
Settings().setValue('advanced/proxy http', '')
Settings().setValue('advanced/proxy https', '')
Settings().setValue('advanced/proxy username', 'user')
Settings().setValue('advanced/proxy password', 'pass')
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# THEN: The returned value should be the proxy servers set to None
assert result == {'http': None, 'https': None}

View File

@ -0,0 +1,174 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Module to test the custom widgets.
"""
from unittest import TestCase
from unittest.mock import MagicMock, call, patch
from openlp.core.common.registry import Registry
from openlp.core.common.settings import ProxyMode
from openlp.core.widgets.widgets import ProxyWidget
from tests.helpers.testmixin import TestMixin
class TestProxyWidget(TestCase, TestMixin):
"""
Test the EditCustomForm.
"""
def setUp(self):
"""
Create the UI
"""
Registry.create()
self.setup_application()
def test_radio_button_exclusivity_no_proxy(self):
"""
Test that only one radio button can be checked at a time, and that the line edits are only enabled when the
`manual_proxy_radio` is checked
"""
# GIVEN: An instance of the `openlp.core.common.widgets.widgets.ProxyWidget` with a radio already checked
proxy_widget = ProxyWidget()
proxy_widget.manual_proxy_radio.setChecked(True)
# WHEN: 'Checking' the `no_proxy_radio` button
proxy_widget.no_proxy_radio.setChecked(True)
# THEN: The other radio buttons should not be checked and the line edits should not be enabled
assert proxy_widget.use_sysem_proxy_radio.isChecked() is False
assert proxy_widget.manual_proxy_radio.isChecked() is False
assert proxy_widget.http_edit.isEnabled() is False
assert proxy_widget.https_edit.isEnabled() is False
assert proxy_widget.username_edit.isEnabled() is False
assert proxy_widget.password_edit.isEnabled() is False
def test_radio_button_exclusivity_system_proxy(self):
"""
Test that only one radio button can be checked at a time, and that the line edits are only enabled when the
`manual_proxy_radio` is checked
"""
# GIVEN: An instance of the `openlp.core.common.widgets.widgets.ProxyWidget` with a radio already checked
proxy_widget = ProxyWidget()
proxy_widget.manual_proxy_radio.setChecked(True)
# WHEN: 'Checking' the `use_sysem_proxy_radio` button
proxy_widget.use_sysem_proxy_radio.setChecked(True)
# THEN: The other radio buttons should not be checked and the line edits should not be enabled
assert proxy_widget.no_proxy_radio.isChecked() is False
assert proxy_widget.manual_proxy_radio.isChecked() is False
assert proxy_widget.http_edit.isEnabled() is False
assert proxy_widget.https_edit.isEnabled() is False
assert proxy_widget.username_edit.isEnabled() is False
assert proxy_widget.password_edit.isEnabled() is False
def test_radio_button_exclusivity_manual_proxy(self):
"""
Test that only one radio button can be checked at a time, and that the line edits are only enabled when the
`manual_proxy_radio` is checked
"""
# GIVEN: An instance of the `openlp.core.common.widgets.widgets.ProxyWidget` with a radio already checked
proxy_widget = ProxyWidget()
proxy_widget.no_proxy_radio.setChecked(True)
# WHEN: 'Checking' the `manual_proxy_radio` button
proxy_widget.manual_proxy_radio.setChecked(True)
# THEN: The other radio buttons should not be checked and the line edits should be enabled
assert proxy_widget.no_proxy_radio.isChecked() is False
assert proxy_widget.use_sysem_proxy_radio.isChecked() is False
assert proxy_widget.http_edit.isEnabled() is True
assert proxy_widget.https_edit.isEnabled() is True
assert proxy_widget.username_edit.isEnabled() is True
assert proxy_widget.password_edit.isEnabled() is True
def test_proxy_widget_load_default_settings(self):
"""
Test that the default settings are loaded from the config correctly
"""
# GIVEN: And instance of the widget with default settings
proxy_widget = ProxyWidget()
# WHEN: Calling the `load` method
proxy_widget.load()
# THEN: The widget should be in its default state
assert proxy_widget.use_sysem_proxy_radio.isChecked() is True
assert proxy_widget.http_edit.text() == ''
assert proxy_widget.https_edit.text() == ''
assert proxy_widget.username_edit.text() == ''
assert proxy_widget.password_edit.text() == ''
@patch.object(ProxyWidget, 'load')
@patch('openlp.core.widgets.widgets.Settings')
def test_proxy_widget_save_no_proxy_settings(self, settings_patcher, proxy_widget_load_patcher):
"""
Test that the settings are saved correctly
"""
# GIVEN: A Mocked settings instance of the proxy widget with some known values set
settings_instance = MagicMock()
settings_patcher.return_value = settings_instance
proxy_widget = ProxyWidget()
proxy_widget.no_proxy_radio.setChecked(True)
proxy_widget.http_edit.setText('')
proxy_widget.https_edit.setText('')
proxy_widget.username_edit.setText('')
proxy_widget.password_edit.setText('')
# WHEN: Calling save
proxy_widget.save()
# THEN: The settings should be set as expected
settings_instance.setValue.assert_has_calls(
[call('advanced/proxy mode', ProxyMode.NO_PROXY),
call('advanced/proxy http', ''),
call('advanced/proxy https', ''),
call('advanced/proxy username', ''),
call('advanced/proxy password', '')])
@patch.object(ProxyWidget, 'load')
@patch('openlp.core.widgets.widgets.Settings')
def test_proxy_widget_save_manual_settings(self, settings_patcher, proxy_widget_load_patcher):
"""
Test that the settings are saved correctly
"""
# GIVEN: A Mocked and instance of the proxy widget with some known values set
settings_instance = MagicMock()
settings_patcher.return_value = settings_instance
proxy_widget = ProxyWidget()
proxy_widget.manual_proxy_radio.setChecked(True)
proxy_widget.http_edit.setText('http_proxy_server:port')
proxy_widget.https_edit.setText('https_proxy_server:port')
proxy_widget.username_edit.setText('username')
proxy_widget.password_edit.setText('password')
# WHEN: Calling save
proxy_widget.save()
# THEN: The settings should be set as expected
settings_instance.setValue.assert_has_calls(
[call('advanced/proxy mode', ProxyMode.MANUAL_PROXY),
call('advanced/proxy http', 'http_proxy_server:port'),
call('advanced/proxy https', 'https_proxy_server:port'),
call('advanced/proxy username', 'username'),
call('advanced/proxy password', 'password')])