From de7cb07ea9b3ee3b36b8dccb3f2333ec76ac34f2 Mon Sep 17 00:00:00 2001 From: Kevin Guillaumond Date: Sat, 6 Apr 2024 16:27:35 -0700 Subject: [PATCH] Write unit test for TaskView.get_title() I started to look at #1077 and I thought it would be nice to be able to unit test this type of thing. A simple unit test for TaskView would be * create a task * create a view for it * check that the title of the view is the title of the task In the current state of affairs, this is difficult because the code that puts the Task data into the view lives in TaskEditor. But we pass a Task to the constructor of TaskView, so TaskView actually has all the data it needs. So I moved the logic there. I also moved the `is_new()` logic from the TaskEditor down to the Task. I feel like it should be possible to eventually do this in the TaskView constructor but for now, TaskEditor calls `self.textview.set_text_from_task()` from the same place it used to run that code. The test triggers a segmentation fault when running on GitHub Actions, so it's skipped while I figure out why. --- GTG/core/tasks.py | 6 +++- GTG/gtk/editor/editor.py | 55 ++++--------------------------------- GTG/gtk/editor/taskview.py | 32 +++++++++++++++++++++ tests/core/test_task.py | 16 +++++++++++ tests/core/test_taskview.py | 22 ++++++++++++++- 5 files changed, 79 insertions(+), 52 deletions(-) diff --git a/GTG/core/tasks.py b/GTG/core/tasks.py index a6eecfce5c..5a197e149b 100644 --- a/GTG/core/tasks.py +++ b/GTG/core/tasks.py @@ -295,6 +295,10 @@ def date_modified(self, value: Any) -> None: self._date_modified = Date(value) + def is_new(self) -> bool: + return self.title == DEFAULT_TITLE and not self.content + + @GObject.Property(type=str) def title(self) -> str: return self.raw_title @@ -753,7 +757,7 @@ def duplicate_for_recurrent(self, task: Task) -> Task: return new_task - def new(self, title: str = None, parent: UUID = None) -> Task: + def new(self, title: str = '', parent: UUID = None) -> Task: """Create a new task and add it to the store.""" tid = uuid4() diff --git a/GTG/gtk/editor/editor.py b/GTG/gtk/editor/editor.py index d01be5cfe9..48b05e686e 100644 --- a/GTG/gtk/editor/editor.py +++ b/GTG/gtk/editor/editor.py @@ -40,7 +40,7 @@ from GTG.gtk.editor.taskview import TaskView from GTG.gtk.tag_completion import tag_filter from GTG.gtk.colors import rgb_to_hex -from GTG.core.tasks import Task, Status, DEFAULT_TITLE +from GTG.core.tasks import Task, Status log = logging.getLogger(__name__) @@ -149,21 +149,9 @@ def __init__(self, app, task): provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) - # self.textview.browse_tag_cb = app.select_tag - # self.textview.new_subtask_cb = self.new_subtask - # self.textview.get_subtasks_cb = task.get_children - # self.textview.delete_subtask_cb = self.remove_subtask - # self.textview.rename_subtask_cb = self.rename_subtask - # self.textview.open_subtask_cb = self.open_subtask - # self.textview.save_cb = self.light_save - # self.textview.add_tasktag_cb = self.tag_added - # self.textview.remove_tasktag_cb = self.tag_removed - # self.textview.refresh_cb = self.refresh_editor - # self.textview.get_tagslist_cb = task.get_tags_name - # self.textview.tid = task.id - self.textview.browse_tag_cb = app.select_tag self.textview.new_subtask_cb = self.new_subtask + # self.textview.get_subtasks_cb = task.get_children self.textview.delete_subtask_cb = self.remove_subtask self.textview.rename_subtask_cb = self.rename_subtask self.textview.open_subtask_cb = self.open_subtask @@ -171,6 +159,7 @@ def __init__(self, app, task): self.textview.add_tasktag_cb = self.tag_added self.textview.remove_tasktag_cb = self.tag_removed self.textview.refresh_cb = self.refresh_editor + # self.textview.get_tagslist_cb = task.get_tags_name self.textview.tid = task.id # Voila! it's done @@ -179,40 +168,7 @@ def __init__(self, app, task): textview_focus_controller.connect("leave", self.on_textview_focus_out) self.textview.add_controller(textview_focus_controller) - tags = task.tags - text = self.task.content - title = self.task.title - - # Insert text and tags as a non_undoable action, otherwise - # the user can CTRL+Z even this inserts. - self.textview.buffer.begin_irreversible_action() - self.textview.buffer.set_text(f"{title}\n") - - if text: - self.textview.insert(text) - - # Insert any remaining tags - if tags: - tag_names = [t.name for t in tags] - self.textview.insert_tags(tag_names) - else: - # If not text, we insert tags - if tags: - tag_names = [t.name for t in tags] - self.textview.insert_tags(tag_names) - start = self.textview.buffer.get_end_iter() - self.textview.buffer.insert(start, '\n') - - # Insert subtasks if they weren't inserted in the text - subtasks = task.children - for sub in subtasks: - if sub.id not in self.textview.subtasks['tags']: - self.textview.insert_existing_subtask(sub) - - if self.is_new(): - self.textview.select_title() - - self.textview.buffer.end_irreversible_action() + self.textview.set_text_from_task() # Connect search field to tags popup self.tags_tree.set_search_entry(self.tags_entry) @@ -873,8 +829,7 @@ def on_window_focus_change(self, window, gparam): def is_new(self) -> bool: - return (self.task.title == DEFAULT_TITLE - and self.textview.get_text() == '') + return self.task.is_new() def destruction(self, _=None): diff --git a/GTG/gtk/editor/taskview.py b/GTG/gtk/editor/taskview.py index 23dfb3f279..adc2c62bfa 100644 --- a/GTG/gtk/editor/taskview.py +++ b/GTG/gtk/editor/taskview.py @@ -165,6 +165,38 @@ def __init__(self, ds: Datastore, task, clipboard, dark) -> None: press_gesture.connect('begin', self.on_single_begin) self.add_controller(press_gesture) + def set_text_from_task(self) -> None: + """Sets the text of the view, from the text of the task""" + # Insert text and tags as a non_undoable action, otherwise + # the user can CTRL+Z even this inserts. + self.buffer.begin_irreversible_action() + self.buffer.set_text(f"{self.task.title}\n") + + if self.task.content: + self.insert(self.task.content) + + # Insert any remaining tags + if self.task.tags: + tag_names = [t.name for t in self.task.tags] + self.insert_tags(tag_names) + else: + # If not text, we insert tags + if self.task.tags: + tag_names = [t.name for t in self.task.tags] + self.insert_tags(tag_names) + start = self.buffer.get_end_iter() + self.buffer.insert(start, '\n') + + # Insert subtasks if they weren't inserted in the text + subtasks = self.task.children + for sub in subtasks: + if sub.id not in self.subtasks['tags']: + self.insert_existing_subtask(sub) + + if self.task.is_new(): + self.select_title() + + self.buffer.end_irreversible_action() def on_modified(self, buffer: Gtk.TextBuffer) -> None: """Called every time the text buffer changes.""" diff --git a/tests/core/test_task.py b/tests/core/test_task.py index a9ab36b1b3..f3583c6ca6 100644 --- a/tests/core/test_task.py +++ b/tests/core/test_task.py @@ -29,6 +29,22 @@ class TestTask(TestCase): + def test_default_task_from_store_is_new(self): + task = TaskStore().new() + + self.assertTrue(task.is_new()) + + def test_task_with_content_is_not_new(self): + task = TaskStore().new() + task.content = 'foobar' + + self.assertFalse(task.is_new()) + + def test_task_with_title_is_not_new(self): + task = TaskStore().new(title='My new task') + + self.assertFalse(task.is_new()) + def test_title(self): task = Task(id=uuid4(), title='\tMy Title\n') diff --git a/tests/core/test_taskview.py b/tests/core/test_taskview.py index 41611d6130..ba59fd79d4 100644 --- a/tests/core/test_taskview.py +++ b/tests/core/test_taskview.py @@ -16,9 +16,14 @@ # this program. If not, see . # ----------------------------------------------------------------------------- +import os +import pytest import re +from uuid import uuid4 from unittest import TestCase -from GTG.gtk.editor.taskview import TAG_REGEX +from GTG.core.datastore import Datastore +from GTG.core.tasks import Task +from GTG.gtk.editor.taskview import TaskView, TAG_REGEX class TestTaskView(TestCase): @@ -41,3 +46,18 @@ def test_no_detect_tags(self): matches = re.findall(TAG_REGEX, content) self.assertEqual([], matches) + + @pytest.mark.skipif( + os.environ.get('GITHUB_ACTIONS') == 'true', + reason='Triggers a segfault on GHA. Still investigating.') + def test_get_title(self): + task_title = 'Very important task' + task = Task(id = uuid4(), title=task_title) + view = TaskView(Datastore(), task, None, False) + view.refresh_cb = lambda x: x # Refresh CB that does nothing + view.set_text_from_task() + view.detect_title() + + view_title = view.get_title() + + self.assertEqual(view_title, task_title)