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 @@ ...@@ -249,3 +249,14 @@
background-color: white; background-color: white;
box-shadow: 0 10px 20px #5C5C5C; 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) { ...@@ -158,21 +158,52 @@ function MentoringWithStepsBlock(runtime, element) {
showActiveStep(); showActiveStep();
validateXBlock(); validateXBlock();
updateNextLabel(); updateNextLabel();
// Reinstate default event handlers
nextDOM.on('click', updateDisplay);
reviewButtonDOM.on('click', showGrade);
var step = steps[activeStep]; var step = steps[activeStep];
if (step.hasQuestion()) { if (step.hasQuestion()) { // Step includes one or more questions
nextDOM.attr('disabled', 'disabled'); 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'); nextDOM.removeAttr('disabled');
} submitDOM.hide();
if (isLastStep() && hasAReviewStep) { if (isLastStep()) { // Step is last step
if (step.hasQuestion()) { // Remove default event handler from button that displays review.
reviewButtonDOM.attr('disabled', 'disabled'); // This is necessary to make sure updateDisplay is not called twice
} else { // when user clicks this button next;
reviewButtonDOM.removeAttr('disabled') // "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() { function showReviewStep() {
...@@ -189,7 +220,7 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -189,7 +220,7 @@ function MentoringWithStepsBlock(runtime, element) {
} }
function hideReviewStep() { function hideReviewStep() {
reviewStepDOM.hide() reviewStepDOM.hide();
} }
function getStepToReview(event) { function getStepToReview(event) {
...@@ -225,6 +256,9 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -225,6 +256,9 @@ function MentoringWithStepsBlock(runtime, element) {
reviewLinkDOM.show(); reviewLinkDOM.show();
getResults(); getResults();
// Scroll to top of this block
scrollIntoView();
} }
function showAttempts() { function showAttempts() {
...@@ -236,8 +270,8 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -236,8 +270,8 @@ function MentoringWithStepsBlock(runtime, element) {
function showActiveStep() { function showActiveStep() {
var step = steps[activeStep]; var step = steps[activeStep];
step.updatePlots();
$(step.element).show(); $(step.element).show();
step.updateChildren();
} }
function onChange() { function onChange() {
...@@ -260,20 +294,6 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -260,20 +294,6 @@ function MentoringWithStepsBlock(runtime, element) {
} else { } else {
submitDOM.removeAttr('disabled'); 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) { function initSteps(options) {
...@@ -323,6 +343,9 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -323,6 +343,9 @@ function MentoringWithStepsBlock(runtime, element) {
nextDOM.off(); nextDOM.off();
nextDOM.on('click', reviewNextStep); nextDOM.on('click', reviewNextStep);
reviewLinkDOM.hide(); reviewLinkDOM.hide();
// Scroll to top of this block
scrollIntoView();
} }
function reviewNextStep() { function reviewNextStep() {
...@@ -363,6 +386,19 @@ function MentoringWithStepsBlock(runtime, element) { ...@@ -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() { function initClickHandlers() {
$(document).on("click", function(event, ui) { $(document).on("click", function(event, ui) {
var target = $(event.target); var target = $(event.target);
......
...@@ -57,11 +57,13 @@ function PlotBlock(runtime, element) { ...@@ -57,11 +57,13 @@ function PlotBlock(runtime, element) {
// Create axes // Create axes
var xAxis = d3.svg.axis() var xAxis = d3.svg.axis()
.scale(xScale) .scale(xScale)
.orient("bottom"); .orient("bottom")
.tickValues([]);
var yAxis = d3.svg.axis() var yAxis = d3.svg.axis()
.scale(yScale) .scale(yScale)
.orient("left"); .orient("left")
.tickValues([]);
// Create SVG group elements for axes and call the xAxis and yAxis functions // Create SVG group elements for axes and call the xAxis and yAxis functions
var xAxisGroup = svgContainer.append("g") var xAxisGroup = svgContainer.append("g")
......
function MentoringStepBlock(runtime, element) { function MentoringStepBlock(runtime, element) {
var children = runtime.children(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, var submitXHR, resultsXHR,
message = $(element).find('.sb-step-message'); message = $(element).find('.sb-step-message');
...@@ -21,6 +13,14 @@ function MentoringStepBlock(runtime, element) { ...@@ -21,6 +13,14 @@ function MentoringStepBlock(runtime, element) {
} }
} }
function updateVideo(video) {
video.resizer.align();
}
function updatePlot(plot) {
plot.update();
}
return { return {
initChildren: function(options) { initChildren: function(options) {
...@@ -103,13 +103,18 @@ function MentoringStepBlock(runtime, element) { ...@@ -103,13 +103,18 @@ function MentoringStepBlock(runtime, element) {
return $('.sb-step', element).data('has-question') return $('.sb-step', element).data('has-question')
}, },
updatePlots: function() { updateChildren: function() {
if (plots) { children.forEach(function(child) {
for (var i in plots) { var type = $(child.element).data('block-type');
var plot = plots[i]; switch (type) {
plot.update(); case 'video':
updateVideo(child);
break;
case 'sb-plot':
updatePlot(child);
break;
} }
} });
} }
}; };
......
...@@ -26,3 +26,7 @@ ...@@ -26,3 +26,7 @@
margin-left: 1.8em; margin-left: 1.8em;
padding-left: 0; padding-left: 0;
} }
.themed-xblock.mentoring .copyright {
display: none;
}
...@@ -56,4 +56,9 @@ ...@@ -56,4 +56,9 @@
<div class="assessment-review-tips"></div> <div class="assessment-review-tips"></div>
</div> </div>
<div class="review-link"><a href="#">Review final grade</a></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> </div>
...@@ -28,4 +28,9 @@ ...@@ -28,4 +28,9 @@
<div class="review-link"><a href="#">Review final grade</a></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> </div>
import time
from mock import patch from mock import patch
from ddt import ddt, data from ddt import ddt, data
from selenium.webdriver.support.ui import WebDriverWait
from workbench.runtime import WorkbenchRuntime from workbench.runtime import WorkbenchRuntime
from .base_test import CORRECT, INCORRECT, PARTIAL, MentoringAssessmentBaseTest, GetChoices from .base_test import CORRECT, INCORRECT, PARTIAL, MentoringAssessmentBaseTest, GetChoices
...@@ -657,14 +660,17 @@ class StepBuilderTest(MentoringAssessmentBaseTest, MultipleSliderBlocksTestMixin ...@@ -657,14 +660,17 @@ class StepBuilderTest(MentoringAssessmentBaseTest, MultipleSliderBlocksTestMixin
) )
choices.select(choice_name) 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() controls.submit.click()
self.wait_until_clickable(controls.next_question) self.wait_until_clickable(controls.next_question)
controls.next_question.click() controls.next_question.click()
if last: if last:
self.wait_until_hidden(controls.next_question) self.wait_until_hidden(controls.next_question)
else: 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): def plot_controls(self, step_builder):
class Namespace(object): class Namespace(object):
...@@ -777,9 +783,16 @@ class StepBuilderTest(MentoringAssessmentBaseTest, MultipleSliderBlocksTestMixin ...@@ -777,9 +783,16 @@ class StepBuilderTest(MentoringAssessmentBaseTest, MultipleSliderBlocksTestMixin
plot_controls.quadrants_button.click() plot_controls.quadrants_button.click()
self.check_quadrant_labels(step_builder, plot_controls, hidden=False) 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): def check_overlays(self, step_builder, total_num_points, overlays):
points = step_builder.find_elements_by_css_selector("circle") self.wait_for_multiple_elements(step_builder, "circle", total_num_points)
self.assertEquals(len(points), total_num_points)
for overlay in overlays: for overlay in overlays:
# Check if correct number of points is present # Check if correct number of points is present
...@@ -1288,3 +1301,104 @@ class StepBuilderTest(MentoringAssessmentBaseTest, MultipleSliderBlocksTestMixin ...@@ -1288,3 +1301,104 @@ class StepBuilderTest(MentoringAssessmentBaseTest, MultipleSliderBlocksTestMixin
# In that case, submitting an answer and will fail, # In that case, submitting an answer and will fail,
# as it requires the corresponding question to be visible: # as it requires the corresponding question to be visible:
self.freeform_answer(None, step_builder, controls, 'This is the answer', CORRECT) 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