diff --git a/securedrop_client/gui/__init__.py b/securedrop_client/gui/__init__.py
index 3fd4c9d4e..77f7f60fe 100644
--- a/securedrop_client/gui/__init__.py
+++ b/securedrop_client/gui/__init__.py
@@ -148,6 +148,9 @@ def update_image(self, filename: str, svg_size: str = None) -> None:
class SecureQLabel(QLabel):
+
+ MAX_PREVIEW_LENGTH = 200
+
def __init__(
self,
text: str = "",
@@ -168,6 +171,7 @@ def __init__(
def setText(self, text: str) -> None:
text = text.strip()
self.setTextFormat(Qt.PlainText)
+ self.preview_text = text[: self.MAX_PREVIEW_LENGTH]
elided_text = self.get_elided_text(text)
self.elided = True if elided_text != text else False
if self.elided and self.with_tooltip:
@@ -175,6 +179,9 @@ def setText(self, text: str) -> None:
self.setToolTip(tooltip_label.text())
super().setText(elided_text)
+ def refresh_preview_text(self) -> None:
+ self.setText(self.preview_text)
+
def get_elided_text(self, full_text: str) -> str:
if not self.max_length:
return full_text
diff --git a/securedrop_client/gui/widgets.py b/securedrop_client/gui/widgets.py
index 51aca64a5..b973eb331 100644
--- a/securedrop_client/gui/widgets.py
+++ b/securedrop_client/gui/widgets.py
@@ -596,8 +596,8 @@ def __init__(self, parent: QObject):
self.view_layout.addWidget(self.empty_conversation_view)
# Add widgets to layout
- self.layout.addWidget(self.source_list)
- self.layout.addWidget(self.view_holder)
+ self.layout.addWidget(self.source_list, stretch=1)
+ self.layout.addWidget(self.view_holder, stretch=2)
# Note: We should not delete SourceConversationWrapper when its source is unselected. This
# is a temporary solution to keep copies of our objects since we do delete them.
@@ -802,8 +802,10 @@ class SourceList(QListWidget):
"""
NUM_SOURCES_TO_ADD_AT_A_TIME = 32
+ INITIAL_UPDATE_SCROLLBAR_WIDTH = 20
source_selected = pyqtSignal(str)
+ adjust_preview = pyqtSignal(int)
def __init__(self):
super().__init__()
@@ -815,12 +817,19 @@ def __init__(self):
layout = QVBoxLayout(self)
self.setLayout(layout)
+ # Disable horizontal scrollbar for SourceList widget
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+
# Enable ordering.
self.setSortingEnabled(True)
# To hold references to SourceListWidgetItem instances indexed by source UUID.
self.source_items = {}
+ def resizeEvent(self, event):
+ self.adjust_preview.emit(event.size().width())
+ super().resizeEvent(event)
+
def setup(self, controller):
self.controller = controller
self.controller.reply_succeeded.connect(self.set_snippet)
@@ -876,13 +885,14 @@ def update(self, sources: List[Source]) -> List[str]:
# Add widgets for new sources
for uuid in sources_to_add:
source_widget = SourceWidget(
- self.controller, sources_to_add[uuid], self.source_selected
+ self.controller, sources_to_add[uuid], self.source_selected, self.adjust_preview
)
source_item = SourceListWidgetItem(self)
source_item.setSizeHint(source_widget.sizeHint())
self.insertItem(0, source_item)
self.setItemWidget(source_item, source_widget)
self.source_items[uuid] = source_item
+ self.adjust_preview.emit(self.width() - self.INITIAL_UPDATE_SCROLLBAR_WIDTH)
# Re-sort SourceList to make sure the most recently-updated sources appear at the top
self.sortItems(Qt.DescendingOrder)
@@ -905,6 +915,7 @@ def add_source(self, sources, slice_size=1):
def schedule_source_management(slice_size=slice_size):
if not sources:
+ self.adjust_preview.emit(self.width() - self.INITIAL_UPDATE_SCROLLBAR_WIDTH)
return
# Process the remaining "slice_size" number of sources.
@@ -912,7 +923,9 @@ def schedule_source_management(slice_size=slice_size):
for source in sources_slice:
try:
source_uuid = source.uuid
- source_widget = SourceWidget(self.controller, source, self.source_selected)
+ source_widget = SourceWidget(
+ self.controller, source, self.source_selected, self.adjust_preview
+ )
source_item = SourceListWidgetItem(self)
source_item.setSizeHint(source_widget.sizeHint())
self.insertItem(0, source_item)
@@ -975,6 +988,28 @@ def set_snippet(self, source_uuid: str, collection_item_uuid: str, content: str)
source_widget.set_snippet(source_uuid, collection_item_uuid, content)
+class SourcePreview(SecureQLabel):
+ PREVIEW_WIDTH_DIFFERENCE = 140
+
+ def __init__(self):
+ super().__init__()
+
+ def adjust_preview(self, width):
+ """
+ This is a workaround to the workaround for https://bugreports.qt.io/browse/QTBUG-85498.
+ Since QLabels containing text with long strings that cannot be wrapped have to have a fixed
+ width in order to fit within the scroll list widget, we have to override the normal resizing
+ logic.
+ """
+ new_width = width - self.PREVIEW_WIDTH_DIFFERENCE
+ if self.width() == new_width:
+ return
+
+ self.setFixedWidth(new_width)
+ self.max_length = self.width()
+ self.refresh_preview_text()
+
+
class SourceWidget(QWidget):
"""
Used to display summary information about a source in the list view.
@@ -983,9 +1018,6 @@ class SourceWidget(QWidget):
TOP_MARGIN = 11
BOTTOM_MARGIN = 7
SIDE_MARGIN = 10
- PREVIEW_WIDTH = 360
- PREVIEW_WIDGET_WIDTH = 380
- PREVIEW_WIDGET_HEIGHT = 22
SPACER = 14
BOTTOM_SPACER = 11
STAR_WIDTH = 20
@@ -995,7 +1027,13 @@ class SourceWidget(QWidget):
SOURCE_PREVIEW_CSS = load_css("source_preview.css")
SOURCE_TIMESTAMP_CSS = load_css("source_timestamp.css")
- def __init__(self, controller: Controller, source: Source, source_selected_signal: pyqtSignal):
+ def __init__(
+ self,
+ controller: Controller,
+ source: Source,
+ source_selected_signal: pyqtSignal,
+ adjust_preview: pyqtSignal,
+ ):
super().__init__()
self.controller = controller
@@ -1003,15 +1041,14 @@ def __init__(self, controller: Controller, source: Source, source_selected_signa
self.controller.source_deletion_failed.connect(self._on_source_deletion_failed)
self.controller.authentication_state.connect(self._on_authentication_changed)
source_selected_signal.connect(self._on_source_selected)
+ adjust_preview.connect(self._on_adjust_preview)
- # Store source
self.source = source
self.seen = self.source.seen
self.source_uuid = self.source.uuid
self.last_updated = self.source.last_updated
self.selected = False
- # Set cursor.
self.setCursor(QCursor(Qt.PointingHandCursor))
retain_space = self.sizePolicy()
@@ -1021,14 +1058,10 @@ def __init__(self, controller: Controller, source: Source, source_selected_signa
self.star.setFixedWidth(self.STAR_WIDTH)
self.name = QLabel()
self.name.setObjectName("SourceWidget_name")
- self.preview = SecureQLabel(max_length=self.PREVIEW_WIDTH)
+ self.preview = SourcePreview()
self.preview.setObjectName("SourceWidget_preview")
- self.preview.setFixedSize(QSize(self.PREVIEW_WIDGET_WIDTH, self.PREVIEW_WIDGET_HEIGHT))
self.waiting_delete_confirmation = QLabel("Deletion in progress")
self.waiting_delete_confirmation.setObjectName("SourceWidget_source_deleted")
- self.waiting_delete_confirmation.setFixedSize(
- QSize(self.PREVIEW_WIDGET_WIDTH, self.PREVIEW_WIDGET_HEIGHT)
- )
self.waiting_delete_confirmation.hide()
self.paperclip = SvgLabel("paperclip.svg", QSize(11, 17)) # Set to size provided in the svg
self.paperclip.setObjectName("SourceWidget_paperclip")
@@ -1056,9 +1089,9 @@ def __init__(self, controller: Controller, source: Source, source_selected_signa
source_widget_layout.setSpacing(0)
source_widget_layout.setContentsMargins(0, self.TOP_MARGIN, 0, self.BOTTOM_MARGIN)
source_widget_layout.addWidget(self.star, 0, 0, 1, 1)
- self.spacer_widget = QWidget()
- self.spacer_widget.setFixedWidth(self.SPACER)
- source_widget_layout.addWidget(self.spacer_widget, 0, 1, 1, 1)
+ self.spacer = QWidget()
+ self.spacer.setFixedWidth(self.SPACER)
+ source_widget_layout.addWidget(self.spacer, 0, 1, 1, 1)
source_widget_layout.addWidget(self.name, 0, 2, 1, 1)
source_widget_layout.addWidget(self.paperclip, 0, 3, 1, 1)
source_widget_layout.addWidget(self.preview, 1, 2, 1, 1, alignment=Qt.AlignLeft)
@@ -1076,6 +1109,11 @@ def __init__(self, controller: Controller, source: Source, source_selected_signa
self.update()
+ @pyqtSlot(int)
+ def _on_adjust_preview(self, width):
+ self.setFixedWidth(width)
+ self.preview.adjust_preview(width)
+
def update(self):
"""
Updates the displayed values with the current values from self.source.
@@ -1117,6 +1155,7 @@ def set_snippet(self, source_uuid: str, collection_uuid: str = None, content: st
content = str(last_activity)
self.preview.setText(content)
+ self.preview.adjust_preview(self.width())
def delete_source(self, event):
if self.controller.api is None:
@@ -2886,7 +2925,6 @@ class ConversationScrollArea(QScrollArea):
def __init__(self):
super().__init__()
- self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setWidgetResizable(True)
self.setObjectName("ConversationScrollArea")
diff --git a/securedrop_client/resources/css/sdclient.css b/securedrop_client/resources/css/sdclient.css
index 8bb7ec9c4..667866376 100644
--- a/securedrop_client/resources/css/sdclient.css
+++ b/securedrop_client/resources/css/sdclient.css
@@ -182,7 +182,7 @@ QListView#SourceList {
border: none;
show-decoration-selected: 0;
border-right: 3px solid #f3f5f9;
- min-width: 500px;
+ min-width: 400px;
max-width: 540px;
}
@@ -191,7 +191,7 @@ QListView#SourceList::item:selected {
}
QListView#SourceList::item:hover {
- border: 500px solid #f9f9ff;
+ background-color: #f9f9ff;
}
#SourceWidget_container {
diff --git a/tests/gui/test_widgets.py b/tests/gui/test_widgets.py
index 5cba2d20b..6e684e018 100644
--- a/tests/gui/test_widgets.py
+++ b/tests/gui/test_widgets.py
@@ -9,8 +9,8 @@
import pytest
import sqlalchemy
import sqlalchemy.orm.exc
-from PyQt5.QtCore import QEvent, Qt
-from PyQt5.QtGui import QFocusEvent, QKeyEvent, QMovie
+from PyQt5.QtCore import QEvent, QSize, Qt
+from PyQt5.QtGui import QFocusEvent, QKeyEvent, QMovie, QResizeEvent
from PyQt5.QtTest import QTest
from PyQt5.QtWidgets import QApplication, QLineEdit, QMainWindow, QMessageBox, QVBoxLayout, QWidget
from sqlalchemy.orm import attributes, scoped_session, sessionmaker
@@ -46,6 +46,7 @@
SourceList,
SourceListWidgetItem,
SourceMenu,
+ SourcePreview,
SourceProfileShortWidget,
SourceWidget,
SpeechBubble,
@@ -987,7 +988,9 @@ def test_SourceList_initial_update_does_not_raise_exc_and_no_widget_created(mock
sl.controller = mocker.MagicMock()
mark_seen_signal = mocker.MagicMock()
# Make sure SourceWidget constructor doesn't raise
- source_widget = SourceWidget(sl.controller, factory.Source(), mark_seen_signal)
+ source_widget = SourceWidget(
+ sl.controller, factory.Source(), mark_seen_signal, mocker.MagicMock()
+ )
mocker.patch("securedrop_client.gui.widgets.SourceWidget", return_value=source_widget)
source = DeletedSource()
sl.initial_update([source])
@@ -1196,7 +1199,7 @@ def test_SourceList_set_snippet(mocker):
sl = SourceList()
mark_seen_signal = mocker.MagicMock()
source_widget = SourceWidget(
- mocker.MagicMock(), factory.Source(uuid="mock_uuid"), mark_seen_signal
+ mocker.MagicMock(), factory.Source(uuid="mock_uuid"), mark_seen_signal, mocker.MagicMock()
)
source_widget.set_snippet = mocker.MagicMock()
source_item = SourceListWidgetItem(sl)
@@ -1236,7 +1239,7 @@ def test_SourceList_get_source_widget_if_one_exists_in_cache(mocker):
sl = SourceList()
mark_seen_signal = mocker.MagicMock()
source_widget = SourceWidget(
- mocker.MagicMock(), factory.Source(uuid="mock_uuid"), mark_seen_signal
+ mocker.MagicMock(), factory.Source(uuid="mock_uuid"), mark_seen_signal, mocker.MagicMock()
)
source_item = SourceListWidgetItem(sl)
sl.setItemWidget(source_item, source_widget)
@@ -1290,7 +1293,7 @@ def test_SourceWidget_init_for_seen_source(mocker, session):
session.commit()
- sw = SourceWidget(controller, source, mocker.MagicMock())
+ sw = SourceWidget(controller, source, mocker.MagicMock(), mocker.MagicMock())
assert sw.source == source
assert sw.seen
@@ -1351,7 +1354,7 @@ def test_SourceWidget_init_for_seen_source_with_legacy_data(mocker, session):
session.commit()
- sw = SourceWidget(controller, source, mocker.MagicMock())
+ sw = SourceWidget(controller, source, mocker.MagicMock(), mocker.MagicMock())
assert sw.source == source
assert sw.seen
@@ -1390,7 +1393,7 @@ def test_SourceWidget_init_for_seen_source_legacy_only(mocker, session):
session.commit()
- sw = SourceWidget(controller, source, mocker.MagicMock())
+ sw = SourceWidget(controller, source, mocker.MagicMock(), mocker.MagicMock())
assert sw.source == source
assert sw.source.seen
@@ -1450,7 +1453,7 @@ def test_SourceWidget_init_for_unseen_source(mocker, session):
session.commit()
- sw = SourceWidget(controller, source, mocker.MagicMock())
+ sw = SourceWidget(controller, source, mocker.MagicMock(), mocker.MagicMock())
assert sw.source == source
assert not sw.seen
@@ -1496,7 +1499,7 @@ def test_SourceWidget_init_for_unseen_source_legacy_only(mocker, session):
session.commit()
- sw = SourceWidget(controller, source, mocker.MagicMock())
+ sw = SourceWidget(controller, source, mocker.MagicMock(), mocker.MagicMock())
assert sw.source == source
assert not sw.seen
@@ -1512,7 +1515,7 @@ def test_SourceWidget_html_init(mocker):
mock_source.journalist_designation = "foo bar baz"
mark_seen_signal = mocker.MagicMock()
- sw = SourceWidget(controller, mock_source, mark_seen_signal)
+ sw = SourceWidget(controller, mock_source, mark_seen_signal, mocker.MagicMock())
sw.name = mocker.MagicMock()
sw.summary_layout = mocker.MagicMock()
@@ -1522,11 +1525,43 @@ def test_SourceWidget_html_init(mocker):
sw.name.setText.assert_called_once_with("foo bar baz")
+def test_SourceWidget__on_adjust_preview(mocker):
+ """
+ Ensure width of the source widget is set to the width passed into adjust_preview.
+ """
+ sl = SourceList()
+ sw = SourceWidget(mocker.MagicMock(), factory.Source(), mocker.MagicMock(), mocker.MagicMock())
+ sw.preview = mocker.MagicMock()
+ source_item = SourceListWidgetItem(sl)
+ sl.setItemWidget(source_item, sw)
+
+ sw._on_adjust_preview(100)
+
+ assert sw.width() == 100
+ sw.preview.adjust_preview.assert_called_with(100)
+
+
+def test_SourceList_resizeEvent(mocker):
+ sl = SourceList()
+ sl.adjust_preview = mocker.MagicMock()
+ sl.resizeEvent(QResizeEvent(QSize(100, 100), QSize(100, 100)))
+ sl.adjust_preview.emit.assert_called_once_with(100)
+
+
+def test_SourcePreview_adjust_preview(mocker):
+ preview = SourcePreview()
+ preview.refresh_preview_text = mocker.MagicMock()
+ preview.adjust_preview(400)
+ preview.refresh_preview_text.assert_called_once_with()
+ assert preview.max_length == 400 - preview.PREVIEW_WIDTH_DIFFERENCE
+ assert preview.width() == 400 - preview.PREVIEW_WIDTH_DIFFERENCE
+
+
def test_SourceWidget_update_styles_to_read(mocker):
"""
Ensure styles are updated so that the source widget appears read when seen is True.
"""
- sw = SourceWidget(mocker.MagicMock(), factory.Source(), mocker.MagicMock())
+ sw = SourceWidget(mocker.MagicMock(), factory.Source(), mocker.MagicMock(), mocker.MagicMock())
sw.seen = True
name = mocker.patch.object(sw, "name")
timestamp = mocker.patch.object(sw, "timestamp")
@@ -1544,7 +1579,7 @@ def test_SourceWidget_update_styles_to_read_selected(mocker):
Ensure styles are updated so that the source widget appears read and selected when seen and
selected are True.
"""
- sw = SourceWidget(mocker.MagicMock(), factory.Source(), mocker.MagicMock())
+ sw = SourceWidget(mocker.MagicMock(), factory.Source(), mocker.MagicMock(), mocker.MagicMock())
sw.seen = True
sw.selected = True
name = mocker.patch.object(sw, "name")
@@ -1562,7 +1597,7 @@ def test_test_SourceWidget_update_styles_to_unread(mocker):
"""
Ensure styles are updated so that the source widget appears unread when seen is False.
"""
- sw = SourceWidget(mocker.MagicMock(), factory.Source(), mocker.MagicMock())
+ sw = SourceWidget(mocker.MagicMock(), factory.Source(), mocker.MagicMock(), mocker.MagicMock())
sw.seen = False
name = mocker.patch.object(sw, "name")
timestamp = mocker.patch.object(sw, "timestamp")
@@ -1583,7 +1618,7 @@ def test_SourceWidget__on_authentication_changed(mocker):
* The source widget's seen status remains unchanged when the authentication status changes
to the user being online. (Seen status will be corrected in `SourceWidget.update`.)
"""
- sw = SourceWidget(mocker.MagicMock(), factory.Source(), mocker.MagicMock())
+ sw = SourceWidget(mocker.MagicMock(), factory.Source(), mocker.MagicMock(), mocker.MagicMock())
sw.seen = False
sw.update_styles = mocker.MagicMock()
@@ -1607,7 +1642,7 @@ def test_SourceWidget__on_source_selected(mocker, session):
controller = mocker.MagicMock()
controller.authenticated_user = factory.User(id=1)
source = factory.Source()
- sw = SourceWidget(controller, source, mocker.MagicMock())
+ sw = SourceWidget(controller, source, mocker.MagicMock(), mocker.MagicMock())
sw.seen = False
sw.update_styles = mocker.MagicMock()
@@ -1625,7 +1660,7 @@ def test_SourceWidget__on_source_selected_skips_op_if_uuid_does_not_match(mocker
"""
controller = mocker.MagicMock()
source = factory.Source()
- sw = SourceWidget(controller, source, mocker.MagicMock())
+ sw = SourceWidget(controller, source, mocker.MagicMock(), mocker.MagicMock())
sw.seen = False
sw.update_styles = mocker.MagicMock()
@@ -1639,7 +1674,7 @@ def test_SourceWidget__on_source_selected_skips_op_if_uuid_does_not_match(mocker
def test_SourceWidget__on_source_selected_skips_op_if_already_seen(mocker):
controller = mocker.MagicMock()
source = factory.Source()
- sw = SourceWidget(controller, source, mocker.MagicMock())
+ sw = SourceWidget(controller, source, mocker.MagicMock(), mocker.MagicMock())
sw.seen = True
sw.update_styles = mocker.MagicMock()
@@ -1656,7 +1691,7 @@ def test_SourceWidget_update_attachment_icon(mocker):
controller = mocker.MagicMock()
source = factory.Source(document_count=1)
mark_seen_signal = mocker.MagicMock()
- sw = SourceWidget(controller, source, mark_seen_signal)
+ sw = SourceWidget(controller, source, mark_seen_signal, mocker.MagicMock())
sw.update()
assert not sw.paperclip.isHidden()
@@ -1675,7 +1710,7 @@ def test_SourceWidget_update_does_not_raise_exception(mocker):
controller = mocker.MagicMock()
source = factory.Source(document_count=1)
mark_seen_signal = mocker.MagicMock()
- sw = SourceWidget(controller, source, mark_seen_signal)
+ sw = SourceWidget(controller, source, mark_seen_signal, mocker.MagicMock())
ex = sqlalchemy.exc.InvalidRequestError()
controller.session.refresh.side_effect = ex
mock_logger = mocker.MagicMock()
@@ -1699,7 +1734,7 @@ def test_SourceWidget_set_snippet_draft_only(mocker, session_maker, session, hom
session.add(reply)
session.commit()
- sw = SourceWidget(controller, source, mark_seen_signal)
+ sw = SourceWidget(controller, source, mark_seen_signal, mocker.MagicMock())
sw.set_snippet(source.uuid, reply.uuid, f.filename)
assert sw.preview.text() == "File: " + f.filename
@@ -1717,7 +1752,7 @@ def test_SourceWidget_set_snippet(mocker, session_maker, session, homedir):
session.add(source)
session.commit()
- sw = SourceWidget(controller, source, mark_seen_signal)
+ sw = SourceWidget(controller, source, mark_seen_signal, mocker.MagicMock())
sw.set_snippet(source.uuid, "mock_file_uuid", f.filename)
assert sw.preview.text() == "File: " + f.filename
@@ -1743,7 +1778,7 @@ def test_SourceWidget_update_truncate_latest_msg(mocker):
source.journalist_designation = "Testy McTestface"
source.collection = [factory.Message(content="a" * 151)]
mark_seen_signal = mocker.MagicMock()
- sw = SourceWidget(controller, source, mark_seen_signal)
+ sw = SourceWidget(controller, source, mark_seen_signal, mocker.MagicMock())
sw.update()
assert sw.preview.text().endswith("...")
@@ -1757,7 +1792,7 @@ def test_SourceWidget_delete_source(mocker, session, source):
)
mark_seen_signal = mocker.MagicMock()
- sw = SourceWidget(mock_controller, source["source"], mark_seen_signal)
+ sw = SourceWidget(mock_controller, source["source"], mark_seen_signal, mocker.MagicMock())
mocker.patch("securedrop_client.gui.widgets.DeleteSourceMessageBox", mock_delete_source_message)
@@ -1778,7 +1813,7 @@ def test_SourceWidget_delete_source_when_user_chooses_cancel(mocker, session, so
mock_message_box_question.return_value = QMessageBox.Cancel
mock_controller = mocker.MagicMock()
- sw = SourceWidget(mock_controller, source, mark_seen_signal)
+ sw = SourceWidget(mock_controller, source, mark_seen_signal, mocker.MagicMock())
mocker.patch("securedrop_client.gui.widgets.QMessageBox.question", mock_message_box_question)
sw.delete_source(None)
@@ -1788,7 +1823,7 @@ def test_SourceWidget_delete_source_when_user_chooses_cancel(mocker, session, so
def test_SourceWidget__on_source_deleted(mocker, session, source):
controller = mocker.MagicMock()
mark_seen_signal = mocker.MagicMock()
- sw = SourceWidget(controller, factory.Source(uuid="123"), mark_seen_signal)
+ sw = SourceWidget(controller, factory.Source(uuid="123"), mark_seen_signal, mocker.MagicMock())
sw._on_source_deleted("123")
assert sw.star.isHidden()
assert not sw.name.isHidden()
@@ -1803,7 +1838,7 @@ def test_SourceWidget__on_source_deleted_wrong_uuid(mocker, session, source):
mark_seen_signal = mocker.MagicMock()
source = factory.Source(uuid="123")
source.document_count = mocker.MagicMock(return_value=1)
- sw = SourceWidget(controller, source, mark_seen_signal)
+ sw = SourceWidget(controller, source, mark_seen_signal, mocker.MagicMock())
sw._on_source_deleted("321")
assert not sw.star.isHidden()
assert not sw.name.isHidden()
@@ -1816,7 +1851,7 @@ def test_SourceWidget__on_source_deleted_wrong_uuid(mocker, session, source):
def test_SourceWidget__on_source_deletion_failed(mocker, session, source):
controller = mocker.MagicMock()
mark_seen_signal = mocker.MagicMock()
- sw = SourceWidget(controller, factory.Source(uuid="123"), mark_seen_signal)
+ sw = SourceWidget(controller, factory.Source(uuid="123"), mark_seen_signal, mocker.MagicMock())
sw._on_source_deleted("123")
sw._on_source_deletion_failed("123")
@@ -1832,7 +1867,7 @@ def test_SourceWidget__on_source_deletion_failed(mocker, session, source):
def test_SourceWidget__on_source_deletion_failed_wrong_uuid(mocker, session, source):
controller = mocker.MagicMock()
mark_seen_signal = mocker.MagicMock()
- sw = SourceWidget(controller, factory.Source(uuid="123"), mark_seen_signal)
+ sw = SourceWidget(controller, factory.Source(uuid="123"), mark_seen_signal, mocker.MagicMock())
sw._on_source_deleted("123")
sw._on_source_deletion_failed("321")
@@ -1854,7 +1889,7 @@ def test_SourceWidget_uses_SecureQLabel(mocker):
source.journalist_designation = "Testy McTestface"
source.collection = [factory.Message(content="a" * 121)]
mark_seen_signal = mocker.MagicMock()
- sw = SourceWidget(controller, source, mark_seen_signal)
+ sw = SourceWidget(controller, source, mark_seen_signal, mocker.MagicMock())
sw.update()
assert isinstance(sw.preview, SecureQLabel)
@@ -4709,7 +4744,9 @@ def test_DeleteSource_from_source_widget_when_user_is_loggedout(mocker):
with mocker.patch(
"securedrop_client.gui.widgets.DeleteSourceMessageBox", mock_delete_source_message_box
):
- source_widget = SourceWidget(mock_controller, mock_source, mark_seen_signal)
+ source_widget = SourceWidget(
+ mock_controller, mock_source, mark_seen_signal, mocker.MagicMock()
+ )
source_widget.delete_source(mock_event)
mock_delete_source_message_box_obj.launch.assert_not_called()