Time tracking you can host anywhere, built on Django. Full export support in multiple formats and easily extensible.
djangodockerhandcodedpythonself-hostedtime-trackingtimetracker
1from datetime import timedelta, datetime
2
3from django.contrib.auth.models import User
4
5from selenium.common.exceptions import NoSuchElementException
6
7from core.models import Entry, Project, Task
8from core.utils import parse_duration
9
10from . import SeleniumTestCase
11
12
13class EntriesTestCase(SeleniumTestCase):
14 def test_entries_navigation(self):
15 nav_timesheet = self.find("nav-timesheet")
16 nav_timesheet.click()
17
18 self.find("view-timesheet")
19
20 def test_entries_visible(self):
21 self.test_entries_navigation()
22
23 entries = Entry.objects.filter(user=User.objects.get(username="admin"))
24 for entry in entries:
25 entry_id = "entry-%s" % (entry.id,)
26 self.find(entry_id)
27
28 def test_entries_add(self):
29 self.test_entries_navigation()
30
31 tracker_project = self.find("tracker-project")
32 tracker_task = self.find("tracker-task")
33 tracker_note = self.find("tracker-note")
34 tracker_duration = self.find("tracker-duration")
35
36 project = Project.objects.first()
37 task = Task.objects.first()
38
39 self.select(project.name, tracker_project)
40 self.select(task.name, tracker_task)
41 tracker_note.send_keys("new entry note")
42 tracker_duration.send_keys("3:50")
43
44 # Submit button only shows up after duration is entered
45 tracker_submit = self.find("tracker-submit")
46 tracker_submit.click()
47
48 # TODO: Find the entry and not the view?
49 view_timesheet = self.find("view-timesheet")
50 self.contains(project.name, view_timesheet)
51 self.contains(task.name, view_timesheet)
52 self.contains("new entry note", view_timesheet)
53 self.contains("3:50", view_timesheet)
54
55 entry = Entry.objects.get(note="new entry note")
56 self.assertEqual(entry.note, "new entry note")
57 self.assertEqual(entry.duration, timedelta(hours=3, minutes=50))
58
59 def test_entries_delete(self):
60 self.test_entries_navigation()
61
62 entry = Entry.objects.filter(
63 user=User.objects.get(username="admin")
64 ).first() # noqa: E501
65 entry_id = "entry-%s" % (entry.id,)
66 entry_element = self.find(entry_id)
67 entry_menu = self.find("entry-menu", entry_element)
68 entry_menu_delete = self.find("entry-menu-delete", entry_element)
69
70 entry_menu.click()
71 entry_menu_delete.click()
72
73 self.assertRaises(NoSuchElementException, self.find, entry_id)
74 self.assertRaises(Task.DoesNotExist, Task.objects.get, id=entry.id)
75
76 def test_entries_change(self):
77 # TODO: Test select field changes (project, task) and date
78 self.test_entries_navigation()
79
80 entry = Entry.objects.filter(
81 user=User.objects.get(username="admin")
82 ).first() # noqa: E501
83 entry_id = "entry-%s" % (entry.id,)
84 entry_element = self.find(entry_id)
85 entry_menu = self.find("entry-menu", entry_element)
86 entry_menu_change = self.find("entry-menu-change", entry_element)
87
88 entry_menu.click()
89 entry_menu_change.click()
90
91 entry_modal_date = self.find("entry-modal-date")
92 entry_modal_project = self.find("entry-modal-project")
93 entry_modal_task = self.find("entry-modal-task")
94 entry_modal_note = self.find("entry-modal-note")
95 entry_modal_duration = self.find("entry-modal-duration")
96 entry_modal_submit = self.find("entry-modal-submit")
97
98 cleaned_date = datetime.strptime(
99 entry_modal_date.get_attribute("value"), "%Y-%m-%d"
100 ).date()
101 cleaned_project = int(entry_modal_project.get_attribute("value").split("/")[-2])
102 cleaned_task = int(entry_modal_task.get_attribute("value").split("/")[-2])
103 cleaned_duration = parse_duration(entry_modal_duration.get_attribute("value"))
104
105 self.assertEqual(entry.date, cleaned_date)
106 self.assertEqual(entry.project.id, cleaned_project)
107 self.assertEqual(entry.task.id, cleaned_task)
108 self.assertEqual(entry.note, entry_modal_note.get_attribute("value"))
109 self.assertEqual(entry.duration, cleaned_duration)
110
111 self.clear(entry_modal_note)
112 entry_modal_note.send_keys("new entry note")
113 self.clear(entry_modal_duration)
114 entry_modal_duration.send_keys("3:50")
115 entry_modal_submit.click()
116
117 self.contains("new entry note", entry_element)
118 self.contains("3:50", entry_element)
119
120 entry = Entry.objects.get(id=entry.id)
121 self.assertEqual(entry.note, "new entry note")
122 self.assertEqual(entry.duration, timedelta(hours=3, minutes=50))