Skip to content

Commit

Permalink
WIP: MainView tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rocodes committed Oct 15, 2024
1 parent 387da47 commit 993dca9
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 53 deletions.
4 changes: 3 additions & 1 deletion client/tests/gui/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ def test_deletes_source_when_dialog_accepted(self):
self.action.trigger()

self._controller.delete_sources.assert_called_once()
assert self._source in self._controller.delete_sources.call_args[0][0], self._controller.delete_sources.call_args[0][0]
assert (
self._source in self._controller.delete_sources.call_args[0][0]
), self._controller.delete_sources.call_args[0][0]

def test_does_not_delete_source_when_dialog_rejected(self):
# Reject the confirmation dialog from a separate thread.
Expand Down
136 changes: 84 additions & 52 deletions client/tests/gui/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
LoginButton,
MainView,
MessageWidget,
MultiSelectView,
NothingSelectedView,
ReplyBoxWidget,
ReplyTextEdit,
ReplyTextEditPlaceholder,
Expand Down Expand Up @@ -533,21 +535,19 @@ def test_MainView_show_sources_with_none_selected(mocker):

# Set up SourceList so that SourceList.get_selected_source() returns a source
mv.source_list = SourceList()
mv.source_list.controller = mocker.MagicMock()
source_widget = SourceWidget(
mocker.MagicMock(), factory.Source(uuid="stub_uuid"), mocker.MagicMock(), mocker.MagicMock()
)
source_item = SourceListWidgetItem(mv.source_list)
mv.source_list.setItemWidget(source_item, source_widget)
mv.source_list.source_items["stub_uuid"] = source_item
mocker.patch.object(mv.source_list, "update_sources")
# mocker.patch.object(mv.source_list, "update_sources")

mv.empty_conversation_view = mocker.MagicMock()

mv.show_sources([1, 2, 3])
mv.show_sources([factory.Source(), factory.Source(), factory.Source()])

mv.source_list.update_sources.assert_called_once_with([1, 2, 3])
mv.empty_conversation_view.show_no_source_selected_message.assert_called_once_with()
mv.empty_conversation_view.show.assert_called_once_with()
# mv.source_list.update_sources.assert_called_once_with([1, 2, 3])
assert mv.view_layout.currentIndex() == mv.NOTHING_SELECTED_INDEX


