Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 27 additions & 43 deletions dojo_toolkit/notifier.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,38 @@
import asyncio
import os
from unittest import mock
from pathlib import Path

from dojo_toolkit.settings import ASSETS_DIR

# workaround to tests run on travis
try:
import gi

gi.require_version("Notify", "0.7")
from gi.repository import GdkPixbuf, Notify
except ImportError:
Notify = mock.Mock()
GdkPixbuf = mock.Mock()


class BaseNotifier:
def __init__(self):
self.fail_img_path = os.path.join(ASSETS_DIR, "r.jpg")
self.success_img_path = os.path.join(ASSETS_DIR, "g.jpg")
import desktop_notifier

def get_notifier(self):
raise NotImplementedError()

def notify(self, message, image=None):
raise NotImplementedError()
from dojo_toolkit.settings import ASSETS_DIR


class GnomeNotifier(BaseNotifier):
class NotifierClient:
def __init__(self):
super().__init__()

self.fail_img = GdkPixbuf.Pixbuf.new_from_file(self.fail_img_path)
self.success_img = GdkPixbuf.Pixbuf.new_from_file(self.success_img_path)

self._notifier = self.get_notifier()

def get_notifier(self):
Notify.init("not")
return Notify.Notification.new("", "", "")

def notify(self, message, image_path="", timeout=5 * 60 * 1000):
self._notifier.update(message, "", image_path)
self._notifier.set_timeout(timeout)
self._notifier.show()
self.notifier = desktop_notifier.DesktopNotifier(app_name="dojo toolkit")
self.fail_img_path = os.path.join(ASSETS_DIR, "./assets/failure_notification_image.jpg")
self.success_img_path = os.path.join(ASSETS_DIR, "./assets/success_notification_image.jpg")

def notify(self, message, title="", image_path="", timeout=5 * 60 * 1000):
icon_path = Path(image_path).resolve()
icon = desktop_notifier.Icon(path=icon_path)

asyncio.create_task(
self.notifier.send(
icon=icon,
title=title,
message=message,
urgency=desktop_notifier.Urgency.Low,
sound=desktop_notifier.DEFAULT_SOUND,
timeout=timeout,
)
)

def success(self, message):
self.notify(message, self.success_img_path)
self.notify(message=message, image_path=self.success_img_path)

def fail(self, message):
self.notify(message, self.fail_img_path)
def failure(self, message):
self.notify(message=message, image_path=self.fail_img_path)


notifier = GnomeNotifier()
notifier = NotifierClient()
2 changes: 1 addition & 1 deletion dojo_toolkit/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def _handle_success(self):

def _handle_failure(self):
print(getattr(colored, "red")("\nTests failed!\n"))
notifier.fail("NOT OK TO TALK")
notifier.failure("NOT OK TO TALK")


class DoctestTestRunner(LocalTestRunner):
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ dependencies = [
"pyglet>=1.5.21,<2.0.0",
"clint>=0.5.1,<0.6.0",
"typer[all]>=0.9.0,<1.0.0",
"pgi>=0.0.11; sys_platform == 'linux'"
"pgi>=0.0.11; sys_platform == 'linux'",
"desktop-notifier>=6.2.0",
]

[dependency-groups]
Expand Down
130 changes: 71 additions & 59 deletions tests/test_notifier.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,73 @@
from unittest import mock

import pytest

from dojo_toolkit.notifier import BaseNotifier, GnomeNotifier


@pytest.fixture
def gnome_notifier():
return GnomeNotifier()


def test_base_notifier():
notifier = BaseNotifier()
assert notifier.fail_img_path
assert notifier.success_img_path
with pytest.raises(NotImplementedError):
notifier.notify("foo")
with pytest.raises(NotImplementedError):
notifier.get_notifier()


@mock.patch("dojo_toolkit.notifier.Notify")
@mock.patch("dojo_toolkit.notifier.GdkPixbuf")
def test_gnome_notifier(pixbuf, notify):
gnome_notifier = GnomeNotifier()

calls = [mock.call(gnome_notifier.fail_img_path), mock.call(gnome_notifier.success_img_path)]
pixbuf.Pixbuf.new_from_file.assert_has_calls(calls)


