Commit cc0c234e by Matjaz Gregoric

Merge pull request #91 from open-craft/competing-evidence-qa

Competing Evidence: Fix issues from client QA
parents 37d5d426 6157e302
......@@ -249,3 +249,14 @@
background-color: white;
box-shadow: 0 10px 20px #5C5C5C;
}
.mentoring .copyright {
color: #AAA;
clear: both;
font-size: 10px;
margin: 3em 0;
}
.mentoring .copyright a {
color: #69C0E8;
}
......@@ -158,21 +158,52 @@ function MentoringWithStepsBlock(runtime, element) {
showActiveStep();
validateXBlock();
updateNextLabel();
// Reinstate default event handlers
nextDOM.on('click', updateDisplay);
reviewButtonDOM.on('click', showGrade);
var step = steps[activeStep];
if (step.hasQuestion()) {
if (step.hasQuestion()) { // Step includes one or more questions
nextDOM.attr('disabled', 'disabled');
} else {
submitDOM.show();
if (isLastStep()) { // Step is last step
nextDOM.hide();
if (hasAReviewStep) { // Step Builder includes review step
reviewButtonDOM.attr('disabled', 'disabled');
reviewButtonDOM.show();
}
}
} else { // Step does not include any questions
nextDOM.removeAttr('disabled');
}
if (isLastStep() && hasAReviewStep) {
if (step.hasQuestion()) {
reviewButtonDOM.attr('disabled', 'disabled');
} else {
reviewButtonDOM.removeAttr('disabled')
submitDOM.hide();
if (isLastStep()) { // Step is last step
// Remove default event handler from button that displays review.
// This is necessary to make sure updateDisplay is not called twice
// when user clicks this button next;
// "submit" already does the right thing with respect to updating the display,
// and calling updateDisplay twice causes issues with scrolling behavior:
reviewButtonDOM.off();
reviewButtonDOM.one('click', submit);
reviewButtonDOM.removeAttr('disabled');
nextDOM.hide();
if (hasAReviewStep) { // Step Builder includes review step
reviewButtonDOM.show();
}
} else { // Step is not last step
// Remove default event handler from button that displays next step.
// This is necessary to make sure updateDisplay is not called twice
// when user clicks this button next;
// "submit" already does the right thing with respect to updating the display,
// and calling updateDisplay twice causes issues with scrolling behavior:
nextDOM.off();
nextDOM.one('click', submit);
}
reviewButtonDOM.show();
}
}
// Scroll to top of this block
scrollIntoView();
}
function showReviewStep() {
......@@ -189,7 +220,7 @@ function MentoringWithStepsBlock(runtime, element) {
}
function hideReviewStep() {
reviewStepDOM.hide()
reviewStepDOM.hide();
}
function getStepToReview(event) {
......@@ -225,6 +256,9 @@ function MentoringWithStepsBlock(runtime, element) {
reviewLinkDOM.show();
getResults();
// Scroll to top of this block
scrollIntoView();
}
function showAttempts() {
......@@ -236,8 +270,8 @@ function MentoringWithStepsBlock(runtime, element) {
function showActiveStep() {
var step = steps[activeStep];
step.updatePlots();
$(step.element).show();
step.updateChildren();
}
function onChange() {
......@@ -260,20 +294,6 @@ function MentoringWithStepsBlock(runtime, element) {
} else {
submitDOM.removeAttr('disabled');
}
if (isLastStep() && step.hasQuestion()) {
nextDOM.hide();
} else if (isLastStep()) {
reviewButtonDOM.one('click', submit);
reviewButtonDOM.removeAttr('disabled');
nextDOM.hide()
} else if (!step.hasQuestion()) {
nextDOM.one('click', submit);
}
if (step.hasQuestion()) {
submitDOM.show();
} else {
submitDOM.hide();
}
}
function initSteps(options) {
......@@ -323,6 +343,9 @@ function MentoringWithStepsBlock(runtime, element) {
nextDOM.off();
nextDOM.on('click', reviewNextStep);
reviewLinkDOM.hide();
// Scroll to top of this block
scrollIntoView();
}
function reviewNextStep() {
......@@ -363,6 +386,19 @@ function MentoringWithStepsBlock(runtime, element) {
}
}
function scrollIntoView() {
// This function can be called multiple times per step initialization.
// We must make sure that only one animation is queued or running at any given time,
// that's why we use a special animation queue and make sure to .stop() any running/queued
// animations before enqueueing a new one.
var rootBlock = $(element),
rootBlockOffset = rootBlock.offset().top,
queue = 'sb-scroll',
props = {scrollTop: rootBlockOffset},
opts = {duration: 500, queue: queue};
$('html, body').stop(queue, true).animate(props, opts).dequeue(queue);
}
function initClickHandlers() {
$(document).on("click", function(event, ui) {
var target = $(event.target);
......
......@@ -57,11 +57,13 @@ function PlotBlock(runtime, element) {
// Create axes
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
.orient("bottom")
.tickValues([]);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
.orient("left")
.tickValues([]);
// Create SVG group elements for axes and call the xAxis and yAxis functions
var xAxisGroup = svgContainer.append("g")
......
function MentoringStepBlock(runtime, element) {
var children = runtime.children(element);
var plots = [];
for (var i in children) {
var child = children[i];
var blockType = $(child.element).data('block-type');
if (blockType === 'sb-plot') {
plots.push(child);
}
}
var submitXHR, resultsXHR,
message = $(element).find('.sb-step-message');
......@@ -21,6 +13,14 @@ function MentoringStepBlock(runtime, element) {
}
}
function updateVideo(video) {
video.resizer.align();
}
function updatePlot(plot) {
plot.update();
}
return {
initChildren: function(options) {
......@@ -103,13 +103,18 @@ function MentoringStepBlock(runtime, element) {
return $('.sb-step', element).data('has-question')
},
updatePlots: function() {
if (plots) {
for (var i in plots) {
var plot = plots[i];
plot.update();
updateChildren: function() {
children.forEach(function(child) {
var type = $(child.element).data('block-type');
switch (type) {
case 'video':
updateVideo(child);
break;
case 'sb-plot':
updatePlot(child);
break;
}
}
});
}
};
......
......@@ -26,3 +26,7 @@
margin-left: 1.8em;
padding-left: 0;
}
.themed-xblock.mentoring .copyright {
display: none;
}
......@@ -56,4 +56,9 @@
<div class="assessment-review-tips"></div>
</div>
<div class="review-link"><a href="#">Review final grade</a></div>
<p class="copyright">
Copyright &copy; 2013&ndash;2015 OpenCraft, Harvard, edX, McKinsey, and The People's Science, released under the
<a target="_blank" href="https://github.com/open-craft/problem-builder/blob/master/LICENSE">APGLv3 license</a>
</p>
</div>
......@@ -28,4 +28,9 @@
<div class="review-link"><a href="#">Review final grade</a></div>
<p class="copyright">
Copyright &copy; 2013&ndash;2015 OpenCraft, Harvard, edX, McKinsey, and The People's Science, released under the
<a target="_blank" href="https://github.com/open-craft/problem-builder/blob/master/LICENSE">APGLv3 license</a>
</p>
</div>
import time
from mock import patch
from ddt import ddt, data
from selenium.webdriver.support.ui import WebDriverWait
from workbench.runtime import WorkbenchRuntime
from .base_test import CORRECT, INCORRECT, PARTIAL, MentoringAssessmentBaseTest, GetChoices
......@@ -657,14 +660,17 @@ class StepBuilderTest(MentoringAssessmentBaseTest, MultipleSliderBlocksTestMixin
)
choices.select(choice_name)
def submit_and_go_to_next_step(self, controls, last=False):
def submit_and_go_to_next_step(self, controls, last=False, no_questions=False):
controls.submit.click()
self.wait_until_clickable(controls.next_question)
controls.next_question.click()
if last:
self.wait_until_hidden(controls.next_question)
else:
self.wait_until_disabled(controls.next_question)
if no_questions:
self.wait_until_hidden(controls.submit)
else:
self.wait_until_disabled(controls.next_question)
def plot_controls(self, step_builder):
class Namespace(object):
......@@ -777,9 +783,16 @@ class StepBuilderTest(MentoringAssessmentBaseTest, MultipleSliderBlocksTestMixin
plot_controls.quadrants_button.click()
self.check_quadrant_labels(step_builder, plot_controls, hidden=False)
def wait_for_multiple_elements(self, step_builder, selector, expected_number_of_elements):
def wait_for_elements(container):
elements = container.find_elements_by_css_selector(selector)
return len(elements) == expected_number_of_elements
wait = WebDriverWait(step_builder, self.timeout)
wait.until(wait_for_elements)
def check_overlays(self, step_builder, total_num_points, overlays):
points = step_builder.find_elements_by_css_selector("circle")
self.assertEquals(len(points), total_num_points)
self.wait_for_multiple_elements(step_builder, "circle", total_num_points)
for overlay in overlays:
# Check if correct number of points is present
......@@ -1288,3 +1301,104 @@ class StepBuilderTest(MentoringAssessmentBaseTest, MultipleSliderBlocksTestMixin
# In that case, submitting an answer and will fail,
# as it requires the corresponding question to be visible:
self.freeform_answer(None, step_builder, controls, 'This is the answer', CORRECT)
def provide_freeform_answer(self, step_number, question_number, step_builder, text_input):
steps = step_builder.find_elements_by_css_selector('div[data-block-type="sb-step"]')
current_step = steps[step_number-1]
freeform_questions = current_step.find_elements_by_css_selector('div[data-block-type="pb-answer"]')
current_question = freeform_questions[question_number-1]
question_text = self.question_text(question_number)
self.wait_until_text_in(question_text, current_question)
self.assertIn("What is your goal?", current_question.text)
textarea = current_question.find_element_by_css_selector("textarea")
textarea.clear()
textarea.send_keys(text_input)
self.assertEquals(textarea.get_attribute("value"), text_input)
def submit_and_go_to_review_step(self, step_builder, controls, result):
controls.submit.click()
self.do_submit_wait(controls, last=True)
self._assert_checkmark(step_builder, result)
self.do_post(controls, last=True)
def check_viewport(self):
step_builder_offset = int(self.browser.execute_script(
"return $('div[data-block-type=\"step-builder\"]').offset().top")
)
def is_scrolled_to_top(driver):
scroll_top = int(driver.execute_script("return $(window).scrollTop()"))
return abs(scroll_top - step_builder_offset) < 1
wait = WebDriverWait(self.browser, 5)
wait.until(is_scrolled_to_top)
def scroll_down(self):
self.browser.execute_script("$(window).scrollTop(50)")
def test_scroll_into_view(self):
# Make window small, so that we have to scroll.
self.browser.set_window_size(400, 400)
step_builder, controls = self.load_assessment_scenario("step_builder_long_steps.xml", {})
# First step
self.check_viewport()
# - Answer questions
self.provide_freeform_answer(1, 1, step_builder, "This is the answer")
self.provide_freeform_answer(1, 2, step_builder, "This is the answer")
self.provide_freeform_answer(1, 3, step_builder, "This is the answer")
self.provide_freeform_answer(1, 4, step_builder, "This is the answer")
self.provide_freeform_answer(1, 5, step_builder, "This is the answer")
# - Submit and go to next step
self.submit_and_go_to_next_step(controls, no_questions=True)
# Second step
self.check_viewport()
self.scroll_down()
self.html_section(step_builder, controls)
# Last step
self.check_viewport()
# - Answer questions
self.provide_freeform_answer(3, 1, step_builder, "This is the answer")
self.provide_freeform_answer(3, 2, step_builder, "This is the answer")
self.provide_freeform_answer(3, 3, step_builder, "This is the answer")
self.provide_freeform_answer(3, 4, step_builder, "This is the answer")
self.provide_freeform_answer(3, 5, step_builder, "This is the answer")
# - Submit and go to review step
self.submit_and_go_to_review_step(step_builder, controls, result=CORRECT)
# Review step
self.check_viewport()
question_links = step_builder.find_elements_by_css_selector('.correct-list li a')
# - Review questions belonging to first step
question_links[2].click()
self.check_viewport()
self.scroll_down()
# - Jump to review step
controls.review_link.click()
self.check_viewport()
self.scroll_down()
# - Review questions belonging to last step
question_links[7].click()
self.check_viewport()
self.scroll_down()
# - Jump to review step
controls.review_link.click()
self.check_viewport()
self.scroll_down()
# - Review questions belonging to first step
question_links[2].click()
self.check_viewport()
self.scroll_down()
# - Navigate to second step
controls.next_question.click()
self.check_viewport()
self.scroll_down()
# - Review questions belonging to last step
controls.next_question.click()
self.check_viewport()
self.scroll_down()
# - Navigate to review step
controls.review.click()
self.check_viewport()
<step-builder url_name="step-builder" display_name="Step Builder"
max_attempts="1" extended_feedback="true">
<sb-step display_name="First step">
<pb-answer name="goal" question="What is your goal?" />
<pb-answer name="goal" question="What is your goal?" />
<pb-answer name="goal" question="What is your goal?" />
<pb-answer name="goal" question="What is your goal?" />
<pb-answer name="goal" question="What is your goal?" />
</sb-step>
<sb-step display_name="Second Step">
<html_demo>Test test test</html_demo>
</sb-step>
<sb-step display_name="Last step">
<pb-answer name="goal" question="What is your goal?" />
<pb-answer name="goal" question="What is your goal?" />
<pb-answer name="goal" question="What is your goal?" />
<pb-answer name="goal" question="What is your goal?" />
<pb-answer name="goal" question="What is your goal?" />
</sb-step>
<sb-review-step>
<sb-review-score/>
</sb-review-step>
</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