def test_MainView_show_sources_from_cold_start(mocker):
Expand All @@ -570,32 +570,32 @@ def test_MainView_show_sources_with_no_sources_at_all(mocker):
"""
mv = MainView(None)
mv.source_list = mocker.MagicMock()
mv.empty_conversation_view = mocker.MagicMock()
mv._on_update_conversation_context = mocker.MagicMock(wraps=mv._on_update_conversation_context)

mv.show_sources([])

mv.source_list.update_sources.assert_called_once_with([])
mv.empty_conversation_view.show_no_sources_message.assert_called_once_with()
mv.empty_conversation_view.show.assert_called_once_with()
mv._on_update_conversation_context.assert_called()
assert mv.view_layout.currentIndex() == mv.NO_SOURCES_INDEX


def test_MainView_show_sources_when_sources_are_deleted(mocker):
"""
Ensure that show_sources also deletes the SourceConversationWrapper for a deleted source.
"""
mv = MainView(None)
sources = [factory.Source(), factory.Source(), factory.Source(), factory.Source()]
mv.source_list = mocker.MagicMock()
mv.empty_conversation_view = mocker.MagicMock()
mv.source_list.update_sources = mocker.MagicMock(return_value=[])
mv.delete_conversation = mocker.MagicMock()

mv.show_sources([1, 2, 3, 4])
mv.show_sources(sources)

mv.source_list.update_sources = mocker.MagicMock(return_value=[4])
mv.source_list.update_sources = mocker.MagicMock(return_value=[sources[-1]])

mv.show_sources([1, 2, 3])
mv.show_sources(sources[:-1])

mv.delete_conversation.assert_called_once_with(4)
mv.delete_conversation.assert_called_once_with(sources[-1])


def test_MainView_delete_conversation_when_conv_wrapper_exists(mocker):
Expand Down Expand Up @@ -638,6 +638,8 @@ def test_MainView_on_source_changed(mocker):
source = factory.Source()
mv.source_list = mocker.MagicMock()
mv.source_list.get_selected_source = mocker.MagicMock(return_value=source)
mv.source_list.selectedItems = mocker.MagicMock(return_value=[source])
mv.source_list.count = mocker.MagicMock(return_value=3)
mv.controller = mocker.MagicMock(is_authenticated=True)
mocker.patch("securedrop_client.gui.widgets.source_exists", return_value=True)
# scw = SourceConversationWrapper(source, mv.controller)
Expand All @@ -646,18 +648,19 @@ def test_MainView_on_source_changed(mocker):
mv.on_source_changed()

mv.source_list.get_selected_source.assert_called_once_with()
mv.set_conversation.assert_called_once_with(SourceConversationWrapper(source, mv.controller))
mv.set_conversation.assert_called_once()
assert mv.set_conversation.call_args[0][0].source == source


def test_MainView_on_source_changed_shows_correct_context(mocker):
"""
Ensure correct context presented based on number of sources selected.
"""
# Build sourcelist
sources = []
for i in range(0, 10):
sources = []
for i in range(10):
sources.append(factory.Source())

mock_controller = mocker.MagicMock(spec=logic.Controller)
mock_session = mocker.MagicMock(is_authenticated=True)
mock_controller.session = mock_session
Expand All @@ -668,21 +671,22 @@ def test_MainView_on_source_changed_shows_correct_context(mocker):
mv.show()
mv.show_sources(sources)

assert mv.empty_conversation_view.isVisible()
assert mv.empty_conversation_view.no_source_selected.isVisible()
assert mv.empty_conversation_view.no_sources.isHidden()
assert mv.view_layout.currentIndex() == mv.NOTHING_SELECTED_INDEX

# Select a source, ensure the correct view context is shown
mv.source_list.item(1).setSelected(True)

assert mv.empty_conversation_view.isHidden()
assert mock_controller.session.refresh.call_args[0][0] == mv.source_list.itemWidget(mv.source_list.item(1)).source
assert mv.view_layout.currentItem() == mv.CONVERSATION_INDEX
assert (
mock_controller.session.refresh.call_args[0][0]
== mv.source_list.itemWidget(mv.source_list.item(1)).source
)

# Now ensure the "multiple sources selected" view is shown
mv.source_list.selectAll()

assert mv.empty_conversation_view.isVisible()
assert mv.empty_conversation_view.multi_source_selected.isVisible()
assert mv.view_layout.currentItem() == mv.MULTI_SELECTED_INDEX


def test_MainView_on_source_changed_does_not_raise_InvalidRequestError(mocker):
"""
Expand All @@ -709,18 +713,20 @@ def test_MainView_on_source_changed_does_not_raise_InvalidRequestError(mocker):

assert mock_logger.debug.call_count == 1, mock_logger.debug.call_args


def test_MainView_on_source_changed_when_source_no_longer_exists(mocker):
"""
Test that conversation for a source is cleared when the source no longer exists.
"""
mv = MainView(None)
mv.set_conversation = mocker.MagicMock()
mv.source_list = mocker.MagicMock()
mv.source_list.selectedItems = mocker.MagicMock()
mv.source_list.get_selected_source = mocker.MagicMock(return_value=None)

mv.on_source_changed()

mv.source_list.get_selected_source.assert_called_once_with()
assert mv.view_layout.currentIndex() == mv.NOTHING_SELECTED_INDEX
mv.set_conversation.assert_not_called()


Expand All @@ -729,9 +735,12 @@ def test_MainView_on_source_changed_updates_conversation_view(mocker, session):
Test that the source collection is displayed in the conversation view.
"""
mv = MainView(None)
# mv.source_list = mocker.MagicMock()
mv.controller = mocker.MagicMock(is_authenticated=True)
source = factory.Source()

mv.source_list = mocker.MagicMock()
mv.source_list.count = mocker.MagicMock(return_value=1)
mv.source_list.selectedItems = mocker.MagicMock(return_value=[source])
mv.controller = mocker.MagicMock(is_authenticated=True)
session.add(source)
file = factory.File(source=source, filename="0-mock-doc.gpg")
message = factory.Message(source=source, filename="0-mock-msg.gpg")
Expand Down Expand Up @@ -769,16 +778,20 @@ def test_MainView_on_source_changed_SourceConversationWrapper_is_preserved(mocke
SourceConversationWrapper when we click away from a given source. We should create it the
first time, and then it should persist.
"""
mv = MainView(None)
mv.set_conversation = mocker.MagicMock()
source_selected = mocker.patch("securedrop_client.gui.widgets.SourceList.source_selected")
mv.controller = mocker.MagicMock(is_authenticated=True)
source = factory.Source()
source2 = factory.Source()
session.add(source)
session.add(source2)
session.commit()

mv = MainView(None)
mv.source_list = mocker.MagicMock()
mv.source_list.count = mocker.MagicMock(return_value=2)
mv.source_list.selectedItems = mocker.MagicMock()
mv.set_conversation = mocker.MagicMock()
source_selected = mocker.patch("securedrop_client.gui.widgets.SourceList.source_selected")
mv.controller = mocker.MagicMock(is_authenticated=True)

source_conversation_init = mocker.patch(
"securedrop_client.gui.widgets.SourceConversationWrapper.__init__", return_value=None
)
Expand Down Expand Up @@ -968,31 +981,52 @@ def test_MainView_set_conversation(mocker):
(i.e. that area of the screen on the right hand side).
"""
mv = MainView(None)
mv.view_layout = mocker.MagicMock()

mock_widget = mocker.MagicMock()
mv.set_conversation(mock_widget)
scw = SourceConversationWrapper(factory.Source(), mocker.MagicMock())
mv.set_conversation(scw)

mv.view_layout.takeAt.assert_called_once_with(0)
mv.view_layout.addWidget.assert_called_once_with(mock_widget)
assert mv.view_layout.widget(mv.CONVERSATION_INDEX) == scw


def test_EmptyConversationView_show_no_sources_message(mocker):
ecv = EmptyConversationView()
def test_EmptyConversationView(mocker):
mv = MainView(None)
mv.source_list = mocker.MagicMock()
mv.source_list.count = mocker.MagicMock(return_value=0)
mv.source_list.selectedItems = mocker.MagicMock(return_value=[])
mv.show()
assert mv.view_layout.count() == 4 # Sanity - are all the pages there?
mv.show_sources([])
assert mv.view_layout.currentIndex() == mv.NO_SOURCES_INDEX
assert isinstance(mv.view_layout.widget(mv.view_layout.currentIndex()), EmptyConversationView)

ecv.show_no_sources_message()

assert not ecv.no_sources.isHidden()
assert ecv.no_source_selected.isHidden()
def test_NothingSelectedView(mocker):
mv = MainView(None)
mv.show()
mv.source_list = mocker.MagicMock()
mv.source_list.count = mocker.MagicMock(return_value=4)
mv.source_list.selectedItems = mocker.MagicMock(return_value=[])
assert mv.view_layout.count() == 4 # Sanity - are all the pages there?

mv.show_sources([factory.Source(), factory.Source(), factory.Source()])
assert mv.view_layout.currentIndex() == mv.NOTHING_SELECTED_INDEX
assert isinstance(mv.view_layout.widget(mv.view_layout.currentIndex()), NothingSelectedView)

def test_EmptyConversationView_show_no_source_selected_message(mocker):
ecv = EmptyConversationView()

ecv.show_no_source_selected_message()
def test_MultiSelectedView(mocker):
mv = MainView(None)
sources = [factory.Source(), factory.Source(), factory.Source()]
mv.source_list = mocker.MagicMock()
mv.source_list.count = mocker.MagicMock(return_value=len(sources))
mv.source_list.selectedItems = mocker.MagicMock(return_value=sources[:-1])
mv._on_update_conversation_context = mocker.MagicMock(wraps=mv._on_update_conversation_context)
mv.show_sources(sources)

assert ecv.no_sources.isHidden()
assert not ecv.no_source_selected.isHidden()
mv.show()
assert mv.view_layout.count() == 4 # Sanity - are all the pages there?
mv._on_update_conversation_context.assert_called()
assert mv.view_layout.currentIndex() == mv.MULTI_SELECTED_INDEX
assert isinstance(mv.view_layout.widget(mv.view_layout.currentIndex()), MultiSelectView)


def test_SourceList_get_selected_source(mocker):
Expand Down Expand Up @@ -3809,14 +3843,12 @@ def test_SourceConversationWrapper_on_source_deleted(mocker):
mv.source_list = mocker.MagicMock()
mv.source_list.get_selected_source = mocker.MagicMock(return_value=source)
mv.controller = mocker.MagicMock(is_authenticated=True)
mv.show()

# Detached sourceconversationwrapper, just for unit testing
scw = SourceConversationWrapper(source, mv.controller, None)
mocker.patch("securedrop_client.gui.widgets.SourceConversationWrapper", return_value=scw)
mv.on_source_changed()
scw.on_source_deleted("123")

assert mv.isVisible()
assert scw.isVisible()
assert not scw.conversation_title_bar.isHidden()
assert not scw.reply_box.isHidden()
assert not scw.reply_box.text_edit.isEnabled()
Expand Down

0 comments on commit 993dca9

Please sign in to comment.