Merge branch 'develop' into 910_flatpak

This commit is contained in:
Micah Lee 2020-06-30 10:56:25 -07:00
commit 17b0af98f2
No known key found for this signature in database
GPG Key ID: 403C2657CD994F73
24 changed files with 462 additions and 168 deletions

View File

@ -103,7 +103,7 @@ Create a .rpm on Fedora-like distros: `./install/build_rpm.sh`
For openSUSE: There are instructions for building [in the wiki](https://github.com/micahflee/onionshare/wiki/Linux-Distribution-Support#opensuse-leap-150).
For ArchLinux: There is a PKBUILD available [here](https://aur.archlinux.org/packages/onionshare/) that can be used to install OnionShare.
For ArchLinux: There is a PKBUILD available [here](https://www.archlinux.org/packages/community/any/onionshare/) that can be used to install OnionShare.
If you find that these instructions don't work for your Linux distribution or version, consult the [Linux Distribution Support wiki guide](https://github.com/micahflee/onionshare/wiki/Linux-Distribution-Support), which might contain extra instructions.

View File

@ -211,6 +211,11 @@ class GuiCommon:
color: #cc0000;
}""",
# Share mode and child widget styles
"share_delete_all_files_button": """
QPushButton {
color: #3f7fcf;
}
""",
"share_zip_progess_bar": """
QProgressBar {
border: 1px solid #4e064f;

View File

@ -138,7 +138,7 @@ class Mode(QtWidgets.QWidget):
"""
# If this is a scheduled share, display the countdown til the share starts
if self.server_status.status == ServerStatus.STATUS_WORKING:
if self.server_status.autostart_timer_datetime:
if self.settings.get("general", "autostart_timer"):
now = QtCore.QDateTime.currentDateTime()
if self.server_status.local_only:
seconds_remaining = now.secsTo(
@ -227,13 +227,8 @@ class Mode(QtWidgets.QWidget):
# Start the onion thread. If this share was scheduled for a future date,
# the OnionThread will start and exit 'early' to obtain the port, password
# and onion address, but it will not start the WebThread yet.
if self.server_status.autostart_timer_datetime:
if self.settings.get("general", "autostart_timer"):
self.start_onion_thread(obtain_onion_early=True)
else:
self.start_onion_thread()
# If scheduling a share, delay starting the real share
if self.server_status.autostart_timer_datetime:
self.common.log("Mode", "start_server", "Starting auto-start timer")
self.startup_thread = AutoStartTimer(self)
# Once the timer has finished, start the real share, with a WebThread
@ -241,6 +236,8 @@ class Mode(QtWidgets.QWidget):
self.startup_thread.error.connect(self.start_server_error)
self.startup_thread.canceled = False
self.startup_thread.start()
else:
self.start_onion_thread()
def start_onion_thread(self, obtain_onion_early=False):
self.common.log("Mode", "start_server", "Starting an onion thread")

View File

@ -338,8 +338,8 @@ class FileSelection(QtWidgets.QVBoxLayout):
else:
self.add_button = QtWidgets.QPushButton(strings._("gui_add"))
self.add_button.clicked.connect(self.add)
self.delete_button = QtWidgets.QPushButton(strings._("gui_delete"))
self.delete_button.clicked.connect(self.delete)
self.remove_button = QtWidgets.QPushButton(strings._("gui_remove"))
self.remove_button.clicked.connect(self.delete)
button_layout = QtWidgets.QHBoxLayout()
button_layout.addStretch()
if self.common.platform == "Darwin":
@ -347,7 +347,7 @@ class FileSelection(QtWidgets.QVBoxLayout):
button_layout.addWidget(self.add_folder_button)
else:
button_layout.addWidget(self.add_button)
button_layout.addWidget(self.delete_button)
button_layout.addWidget(self.remove_button)
# Add the widgets
self.addWidget(self.file_list)
@ -366,7 +366,7 @@ class FileSelection(QtWidgets.QVBoxLayout):
self.add_folder_button.hide()
else:
self.add_button.hide()
self.delete_button.hide()
self.remove_button.hide()
else:
if self.common.platform == "Darwin":
self.add_files_button.show()
@ -376,9 +376,9 @@ class FileSelection(QtWidgets.QVBoxLayout):
# Delete button should be hidden if item isn't selected
if len(self.file_list.selectedItems()) == 0:
self.delete_button.hide()
self.remove_button.hide()
else:
self.delete_button.show()
self.remove_button.show()
# Update the file list
self.file_list.update()

View File

@ -76,7 +76,10 @@ class ModeSettingsWidget(QtWidgets.QWidget):
self.autostart_timer_widget.setCurrentSection(
QtWidgets.QDateTimeEdit.MinuteSection
)
self.autostart_timer_widget.hide()
if self.settings.get("general", "autostart_timer"):
self.autostart_timer_widget.show()
else:
self.autostart_timer_widget.hide()
# Autostart timer layout
autostart_timer_layout = QtWidgets.QHBoxLayout()
@ -104,7 +107,10 @@ class ModeSettingsWidget(QtWidgets.QWidget):
self.autostop_timer_widget.setCurrentSection(
QtWidgets.QDateTimeEdit.MinuteSection
)
self.autostop_timer_widget.hide()
if self.settings.get("general", "autostop_timer"):
self.autostop_timer_widget.show()
else:
self.autostop_timer_widget.hide()
# Autostop timer layout
autostop_timer_layout = QtWidgets.QHBoxLayout()

View File

@ -111,6 +111,17 @@ class ShareMode(Mode):
self.info_label = QtWidgets.QLabel()
self.info_label.hide()
# Delete all files button
self.remove_all_button = QtWidgets.QPushButton(
strings._("gui_file_selection_remove_all")
)
self.remove_all_button.setFlat(True)
self.remove_all_button.setStyleSheet(
self.common.gui.css["share_delete_all_files_button"]
)
self.remove_all_button.clicked.connect(self.delete_all)
self.remove_all_button.hide()
# Toggle history
self.toggle_history = ToggleHistory(
self.common,
@ -126,6 +137,7 @@ class ShareMode(Mode):
top_bar_layout = QtWidgets.QHBoxLayout()
top_bar_layout.addWidget(self.info_label)
top_bar_layout.addStretch()
top_bar_layout.addWidget(self.remove_all_button)
top_bar_layout.addWidget(self.toggle_history)
# Primary action layout
@ -198,6 +210,8 @@ class ShareMode(Mode):
# Hide and reset the downloads if we have previously shared
self.reset_info_counters()
self.remove_all_button.hide()
def start_server_step2_custom(self):
"""
Step 2 in starting the server. Zipping up files.
@ -257,6 +271,8 @@ class ShareMode(Mode):
self.history.update_in_progress()
self.file_selection.file_list.adjustSize()
self.remove_all_button.show()
def cancel_server_custom(self):
"""
Stop the compression thread on cancel
@ -343,6 +359,7 @@ class ShareMode(Mode):
if self.server_status.file_selection.get_num_files() > 0:
self.primary_action.show()
self.info_label.show()
self.remove_all_button.show()
def update_primary_action(self):
self.common.log("ShareMode", "update_primary_action")
@ -352,6 +369,7 @@ class ShareMode(Mode):
if file_count > 0:
self.primary_action.show()
self.info_label.show()
self.remove_all_button.show()
# Update the file count in the info label
total_size_bytes = 0
@ -374,6 +392,7 @@ class ShareMode(Mode):
else:
self.primary_action.hide()
self.info_label.hide()
self.remove_all_button.hide()
def reset_info_counters(self):
"""
@ -383,6 +402,15 @@ class ShareMode(Mode):
self.toggle_history.indicator_count = 0
self.toggle_history.update_indicator()
def delete_all(self):
"""
Delete All button clicked
"""
self.file_selection.file_list.clear()
self.file_selection.file_list.files_updated.emit()
self.file_selection.file_list.setCurrentItem(None)
@staticmethod
def _compute_total_size(filenames):
total_size = 0

View File

@ -114,6 +114,17 @@ class WebsiteMode(Mode):
self.info_label = QtWidgets.QLabel()
self.info_label.hide()
# Delete all files button
self.remove_all_button = QtWidgets.QPushButton(
strings._("gui_file_selection_remove_all")
)
self.remove_all_button.setFlat(True)
self.remove_all_button.setStyleSheet(
self.common.gui.css["share_delete_all_files_button"]
)
self.remove_all_button.clicked.connect(self.delete_all)
self.remove_all_button.hide()
# Toggle history
self.toggle_history = ToggleHistory(
self.common,
@ -129,6 +140,7 @@ class WebsiteMode(Mode):
top_bar_layout = QtWidgets.QHBoxLayout()
top_bar_layout.addWidget(self.info_label)
top_bar_layout.addStretch()
top_bar_layout.addWidget(self.remove_all_button)
top_bar_layout.addWidget(self.toggle_history)
# Primary action layout
@ -191,6 +203,8 @@ class WebsiteMode(Mode):
# Hide and reset the downloads if we have previously shared
self.reset_info_counters()
self.remove_all_button.hide()
def start_server_step2_custom(self):
"""
Step 2 in starting the server. Zipping up files.
@ -228,6 +242,8 @@ class WebsiteMode(Mode):
self.history.completed_count = 0
self.file_selection.file_list.adjustSize()
self.remove_all_button.show()
def cancel_server_custom(self):
"""
Log that the server has been cancelled
@ -248,6 +264,7 @@ class WebsiteMode(Mode):
if self.server_status.file_selection.get_num_files() > 0:
self.primary_action.show()
self.info_label.show()
self.remove_all_button.show()
def update_primary_action(self):
self.common.log("WebsiteMode", "update_primary_action")
@ -257,6 +274,7 @@ class WebsiteMode(Mode):
if file_count > 0:
self.primary_action.show()
self.info_label.show()
self.remove_all_button.show()
# Update the file count in the info label
total_size_bytes = 0
@ -279,6 +297,7 @@ class WebsiteMode(Mode):
else:
self.primary_action.hide()
self.info_label.hide()
self.remove_all_button.hide()
def reset_info_counters(self):
"""
@ -288,6 +307,15 @@ class WebsiteMode(Mode):
self.toggle_history.indicator_count = 0
self.toggle_history.update_indicator()
def delete_all(self):
"""
Delete All button clicked
"""
self.file_selection.file_list.clear()
self.file_selection.file_list.files_updated.emit()
self.file_selection.file_list.setCurrentItem(None)
@staticmethod
def _compute_total_size(filenames):
total_size = 0

View File

@ -20,10 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import platform
import textwrap
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import Qt
from onionshare import strings
from ..widgets import Alert
from ..widgets import QRCodeDialog
class ServerStatus(QtWidgets.QWidget):
@ -85,17 +87,31 @@ class ServerStatus(QtWidgets.QWidget):
self.url.setWordWrap(True)
self.url.setMinimumSize(self.url.sizeHint())
self.url.setStyleSheet(self.common.gui.css["server_status_url"])
self.url.setTextInteractionFlags(
Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard
)
self.copy_url_button = QtWidgets.QPushButton(strings._("gui_copy_url"))
self.copy_url_button.setFlat(True)
self.copy_url_button.setStyleSheet(
self.common.gui.css["server_status_url_buttons"]
)
self.copy_url_button.setMinimumHeight(65)
self.copy_url_button.clicked.connect(self.copy_url)
self.copy_hidservauth_button = QtWidgets.QPushButton(
strings._("gui_copy_hidservauth")
)
self.show_url_qr_code_button = QtWidgets.QPushButton(
strings._("gui_show_url_qr_code")
)
self.show_url_qr_code_button.hide()
self.show_url_qr_code_button.clicked.connect(
self.show_url_qr_code_button_clicked
)
self.show_url_qr_code_button.setFlat(True)
self.show_url_qr_code_button.setStyleSheet(
self.common.gui.css["server_status_url_buttons"]
)
self.copy_hidservauth_button.setFlat(True)
self.copy_hidservauth_button.setStyleSheet(
self.common.gui.css["server_status_url_buttons"]
@ -103,6 +119,7 @@ class ServerStatus(QtWidgets.QWidget):
self.copy_hidservauth_button.clicked.connect(self.copy_hidservauth)
url_buttons_layout = QtWidgets.QHBoxLayout()
url_buttons_layout.addWidget(self.copy_url_button)
url_buttons_layout.addWidget(self.show_url_qr_code_button)
url_buttons_layout.addWidget(self.copy_hidservauth_button)
url_buttons_layout.addStretch()
@ -190,6 +207,8 @@ class ServerStatus(QtWidgets.QWidget):
self.url.show()
self.copy_url_button.show()
self.show_url_qr_code_button.show()
if self.settings.get("general", "client_auth"):
self.copy_hidservauth_button.show()
else:
@ -269,7 +288,7 @@ class ServerStatus(QtWidgets.QWidget):
self.common.gui.css["server_status_button_working"]
)
self.server_button.setEnabled(True)
if self.autostart_timer_datetime:
if self.settings.get("general", "autostart_timer"):
self.server_button.setToolTip(
strings._("gui_start_server_autostart_timer_tooltip").format(
self.mode_settings_widget.autostart_timer_widget.dateTime().toString(
@ -279,15 +298,6 @@ class ServerStatus(QtWidgets.QWidget):
)
else:
self.server_button.setText(strings._("gui_please_wait"))
if self.settings.get("general", "autostart_timer"):
self.server_button.setToolTip(
strings._("gui_start_server_autostart_timer_tooltip").format(
self.mode_settings_widget.autostart_timer_widget.dateTime().toString(
"h:mm AP, MMMM dd, yyyy"
)
)
)
else:
self.server_button.setStyleSheet(
self.common.gui.css["server_status_button_working"]
@ -364,6 +374,12 @@ class ServerStatus(QtWidgets.QWidget):
self.cancel_server()
self.button_clicked.emit()
def show_url_qr_code_button_clicked(self):
"""
Show a QR code of the onion URL.
"""
self.qr_code_dialog = QRCodeDialog(self.common, self.get_url())
def start_server(self):
"""
Start the server.
@ -377,7 +393,7 @@ class ServerStatus(QtWidgets.QWidget):
The server has finished starting.
"""
self.status = self.STATUS_STARTED
self.copy_url()
# self.copy_url()
self.update()
self.server_started_finished.emit()

View File

@ -287,7 +287,7 @@ class Tab(QtWidgets.QWidget):
strings._("gui_status_indicator_share_stopped")
)
elif self.share_mode.server_status.status == ServerStatus.STATUS_WORKING:
if self.share_mode.server_status.autostart_timer_datetime:
if self.settings.get("general", "autostart_timer"):
self.set_server_status_indicator_working(
strings._("gui_status_indicator_share_scheduled")
)
@ -306,9 +306,14 @@ class Tab(QtWidgets.QWidget):
strings._("gui_status_indicator_share_stopped")
)
elif self.website_mode.server_status.status == ServerStatus.STATUS_WORKING:
self.set_server_status_indicator_working(
strings._("gui_status_indicator_share_working")
)
if self.website_mode.server_status.autostart_timer_datetime:
self.set_server_status_indicator_working(
strings._("gui_status_indicator_share_scheduled")
)
else:
self.set_server_status_indicator_working(
strings._("gui_status_indicator_share_working")
)
elif self.website_mode.server_status.status == ServerStatus.STATUS_STARTED:
self.set_server_status_indicator_started(
strings._("gui_status_indicator_share_started")
@ -320,7 +325,7 @@ class Tab(QtWidgets.QWidget):
strings._("gui_status_indicator_receive_stopped")
)
elif self.receive_mode.server_status.status == ServerStatus.STATUS_WORKING:
if self.receive_mode.server_status.autostart_timer_datetime:
if self.settings.get("general", "autostart_timer"):
self.set_server_status_indicator_working(
strings._("gui_status_indicator_receive_scheduled")
)

View File

@ -59,6 +59,7 @@ class TabWidget(QtWidgets.QTabWidget):
# Use a custom tab bar
tab_bar = TabBar()
tab_bar.move_new_tab_button.connect(self.move_new_tab_button)
tab_bar.currentChanged.connect(self.tab_changed)
self.setTabBar(tab_bar)
# Set up the tab widget
@ -106,6 +107,24 @@ class TabWidget(QtWidgets.QTabWidget):
self.new_tab_button.move(pos)
self.new_tab_button.raise_()
def tab_changed(self):
# Active tab was changed
tab_id = self.currentIndex()
self.common.log("TabWidget", "tab_changed", f"Tab was changed to {tab_id}")
try:
mode = self.tabs[tab_id].get_mode()
if mode:
# Update the server status indicator to reflect that of the current tab
self.tabs[tab_id].update_server_status_indicator()
else:
# If this tab doesn't have a mode set yet, blank the server status indicator
self.status_bar.server_status_image_label.clear()
self.status_bar.server_status_label.clear()
except KeyError:
# When all current tabs are closed, index briefly drops to -1 before resetting to 0
# which will otherwise trigger a KeyError on tab.get_mode() above.
pass
def new_tab_clicked(self):
# Create a new tab
self.add_tab()

View File

@ -18,7 +18,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from PyQt5 import QtCore, QtWidgets, QtGui
from onionshare import strings
import qrcode
class Alert(QtWidgets.QMessageBox):
"""
@ -90,3 +91,64 @@ class MinimumWidthWidget(QtWidgets.QWidget):
super(MinimumWidthWidget, self).__init__()
self.setMinimumWidth(width)
class Image(qrcode.image.base.BaseImage):
"""
A custom Image class, for use with the QR Code pixmap.
"""
def __init__(self, border, width, box_size):
self.border = border
self.width = width
self.box_size = box_size
size = (width + border * 2) * box_size
self._image = QtGui.QImage(
size, size, QtGui.QImage.Format_RGB16)
self._image.fill(QtCore.Qt.white)
def pixmap(self):
return QtGui.QPixmap.fromImage(self._image)
def drawrect(self, row, col):
painter = QtGui.QPainter(self._image)
painter.fillRect(
(col + self.border) * self.box_size,
(row + self.border) * self.box_size,
self.box_size, self.box_size,
QtCore.Qt.black)
def save(self, stream, kind=None):
pass
class QRCodeDialog(QtWidgets.QDialog):
"""
A dialog showing a QR code.
"""
def __init__(self, common, text):
super(QRCodeDialog, self).__init__()
self.common = common
self.text = text
self.common.log("QrCode", "__init__")
self.qr_label = QtWidgets.QLabel(self)
self.qr_label.setPixmap(
qrcode.make(self.text, image_factory=Image).pixmap())
self.qr_label_description = QtWidgets.QLabel(self)
self.qr_label_description.setText(strings._("gui_qr_code_description"))
self.qr_label_description.setWordWrap(True)
self.setWindowTitle(strings._("gui_qr_code_dialog_title"))
self.setWindowIcon(
QtGui.QIcon(self.common.get_resource_path("images/logo.png"))
)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.qr_label)
layout.addWidget(self.qr_label_description)
self.exec_()

276
poetry.lock generated
View File

@ -12,7 +12,7 @@ description = "Atomic file writes."
name = "atomicwrites"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.3.0"
version = "1.4.0"
[[package]]
category = "dev"
@ -34,7 +34,7 @@ description = "Python package for providing Mozilla's CA Bundle."
name = "certifi"
optional = false
python-versions = "*"
version = "2020.4.5.1"
version = "2020.6.20"
[[package]]
category = "main"
@ -50,12 +50,12 @@ description = "Composable command line interface toolkit"
name = "click"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "7.1.1"
version = "7.1.2"
[[package]]
category = "dev"
category = "main"
description = "Cross-platform colored terminal text."
marker = "sys_platform == \"win32\""
marker = "platform_system == \"Windows\" or sys_platform == \"win32\""
name = "colorama"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
@ -95,7 +95,7 @@ description = "Basic and Digest HTTP authentication for Flask routes"
name = "flask-httpauth"
optional = false
python-versions = "*"
version = "3.3.0"
version = "4.1.0"
[package.dependencies]
Flask = "*"
@ -114,7 +114,7 @@ description = "Internationalized Domain Names in Applications (IDNA)"
name = "idna"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "2.9"
version = "2.10"
[[package]]
category = "dev"
@ -123,14 +123,14 @@ marker = "python_version < \"3.8\""
name = "importlib-metadata"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
version = "1.6.0"
version = "1.7.0"
[package.dependencies]
zipp = ">=0.5"
[package.extras]
docs = ["sphinx", "rst.linker"]
testing = ["packaging", "importlib-resources"]
testing = ["packaging", "pep517", "importlib-resources (>=1.3)"]
[[package]]
category = "main"
@ -146,7 +146,7 @@ description = "A very fast and expressive template engine."
name = "jinja2"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "2.11.1"
version = "2.11.2"
[package.dependencies]
MarkupSafe = ">=0.23"
@ -179,7 +179,7 @@ description = "More routines for operating on iterables, beyond itertools"
name = "more-itertools"
optional = false
python-versions = ">=3.5"
version = "8.2.0"
version = "8.4.0"
[[package]]
category = "dev"
@ -187,12 +187,20 @@ description = "Core utilities for Python packages"
name = "packaging"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "20.3"
version = "20.4"
[package.dependencies]
pyparsing = ">=2.0.2"
six = "*"
[[package]]
category = "main"
description = "File system general utilities"
name = "pathtools"
optional = false
python-versions = "*"
version = "0.1.2"
[[package]]
category = "main"
description = "Python PE parsing module"
@ -220,13 +228,24 @@ version = ">=0.12"
[package.extras]
dev = ["pre-commit", "tox"]
[[package]]
category = "main"
description = "Cross-platform lib for process and system monitoring in Python."
name = "psutil"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "5.7.0"
[package.extras]
enum = ["enum34"]
[[package]]
category = "dev"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
name = "py"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.8.1"
version = "1.9.0"
[[package]]
category = "main"
@ -234,7 +253,7 @@ description = "Cryptographic library for Python"
name = "pycryptodome"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "3.9.7"
version = "3.9.8"
[[package]]
category = "dev"
@ -275,7 +294,7 @@ description = "The sip module support for PyQt5"
name = "pyqt5-sip"
optional = false
python-versions = ">=3.5"
version = "12.7.2"
version = "12.8.0"
[[package]]
category = "main"
@ -291,7 +310,7 @@ description = "pytest: simple powerful testing with Python"
name = "pytest"
optional = false
python-versions = ">=3.5"
version = "5.4.1"
version = "5.4.3"
[package.dependencies]
atomicwrites = ">=1.0"
@ -337,13 +356,31 @@ pytest = ">=3.0.0"
dev = ["pre-commit", "tox"]
doc = ["sphinx", "sphinx-rtd-theme"]
[[package]]
category = "main"
description = "QR Code image generator"
name = "qrcode"
optional = false
python-versions = "*"
version = "6.1"
[package.dependencies]
colorama = "*"
six = "*"
[package.extras]
dev = ["tox", "pytest", "mock"]
maintainer = ["zest.releaser"]
pil = ["pillow"]
test = ["pytest", "pytest-cov", "mock"]
[[package]]
category = "main"
description = "Python HTTP for Humans."
name = "requests"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "2.23.0"
version = "2.24.0"
[package.dependencies]
certifi = ">=2017.4.17"
@ -356,12 +393,12 @@ security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"]
[[package]]
category = "dev"
category = "main"
description = "Python 2 and 3 compatibility utilities"
name = "six"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
version = "1.14.0"
version = "1.15.0"
[[package]]
category = "main"
@ -377,20 +414,34 @@ description = "HTTP library with thread-safe connection pooling, file post, and
name = "urllib3"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
version = "1.25.8"
version = "1.25.9"
[package.extras]
brotli = ["brotlipy (>=0.6.0)"]
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"]
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"]
[[package]]
category = "main"
description = "Filesystem events monitoring"
name = "watchdog"
optional = false
python-versions = "*"
version = "0.10.3"
[package.dependencies]
pathtools = ">=0.1.1"
[package.extras]
watchmedo = ["PyYAML (>=3.10)", "argh (>=0.24.1)"]
[[package]]
category = "dev"
description = "Measures number of Terminal column cells of wide-character codes"
description = "Measures the displayed width of unicode strings in a terminal"
name = "wcwidth"
optional = false
python-versions = "*"
version = "0.1.9"
version = "0.2.5"
[[package]]
category = "main"
@ -418,7 +469,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
testing = ["jaraco.itertools", "func-timeout"]
[metadata]
content-hash = "e341ce712d9198499f5e816edc1a62d75f5a913f22880ed631a8698c78d88fb7"
content-hash = "3f46cfec01bcb5166c9f354aaf4439064b477955f3ea2373fcfdb65d5b89276e"
python-versions = "^3.7"
[metadata.files]
@ -427,24 +478,24 @@ altgraph = [
{file = "altgraph-0.17.tar.gz", hash = "sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa"},
]
atomicwrites = [
{file = "atomicwrites-1.3.0-py2.py3-none-any.whl", hash = "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4"},
{file = "atomicwrites-1.3.0.tar.gz", hash = "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"},
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
]
attrs = [
{file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"},
{file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"},
]
certifi = [
{file = "certifi-2020.4.5.1-py2.py3-none-any.whl", hash = "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304"},
{file = "certifi-2020.4.5.1.tar.gz", hash = "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"},
{file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"},
{file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"},
]
chardet = [
{file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},
{file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"},
]
click = [
{file = "click-7.1.1-py2.py3-none-any.whl", hash = "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"},
{file = "click-7.1.1.tar.gz", hash = "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc"},
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
{file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
]
colorama = [
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
@ -460,27 +511,27 @@ flask = [
{file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"},
]
flask-httpauth = [
{file = "Flask-HTTPAuth-3.3.0.tar.gz", hash = "sha256:6ef8b761332e780f9ff74d5f9056c2616f52babc1998b01d9f361a1e439e61b9"},
{file = "Flask_HTTPAuth-3.3.0-py2.py3-none-any.whl", hash = "sha256:0149953720489407e51ec24bc2f86273597b7973d71cd51f9443bd0e2a89bd72"},
{file = "Flask-HTTPAuth-4.1.0.tar.gz", hash = "sha256:9e028e4375039a49031eb9ecc40be4761f0540476040f6eff329a31dabd4d000"},
{file = "Flask_HTTPAuth-4.1.0-py2.py3-none-any.whl", hash = "sha256:29e0288869a213c7387f0323b6bf2c7191584fb1da8aa024d9af118e5cd70de7"},
]
future = [
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
]
idna = [
{file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"},
{file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"},
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
]
importlib-metadata = [
{file = "importlib_metadata-1.6.0-py2.py3-none-any.whl", hash = "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f"},
{file = "importlib_metadata-1.6.0.tar.gz", hash = "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"},
{file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"},
{file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"},
]
itsdangerous = [
{file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"},
{file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"},
]
jinja2 = [
{file = "Jinja2-2.11.1-py2.py3-none-any.whl", hash = "sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"},
{file = "Jinja2-2.11.1.tar.gz", hash = "sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250"},
{file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
{file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
]
macholib = [
{file = "macholib-1.14-py2.py3-none-any.whl", hash = "sha256:c500f02867515e6c60a27875b408920d18332ddf96b4035ef03beddd782d4281"},
@ -522,12 +573,15 @@ markupsafe = [
{file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
]
more-itertools = [
{file = "more-itertools-8.2.0.tar.gz", hash = "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"},
{file = "more_itertools-8.2.0-py3-none-any.whl", hash = "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c"},
{file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"},
{file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"},
]
packaging = [
{file = "packaging-20.3-py2.py3-none-any.whl", hash = "sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752"},
{file = "packaging-20.3.tar.gz", hash = "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3"},
{file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"},
{file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"},
]
pathtools = [
{file = "pathtools-0.1.2.tar.gz", hash = "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0"},
]
pefile = [
{file = "pefile-2019.4.18.tar.gz", hash = "sha256:a5d6e8305c6b210849b47a6174ddf9c452b2888340b8177874b862ba6c207645"},
@ -536,41 +590,54 @@ pluggy = [
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
]
psutil = [
{file = "psutil-5.7.0-cp27-none-win32.whl", hash = "sha256:298af2f14b635c3c7118fd9183843f4e73e681bb6f01e12284d4d70d48a60953"},
{file = "psutil-5.7.0-cp27-none-win_amd64.whl", hash = "sha256:75e22717d4dbc7ca529ec5063000b2b294fc9a367f9c9ede1f65846c7955fd38"},
{file = "psutil-5.7.0-cp35-cp35m-win32.whl", hash = "sha256:f344ca230dd8e8d5eee16827596f1c22ec0876127c28e800d7ae20ed44c4b310"},
{file = "psutil-5.7.0-cp35-cp35m-win_amd64.whl", hash = "sha256:e2d0c5b07c6fe5a87fa27b7855017edb0d52ee73b71e6ee368fae268605cc3f5"},
{file = "psutil-5.7.0-cp36-cp36m-win32.whl", hash = "sha256:a02f4ac50d4a23253b68233b07e7cdb567bd025b982d5cf0ee78296990c22d9e"},
{file = "psutil-5.7.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1413f4158eb50e110777c4f15d7c759521703bd6beb58926f1d562da40180058"},
{file = "psutil-5.7.0-cp37-cp37m-win32.whl", hash = "sha256:d008ddc00c6906ec80040d26dc2d3e3962109e40ad07fd8a12d0284ce5e0e4f8"},
{file = "psutil-5.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:73f35ab66c6c7a9ce82ba44b1e9b1050be2a80cd4dcc3352cc108656b115c74f"},
{file = "psutil-5.7.0-cp38-cp38-win32.whl", hash = "sha256:60b86f327c198561f101a92be1995f9ae0399736b6eced8f24af41ec64fb88d4"},
{file = "psutil-5.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:d84029b190c8a66a946e28b4d3934d2ca1528ec94764b180f7d6ea57b0e75e26"},
{file = "psutil-5.7.0.tar.gz", hash = "sha256:685ec16ca14d079455892f25bd124df26ff9137664af445563c1bd36629b5e0e"},
]
py = [
{file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"},
{file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"},
{file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"},
{file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"},
]
pycryptodome = [
{file = "pycryptodome-3.9.7-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:0e10f352ccbbcb5bb2dc4ecaf106564e65702a717d72ab260f9ac4c19753cfc2"},
{file = "pycryptodome-3.9.7-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:9c739b7795ccf2ef1fdad8d44e539a39ad300ee6786e804ea7f0c6a786eb5343"},
{file = "pycryptodome-3.9.7-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9977086e0f93adb326379897437373871b80501e1d176fec63c7f46fb300c862"},
{file = "pycryptodome-3.9.7-cp27-cp27m-win32.whl", hash = "sha256:83295a3fb5cf50c48631eb5b440cb5e9832d8c14d81d1d45f4497b67a9987de8"},
{file = "pycryptodome-3.9.7-cp27-cp27m-win_amd64.whl", hash = "sha256:b1e332587b3b195542e77681389c296e1837ca01240399d88803a075447d3557"},
{file = "pycryptodome-3.9.7-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9378c309aec1f8cd8bad361ed0816a440151b97a2a3f6ffdaba1d1a1fb76873a"},
{file = "pycryptodome-3.9.7-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4f94368ce2d65873a87ad867eb3bf63f4ba81eb97a9ee66d38c2b71ce5a7439"},
{file = "pycryptodome-3.9.7-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:f655addaaaa9974108d4808f4150652589cada96074c87115c52e575bfcd87d5"},
{file = "pycryptodome-3.9.7-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:9a94fca11fdc161460bd8659c15b6adef45c1b20da86402256eaf3addfaab324"},
{file = "pycryptodome-3.9.7-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:ea83bcd9d6c03248ebd46e71ac313858e0afd5aa2fa81478c0e653242f3eb476"},
{file = "pycryptodome-3.9.7-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:07024fc364869eae8d6ac0d316e089956e6aeffe42dbdcf44fe1320d96becf7f"},
{file = "pycryptodome-3.9.7-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:426c188c83c10df71f053e04b4003b1437bae5cb37606440e498b00f160d71d0"},
{file = "pycryptodome-3.9.7-cp35-cp35m-win32.whl", hash = "sha256:d61b012baa8c2b659e9890011358455c0019a4108536b811602d2f638c40802a"},
{file = "pycryptodome-3.9.7-cp35-cp35m-win_amd64.whl", hash = "sha256:1f4752186298caf2e9ff5354f2e694d607ca7342aa313a62005235d46e28cf04"},
{file = "pycryptodome-3.9.7-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:767ad0fb5d23efc36a4d5c2fc608ac603f3de028909bcf59abc943e0d0bc5a36"},
{file = "pycryptodome-3.9.7-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:2fbc472e0b567318fe2052281d5a8c0ae70099b446679815f655e9fbc18c3a65"},
{file = "pycryptodome-3.9.7-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:9230fcb5d948c3fb40049bace4d33c5d254f8232c2c0bba05d2570aea3ba4520"},
{file = "pycryptodome-3.9.7-cp36-cp36m-win32.whl", hash = "sha256:8f06556a8f7ea7b1e42eff39726bb0dca1c251205debae64e6eebea3cd7b438a"},
{file = "pycryptodome-3.9.7-cp36-cp36m-win_amd64.whl", hash = "sha256:d6e1bc5c94873bec742afe2dfadce0d20445b18e75c47afc0c115b19e5dd38dd"},
{file = "pycryptodome-3.9.7-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:3ec3dc2f80f71fd0c955ce48b81bfaf8914c6f63a41a738f28885a1c4892968a"},
{file = "pycryptodome-3.9.7-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cff31f5a8977534f255f729d5d2467526f2b10563a30bbdade92223e0bf264bd"},
{file = "pycryptodome-3.9.7-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ed5761b37615a1f222c5345bbf45272ae2cf8c7dff88a4f53a1e9f977cbb6d95"},
{file = "pycryptodome-3.9.7-cp37-cp37m-win32.whl", hash = "sha256:f011cd0062e54658b7086a76f8cf0f4222812acc66e219e196ea2d0a8849d0ed"},
{file = "pycryptodome-3.9.7-cp37-cp37m-win_amd64.whl", hash = "sha256:626c0a1d4d83ec6303f970a17158114f75c3ba1736f7f2983f7b40a265861bd8"},
{file = "pycryptodome-3.9.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be56bde3312e022d9d1d6afa124556460ad5c844c2fc63642f6af723c098d35"},
{file = "pycryptodome-3.9.7-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c818dc1f3eace93ee50c2b6b5c2becf7c418fa5dd1ba6fc0ef7db279ea21d5e4"},
{file = "pycryptodome-3.9.7-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:09b6d6bcc01a4eb1a2b4deeff5aa602a108ec5aed8ac75ae554f97d1d7f0a5ad"},
{file = "pycryptodome-3.9.7-cp38-cp38-win32.whl", hash = "sha256:7ac729d9091ed5478af2b4a4f44f5335a98febbc008af619e4569a59fe503e40"},
{file = "pycryptodome-3.9.7-cp38-cp38-win_amd64.whl", hash = "sha256:c109a26a21f21f695d369ff9b87f5d43e0d6c768d8384e10bc74142bed2e092e"},
{file = "pycryptodome-3.9.7.tar.gz", hash = "sha256:f1add21b6d179179b3c177c33d18a2186a09cc0d3af41ff5ed3f377360b869f2"},
{file = "pycryptodome-3.9.8-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:50348edd283afdccddc0938cdc674484533912ba8a99a27c7bfebb75030aa856"},
{file = "pycryptodome-3.9.8-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:80d57177a0b7c14d4594c62bbb47fe2f6309ad3b0a34348a291d570925c97a82"},
{file = "pycryptodome-3.9.8-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94"},
{file = "pycryptodome-3.9.8-cp27-cp27m-win32.whl", hash = "sha256:bcd5b8416e73e4b0d48afba3704d8c826414764dafaed7a1a93c442188d90ccc"},
{file = "pycryptodome-3.9.8-cp27-cp27m-win_amd64.whl", hash = "sha256:360955eece2cd0fa694a708d10303c6abd7b39614fa2547b6bd245da76198beb"},
{file = "pycryptodome-3.9.8-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:1e655746f539421d923fd48df8f6f40b3443d80b75532501c0085b64afed9df5"},
{file = "pycryptodome-3.9.8-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:709b9f144d23e290b9863121d1ace14a72e01f66ea9c903fbdc690520dfdfcf0"},
{file = "pycryptodome-3.9.8-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:6276478ada411aca97c0d5104916354b3d740d368407912722bd4d11aa9ee4c2"},
{file = "pycryptodome-3.9.8-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:54bdedd28476dea8a3cd86cb67c0df1f0e3d71cae8022354b0f879c41a3d27b2"},
{file = "pycryptodome-3.9.8-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f521178e5a991ffd04182ed08f552daca1affcb826aeda0e1945cd989a9d4345"},
{file = "pycryptodome-3.9.8-cp35-cp35m-win32.whl", hash = "sha256:a207231a52426de3ff20f5608f0687261a3329d97a036c51f7d4c606a6f30c23"},
{file = "pycryptodome-3.9.8-cp35-cp35m-win_amd64.whl", hash = "sha256:2b998dc45ef5f4e5cf5248a6edfcd8d8e9fb5e35df8e4259b13a1b10eda7b16b"},
{file = "pycryptodome-3.9.8-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:03d5cca8618620f45fd40f827423f82b86b3a202c8d44108601b0f5f56b04299"},
{file = "pycryptodome-3.9.8-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f78a68c2c820e4731e510a2df3eef0322f24fde1781ced970bf497b6c7d92982"},
{file = "pycryptodome-3.9.8-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:132a56abba24e2e06a479d8e5db7a48271a73a215f605017bbd476d31f8e71c1"},
{file = "pycryptodome-3.9.8-cp36-cp36m-win32.whl", hash = "sha256:67dcad1b8b201308586a8ca2ffe89df1e4f731d5a4cdd0610cc4ea790351c739"},
{file = "pycryptodome-3.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:b56638d58a3a4be13229c6a815cd448f9e3ce40c00880a5398471b42ee86f50e"},
{file = "pycryptodome-3.9.8-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:bec2bcdf7c9ce7f04d718e51887f3b05dc5c1cfaf5d2c2e9065ecddd1b2f6c9a"},
{file = "pycryptodome-3.9.8-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:abc2e126c9490e58a36a0f83516479e781d83adfb134576a5cbe5c6af2a3e93c"},
{file = "pycryptodome-3.9.8-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ef39c98d9b8c0736d91937d193653e47c3b19ddf4fc3bccdc5e09aaa4b0c5d21"},
{file = "pycryptodome-3.9.8-cp37-cp37m-win32.whl", hash = "sha256:4350a42028240c344ee855f032c7d4ad6ff4f813bfbe7121547b7dc579ecc876"},
{file = "pycryptodome-3.9.8-cp37-cp37m-win_amd64.whl", hash = "sha256:c8bf40cf6e281a4378e25846924327e728a887e8bf0ee83b2604a0f4b61692e8"},
{file = "pycryptodome-3.9.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d8074c8448cfd0705dfa71ca333277fce9786d0b9cac75d120545de6253f996a"},
{file = "pycryptodome-3.9.8-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8063a712fba642f78d3c506b0896846601b6de7f5c3d534e388ad0cc07f5a149"},
{file = "pycryptodome-3.9.8-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:dd302b6ae3965afeb5ef1b0d92486f986c0e65183cd7835973f0b593800590e6"},
{file = "pycryptodome-3.9.8-cp38-cp38-win32.whl", hash = "sha256:02e51e1d5828d58f154896ddfd003e2e7584869c275e5acbe290443575370fba"},
{file = "pycryptodome-3.9.8-cp38-cp38-win_amd64.whl", hash = "sha256:55eb61aca2c883db770999f50d091ff7c14016f2769ad7bca3d9b75d1d7c1b68"},
{file = "pycryptodome-3.9.8-cp39-cp39-manylinux1_i686.whl", hash = "sha256:39ef9fb52d6ec7728fce1f1693cb99d60ce302aeebd59bcedea70ca3203fda60"},
{file = "pycryptodome-3.9.8-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:de6e1cd75677423ff64712c337521e62e3a7a4fc84caabbd93207752e831a85a"},
{file = "pycryptodome-3.9.8.tar.gz", hash = "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4"},
]
pyinstaller = [
{file = "PyInstaller-3.6.tar.gz", hash = "sha256:3730fa80d088f8bb7084d32480eb87cbb4ddb64123363763cf8f2a1378c1c4b7"},
@ -587,23 +654,23 @@ pyqt5 = [
{file = "PyQt5-5.14.0.tar.gz", hash = "sha256:0145a6b7de15756366decb736c349a0cb510d706c83fda5b8cd9e0557bc1da72"},
]
pyqt5-sip = [
{file = "PyQt5_sip-12.7.2-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:df4f5cdb86f47df5f6fc35be29cc45df7b5a2c171d07dbf377d558b226554ea3"},
{file = "PyQt5_sip-12.7.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:360de29634e2ce1df84d2b588bd8c1a29b768f3a5225869d63adb03bc21bd32a"},
{file = "PyQt5_sip-12.7.2-cp35-cp35m-win32.whl", hash = "sha256:31c74602ccd6b70e4352550eb41aa980dc1d6009444f3c8eb1b844e84bd144cf"},
{file = "PyQt5_sip-12.7.2-cp35-cp35m-win_amd64.whl", hash = "sha256:168a6d700daf366b7cf255a8cabf8d07bfe2294859e6b3b2636c36c2f89265c9"},
{file = "PyQt5_sip-12.7.2-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:b68cfe632a512c0551e8860f35c1fcab5cd1ad5e168b4814fddd88121f447b0a"},
{file = "PyQt5_sip-12.7.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:b068f4791e97427d82a27e7df28cc3ee33f7e4353f48ed6a123f8cdba44266b2"},
{file = "PyQt5_sip-12.7.2-cp36-cp36m-win32.whl", hash = "sha256:9dd5769e83e64d017d02981563c8159d825425b6c4998c937a880888f4dcb7a3"},
{file = "PyQt5_sip-12.7.2-cp36-cp36m-win_amd64.whl", hash = "sha256:16a9a4daf85bfaa3aec35237ff28d8773a3ec937d9f8dc7fc3db7716de42d4a9"},
{file = "PyQt5_sip-12.7.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3cb9076ba0e574b2f026759103eb0e12051128714f5aa136cca53229d3ad72d1"},
{file = "PyQt5_sip-12.7.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:65fceeea2ac738a92f7e3e459ece1b4e2fbf49fd1d6b732a73d0d4bcfc434452"},
{file = "PyQt5_sip-12.7.2-cp37-cp37m-win32.whl", hash = "sha256:11f8cc2de287c3457fee53e781f06fb71f04251e7ae408ed22696ed65fd2bcf4"},
{file = "PyQt5_sip-12.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:85e68b8936f1756060ddcb3ef0a84af78ce89993fa6594b3049a0eca53d6d2fa"},
{file = "PyQt5_sip-12.7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4f87d59d29ca1c5a4005bbec27af002be787210dc5f8f87fe5d747883a836083"},
{file = "PyQt5_sip-12.7.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b34c1f227a8f8e97059f20e5424f117f66a302b42e34d4039158494c6371b1ce"},
{file = "PyQt5_sip-12.7.2-cp38-cp38-win32.whl", hash = "sha256:01919371d32b26208b2f0318f1e15680d3aa60d1ced1812a5dac8bdb483fea69"},
{file = "PyQt5_sip-12.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:a8a6c0512641fc042726b6253b2d5f3f3f800098334d198d8ebdf337d85ab3d7"},
{file = "PyQt5_sip-12.7.2.tar.gz", hash = "sha256:16a19b9f36985b8bff30b89fb8859d831713dd528fba5600563e36ff077960a2"},
{file = "PyQt5_sip-12.8.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:9ef12754021bcc1246f97e00ea62b5594dd5c61192830639ab4a1640bd4b7940"},
{file = "PyQt5_sip-12.8.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:fa3d70f370604efc67085849d3d1d3d2109faa716c520faf601d15845df64de6"},
{file = "PyQt5_sip-12.8.0-cp35-cp35m-win32.whl", hash = "sha256:61aa60fb848d740581646603a12c2dcb8d7c4cbd2a9c476a1c891ec360ff0b87"},
{file = "PyQt5_sip-12.8.0-cp35-cp35m-win_amd64.whl", hash = "sha256:8d9f4dc7dbae9783c5dafd66801875a2ebf9302c3addd5739f772285c1c1e91c"},
{file = "PyQt5_sip-12.8.0-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:9b69db29571dde679908fb237784a8e7af4a2cbf1b7bb25bdb86e487210e04d2"},
{file = "PyQt5_sip-12.8.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:58eae636e0b1926cddec98a703319a47f671cef07d73aaa525ba421cd4adfeb5"},
{file = "PyQt5_sip-12.8.0-cp36-cp36m-win32.whl", hash = "sha256:e6254647fa35e1260282aeb9c32a3dd363287b9a1ffcc4f22bd27e54178e92e4"},
{file = "PyQt5_sip-12.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f4c294bfaf2be8004583266d4621bfd3a387e12946f548f966a7fbec91845f1b"},
{file = "PyQt5_sip-12.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:224e2fbb7088595940c348d168a317caa2110cbb7a5b957a8c3fc0d9296ee069"},
{file = "PyQt5_sip-12.8.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c19c4ad67af087e8f4411da7422391b236b941f5f0697f615c5816455d1355d"},
{file = "PyQt5_sip-12.8.0-cp37-cp37m-win32.whl", hash = "sha256:2a1153cda63f2632d3d5698f0cf29f6b1f1d5162305dc6f5b23336ad8f1039ed"},
{file = "PyQt5_sip-12.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:94c80677b1e8c92fa080e24045d54ace5e4343c4ee6d0216675cd91d6f8e122a"},
{file = "PyQt5_sip-12.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2a2239d16a49ce6eaf10166a84424543111f8ebe49d3c124d02af91b01a58425"},
{file = "PyQt5_sip-12.8.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b1bbe763d431d26f9565cba3e99866768761366ab6d609d2506d194882156fa7"},
{file = "PyQt5_sip-12.8.0-cp38-cp38-win32.whl", hash = "sha256:d7b8a8f89385ad9e3da38e0123c22c0efc18005e0e2731b6b95e4c21db2049d2"},
{file = "PyQt5_sip-12.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:1d65ce08a56282fb0273dd06585b8927b88d4fba71c01a54f8e2ac87ac1ed387"},
{file = "PyQt5_sip-12.8.0.tar.gz", hash = "sha256:0a34b6596bdd28d52da3a51fa8d9bb0b287bcb605c2512aa3251b9028cc71f4d"},
]
pysocks = [
{file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"},
@ -611,8 +678,8 @@ pysocks = [
{file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"},
]
pytest = [
{file = "pytest-5.4.1-py3-none-any.whl", hash = "sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172"},
{file = "pytest-5.4.1.tar.gz", hash = "sha256:84dde37075b8805f3d1f392cc47e38a0e59518fb46a431cfdaf7cf1ce805f970"},
{file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"},
{file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"},
]
pytest-faulthandler = [
{file = "pytest-faulthandler-2.0.1.tar.gz", hash = "sha256:ed72bbce87ac344da81eb7d882196a457d4a1026a3da4a57154dacd85cd71ae5"},
@ -622,24 +689,31 @@ pytest-qt = [
{file = "pytest-qt-3.3.0.tar.gz", hash = "sha256:714b0bf86c5313413f2d300ac613515db3a1aef595051ab8ba2ffe619dbe8925"},
{file = "pytest_qt-3.3.0-py2.py3-none-any.whl", hash = "sha256:5f8928288f50489d83f5d38caf2d7d9fcd6e7cf769947902caa4661dc7c851e3"},
]
qrcode = [
{file = "qrcode-6.1-py2.py3-none-any.whl", hash = "sha256:3996ee560fc39532910603704c82980ff6d4d5d629f9c3f25f34174ce8606cf5"},
{file = "qrcode-6.1.tar.gz", hash = "sha256:505253854f607f2abf4d16092c61d4e9d511a3b4392e60bff957a68592b04369"},
]
requests = [
{file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"},
{file = "requests-2.23.0.tar.gz", hash = "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"},
{file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"},
{file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
]
six = [
{file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"},
{file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"},
{file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
{file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
]
stem = [
{file = "stem-1.8.0.tar.gz", hash = "sha256:a0b48ea6224e95f22aa34c0bc3415f0eb4667ddeae3dfb5e32a6920c185568c2"},
]
urllib3 = [
{file = "urllib3-1.25.8-py2.py3-none-any.whl", hash = "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc"},
{file = "urllib3-1.25.8.tar.gz", hash = "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"},
{file = "urllib3-1.25.9-py2.py3-none-any.whl", hash = "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"},
{file = "urllib3-1.25.9.tar.gz", hash = "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527"},
]
watchdog = [
{file = "watchdog-0.10.3.tar.gz", hash = "sha256:4214e1379d128b0588021880ccaf40317ee156d4603ac388b9adcf29165e0c04"},
]
wcwidth = [
{file = "wcwidth-0.1.9-py2.py3-none-any.whl", hash = "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1"},
{file = "wcwidth-0.1.9.tar.gz", hash = "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1"},
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
{file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
]
werkzeug = [
{file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"},

View File

@ -28,6 +28,9 @@ requests = "*"
stem = "*"
urllib3 = "*"
Werkzeug = "*"
watchdog = "*"
psutil = "*"
qrcode = "^6.1"
[tool.poetry.dev-dependencies]
atomicwrites = "*"

View File

@ -11,7 +11,8 @@
"gui_add": "Add",
"gui_add_files": "Add Files",
"gui_add_folder": "Add Folder",
"gui_delete": "Delete",
"gui_remove": "Remove",
"gui_file_selection_remove_all": "Remove All",
"gui_choose_items": "Choose",
"gui_share_start_server": "Start sharing",
"gui_share_stop_server": "Stop sharing",
@ -28,6 +29,9 @@
"gui_copied_url": "OnionShare address copied to clipboard",
"gui_copied_hidservauth_title": "Copied HidServAuth",
"gui_copied_hidservauth": "HidServAuth line copied to clipboard",
"gui_show_url_qr_code": "Show QR code",
"gui_qr_code_dialog_title": "OnionShare QR Code",
"gui_qr_code_description": "Scan this QR code with a QR reader, such as the camera on your phone, in order to more easily share the OnionShare address with someone.",
"gui_waiting_to_start": "Scheduled to start in {}. Click to cancel.",
"gui_please_wait": "Starting… Click to cancel.",
"error_rate_limit": "Someone has made too many wrong attempts to guess your password, so OnionShare has stopped the server. Start sharing again and send the recipient a new address to share.",
@ -205,4 +209,4 @@
"mode_settings_receive_data_dir_label": "Save files to",
"mode_settings_receive_data_dir_browse_button": "Browse",
"mode_settings_website_disable_csp_checkbox": "Disable Content Security Policy header (allows your website to use third-party resources)"
}
}

File diff suppressed because one or more lines are too long

2
share/static/js/jquery-3.5.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -4,6 +4,9 @@ $(function(){
$('#flashes').append($('<li>').addClass(category).text(message));
};
var scriptSrc = document.getElementById('receive-script').src;
var staticImgPath = scriptSrc.substr(0, scriptSrc.lastIndexOf( '/' )+1).replace('js', 'img');
// Intercept submitting the form
$('#send').submit(function(event){
event.preventDefault();
@ -38,7 +41,7 @@ $(function(){
// and update the status
if(event.loaded == event.total) {
$('.cancel', ajax.$upload_div).remove();
$('.upload-status', ajax.$upload_div).html('<img src="/static/img/ajax.gif" alt="" /> Waiting for data to finish traversing Tor network ...');
$('.upload-status', ajax.$upload_div).html('<img src="' + staticImgPath + '/ajax.gif" alt="" /> Waiting for data to finish traversing Tor network ...');
}
}, false);

View File

@ -38,7 +38,7 @@
</form>
</div>
<script src="{{ static_url_path }}/js/jquery-3.4.0.min.js"></script>
<script async src="{{ static_url_path }}/js/receive.js"></script>
<script src="{{ static_url_path }}/js/jquery-3.5.1.min.js"></script>
<script async src="{{ static_url_path }}/js/receive.js" id="receive-script"></script>
</body>
</html>

View File

@ -297,6 +297,17 @@ class GuiBaseTest(unittest.TestCase):
f"http://onionshare:{tab.get_mode().server_status.web.password}@127.0.0.1:{tab.app.port}",
)
def have_show_qr_code_button(self, tab):
"""Test that the Show QR Code URL button is shown and that it loads a QR Code Dialog"""
self.assertTrue(tab.get_mode().server_status.show_url_qr_code_button.isVisible())
def accept_dialog():
window = tab.common.gui.qtapp.activeWindow()
if window:
window.close()
QtCore.QTimer.singleShot(500, accept_dialog)
tab.get_mode().server_status.show_url_qr_code_button.click()
def server_status_indicator_says_started(self, tab):
"""Test that the Server Status indicator shows we are started"""
if type(tab.get_mode()) == ReceiveMode:
@ -388,13 +399,13 @@ class GuiBaseTest(unittest.TestCase):
tab.get_mode().server_status.file_selection.get_num_files(), num
)
def add_delete_buttons_hidden(self, tab):
"""Test that the add and delete buttons are hidden when the server starts"""
def add_remove_buttons_hidden(self, tab):
"""Test that the add and remove buttons are hidden when the server starts"""
self.assertFalse(
tab.get_mode().server_status.file_selection.add_button.isVisible()
)
self.assertFalse(
tab.get_mode().server_status.file_selection.delete_button.isVisible()
tab.get_mode().server_status.file_selection.remove_button.isVisible()
)
# Auto-stop timer tests

View File

@ -20,7 +20,7 @@ do
shift
done
pytest $PARAMS -vvv ./tests/test_cli*.py
pytest $PARAMS -vvv ./tests/test_cli*.py || exit 1
for filename in ./tests/test_gui_*.py; do
pytest $PARAMS -vvv --no-qt-log $filename
pytest $PARAMS -vvv --no-qt-log $filename || exit 1
done

View File

@ -112,6 +112,7 @@ class TestReceive(GuiBaseTest):
self.have_a_password(tab)
self.url_description_shown(tab)
self.have_copy_url_button(tab)
self.have_show_qr_code_button(tab)
self.server_status_indicator_says_started(tab)
self.web_page(tab, "Select the files you want to send, then click")

View File

@ -12,8 +12,8 @@ from .gui_base_test import GuiBaseTest
class TestShare(GuiBaseTest):
# Shared test methods
def deleting_all_files_hides_delete_button(self, tab):
"""Test that clicking on the file item shows the delete button. Test that deleting the only item in the list hides the delete button"""
def removing_all_files_hides_remove_button(self, tab):
"""Test that clicking on the file item shows the remove button. Test that removing the only item in the list hides the remove button"""
rect = tab.get_mode().server_status.file_selection.file_list.visualItemRect(
tab.get_mode().server_status.file_selection.file_list.item(0)
)
@ -22,12 +22,12 @@ class TestShare(GuiBaseTest):
QtCore.Qt.LeftButton,
pos=rect.center(),
)
# Delete button should be visible
# Remove button should be visible
self.assertTrue(
tab.get_mode().server_status.file_selection.delete_button.isVisible()
tab.get_mode().server_status.file_selection.remove_button.isVisible()
)
# Click delete, delete button should still be visible since we have one more file
tab.get_mode().server_status.file_selection.delete_button.click()
# Click remove, remove button should still be visible since we have one more file
tab.get_mode().server_status.file_selection.remove_button.click()
rect = tab.get_mode().server_status.file_selection.file_list.visualItemRect(
tab.get_mode().server_status.file_selection.file_list.item(0)
)
@ -37,17 +37,17 @@ class TestShare(GuiBaseTest):
pos=rect.center(),
)
self.assertTrue(
tab.get_mode().server_status.file_selection.delete_button.isVisible()
tab.get_mode().server_status.file_selection.remove_button.isVisible()
)
tab.get_mode().server_status.file_selection.delete_button.click()
tab.get_mode().server_status.file_selection.remove_button.click()
# No more files, the delete button should be hidden
# No more files, the remove button should be hidden
self.assertFalse(
tab.get_mode().server_status.file_selection.delete_button.isVisible()
tab.get_mode().server_status.file_selection.remove_button.isVisible()
)
def add_a_file_and_delete_using_its_delete_widget(self, tab):
"""Test that we can also delete a file by clicking on its [X] widget"""
def add_a_file_and_remove_using_its_remove_widget(self, tab):
"""Test that we can also remove a file by clicking on its [X] widget"""
num_files = tab.get_mode().server_status.file_selection.get_num_files()
tab.get_mode().server_status.file_selection.file_list.add_file(self.tmpfiles[0])
tab.get_mode().server_status.file_selection.file_list.item(
@ -55,6 +55,14 @@ class TestShare(GuiBaseTest):
).item_button.click()
self.file_selection_widget_has_files(tab, num_files)
def add_a_file_and_remove_using_remove_all_widget(self, tab):
"""Test that we can also remove all files by clicking on the Remove All widget"""
tab.get_mode().server_status.file_selection.file_list.add_file(self.tmpfiles[0])
tab.get_mode().server_status.file_selection.file_list.add_file(self.tmpfiles[1])
tab.get_mode().remove_all_button.click()
# Should be no files after clearing all
self.file_selection_widget_has_files(tab, 0)
def file_selection_widget_read_files(self, tab):
"""Re-add some files to the list so we can share"""
num_files = tab.get_mode().server_status.file_selection.get_num_files()
@ -189,7 +197,7 @@ class TestShare(GuiBaseTest):
"""Test that we can cancel a share before it's started up """
self.server_working_on_start_button_pressed(tab)
self.server_status_indicator_says_scheduled(tab)
self.add_delete_buttons_hidden(tab)
self.add_remove_buttons_hidden(tab)
self.mode_settings_widget_is_hidden(tab)
self.set_autostart_timer(tab, 10)
QtTest.QTest.mousePress(
@ -216,21 +224,22 @@ class TestShare(GuiBaseTest):
self.history_is_not_visible(tab)
self.click_toggle_history(tab)
self.history_is_visible(tab)
self.deleting_all_files_hides_delete_button(tab)
self.add_a_file_and_delete_using_its_delete_widget(tab)
self.removing_all_files_hides_remove_button(tab)
self.add_a_file_and_remove_using_its_remove_widget(tab)
self.file_selection_widget_read_files(tab)
def run_all_share_mode_started_tests(self, tab, startup_time=2000):
"""Tests in share mode after starting a share"""
self.server_working_on_start_button_pressed(tab)
self.server_status_indicator_says_starting(tab)
self.add_delete_buttons_hidden(tab)
self.add_remove_buttons_hidden(tab)
self.mode_settings_widget_is_hidden(tab)
self.server_is_started(tab, startup_time)
self.web_server_is_running(tab)
self.have_a_password(tab)
self.url_description_shown(tab)
self.have_copy_url_button(tab)
self.have_show_qr_code_button(tab)
self.server_status_indicator_says_started(tab)
def run_all_share_mode_download_tests(self, tab):
@ -269,7 +278,7 @@ class TestShare(GuiBaseTest):
self.run_all_share_mode_started_tests(tab)
self.run_all_share_mode_download_tests(tab)
def run_all_clear_all_button_tests(self, tab):
def run_all_clear_all_history_button_tests(self, tab):
"""Test the Clear All history button"""
self.run_all_share_mode_setup_tests(tab)
self.run_all_share_mode_started_tests(tab)
@ -279,6 +288,11 @@ class TestShare(GuiBaseTest):
self.individual_file_is_viewable_or_not(tab)
self.clear_all_history_items(tab, 2)
def run_all_remove_all_file_selection_button_tests(self, tab):
"""Test the Remove All File Selection button"""
self.run_all_share_mode_setup_tests(tab)
self.add_a_file_and_remove_using_remove_all_widget(tab)
def run_all_share_mode_individual_file_tests(self, tab):
"""Tests in share mode when viewing an individual file"""
self.run_all_share_mode_setup_tests(tab)
@ -375,15 +389,27 @@ class TestShare(GuiBaseTest):
self.close_all_tabs()
@pytest.mark.gui
def test_clear_all_button(self):
def test_clear_all_history_button(self):
"""
Test canceling a scheduled share
Test clearing all history items
"""
tab = self.new_share_tab()
tab.get_mode().autostop_sharing_checkbox.click()
self.run_all_common_setup_tests()
self.run_all_clear_all_button_tests(tab)
self.run_all_clear_all_history_button_tests(tab)
self.close_all_tabs()
@pytest.mark.gui
def test_remove_all_file_selection_button(self):
"""
Test remove all file items at once
"""
tab = self.new_share_tab()
self.run_all_common_setup_tests()
self.run_all_remove_all_file_selection_button_tests(tab)
self.close_all_tabs()
@ -561,6 +587,7 @@ class TestShare(GuiBaseTest):
Rate limit should be triggered
"""
tab = self.new_share_tab()
def accept_dialog():
window = tab.common.gui.qtapp.activeWindow()
if window:
@ -580,6 +607,7 @@ class TestShare(GuiBaseTest):
Public mode should skip the rate limit
"""
tab = self.new_share_tab()
def accept_dialog():
window = tab.common.gui.qtapp.activeWindow()
if window:

View File

@ -138,18 +138,21 @@ class TestTabs(GuiBaseTest):
self.gui.tabs.widget(1).share_button.click()
self.assertFalse(self.gui.tabs.widget(1).new_tab.isVisible())
self.assertTrue(self.gui.tabs.widget(1).share_mode.isVisible())
self.assertEqual(self.gui.status_bar.server_status_label.text(), 'Ready to share')
# New tab, receive files
self.gui.tabs.new_tab_button.click()
self.gui.tabs.widget(2).receive_button.click()
self.assertFalse(self.gui.tabs.widget(2).new_tab.isVisible())
self.assertTrue(self.gui.tabs.widget(2).receive_mode.isVisible())
self.assertEqual(self.gui.status_bar.server_status_label.text(), 'Ready to receive')
# New tab, publish website
self.gui.tabs.new_tab_button.click()
self.gui.tabs.widget(3).website_button.click()
self.assertFalse(self.gui.tabs.widget(3).new_tab.isVisible())
self.assertTrue(self.gui.tabs.widget(3).website_mode.isVisible())
self.assertEqual(self.gui.status_bar.server_status_label.text(), 'Ready to share')
# Close tabs
self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide).click()

View File

@ -64,12 +64,13 @@ class TestWebsite(GuiBaseTest):
"""Tests in website mode after starting a share"""
self.server_working_on_start_button_pressed(tab)
self.server_status_indicator_says_starting(tab)
self.add_delete_buttons_hidden(tab)
self.add_remove_buttons_hidden(tab)
self.server_is_started(tab, startup_time)
self.web_server_is_running(tab)
self.have_a_password(tab)
self.url_description_shown(tab)
self.have_copy_url_button(tab)
self.have_show_qr_code_button(tab)
self.server_status_indicator_says_started(tab)
def run_all_website_mode_download_tests(self, tab):