Skip to content

Commit

Permalink
updated tests for signals/slots
Browse files Browse the repository at this point in the history
  • Loading branch information
heartsucker committed Jan 9, 2019
1 parent d53c47a commit 889642d
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 115 deletions.
6 changes: 4 additions & 2 deletions tests/gui/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from securedrop_client.gui.main import Window
from securedrop_client.resources import load_icon
from securedrop_client.db import Submission
from uuid import uuid4


app = QApplication([])
Expand Down Expand Up @@ -233,7 +234,8 @@ def test_conversation_pending_message(mocker):
mock_source = mocker.MagicMock()
mock_source.journalistic_designation = 'Testy McTestface'

submission = Submission(source=mock_source, uuid="test", size=123,
msg_uuid = str(uuid4())
submission = Submission(source=mock_source, uuid=msg_uuid, size=123,
filename="test.msg.gpg",
download_url='http://test/test')

Expand All @@ -248,7 +250,7 @@ def test_conversation_pending_message(mocker):
w.show_conversation_for(mock_source)

assert mocked_add_message.call_count == 1
assert mocked_add_message.call_args == mocker.call("<Message not yet downloaded>")
assert mocked_add_message.call_args == mocker.call(msg_uuid, "<Message not yet downloaded>")


def test_set_status(mocker):
Expand Down
127 changes: 87 additions & 40 deletions tests/gui/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,9 +542,33 @@ def test_SpeechBubble_init(mocker):
mock_label = mocker.patch('securedrop_client.gui.widgets.QLabel')
mocker.patch('securedrop_client.gui.widgets.QVBoxLayout')
mocker.patch('securedrop_client.gui.widgets.SpeechBubble.setLayout')
mock_signal = mocker.Mock()
mock_connect = mocker.Mock()
mock_signal.connect = mock_connect

SpeechBubble('hello')
SpeechBubble('mock id', 'hello', mock_signal)
mock_label.assert_called_once_with('hello')
assert mock_connect.called


def test_SpeechBubble_update_text(mocker):
"""
Check that the calling the slot updates the text.
"""
mocker.patch('securedrop_client.gui.widgets.QVBoxLayout')
mocker.patch('securedrop_client.gui.widgets.SpeechBubble.setLayout')
mock_signal = mocker.MagicMock()

msg_id = 'abc123'
sb = SpeechBubble(msg_id, 'hello', mock_signal)

new_msg = 'new message'
sb._update_text(msg_id, new_msg)
assert sb.message.text() == new_msg

newer_msg = 'an even newer message'
sb._update_text(msg_id + 'xxxxx', newer_msg)
assert sb.message.text() == new_msg


def test_SpeechBubble_html_init(mocker):
Expand All @@ -555,8 +579,9 @@ def test_SpeechBubble_html_init(mocker):
mock_label = mocker.patch('securedrop_client.gui.widgets.QLabel')
mocker.patch('securedrop_client.gui.widgets.QVBoxLayout')
mocker.patch('securedrop_client.gui.widgets.SpeechBubble.setLayout')
mock_signal = mocker.MagicMock()

SpeechBubble('<b>hello</b>')
SpeechBubble('mock id', '<b>hello</b>', mock_signal)
mock_label.assert_called_once_with('&lt;b&gt;hello&lt;/b&gt;')


Expand All @@ -565,48 +590,73 @@ def test_SpeechBubble_with_apostrophe_in_text(mocker):
mock_label = mocker.patch('securedrop_client.gui.widgets.QLabel')
mocker.patch('securedrop_client.gui.widgets.QVBoxLayout')
mocker.patch('securedrop_client.gui.widgets.SpeechBubble.setLayout')
mock_signal = mocker.MagicMock()

message = "I'm sure, you are reading my message."
SpeechBubble(message)
SpeechBubble('mock id', message, mock_signal)
mock_label.assert_called_once_with(message)


def test_ConversationWidget_init_left():
def test_ConversationWidget_init_left(mocker):
"""
Check the ConversationWidget is configured correctly for align-left.
"""
cw = ConversationWidget('hello', align='left')
mock_signal = mocker.Mock()
mock_connect = mocker.Mock()
mock_signal.connect = mock_connect

cw = ConversationWidget('mock id', 'hello', mock_signal, align='left')
layout = cw.layout()

assert isinstance(layout.takeAt(0), QWidgetItem)
assert isinstance(layout.takeAt(0), QSpacerItem)
assert mock_connect.called


def test_ConversationWidget_init_right():
def test_ConversationWidget_init_right(mocker):
"""
Check the ConversationWidget is configured correctly for align-left.
"""
cw = ConversationWidget('hello', align='right')
mock_signal = mocker.Mock()
mock_connect = mocker.Mock()
mock_signal.connect = mock_connect

cw = ConversationWidget('mock id', 'hello', mock_signal, align='right')
layout = cw.layout()

assert isinstance(layout.takeAt(0), QSpacerItem)
assert isinstance(layout.takeAt(0), QWidgetItem)
assert mock_connect.called


def test_MessageWidget_init():
def test_MessageWidget_init(mocker):
"""
Check the CSS is set as expected.
"""
mw = MessageWidget('hello')
mock_signal = mocker.Mock()
mock_connected = mocker.Mock()
mock_signal.connect = mock_connected

mw = MessageWidget('mock id', 'hello', mock_signal)
ss = mw.styleSheet()

assert 'background-color' in ss
assert mock_connected.called


def test_ReplyWidget_init():
def test_ReplyWidget_init(mocker):
"""
Check the CSS is set as expected.
"""
rw = ReplyWidget('hello')
mock_signal = mocker.Mock()
mock_connected = mocker.Mock()
mock_signal.connect = mock_connected

rw = ReplyWidget('mock id', 'hello', mock_signal)
ss = rw.styleSheet()

assert 'background-color' in ss
assert mock_connected.called


def test_FileWidget_init_left(mocker):
Expand Down Expand Up @@ -680,78 +730,74 @@ def test_FileWidget_mousePressEvent_open(mocker):
fw.controller.on_file_open.assert_called_once_with(submission)


def test_ConversationView_init(mocker):
def test_ConversationView_init(mocker, homedir):
"""
Ensure the conversation view has a layout to add widgets to.
"""
mocked_source = mocker.MagicMock()
cv = ConversationView(mocked_source)
mocked_controller = mocker.MagicMock()
cv = ConversationView(mocked_source, homedir, mocked_controller)
assert isinstance(cv.conversation_layout, QVBoxLayout)


def test_ConversationView_setup(mocker):
"""
Ensure the controller is set
"""
mocked_source = mocker.MagicMock()
cv = ConversationView(mocked_source)
mock_controller = mocker.MagicMock()
cv.setup(mock_controller)
assert cv.controller == mock_controller


def test_ConversationView_move_to_bottom(mocker):
def test_ConversationView_move_to_bottom(mocker, homedir):
"""
Check the signal handler sets the correct value for the scrollbar to be
the maximum possible value.
"""
mocked_source = mocker.MagicMock()
cv = ConversationView(mocked_source)
mocked_controller = mocker.MagicMock()

cv = ConversationView(mocked_source, homedir, mocked_controller)

cv.scroll = mocker.MagicMock()
cv.move_to_bottom(0, 6789)
cv.scroll.verticalScrollBar().setValue.assert_called_once_with(6789)


def test_ConversationView_add_message(mocker):
def test_ConversationView_add_message(mocker, homedir):
"""
Adding a message results in a new MessageWidget added to the layout.
"""
mocked_source = mocker.MagicMock()
cv = ConversationView(mocked_source)
cv.controller = mocker.MagicMock()
mocked_controller = mocker.MagicMock()

cv = ConversationView(mocked_source, homedir, mocked_controller)
cv.conversation_layout = mocker.MagicMock()

cv.add_message('hello')
cv.add_message('mock id', 'hello')
assert cv.conversation_layout.addWidget.call_count == 1

cal = cv.conversation_layout.addWidget.call_args_list
assert isinstance(cal[0][0][0], MessageWidget)


def test_ConversationView_add_reply(mocker):
def test_ConversationView_add_reply(mocker, homedir):
"""
Adding a reply results in a new ReplyWidget added to the layout.
"""
mocked_source = mocker.MagicMock()
cv = ConversationView(mocked_source)
cv.controller = mocker.MagicMock()
mocked_controller = mocker.MagicMock()

cv = ConversationView(mocked_source, homedir, mocked_controller)
cv.conversation_layout = mocker.MagicMock()

cv.add_reply('hello')
cv.add_reply('mock id', 'hello')
assert cv.conversation_layout.addWidget.call_count == 1

cal = cv.conversation_layout.addWidget.call_args_list
assert isinstance(cal[0][0][0], ReplyWidget)


def test_ConversationView_add_downloaded_file(mocker):
def test_ConversationView_add_downloaded_file(mocker, homedir):
"""
Adding a file results in a new FileWidget added to the layout with the
proper QLabel.
"""
mocked_source = mocker.MagicMock()
cv = ConversationView(mocked_source)
cv.controller = mocker.MagicMock()
mocked_controller = mocker.MagicMock()

cv = ConversationView(mocked_source, homedir, mocked_controller)
cv.conversation_layout = mocker.MagicMock()

mock_source = mocker.MagicMock()
Expand All @@ -769,14 +815,15 @@ def test_ConversationView_add_downloaded_file(mocker):
assert isinstance(cal[0][0][0], FileWidget)


def test_ConversationView_add_not_downloaded_file(mocker):
def test_ConversationView_add_not_downloaded_file(mocker, homedir):
"""
Adding a file results in a new FileWidget added to the layout with the
proper QLabel.
"""
mocked_source = mocker.MagicMock()
cv = ConversationView(mocked_source)
cv.controller = mocker.MagicMock()
mocked_controller = mocker.MagicMock()

cv = ConversationView(mocked_source, homedir, mocked_controller)
cv.conversation_layout = mocker.MagicMock()

mock_source = mocker.MagicMock()
Expand Down
6 changes: 4 additions & 2 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ def fake_known_args():
mock_start_app.assert_called_once_with(mock_args, mock_qt_args)


def test_signal_interception(mocker):
def test_signal_interception(mocker, homedir):
# check that initializing an app calls configure_signal_handlers
mocker.patch('securedrop_client.app.QApplication')
mocker.patch('securedrop_client.app.prevent_second_instance')
Expand All @@ -245,8 +245,10 @@ def test_signal_interception(mocker):
mocker.patch('securedrop_client.logic.GpgHelper')
mocker.patch('securedrop_client.app.configure_logging')
mock_signal_handlers = mocker.patch('securedrop_client.app.configure_signal_handlers')
mock_args = mocker.Mock()
mock_args.sdc_home = homedir

start_app(mocker.MagicMock(), [])
start_app(mock_args, [])
assert mock_signal_handlers.called

# check that a signal interception calls quit on the app
Expand Down
56 changes: 15 additions & 41 deletions tests/test_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -680,59 +680,29 @@ def test_Client_update_sources(homedir, config, mocker):
mock_gui.show_sources.assert_called_once_with(source_list)


def test_Client_update_conversation_view_current_source(homedir, config, mocker):
def test_Client_update_conversation_views(homedir, config, mocker):
"""
Ensure the UI displays the latest version of the messages/replies that
have been downloaded/decrypted in the current conversation view.
Using the `config` fixture to ensure the config is written to disk.
"""
mock_gui = mocker.MagicMock()
mock_gui.current_source = 'teehee'
mock_gui.show_conversation_for = mocker.MagicMock()
mock_gui = mocker.Mock()
mock_conversation = mocker.MagicMock()
mock_update_conversation = mocker.MagicMock()
mock_conversation.update_conversation = mock_update_conversation
mock_gui.conversations = {'foo': mock_conversation}
mock_session = mocker.MagicMock()

# Since we use the set-like behavior of self.session
# to check if the source is still persistent, let's mock that here
mock_session.__contains__ = mocker.MagicMock()
mock_session.__contains__.return_value = [mock_gui.current_source]
mock_session.__contains__ = mocker.Mock()
mock_session.__contains__.return_value = True

mock_session.refresh = mocker.MagicMock()
cl = Client('http://localhost', mock_gui, mock_session, homedir)
cl.update_conversation_view()
mock_session.refresh.assert_called_with(mock_gui.current_source)
mock_gui.show_conversation_for.assert_called_once_with(
mock_gui.current_source)


def test_Client_update_conversation_deleted_source(homedir, config, mocker):
"""
Ensure the UI does not attempt to refresh and display a deleted
source.
Using the `config` fixture to ensure the config is written to disk.
"""
mock_gui = mocker.MagicMock()
mock_gui.current_source = 'teehee'
mock_gui.show_conversation_for = mocker.MagicMock()
mock_session = mocker.MagicMock()
mock_session.refresh = mocker.MagicMock()
cl = Client('http://localhost', mock_gui, mock_session, homedir)
cl.update_conversation_view()
mock_session.refresh.assert_not_called()
mock_gui.show_conversation_for.assert_not_called()


def test_Client_update_conversation_view_no_current_source(homedir, config, mocker):
"""
Ensure that if there is no current source (i.e. the user has not clicked
a source in the sidebar), the UI will not redraw the conversation view.
Using the `config` fixture to ensure the config is written to disk.
"""
mock_gui = mocker.MagicMock()
mock_gui.current_source = None
mock_session = mocker.MagicMock()
cl = Client('http://localhost', mock_gui, mock_session, homedir)
cl.update_conversation_view()
mock_gui.show_conversation_for.assert_not_called()
cl.update_conversation_views()
assert mock_session.refresh.called
assert mock_update_conversation.called


def test_Client_unstars_a_source_if_starred(homedir, config, mocker):
Expand All @@ -743,18 +713,22 @@ def test_Client_unstars_a_source_if_starred(homedir, config, mocker):
mock_gui = mocker.MagicMock()
mock_session = mocker.MagicMock()
cl = Client('http://localhost', mock_gui, mock_session, homedir)

source_db_object = mocker.MagicMock()
source_db_object.uuid = mocker.MagicMock()
source_db_object.is_starred = True

cl.call_api = mocker.MagicMock()
cl.api = mocker.MagicMock()
cl.api.remove_star = mocker.MagicMock()
cl.on_update_star_complete = mocker.MagicMock()
cl.on_sidebar_action_timeout = mocker.MagicMock()

source_sdk_object = mocker.MagicMock()
mock_source = mocker.patch('sdclientapi.Source')
mock_source.return_value = source_sdk_object
cl.update_star(source_db_object)

cl.call_api.assert_called_once_with(
cl.api.remove_star, cl.on_update_star_complete,
cl.on_sidebar_action_timeout, source_sdk_object)
Expand Down
Loading

0 comments on commit 889642d

Please sign in to comment.