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:
Jeremy Neander 2018-06-01 10:08:45 -05:00
parent 9e30db867c
commit 1fc92e13f9
2 changed files with 582 additions and 555 deletions

View File

@ -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."
)
})

View File

@ -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)')
})
})
})