Commit 9e94e81f by Tim Krones

Implement integration tests.

parent bef01e81
......@@ -918,7 +918,7 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
if not self.max_attempts_reached:
return self.get_message_content('on-assessment-review', or_default=True)
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)
@property
......@@ -985,6 +985,7 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
'children_contents': children_contents,
}))
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_resource(loader.load_unicode('templates/html/mentoring_attempts.html'), "text/html")
......
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 steps = children.filter(
function(c) { return c.element.className.indexOf('sb-step') > -1; }
);
var steps = [];
var reviewStep;
for (var i = 0; i < children.length; 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;
break;
}
}
......@@ -35,6 +41,11 @@ function MentoringWithStepsBlock(runtime, element) {
return (data.num_attempts < data.max_attempts);
}
function extendedFeedbackEnabled() {
var data = gradeDOM.data();
return data.extended_feedback === "True";
}
function showFeedback(response) {
if (response.step_status === 'correct') {
checkmark.addClass('checkmark-correct icon-ok fa-check');
......@@ -139,6 +150,10 @@ function MentoringWithStepsBlock(runtime, element) {
}
}
function clearSelections() {
$('input[type=radio], input[type=checkbox]', element).prop('checked', false);
}
function cleanAll() {
checkmark.removeClass('checkmark-correct icon-ok fa-check');
checkmark.removeClass('checkmark-partially-correct icon-ok fa-check');
......@@ -176,7 +191,7 @@ function MentoringWithStepsBlock(runtime, element) {
var data = gradeDOM.data();
// Forward to review step to render grade data
var showExtendedFeedback = (!someAttemptsLeft() && data.extended_feedback);
var showExtendedFeedback = (!someAttemptsLeft() && extendedFeedbackEnabled());
reviewStep.renderGrade(gradeDOM, showExtendedFeedback);
// Add click handler that takes care of showing associated step to step links
......@@ -272,7 +287,8 @@ function MentoringWithStepsBlock(runtime, element) {
for (var i=0; i < steps.length; i++) {
var step = steps[i];
var mentoring = {
setContent: setContent
setContent: setContent,
publish_event: publishEvent
};
options.mentoring = mentoring;
step.initChildren(options);
......@@ -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() {
cleanAll();
showAssessmentMessage();
......@@ -310,6 +334,7 @@ function MentoringWithStepsBlock(runtime, element) {
function handleTryAgain(result) {
activeStep = result.active_step;
clearSelections();
updateDisplay();
tryAgainDOM.hide();
submitDOM.show();
......@@ -329,6 +354,23 @@ function MentoringWithStepsBlock(runtime, element) {
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() {
checkmark = $('.assessment-checkmark', element);
......@@ -366,6 +408,7 @@ function MentoringWithStepsBlock(runtime, element) {
updateDisplay();
}
initClickHandlers();
initXBlockView();
}
......@@ -30,6 +30,8 @@ MentoringBlock.url_name = String()
loader = ResourceLoader(__name__)
CORRECT, INCORRECT, PARTIAL = "correct", "incorrect", "partially-correct"
class PopupCheckMixin(object):
"""
......@@ -133,6 +135,88 @@ class MentoringAssessmentBaseTest(ProblemBuilderBaseTest):
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):
if not question_text:
question_text = self.question_text(number)
......@@ -163,6 +247,14 @@ class MentoringAssessmentBaseTest(ProblemBuilderBaseTest):
self.wait_until_clickable(controls.next_question)
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):
""" Helper class for interacting with MCQ options """
......
......@@ -18,9 +18,7 @@
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
from ddt import ddt, unpack, data
from .base_test import MentoringAssessmentBaseTest, GetChoices
CORRECT, INCORRECT, PARTIAL = "correct", "incorrect", "partially-correct"
from .base_test import CORRECT, INCORRECT, PARTIAL, MentoringAssessmentBaseTest, GetChoices
@ddt
......@@ -47,29 +45,10 @@ class MentoringAssessmentTest(MentoringAssessmentBaseTest):
controls.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):
self.assertIn("A Simple Assessment", 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):
self.browser.get(self.live_server_url)
......@@ -104,35 +83,6 @@ class MentoringAssessmentTest(MentoringAssessmentBaseTest):
self._assert_checkmark(mentoring, result)
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):
question = self.expect_question_visible(number, mentoring)
......@@ -213,44 +163,6 @@ class MentoringAssessmentTest(MentoringAssessmentBaseTest):
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):
self.wait_until_text_in("You scored {percentage}% on this assessment.".format(**expected), mentoring)
self.assert_persistent_elements_present(mentoring)
......@@ -288,15 +200,6 @@ class MentoringAssessmentTest(MentoringAssessmentBaseTest):
self.assert_hidden(controls.review)
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):
# Multiple choice is third correctly answered question
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