@mock.patch("dojo_toolkit.notifier.Notify")
def test_gnome_notifier_get_notifier(notify, gnome_notifier):
assert gnome_notifier.get_notifier()

notify.init.assert_called_once_with("not")
notify.Notification.new.assert_called_once_with("", "", "")


@mock.patch("dojo_toolkit.notifier.GnomeNotifier.get_notifier")
def test_gnome_notifier_notify(get_notifier, gnome_notifier):
gnome_notifier = GnomeNotifier()

gnome_notifier.notify("message", "foo.png", 10)

_notifier = get_notifier.return_value
_notifier.update.assert_called_with("message", "", "foo.png")
_notifier.set_timeout.assert_called_with(10)
assert _notifier.show.called


@mock.patch("dojo_toolkit.notifier.GnomeNotifier.notify")
def test_gnome_notifier_success(notify, gnome_notifier):
gnome_notifier.success("message")
notify.assert_called_with("message", gnome_notifier.success_img_path)


@mock.patch("dojo_toolkit.notifier.GnomeNotifier.notify")
def test_gnome_notifier_fail(notify, gnome_notifier):
gnome_notifier.fail("message")
notify.assert_called_with("message", gnome_notifier.fail_img_path)
from dojo_toolkit.notifier import NotifierClient
from dojo_toolkit.settings import ASSETS_DIR


@mock.patch("dojo_toolkit.notifier.desktop_notifier.DesktopNotifier")
def test_init(mock_desktop_notifier):
"""Test NotifierClient initialization."""
notifier_client = NotifierClient()
assert notifier_client.notifier == mock_desktop_notifier.return_value
assert notifier_client.fail_img_path == f"{ASSETS_DIR}/./assets/failure_notification_image.jpg"
assert (
notifier_client.success_img_path == f"{ASSETS_DIR}/./assets/success_notification_image.jpg"
)
mock_desktop_notifier.assert_called_once_with(app_name="dojo toolkit")


@mock.patch("dojo_toolkit.notifier.asyncio.create_task")
@mock.patch("dojo_toolkit.notifier.desktop_notifier.Icon")
@mock.patch("dojo_toolkit.notifier.desktop_notifier.DesktopNotifier")
def test_notify(mock_desktop_notifier_cls, mock_icon_cls, mock_create_task):
"""Test that notify sends correct notification via DesktopNotifier."""
mock_notifier_instance = mock_desktop_notifier_cls.return_value
mock_icon_instance = mock_icon_cls.return_value

client = NotifierClient()
client.notify(
message="Test Message",
title="Test Title",
image_path="/some/path/to/image.jpg",
timeout=10000,
)

# Icon class should be called with correct path
mock_icon_cls.assert_called_once()
assert str(mock_icon_cls.call_args[1]["path"]).endswith("image.jpg")

# DesktopNotifier.send should be wrapped in create_task
mock_notifier_instance.send.assert_called_once_with(
icon=mock_icon_instance,
title="Test Title",
message="Test Message",
urgency=mock.ANY,
sound=mock.ANY,
timeout=10000,
)

mock_create_task.assert_called_once()


@mock.patch.object(NotifierClient, "notify")
def test_success(mock_notify):
"""Test success notification uses the correct image."""
client = NotifierClient()
client.success("Operation succeeded")

mock_notify.assert_called_once_with(
message="Operation succeeded",
image_path=client.success_img_path,
)


@mock.patch.object(NotifierClient, "notify")
def test_failure(mock_notify):
"""Test failure notification uses the correct image."""
client = NotifierClient()
client.failure("Operation failed")

mock_notify.assert_called_once_with(
message="Operation failed",
image_path=client.fail_img_path,
)
2 changes: 1 addition & 1 deletion tests/test_test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_doctest_test_runner_real_file_cmd_fail(notifier_mock, print_mock, code_
)

assert test_runner.run() is False
notifier_mock.fail.assert_called_once_with("NOT OK TO TALK")
notifier_mock.failure.assert_called_once_with("NOT OK TO TALK")
assert print_mock.call_count == 2


Expand Down