Skip to content

Commit

Permalink
tests: boxes: Add tests for saving drafts.
Browse files Browse the repository at this point in the history
Test functions added:
- 2 test functions for stream compositions
  - when a draft previously exists, call the confirmation popup
  - when new draft matches saved draft, do not save again
- 3 test functions for private compositions
  - valid recipients,
  - invalid recipients,
  - new draft matches already saved draft

Fixtures added for:
- list saved drafts
- list stream compositions
- list private compositions
- setup for private draft tests
- setup for stream draft tests

Factories added:
- composition factory (generates both stream and private compositions)
- saved draft factory
  • Loading branch information
Niloth-p committed Jun 30, 2024
1 parent e5e9010 commit 696eba6
Showing 1 changed file with 255 additions and 1 deletion.
256 changes: 255 additions & 1 deletion tests/ui_tools/test_boxes.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import datetime
from collections import OrderedDict
from typing import Any, Callable, Dict, List, Optional
from functools import reduce
from typing import Any, Callable, Dict, List, Optional, Tuple, Union

import pytest
from pytest import FixtureRequest
from pytest import param as case
from pytest_mock import MockerFixture
from urwid import Widget
Expand All @@ -11,6 +13,9 @@
TYPING_STARTED_EXPIRY_PERIOD,
TYPING_STARTED_WAIT_PERIOD,
TYPING_STOPPED_WAIT_PERIOD,
Composition,
PrivateComposition,
StreamComposition,
)
from zulipterminal.config.keys import (
keys_for_command,
Expand All @@ -33,6 +38,37 @@
WRITEBOX = MODULE + ".WriteBox"


def composition_factory(
type: str,
to: Union[str, List[int]] = "",
subject: Optional[str] = None,
read_by_sender: bool = True,
) -> Dict[str, object]:
data = {
"type": type,
"content": f"Random {type} message",
"read_by_sender": read_by_sender,
"to": to,
}
if subject:
data["subject"] = subject
return data


def saved_draft_factory(
draft_composition: Optional[Composition] = None,
) -> Dict[str, Union[Optional[Composition], str]]:
expected_function = (
"view.controller.save_draft_confirmation_popup"
if draft_composition is not None
else "model.save_draft"
)
return {
"draft_composition": draft_composition,
"expected_function": expected_function,
}


class TestWriteBox:
@pytest.fixture(autouse=True)
def mock_external_classes(
Expand Down Expand Up @@ -103,6 +139,224 @@ def test_not_calling_typing_method_without_recipients(

assert not write_box.model.send_typing_status_by_user_ids.called

@pytest.fixture(
params=[
saved_draft_factory(),
saved_draft_factory(
StreamComposition(
**composition_factory( # type: ignore[typeddict-item]
type="stream", to="Current stream", subject="Topic"
)
)
),
saved_draft_factory(
PrivateComposition(
**composition_factory( # type: ignore[typeddict-item]
type="private", to=[5179]
)
)
),
saved_draft_factory(
PrivateComposition(
**composition_factory( # type: ignore[typeddict-item]
type="group", to=[5140, 5179]
)
)
),
],
ids=[
"no_saved_draft_exists",
"saved_stream_draft_exists",
"saved_private_draft_exists",
"saved_private_group_draft_exists",
],
)
def saved_draft(
self, request: FixtureRequest
) -> Dict[str, Union[Optional[Composition], str]]:
return request.param

@pytest.fixture(
params=[
composition_factory("private", to=[5140]),
composition_factory("private", to=[5140, 5180]),
],
ids=["private_draft", "group_draft"],
)
def private_draft_composition(
self, request: FixtureRequest
) -> Tuple[List[Composition], List[str]]:
return request.param

@pytest.fixture(
params=[composition_factory("stream", to="Another stream", subject="Topic")],
)
def stream_draft_composition(
self, request: FixtureRequest
) -> Tuple[List[Composition], List[str]]:
return request.param

@pytest.fixture
def private_draft_setup_fixture(
self,
mocker: MockerFixture,
private_draft_composition: PrivateComposition,
write_box: WriteBox,
) -> Tuple[MockerFixture, WriteBox]:
mocker.patch(MODULE + ".WriteBox.update_recipients")
write_box.msg_write_box = mocker.Mock(
edit_text=private_draft_composition["content"]
)
write_box.to_write_box = mocker.Mock()
write_box.compose_box_status = "open_with_private"
write_box.recipient_user_ids = private_draft_composition["to"]
return mocker, write_box

@pytest.fixture
def stream_draft_setup_fixture(
self,
mocker: MockerFixture,
stream_draft_composition: StreamComposition,
write_box: WriteBox,
) -> Tuple[MockerFixture, WriteBox]:
mocker.patch(MODULE + ".WriteBox.update_recipients")
write_box.msg_write_box = mocker.Mock(
edit_text=stream_draft_composition["content"]
)
write_box.stream_write_box = mocker.Mock(
edit_text=stream_draft_composition["to"]
)
write_box.title_write_box = mocker.Mock(
edit_text=stream_draft_composition["subject"]
)
write_box.compose_box_status = "open_with_stream"
write_box.stream_id = 1
return mocker, write_box

@pytest.mark.parametrize("key", keys_for_command("SAVE_AS_DRAFT"))
def test_keypress_SAVE_AS_DRAFT_stream(
self,
key: str,
saved_draft: Dict[str, Union[Optional[Composition], str]],
stream_draft_composition: StreamComposition,
stream_draft_setup_fixture: Tuple[MockerFixture, WriteBox],
write_box: WriteBox,
widget_size: Callable[[Widget], urwid_Size],
) -> None:
draft_saved_in_current_session = saved_draft["draft_composition"]
_, write_box = stream_draft_setup_fixture
assert isinstance(saved_draft["expected_function"], str)
expected_function = reduce(
getattr, saved_draft["expected_function"].split("."), write_box
)
write_box.model.session_draft_message.return_value = (
draft_saved_in_current_session
)

size = widget_size(write_box)
write_box.keypress(size, key)

write_box.model.session_draft_message.assert_called()
expected_function.assert_called_once_with(stream_draft_composition)

@pytest.mark.parametrize("key", keys_for_command("SAVE_AS_DRAFT"))
def test_keypress_SAVE_AS_DRAFT_private__valid_recipients(
self,
key: str,
mocker: MockerFixture,
private_draft_composition: PrivateComposition,
saved_draft: Dict[str, Union[Optional[Composition], str]],
private_draft_setup_fixture: Tuple[MockerFixture, WriteBox],
write_box: WriteBox,
widget_size: Callable[[Widget], urwid_Size],
) -> None:
draft_saved_in_current_session = saved_draft["draft_composition"]
mocker, write_box = private_draft_setup_fixture
assert isinstance(saved_draft["expected_function"], str)
expected_function = reduce(
getattr, saved_draft["expected_function"].split("."), write_box
)
mocker.patch(
MODULE + ".WriteBox._tidy_valid_recipients_and_notify_invalid_ones",
return_value=True,
)
write_box.model.session_draft_message.return_value = (
draft_saved_in_current_session
)

size = widget_size(write_box)
write_box.keypress(size, key)

write_box.model.session_draft_message.assert_called()
expected_function.assert_called_once_with(private_draft_composition)

@pytest.mark.parametrize("key", keys_for_command("SAVE_AS_DRAFT"))
def test_keypress_SAVE_AS_DRAFT_private__invalid_recipients(
self,
key: str,
mocker: MockerFixture,
saved_draft: Dict[str, Union[Optional[Composition], str]],
private_draft_setup_fixture: Tuple[MockerFixture, WriteBox],
write_box: WriteBox,
widget_size: Callable[[Widget], urwid_Size],
) -> None:
draft_saved_in_current_session = saved_draft["draft_composition"]
mocker, write_box = private_draft_setup_fixture
mocker.patch(
MODULE + ".WriteBox._tidy_valid_recipients_and_notify_invalid_ones",
return_value=False,
)
write_box.model.session_draft_message.return_value = (
draft_saved_in_current_session
)

size = widget_size(write_box)
write_box.keypress(size, key)

write_box.model.save_draft.assert_not_called()
write_box.view.controller.save_draft_confirmation_popup.assert_not_called()

@pytest.mark.parametrize("key", keys_for_command("SAVE_AS_DRAFT"))
def test_keypress_SAVE_AS_DRAFT_stream__already_saved(
self,
key: str,
stream_draft_composition: StreamComposition,
stream_draft_setup_fixture: Tuple[MockerFixture, WriteBox],
write_box: WriteBox,
widget_size: Callable[[Widget], urwid_Size],
) -> None:
write_box.model.session_draft_message.return_value = stream_draft_composition
_, write_box = stream_draft_setup_fixture

size = widget_size(write_box)
write_box.keypress(size, key)

write_box.model.session_draft_message.assert_called()
write_box.view.controller.save_draft_confirmation_popup.assert_not_called()

@pytest.mark.parametrize("key", keys_for_command("SAVE_AS_DRAFT"))
def test_keypress_SAVE_AS_DRAFT_private__same_as_saved_draft(
self,
key: str,
mocker: MockerFixture,
private_draft_composition: PrivateComposition,
private_draft_setup_fixture: Tuple[MockerFixture, WriteBox],
write_box: WriteBox,
widget_size: Callable[[Widget], urwid_Size],
) -> None:
mocker, write_box = private_draft_setup_fixture
mocker.patch(
MODULE + ".WriteBox._tidy_valid_recipients_and_notify_invalid_ones",
return_value=True,
)
write_box.model.session_draft_message.return_value = private_draft_composition

size = widget_size(write_box)
write_box.keypress(size, key)

write_box.model.session_draft_message.assert_called()
write_box.view.controller.save_draft_confirmation_popup.assert_not_called()

@pytest.mark.parametrize(
"text, state, is_valid_stream, required_typeahead",
[
Expand Down

0 comments on commit 696eba6

Please sign in to comment.