Repository: CiviWiki/OpenCiviWiki

Issue:
{FEAT}: Automated testing with actions.
### Idea summary

Usage of GitHub actions.

### Further details

We can use GitHub Actions to check/test the code that is being pushed upstream via PRs and it can be tested before merging automatically (Technically it is Continuous Integration).


Discussion:
If this issue is open, I can take a look at it.
However I think i would be better to get @brylie  input on this
I do believe it is a simple yet necessary solution which we need. Once brylie approves it would be useful if you make files for the workflow yml. I would like to review the same though.

I agree this is an important issue. It is also a duplicate of #146. @wassafshahzad I'll assign you to this issue and close #146 
Thanks for closing #146, I believe it was left unattended or it wasn't upgraded for Actions.
One thing to note: `api` app has a [test](https://github.com/CiviWiki/OpenCiviWiki/blob/develop/project/api/tests/test_propublica.py) file that is obsolete. For example, (at least) one package imported by this test file is missing, so the project itself does not include it. Running the tests of the whole project gives an error for this reason. Since, I am now working on unit tests for initially `accounts` app (issue #952), I run tests for only `accounts` app. So you should do the same thing for now. 

@brylie, what can we do about tests in `api` app? We can remove it. Or we won't run its tests? Or, if we use the related part which is tested in the future, we try to solve the problem?
> what can we do about tests in api app?

I'm in favor of removing obsolete code. As we deprecate the `api` app, we can make sure the code we move to proper apps has good test coverage.
`ProPublicaAPI` is the missing module that is imported there. Our project does not have such a module. Are we going to use this module in the future? If not, I will remove it safely.
I would remove the unwanted code in my pr as well
> ProPublicaAPI is the missing module that is imported there.

ProPublica has been removed #857. Feel free to remove that API/test code as well.
I have removed the test module from api, should I remove the test_framework as well ?
>  should I remove the test_framework as well ?

Yes, clean up any unused test code. We will be adding tests within the new Django apps.

Resolving patch:
diff --git a/project/accounts/models.py b/project/accounts/models.py
--- a/project/accounts/models.py
+++ b/project/accounts/models.py
@@ -9,7 +9,7 @@
 
 from taggit.managers import TaggableManager
 
-from api.models.category import Category
+from api.models import Category
 from common.utils import PathAndRename
 
 


Test patch:
diff --git a/project/api/tests/__init__.py b/project/api/tests/__init__.py
deleted file mode 100644
diff --git a/project/api/tests/test_propublica.py b/project/api/tests/test_propublica.py
deleted file mode 100644
--- a/project/api/tests/test_propublica.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from mock import patch, MagicMock
-
-from rest_framework.test import APITestCase, override_settings
-
-from ..propublica import ProPublicaAPI
-from .propublica_responses import PROPUBLICA_CORRECT_RESPONSE
-
-
-@override_settings(PROPUBLICA_API_KEY="test-token")
-class ProPublicaAPITestCase(APITestCase):
-    def setUp(self):
-        self.api_instance = ProPublicaAPI()
-
-    def test_headers_are_set_correctly(self):
-        self.assertDictEqual(
-            self.api_instance.auth_headers, {"X-API-Key": "test-token"}
-        )
-
-    @patch("api.propublica.requests.get")
-    def test_if_search_returns_correct_results(self, get_mock):
-        get_mock.return_value = MagicMock(
-            json=MagicMock(return_value=PROPUBLICA_CORRECT_RESPONSE)
-        )
-        query = "query"
-        data = self.api_instance.search(query)
-        self.assertEqual(data, PROPUBLICA_CORRECT_RESPONSE)
-        get_mock.assert_called_once_with(
-            self.api_instance.URL.format(query=query),
-            headers=self.api_instance.auth_headers,
-        )
diff --git a/project/test_framework/__init__.py b/project/test_framework/__init__.py
deleted file mode 100644
--- a/project/test_framework/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from contextlib import contextmanager
-
-from aloe import around, world
-from selenium import webdriver
-
-"""
-this method sets a global variable of type WebDriver. This is the browser.
-@around.all is a global hook. It will be ran once before all scenarios
-After the last scenario is completed (pass or fail) the WebDriver will be closed
-"""
-
-
-@around.all
-@contextmanager
-def with_chrome():
-    options = webdriver.ChromeOptions()
-    options.add_argument("--headless")
-    options.add_argument("--disable-extensions")
-    options.add_argument("--no-sandbox")
-    world.browser = webdriver.Chrome(options=options)
-    yield
-    world.browser.quit()
-    delattr(world, "browser")
diff --git a/project/test_framework/accessability/__init__.py b/project/test_framework/accessability/__init__.py
deleted file mode 100644
diff --git a/project/test_framework/accessability/features/__init__.py b/project/test_framework/accessability/features/__init__.py
deleted file mode 100644
--- a/project/test_framework/accessability/features/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from aloe import step
-
-from .accessability import login, logout, is_user_loggedin
-
-
-@step(r"a user with \"(.*?)\" credentials tries to log in")
-def user_logs_in(self, correct):
-    login(correct)
-
-
-@step(r"the user is logged in")
-def user_is_logged_in(self):
-    assert is_user_loggedin()
-
-
-@step(r"the user can log out")
-def user_logs_out(self):
-    logout()
-
-
-@step(r"the user is not logged in")
-def user_not_logged_in(self):
-    assert not is_user_loggedin()
diff --git a/project/test_framework/accessability/features/accessability.feature b/project/test_framework/accessability/features/accessability.feature
deleted file mode 100644
--- a/project/test_framework/accessability/features/accessability.feature
+++ /dev/null
@@ -1,10 +0,0 @@
-Feature: Accessibility  tests
-
-  Scenario: A user can login and logout
-    Given a user with "correct" credentials tries to log in
-    When the user is logged in
-    Then the user can log out
-
-  Scenario: Users cannot login with wrong password
-    Given a user with "incorrect" credentials tries to log in
-    Then the user is not logged in
\ No newline at end of file
diff --git a/project/test_framework/accessability/features/accessability.py b/project/test_framework/accessability/features/accessability.py
deleted file mode 100644
--- a/project/test_framework/accessability/features/accessability.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from test_framework.utils import *
-
-
-def login(credentials):
-    if is_user_loggedin():
-        return
-    else:
-        open_home_page()
-        el = find_element(login_signup_button)
-        el.click()
-        wait_for_page("login")
-        find_element(username_field).send_keys(username)
-        if credentials == "correct":
-            find_element(password_field).send_keys(password)
-            find_element(login_button).click()
-        elif credentials == "incorrect":
-            find_element(password_field).send_keys(password_wrong)
-
-
-def is_user_loggedin():
-    try:
-        return find_element(user_label).is_displayed()
-    except:
-        return
-
-
-def logout():
-    find_element(nav_menu).click()
-    find_element(logout_button).click()
diff --git a/project/test_framework/my_feed/__init__.py b/project/test_framework/my_feed/__init__.py
deleted file mode 100644
diff --git a/project/test_framework/my_feed/features/__init__.py b/project/test_framework/my_feed/features/__init__.py
deleted file mode 100644
--- a/project/test_framework/my_feed/features/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from test_framework.accessability.features import *
-from test_framework.accessability.features.accessability import *
diff --git a/project/test_framework/my_feed/features/my_feed.feature b/project/test_framework/my_feed/features/my_feed.feature
deleted file mode 100644
--- a/project/test_framework/my_feed/features/my_feed.feature
+++ /dev/null
@@ -1,10 +0,0 @@
-Feature: My Feed tests
-
-  Scenario: User can view My Feed
-    Given a user with "correct" credentials tries to log in
-    When user opens the "My Feed" menu
-    And My Feed is displayed
-    And user can filter threads by category
-    And the user can "remove" the "Agriculture" category
-    And the user can "add" the "Agriculture" category
-    Then the trending issues are displayed
\ No newline at end of file
diff --git a/project/test_framework/my_profile/__init__.py b/project/test_framework/my_profile/__init__.py
deleted file mode 100644
diff --git a/project/test_framework/my_profile/features/__init__.py b/project/test_framework/my_profile/features/__init__.py
deleted file mode 100644
--- a/project/test_framework/my_profile/features/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from test_framework.accessability.features import *
-from test_framework.accessability.features.accessability import *
diff --git a/project/test_framework/my_profile/features/my_profile.feature b/project/test_framework/my_profile/features/my_profile.feature
deleted file mode 100644
--- a/project/test_framework/my_profile/features/my_profile.feature
+++ /dev/null
@@ -1,7 +0,0 @@
-Feature: My Profile tests
-
-  Scenario: User can change My Profile settings
-    Given a user with "correct" credentials tries to log in
-    When user opens the "My Profile" menu
-    And the user can view it's activity
-    Then the user can change About Me
\ No newline at end of file
diff --git a/project/test_framework/selectors.py b/project/test_framework/selectors.py
deleted file mode 100644
--- a/project/test_framework/selectors.py
+++ /dev/null
@@ -1,55 +0,0 @@
-civi_wiki_url = "https://www.civiwiki.org"
-username = "unit1test"
-password = "unit1test"
-password_wrong = "unit123test"
-
-# Accessibility
-login_signup_button = ".login-btn"
-username_field = "#username"
-password_field = "#password"
-login_button = ".login-button"
-user_label = ".label-username"
-civi_wiki_logo = ".wordmark"
-nav_menu = ".nav-menu"
-logout_button = "a.dropdown-item:nth-child(5)"
-settings_button = "a.dropdown-item:nth-child(4)"
-my_profile_button = "a.dropdown-item:nth-child(2)"
-my_feed_button = "a.dropdown-item:nth-child(1)"
-
-# Settings
-email_field = "#email"
-first_name_field = "#first_name"
-last_name_field = "#last_name"
-location_field = ".location-info"
-location_set = "#autocomplete"
-activity = ".profile-civi-list"
-edit_profile_butoon = "a.btn"
-about_me_field = "#about_me"
-my_feed_list = "#feed-list"
-manage_categories = "#manage-categories"
-choose_button = "#choose-category"
-trending_issues = ".list-title"
-
-# Threads
-add_civi_button = ".add-civi"
-add_problem_button = "button.btn-small:nth-child(1)"
-add_cause_button = "button.btn-small:nth-child(2)"
-add_solution_button = "button.btn:nth-child(3)"
-civi_title_field = "#civi-title"
-civi_description_field = "#civi-body"
-civi_links_field = "#civi_links"
-create_civi_button = ".create-new-civi"
-first_civi = ".civi-title > span:nth-child(1)"
-create_thread_button = ".new-thread"
-thread_title = "#thread-title"
-thread_summary = "#thread-body"
-thread_type_dropdown = (
-    ".new-thread-level-selection > div:nth-child(1) > div:nth-child(1)"
-)
-thread_category_dropdown = ".new-thread-category-selection > div:nth-child(1) > div:nth-child(1) > input:nth-child(2)"
-thread_create_draft = ".draft-new-thread"
-thread_publish = "#js-publish-btn"
-add_response_button = "#add-new-response"
-response_title = "#response-title"
-response_body = "#response-body"
-response_confirm = ".create-new-response"
diff --git a/project/test_framework/settings/__init__.py b/project/test_framework/settings/__init__.py
deleted file mode 100644
diff --git a/project/test_framework/settings/features/__init__.py b/project/test_framework/settings/features/__init__.py
deleted file mode 100644
--- a/project/test_framework/settings/features/__init__.py
+++ /dev/null
@@ -1,149 +0,0 @@
-import time
-from selenium.webdriver.common.keys import Keys
-
-from test_framework.utils import *
-from test_framework.accessability.features import *
-from test_framework.accessability.features.accessability import *
-
-
-def open_menu(menu):
-    find_element(nav_menu).click()
-    if menu == "Settings":
-        find_element(settings_button).click()
-        wait_for_page("/settings")
-    if menu == "My Profile":
-        find_element(my_profile_button).click()
-        wait_for_page(username)
-    if menu == "My Feed":
-        find_element(my_feed_button).click()
-
-
-def get_settings():
-    settings = {
-        "email": world.browser.find_element_by_css_selector(email_field).get_attribute(
-            "value"
-        ),
-        "username": world.browser.find_element_by_css_selector(
-            username_field
-        ).get_attribute("value"),
-        "first name": find_element(first_name_field).get_attribute("value"),
-        "last name": find_element(last_name_field).get_attribute("value"),
-        "location": find_element(location_field).text,
-    }
-    return settings
-
-
-def set_settings():
-    time.sleep(1)
-    first_name_1 = "unit"
-    first_name_2 = "unit2"
-    last_name_1 = "test"
-    last_name_2 = "test2"
-    location_1 = "Washington, DC, United States"
-    location_2 = "New York, NY, United States"
-
-    if find_element(first_name_field).get_attribute("value") == first_name_1:
-        find_element(first_name_field).clear()
-        find_element(first_name_field).send_keys(first_name_2)
-    elif find_element(first_name_field).get_attribute("value") == first_name_2:
-        find_element(first_name_field).clear()
-        find_element(first_name_field).send_keys(first_name_1)
-
-    if find_element(last_name_field).get_attribute("value") == last_name_1:
-        find_element(last_name_field).clear()
-        find_element(last_name_field).send_keys(last_name_2)
-    elif find_element(last_name_field).get_attribute("value") == last_name_2:
-        find_element(last_name_field).clear()
-        find_element(last_name_field).send_keys(last_name_1)
-
-    if (
-        location_1.replace(", DC, United States", "")
-        in find_element(location_field).text
-    ):
-        find_element(location_set).send_keys(location_2)
-        time.sleep(1)
-        find_element(location_set).send_keys(Keys.DOWN)
-        find_element(location_set).send_keys(Keys.RETURN)
-    elif (
-        location_2.replace(", NY, United States", "")
-        in find_element(location_field).text
-    ):
-        find_element(location_set).send_keys(location_1)
-        time.sleep(1)
-        find_element(location_set).send_keys(Keys.DOWN)
-        find_element(location_set).send_keys(Keys.RETURN)
-
-    find_element(first_name_field).click()
-
-
-def is_activity_displayed():
-    return find_element(activity).is_displayed()
-
-
-def change_about_me():
-    first_about = "this is the first about me string"
-    second_about = "this is the secod about me string"
-    find_element(edit_profile_butoon).click()
-    about_me = find_element(about_me_field)
-    world.first_about_me = about_me.get_attribute("value")
-
-    if about_me.get_attribute("value") == first_about:
-        about_me.clear()
-        about_me.send_keys(second_about)
-        about_me.send_keys(Keys.TAB)
-    elif about_me.get_attribute("value") == second_about:
-        about_me.clear()
-        about_me.send_keys(first_about)
-        about_me.send_keys(Keys.TAB)
-
-
-def get_about_me():
-    find_element(edit_profile_butoon).click()
-    return find_element(about_me_field).get_attribute("value")
-
-
-def is_my_feed_displayed():
-    return find_element(my_feed_list).is_displayed()
-
-
-def select_filter(filter):
-    categories = find_elements_by_class("category-item")
-
-    for category in categories:
-        if category.text == filter:
-            category.click()
-            break
-
-
-def get_threads_count():
-    return len(find_elements_by_class("s12"))
-
-
-def add_remove_category(mode, wanted_category):
-    find_element(manage_categories).click()
-    time.sleep(1)
-    categories = find_elements_by_class("category-checkbox")
-    for category in categories:
-        if wanted_category in category.text:
-            checkbox = category.find_element_by_class_name("filled-in")
-            if mode == "remove" and checkbox.get_attribute("checked") == "true":
-                category.find_elements_by_css_selector("*")[1].click()
-                break
-            elif mode == "add" and checkbox.get_attribute("checked") != "true":
-                category.find_elements_by_css_selector("*")[1].click()
-                break
-
-    find_element(choose_button).click()
-    time.sleep(3)
-
-
-def find_category(wanted_category):
-    categories = find_elements_by_class("category-item")
-    for category in categories:
-        if category.text == wanted_category:
-            return "true"
-    return
-
-
-def is_trending_issues():
-    return find_element(trending_issues).is_displayed()
diff --git a/project/test_framework/settings/features/settings.feature b/project/test_framework/settings/features/settings.feature
deleted file mode 100644
--- a/project/test_framework/settings/features/settings.feature
+++ /dev/null
@@ -1,8 +0,0 @@
-Feature: Settings tests
-
-  Scenario: User can change it's settings
-    Given a user with "correct" credentials tries to log in
-    When user opens the "Settings" menu
-    And the current settings are saved
-    And the user enters new settings
-    Then the settings are changed
\ No newline at end of file
diff --git a/project/test_framework/threads/__init__.py b/project/test_framework/threads/__init__.py
deleted file mode 100644
diff --git a/project/test_framework/threads/features/__init__.py b/project/test_framework/threads/features/__init__.py
deleted file mode 100644
--- a/project/test_framework/threads/features/__init__.py
+++ /dev/null
@@ -1,132 +0,0 @@
-import time
-from selenium.webdriver.common.keys import Keys
-
-from test_framework.utils import *
-from test_framework.accessability.features import *
-
-
-def open_thread(thread_name):
-    time.sleep(3)
-    threads = find_elements_by_class("thread-title")
-    for thread in threads:
-        if thread.text == thread_name:
-            thread.click()
-            wait_for_page("/thread/")
-            break
-
-
-def add_civi(mode, title, description, links):
-    time.sleep(1)
-    find_element(add_civi_button).click()
-    if mode == "problem":
-        find_element(add_problem_button).click()
-        find_element(civi_title_field).send_keys(title)
-        find_element(civi_description_field).send_keys(description)
-    elif mode == "cause":
-        find_element(add_cause_button).click()
-        find_element(civi_title_field).send_keys(title)
-        find_element(civi_description_field).send_keys(description)
-        find_element(civi_links_field).send_keys(links)
-        find_element(civi_links_field).send_keys(Keys.RETURN)
-        time.sleep(1)
-    elif mode == "solution":
-        find_element(add_solution_button).click()
-        find_element(civi_title_field).send_keys(title)
-        find_element(civi_description_field).send_keys(description)
-        find_element(civi_links_field).send_keys(links)
-        find_element(civi_links_field).send_keys(Keys.RETURN)
-        time.sleep(1)
-
-    find_element(create_civi_button).click()
-    return find_element(first_civi).is_displayed()
-
-
-def create_thread(thread_type, title, summary, category):
-    find_element(create_thread_button).click()
-    find_element(thread_title).send_keys(title)
-    find_element(thread_summary).send_keys(summary)
-    find_element(thread_type_dropdown).click()
-
-    for element in find_element(thread_type_dropdown).find_elements_by_css_selector(
-        "*"
-    ):
-        if element.text == thread_type:
-            element.click()
-            break
-
-    find_element(thread_category_dropdown).click()
-    time.sleep(1)
-    find_element(thread_category_dropdown).send_keys(Keys.DOWN)
-    find_element(thread_category_dropdown).send_keys(Keys.RETURN)
-    time.sleep(1)
-    # for element in find_element_by_class("new-thread-category-selection").find_elements_by_css_selector("*"):
-    #     if element.text in category:
-    #         scroll_into_view(element)
-    #         # js_click(element)
-    #         element.click()
-    #         break
-    find_element(thread_create_draft).click()
-    time.sleep(1)
-    open_thread(title)
-    find_element(thread_publish).click()
-    time.sleep(3)
-
-
-def civi_vote(civi_title, vote):
-    for civi in find_elements_by_class("civi-card"):
-        if civi_title in civi.find_element_by_class_name("text-wrapper").text:
-            click_vote(vote, civi)
-
-
-def click_vote(vote, element):
-    options = element.find_elements_by_class_name("rating-wrapper")
-    if vote == "DISAGREE":
-        options[0].find_element_by_class_name("rating-button").click()
-    elif vote == "NEUTRAL":
-        options[2].find_element_by_class_name("rating-button").click()
-    elif vote == "AGREE":
-        options[4].find_element_by_class_name("rating-button").click()
-    time.sleep(3)
-
-
-def is_civi_visible(type):
-    if type == "cause":
-        number_of_elements = find_elements_by_class("cause-nav-indiv")
-    elif type == "solution":
-        number_of_elements = find_elements_by_class("solution-nav-indiv")
-    elif type == "problem":
-        number_of_elements = find_elements_by_class("problem-nav-indiv")
-
-    if len(number_of_elements) > 0:
-        return "true"
-    else:
-        return
-
-
-def add_comment(civi_title, response_title_text, response_body_text):
-    for civi in find_elements_by_class("civi-card"):
-        if civi_title in civi.find_element_by_class_name("text-wrapper").text:
-            # time.sleep(1)
-            civi.click()
-            # time.sleep(1)
-            try:
-                find_element(add_response_button).click()
-            except:
-                civi.click()
-                find_element(add_response_button).click()
-            find_element(response_title).send_keys(response_title_text)
-            find_element(response_body).send_keys(response_body_text)
-            find_element(response_confirm).click()
-            # time.sleep(3)
-            break
-
-
-def remove_civi(civi_title):
-    time.sleep(1)
-    for civi in find_elements_by_class("civi-card"):
-        if civi_title in civi.find_element_by_class_name("text-wrapper").text:
-            try:
-                civi.find_element_by_class_name("delete").click()
-            except:
-                time.sleep(2)
-                civi.find_element_by_class_name("delete").click()
diff --git a/project/test_framework/threads/features/threads.feature b/project/test_framework/threads/features/threads.feature
deleted file mode 100644
--- a/project/test_framework/threads/features/threads.feature
+++ /dev/null
@@ -1,38 +0,0 @@
-Feature: Threads tests
-
-  Scenario: User can create threads
-    Given a user with "correct" credentials tries to log in
-    And user opens the "My Feed" menu
-    When user can create a new "Federal" thread
-    And user opens the "My Feed" menu
-    And user can create a new "State" thread
-    And user opens the "My Feed" menu
-#    Then the user can delete a thread
-
-  Scenario: User can add new problems, causes and solutions
-    Given a user with "correct" credentials tries to log in
-    And user opens the "My Feed" menu
-    And the user opens the "automation Federal thread" thread
-    When the user can add a "problem" to the thread
-    And the user can add a "cause" to the thread
-    And the user can add a "solution" to the thread
-
-  Scenario: Civis are only visible when user votes AGREE
-    Given a user with "correct" credentials tries to log in
-    When the user opens the "automation Federal thread" thread
-    Then the "cause" is "not visible" when user clicks "DISAGREE" on "this is a problem"
-    And the "cause" is "visible" when user clicks "AGREE" on "this is a problem"
-    And the "solution" is "not visible" when user clicks "DISAGREE" on "c1"
-    And the "solution" is "visible" when user clicks "AGREE" on "c1"
-
-  Scenario: User can add response to civis
-    Given a user with "correct" credentials tries to log in
-    When the user opens the "automation Federal thread" thread
-    Then the user can add response to "this is a problem"
-
-  Scenario: User can remove civis
-    Given a user with "correct" credentials tries to log in
-    When the user opens the "automation Federal thread" thread
-    Then the user can remove "solution" called "this is a test solution"
-    And the user can remove "cause" called "c1"
-    And the user can remove "problem" called "this is a problem"
\ No newline at end of file
diff --git a/project/test_framework/utils.py b/project/test_framework/utils.py
deleted file mode 100644
--- a/project/test_framework/utils.py
+++ /dev/null
@@ -1,63 +0,0 @@
-from aloe import world
-from selenium.webdriver.common.by import By
-from selenium.webdriver.support.wait import WebDriverWait
-from selenium.webdriver.support import expected_conditions as EC
-
-from test_framework.selectors import *
-
-webDriverWaitInSeconds = 5
-
-
-def open_home_page():
-    world.browser.get(civi_wiki_url)
-    wait_for_page(civi_wiki_url)
-
-
-########################################################
-# Waits for page with URL containing "url" to load
-def wait_for_page(url):
-    wait = WebDriverWait(world.browser, webDriverWaitInSeconds)
-    wait.until(EC.url_contains(url))
-
-
-# Returns a list of WebElement found by a CSS Selector
-def find_elements(selector):
-    return world.browser.find_elements_by_css_selector(selector)
-
-
-# Returns a list of WebElement found by Class name
-def find_elements_by_class(selector):
-    return world.browser.find_elements_by_class_name(selector)
-
-
-# Returns a single WebElement found by a CSS Selector
-def find_element(selector):
-    element = WebDriverWait(world.browser, webDriverWaitInSeconds).until(
-        EC.element_to_be_clickable((By.CSS_SELECTOR, selector))
-    )
-    return element
-
-
-# Returns a single WebElement found by Class name
-def find_element_by_class(selector):
-    return world.browser.find_element_by_class_name(selector)
-
-
-# Returns a single WebElement found by Name
-def find_element_by_name(selector):
-    element = WebDriverWait(world.browser, webDriverWaitInSeconds).until(
-        EC.element_to_be_clickable((By.NAME, selector))
-    )
-    return element
-
-
-# Scrolls "element" into view. Used when element is outside the field of view
-def scroll_into_view(element):
-    world.browser.execute_script("arguments[0].scrollIntoView();", element)
-    return element
-
-
-# Uses JS to click on an "element". Used when "element" is not a button or text field
-# Ex. homes in the Homes page
-def js_click(element):
-    world.browser.execute_script("arguments[0].click();", element)
diff --git a/project/threads/tests.py b/project/threads/tests.py
--- a/project/threads/tests.py
+++ b/project/threads/tests.py
@@ -1,3 +1 @@
-from django.test import TestCase
-
 # Create your tests here.