Skip to content

Commit 9f847f7

Browse files
committed
model: Fetch and store custom emojis.
* Fetch custom emojis from server which can be used by the user using typeahead. * Model tests amended. * New fixture and test added for custom emojis. Minor note: The emojis are being sorted twice here, once when unicode emojis are stored in the file using the script ( which happens occasionally when it is manually updated ) and again after custom emojis are fetched ( which happens during every load ). The first sorting seems pointless, at first glance, but it improves sorting time for the second one since sorted() uses Timsort that can perform better if subsequences of the data are already sorted.
1 parent a89c1fa commit 9f847f7

File tree

3 files changed

+66
-5
lines changed

3 files changed

+66
-5
lines changed

tests/conftest.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,15 @@ def streams_fixture():
180180
return deepcopy(streams)
181181

182182

183+
@pytest.fixture
184+
def custom_emojis():
185+
return OrderedDict([
186+
('urwid', {'code': '100', 'type': 'realm_emoji'}),
187+
('dancing', {'code': '3', 'type': 'realm_emoji'}),
188+
('snape', {'code': '20', 'type': 'realm_emoji'}),
189+
])
190+
191+
183192
@pytest.fixture
184193
def unicode_emojis():
185194
return OrderedDict([

tests/model/test_model.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
from collections import OrderedDict
23
from copy import deepcopy
34
from typing import Any
45

@@ -25,7 +26,7 @@ def mock_external_classes(self, mocker: Any) -> None:
2526

2627
@pytest.fixture
2728
def model(self, mocker, initial_data, user_profile,
28-
unicode_emojis):
29+
unicode_emojis, custom_emojis):
2930
mocker.patch('zulipterminal.model.Model.get_messages',
3031
return_value='')
3132
self.client.register.return_value = initial_data
@@ -39,13 +40,15 @@ def model(self, mocker, initial_data, user_profile,
3940
'zulipterminal.model.classify_unread_counts',
4041
return_value=[])
4142
self.client.get_profile.return_value = user_profile
42-
mocker.patch('zulipterminal.model.emoji_data',
43+
mocker.patch('zulipterminal.model.unicode_emojis',
4344
EMOJI_DATA=unicode_emojis)
45+
mocker.patch('zulipterminal.model.Model.fetch_custom_emojis',
46+
return_value=custom_emojis)
4447
model = Model(self.controller)
4548
return model
4649

4750
def test_init(self, model, initial_data, user_profile,
48-
unicode_emojis):
51+
unicode_emojis, custom_emojis):
4952
assert hasattr(model, 'controller')
5053
assert hasattr(model, 'client')
5154
assert model.narrow == []
@@ -75,7 +78,8 @@ def test_init(self, model, initial_data, user_profile,
7578
assert model.unpinned_streams == []
7679
self.classify_unread_counts.assert_called_once_with(model)
7780
assert model.unread_counts == []
78-
assert model.emoji_data == unicode_emojis
81+
assert model.active_emoji_data == OrderedDict(sorted(
82+
{**unicode_emojis, **custom_emojis}.items(), key=lambda e: e[0]))
7983

8084
def test_init_InvalidAPIKey_response(self, mocker, initial_data):
8185
# Both network calls indicate the same response
@@ -1597,3 +1601,39 @@ def test_is_muted_topic(self, topic, is_muted, stream_dict, model):
15971601
]
15981602
assert model.is_muted_topic(stream_id=topic[0],
15991603
topic=topic[1]) == is_muted
1604+
1605+
@pytest.mark.parametrize('response', [
1606+
{
1607+
'emoji': {
1608+
'100': {
1609+
'deactivated': False,
1610+
'id': '100',
1611+
'name': 'urwid',
1612+
},
1613+
'20': {
1614+
'deactivated': False,
1615+
'id': '20',
1616+
'name': 'snape',
1617+
},
1618+
'3': {
1619+
'deactivated': False,
1620+
'id': '3',
1621+
'name': 'dancing',
1622+
},
1623+
'81': { # Not included in custom_emojis because deactivated.
1624+
'deactivated': True,
1625+
'id': '81',
1626+
'name': 'green_tick',
1627+
},
1628+
},
1629+
'msg': '',
1630+
'result': 'success'
1631+
}
1632+
])
1633+
def test_fetch_custom_emojis(self, mocker, model, custom_emojis,
1634+
response):
1635+
self.client.get_realm_emoji.return_value = response
1636+
1637+
fetched_custom_emojis = model.fetch_custom_emojis()
1638+
1639+
assert fetched_custom_emojis == custom_emojis

zulipterminal/model.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,11 @@ def __init__(self, controller: Any) -> None:
126126

127127
self.unread_counts = classify_unread_counts(self)
128128

129-
self.active_emoji_data = unicode_emojis.EMOJI_DATA
129+
custom_emojis = self.fetch_custom_emojis()
130+
all_emojis = (list(unicode_emojis.EMOJI_DATA.items())
131+
+ list(custom_emojis.items()))
132+
self.active_emoji_data = OrderedDict(sorted(all_emojis,
133+
key=lambda e: e[0]))
130134

131135
self.new_user_input = True
132136
self._start_presence_updates()
@@ -344,6 +348,14 @@ def update_stream_message(self, topic: str, msg_id: int,
344348
display_error_if_present(response, self.controller)
345349
return response['result'] == 'success'
346350

351+
def fetch_custom_emojis(self) -> Dict[str, Dict[str, str]]:
352+
response = self.client.get_realm_emoji()
353+
custom_emojis = {emoji['name']: {'code': emoji_code,
354+
'type': 'realm_emoji'}
355+
for emoji_code, emoji in response['emoji'].items()
356+
if not emoji['deactivated']}
357+
return custom_emojis
358+
347359
def get_messages(self, *,
348360
num_after: int, num_before: int,
349361
anchor: Optional[int]) -> str:

0 commit comments

Comments
 (0)