Files
cthulhu/tests/test_notification_presenter_mako_regressions.py

284 lines
9.1 KiB
Python

import sys
import unittest
from pathlib import Path
from unittest import mock
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from cthulhu import notification_presenter
from cthulhu import messages
from cthulhu.notification_presenter import NotificationPresenter
class _FakeMonitor:
def __init__(self):
self.dismissed = []
self.invoked = []
self.current_generation = 2
def refresh(self, announce_new=False):
return True
def is_current_entry(self, entry):
return entry.source == "mako" and entry.source_generation == self.current_generation
def dismiss_notification(self, notification_id):
self.dismissed.append(notification_id)
return True
def invoke_action(self, notification_id, action_key):
self.invoked.append((notification_id, action_key))
return True
class _FakeButton:
def __init__(self, label=None):
self.label = label
self.sensitive = True
self._signals = {}
def connect(self, signal_name, callback, *args):
self._signals[signal_name] = (callback, args)
def set_sensitive(self, value):
self.sensitive = value
def set_margin_top(self, _value):
return None
def click(self):
callback, args = self._signals["clicked"]
callback(self, *args)
class _FakeBox:
def __init__(self):
self.children = []
def get_children(self):
return list(self.children)
def remove(self, child):
self.children.remove(child)
def pack_start(self, child, expand, fill, padding):
del expand, fill, padding
self.children.append(child)
def show_all(self):
return None
class _FakeLabel:
def __init__(self):
self.text = ""
self.visible = False
def set_text(self, value):
self.text = value
def set_visible(self, value):
self.visible = value
class NotificationPresenterMakoTests(unittest.TestCase):
def setUp(self):
self.presenter = NotificationPresenter()
self.monitor = _FakeMonitor()
self.presenter.set_mako_monitor(self.monitor)
def test_sync_updates_current_generation_only(self):
old_entry = self.presenter.save_notification(
"Notification old",
source="mako",
source_generation=1,
notification_id=5,
live=False,
actions={"default": "Old"},
)
current_entry = self.presenter.save_notification(
"Notification current",
source="mako",
source_generation=2,
notification_id=6,
live=True,
actions={"default": "View"},
)
self.presenter.sync_live_notifications(
"mako",
{
5: {
"message": "Notification replaced",
"actions": {"default": "New"},
"app_name": "discord",
"summary": "replaced",
"body": "",
"urgency": 1,
"desktop_entry": "discord",
}
},
source_generation=2,
)
self.assertEqual(old_entry.message, "Notification old")
self.assertEqual(old_entry.actions, {"default": "Old"})
self.assertFalse(current_entry.live)
def test_can_control_entry_and_get_actions_require_current_monitor_generation(self):
entry = self.presenter.save_notification(
"Notification current",
source="mako",
source_generation=2,
notification_id=6,
live=True,
actions={"default": "View"},
)
self.assertTrue(self.presenter.can_control_entry(entry))
self.assertEqual(self.presenter.get_actions_for_entry(entry), {"default": "View"})
stale_entry = self.presenter.save_notification(
"Notification stale",
source="mako",
source_generation=1,
notification_id=7,
live=True,
actions={"default": "Open"},
)
self.assertFalse(self.presenter.can_control_entry(stale_entry))
self.assertEqual(self.presenter.get_actions_for_entry(stale_entry), {})
def test_dismiss_and_invoke_action_route_through_monitor(self):
entry = self.presenter.save_notification(
"Notification current",
source="mako",
source_generation=2,
notification_id=6,
live=True,
actions={"default": "View"},
)
script = mock.Mock()
self.assertTrue(self.presenter.dismiss_entry(script, entry))
self.assertEqual(self.monitor.dismissed, [6])
script.presentMessage.assert_called_with(messages.NOTIFICATION_DISMISSED)
script.reset_mock()
self.assertTrue(self.presenter.invoke_action_for_entry(script, entry, "default"))
self.assertEqual(self.monitor.invoked, [(6, "default")])
script.presentMessage.assert_called_with(messages.NOTIFICATION_ACTION_INVOKED)
def test_notification_list_builds_inline_action_buttons_in_reported_order(self):
entry = self.presenter.save_notification(
"Notification current",
source="mako",
source_generation=2,
notification_id=6,
live=True,
actions={"default": "View", "snooze": "Snooze"},
)
gui = notification_presenter.NotificationListGUI.__new__(
notification_presenter.NotificationListGUI
)
gui._script = mock.Mock()
gui._presenter = mock.Mock()
gui._presenter.can_control_entry.return_value = True
gui._presenter.get_actions_for_entry.return_value = {
"default": "View",
"snooze": "Snooze",
}
gui._presenter.invoke_action_for_entry.return_value = True
gui._dismiss_button = _FakeButton()
gui._actions_box = _FakeBox()
gui._actions_status_label = _FakeLabel()
gui._get_selected_entry = mock.Mock(return_value=entry)
with mock.patch.object(notification_presenter.Gtk, "Button", _FakeButton):
gui._update_action_buttons()
self.assertTrue(gui._dismiss_button.sensitive)
self.assertEqual(
[button.label for button in gui._actions_box.children],
["View", "Snooze"],
)
gui._actions_box.children[1].click()
gui._presenter.invoke_action_for_entry.assert_called_once_with(
gui._script,
entry,
"snooze",
)
def test_notification_list_clears_inline_actions_when_notification_is_unavailable(self):
entry = self.presenter.save_notification(
"Notification stale",
source="mako",
source_generation=1,
notification_id=7,
live=True,
actions={"default": "Open"},
)
gui = notification_presenter.NotificationListGUI.__new__(
notification_presenter.NotificationListGUI
)
gui._script = mock.Mock()
gui._presenter = mock.Mock()
gui._presenter.can_control_entry.return_value = False
gui._presenter.get_actions_for_entry.return_value = {}
gui._dismiss_button = _FakeButton()
gui._actions_box = _FakeBox()
gui._actions_box.children.append(_FakeButton("Old"))
gui._actions_status_label = _FakeLabel()
gui._get_selected_entry = mock.Mock(return_value=entry)
with mock.patch.object(notification_presenter.Gtk, "Button", _FakeButton):
gui._update_action_buttons()
self.assertFalse(gui._dismiss_button.sensitive)
self.assertEqual(gui._actions_box.children, [])
self.assertEqual(
gui._actions_status_label.text,
messages.NOTIFICATION_UNAVAILABLE,
)
self.assertTrue(gui._actions_status_label.visible)
def test_notification_list_shows_no_actions_state_for_live_notification_without_actions(self):
entry = self.presenter.save_notification(
"Notification current",
source="mako",
source_generation=2,
notification_id=8,
live=True,
actions={},
)
gui = notification_presenter.NotificationListGUI.__new__(
notification_presenter.NotificationListGUI
)
gui._script = mock.Mock()
gui._presenter = mock.Mock()
gui._presenter.can_control_entry.return_value = True
gui._presenter.get_actions_for_entry.return_value = {}
gui._dismiss_button = _FakeButton()
gui._actions_box = _FakeBox()
gui._actions_status_label = _FakeLabel()
gui._get_selected_entry = mock.Mock(return_value=entry)
with mock.patch.object(notification_presenter.Gtk, "Button", _FakeButton):
gui._update_action_buttons()
self.assertTrue(gui._dismiss_button.sensitive)
self.assertEqual(gui._actions_box.children, [])
self.assertEqual(
gui._actions_status_label.text,
messages.NOTIFICATION_NO_ACTIONS,
)
self.assertTrue(gui._actions_status_label.visible)
if __name__ == "__main__":
unittest.main()