Time tracking you can host anywhere, built on Django. Full export support in multiple formats and easily extensible.
djangodockerhandcodedpythonself-hostedtime-trackingtimetracker
1from os import environ
2from time import sleep
3
4from django.contrib.staticfiles.testing import StaticLiveServerTestCase
5from django.test import override_settings
6from django.core.management import call_command
7
8from selenium.webdriver import Firefox as FirefoxDriver
9from selenium.webdriver.firefox.options import Options as FirefoxOptions
10from selenium.webdriver.common.keys import Keys
11
12
13@override_settings(
14 STATICFILES_STORAGE="django.contrib.staticfiles.storage.StaticFilesStorage"
15)
16class SeleniumTestCase(StaticLiveServerTestCase):
17 def _flakyTestWrapper(self):
18 testMethod = getattr(self, self.flakyTestMethodName)
19 retries = 0
20 while retries < 3:
21 try:
22 return testMethod()
23 except Exception as e:
24 print("\nRetrying flaky test %s" % self.flakyTestMethodName)
25 retries += 1
26 lastException = e
27 sleep(1)
28 raise lastException
29
30 def run(self, result=None):
31 self.flakyTestMethodName = self._testMethodName
32 self._testMethodName = "_flakyTestWrapper"
33 super().run(result)
34 self._testMethodName = self.flakyTestMethodName
35
36 @classmethod
37 def setUpClass(cls):
38 options = FirefoxOptions()
39 if environ.get("MOZ_HEADLESS") != "0":
40 # Setting environ var might seem redundant but some versions of firefox
41 # and geckodriver fail to obey -headless properly
42 environ["MOZ_HEADLESS"] = "1"
43 options.add_argument("-headless")
44 cls.driver = FirefoxDriver(firefox_options=options)
45 cls.driver.maximize_window()
46 cls.driver.implicitly_wait(2)
47 cls.driver.set_page_load_timeout(30)
48
49 super().setUpClass()
50
51 @classmethod
52 def tearDownClass(cls):
53 cls.driver.quit()
54 super().tearDownClass()
55
56 def setUp(self):
57 call_command("migrate", verbosity=0)
58 call_command("fake", verbosity=0, iterations=1)
59
60 self.logIn()
61
62 super().setUp()
63
64 def logIn(self):
65 self.driver.get("%s%s" % (self.live_server_url, "/login/"))
66
67 input_username = self.find("input-username")
68 input_password = self.find("input-password")
69 button_submit = self.find("button-submit")
70
71 input_username.send_keys("admin")
72 input_password.send_keys("admin")
73 button_submit.click()
74
75 self.find("app")
76
77 def clear(self, element):
78 # Selenium's clear command doesn't work
79 value = element.get_attribute("value")
80 for character in value:
81 element.send_keys(Keys.BACK_SPACE)
82
83 def find(self, id, element=None):
84 # Our primary way of finding things
85 if element:
86 return element.find_element_by_id(id)
87 else:
88 return self.driver.find_element_by_id(id)
89
90 def contains(self, string, element=None):
91 # Finding things by a text string
92 xpath = "//*[contains(text(), '%s')]" % (string,)
93 if element:
94 return element.find_element_by_xpath(xpath)
95 else:
96 return self.driver.find_element_by_xpath(xpath)
97
98 def select(self, value, element):
99 # Our select field uses the select2 jQuery plugin
100 element = element.find_element_by_xpath("..")
101 element_arrow = element.find_element_by_class_name(
102 "select2-selection__arrow"
103 ) # noqa: E501
104 element_arrow.click()
105 element_search = self.driver.find_element_by_class_name(
106 "select2-search__field"
107 ) # noqa: E501
108 element_search.send_keys(value)
109 element_search.send_keys(Keys.RETURN)