Commit b5632bef by Xavier Antoviaque

Rename `<quizz>` into `<mcq>` for consistency with upcoming `<mrq>`

Also preserve `<quizz>` binding for compatibility. Split quizz/MCQ
blocks into individual python files
parent 85b2708d
......@@ -6,7 +6,7 @@ This XBlock allows to automate the workflow of real-life mentoring, within an ed
It supports:
* **Free-form answers** (textarea) which can be shared accross different XBlock instances (for example, to remind a student of an answer he gave before). Editable or read-only.
* **Self-assessment quizzes** (multiple choice), to display predetermined feedback to a student based on his choices in the self-assessment. Supports rating scales and arbitrary answers.
* **Self-assessment MCQs** (multiple choice), to display predetermined feedback to a student based on his choices in the self-assessment. Supports rating scales and arbitrary answers.
* **Progression tracking**, allowing to check that the student has completed the previous steps before allowing to complete a given XBlock instance. Provides a link to the next step to the student.
* **Tables**, which allow to present answers from the student to free-form answers in a concise way. Supports custom headers.
* **Data export**, to allow course authors to download a CSV file containing the free-form answers entered by the students
......@@ -44,12 +44,12 @@ Second XBlock instance:
</mentoring>
```
### Self-assessment quizzes
### Self-assessment MCQs
```xml
<mentoring url_name="quizz_1" enforce_dependency="false">
<quizz name="quizz_1_1" type="choices">
<question>Do you like this quizz?</question>
<mentoring url_name="mcq_1" enforce_dependency="false">
<mcq name="mcq_1_1" type="choices">
<question>Do you like this MCQ?</question>
<choice value="yes">Yes</choice>
<choice value="maybenot">Maybe not</choice>
<choice value="understand">I don't understand</choice>
......@@ -57,16 +57,16 @@ Second XBlock instance:
<tip display="yes">Great!</tip>
<tip reject="maybenot">Ah, damn.</tip>
<tip reject="understand"><html><div id="test-custom-html">Really?</div></html></tip>
</quizz>
</mcq>
<quizz name="quizz_1_2" type="rating" low="Not good at all" high="Extremely good">
<question>How much do you rate this quizz?</question>
<mcq name="mcq_1_2" type="rating" low="Not good at all" high="Extremely good">
<question>How much do you rate this MCQ?</question>
<choice value="notwant">I don't want to rate it</choice>
<tip display="4,5">I love good grades.</tip>
<tip reject="1,2,3">Will do better next time...</tip>
<tip reject="notwant">Your loss!</tip>
</quizz>
</MCQ>
<message type="completed">
All is good now...
......
from .answer import AnswerBlock
from .choice import ChoiceBlock
from .dataexport import MentoringDataExportBlock
from .html import HTMLBlock
from .quizz import QuizzBlock, QuizzChoiceBlock, QuizzTipBlock
from .mcq import MCQBlock
from .mentoring import MentoringBlock
from .message import MentoringMessageBlock
from .table import MentoringTableBlock, MentoringTableColumnBlock, MentoringTableColumnHeaderBlock
from .tip import TipBlock
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 Harvard
#
# Authors:
# Xavier Antoviaque <xavier@antoviaque.org>
#
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
# Imports ###########################################################
import logging
from .light_children import LightChild, Scope, String
# Globals ###########################################################
log = logging.getLogger(__name__)
# Classes ###########################################################
class ChoiceBlock(LightChild):
"""
Custom choice of an answer for a MCQ/MRQ
"""
value = String(help="Value of the choice when selected", scope=Scope.content, default="")
content = String(help="Human-readable version of the choice value", scope=Scope.content, default="")
......@@ -27,6 +27,8 @@ import logging
from xblock.fragment import Fragment
from .choice import ChoiceBlock
from .tip import TipBlock
from .light_children import LightChild, Scope, String
from .utils import render_template
......@@ -36,23 +38,9 @@ from .utils import render_template
log = logging.getLogger(__name__)
# Functions #########################################################
def commas_to_list(commas_str):
"""
Converts a comma-separated string to a list
"""
if commas_str is None:
return None # Means default value (which can be non-empty)
elif commas_str == '':
return [] # Means empty list
else:
return commas_str.split(',')
# Classes ###########################################################
class QuizzBlock(LightChild):
class MCQBlock(LightChild):
"""
An XBlock used to ask multiple-choice questions
......@@ -61,7 +49,7 @@ class QuizzBlock(LightChild):
set, with preset choices and author-defined values.
"""
question = String(help="Question to ask the student", scope=Scope.content, default="")
type = String(help="Type of quizz", scope=Scope.content, default="choices")
type = String(help="Type of MCQ", scope=Scope.content, default="choices")
student_choice = String(help="Last input submitted by the student", default="", scope=Scope.user_state)
low = String(help="Label for low ratings", scope=Scope.content, default="Less")
high = String(help="Label for high ratings", scope=Scope.content, default="More")
......@@ -82,9 +70,9 @@ class QuizzBlock(LightChild):
def mentoring_view(self, context=None):
if str(self.type) not in ('rating', 'choices'):
raise ValueError, u'Invalid value for QuizzBlock.type: `{}`'.format(self.type)
raise ValueError, u'Invalid value for MCQBlock.type: `{}`'.format(self.type)
template_path = 'templates/html/quizz_{}.html'.format(self.type)
template_path = 'templates/html/mcq_{}.html'.format(self.type)
html = render_template(template_path, {
'self': self,
'custom_choices': self.custom_choices,
......@@ -92,26 +80,27 @@ class QuizzBlock(LightChild):
fragment = Fragment(html)
fragment.add_css_url(self.runtime.local_resource_url(self.xblock_container,
'public/css/quizz.css'))
'public/css/mcq.css'))
fragment.add_javascript_url(self.runtime.local_resource_url(self.xblock_container,
'public/js/quizz.js'))
fragment.initialize_js('QuizzBlock')
'public/js/mcq.js'))
fragment.initialize_js('MCQBlock')
return fragment
@property
def custom_choices(self):
custom_choices = []
for child in self.get_children_objects():
if isinstance(child, QuizzChoiceBlock):
if isinstance(child, ChoiceBlock):
custom_choices.append(child)
return custom_choices
def submit(self, submission):
log.debug(u'Received quizz submission: "%s"', submission)
log.debug(u'Received MCQ submission: "%s"', submission)
completed = True
tips_fragments = []
for tip in self.get_tips():
log.warn(tip)
completed = completed and tip.is_completed(submission)
if tip.is_tip_displayed(submission):
tips_fragments.append(tip.render(submission))
......@@ -129,7 +118,7 @@ class QuizzBlock(LightChild):
'completed': completed,
'tips': formatted_tips,
}
log.debug(u'Quizz submission result: %s', result)
log.debug(u'MCQ submission result: %s', result)
return result
def get_submission_display(self, submission):
......@@ -147,56 +136,6 @@ class QuizzBlock(LightChild):
"""
tips = []
for child in self.get_children_objects():
if isinstance(child, QuizzTipBlock):
if isinstance(child, TipBlock):
tips.append(child)
return tips
class QuizzTipBlock(LightChild):
"""
Each quizz
"""
content = String(help="Text of the tip to provide if needed", scope=Scope.content, default="")
display = String(help="List of choices to display the tip for", scope=Scope.content, default=None)
reject = String(help="List of choices to reject", scope=Scope.content, default=None)
def render(self, submission):
"""
Returns a fragment containing the formatted tip
"""
fragment, named_children = self.get_children_fragment({})
fragment.add_content(render_template('templates/html/tip.html', {
'self': self,
'named_children': named_children,
}))
return self.xblock_container.fragment_text_rewriting(fragment)
def is_completed(self, submission):
return submission and submission not in self.reject_with_defaults
def is_tip_displayed(self, submission):
return submission in self.display_with_defaults
@property
def display_with_defaults(self):
display = commas_to_list(self.display)
if display is None:
display = self.reject_with_defaults
else:
display += [choice for choice in self.reject_with_defaults
if choice not in display]
return display
@property
def reject_with_defaults(self):
reject = commas_to_list(self.reject)
log.debug(reject)
return reject or []
class QuizzChoiceBlock(LightChild):
"""
Custom choice of an answer for a quizz
"""
value = String(help="Value of the choice when selected", scope=Scope.content, default="")
content = String(help="Human-readable version of the choice value", scope=Scope.content, default="")
......@@ -49,8 +49,8 @@ class MentoringBlock(XBlockWithLightChildren):
"""
An XBlock providing mentoring capabilities
Composed of text, answers input fields, and a set of multiple choice quizzes with advices.
A set of conditions on the provided answers and quizzes choices will determine if the
Composed of text, answers input fields, and a set of MRQ/MCQ with advices.
A set of conditions on the provided answers and MCQ/MRQ choices will determine if the
student is a) provided mentoring advices and asked to alter his answer, or b) is given the
ok to continue.
"""
......
function QuizzBlock(runtime, element) {
function MCQBlock(runtime, element) {
return {
submit: function() {
var checkedRadio = $('input[type=radio]:checked', element);
......
<div class="quizz-tip">
<div class="mcq-tip">
<strong>
To the question <span class="italic">"{{ self.question }}"</span>,
{% if submission %}
......
......@@ -5,8 +5,8 @@
<answer name="goal" />
<quizz name="quizz_1_1" type="choices">
<question>Do you like this quizz?</question>
<mcq name="mcq_1_1" type="choices">
<question>Do you like this MCQ?</question>
<choice value="yes">Yes</choice>
<choice value="maybenot">Maybe not</choice>
<choice value="understand">I don't understand</choice>
......@@ -14,16 +14,16 @@
<tip display="yes">Great!</tip>
<tip reject="maybenot">Ah, damn.</tip>
<tip reject="understand"><html><div id="test-custom-html">Really?</div></html></tip>
</quizz>
</mcq>
<quizz name="quizz_1_2" type="rating" low="Not good at all" high="Extremely good">
<question>How much do you rate this quizz?</question>
<mcq name="mcq_1_2" type="rating" low="Not good at all" high="Extremely good">
<question>How much do you rate this MCQ?</question>
<choice value="notwant">I don't want to rate it</choice>
<tip display="4,5">I love good grades.</tip>
<tip reject="1,2,3">Will do better next time...</tip>
<tip reject="notwant">Your loss!</tip>
</quizz>
</mcq>
<message type="completed">
<html><p>Congratulations!</p></html>
......
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 Harvard
#
# Authors:
# Xavier Antoviaque <xavier@antoviaque.org>
#
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
# Imports ###########################################################
import logging
from .light_children import LightChild, Scope, String
from .utils import render_template
# Globals ###########################################################
log = logging.getLogger(__name__)
# Functions #########################################################
def commas_to_list(commas_str):
"""
Converts a comma-separated string to a list
"""
if commas_str is None:
return None # Means default value (which can be non-empty)
elif commas_str == '':
return [] # Means empty list
else:
return commas_str.split(',')
# Classes ###########################################################
class TipBlock(LightChild):
"""
Each choice can define a tip depending on selection
"""
content = String(help="Text of the tip to provide if needed", scope=Scope.content, default="")
display = String(help="List of choices to display the tip for", scope=Scope.content, default=None)
reject = String(help="List of choices to reject", scope=Scope.content, default=None)
def render(self, submission):
"""
Returns a fragment containing the formatted tip
"""
fragment, named_children = self.get_children_fragment({})
fragment.add_content(render_template('templates/html/tip.html', {
'self': self,
'named_children': named_children,
}))
return self.xblock_container.fragment_text_rewriting(fragment)
def is_completed(self, submission):
return submission and submission not in self.reject_with_defaults
def is_tip_displayed(self, submission):
return submission in self.display_with_defaults
@property
def display_with_defaults(self):
display = commas_to_list(self.display)
if display is None:
display = self.reject_with_defaults
else:
display += [choice for choice in self.reject_with_defaults
if choice not in display]
return display
@property
def reject_with_defaults(self):
reject = commas_to_list(self.reject)
log.debug(reject)
return reject or []
......@@ -52,10 +52,11 @@ BLOCKS_CHILDREN = [
'column = mentoring:MentoringTableColumnBlock',
'header = mentoring:MentoringTableColumnHeaderBlock',
'answer = mentoring:AnswerBlock',
'quizz = mentoring:QuizzBlock',
'quizz = mentoring:MCQBlock',
'mcq = mentoring:MCQBlock',
'message = mentoring:MentoringMessageBlock',
'tip = mentoring:QuizzTipBlock',
'choice = mentoring:QuizzChoiceBlock',
'tip = mentoring:TipBlock',
'choice = mentoring:ChoiceBlock',
'html = mentoring:HTMLBlock',
]
......
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 Harvard
#
# Authors:
# Xavier Antoviaque <xavier@antoviaque.org>
#
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
# Imports ###########################################################
import time
from mentoring.test_base import MentoringBaseTest
# Classes ###########################################################
class MCQBlockTest(MentoringBaseTest):
def test_mcq_choices_rating(self):
"""
Mentoring MCQ should display tips according to user choice
"""
# Initial MCQ status
mentoring = self.go_to_page('MCQ 1')
mcq1 = mentoring.find_element_by_css_selector('fieldset.choices')
mcq2 = mentoring.find_element_by_css_selector('fieldset.rating')
messages = mentoring.find_element_by_css_selector('.messages')
progress = mentoring.find_element_by_css_selector('.progress > .indicator')
self.assertEqual(messages.text, '')
self.assertFalse(messages.find_elements_by_xpath('./*'))
self.assertEqual(progress.text, '(Not completed)')
self.assertFalse(progress.find_elements_by_xpath('./*'))
mcq1_legend = mcq1.find_element_by_css_selector('legend')
mcq2_legend = mcq2.find_element_by_css_selector('legend')
self.assertEqual(mcq1_legend.text, 'Do you like this MCQ?')
self.assertEqual(mcq2_legend.text, 'How much do you rate this MCQ?')
mcq1_choices = mcq1.find_elements_by_css_selector('.choices .choice label')
mcq2_choices = mcq2.find_elements_by_css_selector('.choices .choice label')
self.assertEqual(len(mcq1_choices), 3)
self.assertEqual(len(mcq2_choices), 6)
self.assertEqual(mcq1_choices[0].text, 'Yes')
self.assertEqual(mcq1_choices[1].text, 'Maybe not')
self.assertEqual(mcq1_choices[2].text, "I don't understand")
self.assertEqual(mcq2_choices[0].text, '1')
self.assertEqual(mcq2_choices[1].text, '2')
self.assertEqual(mcq2_choices[2].text, '3')
self.assertEqual(mcq2_choices[3].text, '4')
self.assertEqual(mcq2_choices[4].text, '5')
self.assertEqual(mcq2_choices[5].text, "I don't want to rate it")
mcq1_choices_input = [
mcq1_choices[0].find_element_by_css_selector('input'),
mcq1_choices[1].find_element_by_css_selector('input'),
mcq1_choices[2].find_element_by_css_selector('input'),
]
mcq2_choices_input = [
mcq2_choices[0].find_element_by_css_selector('input'),
mcq2_choices[1].find_element_by_css_selector('input'),
mcq2_choices[2].find_element_by_css_selector('input'),
mcq2_choices[3].find_element_by_css_selector('input'),
mcq2_choices[4].find_element_by_css_selector('input'),
mcq2_choices[5].find_element_by_css_selector('input'),
]
self.assertEqual(mcq1_choices_input[0].get_attribute('value'), 'yes')
self.assertEqual(mcq1_choices_input[1].get_attribute('value'), 'maybenot')
self.assertEqual(mcq1_choices_input[2].get_attribute('value'), 'understand')
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')
self.assertEqual(mcq2_choices_input[3].get_attribute('value'), '4')
self.assertEqual(mcq2_choices_input[4].get_attribute('value'), '5')
self.assertEqual(mcq2_choices_input[5].get_attribute('value'), 'notwant')
# Submit without selecting anything
submit = mentoring.find_element_by_css_selector('input.submit')
submit.click()
tips = messages.find_elements_by_xpath('./*')
self.assertEqual(len(tips), 2)
self.assertEqual(tips[0].text, 'To the question "Do you like this MCQ?", you have not provided an answer.')
self.assertEqual(tips[1].text, 'To the question "How much do you rate this MCQ?", you have not provided an answer.')
self.assertEqual(progress.text, '(Not completed)')
self.assertFalse(progress.find_elements_by_xpath('./*'))
# Select only one option
mcq1_choices_input[1].click()
submit.click()
time.sleep(1)
tips = messages.find_elements_by_xpath('./*')
self.assertEqual(len(tips), 2)
self.assertEqual(tips[0].text, 'To the question "Do you like this MCQ?", you answered "Maybe not".\nAh, damn.')
self.assertEqual(tips[1].text, 'To the question "How much do you rate this MCQ?", you have not provided an answer.')
self.assertEqual(progress.text, '(Not completed)')
self.assertFalse(progress.find_elements_by_xpath('./*'))
# One with only display tip, one with reject tip - should not complete
mcq1_choices_input[0].click()
mcq2_choices_input[2].click()
submit.click()
time.sleep(1)
tips = messages.find_elements_by_xpath('./*')
self.assertEqual(len(tips), 2)
self.assertEqual(tips[0].text, 'To the question "Do you like this MCQ?", you answered "Yes".\nGreat!')
self.assertEqual(tips[1].text, 'To the question "How much do you rate this MCQ?", you answered "3".\nWill do better next time...')
self.assertEqual(progress.text, '(Not completed)')
self.assertFalse(progress.find_elements_by_xpath('./*'))
# Only display tips, to allow to complete
mcq1_choices_input[0].click()
mcq2_choices_input[3].click()
submit.click()
time.sleep(1)
tips = messages.find_elements_by_xpath('./*')
self.assertEqual(len(tips), 3)
self.assertEqual(tips[0].text, 'To the question "Do you like this MCQ?", you answered "Yes".\nGreat!')
self.assertEqual(tips[1].text, 'To the question "How much do you rate this MCQ?", you answered "4".\nI love good grades.')
self.assertEqual(tips[2].text, 'Congratulations!\nAll is good now...') # Includes child <html>
self.assertEqual(progress.text, '')
self.assertTrue(progress.find_elements_by_css_selector('img'))
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 Harvard
#
# Authors:
# Xavier Antoviaque <xavier@antoviaque.org>
#
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
# Imports ###########################################################
import time
from mentoring.test_base import MentoringBaseTest
# Classes ###########################################################
class QuizzBlockTest(MentoringBaseTest):
def test_quizz_choices_rating(self):
"""
Mentoring quizz should display tips according to user choice
"""
# Initial quizzes status
mentoring = self.go_to_page('Quizz 1')
quizz1 = mentoring.find_element_by_css_selector('fieldset.choices')
quizz2 = mentoring.find_element_by_css_selector('fieldset.rating')
messages = mentoring.find_element_by_css_selector('.messages')
progress = mentoring.find_element_by_css_selector('.progress > .indicator')
self.assertEqual(messages.text, '')
self.assertFalse(messages.find_elements_by_xpath('./*'))
self.assertEqual(progress.text, '(Not completed)')
self.assertFalse(progress.find_elements_by_xpath('./*'))
quizz1_legend = quizz1.find_element_by_css_selector('legend')
quizz2_legend = quizz2.find_element_by_css_selector('legend')
self.assertEqual(quizz1_legend.text, 'Do you like this quizz?')
self.assertEqual(quizz2_legend.text, 'How much do you rate this quizz?')
quizz1_choices = quizz1.find_elements_by_css_selector('.choices .choice label')
quizz2_choices = quizz2.find_elements_by_css_selector('.choices .choice label')
self.assertEqual(len(quizz1_choices), 3)
self.assertEqual(len(quizz2_choices), 6)
self.assertEqual(quizz1_choices[0].text, 'Yes')
self.assertEqual(quizz1_choices[1].text, 'Maybe not')
self.assertEqual(quizz1_choices[2].text, "I don't understand")
self.assertEqual(quizz2_choices[0].text, '1')
self.assertEqual(quizz2_choices[1].text, '2')
self.assertEqual(quizz2_choices[2].text, '3')
self.assertEqual(quizz2_choices[3].text, '4')
self.assertEqual(quizz2_choices[4].text, '5')
self.assertEqual(quizz2_choices[5].text, "I don't want to rate it")
quizz1_choices_input = [
quizz1_choices[0].find_element_by_css_selector('input'),
quizz1_choices[1].find_element_by_css_selector('input'),
quizz1_choices[2].find_element_by_css_selector('input'),
]
quizz2_choices_input = [
quizz2_choices[0].find_element_by_css_selector('input'),
quizz2_choices[1].find_element_by_css_selector('input'),
quizz2_choices[2].find_element_by_css_selector('input'),
quizz2_choices[3].find_element_by_css_selector('input'),
quizz2_choices[4].find_element_by_css_selector('input'),
quizz2_choices[5].find_element_by_css_selector('input'),
]
self.assertEqual(quizz1_choices_input[0].get_attribute('value'), 'yes')
self.assertEqual(quizz1_choices_input[1].get_attribute('value'), 'maybenot')
self.assertEqual(quizz1_choices_input[2].get_attribute('value'), 'understand')
self.assertEqual(quizz2_choices_input[0].get_attribute('value'), '1')
self.assertEqual(quizz2_choices_input[1].get_attribute('value'), '2')
self.assertEqual(quizz2_choices_input[2].get_attribute('value'), '3')
self.assertEqual(quizz2_choices_input[3].get_attribute('value'), '4')
self.assertEqual(quizz2_choices_input[4].get_attribute('value'), '5')
self.assertEqual(quizz2_choices_input[5].get_attribute('value'), 'notwant')
# Submit without selecting anything
submit = mentoring.find_element_by_css_selector('input.submit')
submit.click()
tips = messages.find_elements_by_xpath('./*')
self.assertEqual(len(tips), 2)
self.assertEqual(tips[0].text, 'To the question "Do you like this quizz?", you have not provided an answer.')
self.assertEqual(tips[1].text, 'To the question "How much do you rate this quizz?", you have not provided an answer.')
self.assertEqual(progress.text, '(Not completed)')
self.assertFalse(progress.find_elements_by_xpath('./*'))
# Select only one option
quizz1_choices_input[1].click()
submit.click()
time.sleep(1)
tips = messages.find_elements_by_xpath('./*')
self.assertEqual(len(tips), 2)
self.assertEqual(tips[0].text, 'To the question "Do you like this quizz?", you answered "Maybe not".\nAh, damn.')
self.assertEqual(tips[1].text, 'To the question "How much do you rate this quizz?", you have not provided an answer.')
self.assertEqual(progress.text, '(Not completed)')
self.assertFalse(progress.find_elements_by_xpath('./*'))
# One with only display tip, one with reject tip - should not complete
quizz1_choices_input[0].click()
quizz2_choices_input[2].click()
submit.click()
time.sleep(1)
tips = messages.find_elements_by_xpath('./*')
self.assertEqual(len(tips), 2)
self.assertEqual(tips[0].text, 'To the question "Do you like this quizz?", you answered "Yes".\nGreat!')
self.assertEqual(tips[1].text, 'To the question "How much do you rate this quizz?", you answered "3".\nWill do better next time...')
self.assertEqual(progress.text, '(Not completed)')
self.assertFalse(progress.find_elements_by_xpath('./*'))
# Only display tips, to allow to complete
quizz1_choices_input[0].click()
quizz2_choices_input[3].click()
submit.click()
time.sleep(1)
tips = messages.find_elements_by_xpath('./*')
self.assertEqual(len(tips), 3)
self.assertEqual(tips[0].text, 'To the question "Do you like this quizz?", you answered "Yes".\nGreat!')
self.assertEqual(tips[1].text, 'To the question "How much do you rate this quizz?", you answered "4".\nI love good grades.')
self.assertEqual(tips[2].text, 'Congratulations!\nAll is good now...') # Includes child <html>
self.assertEqual(progress.text, '')
self.assertTrue(progress.find_elements_by_css_selector('img'))
<vertical>
<mentoring url_name="quizz_1" enforce_dependency="false">
<quizz name="quizz_1_1" type="choices">
<question>Do you like this quizz?</question>
<mentoring url_name="mcq_1" enforce_dependency="false">
<mcq name="mcq_1_1" type="choices">
<question>Do you like this MCQ?</question>
<choice value="yes">Yes</choice>
<choice value="maybenot">Maybe not</choice>
<choice value="understand">I don't understand</choice>
......@@ -9,16 +9,16 @@
<tip display="yes">Great!</tip>
<tip reject="maybenot">Ah, damn.</tip>
<tip reject="understand"><html><div id="test-custom-html">Really?</div></html></tip>
</quizz>
</mcq>
<quizz name="quizz_1_2" type="rating" low="Not good at all" high="Extremely good">
<question>How much do you rate this quizz?</question>
<mcq name="mcq_1_2" type="rating" low="Not good at all" high="Extremely good">
<question>How much do you rate this MCQ?</question>
<choice value="notwant">I don't want to rate it</choice>
<tip display="4,5">I love good grades.</tip>
<tip reject="1,2,3">Will do better next time...</tip>
<tip reject="notwant">Your loss!</tip>
</quizz>
</mcq>
<message type="completed">
All is good now...
......
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