Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
6ff762b6
Commit
6ff762b6
authored
Sep 19, 2016
by
muhammad-ammar
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
allow html inside label and descriptions
TNL-5557
parent
2b80c619
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
254 additions
and
133 deletions
+254
-133
common/lib/capa/capa/capa_problem.py
+8
-12
common/lib/capa/capa/inputtypes.py
+2
-2
common/lib/capa/capa/templates/chemicalequationinput.html
+2
-1
common/lib/capa/capa/templates/choicegroup.html
+4
-3
common/lib/capa/capa/templates/choicetext.html
+2
-1
common/lib/capa/capa/templates/formulaequationinput.html
+1
-1
common/lib/capa/capa/templates/optioninput.html
+2
-1
common/lib/capa/capa/templates/schematicinput.html
+2
-1
common/lib/capa/capa/templates/textline.html
+1
-1
common/lib/capa/capa/tests/test_capa_problem.py
+47
-50
common/lib/capa/capa/tests/test_html_render.py
+1
-1
common/lib/capa/capa/tests/test_input_templates.py
+132
-32
common/lib/capa/capa/tests/test_inputtypes.py
+27
-26
common/lib/capa/capa/tests/test_util.py
+10
-1
common/lib/capa/capa/util.py
+13
-0
No files found.
common/lib/capa/capa/capa_problem.py
View file @
6ff762b6
...
@@ -31,6 +31,8 @@ import capa.responsetypes as responsetypes
...
@@ -31,6 +31,8 @@ import capa.responsetypes as responsetypes
from
capa.util
import
contextualize_text
,
convert_files_to_filenames
from
capa.util
import
contextualize_text
,
convert_files_to_filenames
import
capa.xqueue_interface
as
xqueue_interface
import
capa.xqueue_interface
as
xqueue_interface
from
capa.safe_exec
import
safe_exec
from
capa.safe_exec
import
safe_exec
from
openedx.core.djangolib.markup
import
HTML
from
xmodule.stringify
import
stringify_children
# extra things displayed after "show answers" is pressed
# extra things displayed after "show answers" is pressed
...
@@ -926,7 +928,7 @@ class LoncapaProblem(object):
...
@@ -926,7 +928,7 @@ class LoncapaProblem(object):
group_label_tag
.
tag
=
'p'
group_label_tag
.
tag
=
'p'
group_label_tag
.
set
(
'id'
,
responsetype_id
)
group_label_tag
.
set
(
'id'
,
responsetype_id
)
group_label_tag
.
set
(
'class'
,
'multi-inputs-group-label'
)
group_label_tag
.
set
(
'class'
,
'multi-inputs-group-label'
)
group_label_tag_text
=
group_label_tag
.
text
group_label_tag_text
=
stringify_children
(
group_label_tag
)
for
inputfield
in
inputfields
:
for
inputfield
in
inputfields
:
problem_data
[
inputfield
.
get
(
'id'
)]
=
{
problem_data
[
inputfield
.
get
(
'id'
)]
=
{
...
@@ -938,7 +940,7 @@ class LoncapaProblem(object):
...
@@ -938,7 +940,7 @@ class LoncapaProblem(object):
# Extract label value from <label> tag or label attribute from inside the responsetype
# Extract label value from <label> tag or label attribute from inside the responsetype
responsetype_label_tag
=
response
.
find
(
'label'
)
responsetype_label_tag
=
response
.
find
(
'label'
)
if
responsetype_label_tag
is
not
None
:
if
responsetype_label_tag
is
not
None
:
label
=
responsetype_label_tag
.
text
label
=
stringify_children
(
responsetype_label_tag
)
# store <label> tag containing question text to delete
# store <label> tag containing question text to delete
# it later otherwise question will be rendered twice
# it later otherwise question will be rendered twice
element_to_be_deleted
=
responsetype_label_tag
element_to_be_deleted
=
responsetype_label_tag
...
@@ -950,21 +952,15 @@ class LoncapaProblem(object):
...
@@ -950,21 +952,15 @@ class LoncapaProblem(object):
p_tag
=
response
.
xpath
(
'preceding-sibling::*[1][self::p]'
)
p_tag
=
response
.
xpath
(
'preceding-sibling::*[1][self::p]'
)
if
p_tag
and
p_tag
[
0
]
.
text
==
inputfields
[
0
]
.
attrib
[
'label'
]:
if
p_tag
and
p_tag
[
0
]
.
text
==
inputfields
[
0
]
.
attrib
[
'label'
]:
label
=
p_tag
[
0
]
.
text
label
=
stringify_children
(
p_tag
[
0
])
p_tag_children
=
list
(
p_tag
[
0
])
if
len
(
p_tag_children
)
==
0
:
element_to_be_deleted
=
p_tag
[
0
]
element_to_be_deleted
=
p_tag
[
0
]
else
:
else
:
# Delete the text from the p-tag, but leave the children.
p_tag
[
0
]
.
text
=
''
else
:
# In this case the problems don't have tag or label attribute inside the responsetype
# In this case the problems don't have tag or label attribute inside the responsetype
# so we will get the first preceding label tag w.r.t to this responsetype.
# so we will get the first preceding label tag w.r.t to this responsetype.
# This will take care of those multi-question problems that are not using --- in their markdown.
# This will take care of those multi-question problems that are not using --- in their markdown.
label_tag
=
response
.
xpath
(
'preceding-sibling::*[1][self::label]'
)
label_tag
=
response
.
xpath
(
'preceding-sibling::*[1][self::label]'
)
if
label_tag
:
if
label_tag
:
label
=
label_tag
[
0
]
.
text
label
=
stringify_children
(
label_tag
[
0
])
element_to_be_deleted
=
label_tag
[
0
]
element_to_be_deleted
=
label_tag
[
0
]
# delete label or p element only if inputtype is fully accessible
# delete label or p element only if inputtype is fully accessible
...
@@ -978,11 +974,11 @@ class LoncapaProblem(object):
...
@@ -978,11 +974,11 @@ class LoncapaProblem(object):
for
description
in
description_tags
:
for
description
in
description_tags
:
descriptions
[
descriptions
[
"description_
%
s_
%
i"
%
(
responsetype_id
,
description_id
)
"description_
%
s_
%
i"
%
(
responsetype_id
,
description_id
)
]
=
description
.
text
]
=
HTML
(
stringify_children
(
description
))
response
.
remove
(
description
)
response
.
remove
(
description
)
description_id
+=
1
description_id
+=
1
problem_data
[
inputfields
[
0
]
.
get
(
'id'
)]
=
{
problem_data
[
inputfields
[
0
]
.
get
(
'id'
)]
=
{
'label'
:
label
.
strip
(
)
if
label
else
''
,
'label'
:
HTML
(
label
.
strip
()
)
if
label
else
''
,
'descriptions'
:
descriptions
'descriptions'
:
descriptions
}
}
common/lib/capa/capa/inputtypes.py
View file @
6ff762b6
...
@@ -322,14 +322,14 @@ class InputTypeBase(object):
...
@@ -322,14 +322,14 @@ class InputTypeBase(object):
'msg'
:
self
.
msg
,
'msg'
:
self
.
msg
,
'response_data'
:
self
.
response_data
,
'response_data'
:
self
.
response_data
,
'STATIC_URL'
:
self
.
capa_system
.
STATIC_URL
,
'STATIC_URL'
:
self
.
capa_system
.
STATIC_URL
,
'describedby'
:
''
,
'describedby
_html
'
:
''
,
}
}
# Don't add aria-describedby attribute if there are no descriptions
# Don't add aria-describedby attribute if there are no descriptions
if
self
.
response_data
.
get
(
'descriptions'
):
if
self
.
response_data
.
get
(
'descriptions'
):
description_ids
=
' '
.
join
(
self
.
response_data
.
get
(
'descriptions'
)
.
keys
())
description_ids
=
' '
.
join
(
self
.
response_data
.
get
(
'descriptions'
)
.
keys
())
context
.
update
(
context
.
update
(
{
'describedby'
:
'aria-describedby="{}"'
.
format
(
description_ids
)}
{
'describedby
_html
'
:
'aria-describedby="{}"'
.
format
(
description_ids
)}
)
)
context
.
update
(
context
.
update
(
...
...
common/lib/capa/capa/templates/chemicalequationinput.html
View file @
6ff762b6
<
%!
from
capa
.
util
import
remove_markup
%
>
<div
id=
"chemicalequationinput_${id}"
class=
"chemicalequationinput"
>
<div
id=
"chemicalequationinput_${id}"
class=
"chemicalequationinput"
>
<div
class=
"script_placeholder"
data-src=
"${previewer}"
/>
<div
class=
"script_placeholder"
data-src=
"${previewer}"
/>
<div
class=
"${status.classname}"
id=
"status_${id}"
>
<div
class=
"${status.classname}"
id=
"status_${id}"
>
<input
type=
"text"
name=
"input_${id}"
id=
"input_${id}"
aria-label=
"${re
sponse_data['label']
}"
aria-describedby=
"answer_${id}"
data-input-id=
"${id}"
value=
"${value|h}"
<input
type=
"text"
name=
"input_${id}"
id=
"input_${id}"
aria-label=
"${re
move_markup(response_data['label'])
}"
aria-describedby=
"answer_${id}"
data-input-id=
"${id}"
value=
"${value|h}"
%
if
size:
%
if
size:
size=
"${size}"
size=
"${size}"
%
endif
%
endif
...
...
common/lib/capa/capa/templates/choicegroup.html
View file @
6ff762b6
<
%
page
expression_filter=
"h"
/>
<
%!
from
openedx
.
core
.
djangolib
.
markup
import
HTML
%
>
<
%!
from
openedx
.
core
.
djangolib
.
markup
import
HTML
%
>
<
%
<
%
def
is_radio_input
(
choice_id
)
:
def
is_radio_input
(
choice_id
)
:
...
@@ -6,7 +7,7 @@
...
@@ -6,7 +7,7 @@
))
))
%
>
%
>
<form
class=
"choicegroup capa_inputtype"
id=
"inputtype_${id}"
>
<form
class=
"choicegroup capa_inputtype"
id=
"inputtype_${id}"
>
<fieldset
${
describedby
}
>
<fieldset
${
HTML
(
describedby_html
)
}
>
% if response_data['label']:
% if response_data['label']:
<legend
id=
"${id}-legend"
class=
"response-fieldset-legend field-group-hd"
>
${response_data['label']}
</legend>
<legend
id=
"${id}-legend"
class=
"response-fieldset-legend field-group-hd"
>
${response_data['label']}
</legend>
% endif
% endif
...
@@ -36,7 +37,7 @@
...
@@ -36,7 +37,7 @@
% endif
% endif
% endif
% endif
class="${label_class}"
class="${label_class}"
${
describedby
}
${
HTML(describedby_html)
}
>
>
<input
type=
"${input_type}"
name=
"input_${id}${name_array_suffix}"
id=
"input_${id}_${choice_id}"
class=
"field-input input-${input_type}"
value=
"${choice_id}"
<input
type=
"${input_type}"
name=
"input_${id}${name_array_suffix}"
id=
"input_${id}_${choice_id}"
class=
"field-input input-${input_type}"
value=
"${choice_id}"
##
If
the
student
selected
this
choice
...
##
If
the
student
selected
this
choice
...
...
@@ -45,7 +46,7 @@
...
@@ -45,7 +46,7 @@
%
elif
input_type
!=
'
radio
'
and
choice_id
in
value:
%
elif
input_type
!=
'
radio
'
and
choice_id
in
value:
checked=
"true"
checked=
"true"
%
endif
%
endif
/>
${
choice_label
}
/>
${
HTML(choice_label)
}
% if is_radio_input(choice_id):
% if is_radio_input(choice_id):
% if status in ('correct', 'partially-correct', 'incorrect') and not show_correctness == 'never':
% if status in ('correct', 'partially-correct', 'incorrect') and not show_correctness == 'never':
...
...
common/lib/capa/capa/templates/choicetext.html
View file @
6ff762b6
<
%!
from
capa
.
util
import
remove_markup
%
>
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%
element_checked =
False
%
>
<
%
element_checked =
False
%
>
% for choice_id, _ in choices:
% for choice_id, _ in choices:
...
@@ -10,7 +11,7 @@
...
@@ -10,7 +11,7 @@
<form
class=
"choicetextgroup capa_inputtype"
id=
"inputtype_${id}"
>
<form
class=
"choicetextgroup capa_inputtype"
id=
"inputtype_${id}"
>
<div
class=
"script_placeholder"
data-src=
"${STATIC_URL}js/capa/choicetextinput.js"
/>
<div
class=
"script_placeholder"
data-src=
"${STATIC_URL}js/capa/choicetextinput.js"
/>
<fieldset
aria-label=
"${re
sponse_data['label']
}"
>
<fieldset
aria-label=
"${re
move_markup(response_data['label'])
}"
>
% for choice_id, choice_description in choices:
% for choice_id, choice_description in choices:
<
%
choice_id =
choice_id
%
>
<
%
choice_id =
choice_id
%
>
<section
id=
"forinput${choice_id}"
<section
id=
"forinput${choice_id}"
...
...
common/lib/capa/capa/templates/formulaequationinput.html
View file @
6ff762b6
...
@@ -11,7 +11,7 @@
...
@@ -11,7 +11,7 @@
% endfor
% endfor
<input
type=
"text"
name=
"input_${id}"
id=
"input_${id}"
<input
type=
"text"
name=
"input_${id}"
id=
"input_${id}"
data-input-id=
"${id}"
value=
"${value}"
data-input-id=
"${id}"
value=
"${value}"
${
describedby
|
n
,
decode
.
utf8
}
${
HTML
(
describedby_html
)
}
%
if
size:
%
if
size:
size=
"${size}"
size=
"${size}"
%
endif
%
endif
...
...
common/lib/capa/capa/templates/optioninput.html
View file @
6ff762b6
<
%
page
expression_filter=
"h"
/>
<
%!
from
openedx
.
core
.
djangolib
.
markup
import
HTML
%
>
<
%!
from
openedx
.
core
.
djangolib
.
markup
import
HTML
%
>
<
%
doinline =
"inline"
if
inline
else
""
%
>
<
%
doinline =
"inline"
if
inline
else
""
%
>
...
@@ -10,7 +11,7 @@
...
@@ -10,7 +11,7 @@
<p
class=
"question-description"
id=
"${description_id}"
>
${description_text}
</p>
<p
class=
"question-description"
id=
"${description_id}"
>
${description_text}
</p>
% endfor
% endfor
<select
name=
"input_${id}"
id=
"input_${id}"
${
describedby
}
>
<select
name=
"input_${id}"
id=
"input_${id}"
${
HTML
(
describedby_html
)
}
>
<option
value=
"option_${id}_dummy_default"
>
${default_option_text}
</option>
<option
value=
"option_${id}_dummy_default"
>
${default_option_text}
</option>
% for option_id, option_description in options:
% for option_id, option_description in options:
<option
value=
"${option_id}"
<option
value=
"${option_id}"
...
...
common/lib/capa/capa/templates/schematicinput.html
View file @
6ff762b6
<
%!
from
capa
.
util
import
remove_markup
%
>
<div>
<div>
<div
class=
"script_placeholder"
data-src=
"${setup_script}"
/>
<div
class=
"script_placeholder"
data-src=
"${setup_script}"
/>
<input
type=
"hidden"
<input
type=
"hidden"
...
@@ -8,7 +9,7 @@
...
@@ -8,7 +9,7 @@
analyses=
"${analyses}"
analyses=
"${analyses}"
name=
"input_${id}"
name=
"input_${id}"
id=
"input_${id}"
id=
"input_${id}"
aria-label=
"${re
sponse_data['label']
}"
aria-label=
"${re
move_markup(response_data['label'])
}"
aria-describedby=
"answer_${id}"
aria-describedby=
"answer_${id}"
value=
"${value|h}"
value=
"${value|h}"
initial_value=
"${initial_value|h}"
initial_value=
"${initial_value|h}"
...
...
common/lib/capa/capa/templates/textline.html
View file @
6ff762b6
...
@@ -23,7 +23,7 @@
...
@@ -23,7 +23,7 @@
% for description_id, description_text in response_data['descriptions'].items():
% for description_id, description_text in response_data['descriptions'].items():
<p
class=
"question-description"
id=
"${description_id}"
>
${description_text}
</p>
<p
class=
"question-description"
id=
"${description_id}"
>
${description_text}
</p>
% endfor
% endfor
<input
type=
"text"
name=
"input_${id}"
id=
"input_${id}"
${
describedby
|
n
,
decode
.
utf8
}
value=
"${value}"
<input
type=
"text"
name=
"input_${id}"
id=
"input_${id}"
${
HTML
(
describedby_html
)
}
value=
"${value}"
%
if
do_math:
%
if
do_math:
class=
"math"
class=
"math"
%
endif
%
endif
...
...
common/lib/capa/capa/tests/test_capa_problem.py
View file @
6ff762b6
...
@@ -13,7 +13,12 @@ from capa.tests.helpers import new_loncapa_problem
...
@@ -13,7 +13,12 @@ from capa.tests.helpers import new_loncapa_problem
class
CAPAProblemTest
(
unittest
.
TestCase
):
class
CAPAProblemTest
(
unittest
.
TestCase
):
""" CAPA problem related tests"""
""" CAPA problem related tests"""
def
test_label_and_description_inside_responsetype
(
self
):
@ddt.unpack
@ddt.data
(
{
'question'
:
'Select the correct synonym of paranoid?'
},
{
'question'
:
'Select the correct <em>synonym</em> of <strong>paranoid</strong>?'
},
)
def
test_label_and_description_inside_responsetype
(
self
,
question
):
"""
"""
Verify that
Verify that
* label is extracted
* label is extracted
...
@@ -25,7 +30,7 @@ class CAPAProblemTest(unittest.TestCase):
...
@@ -25,7 +30,7 @@ class CAPAProblemTest(unittest.TestCase):
xml
=
"""
xml
=
"""
<problem>
<problem>
<choiceresponse>
<choiceresponse>
<label>
Select the correct synonym of paranoid?
</label>
<label>
{question}
</label>
<description>Only the paranoid survive.</description>
<description>Only the paranoid survive.</description>
<checkboxgroup>
<checkboxgroup>
<choice correct="true">over-suspicious</choice>
<choice correct="true">over-suspicious</choice>
...
@@ -33,25 +38,35 @@ class CAPAProblemTest(unittest.TestCase):
...
@@ -33,25 +38,35 @@ class CAPAProblemTest(unittest.TestCase):
</checkboxgroup>
</checkboxgroup>
</choiceresponse>
</choiceresponse>
</problem>
</problem>
"""
"""
.
format
(
question
=
question
)
problem
=
new_loncapa_problem
(
xml
)
problem
=
new_loncapa_problem
(
xml
)
self
.
assertEqual
(
self
.
assertEqual
(
problem
.
problem_data
,
problem
.
problem_data
,
{
{
'1_2_1'
:
'1_2_1'
:
{
{
'label'
:
'Select the correct synonym of paranoid?'
,
'label'
:
question
,
'descriptions'
:
{
'description_1_1_1'
:
'Only the paranoid survive.'
}
'descriptions'
:
{
'description_1_1_1'
:
'Only the paranoid survive.'
}
}
}
}
}
)
)
self
.
assertEqual
(
len
(
problem
.
tree
.
xpath
(
'//label'
)),
0
)
self
.
assertEqual
(
len
(
problem
.
tree
.
xpath
(
'//label'
)),
0
)
def
test_legacy_problem
(
self
):
@ddt.unpack
@ddt.data
(
{
'question'
:
'Once we become predictable, we become ______?'
,
'label_attr'
:
'Once we become predictable, we become ______?'
},
{
'question'
:
'Once we become predictable, we become ______?<img src="img/src"/>'
,
'label_attr'
:
'Once we become predictable, we become ______?'
},
)
def
test_legacy_problem
(
self
,
question
,
label_attr
):
"""
"""
Verify that legacy problem is handled correctly.
Verify that legacy problem is handled correctly.
"""
"""
question
=
"Once we become predictable, we become ______?"
xml
=
"""
xml
=
"""
<problem>
<problem>
<p>Be sure to check your spelling.</p>
<p>Be sure to check your spelling.</p>
...
@@ -60,7 +75,7 @@ class CAPAProblemTest(unittest.TestCase):
...
@@ -60,7 +75,7 @@ class CAPAProblemTest(unittest.TestCase):
<textline label="{}" size="40"/>
<textline label="{}" size="40"/>
</stringresponse>
</stringresponse>
</problem>
</problem>
"""
.
format
(
question
,
question
)
"""
.
format
(
question
,
label_attr
)
problem
=
new_loncapa_problem
(
xml
)
problem
=
new_loncapa_problem
(
xml
)
self
.
assertEqual
(
self
.
assertEqual
(
problem
.
problem_data
,
problem
.
problem_data
,
...
@@ -77,7 +92,18 @@ class CAPAProblemTest(unittest.TestCase):
...
@@ -77,7 +92,18 @@ class CAPAProblemTest(unittest.TestCase):
0
0
)
)
def
test_neither_label_tag_nor_attribute
(
self
):
@ddt.unpack
@ddt.data
(
{
'question1'
:
'People who say they have nothing to ____ almost always do?'
,
'question2'
:
'Select the correct synonym of paranoid?'
},
{
'question1'
:
'<b>People</b> who say they have <mark>nothing</mark> to ____ almost always do?'
,
'question2'
:
'Select the <sup>correct</sup> synonym of <mark>paranoid</mark>?'
},
)
def
test_neither_label_tag_nor_attribute
(
self
,
question1
,
question2
):
"""
"""
Verify that label is extracted correctly.
Verify that label is extracted correctly.
...
@@ -86,8 +112,6 @@ class CAPAProblemTest(unittest.TestCase):
...
@@ -86,8 +112,6 @@ class CAPAProblemTest(unittest.TestCase):
tag and label attribute inside responsetype. But we have a label tag
tag and label attribute inside responsetype. But we have a label tag
before the responsetype.
before the responsetype.
"""
"""
question1
=
'People who say they have nothing to ____ almost always do?'
question2
=
'Select the correct synonym of paranoid?'
xml
=
"""
xml
=
"""
<problem>
<problem>
<p>Be sure to check your spelling.</p>
<p>Be sure to check your spelling.</p>
...
@@ -131,17 +155,19 @@ class CAPAProblemTest(unittest.TestCase):
...
@@ -131,17 +155,19 @@ class CAPAProblemTest(unittest.TestCase):
"""
"""
Verify that multiple descriptions are handled correctly.
Verify that multiple descriptions are handled correctly.
"""
"""
desc1
=
"The problem with trying to be the <em>bad guy</em>, there's always someone <strong>worse</strong>."
desc2
=
"Anyone who looks the world as if it was a game of chess deserves to lose."
xml
=
"""
xml
=
"""
<problem>
<problem>
<p>Be sure to check your spelling.</p>
<p>Be sure to check your spelling.</p>
<stringresponse answer="War" type="ci">
<stringresponse answer="War" type="ci">
<label>___ requires sacrifices.</label>
<label>___ requires sacrifices.</label>
<description>
The problem with trying to be the bad guy, there's always someone worse.
</description>
<description>
{}
</description>
<description>
Anyone who looks the world as if it was a game of chess deserves to lose.
</description>
<description>
{}
</description>
<textline size="40"/>
<textline size="40"/>
</stringresponse>
</stringresponse>
</problem>
</problem>
"""
"""
.
format
(
desc1
,
desc2
)
problem
=
new_loncapa_problem
(
xml
)
problem
=
new_loncapa_problem
(
xml
)
self
.
assertEqual
(
self
.
assertEqual
(
problem
.
problem_data
,
problem
.
problem_data
,
...
@@ -150,8 +176,8 @@ class CAPAProblemTest(unittest.TestCase):
...
@@ -150,8 +176,8 @@ class CAPAProblemTest(unittest.TestCase):
{
{
'label'
:
'___ requires sacrifices.'
,
'label'
:
'___ requires sacrifices.'
,
'descriptions'
:
{
'descriptions'
:
{
'description_1_1_1'
:
"The problem with trying to be the bad guy, there's always someone worse."
,
'description_1_1_1'
:
desc1
,
'description_1_1_2'
:
"Anyone who looks the world as if it was a game of chess deserves to lose."
'description_1_1_2'
:
desc2
}
}
}
}
}
}
...
@@ -298,11 +324,15 @@ class CAPAProblemTest(unittest.TestCase):
...
@@ -298,11 +324,15 @@ class CAPAProblemTest(unittest.TestCase):
1
1
)
)
def
test_multiple_inputtypes
(
self
):
@ddt.unpack
@ddt.data
(
{
'group_label'
:
'Choose the correct color'
},
{
'group_label'
:
'Choose the <b>correct</b> <mark>color</mark>'
},
)
def
test_multiple_inputtypes
(
self
,
group_label
):
"""
"""
Verify that group label and labels for individual inputtypes are extracted correctly.
Verify that group label and labels for individual inputtypes are extracted correctly.
"""
"""
group_label
=
'Choose the correct color'
input1_label
=
'What color is the sky?'
input1_label
=
'What color is the sky?'
input2_label
=
'What color are pine needles?'
input2_label
=
'What color are pine needles?'
xml
=
"""
xml
=
"""
...
@@ -424,39 +454,6 @@ class CAPAProblemTest(unittest.TestCase):
...
@@ -424,39 +454,6 @@ class CAPAProblemTest(unittest.TestCase):
self
.
assert_question_tag
(
question1
,
question2
,
tag
=
'label'
,
label_attr
=
False
)
self
.
assert_question_tag
(
question1
,
question2
,
tag
=
'label'
,
label_attr
=
False
)
self
.
assert_question_tag
(
question1
,
question2
,
tag
=
'p'
,
label_attr
=
True
)
self
.
assert_question_tag
(
question1
,
question2
,
tag
=
'p'
,
label_attr
=
True
)
def
test_question_tag_child_left
(
self
):
"""
If the "old" question tag has children, don't delete the children when
transforming to the new label tag.
"""
xml
=
"""
<problem>
<p>Question<img src='img/src'/></p>
<choiceresponse>
<checkboxgroup label="Question">
<choice correct="true">choice1</choice>
<choice correct="false">choice2</choice>
</checkboxgroup>
</choiceresponse>
</problem>
"""
problem
=
new_loncapa_problem
(
xml
)
self
.
assertEqual
(
problem
.
problem_data
,
{
'1_2_1'
:
{
'label'
:
"Question"
,
'descriptions'
:
{}
}
}
)
# img tag is still present within the paragraph, but p text has been deleted
self
.
assertEqual
(
len
(
problem
.
tree
.
xpath
(
'//p'
)),
1
)
self
.
assertEqual
(
problem
.
tree
.
xpath
(
'//p'
)[
0
]
.
text
,
''
)
self
.
assertEqual
(
len
(
problem
.
tree
.
xpath
(
'//p/img'
)),
1
)
@ddt.ddt
@ddt.ddt
class
CAPAMultiInputProblemTest
(
unittest
.
TestCase
):
class
CAPAMultiInputProblemTest
(
unittest
.
TestCase
):
...
...
common/lib/capa/capa/tests/test_html_render.py
View file @
6ff762b6
...
@@ -186,7 +186,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
...
@@ -186,7 +186,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
'trailing_text'
:
''
,
'trailing_text'
:
''
,
'size'
:
None
,
'size'
:
None
,
'response_data'
:
{
'label'
:
''
,
'descriptions'
:
{}},
'response_data'
:
{
'label'
:
''
,
'descriptions'
:
{}},
'describedby'
:
''
'describedby
_html
'
:
''
}
}
expected_solution_context
=
{
'id'
:
'1_solution_1'
}
expected_solution_context
=
{
'id'
:
'1_solution_1'
}
...
...
common/lib/capa/capa/tests/test_input_templates.py
View file @
6ff762b6
...
@@ -11,6 +11,7 @@ from lxml import etree
...
@@ -11,6 +11,7 @@ from lxml import etree
from
mako.template
import
Template
as
MakoTemplate
from
mako.template
import
Template
as
MakoTemplate
from
mako
import
exceptions
from
mako
import
exceptions
from
capa.inputtypes
import
Status
from
capa.inputtypes
import
Status
from
xmodule.stringify
import
stringify_children
class
TemplateError
(
Exception
):
class
TemplateError
(
Exception
):
...
@@ -31,7 +32,12 @@ class TemplateTestCase(unittest.TestCase):
...
@@ -31,7 +32,12 @@ class TemplateTestCase(unittest.TestCase):
# for example: choicegroup.html
# for example: choicegroup.html
TEMPLATE_NAME
=
None
TEMPLATE_NAME
=
None
DESCRIBEDBY
=
'aria-describedby="desc-1 desc-2"'
DESCRIBEDBY
=
'aria-describedby="desc-1 desc-2"'
DESCRIPTIONS
=
OrderedDict
([(
'desc-1'
,
'description text 1'
),
(
'desc-2'
,
'description text 2'
)])
DESCRIPTIONS
=
OrderedDict
(
[
(
'desc-1'
,
'description text 1'
),
(
'desc-2'
,
'<em>description</em> <mark>text</mark> 2'
)
]
)
DESCRIPTION_IDS
=
' '
.
join
(
DESCRIPTIONS
.
keys
())
DESCRIPTION_IDS
=
' '
.
join
(
DESCRIPTIONS
.
keys
())
RESPONSE_DATA
=
{
RESPONSE_DATA
=
{
'label'
:
'question text 101'
,
'label'
:
'question text 101'
,
...
@@ -48,7 +54,7 @@ class TemplateTestCase(unittest.TestCase):
...
@@ -48,7 +54,7 @@ class TemplateTestCase(unittest.TestCase):
'templates'
,
'templates'
,
self
.
TEMPLATE_NAME
)
self
.
TEMPLATE_NAME
)
with
open
(
self
.
template_path
)
as
f
:
with
open
(
self
.
template_path
)
as
f
:
self
.
template
=
MakoTemplate
(
f
.
read
())
self
.
template
=
MakoTemplate
(
f
.
read
()
,
default_filters
=
[
'decode.utf8'
]
)
self
.
context
=
{}
self
.
context
=
{}
...
@@ -118,25 +124,22 @@ class TemplateTestCase(unittest.TestCase):
...
@@ -118,25 +124,22 @@ class TemplateTestCase(unittest.TestCase):
self
.
assertGreater
(
len
(
element_list
),
0
,
"Could not find element at '
%
s'"
%
str
(
xpath
))
self
.
assertGreater
(
len
(
element_list
),
0
,
"Could not find element at '
%
s'"
%
str
(
xpath
))
if
exact
:
if
exact
:
self
.
assertEqual
(
text
,
element_list
[
0
]
.
text
)
self
.
assertEqual
(
text
,
element_list
[
0
]
.
text
.
strip
()
)
else
:
else
:
self
.
assertIn
(
text
,
element_list
[
0
]
.
text
)
self
.
assertIn
(
text
,
element_list
[
0
]
.
text
.
strip
()
)
def
assert_description
(
self
,
describedby_xpaths
,
descriptions
=
True
):
def
assert_description
(
self
,
describedby_xpaths
):
"""
"""
Verify that descriptions information is correct.
Verify that descriptions information is correct.
Arguments:
Arguments:
describedby_xpaths (list): list of xpaths to check aria-describedby attribute
describedby_xpaths (list): list of xpaths to check aria-describedby attribute
descriptions (bool): tells whether we need to check description <p> tags
"""
"""
xml
=
self
.
render_to_xml
(
self
.
context
)
xml
=
self
.
render_to_xml
(
self
.
context
)
# TODO! This check should be removed once description <p> tags are added into all templates.
if
descriptions
:
# Verify that each description <p> tag has correct id, text and order
# Verify that each description <p> tag has correct id, text and order
descriptions
=
OrderedDict
(
descriptions
=
OrderedDict
(
(
tag
.
get
(
'id'
),
tag
.
text
)
for
tag
in
xml
.
xpath
(
'//p[@class="question-description"]'
)
(
tag
.
get
(
'id'
),
stringify_children
(
tag
)
)
for
tag
in
xml
.
xpath
(
'//p[@class="question-description"]'
)
)
)
self
.
assertEqual
(
self
.
DESCRIPTIONS
,
descriptions
)
self
.
assertEqual
(
self
.
DESCRIPTIONS
,
descriptions
)
...
@@ -157,7 +160,7 @@ class TemplateTestCase(unittest.TestCase):
...
@@ -157,7 +160,7 @@ class TemplateTestCase(unittest.TestCase):
Arguments:
Arguments:
describedby_xpaths (list): list of xpaths to check aria-describedby attribute
describedby_xpaths (list): list of xpaths to check aria-describedby attribute
"""
"""
self
.
context
[
'describedby'
]
=
''
self
.
context
[
'describedby
_html
'
]
=
''
xml
=
self
.
render_to_xml
(
self
.
context
)
xml
=
self
.
render_to_xml
(
self
.
context
)
# for each xpath verify that description_ids are set correctly
# for each xpath verify that description_ids are set correctly
...
@@ -199,6 +202,44 @@ class TemplateTestCase(unittest.TestCase):
...
@@ -199,6 +202,44 @@ class TemplateTestCase(unittest.TestCase):
self
.
context
[
'status'
]
.
display_tooltip
self
.
context
[
'status'
]
.
display_tooltip
)
)
def
assert_label
(
self
,
xpath
=
None
,
aria_label
=
False
):
"""
Verify label is rendered correctly.
Arguments:
xpath (str): xpath expression for label element
aria_label (bool): check aria-label attribute value
"""
labels
=
[
{
'actual'
:
"You see, but you do not observe. The distinction is clear."
,
'expected'
:
"You see, but you do not observe. The distinction is clear."
,
},
{
'actual'
:
"I choose to have <mark>faith</mark> because without that, I have <em>nothing</em>."
,
'expected'
:
"I choose to have faith because without that, I have nothing."
,
}
]
response_data
=
{
'response_data'
:
{
'descriptions'
:
{},
'label'
:
''
}
}
self
.
context
.
update
(
response_data
)
for
label
in
labels
:
self
.
context
[
'response_data'
][
'label'
]
=
label
[
'actual'
]
xml
=
self
.
render_to_xml
(
self
.
context
)
if
aria_label
:
self
.
assert_has_xpath
(
xml
,
"//*[@aria-label='
%
s']"
%
label
[
'expected'
],
self
.
context
)
else
:
element_list
=
xml
.
xpath
(
xpath
)
self
.
assertEqual
(
len
(
element_list
),
1
)
self
.
assertEqual
(
stringify_children
(
element_list
[
0
]),
label
[
'actual'
])
class
ChoiceGroupTemplateTest
(
TemplateTestCase
):
class
ChoiceGroupTemplateTest
(
TemplateTestCase
):
"""
"""
...
@@ -218,7 +259,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
...
@@ -218,7 +259,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
'name_array_suffix'
:
'1'
,
'name_array_suffix'
:
'1'
,
'value'
:
'3'
,
'value'
:
'3'
,
'response_data'
:
self
.
RESPONSE_DATA
,
'response_data'
:
self
.
RESPONSE_DATA
,
'describedby'
:
self
.
DESCRIBEDBY
,
'describedby
_html
'
:
self
.
DESCRIBEDBY
,
}
}
def
test_problem_marked_correct
(
self
):
def
test_problem_marked_correct
(
self
):
...
@@ -429,9 +470,10 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
...
@@ -429,9 +470,10 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
self
.
assert_no_xpath
(
xml
,
"//div[@class='capa_alert']"
,
self
.
context
)
self
.
assert_no_xpath
(
xml
,
"//div[@class='capa_alert']"
,
self
.
context
)
def
test_label
(
self
):
def
test_label
(
self
):
xml
=
self
.
render_to_xml
(
self
.
context
)
"""
xpath
=
"//legend"
Verify label element value rendering.
self
.
assert_has_text
(
xml
,
xpath
,
self
.
context
[
'response_data'
][
'label'
])
"""
self
.
assert_label
(
xpath
=
"//legend"
)
def
test_description
(
self
):
def
test_description
(
self
):
"""
"""
...
@@ -464,7 +506,7 @@ class TextlineTemplateTest(TemplateTestCase):
...
@@ -464,7 +506,7 @@ class TextlineTemplateTest(TemplateTestCase):
'preprocessor'
:
None
,
'preprocessor'
:
None
,
'trailing_text'
:
None
,
'trailing_text'
:
None
,
'response_data'
:
self
.
RESPONSE_DATA
,
'response_data'
:
self
.
RESPONSE_DATA
,
'describedby'
:
self
.
DESCRIBEDBY
,
'describedby
_html
'
:
self
.
DESCRIBEDBY
,
}
}
def
test_section_class
(
self
):
def
test_section_class
(
self
):
...
@@ -487,8 +529,10 @@ class TextlineTemplateTest(TemplateTestCase):
...
@@ -487,8 +529,10 @@ class TextlineTemplateTest(TemplateTestCase):
self
.
assert_status
(
status_div
=
True
)
self
.
assert_status
(
status_div
=
True
)
def
test_label
(
self
):
def
test_label
(
self
):
xml
=
self
.
render_to_xml
(
self
.
context
)
"""
self
.
assert_has_xpath
(
xml
,
"//label[@class='problem-group-label']"
,
self
.
RESPONSE_DATA
[
'label'
])
Verify label element value rendering.
"""
self
.
assert_label
(
xpath
=
"//label[@class='problem-group-label']"
)
def
test_hidden
(
self
):
def
test_hidden
(
self
):
self
.
context
[
'hidden'
]
=
True
self
.
context
[
'hidden'
]
=
True
...
@@ -588,7 +632,7 @@ class FormulaEquationInputTemplateTest(TemplateTestCase):
...
@@ -588,7 +632,7 @@ class FormulaEquationInputTemplateTest(TemplateTestCase):
'reported_status'
:
'REPORTED_STATUS'
,
'reported_status'
:
'REPORTED_STATUS'
,
'trailing_text'
:
None
,
'trailing_text'
:
None
,
'response_data'
:
self
.
RESPONSE_DATA
,
'response_data'
:
self
.
RESPONSE_DATA
,
'describedby'
:
self
.
DESCRIBEDBY
,
'describedby
_html
'
:
self
.
DESCRIBEDBY
,
}
}
def
test_no_size
(
self
):
def
test_no_size
(
self
):
...
@@ -615,6 +659,12 @@ class FormulaEquationInputTemplateTest(TemplateTestCase):
...
@@ -615,6 +659,12 @@ class FormulaEquationInputTemplateTest(TemplateTestCase):
"""
"""
self
.
assert_status
(
status_div
=
True
)
self
.
assert_status
(
status_div
=
True
)
def
test_label
(
self
):
"""
Verify label element value rendering.
"""
self
.
assert_label
(
xpath
=
"//label[@class='problem-group-label']"
)
class
AnnotationInputTemplateTest
(
TemplateTestCase
):
class
AnnotationInputTemplateTest
(
TemplateTestCase
):
"""
"""
...
@@ -802,13 +852,13 @@ class OptionInputTemplateTest(TemplateTestCase):
...
@@ -802,13 +852,13 @@ class OptionInputTemplateTest(TemplateTestCase):
'value'
:
0
,
'value'
:
0
,
'default_option_text'
:
'Select an option'
,
'default_option_text'
:
'Select an option'
,
'response_data'
:
self
.
RESPONSE_DATA
,
'response_data'
:
self
.
RESPONSE_DATA
,
'describedby'
:
self
.
DESCRIBEDBY
,
'describedby
_html
'
:
self
.
DESCRIBEDBY
,
}
}
def
test_select_options
(
self
):
def
test_select_options
(
self
):
# Create options 0-4, and select option 2
# Create options 0-4, and select option 2
self
.
context
[
'options'
]
=
[(
id_num
,
'
<b>Option {0}</b>
'
.
format
(
id_num
))
self
.
context
[
'options'
]
=
[(
id_num
,
'
Option {0}
'
.
format
(
id_num
))
for
id_num
in
range
(
5
)]
for
id_num
in
range
(
5
)]
self
.
context
[
'value'
]
=
2
self
.
context
[
'value'
]
=
2
...
@@ -818,15 +868,12 @@ class OptionInputTemplateTest(TemplateTestCase):
...
@@ -818,15 +868,12 @@ class OptionInputTemplateTest(TemplateTestCase):
xpath
=
"//option[@value='option_2_dummy_default']"
xpath
=
"//option[@value='option_2_dummy_default']"
self
.
assert_has_xpath
(
xml
,
xpath
,
self
.
context
)
self
.
assert_has_xpath
(
xml
,
xpath
,
self
.
context
)
# Should have each of the options, with the correct description
# The description HTML should NOT be escaped
# (that's why we descend into the <b> tag)
for
id_num
in
range
(
5
):
for
id_num
in
range
(
5
):
xpath
=
"//option[@value='{0}']
/b
"
.
format
(
id_num
)
xpath
=
"//option[@value='{0}']"
.
format
(
id_num
)
self
.
assert_has_text
(
xml
,
xpath
,
'Option {0}'
.
format
(
id_num
))
self
.
assert_has_text
(
xml
,
xpath
,
'Option {0}'
.
format
(
id_num
))
# Should have the correct option selected
# Should have the correct option selected
xpath
=
"//option[@selected='true']
/b
"
xpath
=
"//option[@selected='true']"
self
.
assert_has_text
(
xml
,
xpath
,
'Option 2'
)
self
.
assert_has_text
(
xml
,
xpath
,
'Option 2'
)
def
test_status
(
self
):
def
test_status
(
self
):
...
@@ -836,9 +883,10 @@ class OptionInputTemplateTest(TemplateTestCase):
...
@@ -836,9 +883,10 @@ class OptionInputTemplateTest(TemplateTestCase):
self
.
assert_status
(
status_class
=
True
)
self
.
assert_status
(
status_class
=
True
)
def
test_label
(
self
):
def
test_label
(
self
):
xml
=
self
.
render_to_xml
(
self
.
context
)
"""
xpath
=
"//label[@class='problem-group-label']"
Verify label element value rendering.
self
.
assert_has_xpath
(
xml
,
xpath
,
self
.
RESPONSE_DATA
[
'label'
])
"""
self
.
assert_label
(
xpath
=
"//label[@class='problem-group-label']"
)
def
test_description
(
self
):
def
test_description
(
self
):
"""
"""
...
@@ -1077,7 +1125,59 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
...
@@ -1077,7 +1125,59 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
xpath
=
"//div[@class='indicator-container']/span"
xpath
=
"//div[@class='indicator-container']/span"
self
.
assert_no_xpath
(
xml
,
xpath
,
self
.
context
)
self
.
assert_no_xpath
(
xml
,
xpath
,
self
.
context
)
def
test_label
(
self
):
def
test_aria_label
(
self
):
xml
=
self
.
render_to_xml
(
self
.
context
)
"""
xpath
=
"//fieldset[@aria-label='
%
s']"
%
self
.
context
[
'response_data'
][
'label'
]
Verify aria-label attribute rendering.
self
.
assert_has_xpath
(
xml
,
xpath
,
self
.
context
)
"""
self
.
assert_label
(
aria_label
=
True
)
class
ChemicalEquationTemplateTest
(
TemplateTestCase
):
"""Test mako template for `<chemicalequationinput>` input"""
TEMPLATE_NAME
=
'chemicalequationinput.html'
def
setUp
(
self
):
super
(
ChemicalEquationTemplateTest
,
self
)
.
setUp
()
self
.
context
=
{
'id'
:
'1'
,
'status'
:
Status
(
'correct'
),
'previewer'
:
'dummy.js'
,
'value'
:
'101'
,
}
def
test_aria_label
(
self
):
"""
Verify aria-label attribute rendering.
"""
self
.
assert_label
(
aria_label
=
True
)
class
SchematicInputTemplateTest
(
TemplateTestCase
):
"""Test mako template for `<schematic>` input"""
TEMPLATE_NAME
=
'schematicinput.html'
def
setUp
(
self
):
super
(
SchematicInputTemplateTest
,
self
)
.
setUp
()
self
.
context
=
{
'id'
:
'1'
,
'status'
:
Status
(
'correct'
),
'previewer'
:
'dummy.js'
,
'value'
:
'101'
,
'STATIC_URL'
:
'/dummy-static/'
,
'msg'
:
''
,
'initial_value'
:
'two large batteries'
,
'width'
:
'100'
,
'height'
:
'100'
,
'parts'
:
'resistors, capacitors, and flowers'
,
'setup_script'
:
'/dummy-static/js/capa/schematicinput.js'
,
'analyses'
:
'fast, slow, and pink'
,
'submit_analyses'
:
'maybe'
,
}
def
test_aria_label
(
self
):
"""
Verify aria-label attribute rendering.
"""
self
.
assert_label
(
aria_label
=
True
)
common/lib/capa/capa/tests/test_inputtypes.py
View file @
6ff762b6
...
@@ -78,7 +78,7 @@ class OptionInputTest(unittest.TestCase):
...
@@ -78,7 +78,7 @@ class OptionInputTest(unittest.TestCase):
'id'
:
'sky_input'
,
'id'
:
'sky_input'
,
'default_option_text'
:
'Select an option'
,
'default_option_text'
:
'Select an option'
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -147,7 +147,7 @@ class ChoiceGroupTest(unittest.TestCase):
...
@@ -147,7 +147,7 @@ class ChoiceGroupTest(unittest.TestCase):
'submitted_message'
:
'Answer received.'
,
'submitted_message'
:
'Answer received.'
,
'name_array_suffix'
:
expected_suffix
,
# what is this for??
'name_array_suffix'
:
expected_suffix
,
# what is this for??
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -201,7 +201,7 @@ class JavascriptInputTest(unittest.TestCase):
...
@@ -201,7 +201,7 @@ class JavascriptInputTest(unittest.TestCase):
'display_class'
:
display_class
,
'display_class'
:
display_class
,
'problem_state'
:
problem_state
,
'problem_state'
:
problem_state
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -239,7 +239,7 @@ class TextLineTest(unittest.TestCase):
...
@@ -239,7 +239,7 @@ class TextLineTest(unittest.TestCase):
'trailing_text'
:
''
,
'trailing_text'
:
''
,
'preprocessor'
:
None
,
'preprocessor'
:
None
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -278,7 +278,7 @@ class TextLineTest(unittest.TestCase):
...
@@ -278,7 +278,7 @@ class TextLineTest(unittest.TestCase):
'script_src'
:
script
,
'script_src'
:
script
,
},
},
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -323,7 +323,7 @@ class TextLineTest(unittest.TestCase):
...
@@ -323,7 +323,7 @@ class TextLineTest(unittest.TestCase):
'trailing_text'
:
expected_text
,
'trailing_text'
:
expected_text
,
'preprocessor'
:
None
,
'preprocessor'
:
None
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -366,7 +366,7 @@ class FileSubmissionTest(unittest.TestCase):
...
@@ -366,7 +366,7 @@ class FileSubmissionTest(unittest.TestCase):
'allowed_files'
:
'["runme.py", "nooooo.rb", "ohai.java"]'
,
'allowed_files'
:
'["runme.py", "nooooo.rb", "ohai.java"]'
,
'required_files'
:
'["cookies.py"]'
,
'required_files'
:
'["cookies.py"]'
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -422,7 +422,7 @@ class CodeInputTest(unittest.TestCase):
...
@@ -422,7 +422,7 @@ class CodeInputTest(unittest.TestCase):
'tabsize'
:
int
(
tabsize
),
'tabsize'
:
int
(
tabsize
),
'queue_len'
:
'3'
,
'queue_len'
:
'3'
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -484,7 +484,7 @@ class MatlabTest(unittest.TestCase):
...
@@ -484,7 +484,7 @@ class MatlabTest(unittest.TestCase):
'queue_len'
:
'3'
,
'queue_len'
:
'3'
,
'matlab_editor_js'
:
'/dummy-static/js/vendor/CodeMirror/octave.js'
,
'matlab_editor_js'
:
'/dummy-static/js/vendor/CodeMirror/octave.js'
,
'response_data'
:
{},
'response_data'
:
{},
'describedby'
:
''
'describedby
_html
'
:
''
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -519,7 +519,7 @@ class MatlabTest(unittest.TestCase):
...
@@ -519,7 +519,7 @@ class MatlabTest(unittest.TestCase):
'queue_len'
:
'3'
,
'queue_len'
:
'3'
,
'matlab_editor_js'
:
'/dummy-static/js/vendor/CodeMirror/octave.js'
,
'matlab_editor_js'
:
'/dummy-static/js/vendor/CodeMirror/octave.js'
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -553,7 +553,7 @@ class MatlabTest(unittest.TestCase):
...
@@ -553,7 +553,7 @@ class MatlabTest(unittest.TestCase):
'queue_len'
:
'0'
,
'queue_len'
:
'0'
,
'matlab_editor_js'
:
'/dummy-static/js/vendor/CodeMirror/octave.js'
,
'matlab_editor_js'
:
'/dummy-static/js/vendor/CodeMirror/octave.js'
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -587,7 +587,7 @@ class MatlabTest(unittest.TestCase):
...
@@ -587,7 +587,7 @@ class MatlabTest(unittest.TestCase):
'queue_len'
:
'1'
,
'queue_len'
:
'1'
,
'matlab_editor_js'
:
'/dummy-static/js/vendor/CodeMirror/octave.js'
,
'matlab_editor_js'
:
'/dummy-static/js/vendor/CodeMirror/octave.js'
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -705,13 +705,14 @@ class MatlabTest(unittest.TestCase):
...
@@ -705,13 +705,14 @@ class MatlabTest(unittest.TestCase):
textwrap
.
dedent
(
"""
textwrap
.
dedent
(
"""
<div>{
\'
status
\'
: Status(
\'
queued
\'
),
\'
button_enabled
\'
: True,
<div>{
\'
status
\'
: Status(
\'
queued
\'
),
\'
button_enabled
\'
: True,
\'
rows
\'
:
\'
10
\'
,
\'
queue_len
\'
:
\'
3
\'
,
\'
mode
\'
:
\'\'
,
\'
rows
\'
:
\'
10
\'
,
\'
queue_len
\'
:
\'
3
\'
,
\'
mode
\'
:
\'\'
,
\'
tabsize
\'
: 4,
\'
cols
\'
:
\'
80
\'
,
\'
STATIC_URL
\'
:
\'
/dummy-static/
\'
,
\'
tabsize
\'
: 4,
\'
cols
\'
:
\'
80
\'
,
\'
describedby
\'
:
\'
\'
,
\'
queue_msg
\'
:
\'\'
,
\'
STATIC_URL
\'
:
\'
/dummy-static/
\'
,
\'
linenumbers
\'
:
\'
true
\'
,
\'
queue_msg
\'
:
\'\'
,
\'
value
\'
:
\'
print "good evening"
\'
,
\'
value
\'
:
\'
print "good evening"
\'
,
\'
msg
\'
: u
\'
Submitted. As soon as a response is returned,
\'
msg
\'
: u
\'
Submitted. As soon as a response is returned,
this message will be replaced by that feedback.
\'
,
this message will be replaced by that feedback.
\'
,
\'
matlab_editor_js
\'
:
\'
/dummy-static/js/vendor/CodeMirror/octave.js
\'
,
\'
matlab_editor_js
\'
:
\'
/dummy-static/js/vendor/CodeMirror/octave.js
\'
,
\'
hidden
\'
:
\'\'
,
\'
linenumbers
\'
:
\'
true
\'
,
\'
id
\'
:
\'
prob_1_2
\'
,
\'
response_data
\'
: {}}</div>
\'
hidden
\'
:
\'\'
,
\'
id
\'
:
\'
prob_1_2
\'
,
\'
describedby_html
\'
:
\'\'
,
\'
response_data
\'
: {}}</div>
"""
)
.
replace
(
'
\n
'
,
' '
)
.
strip
()
"""
)
.
replace
(
'
\n
'
,
' '
)
.
strip
()
)
)
...
@@ -818,7 +819,7 @@ class MatlabTest(unittest.TestCase):
...
@@ -818,7 +819,7 @@ class MatlabTest(unittest.TestCase):
'queue_len'
:
'3'
,
'queue_len'
:
'3'
,
'matlab_editor_js'
:
'/dummy-static/js/vendor/CodeMirror/octave.js'
,
'matlab_editor_js'
:
'/dummy-static/js/vendor/CodeMirror/octave.js'
,
'response_data'
:
{},
'response_data'
:
{},
'describedby'
:
''
'describedby
_html
'
:
''
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -929,7 +930,7 @@ class SchematicTest(unittest.TestCase):
...
@@ -929,7 +930,7 @@ class SchematicTest(unittest.TestCase):
'analyses'
:
analyses
,
'analyses'
:
analyses
,
'submit_analyses'
:
submit_analyses
,
'submit_analyses'
:
submit_analyses
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -975,7 +976,7 @@ class ImageInputTest(unittest.TestCase):
...
@@ -975,7 +976,7 @@ class ImageInputTest(unittest.TestCase):
'gy'
:
egy
,
'gy'
:
egy
,
'msg'
:
''
,
'msg'
:
''
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -1031,7 +1032,7 @@ class CrystallographyTest(unittest.TestCase):
...
@@ -1031,7 +1032,7 @@ class CrystallographyTest(unittest.TestCase):
'width'
:
width
,
'width'
:
width
,
'height'
:
height
,
'height'
:
height
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -1079,7 +1080,7 @@ class VseprTest(unittest.TestCase):
...
@@ -1079,7 +1080,7 @@ class VseprTest(unittest.TestCase):
'molecules'
:
molecules
,
'molecules'
:
molecules
,
'geometries'
:
geometries
,
'geometries'
:
geometries
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -1115,7 +1116,7 @@ class ChemicalEquationTest(unittest.TestCase):
...
@@ -1115,7 +1116,7 @@ class ChemicalEquationTest(unittest.TestCase):
'size'
:
self
.
size
,
'size'
:
self
.
size
,
'previewer'
:
'/dummy-static/js/capa/chemical_equation_preview.js'
,
'previewer'
:
'/dummy-static/js/capa/chemical_equation_preview.js'
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -1210,7 +1211,7 @@ class FormulaEquationTest(unittest.TestCase):
...
@@ -1210,7 +1211,7 @@ class FormulaEquationTest(unittest.TestCase):
'inline'
:
False
,
'inline'
:
False
,
'trailing_text'
:
''
,
'trailing_text'
:
''
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -1256,7 +1257,7 @@ class FormulaEquationTest(unittest.TestCase):
...
@@ -1256,7 +1257,7 @@ class FormulaEquationTest(unittest.TestCase):
'inline'
:
False
,
'inline'
:
False
,
'trailing_text'
:
expected_text
,
'trailing_text'
:
expected_text
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
assertEqual
(
context
,
expected
)
self
.
assertEqual
(
context
,
expected
)
...
@@ -1388,7 +1389,7 @@ class DragAndDropTest(unittest.TestCase):
...
@@ -1388,7 +1389,7 @@ class DragAndDropTest(unittest.TestCase):
'msg'
:
''
,
'msg'
:
''
,
'drag_and_drop_json'
:
json
.
dumps
(
user_input
),
'drag_and_drop_json'
:
json
.
dumps
(
user_input
),
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
# as we are dumping 'draggables' dicts while dumping user_input, string
# as we are dumping 'draggables' dicts while dumping user_input, string
...
@@ -1457,7 +1458,7 @@ class AnnotationInputTest(unittest.TestCase):
...
@@ -1457,7 +1458,7 @@ class AnnotationInputTest(unittest.TestCase):
'debug'
:
False
,
'debug'
:
False
,
'return_to_annotation'
:
True
,
'return_to_annotation'
:
True
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
self
.
maxDiff
=
None
self
.
maxDiff
=
None
...
@@ -1522,7 +1523,7 @@ class TestChoiceText(unittest.TestCase):
...
@@ -1522,7 +1523,7 @@ class TestChoiceText(unittest.TestCase):
'show_correctness'
:
'always'
,
'show_correctness'
:
'always'
,
'submitted_message'
:
'Answer received.'
,
'submitted_message'
:
'Answer received.'
,
'response_data'
:
RESPONSE_DATA
,
'response_data'
:
RESPONSE_DATA
,
'describedby'
:
DESCRIBEDBY
'describedby
_html
'
:
DESCRIBEDBY
}
}
expected
.
update
(
state
)
expected
.
update
(
state
)
the_input
=
lookup_tag
(
tag
)(
test_capa_system
(),
element
,
state
)
the_input
=
lookup_tag
(
tag
)(
test_capa_system
(),
element
,
state
)
...
...
common/lib/capa/capa/tests/test_util.py
View file @
6ff762b6
...
@@ -5,7 +5,7 @@ import unittest
...
@@ -5,7 +5,7 @@ import unittest
from
lxml
import
etree
from
lxml
import
etree
from
capa.tests.helpers
import
test_capa_system
from
capa.tests.helpers
import
test_capa_system
from
capa.util
import
compare_with_tolerance
,
sanitize_html
,
get_inner_html_from_xpath
from
capa.util
import
compare_with_tolerance
,
sanitize_html
,
get_inner_html_from_xpath
,
remove_markup
class
UtilTest
(
unittest
.
TestCase
):
class
UtilTest
(
unittest
.
TestCase
):
...
@@ -126,3 +126,12 @@ class UtilTest(unittest.TestCase):
...
@@ -126,3 +126,12 @@ class UtilTest(unittest.TestCase):
"""
"""
xpath_node
=
etree
.
XML
(
'<hint style="smtng">aa<a href="#">bb</a>cc</hint>'
)
xpath_node
=
etree
.
XML
(
'<hint style="smtng">aa<a href="#">bb</a>cc</hint>'
)
self
.
assertEqual
(
get_inner_html_from_xpath
(
xpath_node
),
'aa<a href="#">bb</a>cc'
)
self
.
assertEqual
(
get_inner_html_from_xpath
(
xpath_node
),
'aa<a href="#">bb</a>cc'
)
def
test_remove_markup
(
self
):
"""
Test for markup removal with bleach.
"""
self
.
assertEqual
(
remove_markup
(
"The <mark>Truth</mark> is <em>Out There</em> & you need to <strong>find</strong> it"
),
"The Truth is Out There & you need to find it"
)
common/lib/capa/capa/util.py
View file @
6ff762b6
...
@@ -8,6 +8,7 @@ from calc import evaluator
...
@@ -8,6 +8,7 @@ from calc import evaluator
from
cmath
import
isinf
,
isnan
from
cmath
import
isinf
,
isnan
import
re
import
re
from
lxml
import
etree
from
lxml
import
etree
from
openedx.core.djangolib.markup
import
HTML
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
#
#
# Utility functions used in CAPA responsetypes
# Utility functions used in CAPA responsetypes
...
@@ -195,3 +196,15 @@ def get_inner_html_from_xpath(xpath_node):
...
@@ -195,3 +196,15 @@ def get_inner_html_from_xpath(xpath_node):
# strips outer tag from html string
# strips outer tag from html string
inner_html
=
re
.
sub
(
'(?ms)<
%
s[^>]*>(.*)</
%
s>'
%
(
xpath_node
.
tag
,
xpath_node
.
tag
),
'
\\
1'
,
html
)
inner_html
=
re
.
sub
(
'(?ms)<
%
s[^>]*>(.*)</
%
s>'
%
(
xpath_node
.
tag
,
xpath_node
.
tag
),
'
\\
1'
,
html
)
return
inner_html
.
strip
()
return
inner_html
.
strip
()
def
remove_markup
(
html
):
"""
Return html with markup stripped and text HTML-escaped.
>>> bleach.clean("<b>Rock & Roll</b>", tags=[], strip=True)
u'Rock & Roll'
>>> bleach.clean("<b>Rock & Roll</b>", tags=[], strip=True)
u'Rock & Roll'
"""
return
HTML
(
bleach
.
clean
(
html
,
tags
=
[],
strip
=
True
))
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment