Commit 9e94e81f by Tim Krones

Implement integration tests.

parent bef01e81
...@@ -918,7 +918,7 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes ...@@ -918,7 +918,7 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
if not self.max_attempts_reached: if not self.max_attempts_reached:
return self.get_message_content('on-assessment-review', or_default=True) return self.get_message_content('on-assessment-review', or_default=True)
else: else:
assessment_message = _("Note: you have used all attempts. Continue to the next unit") assessment_message = _("Note: you have used all attempts. Continue to the next unit.")
return '<p>{}</p>'.format(assessment_message) return '<p>{}</p>'.format(assessment_message)
@property @property
...@@ -985,6 +985,7 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes ...@@ -985,6 +985,7 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
'children_contents': children_contents, 'children_contents': children_contents,
})) }))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder.css')) fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder.css'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/vendor/underscore-min.js'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/mentoring_with_steps.js')) fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/mentoring_with_steps.js'))
fragment.add_resource(loader.load_unicode('templates/html/mentoring_attempts.html'), "text/html") fragment.add_resource(loader.load_unicode('templates/html/mentoring_attempts.html'), "text/html")
......
function MentoringWithStepsBlock(runtime, element) { function MentoringWithStepsBlock(runtime, element) {
// Set up gettext in case it isn't available in the client runtime:
if (typeof gettext == "undefined") {
window.gettext = function gettext_stub(string) { return string; };
window.ngettext = function ngettext_stub(strA, strB, n) { return n == 1 ? strA : strB; };
}
var children = runtime.children(element); var children = runtime.children(element);
var steps = children.filter( var steps = [];
function(c) { return c.element.className.indexOf('sb-step') > -1; }
);
var reviewStep; var reviewStep;
for (var i = 0; i < children.length; i++) { for (var i = 0; i < children.length; i++) {
var child = children[i]; var child = children[i];
if (child.type === 'sb-review-step') { var blockType = $(child.element).data('block-type');
if (blockType === 'sb-step') {
steps.push(child);
} else if (blockType === 'sb-review-step') {
reviewStep = child; reviewStep = child;
break;
} }
} }
...@@ -35,6 +41,11 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -35,6 +41,11 @@ function MentoringWithStepsBlock(runtime, element) {
return (data.num_attempts < data.max_attempts); return (data.num_attempts < data.max_attempts);
} }
function extendedFeedbackEnabled() {
var data = gradeDOM.data();
return data.extended_feedback === "True";
}
function showFeedback(response) { function showFeedback(response) {
if (response.step_status === 'correct') { if (response.step_status === 'correct') {
checkmark.addClass('checkmark-correct icon-ok fa-check'); checkmark.addClass('checkmark-correct icon-ok fa-check');
...@@ -139,6 +150,10 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -139,6 +150,10 @@ function MentoringWithStepsBlock(runtime, element) {
} }
} }
function clearSelections() {
$('input[type=radio], input[type=checkbox]', element).prop('checked', false);
}
function cleanAll() { function cleanAll() {
checkmark.removeClass('checkmark-correct icon-ok fa-check'); checkmark.removeClass('checkmark-correct icon-ok fa-check');
checkmark.removeClass('checkmark-partially-correct icon-ok fa-check'); checkmark.removeClass('checkmark-partially-correct icon-ok fa-check');
...@@ -176,7 +191,7 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -176,7 +191,7 @@ function MentoringWithStepsBlock(runtime, element) {
var data = gradeDOM.data(); var data = gradeDOM.data();
// Forward to review step to render grade data // Forward to review step to render grade data
var showExtendedFeedback = (!someAttemptsLeft() && data.extended_feedback); var showExtendedFeedback = (!someAttemptsLeft() && extendedFeedbackEnabled());
reviewStep.renderGrade(gradeDOM, showExtendedFeedback); reviewStep.renderGrade(gradeDOM, showExtendedFeedback);
// Add click handler that takes care of showing associated step to step links // Add click handler that takes care of showing associated step to step links
...@@ -272,7 +287,8 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -272,7 +287,8 @@ function MentoringWithStepsBlock(runtime, element) {
for (var i=0; i < steps.length; i++) { for (var i=0; i < steps.length; i++) {
var step = steps[i]; var step = steps[i];
var mentoring = { var mentoring = {
setContent: setContent setContent: setContent,
publish_event: publishEvent
}; };
options.mentoring = mentoring; options.mentoring = mentoring;
step.initChildren(options); step.initChildren(options);
...@@ -288,6 +304,14 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -288,6 +304,14 @@ function MentoringWithStepsBlock(runtime, element) {
} }
} }
function publishEvent(data) {
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'publish_event'),
data: JSON.stringify(data)
});
}
function showGrade() { function showGrade() {
cleanAll(); cleanAll();
showAssessmentMessage(); showAssessmentMessage();
...@@ -310,6 +334,7 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -310,6 +334,7 @@ function MentoringWithStepsBlock(runtime, element) {
function handleTryAgain(result) { function handleTryAgain(result) {
activeStep = result.active_step; activeStep = result.active_step;
clearSelections();
updateDisplay(); updateDisplay();
tryAgainDOM.hide(); tryAgainDOM.hide();
submitDOM.show(); submitDOM.show();
...@@ -329,6 +354,23 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -329,6 +354,23 @@ function MentoringWithStepsBlock(runtime, element) {
submitXHR = $.post(handlerUrl, JSON.stringify({})).success(handleTryAgain); submitXHR = $.post(handlerUrl, JSON.stringify({})).success(handleTryAgain);
} }
function initClickHandlers() {
$(document).on("click", function(event, ui) {
var target = $(event.target);
var itemFeedbackParentSelector = '.choice';
var itemFeedbackSelector = ".choice .choice-tips";
function clickedInside(selector, parent_selector){
return target.is(selector) || target.parents(parent_selector).length>0;
}
if (!clickedInside(itemFeedbackSelector, itemFeedbackParentSelector)) {
$(itemFeedbackSelector).not(':hidden').hide();
$('.choice-tips-container').removeClass('with-tips');
}
});
}
function initXBlockView() { function initXBlockView() {
checkmark = $('.assessment-checkmark', element); checkmark = $('.assessment-checkmark', element);
...@@ -366,6 +408,7 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -366,6 +408,7 @@ function MentoringWithStepsBlock(runtime, element) {
updateDisplay(); updateDisplay();
} }
initClickHandlers();
initXBlockView(); initXBlockView();
} }
...@@ -30,6 +30,8 @@ MentoringBlock.url_name = String() ...@@ -30,6 +30,8 @@ MentoringBlock.url_name = String()
loader = ResourceLoader(__name__) loader = ResourceLoader(__name__)
CORRECT, INCORRECT, PARTIAL = "correct", "incorrect", "partially-correct"
class PopupCheckMixin(object): class PopupCheckMixin(object):
""" """
...@@ -133,6 +135,88 @@ class MentoringAssessmentBaseTest(ProblemBuilderBaseTest): ...@@ -133,6 +135,88 @@ class MentoringAssessmentBaseTest(ProblemBuilderBaseTest):
return mentoring, controls return mentoring, controls
def assert_hidden(self, elem):
self.assertFalse(elem.is_displayed())
def assert_disabled(self, elem):
self.assertTrue(elem.is_displayed())
self.assertFalse(elem.is_enabled())
def assert_clickable(self, elem):
self.assertTrue(elem.is_displayed())
self.assertTrue(elem.is_enabled())
def ending_controls(self, controls, last):
if last:
self.assert_hidden(controls.next_question)
self.assert_disabled(controls.review)
else:
self.assert_disabled(controls.next_question)
self.assert_hidden(controls.review)
def selected_controls(self, controls, last):
self.assert_clickable(controls.submit)
self.ending_controls(controls, last)
def assert_message_text(self, mentoring, text):
message_wrapper = mentoring.find_element_by_css_selector('.assessment-message')
self.assertEqual(message_wrapper.text, text)
self.assertTrue(message_wrapper.is_displayed())
def assert_no_message_text(self, mentoring):
message_wrapper = mentoring.find_element_by_css_selector('.assessment-message')
self.assertEqual(message_wrapper.text, '')
def check_question_feedback(self, step_builder, question):
question_checkmark = step_builder.find_element_by_css_selector('.assessment-checkmark')
question_feedback = question.find_element_by_css_selector(".feedback")
self.assertTrue(question_feedback.is_displayed())
self.assertEqual(question_feedback.text, "Question Feedback Message")
question.click()
self.assertFalse(question_feedback.is_displayed())
question_checkmark.click()
self.assertTrue(question_feedback.is_displayed())
def do_submit_wait(self, controls, last):
if last:
self.wait_until_clickable(controls.review)
else:
self.wait_until_clickable(controls.next_question)
def do_post(self, controls, last):
if last:
controls.review.click()
else:
controls.next_question.click()
def multiple_response_question(self, number, mentoring, controls, choice_names, result, last=False):
question = self.peek_at_multiple_response_question(number, mentoring, controls, last=last)
choices = GetChoices(question)
expected_choices = {
"Its elegance": False,
"Its beauty": False,
"Its gracefulness": False,
"Its bugs": False,
}
self.assertEquals(choices.state, expected_choices)
for name in choice_names:
choices.select(name)
expected_choices[name] = True
self.assertEquals(choices.state, expected_choices)
self.selected_controls(controls, last)
controls.submit.click()
self.do_submit_wait(controls, last)
self._assert_checkmark(mentoring, result)
controls.review.click()
def expect_question_visible(self, number, mentoring, question_text=None): def expect_question_visible(self, number, mentoring, question_text=None):
if not question_text: if not question_text:
question_text = self.question_text(number) question_text = self.question_text(number)
...@@ -163,6 +247,14 @@ class MentoringAssessmentBaseTest(ProblemBuilderBaseTest): ...@@ -163,6 +247,14 @@ class MentoringAssessmentBaseTest(ProblemBuilderBaseTest):
self.wait_until_clickable(controls.next_question) self.wait_until_clickable(controls.next_question)
controls.next_question.click() controls.next_question.click()
def _assert_checkmark(self, mentoring, result):
"""Assert that only the desired checkmark is present."""
states = {CORRECT: 0, INCORRECT: 0, PARTIAL: 0}
states[result] += 1
for name, count in states.items():
self.assertEqual(len(mentoring.find_elements_by_css_selector(".checkmark-{}".format(name))), count)
class GetChoices(object): class GetChoices(object):
""" Helper class for interacting with MCQ options """ """ Helper class for interacting with MCQ options """
......
...@@ -18,9 +18,7 @@ ...@@ -18,9 +18,7 @@
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>. # "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
# #
from ddt import ddt, unpack, data from ddt import ddt, unpack, data
from .base_test import MentoringAssessmentBaseTest, GetChoices from .base_test import CORRECT, INCORRECT, PARTIAL, MentoringAssessmentBaseTest, GetChoices
CORRECT, INCORRECT, PARTIAL = "correct", "incorrect", "partially-correct"
@ddt @ddt
...@@ -47,29 +45,10 @@ class MentoringAssessmentTest(MentoringAssessmentBaseTest): ...@@ -47,29 +45,10 @@ class MentoringAssessmentTest(MentoringAssessmentBaseTest):
controls.click() controls.click()
title.click() title.click()
def assert_hidden(self, elem):
self.assertFalse(elem.is_displayed())
def assert_disabled(self, elem):
self.assertTrue(elem.is_displayed())
self.assertFalse(elem.is_enabled())
def assert_clickable(self, elem):
self.assertTrue(elem.is_displayed())
self.assertTrue(elem.is_enabled())
def assert_persistent_elements_present(self, mentoring): def assert_persistent_elements_present(self, mentoring):
self.assertIn("A Simple Assessment", mentoring.text) self.assertIn("A Simple Assessment", mentoring.text)
self.assertIn("This paragraph is shared between all questions.", mentoring.text) self.assertIn("This paragraph is shared between all questions.", mentoring.text)
def _assert_checkmark(self, mentoring, result):
"""Assert that only the desired checkmark is present."""
states = {CORRECT: 0, INCORRECT: 0, PARTIAL: 0}
states[result] += 1
for name, count in states.items():
self.assertEqual(len(mentoring.find_elements_by_css_selector(".checkmark-{}".format(name))), count)
def go_to_workbench_main_page(self): def go_to_workbench_main_page(self):
self.browser.get(self.live_server_url) self.browser.get(self.live_server_url)
...@@ -104,35 +83,6 @@ class MentoringAssessmentTest(MentoringAssessmentBaseTest): ...@@ -104,35 +83,6 @@ class MentoringAssessmentTest(MentoringAssessmentBaseTest):
self._assert_checkmark(mentoring, result) self._assert_checkmark(mentoring, result)
self.do_post(controls, last) self.do_post(controls, last)
def ending_controls(self, controls, last):
if last:
self.assert_hidden(controls.next_question)
self.assert_disabled(controls.review)
else:
self.assert_disabled(controls.next_question)
self.assert_hidden(controls.review)
def selected_controls(self, controls, last):
self.assert_clickable(controls.submit)
if last:
self.assert_hidden(controls.next_question)
self.assert_disabled(controls.review)
else:
self.assert_disabled(controls.next_question)
self.assert_hidden(controls.review)
def do_submit_wait(self, controls, last):
if last:
self.wait_until_clickable(controls.review)
else:
self.wait_until_clickable(controls.next_question)
def do_post(self, controls, last):
if last:
controls.review.click()
else:
controls.next_question.click()
def single_choice_question(self, number, mentoring, controls, choice_name, result, last=False): def single_choice_question(self, number, mentoring, controls, choice_name, result, last=False):
question = self.expect_question_visible(number, mentoring) question = self.expect_question_visible(number, mentoring)
...@@ -213,44 +163,6 @@ class MentoringAssessmentTest(MentoringAssessmentBaseTest): ...@@ -213,44 +163,6 @@ class MentoringAssessmentTest(MentoringAssessmentBaseTest):
return question return question
def check_question_feedback(self, mentoring, question):
question_checkmark = mentoring.find_element_by_css_selector('.assessment-checkmark')
question_feedback = question.find_element_by_css_selector(".feedback")
self.assertTrue(question_feedback.is_displayed())
self.assertEqual(question_feedback.text, "Question Feedback Message")
question.click()
self.assertFalse(question_feedback.is_displayed())
question_checkmark.click()
self.assertTrue(question_feedback.is_displayed())
def multiple_response_question(self, number, mentoring, controls, choice_names, result, last=False):
question = self.peek_at_multiple_response_question(number, mentoring, controls, last=last)
choices = GetChoices(question)
expected_choices = {
"Its elegance": False,
"Its beauty": False,
"Its gracefulness": False,
"Its bugs": False,
}
self.assertEquals(choices.state, expected_choices)
for name in choice_names:
choices.select(name)
expected_choices[name] = True
self.assertEquals(choices.state, expected_choices)
self.selected_controls(controls, last)
controls.submit.click()
self.do_submit_wait(controls, last)
self._assert_checkmark(mentoring, result)
controls.review.click()
def peek_at_review(self, mentoring, controls, expected, extended_feedback=False): def peek_at_review(self, mentoring, controls, expected, extended_feedback=False):
self.wait_until_text_in("You scored {percentage}% on this assessment.".format(**expected), mentoring) self.wait_until_text_in("You scored {percentage}% on this assessment.".format(**expected), mentoring)
self.assert_persistent_elements_present(mentoring) self.assert_persistent_elements_present(mentoring)
...@@ -288,15 +200,6 @@ class MentoringAssessmentTest(MentoringAssessmentBaseTest): ...@@ -288,15 +200,6 @@ class MentoringAssessmentTest(MentoringAssessmentBaseTest):
self.assert_hidden(controls.review) self.assert_hidden(controls.review)
self.assert_hidden(controls.review_link) self.assert_hidden(controls.review_link)
def assert_message_text(self, mentoring, text):
message_wrapper = mentoring.find_element_by_css_selector('.assessment-message')
self.assertEqual(message_wrapper.text, text)
self.assertTrue(message_wrapper.is_displayed())
def assert_no_message_text(self, mentoring):
message_wrapper = mentoring.find_element_by_css_selector('.assessment-message')
self.assertEqual(message_wrapper.text, '')
def extended_feedback_checks(self, mentoring, controls, expected_results): def extended_feedback_checks(self, mentoring, controls, expected_results):
# Multiple choice is third correctly answered question # Multiple choice is third correctly answered question
self.assert_hidden(controls.review_link) self.assert_hidden(controls.review_link)
......
<step-builder url_name="step-builder" display_name="Step Builder"
max_attempts="{{max_attempts}}" extended_feedback="{{extended_feedback}}">
<sb-step display_name="First step">
<pb-answer name="goal" question="What is your goal?" />
</sb-step>
<sb-step display_name="Second step">
<pb-mcq name="mcq_1_1" question="Do you like this MCQ?" correct_choices='["yes"]'>
<pb-choice value="yes">Yes</pb-choice>
<pb-choice value="maybenot">Maybe not</pb-choice>
<pb-choice value="understand">I don't understand</pb-choice>
<pb-tip values='["yes"]'>Great!</pb-tip>
<pb-tip values='["maybenot"]'>Ah, damn.</pb-tip>
<pb-tip values='["understand"]'><div id="test-custom-html">Really?</div></pb-tip>
{% if include_review_tips %}
<pb-message type="on-assessment-review-question">
<html>Take another look at <a href="#">Lesson 1</a></html>
</pb-message>
{% endif %}
</pb-mcq>
</sb-step>
<sb-step display_name="Third step">
<pb-rating name="mcq_1_2" low="Not good at all" high="Extremely good" question="How much do you rate this MCQ?" correct_choices='["4","5"]'>
<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='["1","2", "3"]'>Will do better next time...</pb-tip>
<pb-tip values='["notwant"]'>Your loss!</pb-tip>
{% if include_review_tips %}
<pb-message type="on-assessment-review-question">
<html>Take another look at <a href="#">Lesson 2</a></html>
</pb-message>
{% endif %}
</pb-rating>
</sb-step>
<sb-step display_name="Last step">
<pb-mrq name="mrq_1_1" question="What do you like in this MRQ?" required_choices='["gracefulness","elegance","beauty"]' message="Question Feedback Message">
<pb-choice value="elegance">Its elegance</pb-choice>
<pb-choice value="beauty">Its beauty</pb-choice>
<pb-choice value="gracefulness">Its gracefulness</pb-choice>
<pb-choice value="bugs">Its bugs</pb-choice>
<pb-tip values='["gracefulness"]'>This MRQ is indeed very graceful</pb-tip>
<pb-tip values='["elegance","beauty"]'>This is something everyone has to like about this MRQ</pb-tip>
<pb-tip values='["bugs"]'>Nah, there aren't any!</pb-tip>
{% if include_review_tips %}
<pb-message type="on-assessment-review-question">
<html>Take another look at <a href="#">Lesson 3</a></html>
</pb-message>
{% endif %}
</pb-mrq>
</sb-step>
<sb-review-step></sb-review-step>
<pb-message type="on-assessment-review">
<html>Assessment additional feedback message text</html>
</pb-message>
</step-builder>
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