Preview text shorter when starting offline mode
# STR

1. Start the client in offline mode so that you can see all the preview text for existing sources in the source list
2. Now sign in
3. Now sign out

# Expected 

For the preview text in step 1 (when you log in via offline mode) to be the same length as the preview text in step 2 and step 3.

# Actual

The preview text in step 1 is shorter than the preview text in steps 2 and 3.
diff --git a/securedrop_client/gui/__init__.py b/securedrop_client/gui/__init__.py
index 3fd4c9d..77f7f60 100644
--- a/securedrop_client/gui/__init__.py
+++ b/securedrop_client/gui/__init__.py
@@ -148,6 +148,9 @@ class SvgLabel(QLabel):
 
 
 class SecureQLabel(QLabel):
+
+    MAX_PREVIEW_LENGTH = 200
+
     def __init__(
         self,
         text: str = "",
@@ -168,6 +171,7 @@ class SecureQLabel(QLabel):
     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 @@ class SecureQLabel(QLabel):
             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 51aca64..b973eb3 100644
--- a/securedrop_client/gui/widgets.py
+++ b/securedrop_client/gui/widgets.py
@@ -596,8 +596,8 @@ class MainView(QWidget):
         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 @@ class SourceList(QListWidget):
         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 @@ class SourceList(QListWidget):
         # 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 @@ class SourceList(QListWidget):
 
         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 @@ class SourceList(QListWidget):
             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 @@ class SourceList(QListWidget):
             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 @@ class SourceWidget(QWidget):
         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 @@ class SourceWidget(QWidget):
         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 @@ class SourceWidget(QWidget):
         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 @@ class SourceWidget(QWidget):
 
         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 @@ class SourceWidget(QWidget):
             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 8bb7ec9..6678663 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 5cba2d2..6e684e0 100644
--- a/tests/gui/test_widgets.py
+++ b/tests/gui/test_widgets.py
@@ -9,8 +9,8 @@ import arrow
 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 @@ from securedrop_client.gui.widgets import (
     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 <b>bar</b> 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 <b>bar</b> 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()
 
['tests/gui/test_widgets.py', 'securedrop_client/gui/widgets.py', 'securedrop_client/gui/__init__.py', 'securedrop_client/resources/css/sdclient.css']