Commit dd144544 by Eugeny Kolpakov

Merge pull request #81 from open-craft/mcq_messages

Making MCQ use message attribute the same way MRQ does
parents 7cd071d2 2885f774
...@@ -99,6 +99,7 @@ class MCQBlock(SubmittingXBlockMixin, QuestionnaireAbstractBlock): ...@@ -99,6 +99,7 @@ class MCQBlock(SubmittingXBlockMixin, QuestionnaireAbstractBlock):
return { return {
'submission': submission, 'submission': submission,
'message': self.message,
'status': 'correct' if correct else 'incorrect', 'status': 'correct' if correct else 'incorrect',
'tips': formatted_tips, 'tips': formatted_tips,
'weight': self.weight, 'weight': self.weight,
......
// TODO: Split in two files // TODO: Split in two files
function display_message(message, messageView, checkmark){
if (message) {
var msg = '<div class="message-content">' + message + '</div>' +
'<div class="close icon-remove-sign fa-times-circle"></div>';
messageView.showMessage(msg);
if (checkmark) {
checkmark.addClass('checkmark-clickable');
checkmark.on('click', function(ev) {
ev.stopPropagation();
messageView.showMessage(msg);
})
}
}
}
function MessageView(element, mentoring) { function MessageView(element, mentoring) {
return { return {
messageDOM: $('.feedback', element), messageDOM: $('.feedback', element),
...@@ -102,13 +117,15 @@ function MCQBlock(runtime, element) { ...@@ -102,13 +117,15 @@ function MCQBlock(runtime, element) {
$('.choice input', element).prop('disabled', true); $('.choice input', element).prop('disabled', true);
}, },
handleSubmit: function(result) { handleSubmit: function(result, options) {
mentoring = this.mentoring; var mentoring = this.mentoring;
var messageView = MessageView(element, mentoring); var messageView = MessageView(element, mentoring);
messageView.clearResult(); messageView.clearResult();
display_message(result.message, messageView, options.checkmark);
var choiceInputs = $('.choice-selector input', element); var choiceInputs = $('.choice-selector input', element);
$.each(choiceInputs, function(index, choiceInput) { $.each(choiceInputs, function(index, choiceInput) {
var choiceInputDOM = $(choiceInput); var choiceInputDOM = $(choiceInput);
...@@ -127,7 +144,6 @@ function MCQBlock(runtime, element) { ...@@ -127,7 +144,6 @@ function MCQBlock(runtime, element) {
if (result.tips && choiceInputDOM.val() === result.submission) { if (result.tips && choiceInputDOM.val() === result.submission) {
mentoring.setContent(choiceTipsDOM, result.tips); mentoring.setContent(choiceTipsDOM, result.tips);
messageView.showMessage(choiceTipsDOM);
} }
choiceResultDOM.off('click').on('click', function() { choiceResultDOM.off('click').on('click', function() {
...@@ -187,22 +203,11 @@ function MRQBlock(runtime, element) { ...@@ -187,22 +203,11 @@ function MRQBlock(runtime, element) {
handleSubmit: function(result, options) { handleSubmit: function(result, options) {
mentoring = this.mentoring; var mentoring = this.mentoring;
var messageView = MessageView(element, mentoring); var messageView = MessageView(element, mentoring);
if (result.message) { display_message(result.message, messageView, options.checkmark);
var msg = '<div class="message-content">' + result.message + '</div>' +
'<div class="close icon-remove-sign fa-times-circle"></div>';
messageView.showMessage(msg);
if (options.checkmark) {
options.checkmark.addClass('checkmark-clickable');
options.checkmark.on('click', function(ev) {
ev.stopPropagation();
messageView.showMessage(msg);
})
}
}
var questionnaireDOM = $('fieldset.questionnaire', element); var questionnaireDOM = $('fieldset.questionnaire', element);
var data = questionnaireDOM.data(); var data = questionnaireDOM.data();
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
# along with this program in a file in the toplevel directory called # along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>. # "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
# #
import mock
from xblock.fields import String from xblock.fields import String
from xblockutils.base_test import SeleniumBaseTest, SeleniumXBlockTest from xblockutils.base_test import SeleniumBaseTest, SeleniumXBlockTest
...@@ -99,6 +100,22 @@ class MentoringBaseTest(SeleniumBaseTest, PopupCheckMixin): ...@@ -99,6 +100,22 @@ class MentoringBaseTest(SeleniumBaseTest, PopupCheckMixin):
module_name = __name__ module_name = __name__
default_css_selector = 'div.mentoring' default_css_selector = 'div.mentoring'
__asides_patch = None
@classmethod
def setUpClass(cls):
super(MentoringBaseTest, cls).setUpClass()
cls.__asides_patch = mock.patch(
"workbench.runtime.WorkbenchRuntime.applicable_aside_types",
mock.Mock(return_value=[])
)
cls.__asides_patch.start()
@classmethod
def tearDownClass(cls):
cls.__asides_patch.stop()
super(MentoringBaseTest, cls).tearDownClass()
class MentoringAssessmentBaseTest(ProblemBuilderBaseTest): class MentoringAssessmentBaseTest(ProblemBuilderBaseTest):
""" """
......
...@@ -149,7 +149,7 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest): ...@@ -149,7 +149,7 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest):
def _standard_checks(self, answer, mcq, mrq, rating, messages): def _standard_checks(self, answer, mcq, mrq, rating, messages):
self.assertEqual(answer.get_attribute('value'), 'This is the answer') self.assertEqual(answer.get_attribute('value'), 'This is the answer')
self._assert_feedback_showed(mcq, 0, "Great!") self._assert_feedback_showed(mcq, 0, "Great!", click_choice_result=True)
self._assert_feedback_showed( self._assert_feedback_showed(
mrq, 0, "This is something everyone has to like about this MRQ", mrq, 0, "This is something everyone has to like about this MRQ",
click_choice_result=True click_choice_result=True
...@@ -234,7 +234,7 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest): ...@@ -234,7 +234,7 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest):
# precondition - verifying 100% score achieved # precondition - verifying 100% score achieved
self.assertEqual(answer.get_attribute('value'), 'This is the answer') self.assertEqual(answer.get_attribute('value'), 'This is the answer')
self._assert_feedback_showed(mcq, 0, "Great!") self._assert_feedback_showed(mcq, 0, "Great!", click_choice_result=True)
self._assert_feedback_showed( self._assert_feedback_showed(
mrq, 0, "This is something everyone has to like about this MRQ", mrq, 0, "This is something everyone has to like about this MRQ",
click_choice_result=True click_choice_result=True
...@@ -275,7 +275,7 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest): ...@@ -275,7 +275,7 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest):
def assert_state(answer, mcq, mrq, rating, messages): def assert_state(answer, mcq, mrq, rating, messages):
self.assertEqual(answer.get_attribute('value'), 'This is the answer') self.assertEqual(answer.get_attribute('value'), 'This is the answer')
self._assert_feedback_showed(mcq, 0, "Great!") self._assert_feedback_showed(mcq, 0, "Great!", click_choice_result=True)
self._assert_feedback_showed( self._assert_feedback_showed(
mrq, 0, "This is something everyone has to like about this MRQ", mrq, 0, "This is something everyone has to like about this MRQ",
click_choice_result=True click_choice_result=True
......
...@@ -55,6 +55,9 @@ class QuestionnaireBlockTest(MentoringBaseTest): ...@@ -55,6 +55,9 @@ class QuestionnaireBlockTest(MentoringBaseTest):
self.assertEqual(messages.text, '') self.assertEqual(messages.text, '')
self.assertFalse(messages.find_elements_by_xpath('./*')) self.assertFalse(messages.find_elements_by_xpath('./*'))
def _click_result_icon(self, choice):
choice.find_element_by_css_selector(".choice-result").click()
def test_mcq_choices_rating(self): def test_mcq_choices_rating(self):
""" """
Mentoring MCQ should display tips according to user choice Mentoring MCQ should display tips according to user choice
...@@ -69,14 +72,18 @@ class QuestionnaireBlockTest(MentoringBaseTest): ...@@ -69,14 +72,18 @@ class QuestionnaireBlockTest(MentoringBaseTest):
self.assert_messages_empty(messages) self.assert_messages_empty(messages)
self.assertFalse(submit.is_enabled()) self.assertFalse(submit.is_enabled())
mcq1_legend = mcq1.find_element_by_css_selector('legend') self.assertEqual(mcq1.find_element_by_css_selector('legend').text, 'Question 1\nDo you like this MCQ?')
mcq2_legend = mcq2.find_element_by_css_selector('legend') self.assertEqual(mcq2.find_element_by_css_selector('legend').text, 'Question 2\nHow do you rate this MCQ?')
self.assertEqual(mcq1_legend.text, 'Question 1\nDo you like this MCQ?')
self.assertEqual(mcq2_legend.text, 'Question 2\nHow do you rate this MCQ?') mcq1_feedback = mcq1.find_element_by_css_selector('.feedback')
mcq2_feedback = mcq2.find_element_by_css_selector('.feedback')
mcq1_choices = mcq1.find_elements_by_css_selector('.choices .choice') mcq1_choices = mcq1.find_elements_by_css_selector('.choices .choice')
mcq2_choices = mcq2.find_elements_by_css_selector('.rating .choice') mcq2_choices = mcq2.find_elements_by_css_selector('.rating .choice')
mcq1_choices_input = self._get_inputs(mcq1_choices)
mcq2_choices_input = self._get_inputs(mcq2_choices)
self.assertListEqual( self.assertListEqual(
[self._get_choice_label_text(choice) for choice in mcq1_choices], [self._get_choice_label_text(choice) for choice in mcq1_choices],
["Yes", "Maybe not", "I don't understand"] ["Yes", "Maybe not", "I don't understand"]
...@@ -86,18 +93,41 @@ class QuestionnaireBlockTest(MentoringBaseTest): ...@@ -86,18 +93,41 @@ class QuestionnaireBlockTest(MentoringBaseTest):
['1 - Not good at all', '2', '3', '4', '5 - Extremely good', "I don't want to rate it"] ['1 - Not good at all', '2', '3', '4', '5 - Extremely good', "I don't want to rate it"]
) )
mcq1_choices_input = self._get_inputs(mcq1_choices) self.assertListEqual(
mcq2_choices_input = self._get_inputs(mcq2_choices) [choice_input.get_attribute('value') for choice_input in mcq1_choices_input],
['yes', 'maybenot', 'understand']
)
self.assertEqual(mcq1_choices_input[0].get_attribute('value'), 'yes') self.assertListEqual(
self.assertEqual(mcq1_choices_input[1].get_attribute('value'), 'maybenot') [choice_input.get_attribute('value') for choice_input in mcq2_choices_input],
self.assertEqual(mcq1_choices_input[2].get_attribute('value'), 'understand') ['1', '2', '3', '4', '5', 'notwant']
self.assertEqual(mcq2_choices_input[0].get_attribute('value'), '1') )
self.assertEqual(mcq2_choices_input[1].get_attribute('value'), '2')
self.assertEqual(mcq2_choices_input[2].get_attribute('value'), '3') def submit_answer_and_assert_messages(mcq1_answer, mcq2_answer, item_feedback1, item_feedback2):
self.assertEqual(mcq2_choices_input[3].get_attribute('value'), '4') self._selenium_bug_workaround_scroll_to(mcq1)
self.assertEqual(mcq2_choices_input[4].get_attribute('value'), '5')
self.assertEqual(mcq2_choices_input[5].get_attribute('value'), 'notwant') mcq1_choices_input[mcq1_answer].click()
mcq2_choices_input[mcq2_answer].click()
self.assertTrue(submit.is_enabled())
submit.click()
self.wait_until_disabled(submit)
mcq1_tips = mcq1.find_element_by_css_selector(".choice-tips .tip p")
mcq2_tips = mcq2.find_element_by_css_selector(".choice-tips .tip p")
self.assertTrue(mcq1_feedback.is_displayed())
self.assertEqual(mcq1_feedback.text, "Feedback message 1")
self.assertTrue(mcq2_feedback.is_displayed())
self.assertEqual(mcq2_feedback.text, "Feedback message 2")
self.assertFalse(mcq1_tips.is_displayed())
self.assertFalse(mcq2_tips.is_displayed())
self._click_result_icon(mcq1_choices[mcq1_answer])
self.assertEqual(mcq1_tips.text, item_feedback1)
self.assertTrue(mcq1_tips.is_displayed())
self._click_result_icon(mcq2_choices[mcq2_answer])
self.assertEqual(mcq2_tips.text, item_feedback2)
self.assertTrue(mcq2_tips.is_displayed())
# Submit button disabled without selecting anything # Submit button disabled without selecting anything
self.assertFalse(submit.is_enabled()) self.assertFalse(submit.is_enabled())
...@@ -108,38 +138,12 @@ class QuestionnaireBlockTest(MentoringBaseTest): ...@@ -108,38 +138,12 @@ class QuestionnaireBlockTest(MentoringBaseTest):
self.assertFalse(submit.is_enabled()) self.assertFalse(submit.is_enabled())
# Should not show full completion message when wrong answers are selected # Should not show full completion message when wrong answers are selected
self._selenium_bug_workaround_scroll_to(mcq1) submit_answer_and_assert_messages(0, 2, 'Great!', 'Will do better next time...')
mcq1_choices_input[0].click()
mcq2_choices_input[2].click()
self.assertTrue(submit.is_enabled())
submit.click()
self.wait_until_disabled(submit)
mcq1_tips = mcq1.find_element_by_css_selector(".choice-tips .tip p")
mcq2_tips = mcq2.find_element_by_css_selector(".choice-tips .tip p")
self.assertEqual(mcq1_tips.text, 'Great!')
self.assertTrue(mcq1_tips.is_displayed())
self.assertEqual(mcq2_tips.text, 'Will do better next time...')
self.assertTrue(mcq2_tips.is_displayed())
self.assertEqual(messages.text, '') self.assertEqual(messages.text, '')
self.assertFalse(messages.is_displayed()) self.assertFalse(messages.is_displayed())
# Should show full completion when the right answers are selected # Should show full completion when the right answers are selected
self._selenium_bug_workaround_scroll_to(mcq1) submit_answer_and_assert_messages(0, 3, 'Great!', 'I love good grades.')
mcq1_choices_input[0].click()
mcq2_choices_input[3].click()
self.assertTrue(submit.is_enabled())
submit.click()
self.wait_until_disabled(submit)
mcq1_tips = mcq1.find_element_by_css_selector(".choice-tips .tip p")
mcq2_tips = mcq2.find_element_by_css_selector(".choice-tips .tip p")
self.assertEqual(mcq1_tips.text, 'Great!')
self.assertTrue(mcq1_tips.is_displayed())
self.assertEqual(mcq2_tips.text, 'I love good grades.')
self.assertTrue(mcq2_tips.is_displayed())
self.assertIn('All is good now...\nCongratulations!', messages.text) self.assertIn('All is good now...\nCongratulations!', messages.text)
self.assertTrue(messages.is_displayed()) self.assertTrue(messages.is_displayed())
...@@ -150,6 +154,8 @@ class QuestionnaireBlockTest(MentoringBaseTest): ...@@ -150,6 +154,8 @@ class QuestionnaireBlockTest(MentoringBaseTest):
self.assertEqual(len(mcq2_tip_containers), 6) self.assertEqual(len(mcq2_tip_containers), 6)
# Clicking outside the tips should hide the tips and clear the with-tips class. # Clicking outside the tips should hide the tips and clear the with-tips class.
mcq1_tips = mcq1.find_element_by_css_selector(".choice-tips .tip p")
mcq2_tips = mcq2.find_element_by_css_selector(".choice-tips .tip p")
mcq1.find_element_by_css_selector('.mentoring .question-title').click() mcq1.find_element_by_css_selector('.mentoring .question-title').click()
mcq2.find_element_by_css_selector('.mentoring .question-title').click() mcq2.find_element_by_css_selector('.mentoring .question-title').click()
mcq1_tip_containers = mcq1.find_elements_by_css_selector('.choice-tips-container.with-tips') mcq1_tip_containers = mcq1.find_elements_by_css_selector('.choice-tips-container.with-tips')
...@@ -274,16 +280,12 @@ class QuestionnaireBlockTest(MentoringBaseTest): ...@@ -274,16 +280,12 @@ class QuestionnaireBlockTest(MentoringBaseTest):
submit.click() submit.click()
self.wait_until_disabled(submit) self.wait_until_disabled(submit)
self._click_result_icon(choice_wrapper)
item_feedback_popup = choice_wrapper.find_element_by_css_selector(".choice-tips") item_feedback_popup = choice_wrapper.find_element_by_css_selector(".choice-tips")
self.assertTrue(item_feedback_popup.is_displayed()) self.assertTrue(item_feedback_popup.is_displayed())
feedback_height = self._get_inner_height(item_feedback_popup) feedback_height = self._get_inner_height(item_feedback_popup)
self.assertEqual(feedback_height, expected_height) self.assertEqual(feedback_height, expected_height)
choice_wrapper.find_element_by_css_selector(".choice-result").click()
item_feedback_popup = choice_wrapper.find_element_by_css_selector(".choice-tips")
item_feedback_height = self._get_inner_height(item_feedback_popup)
self.assertEqual(item_feedback_height, expected_height)
@patch.object(MentoringBlock, 'get_theme', Mock(return_value={'package': 'problem_builder', @patch.object(MentoringBlock, 'get_theme', Mock(return_value={'package': 'problem_builder',
'locations': ['public/themes/lms.css']})) 'locations': ['public/themes/lms.css']}))
......
<vertical_demo> <vertical_demo>
<problem-builder url_name="mcq_1" enforce_dependency="false"> <problem-builder url_name="mcq_1" enforce_dependency="false">
<pb-mcq name="mcq_1_1" question="Do you like this MCQ?" correct_choices='["yes"]'> <pb-mcq name="mcq_1_1" question="Do you like this MCQ?" correct_choices='["yes"]' message="Feedback message 1">
<pb-choice value="yes">Yes</pb-choice> <pb-choice value="yes">Yes</pb-choice>
<pb-choice value="maybenot">Maybe not</pb-choice> <pb-choice value="maybenot">Maybe not</pb-choice>
<pb-choice value="understand">I don't understand</pb-choice> <pb-choice value="understand">I don't understand</pb-choice>
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
<pb-tip values='["understand"]'><div id="test-custom-html">Really?</div></pb-tip> <pb-tip values='["understand"]'><div id="test-custom-html">Really?</div></pb-tip>
</pb-mcq> </pb-mcq>
<pb-rating name="mcq_1_2" low="Not good at all" high="Extremely good" question="How do you rate this MCQ?" correct_choices='["4","5"]'> <pb-rating name="mcq_1_2" low="Not good at all" high="Extremely good" question="How do you rate this MCQ?"
correct_choices='["4","5"]' message="Feedback message 2"
>
<pb-choice value="notwant">I don't want to rate it</pb-choice> <pb-choice value="notwant">I don't want to rate it</pb-choice>
<pb-tip values='["4","5"]'>I love good grades.</pb-tip> <pb-tip values='["4","5"]'>I love good grades.</pb-tip>
......
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