Commit e43dbd29 by Calen Pennington

Merge pull request #487 from edx/cale/lms-xblock-student-view

Cale/lms xblock student view
parents b27857ca 99f9894f
...@@ -12,6 +12,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError, InsufficientSpecif ...@@ -12,6 +12,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError, InsufficientSpecif
from xblock.core import XBlock, Scope, String, Integer, Float, ModelType from xblock.core import XBlock, Scope, String, Integer, Float, ModelType
from xblock.fragment import Fragment from xblock.fragment import Fragment
from xblock.runtime import Runtime
from xmodule.modulestore.locator import BlockUsageLocator from xmodule.modulestore.locator import BlockUsageLocator
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -870,7 +871,7 @@ class XMLParsingSystem(DescriptorSystem): ...@@ -870,7 +871,7 @@ class XMLParsingSystem(DescriptorSystem):
self.policy = policy self.policy = policy
class ModuleSystem(object): class ModuleSystem(Runtime):
''' '''
This is an abstraction such that x_modules can function independent This is an abstraction such that x_modules can function independent
of the courseware (e.g. import into other types of courseware, LMS, of the courseware (e.g. import into other types of courseware, LMS,
......
...@@ -163,7 +163,7 @@ def get_course_about_section(course, section_key): ...@@ -163,7 +163,7 @@ def get_course_about_section(course, section_key):
html = '' html = ''
if about_module is not None: if about_module is not None:
html = about_module.get_html() html = about_module.runtime.render(about_module, None, 'student_view').content
return html return html
...@@ -211,7 +211,7 @@ def get_course_info_section(request, course, section_key): ...@@ -211,7 +211,7 @@ def get_course_info_section(request, course, section_key):
html = '' html = ''
if info_module is not None: if info_module is not None:
html = info_module.get_html() html = info_module.runtime.render(info_module, None, 'student_view').content
return html return html
......
...@@ -371,6 +371,6 @@ def get_static_tab_contents(request, course, tab): ...@@ -371,6 +371,6 @@ def get_static_tab_contents(request, course, tab):
html = '' html = ''
if tab_module is not None: if tab_module is not None:
html = tab_module.get_html() html = tab_module.runtime.render(tab_module, None, 'student_view').content
return html return html
...@@ -77,13 +77,15 @@ class BaseTestXmodule(ModuleStoreTestCase): ...@@ -77,13 +77,15 @@ class BaseTestXmodule(ModuleStoreTestCase):
data=self.DATA data=self.DATA
) )
system = get_test_system() self.runtime = get_test_system()
system.render_template = lambda template, context: context # Allow us to assert that the template was called in the same way from
# different code paths while maintaining the type returned by render_template
self.runtime.render_template = lambda template, context: unicode((template, sorted(context.items())))
model_data = {'location': self.item_descriptor.location} model_data = {'location': self.item_descriptor.location}
model_data.update(self.MODEL_DATA) model_data.update(self.MODEL_DATA)
self.item_module = self.item_descriptor.module_class( self.item_module = self.item_descriptor.module_class(
system, self.item_descriptor, model_data self.runtime, self.item_descriptor, model_data
) )
self.item_url = Location(self.item_module.location).url() self.item_url = Location(self.item_module.location).url()
......
""" """
Test for lms courseware app, module render unit Test for lms courseware app, module render unit
""" """
from mock import MagicMock, patch from mock import MagicMock, patch, Mock
import json import json
from django.http import Http404, HttpResponse from django.http import Http404, HttpResponse
...@@ -12,8 +12,10 @@ from django.test.client import RequestFactory ...@@ -12,8 +12,10 @@ from django.test.client import RequestFactory
from django.test.utils import override_settings from django.test.utils import override_settings
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.factories import ItemFactory, CourseFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
import courseware.module_render as render import courseware.module_render as render
from courseware.tests.tests import LoginEnrollmentTestCase from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_MONGO_MODULESTORE
from courseware.model_data import ModelDataCache from courseware.model_data import ModelDataCache
from modulestore_config import TEST_DATA_XML_MODULESTORE from modulestore_config import TEST_DATA_XML_MODULESTORE
...@@ -49,8 +51,10 @@ class ModuleRenderTestCase(LoginEnrollmentTestCase): ...@@ -49,8 +51,10 @@ class ModuleRenderTestCase(LoginEnrollmentTestCase):
dispatch=self.dispatch)) dispatch=self.dispatch))
def test_get_module(self): def test_get_module(self):
self.assertIsNone(render.get_module('dummyuser', None, self.assertEqual(
'invalid location', None, None)) None,
render.get_module('dummyuser', None, 'invalid location', None, None)
)
def test_module_render_with_jump_to_id(self): def test_module_render_with_jump_to_id(self):
""" """
...@@ -230,7 +234,8 @@ class TestTOC(TestCase): ...@@ -230,7 +234,8 @@ class TestTOC(TestCase):
'url_name': 'secret:magic', 'display_name': 'secret:magic'}]) 'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, None, model_data_cache) actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, None, model_data_cache)
assert reduce(lambda x, y: x and (y in actual), expected, True) for toc_section in expected:
self.assertIn(toc_section, actual)
def test_toc_toy_from_section(self): def test_toc_toy_from_section(self):
chapter = 'Overview' chapter = 'Overview'
...@@ -257,4 +262,109 @@ class TestTOC(TestCase): ...@@ -257,4 +262,109 @@ class TestTOC(TestCase):
'url_name': 'secret:magic', 'display_name': 'secret:magic'}]) 'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, section, model_data_cache) actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, section, model_data_cache)
assert reduce(lambda x, y: x and (y in actual), expected, True) for toc_section in expected:
self.assertIn(toc_section, actual)
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class TestHtmlModifiers(ModuleStoreTestCase):
"""
Tests to verify that standard modifications to the output of XModule/XBlock
student_view are taking place
"""
def setUp(self):
self.user = UserFactory.create()
self.request = RequestFactory().get('/')
self.request.user = self.user
self.request.session = {}
self.course = CourseFactory.create()
self.content_string = '<p>This is the content<p>'
self.rewrite_link = '<a href="/static/foo/content">Test rewrite</a>'
self.course_link = '<a href="/course/bar/content">Test course rewrite</a>'
self.descriptor = ItemFactory.create(
category='html',
data=self.content_string + self.rewrite_link + self.course_link
)
self.location = self.descriptor.location
self.model_data_cache = ModelDataCache.cache_for_descriptor_descendents(
self.course.id,
self.user,
self.descriptor
)
def test_xmodule_display_wrapper_enabled(self):
module = render.get_module(
self.user,
self.request,
self.location,
self.model_data_cache,
self.course.id,
wrap_xmodule_display=True,
)
result_fragment = module.runtime.render(module, None, 'student_view')
self.assertIn('section class="xmodule_display xmodule_HtmlModule"', result_fragment.content)
def test_xmodule_display_wrapper_disabled(self):
module = render.get_module(
self.user,
self.request,
self.location,
self.model_data_cache,
self.course.id,
wrap_xmodule_display=False,
)
result_fragment = module.runtime.render(module, None, 'student_view')
self.assertNotIn('section class="xmodule_display xmodule_HtmlModule"', result_fragment.content)
def test_static_link_rewrite(self):
module = render.get_module(
self.user,
self.request,
self.location,
self.model_data_cache,
self.course.id,
)
result_fragment = module.runtime.render(module, None, 'student_view')
self.assertIn(
'/c4x/{org}/{course}/asset/foo_content'.format(
org=self.course.location.org,
course=self.course.location.course,
),
result_fragment.content
)
def test_course_link_rewrite(self):
module = render.get_module(
self.user,
self.request,
self.location,
self.model_data_cache,
self.course.id,
)
result_fragment = module.runtime.render(module, None, 'student_view')
self.assertIn(
'/courses/{course_id}/bar/content'.format(
course_id=self.course.id
),
result_fragment.content
)
@patch('courseware.module_render.has_access', Mock(return_value=True))
def test_histogram(self):
module = render.get_module(
self.user,
self.request,
self.location,
self.model_data_cache,
self.course.id,
)
result_fragment = module.runtime.render(module, None, 'student_view')
self.assertIn(
'Staff Debug',
result_fragment.content
)
...@@ -34,9 +34,7 @@ class TestVideo(BaseTestXmodule): ...@@ -34,9 +34,7 @@ class TestVideo(BaseTestXmodule):
def test_videoalpha_constructor(self): def test_videoalpha_constructor(self):
"""Make sure that all parameters extracted correclty from xml""" """Make sure that all parameters extracted correclty from xml"""
# `get_html` return only context, cause we fragment = self.runtime.render(self.item_module, None, 'student_view')
# overwrite `system.render_template`
context = self.item_module.get_html()
expected_context = { expected_context = {
'data_dir': getattr(self, 'data_dir', None), 'data_dir': getattr(self, 'data_dir', None),
'caption_asset_path': '/c4x/MITx/999/asset/subs_', 'caption_asset_path': '/c4x/MITx/999/asset/subs_',
...@@ -51,7 +49,7 @@ class TestVideo(BaseTestXmodule): ...@@ -51,7 +49,7 @@ class TestVideo(BaseTestXmodule):
'youtube_streams': self.item_module.youtube_streams, 'youtube_streams': self.item_module.youtube_streams,
'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True) 'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True)
} }
self.assertDictEqual(context, expected_context) self.assertEqual(fragment.content, self.runtime.render_template('videoalpha.html', expected_context))
class TestVideoNonYouTube(TestVideo): class TestVideoNonYouTube(TestVideo):
...@@ -78,9 +76,7 @@ class TestVideoNonYouTube(TestVideo): ...@@ -78,9 +76,7 @@ class TestVideoNonYouTube(TestVideo):
the template generates an empty string for the YouTube streams. the template generates an empty string for the YouTube streams.
""" """
# `get_html` return only context, cause we fragment = self.runtime.render(self.item_module, None, 'student_view')
# overwrite `system.render_template`
context = self.item_module.get_html()
expected_context = { expected_context = {
'data_dir': getattr(self, 'data_dir', None), 'data_dir': getattr(self, 'data_dir', None),
'caption_asset_path': '/c4x/MITx/999/asset/subs_', 'caption_asset_path': '/c4x/MITx/999/asset/subs_',
...@@ -95,4 +91,4 @@ class TestVideoNonYouTube(TestVideo): ...@@ -95,4 +91,4 @@ class TestVideoNonYouTube(TestVideo):
'youtube_streams': '', 'youtube_streams': '',
'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True) 'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True)
} }
self.assertDictEqual(context, expected_context) self.assertEqual(fragment.content, self.runtime.render_template('videoalpha.html', expected_context))
...@@ -104,10 +104,9 @@ class VideoAlphaModuleUnitTest(unittest.TestCase): ...@@ -104,10 +104,9 @@ class VideoAlphaModuleUnitTest(unittest.TestCase):
def test_videoalpha_constructor(self): def test_videoalpha_constructor(self):
"""Make sure that all parameters extracted correclty from xml""" """Make sure that all parameters extracted correclty from xml"""
module = VideoAlphaFactory.create() module = VideoAlphaFactory.create()
module.runtime.render_template = lambda template, context: unicode((template, sorted(context.items())))
# `get_html` return only context, cause we fragment = module.runtime.render(module, None, 'student_view')
# overwrite `system.render_template`
context = module.get_html()
expected_context = { expected_context = {
'caption_asset_path': '/static/subs/', 'caption_asset_path': '/static/subs/',
'sub': module.sub, 'sub': module.sub,
...@@ -122,7 +121,7 @@ class VideoAlphaModuleUnitTest(unittest.TestCase): ...@@ -122,7 +121,7 @@ class VideoAlphaModuleUnitTest(unittest.TestCase):
'track': module.track, 'track': module.track,
'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True) 'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True)
} }
self.assertDictEqual(context, expected_context) self.assertEqual(fragment.content, module.runtime.render_template('videoalpha.html', expected_context))
self.assertDictEqual( self.assertDictEqual(
json.loads(module.get_instance_state()), json.loads(module.get_instance_state()),
......
...@@ -242,9 +242,7 @@ class TestWordCloud(BaseTestXmodule): ...@@ -242,9 +242,7 @@ class TestWordCloud(BaseTestXmodule):
def test_word_cloud_constructor(self): def test_word_cloud_constructor(self):
"""Make sure that all parameters extracted correclty from xml""" """Make sure that all parameters extracted correclty from xml"""
# `get_html` return only context, cause we fragment = self.runtime.render(self.item_module, None, 'student_view')
# overwrite `system.render_template`
context = self.item_module.get_html()
expected_context = { expected_context = {
'ajax_url': self.item_module.system.ajax_url, 'ajax_url': self.item_module.system.ajax_url,
...@@ -253,4 +251,4 @@ class TestWordCloud(BaseTestXmodule): ...@@ -253,4 +251,4 @@ class TestWordCloud(BaseTestXmodule):
'num_inputs': 5, # default value 'num_inputs': 5, # default value
'submitted': False # default value 'submitted': False # default value
} }
self.assertDictEqual(context, expected_context) self.assertEqual(fragment.content, self.runtime.render_template('word_cloud.html', expected_context))
...@@ -400,7 +400,7 @@ def index(request, course_id, chapter=None, section=None, ...@@ -400,7 +400,7 @@ def index(request, course_id, chapter=None, section=None,
# add in the appropriate timer information to the rendering context: # add in the appropriate timer information to the rendering context:
context.update(check_for_active_timelimit_module(request, course_id, course)) context.update(check_for_active_timelimit_module(request, course_id, course))
context['content'] = section_module.get_html() context['content'] = section_module.runtime.render(section_module, None, 'student_view').content
else: else:
# section is none, so display a message # section is none, so display a message
prev_section = get_current_child(chapter_module) prev_section = get_current_child(chapter_module)
......
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