spec: update grading standard component specs
refs GRADE-544 test plan: * Verify Jenkins likey Change-Id: Iff885917abb73d7fb56532e8759836ff9d1f8e28 Reviewed-on: https://gerrit.instructure.com/152232 Tested-by: Jenkins Reviewed-by: Spencer Olson <solson@instructure.com> Product-Review: Jeremy Neander <jneander@instructure.com> QA-Review: Jeremy Neander <jneander@instructure.com>
This commit is contained in:
parent
9e30db867c
commit
1fc92e13f9
|
@ -1,555 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 - present Instructure, Inc.
|
||||
*
|
||||
* This file is part of Canvas.
|
||||
*
|
||||
* Canvas is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import {Simulate} from 'react-addons-test-utils'
|
||||
import $ from 'jquery'
|
||||
import GradingStandard from 'jsx/grading/gradingStandard'
|
||||
|
||||
QUnit.module('GradingStandard not being edited', {
|
||||
setup() {
|
||||
this.props = {
|
||||
key: 1,
|
||||
standard: {
|
||||
id: 1,
|
||||
title: 'Test Grading Standard',
|
||||
data: [['A', 0.92], ['A-', 0.9], ['F', 0]]
|
||||
},
|
||||
permissions: {manage: true},
|
||||
editing: false,
|
||||
justAdded: false,
|
||||
othersEditing: false,
|
||||
round(number) {
|
||||
return Math.round(number * 100) / 100
|
||||
},
|
||||
onSetEditingStatus() {},
|
||||
onDeleteGradingStandard() {},
|
||||
onSaveGradingStandard() {}
|
||||
}
|
||||
this.mountPoint = $('<div>').appendTo('#fixtures')[0]
|
||||
const GradingStandardElement = <GradingStandard {...this.props} />
|
||||
this.gradingStandard = ReactDOM.render(GradingStandardElement, this.mountPoint)
|
||||
},
|
||||
teardown() {
|
||||
ReactDOM.unmountComponentAtNode(this.mountPoint)
|
||||
$('#fixtures').empty()
|
||||
}
|
||||
})
|
||||
|
||||
test('returns false for assessedAssignment', function() {
|
||||
deepEqual(this.gradingStandard.assessedAssignment(), false)
|
||||
})
|
||||
|
||||
test('renders the correct title', function() {
|
||||
// 'Grading standard title' is wrapped in a screenreader-only span that this
|
||||
// test suite does not ignore. 'Grading standadr title' is not actually displayed
|
||||
deepEqual(
|
||||
this.gradingStandard.refs.title.textContent,
|
||||
'Grading standard titleTest Grading Standard'
|
||||
)
|
||||
})
|
||||
|
||||
test('renders the correct id name', function() {
|
||||
deepEqual(this.gradingStandard.renderIdNames(), 'grading_standard_1')
|
||||
})
|
||||
|
||||
test('renders the edit button', function() {
|
||||
ok(this.gradingStandard.refs.editButton)
|
||||
})
|
||||
|
||||
test('calls onSetEditingStatus when edit button is clicked', function() {
|
||||
const setEditingStatus = this.spy(this.props, 'onSetEditingStatus')
|
||||
const GradingStandardElement = <GradingStandard {...this.props} />
|
||||
this.gradingStandard = ReactDOM.render(GradingStandardElement, this.mountPoint)
|
||||
Simulate.click(this.gradingStandard.refs.editButton)
|
||||
ok(setEditingStatus.calledOnce)
|
||||
return setEditingStatus.restore()
|
||||
})
|
||||
|
||||
test('renders the delete button', function() {
|
||||
ok(this.gradingStandard.refs.deleteButton)
|
||||
})
|
||||
|
||||
test('calls onDeleteGradingStandard when delete button is clicked', function() {
|
||||
const deleteGradingStandard = this.spy(this.props, 'onDeleteGradingStandard')
|
||||
const GradingStandardElement = <GradingStandard {...this.props} />
|
||||
this.gradingStandard = ReactDOM.render(GradingStandardElement, this.mountPoint)
|
||||
Simulate.click(this.gradingStandard.refs.deleteButton)
|
||||
ok(deleteGradingStandard.calledOnce)
|
||||
return deleteGradingStandard.restore()
|
||||
})
|
||||
|
||||
test('does not show a message about not being able to manage', function() {
|
||||
deepEqual(this.gradingStandard.refs.cannotManageMessage, undefined)
|
||||
})
|
||||
|
||||
test('does not show the save button', function() {
|
||||
deepEqual(this.gradingStandard.refs.saveButton, undefined)
|
||||
})
|
||||
|
||||
test('does not show the cancel button', function() {
|
||||
deepEqual(this.gradingStandard.refs.cancelButton, undefined)
|
||||
})
|
||||
|
||||
QUnit.module("GradingStandard without 'manage' permissions", {
|
||||
setup() {
|
||||
const props = {
|
||||
key: 1,
|
||||
standard: {
|
||||
id: 1,
|
||||
title: 'Test Grading Standard',
|
||||
data: [['A', 0.92], ['A-', 0.9], ['F', 0]],
|
||||
context_type: 'Account'
|
||||
},
|
||||
permissions: {manage: false},
|
||||
editing: false,
|
||||
justAdded: false,
|
||||
othersEditing: false,
|
||||
round(number) {
|
||||
return Math.round(number * 100) / 100
|
||||
},
|
||||
onSetEditingStatus() {},
|
||||
onDeleteGradingStandard() {},
|
||||
onSaveGradingStandard() {}
|
||||
}
|
||||
const GradingStandardElement = <GradingStandard {...props} />
|
||||
this.gradingStandard = ReactDOM.render(
|
||||
GradingStandardElement,
|
||||
$('<div>').appendTo('#fixtures')[0]
|
||||
)
|
||||
},
|
||||
teardown() {
|
||||
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this.gradingStandard).parentNode)
|
||||
$('#fixtures').empty()
|
||||
}
|
||||
})
|
||||
|
||||
test('displays a cannot manage message', function() {
|
||||
ok(this.gradingStandard.refs.cannotManageMessage)
|
||||
})
|
||||
|
||||
test('disables edit and delete buttons', function() {
|
||||
ok(this.gradingStandard.refs.disabledButtons)
|
||||
})
|
||||
|
||||
QUnit.module('GradingStandard being edited', {
|
||||
setup() {
|
||||
this.props = {
|
||||
key: 1,
|
||||
standard: {
|
||||
id: 1,
|
||||
title: 'Test Grading Standard',
|
||||
data: [['A', 0.92], ['A-', 0.9], ['F', 0]]
|
||||
},
|
||||
permissions: {manage: true},
|
||||
editing: true,
|
||||
justAdded: false,
|
||||
othersEditing: false,
|
||||
round(number) {
|
||||
return Math.round(number * 100) / 100
|
||||
},
|
||||
onSetEditingStatus() {},
|
||||
onDeleteGradingStandard() {},
|
||||
onSaveGradingStandard: sinon.spy()
|
||||
}
|
||||
this.mountPoint = $('<div>').appendTo('#fixtures')[0]
|
||||
const GradingStandardElement = <GradingStandard {...this.props} />
|
||||
this.gradingStandard = ReactDOM.render(GradingStandardElement, this.mountPoint)
|
||||
},
|
||||
teardown() {
|
||||
ReactDOM.unmountComponentAtNode(this.mountPoint)
|
||||
}
|
||||
})
|
||||
|
||||
test('does not render the edit button', function() {
|
||||
deepEqual(this.gradingStandard.refs.editButton, undefined)
|
||||
})
|
||||
|
||||
test('does not render the delete button', function() {
|
||||
deepEqual(this.gradingStandard.refs.deleteButton, undefined)
|
||||
})
|
||||
|
||||
test('renders the save button', function() {
|
||||
ok(this.gradingStandard.refs.saveButton)
|
||||
})
|
||||
|
||||
test('rowNamesAreValid() returns true with non-empty, unique row names', function() {
|
||||
deepEqual(this.gradingStandard.rowNamesAreValid(), true)
|
||||
})
|
||||
|
||||
test('rowDataIsValid() returns true with non-empty, unique, non-overlapping row values', function() {
|
||||
deepEqual(this.gradingStandard.rowDataIsValid(), true)
|
||||
})
|
||||
|
||||
test('calls onSaveGradingStandard save button is clicked', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
ok(this.gradingStandard.props.onSaveGradingStandard.calledOnce)
|
||||
})
|
||||
|
||||
test('sets the state to saving when the save button is clicked', function() {
|
||||
deepEqual(this.gradingStandard.state.saving, false)
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
deepEqual(this.gradingStandard.state.saving, true)
|
||||
})
|
||||
|
||||
test('shows the cancel button', function() {
|
||||
ok(this.gradingStandard.refs.cancelButton)
|
||||
})
|
||||
|
||||
test('calls onSetEditingStatus when the cancel button is clicked', function() {
|
||||
const setEditingStatus = this.spy(this.props, 'onSetEditingStatus')
|
||||
const GradingStandardElement = <GradingStandard {...this.props} />
|
||||
this.gradingStandard = ReactDOM.render(GradingStandardElement, this.mountPoint)
|
||||
Simulate.click(this.gradingStandard.refs.cancelButton)
|
||||
ok(setEditingStatus.calledOnce)
|
||||
return setEditingStatus.restore()
|
||||
})
|
||||
|
||||
test('deletes the correct row on the editingStandard when deleteDataRow is called', function() {
|
||||
this.gradingStandard.deleteDataRow(1)
|
||||
deepEqual(this.gradingStandard.state.editingStandard.data, [['A', 0.92], ['F', 0]])
|
||||
})
|
||||
|
||||
test('does not delete the row if it is the last data row remaining', function() {
|
||||
this.gradingStandard.deleteDataRow(1)
|
||||
deepEqual(this.gradingStandard.state.editingStandard.data, [['A', 0.92], ['F', 0]])
|
||||
this.gradingStandard.deleteDataRow(0)
|
||||
deepEqual(this.gradingStandard.state.editingStandard.data, [['F', 0]])
|
||||
this.gradingStandard.deleteDataRow(0)
|
||||
deepEqual(this.gradingStandard.state.editingStandard.data, [['F', 0]])
|
||||
})
|
||||
|
||||
test('inserts the correct row on the editingStandard when insertGradingStandardRow is called', function() {
|
||||
this.gradingStandard.insertGradingStandardRow(1)
|
||||
deepEqual(this.gradingStandard.state.editingStandard.data, [
|
||||
['A', 0.92],
|
||||
['A-', 0.9],
|
||||
['', ''],
|
||||
['F', 0]
|
||||
])
|
||||
})
|
||||
|
||||
test('changes the title on the editingStandard when title input changes', function() {
|
||||
Simulate.change(this.gradingStandard.refs.title, {target: {value: 'Brand new title'}})
|
||||
deepEqual(this.gradingStandard.state.editingStandard.title, 'Brand new title')
|
||||
})
|
||||
|
||||
test('changes the min score on the editingStandard when changeRowMinScore is called', function() {
|
||||
this.gradingStandard.changeRowMinScore(2, '23')
|
||||
deepEqual(this.gradingStandard.state.editingStandard.data[2], ['F', '23'])
|
||||
})
|
||||
|
||||
test('changes the row name on the editingStandard when changeRowName is called', function() {
|
||||
this.gradingStandard.changeRowName(2, 'Q')
|
||||
deepEqual(this.gradingStandard.state.editingStandard.data[2], ['Q', 0])
|
||||
})
|
||||
|
||||
QUnit.module('GradingStandard being edited with blank names', {
|
||||
setup() {
|
||||
const props = {
|
||||
key: 1,
|
||||
standard: {
|
||||
id: 1,
|
||||
title: 'Test Grading Standard',
|
||||
data: [['A', 0.92], ['', 0.9], ['F', 0]]
|
||||
},
|
||||
permissions: {manage: true},
|
||||
editing: true,
|
||||
justAdded: false,
|
||||
othersEditing: false,
|
||||
round(number) {
|
||||
return Math.round(number * 100) / 100
|
||||
},
|
||||
onSetEditingStatus() {},
|
||||
onDeleteGradingStandard() {},
|
||||
onSaveGradingStandard() {}
|
||||
}
|
||||
const GradingStandardElement = <GradingStandard {...props} />
|
||||
this.gradingStandard = ReactDOM.render(
|
||||
GradingStandardElement,
|
||||
$('<div>').appendTo('#fixtures')[0]
|
||||
)
|
||||
},
|
||||
teardown() {
|
||||
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this.gradingStandard).parentNode)
|
||||
$('#fixtures').empty()
|
||||
}
|
||||
})
|
||||
|
||||
test('rowNamesAreValid() returns false with empty row names', function() {
|
||||
deepEqual(this.gradingStandard.rowNamesAreValid(), false)
|
||||
})
|
||||
|
||||
test('alerts the user that the input is invalid when they try to save', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
ok(this.gradingStandard.refs.invalidStandardAlert)
|
||||
})
|
||||
|
||||
test('shows a messsage describing why the input is invalid', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
deepEqual(
|
||||
this.gradingStandard.refs.invalidStandardAlert.textContent,
|
||||
"Cannot have duplicate or empty row names. Fix the names and try clicking 'Save' again."
|
||||
)
|
||||
})
|
||||
|
||||
QUnit.module('GradingStandard being edited with duplicate names', {
|
||||
setup() {
|
||||
const props = {
|
||||
key: 1,
|
||||
standard: {
|
||||
id: 1,
|
||||
title: 'Test Grading Standard',
|
||||
data: [['A', 0.92], ['A', 0.9], ['F', 0]]
|
||||
},
|
||||
permissions: {manage: true},
|
||||
editing: true,
|
||||
justAdded: false,
|
||||
othersEditing: false,
|
||||
round(number) {
|
||||
return Math.round(number * 100) / 100
|
||||
},
|
||||
onSetEditingStatus() {},
|
||||
onDeleteGradingStandard() {},
|
||||
onSaveGradingStandard() {}
|
||||
}
|
||||
const GradingStandardElement = <GradingStandard {...props} />
|
||||
this.gradingStandard = ReactDOM.render(
|
||||
GradingStandardElement,
|
||||
$('<div>').appendTo('#fixtures')[0]
|
||||
)
|
||||
},
|
||||
teardown() {
|
||||
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this.gradingStandard).parentNode)
|
||||
$('#fixtures').empty()
|
||||
}
|
||||
})
|
||||
|
||||
test('rowNamesAreValid() returns false with duplicate row names', function() {
|
||||
deepEqual(this.gradingStandard.rowNamesAreValid(), false)
|
||||
})
|
||||
|
||||
test('alerts the user that the input is invalid when they try to save', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
ok(this.gradingStandard.refs.invalidStandardAlert)
|
||||
})
|
||||
|
||||
test('shows a messsage describing why the input is invalid', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
deepEqual(
|
||||
this.gradingStandard.refs.invalidStandardAlert.textContent,
|
||||
"Cannot have duplicate or empty row names. Fix the names and try clicking 'Save' again."
|
||||
)
|
||||
})
|
||||
|
||||
QUnit.module('GradingStandard being edited with empty values', {
|
||||
setup() {
|
||||
const props = {
|
||||
key: 1,
|
||||
standard: {
|
||||
id: 1,
|
||||
title: 'Test Grading Standard',
|
||||
data: [['A', 0.92], ['B', 0.9], ['F', '']]
|
||||
},
|
||||
permissions: {manage: true},
|
||||
editing: true,
|
||||
justAdded: false,
|
||||
othersEditing: false,
|
||||
round(number) {
|
||||
return Math.round(number * 100) / 100
|
||||
},
|
||||
onSetEditingStatus() {},
|
||||
onDeleteGradingStandard() {},
|
||||
onSaveGradingStandard() {}
|
||||
}
|
||||
const GradingStandardElement = <GradingStandard {...props} />
|
||||
this.gradingStandard = ReactDOM.render(
|
||||
GradingStandardElement,
|
||||
$('<div>').appendTo('#fixtures')[0]
|
||||
)
|
||||
},
|
||||
teardown() {
|
||||
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this.gradingStandard).parentNode)
|
||||
$('#fixtures').empty()
|
||||
}
|
||||
})
|
||||
|
||||
test('rowDataIsValid() returns false with empty values', function() {
|
||||
deepEqual(this.gradingStandard.rowDataIsValid(), false)
|
||||
})
|
||||
|
||||
test('alerts the user that the input is invalid when they try to save', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
ok(this.gradingStandard.refs.invalidStandardAlert)
|
||||
})
|
||||
|
||||
test('shows a messsage describing why the input is invalid', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
deepEqual(
|
||||
this.gradingStandard.refs.invalidStandardAlert.textContent,
|
||||
"Cannot have overlapping or empty ranges. Fix the ranges and try clicking 'Save' again."
|
||||
)
|
||||
})
|
||||
|
||||
QUnit.module('GradingStandard being edited with duplicate values', {
|
||||
setup() {
|
||||
const props = {
|
||||
key: 1,
|
||||
standard: {
|
||||
id: 1,
|
||||
title: 'Test Grading Standard',
|
||||
data: [['A', 0.92], ['B', 0.92], ['F', 0]]
|
||||
},
|
||||
permissions: {manage: true},
|
||||
editing: true,
|
||||
justAdded: false,
|
||||
othersEditing: false,
|
||||
round(number) {
|
||||
return Math.round(number * 100) / 100
|
||||
},
|
||||
onSetEditingStatus() {},
|
||||
onDeleteGradingStandard() {},
|
||||
onSaveGradingStandard() {}
|
||||
}
|
||||
const GradingStandardElement = <GradingStandard {...props} />
|
||||
this.gradingStandard = ReactDOM.render(
|
||||
GradingStandardElement,
|
||||
$('<div>').appendTo('#fixtures')[0]
|
||||
)
|
||||
},
|
||||
teardown() {
|
||||
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this.gradingStandard).parentNode)
|
||||
$('#fixtures').empty()
|
||||
}
|
||||
})
|
||||
|
||||
test('rowDataIsValid() returns false with duplicate values', function() {
|
||||
deepEqual(this.gradingStandard.rowDataIsValid(), false)
|
||||
})
|
||||
|
||||
test('alerts the user that the input is invalid when they try to save', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
ok(this.gradingStandard.refs.invalidStandardAlert)
|
||||
})
|
||||
|
||||
test('shows a messsage describing why the input is invalid', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
deepEqual(
|
||||
this.gradingStandard.refs.invalidStandardAlert.textContent,
|
||||
"Cannot have overlapping or empty ranges. Fix the ranges and try clicking 'Save' again."
|
||||
)
|
||||
})
|
||||
|
||||
QUnit.module('GradingStandard being edited with values that round to the same number', {
|
||||
setup() {
|
||||
const props = {
|
||||
key: 1,
|
||||
standard: {
|
||||
id: 1,
|
||||
title: 'Test Grading Standard',
|
||||
data: [['A', 0.92], ['B', 0.91996], ['F', 0]]
|
||||
},
|
||||
permissions: {manage: true},
|
||||
editing: true,
|
||||
justAdded: false,
|
||||
othersEditing: false,
|
||||
round(number) {
|
||||
return Math.round(number * 100) / 100
|
||||
},
|
||||
onSetEditingStatus() {},
|
||||
onDeleteGradingStandard() {},
|
||||
onSaveGradingStandard() {}
|
||||
}
|
||||
const GradingStandardElement = <GradingStandard {...props} />
|
||||
this.gradingStandard = ReactDOM.render(
|
||||
GradingStandardElement,
|
||||
$('<div>').appendTo('#fixtures')[0]
|
||||
)
|
||||
},
|
||||
teardown() {
|
||||
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this.gradingStandard).parentNode)
|
||||
$('#fixtures').empty()
|
||||
}
|
||||
})
|
||||
|
||||
test('rowDataIsValid() returns false with if values round to the same number (91.996 rounds to 92)', function() {
|
||||
deepEqual(this.gradingStandard.rowDataIsValid(), false)
|
||||
})
|
||||
|
||||
test('alerts the user that the input is invalid when they try to save', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
ok(this.gradingStandard.refs.invalidStandardAlert)
|
||||
})
|
||||
|
||||
test('shows a messsage describing why the input is invalid', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
deepEqual(
|
||||
this.gradingStandard.refs.invalidStandardAlert.textContent,
|
||||
"Cannot have overlapping or empty ranges. Fix the ranges and try clicking 'Save' again."
|
||||
)
|
||||
})
|
||||
|
||||
QUnit.module('GradingStandard being edited with overlapping values', {
|
||||
setup() {
|
||||
const props = {
|
||||
key: 1,
|
||||
standard: {
|
||||
id: 1,
|
||||
title: 'Test Grading Standard',
|
||||
data: [['A', 0.92], ['B', 0.93], ['F', 0]]
|
||||
},
|
||||
permissions: {manage: true},
|
||||
editing: true,
|
||||
justAdded: false,
|
||||
othersEditing: false,
|
||||
round(number) {
|
||||
return Math.round(number * 100) / 100
|
||||
},
|
||||
onSetEditingStatus() {},
|
||||
onDeleteGradingStandard() {},
|
||||
onSaveGradingStandard() {}
|
||||
}
|
||||
const GradingStandardElement = <GradingStandard {...props} />
|
||||
this.gradingStandard = ReactDOM.render(
|
||||
GradingStandardElement,
|
||||
$('<div>').appendTo('#fixtures')[0]
|
||||
)
|
||||
},
|
||||
teardown() {
|
||||
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this.gradingStandard).parentNode)
|
||||
$('#fixtures').empty()
|
||||
}
|
||||
})
|
||||
|
||||
test('rowDataIsValid() returns false if scheme values overlap', function() {
|
||||
deepEqual(this.gradingStandard.rowDataIsValid(), false)
|
||||
})
|
||||
|
||||
test('alerts the user that the input is invalid when they try to save', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
ok(this.gradingStandard.refs.invalidStandardAlert)
|
||||
})
|
||||
|
||||
test('shows a messsage describing why the input is invalid', function() {
|
||||
Simulate.click(this.gradingStandard.refs.saveButton)
|
||||
deepEqual(
|
||||
this.gradingStandard.refs.invalidStandardAlert.textContent,
|
||||
"Cannot have overlapping or empty ranges. Fix the ranges and try clicking 'Save' again."
|
||||
)
|
||||
})
|
|
@ -0,0 +1,582 @@
|
|||
/*
|
||||
* Copyright (C) 2018 - present Instructure, Inc.
|
||||
*
|
||||
* This file is part of Canvas.
|
||||
*
|
||||
* Canvas is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from 'react'
|
||||
import {mount} from 'enzyme'
|
||||
|
||||
import fakeENV from 'helpers/fakeENV'
|
||||
import GradingStandard from 'jsx/grading/gradingStandard'
|
||||
|
||||
QUnit.module('GradingStandard', suiteHooks => {
|
||||
let props
|
||||
let wrapper
|
||||
|
||||
suiteHooks.beforeEach(() => {
|
||||
props = {
|
||||
editing: false,
|
||||
justAdded: false,
|
||||
othersEditing: false,
|
||||
permissions: {manage: true},
|
||||
round(number) {
|
||||
return Math.round(number * 100) / 100
|
||||
},
|
||||
onDeleteGradingStandard: sinon.spy(),
|
||||
onSaveGradingStandard: sinon.spy(),
|
||||
onSetEditingStatus: sinon.spy(),
|
||||
standard: {
|
||||
context_code: 'course_1201',
|
||||
context_id: '1201',
|
||||
context_name: 'Calculus 101',
|
||||
context_type: 'Course',
|
||||
data: [['A', 0.9], ['B', 0.8], ['C', 0.7], ['D', 0.6], ['F', 0]],
|
||||
id: '5001',
|
||||
title: 'Example Grading Scheme'
|
||||
},
|
||||
uniqueId: '5001'
|
||||
}
|
||||
|
||||
fakeENV.setup({context_asset_string: 'course_1201'})
|
||||
})
|
||||
|
||||
suiteHooks.afterEach(() => {
|
||||
wrapper.unmount()
|
||||
fakeENV.teardown()
|
||||
})
|
||||
|
||||
function mountComponent() {
|
||||
wrapper = mount(<GradingStandard {...props} />)
|
||||
}
|
||||
|
||||
function addRowAfter(rowIndex) {
|
||||
const dataRow = wrapper.find('.grading_standard_row').at(rowIndex)
|
||||
dataRow.find('.insert_row_button').simulate('click')
|
||||
}
|
||||
|
||||
function removeRow(rowIndex) {
|
||||
const dataRow = wrapper.find('.grading_standard_row').at(rowIndex)
|
||||
dataRow.find('.delete_row_button').simulate('click')
|
||||
}
|
||||
|
||||
function setRowName(rowIndex, name) {
|
||||
const dataRow = wrapper.find('.grading_standard_row').at(rowIndex)
|
||||
const input = dataRow.find('input.standard_name')
|
||||
input.simulate('change', {target: {value: name}})
|
||||
}
|
||||
|
||||
function setRowMinScore(rowIndex, score) {
|
||||
const dataRow = wrapper.find('.grading_standard_row').at(rowIndex)
|
||||
const input = dataRow.find('input.standard_value')
|
||||
input.simulate('change', {target: {value: score}})
|
||||
input.simulate('blur')
|
||||
}
|
||||
|
||||
QUnit.module('when not being edited', () => {
|
||||
test('includes the grading scheme id the id of the container', () => {
|
||||
mountComponent()
|
||||
strictEqual(wrapper.find('#grading_standard_5001').length, 1)
|
||||
})
|
||||
|
||||
test('displays the grading scheme title', () => {
|
||||
mountComponent()
|
||||
const title = wrapper.find('.title').text()
|
||||
ok(title.includes('Example Grading Scheme'))
|
||||
})
|
||||
|
||||
test('displays an edit button', () => {
|
||||
mountComponent()
|
||||
const button = wrapper.find('.edit_grading_standard_button')
|
||||
strictEqual(button.length, 1)
|
||||
})
|
||||
|
||||
test('does not display the button as read-only', () => {
|
||||
mountComponent()
|
||||
const button = wrapper.find('.edit_grading_standard_button')
|
||||
strictEqual(button.hasClass('read_only'), false)
|
||||
})
|
||||
|
||||
test('displays a delete button', () => {
|
||||
mountComponent()
|
||||
const button = wrapper.find('.delete_grading_standard_button')
|
||||
strictEqual(button.length, 1)
|
||||
})
|
||||
|
||||
test('does not display a save button', () => {
|
||||
mountComponent()
|
||||
const button = wrapper.find('.save_button')
|
||||
strictEqual(button.length, 0)
|
||||
})
|
||||
|
||||
test('does not display a cancel button', () => {
|
||||
mountComponent()
|
||||
const button = wrapper.find('.cancel_button')
|
||||
strictEqual(button.length, 0)
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when clicking the "Edit" button', hooks => {
|
||||
hooks.beforeEach(() => {
|
||||
props.onSetEditingStatus = sinon.spy()
|
||||
mountComponent()
|
||||
wrapper.find('.edit_grading_standard_button').simulate('click')
|
||||
})
|
||||
|
||||
test('calls the onSetEditingStatus prop', () => {
|
||||
strictEqual(props.onSetEditingStatus.callCount, 1)
|
||||
})
|
||||
|
||||
test('includes the unique id of the grading scheme when calling the onSetEditingStatus prop', () => {
|
||||
const [uniqueId] = props.onSetEditingStatus.lastCall.args
|
||||
strictEqual(uniqueId, '5001')
|
||||
})
|
||||
|
||||
test('sets the editing status to true when calling the onSetEditingStatus prop', () => {
|
||||
const [, status] = props.onSetEditingStatus.lastCall.args
|
||||
strictEqual(status, true)
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when clicking the "Delete" button', hooks => {
|
||||
hooks.beforeEach(() => {
|
||||
props.onDeleteGradingStandard = sinon.spy()
|
||||
mountComponent()
|
||||
wrapper.find('.delete_grading_standard_button').simulate('click')
|
||||
})
|
||||
|
||||
test('calls the onDeleteGradingStandard prop', () => {
|
||||
strictEqual(props.onDeleteGradingStandard.callCount, 1)
|
||||
})
|
||||
|
||||
test('includes the unique id of the grading scheme when calling the onDeleteGradingStandard prop', () => {
|
||||
const [, uniqueId] = props.onDeleteGradingStandard.lastCall.args
|
||||
strictEqual(uniqueId, '5001')
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when editing', contextHooks => {
|
||||
contextHooks.beforeEach(() => {
|
||||
props.editing = true
|
||||
mountComponent()
|
||||
})
|
||||
|
||||
test('does not display an edit button', () => {
|
||||
const button = wrapper.find('.edit_grading_standard_button')
|
||||
strictEqual(button.length, 0)
|
||||
})
|
||||
|
||||
test('does not display a delete button', () => {
|
||||
const button = wrapper.find('.delete_grading_standard_button')
|
||||
strictEqual(button.length, 0)
|
||||
})
|
||||
|
||||
test('displays a save button', () => {
|
||||
const button = wrapper.find('.save_button')
|
||||
strictEqual(button.length, 1)
|
||||
})
|
||||
|
||||
test('uses "Save" as the save button text', () => {
|
||||
const button = wrapper.find('.save_button')
|
||||
equal(button.text(), 'Save')
|
||||
})
|
||||
|
||||
test('displays a cancel button', () => {
|
||||
const button = wrapper.find('.cancel_button')
|
||||
strictEqual(button.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when editing and removing a row', contextHooks => {
|
||||
contextHooks.beforeEach(() => {
|
||||
props.editing = true
|
||||
})
|
||||
|
||||
test('removes the related row', () => {
|
||||
mountComponent()
|
||||
removeRow(1)
|
||||
const rows = wrapper.find('.grading_standard_row')
|
||||
const rowNames = rows.map(row => row.find('input.standard_name').prop('value'))
|
||||
deepEqual(rowNames, ['A', 'C', 'D', 'F'])
|
||||
})
|
||||
|
||||
test('does not include a delete button when only one row is present', () => {
|
||||
props.standard.data.splice(1)
|
||||
mountComponent()
|
||||
const buttons = wrapper.find('.delete_row_button')
|
||||
strictEqual(buttons.length, 0)
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when editing and adding a row', contextHooks => {
|
||||
contextHooks.beforeEach(() => {
|
||||
props.editing = true
|
||||
})
|
||||
|
||||
test('inserts an unnamed row after the related row', () => {
|
||||
mountComponent()
|
||||
addRowAfter(1)
|
||||
const rows = wrapper.find('.grading_standard_row')
|
||||
const rowNames = rows.map(row => row.find('input.standard_name').prop('value'))
|
||||
deepEqual(rowNames, ['A', 'B', '', 'C', 'D', 'F'])
|
||||
})
|
||||
|
||||
test('assigns no minimum score value to the inserted row', () => {
|
||||
mountComponent()
|
||||
addRowAfter(1)
|
||||
const rows = wrapper.find('.grading_standard_row')
|
||||
const rowScores = rows.map(row => row.find('input.standard_value').prop('value'))
|
||||
deepEqual(rowScores, ['0.9', '0.8', '0', '0.7', '0.6', '0'])
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when saving edits', contextHooks => {
|
||||
contextHooks.beforeEach(() => {
|
||||
props.editing = true
|
||||
mountComponent()
|
||||
})
|
||||
|
||||
function save() {
|
||||
wrapper.find('.save_button').simulate('click')
|
||||
}
|
||||
|
||||
function getSavedGradingScheme() {
|
||||
return props.onSaveGradingStandard.lastCall.args[0]
|
||||
}
|
||||
|
||||
QUnit.module('when nothing was changed', () => {
|
||||
test('calls onSaveGradingStandard', () => {
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 1)
|
||||
})
|
||||
|
||||
test('calls onSaveGradingStandard with the grading scheme', () => {
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 1)
|
||||
deepEqual(getSavedGradingScheme(), props.standard)
|
||||
})
|
||||
|
||||
test('disables the save button', () => {
|
||||
save()
|
||||
const button = wrapper.find('.save_button')
|
||||
equal(button.prop('disabled'), 'true')
|
||||
})
|
||||
|
||||
test('updates the save button to show "Saving..."', () => {
|
||||
save()
|
||||
const button = wrapper.find('.save_button')
|
||||
equal(button.text(), 'Saving...')
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when the title was changed', hooks => {
|
||||
hooks.beforeEach(() => {
|
||||
const input = wrapper.find('input.scheme_name')
|
||||
input.simulate('change', {target: {value: 'Emoji Grading Scheme'}})
|
||||
})
|
||||
|
||||
test('calls onSaveGradingStandard', () => {
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 1)
|
||||
})
|
||||
|
||||
test('saves with the updated title', () => {
|
||||
save()
|
||||
equal(getSavedGradingScheme().title, 'Emoji Grading Scheme')
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when a row name was changed', () => {
|
||||
test('calls onSaveGradingStandard when the new name is valid', () => {
|
||||
setRowName(0, 'A+')
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 1)
|
||||
})
|
||||
|
||||
test('saves with the updated row name', () => {
|
||||
setRowName(0, 'A+')
|
||||
save()
|
||||
const {data} = getSavedGradingScheme()
|
||||
equal(data[0][0], 'A+')
|
||||
})
|
||||
|
||||
test('does not call onSaveGradingStandard when the new name is a duplicate', () => {
|
||||
setRowName(0, 'B')
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 0)
|
||||
})
|
||||
|
||||
test('displays a validation message about duplicate row names', () => {
|
||||
setRowName(0, 'B')
|
||||
save()
|
||||
const message = wrapper.find('#invalid_standard_message_5001').text()
|
||||
ok(message.includes('Cannot have duplicate or empty row names.'))
|
||||
})
|
||||
|
||||
test('does not call onSaveGradingStandard when the new name is blank', () => {
|
||||
setRowName(0, '')
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 0)
|
||||
})
|
||||
|
||||
test('displays a validation message about empty row names', () => {
|
||||
setRowName(0, '')
|
||||
save()
|
||||
const message = wrapper.find('#invalid_standard_message_5001').text()
|
||||
ok(message.includes('Cannot have duplicate or empty row names.'))
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when a row minimum score was changed', () => {
|
||||
test('calls onSaveGradingStandard when the new score is valid', () => {
|
||||
setRowMinScore(0, 0.95)
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 1)
|
||||
})
|
||||
|
||||
test('saves with the updated row score', () => {
|
||||
setRowMinScore(0, 0.95)
|
||||
save()
|
||||
const {data} = getSavedGradingScheme()
|
||||
strictEqual(data[0][1], '0.95')
|
||||
})
|
||||
|
||||
test('does not call onSaveGradingStandard when the row score overlaps', () => {
|
||||
setRowMinScore(1, 0.91)
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 0)
|
||||
})
|
||||
|
||||
test('displays a validation message about overlapping ranges', () => {
|
||||
setRowMinScore(1, 0.91)
|
||||
save()
|
||||
const message = wrapper.find('#invalid_standard_message_5001').text()
|
||||
ok(message.includes('Cannot have overlapping or empty ranges.'))
|
||||
})
|
||||
|
||||
test('does not accept two scores which overlap after rounding to two decimal places', () => {
|
||||
setRowMinScore(0, 0.92)
|
||||
setRowMinScore(1, 0.91996)
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 0)
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when a row was added', hooks => {
|
||||
hooks.beforeEach(() => {
|
||||
addRowAfter(0)
|
||||
})
|
||||
|
||||
test('calls onSaveGradingStandard when the new row is valid', () => {
|
||||
setRowName(1, 'B+')
|
||||
setRowMinScore(1, 0.89)
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 1)
|
||||
})
|
||||
|
||||
test('saves with the added row having the given name', () => {
|
||||
setRowName(1, 'B+')
|
||||
setRowMinScore(1, 0.89)
|
||||
save()
|
||||
const {data} = getSavedGradingScheme()
|
||||
equal(data[1][0], 'B+')
|
||||
})
|
||||
|
||||
test('saves with the added row having the given value', () => {
|
||||
setRowName(1, 'B+')
|
||||
setRowMinScore(1, 0.89)
|
||||
save()
|
||||
const {data} = getSavedGradingScheme()
|
||||
equal(data[1][1], 0.89)
|
||||
})
|
||||
|
||||
test('does not call onSaveGradingStandard when the new row name is a duplicate', () => {
|
||||
setRowName(1, 'B')
|
||||
setRowMinScore(1, 0.89)
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 0)
|
||||
})
|
||||
|
||||
test('displays a validation message about duplicate row names', () => {
|
||||
setRowName(1, 'B')
|
||||
setRowMinScore(1, 0.89)
|
||||
save()
|
||||
const message = wrapper.find('#invalid_standard_message_5001').text()
|
||||
ok(message.includes('Cannot have duplicate or empty row names.'))
|
||||
})
|
||||
|
||||
test('does not call onSaveGradingStandard when the new row name is blank', () => {
|
||||
setRowName(1, '')
|
||||
setRowMinScore(1, 0.89)
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 0)
|
||||
})
|
||||
|
||||
test('displays a validation message about blank row names', () => {
|
||||
setRowName(1, '')
|
||||
setRowMinScore(1, 0.89)
|
||||
save()
|
||||
const message = wrapper.find('#invalid_standard_message_5001').text()
|
||||
ok(message.includes('Cannot have duplicate or empty row names.'))
|
||||
})
|
||||
|
||||
test('does not call onSaveGradingStandard when the row score overlaps', () => {
|
||||
setRowName(1, 'B+')
|
||||
setRowMinScore(1, 0.91)
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 0)
|
||||
})
|
||||
|
||||
test('displays a validation message about overlapping ranges', () => {
|
||||
setRowName(1, 'B+')
|
||||
setRowMinScore(1, 0.91)
|
||||
save()
|
||||
const message = wrapper.find('#invalid_standard_message_5001').text()
|
||||
ok(message.includes('Cannot have overlapping or empty ranges.'))
|
||||
})
|
||||
|
||||
test('does not call onSaveGradingStandard when the row score is blank', () => {
|
||||
setRowName(1, 'B+')
|
||||
setRowMinScore(1, '')
|
||||
save()
|
||||
strictEqual(props.onSaveGradingStandard.callCount, 0)
|
||||
})
|
||||
|
||||
test('displays a validation message about empty ranges', () => {
|
||||
setRowName(1, 'B+')
|
||||
setRowMinScore(1, '')
|
||||
save()
|
||||
const message = wrapper.find('#invalid_standard_message_5001').text()
|
||||
ok(message.includes('Cannot have overlapping or empty ranges.'))
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when a row was removed', () => {
|
||||
test('saves without the removed row', () => {
|
||||
removeRow(1)
|
||||
save()
|
||||
const {data} = getSavedGradingScheme()
|
||||
deepEqual(data.map(datum => datum[0]), ['A', 'C', 'D', 'F'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when canceling edits', contextHooks => {
|
||||
contextHooks.beforeEach(() => {
|
||||
props.editing = true
|
||||
mountComponent()
|
||||
wrapper.find('.cancel_button').simulate('click')
|
||||
})
|
||||
|
||||
test('calls onSetEditingStatus when the cancel button is clicked', () => {
|
||||
strictEqual(props.onSetEditingStatus.callCount, 1)
|
||||
})
|
||||
|
||||
test('includes the unique id of the grading scheme when calling the onSetEditingStatus prop', () => {
|
||||
const [uniqueId] = props.onSetEditingStatus.lastCall.args
|
||||
strictEqual(uniqueId, '5001')
|
||||
})
|
||||
|
||||
test('sets the editing status to false when calling the onSetEditingStatus prop', () => {
|
||||
const [, editing] = props.onSetEditingStatus.lastCall.args
|
||||
strictEqual(editing, false)
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when an assignment using the grading scheme has been assessed', hooks => {
|
||||
hooks.beforeEach(() => {
|
||||
props.standard['assessed_assignment?'] = true
|
||||
})
|
||||
|
||||
test('sets the id of the container to "grading_standard_blank"', () => {
|
||||
mountComponent()
|
||||
strictEqual(wrapper.find('#grading_standard_blank').length, 1)
|
||||
})
|
||||
|
||||
test('displays the edit button as read-only', () => {
|
||||
mountComponent()
|
||||
const button = wrapper.find('.edit_grading_standard_button')
|
||||
strictEqual(button.hasClass('read_only'), true)
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when another grading scheme is being edited', contextHooks => {
|
||||
contextHooks.beforeEach(() => {
|
||||
props.othersEditing = true
|
||||
mountComponent()
|
||||
})
|
||||
|
||||
test('disables the edit button', () => {
|
||||
const button = wrapper.find('.disabled-buttons .icon-edit')
|
||||
strictEqual(button.length, 1)
|
||||
})
|
||||
|
||||
test('disables the delete button', () => {
|
||||
const button = wrapper.find('.disabled-buttons .icon-trash')
|
||||
strictEqual(button.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when the grading scheme belongs to a different context', contextHooks => {
|
||||
contextHooks.beforeEach(() => {
|
||||
ENV.context_asset_string = 'course_1202'
|
||||
})
|
||||
|
||||
test('displays the context type and name', () => {
|
||||
mountComponent()
|
||||
equal(wrapper.find('div.cannot-manage-notification').text(), '(course: Calculus 101)')
|
||||
})
|
||||
|
||||
test('displays only the context type when the grading standard has no context name', () => {
|
||||
delete props.standard.context_name
|
||||
mountComponent()
|
||||
equal(wrapper.find('div.cannot-manage-notification').text(), '(course level)')
|
||||
})
|
||||
|
||||
test('disables the options menu when another grading scheme is being edited', () => {
|
||||
props.othersEditing = true
|
||||
mountComponent()
|
||||
strictEqual(wrapper.find('div.cannot-manage-notification').length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when the user cannot manage the grading scheme', contextHooks => {
|
||||
contextHooks.beforeEach(() => {
|
||||
props.permissions.manage = false
|
||||
})
|
||||
|
||||
test('displays a "cannot manage" message', () => {
|
||||
mountComponent()
|
||||
strictEqual(wrapper.find('div.cannot-manage-notification').length, 1)
|
||||
})
|
||||
|
||||
test('displays the grading scheme title', () => {
|
||||
mountComponent()
|
||||
const title = wrapper.find('.title').text()
|
||||
ok(title.includes('Example Grading Scheme'))
|
||||
})
|
||||
|
||||
test('displays the context type and name', () => {
|
||||
mountComponent()
|
||||
equal(wrapper.find('div.cannot-manage-notification').text(), '(course: Calculus 101)')
|
||||
})
|
||||
|
||||
test('displays only the context type when the grading standard has no context name', () => {
|
||||
delete props.standard.context_name
|
||||
mountComponent()
|
||||
equal(wrapper.find('div.cannot-manage-notification').text(), '(course level)')
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue