blueprint: prevent changing [not-]graded when locked-by-points

test plan:
 - configure a blueprint course to lock points (and not content)
   for assignments
 - create two assignments in the blueprint, one graded, and
   one not graded, and lock both
 - sync to associated courses
 - in an associated course, confirm that:
   1. the graded assignment can't be changed to "Not Graded"
   2. the non-graded assignment can't be changed away from
      "Not Graded"
      2a. a screenreader sees "Display Grade as" label
          linked to a read-only input containing "Not Graded"
   3. the content in both assignments can still be edited and
      the assignment can be saved in the associated course
   4. for an assignment in a closed grading period, "Display
      Grade as" should be read-only as in step 2

fixes ADMIN-789

Change-Id: I76e0fba272df1a43e2cf12cfa38ecbf6896b54b6
Reviewed-on: https://gerrit.instructure.com/141797
Reviewed-by: Mysti Sadler <mysti@instructure.com>
Reviewed-by: Ed Schiebel <eschiebel@instructure.com>
QA-Review: Ed Schiebel <eschiebel@instructure.com>
Tested-by: Jenkins
Product-Review: Jeremy Stanley <jeremy@instructure.com>
This commit is contained in:
Jeremy Stanley 2018-02-23 16:01:32 -07:00
parent ec511dbf16
commit 1b745794b3
6 changed files with 83 additions and 35 deletions

View File

@ -595,6 +595,11 @@ define [
nf:(number, {hash: {format}}) ->
numberFormat[format](number)
# Public: look up an element of a hash or array
#
lookup: (obj, key) ->
obj && obj[key]
}
return Handlebars

View File

@ -50,6 +50,7 @@ define [
@optionProperty 'parentModel'
@optionProperty 'nested'
@optionProperty 'preventNotGraded'
@optionProperty 'lockedItems'
handleGradingTypeChange: (ev) =>
gradingType = @$gradingType.val()
@ -80,6 +81,14 @@ define [
close: -> $(ev.target).focus()
).fixDialogButtons()
gradingTypeMap: () ->
percent: I18n.t 'grading_type_options.percent', 'Percentage'
pass_fail: I18n.t 'grading_type_options.pass_fail', 'Complete/Incomplete'
points: I18n.t 'grading_type_options.points', 'Points'
letter_grade: I18n.t 'grading_type_options.letter_grade', 'Letter Grade'
gpa_scale: I18n.t 'grading_type_options.gpa_scale', 'GPA Scale'
not_graded: I18n.t 'grading_type_options.not_graded', 'Not Graded'
toJSON: =>
gradingType: @parentModel.gradingType()
isNotGraded: @parentModel.isNotGraded()
@ -87,7 +96,8 @@ define [
gpaScaleQuestionLabel: I18n.t('gpa_scale_explainer', "What is GPA Scale Grading?")
isGpaScaled: @parentModel.isGpaScaled()
gradingStandardId: @parentModel.gradingStandardId()
frozenAttributes: @parentModel.frozenAttributes()
nested: @nested
preventNotGraded: @preventNotGraded
inClosedGradingPeriod: @parentModel.inClosedGradingPeriod
preventNotGraded: @preventNotGraded || (@lockedItems?.points && !@parentModel.isNotGraded())
freezeGradingType: _.include(@parentModel.frozenAttributes(), 'grading_type') ||
@parentModel.inClosedGradingPeriod() || (@lockedItems?.points && @parentModel.isNotGraded())
gradingTypeMap: @gradingTypeMap()

View File

@ -32,6 +32,7 @@ import LockManager from '../blueprint_courses/apps/LockManager'
const lockManager = new LockManager()
lockManager.init({ itemType: 'assignment', page: 'edit' })
const lockedItems = lockManager.isChildContent() ? lockManager.getItemLocks() : {}
ENV.ASSIGNMENT.assignment_overrides = ENV.ASSIGNMENT_OVERRIDES
@ -49,7 +50,8 @@ const assignmentGroupSelector = new AssignmentGroupSelector({
})
const gradingTypeSelector = new GradingTypeSelector({
parentModel: assignment,
preventNotGraded: assignment.submissionTypesFrozen()
preventNotGraded: assignment.submissionTypesFrozen(),
lockedItems
})
const groupCategorySelector = new GroupCategorySelector({
parentModel: assignment,
@ -62,8 +64,6 @@ const peerReviewsSelector = new PeerReviewsSelector({
const headerEl = ENV.CONDITIONAL_RELEASE_SERVICE_ENABLED ? '#edit_assignment_header-cr' : '#edit_assignment_header'
const lockedItems = lockManager.isChildContent() ? lockManager.getItemLocks() : {}
const editView = new EditView({
el: '#edit_assignment_form',
model: assignment,

View File

@ -5,35 +5,35 @@
</div>
<div class="form-column-right">
{{#if freezeGradingType}}
<input type="text" id="assignment_grading_type" readonly value="{{lookup gradingTypeMap gradingType}}"/>
<input type="hidden" name="{{#if nested}}assignment[grading_type]{{else}}grading_type{{/if}}" value="{{gradingType}}"/>
{{else}}
<select id="assignment_grading_type"
name="{{#if nested}}assignment[grading_type]{{else}}grading_type{{/if}}"
{{#if inClosedGradingPeriod}}
readonly
aria-readonly="true"
{{/if}}
aria-controls="graded_assignment_fields view-grading-levels"
{{disabledIfIncludes frozenAttributes "grading_type"}}>
aria-controls="graded_assignment_fields view-grading-levels">
<option value="percent" {{selectedIf gradingType "percent"}}>
{{#t "grading_type_options.percent"}}Percentage{{/t}}
{{gradingTypeMap.percent}}
</option>
<option value="pass_fail" {{selectedIf gradingType "pass_fail" }}>
{{#t "grading_type_options.pass_fail"}}Complete/Incomplete{{/t}}
{{gradingTypeMap.pass_fail}}
</option>
<option value="points" {{selectedIf gradingType "points"}}>
{{#t "grading_type_options.points"}}Points{{/t}}
{{gradingTypeMap.points}}
</option>
<option value="letter_grade" {{selectedIf gradingType "letter_grade"}}>
{{#t "grading_type_options.letter_grade"}}Letter Grade{{/t}}
{{gradingTypeMap.letter_grade}}
</option>
<option value="gpa_scale" {{selectedIf gradingType "gpa_scale"}}>
{{#t "grading_type_options.gpa_scale"}}GPA Scale{{/t}}
{{gradingTypeMap.gpa_scale}}
</option>
{{#unless preventNotGraded}}
<option value="not_graded" {{selectedIf isNotGraded}}>
{{#t "grading_type_options.not_graded"}}Not Graded{{/t}}
{{gradingTypeMap.not_graded}}
</option>
{{/unless}}
</select>
{{/if}}
<a id=gpa-scale-question
href="#"
aria-label="{{gpaScaleQuestionLabel}}"

View File

@ -353,11 +353,19 @@ test('disables fields when inClosedGradingPeriod', function() {
ok(view.$el.find('#assignment_group_id').attr('readonly'))
equal(view.$el.find('#assignment_group_id').attr('aria-readonly'), 'true')
ok(view.$el.find('#assignment_grading_type').attr('readonly'))
equal(view.$el.find('#assignment_grading_type').attr('aria-readonly'), 'true')
equal(view.$el.find('input[name="grading_type"]').attr('type'), 'hidden')
ok(view.$el.find('#has_group_category').attr('readonly'))
equal(view.$el.find('#has_group_category').attr('aria-readonly'), 'true')
})
test('disables grading type field when frozen', function() {
const view = this.editView({frozen_attributes: ['grading_type']})
view.$el.appendTo($('#fixtures'))
ok(view.$el.find('#assignment_grading_type').attr('readonly'))
equal(view.$el.find('input[name="grading_type"]').attr('type'), 'hidden')
})
test('does not disable post to sis when inClosedGradingPeriod', function() {
ENV.POST_TO_SIS = true
const view = this.editView({in_closed_grading_period: true})

View File

@ -160,6 +160,31 @@ describe "blueprint courses assignments" do
expect(f("#due_at").attribute("readonly")).to eq "true"
expect(f("#unlock_at").attribute("readonly")).to eq "true"
expect(f("#lock_at").attribute("readonly")).to eq "true"
expect(f('#assignment_grading_type')).to contain_css('option[value="points"]')
expect(f('#assignment_grading_type')).not_to contain_css('option[value="not_graded"]')
end
it "should not allow making a non-graded assignment graded when points are locked" do
not_graded_assignment = @copy_from.assignments.create!(
title: "eh", description: "meh", submission_types: "not_graded", grading_type: 'not_graded'
)
nga_tag = @template.create_content_tag_for!(not_graded_assignment)
# fake the copy instead of doing a full migration
nga_copy = @copy_to.assignments.create(
title: "eh", description: "meh", submission_types: "not_graded", grading_type: 'not_graded'
)
nga_copy.migration_id = nga_tag.migration_id
nga_copy.save!
nga_tag.update(restrictions: {content: true, points: true, due_dates: true, availability_dates: true})
get "/courses/#{@copy_to.id}/assignments/#{nga_copy.id}/edit"
node = f "#assignment_grading_type"
expect(node.tag_name).to eq 'input'
expect(node.attribute('readonly')).to eq 'true'
expect(f('input[name="grading_type"][type="hidden"]').attribute('value')).to eq 'not_graded'
end
it "should not allow popup editing of restricted items" do