diff --git a/functional_tests/base.py b/functional_tests/base.py
index 5766596f3fb710edfb7f2ec7e0dc48d3fc4e1bda..2a2ddd6da34f02669c2bbd078d8ab8925324ebcb 100644
--- a/functional_tests/base.py
+++ b/functional_tests/base.py
@@ -14,6 +14,19 @@ class FunctionalTest(StaticLiveServerTestCase):
 	def tearDown(self):
 		self.browser.quit()
 
+    def wait(fn):
+        def modified_fn(*args, **kwargs):
+            start_time = time.time()
+            while True:
+                try:
+                    return fn(*args, **kwargs)
+                except (AssertionError, WebdriverException) as e:
+                    if time.time() - start_time > MAX_WAIT:
+                        raise e
+                    time.sleep(0.5)
+        return modified_fn
+
+    @wait
 	def wait_for_row_in_list_table(self, row_text):
 		start_time = time.time()
 		while True:
@@ -26,3 +39,24 @@ class FunctionalTest(StaticLiveServerTestCase):
 			    if time.time() - start_time > MAX_WAIT:
 			        raise e
 			    time.sleep(0.5)
+
+    @wait
+    def wait_for(self, fn):
+        return fn()
+
+    @wait
+    def wait_to_be_logged_in(self, email):
+        self.wait_for(
+            lambda: self.browser.find_element_by_link_text('Log out')
+        )
+        navbar = self.browser.find_element_by_css_selector('.navbar')
+        self.assertIn(email, navbar.text)
+
+
+    @wait
+    def wait_to_be_logged_out(self, email):
+        self.wait_for(
+            lambda: self.browser.find_element_by_name('email')
+        )
+        navbar = self.browser.find_element_by_css_selector('.navbar')
+        self.assertNotIn(email, navbar.text)