-import time
+import itertools
+from nose import with_setup
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
class AddCourseTest(LiveServerTestCase):
"""Tests the Add Course form."""
- def setUp(self):
- self.driver = webdriver.Firefox()
- self.driver.implicitly_wait(3)
- self.wait = WebDriverWait(self.driver, 200)
- self.harvard = School.objects.create(name="Harvard University", usde_id=12345)
- self.northeastern = School.objects.create(name="Northeastern University", usde_id=33333)
-
- def tearDown(self):
- self.driver.close()
+ class FieldAction:
+ def __init__(self, field, value, autocomplete, error_expected):
+ self.field = field
+ self.value = value
+ self.autocomplete = autocomplete
+ self.error_expected = error_expected
+
+ def __str__(self):
+ return "{f}: {v}".format(f=self.field, v=self.value)
+
+ class CompositeFieldAction:
+ def __init__(self, subactions, error_expected):
+ self.subactions = subactions
+ self.error_expected = error_expected
+
+ SCHOOL_FIELD_NAME = 'DepartmentForm-school_text'
+ COURSE_FIELD_NAME = 'CourseForm-name'
+ DEPARTMENT_FIELD_NAME = 'DepartmentForm-name_text'
+ PROFESSOR_FIELD_NAME = 'ProfessorForm-name_text'
+ PROFESSOR_EMAIL_FIELD_NAME = 'ProfessorForm-email_text'
+ COURSE_URL_FIELD_NAME = 'CourseForm-url'
+ HONEYPOT_FIELD_NAME = 'CourseForm-instruction_url'
+
+ EXAMPLE_SCHOOL = 'Northeastern University'
+ EXAMPLE_NEW_DEPARTMENT = 'College of Arts and Sciences'
+ EXAMPLE_EXISTING_DEPARTMENT = 'College of Computer and Information Sciences'
+ EXAMPLE_NEW_PROFESSOR = 'Prof. X'
+ EXAMPLE_NEW_PROFESSOR_EMAIL = 'xavier@xmen.edu'
+ EXAMPLE_EXISTING_PROFESSOR = 'Prof. Einstein'
+ EXAMPLE_EXISTING_PROFESSOR_EMAIL = 'einstein@princeton.edu'
+ EXAMPLE_NEW_COURSE_NAME = 'Algorithms and Data'
+ EXAMPLE_EXISTING_COURSE_NAME = 'Theory of Computation'
+ EXAMPLE_NEW_COURSE_URL = 'http://neu.edu/stuff'
+ EXAMPLE_HONEYPOT_VALUE = 'Free drugs!'
+
+ SCHOOL_FIELD_OPTIONS = (
+ # Choose a school from autocomplete
+ FieldAction(SCHOOL_FIELD_NAME, EXAMPLE_SCHOOL, autocomplete=True, error_expected=False),
+ # Type in a school without choosing from autocomplete
+ FieldAction(SCHOOL_FIELD_NAME, EXAMPLE_SCHOOL, autocomplete=False, error_expected=True),
+ # Empty school name
+ FieldAction(SCHOOL_FIELD_NAME, '', autocomplete=False, error_expected=True)
+ )
+
+ DEPARTMENT_FIELD_OPTIONS = (
+ # Type in a new department
+ FieldAction(DEPARTMENT_FIELD_NAME, EXAMPLE_NEW_DEPARTMENT, autocomplete=False, error_expected=False),
+ # Choose an existing department
+ FieldAction(DEPARTMENT_FIELD_NAME, EXAMPLE_EXISTING_DEPARTMENT, autocomplete=True, error_expected=False)
+ )
+
+ PROFESSOR_FIELDS_OPTIONS = (
+ # New name and email typed in
+ CompositeFieldAction([
+ FieldAction(PROFESSOR_FIELD_NAME, EXAMPLE_NEW_PROFESSOR, autocomplete=False, error_expected=False),
+ FieldAction(PROFESSOR_EMAIL_FIELD_NAME, EXAMPLE_NEW_PROFESSOR_EMAIL, autocomplete=False, error_expected=False)],
+ error_expected=False),
+ # existing Professor Name selected, matching Professor Email selected
+ CompositeFieldAction([
+ FieldAction(PROFESSOR_FIELD_NAME, EXAMPLE_EXISTING_PROFESSOR, autocomplete=True, error_expected=False),
+ FieldAction(PROFESSOR_EMAIL_FIELD_NAME, EXAMPLE_EXISTING_PROFESSOR_EMAIL, autocomplete=True, error_expected=False)],
+ error_expected=False),
+ # existing Professor Name selected, no Email selected
+ CompositeFieldAction([
+ FieldAction(PROFESSOR_FIELD_NAME, EXAMPLE_EXISTING_PROFESSOR, autocomplete=True, error_expected=False)],
+ error_expected=False),
+ # existing Professor Email selected, no Name selected
+ CompositeFieldAction([
+ FieldAction(PROFESSOR_EMAIL_FIELD_NAME, EXAMPLE_EXISTING_PROFESSOR_EMAIL, autocomplete=True, error_expected=False)],
+ error_expected=False),
+ # existing Professor Name selected, different Professor Email selected
+ CompositeFieldAction([
+ FieldAction(PROFESSOR_FIELD_NAME, EXAMPLE_EXISTING_PROFESSOR, autocomplete=False, error_expected=False),
+ FieldAction(PROFESSOR_EMAIL_FIELD_NAME, EXAMPLE_NEW_PROFESSOR_EMAIL, autocomplete=False, error_expected=False)],
+ error_expected=True),
+ )
+
+ COURSE_FIELD_OPTIONS = (
+ # new course name
+ FieldAction(COURSE_FIELD_NAME, EXAMPLE_NEW_COURSE_NAME, autocomplete=False, error_expected=False),
+ # empty course name
+ FieldAction(COURSE_FIELD_NAME, '', autocomplete=False, error_expected=True)
+ )
+
+ COURSE_URL_OPTIONS = (
+ # no URL
+ FieldAction(COURSE_URL_FIELD_NAME, '', autocomplete=False, error_expected=False),
+ # URL given
+ FieldAction(COURSE_URL_FIELD_NAME, EXAMPLE_NEW_COURSE_URL, autocomplete=False, error_expected=False)
+ )
+
+ HONEYPOT_FIELD_OPTIONS = (
+ # something in the honeypot
+ FieldAction(HONEYPOT_FIELD_NAME, EXAMPLE_HONEYPOT_VALUE, autocomplete=False, error_expected=True),
+ # nothing in the honeypot
+ FieldAction(HONEYPOT_FIELD_NAME, '', autocomplete=False, error_expected=False)
+ )
+
+ @classmethod
+ def setUpClass(cls):
+ cls.driver = webdriver.Firefox()
+ cls.driver.implicitly_wait(3)
+ cls.wait = WebDriverWait(cls.driver, 200)
+ super(AddCourseTest, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.driver.quit()
+ super(AddCourseTest, cls).tearDownClass()
+
+ def set_up(self):
+ self.northeastern = School.objects.create(name=self.EXAMPLE_SCHOOL, usde_id=33333)
+ self.department = Department.objects.create(name=self.EXAMPLE_EXISTING_DEPARTMENT, school=self.northeastern)
+ self.professor = Professor.objects.create(name=self.EXAMPLE_EXISTING_PROFESSOR, email=self.EXAMPLE_EXISTING_PROFESSOR_EMAIL)
+ self.course = Course.objects.create(name=self.EXAMPLE_EXISTING_COURSE_NAME, department=self.department)
+
+ def tear_down(self):
+ School.objects.all().delete()
+ Department.objects.all().delete()
+ Professor.objects.all().delete()
+ Course.objects.all().delete()
def select_autocomplete(self, name, keys):
+ """Type in the characters into the given field, and then choose
+ the first item in the autocomplete menu that appears."""
input = self.driver.find_element_by_name(name)
input.send_keys(keys)
self.wait.until(EC.element_to_be_clickable(
autocomplete_menu_item = self.driver.find_element_by_id("ui-active-menuitem")
autocomplete_menu_item.click()
- def test_school_name(self):
- self.driver.get(self.live_server_url)
-
- # Click "Add Course"
- add_course_button = self.driver.find_element_by_id("add-course-btn")
- add_course_button.click()
-
- # Scroll down so the autocomplete menu is in view
- # This works around some weird failures
- self.driver.execute_script("javascript:window.scrollBy(0,200)")
-
- # Type in part of a school name
- school_input = self.driver.find_element_by_name("DepartmentForm-school_text")
- school_input.send_keys("harvard u")
-
- # Wait for autocomplete menu to appear
- # li.ui-menu-item:nth-child(1)
- self.wait.until(EC.element_to_be_clickable((By.XPATH, "//ul[contains(@class,'ui-autocomplete')]/li[1]")))
-
- # Choose the first suggestion
- school_input.send_keys(Keys.DOWN)
- active_item = self.driver.find_element_by_id("ui-active-menuitem")
- active_item.click()
-
- school_name_on_deck = self.driver.find_element_by_xpath("//div[contains(@class,'results_on_deck')][1]")
- self.assertIn("Harvard University", school_name_on_deck.text)
-
- school_id = self.driver.find_element_by_name("DepartmentForm-school")
- self.assertEqual(school_id.get_attribute("value"), str(self.harvard.id))
-
- def test_create_course(self):
- self.driver.get(self.live_server_url)
-
- # Click "Add Course"
- add_course_button = self.driver.find_element_by_id("add-course-btn")
- add_course_button.click()
- self.driver.execute_script("javascript:window.scrollBy(0,200)")
-
- # Choose a school
- self.select_autocomplete("DepartmentForm-school_text", "northeastern u")
-
- # Course name
- new_course_name = "SELENIUM TEST COURSE " + uuid.uuid4().hex
- course_name_input = self.driver.find_element_by_name("CourseForm-name")
- course_name_input.send_keys(new_course_name)
-
- # Department name
- new_department_name = "SELENIUM TEST DEPARTMENT " + uuid.uuid4().hex
- department_name_input = self.driver.find_element_by_name("DepartmentForm-name_text")
- department_name_input.send_keys(new_department_name)
-
- # Instructor name
- new_instructor_name = "SELENIUM TEST INSTRUCTOR " + uuid.uuid4().hex
- instructor_name_input = self.driver.find_element_by_name("ProfessorForm-name_text")
- instructor_name_input.send_keys(new_instructor_name)
+ @staticmethod
+ def flatten_actions(actions):
+ results = []
+ for action in actions:
+ if isinstance(action, AddCourseTest.CompositeFieldAction):
+ results.extend(AddCourseTest.flatten_actions(action.subactions))
+ else:
+ results.append(action)
- # Click "Save"
- save_button = self.driver.find_element_by_id("save-btn")
- save_button.click()
-
- # See if we are taken to the new course page
- self.wait.until(EC.title_contains(new_course_name))
-
- def test_create_existing_course(self):
- self.driver.get(self.live_server_url)
+ return results
- # Click "Add Course"
+ def fill_out_form(self, field_actions):
+ """Fill out the Add Course form with the given actions."""
add_course_button = self.driver.find_element_by_id("add-course-btn")
add_course_button.click()
self.driver.execute_script("javascript:window.scrollBy(0,200)")
- # Choose a school
- self.select_autocomplete("DepartmentForm-school_text", "northeastern u")
-
- # Course name
- new_course_name = "SELENIUM TEST COURSE " + uuid.uuid4().hex
- course_name_input = self.driver.find_element_by_name("CourseForm-name")
- course_name_input.send_keys(new_course_name)
-
- # Department name
- new_department_name = "SELENIUM TEST DEPARTMENT " + uuid.uuid4().hex
- department_name_input = self.driver.find_element_by_name("DepartmentForm-name_text")
- department_name_input.send_keys(new_department_name)
-
- # Instructor name
- new_instructor_name = "SELENIUM TEST INSTRUCTOR " + uuid.uuid4().hex
- instructor_name_input = self.driver.find_element_by_name("ProfessorForm-name_text")
- instructor_name_input.send_keys(new_instructor_name)
+ for action in AddCourseTest.flatten_actions(field_actions):
+ if action.autocomplete:
+ self.select_autocomplete(action.field, action.value)
+ else:
+ field = self.driver.find_element_by_name(action.field)
+ field.send_keys(action.value)
# Click "Save"
save_button = self.driver.find_element_by_id("save-btn")
save_button.click()
- # See if we are taken to the new course page
- self.wait.until(EC.title_contains(new_course_name))
-
- # Now go back to the home page
+ def expect_error(self, actions):
+ result = False
+ for action in actions:
+ if isinstance(action, AddCourseTest.CompositeFieldAction) and \
+ self.expect_error(action.subactions):
+ result = True
+ elif action.error_expected:
+ result = True
+
+ return result
+
+ def check_object_exists(self, value, field, exists=True):
+ desired_count = 1 if exists else 0
+
+ if field == self.COURSE_FIELD_NAME:
+ self.assertEqual(Course.objects.filter(name=value).count(), desired_count)
+ elif field == self.DEPARTMENT_FIELD_NAME:
+ self.assertEqual(Department.objects.filter(name=value).count(), desired_count)
+ elif field == self.PROFESSOR_FIELD_NAME:
+ self.assertEqual(Professor.objects.filter(name=value).count(), desired_count)
+ elif field == self.PROFESSOR_EMAIL_FIELD_NAME:
+ self.assertEqual(Professor.objects.filter(email=value).count(), desired_count)
+ elif field == self.COURSE_URL_FIELD_NAME:
+ self.assertEqual(Course.objects.filter(url=value).count(), desired_count)
+
+ def actions_test_and_check(self, actions):
+ self.set_up()
self.driver.get(self.live_server_url)
-
- # Click "Add Course"
- add_course_button = self.driver.find_element_by_id("add-course-btn")
- add_course_button.click()
- self.driver.execute_script("javascript:window.scrollBy(0,200)")
-
- # Choose the SAME school
- self.select_autocomplete("DepartmentForm-school_text", "northeastern u")
-
- # The SAME course name
- self.select_autocomplete("CourseForm-name", new_course_name)
-
- # The SAME instructor name
- self.select_autocomplete("ProfessorForm-name_text", new_instructor_name)
-
- # The SAME department name
- self.select_autocomplete("DepartmentForm-name_text", new_department_name)
-
- save_button = self.driver.find_element_by_id("save-btn")
- save_button.click()
- self.assertEqual(Course.objects.count(), 1, "Duplicated course not created")
-
+ error_expected = self.expect_error(actions)
+ self.fill_out_form(actions)
+ if error_expected:
+ for action in AddCourseTest.flatten_actions(actions):
+ self.check_object_exists(action.value, action.field, exists=False)
+ else:
+ for action in AddCourseTest.flatten_actions(actions):
+ self.check_object_exists(action.value, action.field, exists=True)
+ self.tear_down()
+
+ def test_combinations(self):
+ field_options_combinations = itertools.product(self.SCHOOL_FIELD_OPTIONS,
+ self.DEPARTMENT_FIELD_OPTIONS,
+ self.PROFESSOR_FIELDS_OPTIONS,
+ self.COURSE_FIELD_OPTIONS,
+ self.COURSE_URL_OPTIONS,
+ self.HONEYPOT_FIELD_OPTIONS)
+
+ for actions in field_options_combinations:
+ self.actions_test_and_check(actions)