Commit c6a9f722 by Calen Pennington

Merge pull request #1049 from MITx/feature/jzoldak/cms-unit-testing

Add Studio integration with factory boy for creating XModule objects in the data store
parents 2b6e2b51 0654665b
from factory import Factory
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from time import gmtime
from uuid import uuid4
from xmodule.timeparse import stringify_time
def XMODULE_COURSE_CREATION(class_to_create, **kwargs):
return XModuleCourseFactory._create(class_to_create, **kwargs)
def XMODULE_ITEM_CREATION(class_to_create, **kwargs):
return XModuleItemFactory._create(class_to_create, **kwargs)
class XModuleCourseFactory(Factory):
"""
Factory for XModule courses.
"""
ABSTRACT_FACTORY = True
_creation_function = (XMODULE_COURSE_CREATION,)
@classmethod
def _create(cls, target_class, *args, **kwargs):
template = Location('i4x', 'edx', 'templates', 'course', 'Empty')
org = kwargs.get('org')
number = kwargs.get('number')
display_name = kwargs.get('display_name')
location = Location('i4x', org, number,
'course', Location.clean(display_name))
store = modulestore('direct')
# Write the data to the mongo datastore
new_course = store.clone_item(template, location)
# This metadata code was copied from cms/djangoapps/contentstore/views.py
if display_name is not None:
new_course.metadata['display_name'] = display_name
new_course.metadata['data_dir'] = uuid4().hex
new_course.metadata['start'] = stringify_time(gmtime())
new_course.tabs = [{"type": "courseware"},
{"type": "course_info", "name": "Course Info"},
{"type": "discussion", "name": "Discussion"},
{"type": "wiki", "name": "Wiki"},
{"type": "progress", "name": "Progress"}]
# Update the data in the mongo datastore
store.update_metadata(new_course.location.url(), new_course.own_metadata)
return new_course
class Course:
pass
class CourseFactory(XModuleCourseFactory):
FACTORY_FOR = Course
template = 'i4x://edx/templates/course/Empty'
org = 'MITx'
number = '999'
display_name = 'Robot Super Course'
class XModuleItemFactory(Factory):
"""
Factory for XModule items.
"""
ABSTRACT_FACTORY = True
_creation_function = (XMODULE_ITEM_CREATION,)
@classmethod
def _create(cls, target_class, *args, **kwargs):
DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info']
parent_location = Location(kwargs.get('parent_location'))
template = Location(kwargs.get('template'))
display_name = kwargs.get('display_name')
store = modulestore('direct')
# This code was based off that in cms/djangoapps/contentstore/views.py
parent = store.get_item(parent_location)
dest_location = parent_location._replace(category=template.category, name=uuid4().hex)
new_item = store.clone_item(template, dest_location)
# TODO: This needs to be deleted when we have proper storage for static content
new_item.metadata['data_dir'] = parent.metadata['data_dir']
# replace the display name with an optional parameter passed in from the caller
if display_name is not None:
new_item.metadata['display_name'] = display_name
store.update_metadata(new_item.location.url(), new_item.own_metadata)
if new_item.location.category not in DETACHED_CATEGORIES:
store.update_children(parent_location, parent.definition.get('children', []) + [new_item.location.url()])
return new_item
class Item:
pass
class ItemFactory(XModuleItemFactory):
FACTORY_FOR = Item
parent_location = 'i4x://MITx/999/course/Robot_Super_Course'
template = 'i4x://edx/templates/chapter/Empty'
display_name = 'Section One'
\ No newline at end of file
...@@ -14,6 +14,7 @@ import xmodule.modulestore.django ...@@ -14,6 +14,7 @@ import xmodule.modulestore.django
from xmodule.modulestore import Location from xmodule.modulestore import Location
from xmodule.modulestore.xml_importer import import_from_xml from xmodule.modulestore.xml_importer import import_from_xml
import copy import copy
from factories import *
def parse_json(response): def parse_json(response):
...@@ -220,12 +221,6 @@ class ContentStoreTest(TestCase): ...@@ -220,12 +221,6 @@ class ContentStoreTest(TestCase):
'display_name': 'Robot Super Course', 'display_name': 'Robot Super Course',
} }
self.section_data = {
'parent_location' : 'i4x://MITx/999/course/Robot_Super_Course',
'template' : 'i4x://edx/templates/chapter/Empty',
'display_name': 'Section One',
}
def tearDown(self): def tearDown(self):
# Make sure you flush out the test modulestore after the end # Make sure you flush out the test modulestore after the end
# of the last test because otherwise on the next run # of the last test because otherwise on the next run
...@@ -271,20 +266,27 @@ class ContentStoreTest(TestCase): ...@@ -271,20 +266,27 @@ class ContentStoreTest(TestCase):
status_code=200, status_code=200,
html=True) html=True)
def test_course_factory(self):
course = CourseFactory.create()
self.assertIsInstance(course, xmodule.course_module.CourseDescriptor)
def test_item_factory(self):
course = CourseFactory.create()
item = ItemFactory.create(parent_location=course.location)
self.assertIsInstance(item, xmodule.seq_module.SequenceDescriptor)
def test_course_index_view_with_course(self): def test_course_index_view_with_course(self):
"""Test viewing the index page with an existing course""" """Test viewing the index page with an existing course"""
# Create a course so there is something to view CourseFactory.create(display_name='Robot Super Educational Course')
resp = self.client.post(reverse('create_new_course'), self.course_data)
resp = self.client.get(reverse('index')) resp = self.client.get(reverse('index'))
self.assertContains(resp, self.assertContains(resp,
'<span class="class-name">Robot Super Course</span>', '<span class="class-name">Robot Super Educational Course</span>',
status_code=200, status_code=200,
html=True) html=True)
def test_course_overview_view_with_course(self): def test_course_overview_view_with_course(self):
"""Test viewing the course overview page with an existing course""" """Test viewing the course overview page with an existing course"""
# Create a course so there is something to view CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course')
resp = self.client.post(reverse('create_new_course'), self.course_data)
data = { data = {
'org': 'MITx', 'org': 'MITx',
...@@ -300,8 +302,15 @@ class ContentStoreTest(TestCase): ...@@ -300,8 +302,15 @@ class ContentStoreTest(TestCase):
def test_clone_item(self): def test_clone_item(self):
"""Test cloning an item. E.g. creating a new section""" """Test cloning an item. E.g. creating a new section"""
resp = self.client.post(reverse('create_new_course'), self.course_data) CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course')
resp = self.client.post(reverse('clone_item'), self.section_data)
section_data = {
'parent_location' : 'i4x://MITx/999/course/Robot_Super_Course',
'template' : 'i4x://edx/templates/chapter/Empty',
'display_name': 'Section One',
}
resp = self.client.post(reverse('clone_item'), section_data)
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
data = parse_json(resp) data = parse_json(resp)
...@@ -323,3 +332,4 @@ class ContentStoreTest(TestCase): ...@@ -323,3 +332,4 @@ class ContentStoreTest(TestCase):
def test_edit_unit_full(self): def test_edit_unit_full(self):
self.check_edit_unit('full') self.check_edit_unit('full')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment