Hide After Due setting for Timed Exams

TNL-4366
Changes in studio to allow the hide_after_due setting to be utilized.

Includes:
    -python changes to contentstore, where the data is stored.
    -refactoring of timed-examination-preference-editor.underscore, to add this
        setting and make the editor more accessible.
    -javascript changes to link the new setting to the correct data model.
    -sass updates to fix a11y issues on the editor modal.
    -addition of post-due visibility information to course outline in studio.
    -new tests: python, js, acceptance, and a11y
This commit is contained in:
Eric Fischer
2016-04-21 09:26:22 -04:00
parent ecfd953e80
commit ea77c3ec42
15 changed files with 480 additions and 337 deletions

View File

@@ -598,7 +598,7 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
var getDisplayNameWrapper, setEditModalValues, mockServerValuesJson,
selectDisableSpecialExams, selectBasicSettings, selectAdvancedSettings,
selectAccessSettings, selectTimedExam, selectProctoredExam, selectPracticeExam,
selectPrerequisite, selectLastPrerequisiteSubsection;
selectPrerequisite, selectLastPrerequisiteSubsection, checkOptionFieldVisibility;
getDisplayNameWrapper = function() {
return getItemHeaders('subsection').find('.wrapper-xblock-field');
@@ -612,7 +612,7 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
};
selectDisableSpecialExams = function() {
this.$("#id_not_timed").prop('checked', true).trigger('change');
this.$("input.no_special_exam").prop('checked', true).trigger('change');
};
selectBasicSettings = function() {
@@ -627,22 +627,23 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
this.$(".modal-section .settings-tab-button[data-tab='access']").click();
};
selectTimedExam = function(time_limit) {
this.$("#id_timed_exam").prop('checked', true).trigger('change');
this.$("#id_time_limit").val(time_limit);
this.$("#id_time_limit").trigger('focusout');
selectTimedExam = function(time_limit, hide_after_due) {
this.$("input.timed_exam").prop('checked', true).trigger('change');
this.$(".field-time-limit input").val(time_limit);
this.$(".field-time-limit input").trigger('focusout');
this.$('.field-hide-after-due input').prop('checked', hide_after_due).trigger('change');
};
selectProctoredExam = function(time_limit) {
this.$("#id_proctored_exam").prop('checked', true).trigger('change');
this.$("#id_time_limit").val(time_limit);
this.$("#id_time_limit").trigger('focusout');
this.$("input.proctored_exam").prop('checked', true).trigger('change');
this.$(".field-time-limit input").val(time_limit);
this.$(".field-time-limit input").trigger('focusout');
};
selectPracticeExam = function(time_limit) {
this.$("#id_practice_exam").prop('checked', true).trigger('change');
this.$("#id_time_limit").val(time_limit);
this.$("#id_time_limit").trigger('focusout');
this.$("input.practice_exam").prop('checked', true).trigger('change');
this.$(".field-time-limit input").val(time_limit);
this.$(".field-time-limit input").trigger('focusout');
};
selectPrerequisite = function() {
@@ -654,6 +655,13 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
this.$("#prereq_min_score").val(minScore).trigger('keyup');
};
// Helper to validate oft-checked additional option fields' visibility
checkOptionFieldVisibility = function(time_limit, review_rules, hide_after_due) {
expect($('.field-time-limit').is(':visible')).toBe(time_limit);
expect($('.field-exam-review-rules').is(':visible')).toBe(review_rules);
expect($('.field-hide-after-due').is(':visible')).toBe(hide_after_due);
};
// Contains hard-coded dates because dates are presented in different formats.
mockServerValuesJson = createMockSectionJSON({
release_date: 'Jan 01, 2970 at 05:00 UTC'
@@ -670,8 +678,9 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
is_prereq: false,
"is_time_limited": true,
"is_practice_exam": false,
"is_proctored_exam": true,
"default_time_limit_minutes": 150
"is_proctored_exam": false,
"default_time_limit_minutes": 150,
"hide_after_due": true,
}, [
createMockVerticalJSON({
has_changes: true,
@@ -815,13 +824,13 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
for (i = 0; i < valid_times.length; i++){
time_limit = valid_times[i];
selectTimedExam(time_limit);
expect($("#id_time_limit").val()).toEqual(time_limit);
expect($(".field-time-limit input").val()).toEqual(time_limit);
}
for (i = 0; i < invalid_times.length; i++){
time_limit = invalid_times[i];
selectTimedExam(time_limit);
expect($("#id_time_limit").val()).not.toEqual(time_limit);
expect($("#id_time_limit").val()).toEqual(default_time);
expect($(".field-time-limit input").val()).not.toEqual(time_limit);
expect($(".field-time-limit input").val()).toEqual(default_time);
}
});
@@ -829,7 +838,8 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
selectProctoredExam("02:30");
selectAdvancedSettings();
selectTimedExam("02:30", true);
$(".wrapper-modal-window .action-save").click();
AjaxHelpers.expectJsonRequest(requests, 'POST', '/xblock/mock-subsection', {
"graderType":"Lab",
@@ -842,8 +852,9 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
"exam_review_rules": "",
"is_time_limited": true,
"is_practice_exam": false,
"is_proctored_enabled": true,
"default_time_limit_minutes": 150
"is_proctored_enabled": false,
"default_time_limit_minutes": 150,
"hide_after_due": true,
}
});
expect(requests[0].requestHeaders['X-HTTP-Method-Override']).toBe('PATCH');
@@ -872,30 +883,35 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
expect($("#due_date").val()).toBe('7/10/2014');
expect($("#grading_type").val()).toBe('Lab');
expect($("#staff_lock").is(":checked")).toBe(true);
expect($("#id_timed_exam").is(":checked")).toBe(false);
expect($("#id_proctored_exam").is(":checked")).toBe(true);
expect($("#id_not_timed").is(":checked")).toBe(false);
expect($("#id_practice_exam").is(":checked")).toBe(false);
expect($("#id_time_limit").val()).toBe("02:30");
expect($("input.timed_exam").is(":checked")).toBe(true);
expect($("input.proctored_exam").is(":checked")).toBe(false);
expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("02:30");
expect($(".field-hide-after-due input").is(":checked")).toBe(true);
});
it('can hide the time limit field when the None radio box is selected', function() {
it('can hide time limit and hide after due fields when the None radio box is selected', function() {
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
selectAdvancedSettings();
selectDisableSpecialExams();
// id_time_limit_div should be hidden when None is specified
expect($('#id_time_limit_div')).toHaveClass('is-hidden');
// all additional options should be hidden
expect($('.exam-options').is(':hidden')).toBe(true);
});
it('can select the practice exam', function() {
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
selectAdvancedSettings();
selectPracticeExam("00:30");
// id_time_limit_div should not be hidden when practice exam is specified
expect($('#id_time_limit_div')).not.toHaveClass('is-hidden"');
// time limit should be visible, review rules and hide after due should be hidden
checkOptionFieldVisibility(true, false, false);
$(".wrapper-modal-window .action-save").click();
});
@@ -903,9 +919,12 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
selectAdvancedSettings();
selectTimedExam("00:30");
// id_time_limit_div should not be hidden when timed exam is specified
expect($('#id_time_limit_div')).not.toHaveClass('is-hidden"');
// time limit and hide after due should be visible, review rules should be hidden
checkOptionFieldVisibility(true, false, true);
$(".wrapper-modal-window .action-save").click();
});
@@ -913,9 +932,12 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
selectAdvancedSettings();
selectProctoredExam("00:30");
// id_time_limit_div should not be hidden when timed exam is specified
expect($('#id_time_limit_div')).not.toHaveClass('is-hidden"');
// time limit and review rules should be visible, hide after due should be hidden
checkOptionFieldVisibility(true, true, false);
$(".wrapper-modal-window .action-save").click();
});
@@ -924,10 +946,12 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
selectAdvancedSettings();
selectProctoredExam("abcd");
// id_time_limit_div should not be hidden when timed exam is specified
expect($('#id_time_limit_div')).not.toHaveClass('is-hidden"');
expect($('#id_time_limit')).toHaveValue('00:30');
// time limit field should be visible and have the correct value
expect($('.field-time-limit').is(':visible')).toBe(true);
expect($('.field-time-limit input').val()).toEqual("00:30");
});
@@ -944,21 +968,24 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
"is_time_limited": false,
"is_practice_exam": false,
"is_proctored_exam": false,
"default_time_limit_minutes": 150
"default_time_limit_minutes": 150,
"hide_after_due": false,
}, [
]),
])
]);
createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
expect($("#id_timed_exam").is(":checked")).toBe(false);
expect($("#id_proctored_exam").is(":checked")).toBe(false);
expect($("#id_not_timed").is(":checked")).toBe(true);
expect($("#id_practice_exam").is(":checked")).toBe(false);
expect($("#id_time_limit").val()).toBe("02:30");
selectAdvancedSettings();
expect($("input.timed_exam").is(":checked")).toBe(false);
expect($("input.proctored_exam").is(":checked")).toBe(false);
expect($("input.no_special_exam").is(":checked")).toBe(true);
expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("02:30");
expect($('.field-hide-after-due').is(':hidden')).toBe(true);
});
it('can show a saved timed exam correctly', function() {
it('can show a saved timed exam correctly when hide_after_due is true', function() {
var mockCourseWithSpecialExamJSON = createMockCourseJSON({}, [
createMockSectionJSON({
has_changes: true,
@@ -971,18 +998,51 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
"is_time_limited": true,
"is_practice_exam": false,
"is_proctored_exam": false,
"default_time_limit_minutes": 10
"default_time_limit_minutes": 10,
"hide_after_due": true,
}, [
]),
])
]);
createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
expect($("#id_timed_exam").is(":checked")).toBe(true);
expect($("#id_proctored_exam").is(":checked")).toBe(false);
expect($("#id_not_timed").is(":checked")).toBe(false);
expect($("#id_practice_exam").is(":checked")).toBe(false);
expect($("#id_time_limit").val()).toBe("00:10");
selectAdvancedSettings();
expect($("input.timed_exam").is(":checked")).toBe(true);
expect($("input.proctored_exam").is(":checked")).toBe(false);
expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("00:10");
expect($('.field-hide-after-due input').is(":checked")).toBe(true);
});
it('can show a saved timed exam correctly when hide_after_due is true', function() {
var mockCourseWithSpecialExamJSON = createMockCourseJSON({}, [
createMockSectionJSON({
has_changes: true,
enable_proctored_exams: true,
enable_timed_exams: true
}, [
createMockSubsectionJSON({
has_changes: true,
"is_time_limited": true,
"is_practice_exam": false,
"is_proctored_exam": false,
"default_time_limit_minutes": 10,
"hide_after_due": false,
}, [
]),
])
]);
createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
selectAdvancedSettings();
expect($("input.timed_exam").is(":checked")).toBe(true);
expect($("input.proctored_exam").is(":checked")).toBe(false);
expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("00:10");
expect($('.field-hide-after-due input').is(":checked")).toBe(false);
});
it('can show a saved practice exam correctly', function() {
@@ -1005,11 +1065,13 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
]);
createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
expect($("#id_timed_exam").is(":checked")).toBe(false);
expect($("#id_proctored_exam").is(":checked")).toBe(false);
expect($("#id_not_timed").is(":checked")).toBe(false);
expect($("#id_practice_exam").is(":checked")).toBe(true);
expect($("#id_time_limit").val()).toBe("02:30");
selectAdvancedSettings();
expect($("input.timed_exam").is(":checked")).toBe(false);
expect($("input.proctored_exam").is(":checked")).toBe(false);
expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(true);
expect($(".field-time-limit input").val()).toBe("02:30");
expect($('.field-hide-after-due').is(':hidden')).toBe(true);
});
it('can show a saved proctored exam correctly', function() {
@@ -1032,11 +1094,13 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
]);
createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
expect($("#id_timed_exam").is(":checked")).toBe(false);
expect($("#id_proctored_exam").is(":checked")).toBe(true);
expect($("#id_not_timed").is(":checked")).toBe(false);
expect($("#id_practice_exam").is(":checked")).toBe(false);
expect($("#id_time_limit").val()).toBe("02:30");
selectAdvancedSettings();
expect($("input.timed_exam").is(":checked")).toBe(false);
expect($("input.proctored_exam").is(":checked")).toBe(true);
expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("02:30");
expect($('.field-hide-after-due').is(':hidden')).toBe(true);
});
it('does not show proctored settings if proctored exams not enabled', function() {
@@ -1052,16 +1116,19 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
"is_time_limited": true,
"is_practice_exam": false,
"is_proctored_exam": false,
"default_time_limit_minutes": 150
"default_time_limit_minutes": 150,
"hide_after_due": true,
}, [
]),
])
]);
createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
expect($("#id_timed_exam").is(":checked")).toBe(true);
expect($("#id_not_timed").is(":checked")).toBe(false);
expect($("#id_time_limit").val()).toBe("02:30");
selectAdvancedSettings();
expect($("input.timed_exam").is(":checked")).toBe(true);
expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("02:30");
expect($('.field-hide-after-due input').is(":checked")).toBe(true);
});
it('can select prerequisite', function() {