Merge pull request #10352 from edx/vkaracic/TNL-2896
[TNL-2896] Colons in problem names
This commit is contained in:
205
openedx/core/lib/tests/test_xblock_utils.py
Normal file
205
openedx/core/lib/tests/test_xblock_utils.py
Normal file
@@ -0,0 +1,205 @@
|
||||
"""
|
||||
Tests for xblock_utils.py
|
||||
"""
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import ddt
|
||||
import uuid
|
||||
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
from courseware.models import StudentModule # pylint: disable=import-error
|
||||
from lms.djangoapps.lms_xblock.runtime import quote_slashes
|
||||
from xblock.fragment import Fragment
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
from openedx.core.lib.xblock_utils import (
|
||||
wrap_fragment,
|
||||
request_token,
|
||||
wrap_xblock,
|
||||
replace_jump_to_id_urls,
|
||||
replace_course_urls,
|
||||
replace_static_urls,
|
||||
grade_histogram,
|
||||
sanitize_html_id
|
||||
)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestXblockUtils(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Tests for xblock utility functions.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestXblockUtils, cls).setUpClass()
|
||||
cls.course_mongo = CourseFactory.create(
|
||||
default_store=ModuleStoreEnum.Type.mongo,
|
||||
org='TestX',
|
||||
number='TS01',
|
||||
run='2015'
|
||||
)
|
||||
cls.course_split = CourseFactory.create(
|
||||
default_store=ModuleStoreEnum.Type.split,
|
||||
org='TestX',
|
||||
number='TS02',
|
||||
run='2015'
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super(TestXblockUtils, self).setUp()
|
||||
|
||||
def create_fragment(self, content=None):
|
||||
"""
|
||||
Create a fragment.
|
||||
"""
|
||||
fragment = Fragment(content)
|
||||
fragment.add_css('body {background-color:red;}')
|
||||
fragment.add_javascript('alert("Hi!");')
|
||||
return fragment
|
||||
|
||||
def test_wrap_fragment(self):
|
||||
"""
|
||||
Verify that wrap_fragment adds new content.
|
||||
"""
|
||||
new_content = '<p>New Content<p>'
|
||||
fragment = self.create_fragment()
|
||||
wrapped_fragment = wrap_fragment(fragment, new_content)
|
||||
self.assertEqual('<p>New Content<p>', wrapped_fragment.content)
|
||||
self.assertEqual('body {background-color:red;}', wrapped_fragment.resources[0].data)
|
||||
self.assertEqual('alert("Hi!");', wrapped_fragment.resources[1].data)
|
||||
|
||||
def test_request_token(self):
|
||||
"""
|
||||
Verify that a proper token is returned.
|
||||
"""
|
||||
request_with_token = RequestFactory().get('/')
|
||||
request_with_token._xblock_token = '123' # pylint: disable=protected-access
|
||||
token = request_token(request_with_token)
|
||||
self.assertEqual(token, '123')
|
||||
|
||||
request_without_token = RequestFactory().get('/')
|
||||
token = request_token(request_without_token)
|
||||
# Test to see if the token is an uuid1 hex value
|
||||
test_uuid = uuid.UUID(token, version=1)
|
||||
self.assertEqual(token, test_uuid.hex)
|
||||
|
||||
@ddt.data(
|
||||
('course_mongo', 'data-usage-id="i4x:;_;_TestX;_TS01;_course;_2015"'),
|
||||
('course_split', 'data-usage-id="block-v1:TestX+TS02+2015+type@course+block@course"')
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_wrap_xblock(self, course_id, data_usage_id):
|
||||
"""
|
||||
Verify that new content is added and the resources are the same.
|
||||
"""
|
||||
fragment = self.create_fragment(u"<h1>Test!</h1>")
|
||||
course = getattr(self, course_id)
|
||||
test_wrap_output = wrap_xblock(
|
||||
runtime_class='TestRuntime',
|
||||
block=course,
|
||||
view='baseview',
|
||||
frag=fragment,
|
||||
context=None,
|
||||
usage_id_serializer=lambda usage_id: quote_slashes(unicode(usage_id)),
|
||||
request_token=uuid.uuid1().get_hex()
|
||||
)
|
||||
self.assertIsInstance(test_wrap_output, Fragment)
|
||||
self.assertIn('xblock-baseview', test_wrap_output.content)
|
||||
self.assertIn('data-runtime-class="TestRuntime"', test_wrap_output.content)
|
||||
self.assertIn(data_usage_id, test_wrap_output.content)
|
||||
self.assertIn('<h1>Test!</h1>', test_wrap_output.content)
|
||||
self.assertEqual(test_wrap_output.resources[0].data, u'body {background-color:red;}')
|
||||
self.assertEqual(test_wrap_output.resources[1].data, 'alert("Hi!");')
|
||||
|
||||
@ddt.data('course_mongo', 'course_split')
|
||||
def test_replace_jump_to_id_urls(self, course_id):
|
||||
"""
|
||||
Verify that the jump-to URL has been replaced.
|
||||
"""
|
||||
course = getattr(self, course_id)
|
||||
test_replace = replace_jump_to_id_urls(
|
||||
course_id=course.id,
|
||||
jump_to_id_base_url='/base_url/',
|
||||
block=course,
|
||||
view='baseview',
|
||||
frag=Fragment('<a href="/jump_to_id/id">'),
|
||||
context=None
|
||||
)
|
||||
self.assertIsInstance(test_replace, Fragment)
|
||||
self.assertEqual(test_replace.content, '<a href="/base_url/id">')
|
||||
|
||||
@ddt.data(
|
||||
('course_mongo', '<a href="/courses/TestX/TS01/2015/id">'),
|
||||
('course_split', '<a href="/courses/course-v1:TestX+TS02+2015/id">')
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_replace_course_urls(self, course_id, anchor_tag):
|
||||
"""
|
||||
Verify that the course URL has been replaced.
|
||||
"""
|
||||
course = getattr(self, course_id)
|
||||
test_replace = replace_course_urls(
|
||||
course_id=course.id,
|
||||
block=course,
|
||||
view='baseview',
|
||||
frag=Fragment('<a href="/course/id">'),
|
||||
context=None
|
||||
)
|
||||
self.assertIsInstance(test_replace, Fragment)
|
||||
self.assertEqual(test_replace.content, anchor_tag)
|
||||
|
||||
@ddt.data(
|
||||
('course_mongo', '<a href="/c4x/TestX/TS01/asset/id">'),
|
||||
('course_split', '<a href="/asset-v1:TestX+TS02+2015+type@asset+block/id">')
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_replace_static_urls(self, course_id, anchor_tag):
|
||||
"""
|
||||
Verify that the static URL has been replaced.
|
||||
"""
|
||||
course = getattr(self, course_id)
|
||||
test_replace = replace_static_urls(
|
||||
data_dir=None,
|
||||
course_id=course.id,
|
||||
block=course,
|
||||
view='baseview',
|
||||
frag=Fragment('<a href="/static/id">'),
|
||||
context=None
|
||||
)
|
||||
self.assertIsInstance(test_replace, Fragment)
|
||||
self.assertEqual(test_replace.content, anchor_tag)
|
||||
|
||||
@ddt.data('course_mongo', 'course_split')
|
||||
def test_grade_histogram(self, course_id):
|
||||
"""
|
||||
Verify that a histogram has been created.
|
||||
"""
|
||||
course = getattr(self, course_id)
|
||||
usage_key = course.id.make_usage_key('problem', 'first_problem')
|
||||
StudentModule.objects.create(
|
||||
student_id=1,
|
||||
grade=100,
|
||||
module_state_key=usage_key
|
||||
)
|
||||
StudentModule.objects.create(
|
||||
student_id=2,
|
||||
grade=50,
|
||||
module_state_key=usage_key
|
||||
)
|
||||
|
||||
grades = grade_histogram(usage_key)
|
||||
self.assertEqual(grades[0], (50.0, 1))
|
||||
self.assertEqual(grades[1], (100.0, 1))
|
||||
|
||||
def test_sanitize_html_id(self):
|
||||
"""
|
||||
Verify that colons and dashes are replaced.
|
||||
"""
|
||||
dirty_string = 'I:have-un:allowed_characters'
|
||||
clean_string = sanitize_html_id(dirty_string)
|
||||
|
||||
self.assertEqual(clean_string, 'I_have_un_allowed_characters')
|
||||
@@ -5,9 +5,10 @@ Functions that can are used to modify XBlock fragments for use in the LMS and St
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import markupsafe
|
||||
import re
|
||||
import static_replace
|
||||
import uuid
|
||||
import markupsafe
|
||||
from lxml import html, etree
|
||||
from contracts import contract
|
||||
|
||||
@@ -145,7 +146,7 @@ def wrap_xblock(
|
||||
def replace_jump_to_id_urls(course_id, jump_to_id_base_url, block, view, frag, context): # pylint: disable=unused-argument
|
||||
"""
|
||||
This will replace a link between courseware in the format
|
||||
/jump_to/<id> with a URL for a page that will correctly redirect
|
||||
/jump_to_id/<id> with a URL for a page that will correctly redirect
|
||||
This is similar to replace_course_urls, but much more flexible and
|
||||
durable for Studio authored courses. See more comments in static_replace.replace_jump_to_urls
|
||||
|
||||
@@ -156,7 +157,7 @@ def replace_jump_to_id_urls(course_id, jump_to_id_base_url, block, view, frag, c
|
||||
the end of this URL at re-write time
|
||||
|
||||
output: a new :class:`~xblock.fragment.Fragment` that modifies `frag` with
|
||||
content that has been update with /jump_to links replaced
|
||||
content that has been update with /jump_to_id links replaced
|
||||
"""
|
||||
return wrap_fragment(frag, static_replace.replace_jump_to_id_urls(frag.content, course_id, jump_to_id_base_url))
|
||||
|
||||
@@ -211,6 +212,14 @@ def grade_histogram(module_id):
|
||||
return grades
|
||||
|
||||
|
||||
def sanitize_html_id(html_id):
|
||||
"""
|
||||
Template uses element_id in js function names, so can't allow dashes and colons.
|
||||
"""
|
||||
sanitized_html_id = re.sub(r'[:-]', '_', html_id)
|
||||
return sanitized_html_id
|
||||
|
||||
|
||||
@contract(user=User, has_instructor_access=bool, block=XBlock, view=basestring, frag=Fragment, context="dict|None")
|
||||
def add_staff_markup(user, has_instructor_access, disable_staff_debug_info, block, view, frag, context): # pylint: disable=unused-argument
|
||||
"""
|
||||
@@ -299,8 +308,7 @@ def add_staff_markup(user, has_instructor_access, disable_staff_debug_info, bloc
|
||||
'source_file': source_file,
|
||||
'source_url': '%s/%s/tree/master/%s' % (giturl, data_dir, source_file),
|
||||
'category': str(block.__class__.__name__),
|
||||
# Template uses element_id in js function names, so can't allow dashes
|
||||
'element_id': block.location.html_id().replace('-', '_'),
|
||||
'element_id': sanitize_html_id(block.location.html_id()),
|
||||
'edit_link': edit_link,
|
||||
'user': user,
|
||||
'xqa_server': settings.FEATURES.get('XQA_SERVER', "http://your_xqa_server.com"),
|
||||
|
||||
Reference in New Issue
Block a user