move final grades override setting to modal
closes: GRADE-1867 test plan: - checking and unchecking the final grade override checkbox still works - interacting with both the late policies and the advanced tab works as expected (two separate requests that both fire on save) - interacting with just the late policies tab does not trigger a request to save the gradebook settings - interacting with just the advanced tab settings does not trigger a request to save late policy settings Change-Id: I5c5bfdc7c62cf0adec64d06b8e2af88c4f39ff7a Reviewed-on: https://gerrit.instructure.com/175559 Reviewed-by: Gary Mei <gmei@instructure.com> Reviewed-by: Keith Garner <kgarner@instructure.com> Tested-by: Jenkins QA-Review: Adrian Packel <apackel@instructure.com> Product-Review: Jonathan Fenton <jfenton@instructure.com>
This commit is contained in:
parent
44e5d114dc
commit
21e45fa076
|
@ -2,6 +2,10 @@ function appAndSpecDirsFor(dir) {
|
|||
return `{app/jsx,app/coffeescripts,spec/javascripts/jsx,spec/coffeescripts}/${dir}/**/*.js`
|
||||
}
|
||||
|
||||
function appAndSpecFilesFor(path) {
|
||||
return `{app/jsx,app/coffeescripts,spec/javascripts/jsx,spec/coffeescripts}/${path}{,Spec}.js`
|
||||
}
|
||||
|
||||
// If you are starting a new project or section of greenfield code,
|
||||
// or if there is a folder of code that your team controls that you want
|
||||
// to start ensuring conforms to prettier, add it to this array to opt-in
|
||||
|
@ -12,6 +16,8 @@ const PRETTIER_WHITELIST = module.exports = [
|
|||
'frontend_build/**/*.js',
|
||||
'script/**/*.js',
|
||||
'app/jsx/account_settings/**/*.js',
|
||||
appAndSpecFilesFor('gradezilla/default_gradebook/components/AdvancedTabPanel'),
|
||||
appAndSpecFilesFor('gradezilla/default_gradebook/components/GradebookSettingsModal'),
|
||||
appAndSpecDirsFor('account_course_user_search'),
|
||||
appAndSpecDirsFor('announcements'),
|
||||
appAndSpecDirsFor('assignments_2'),
|
||||
|
|
|
@ -104,8 +104,8 @@ define [
|
|||
EnterGradesAsSetting, SetDefaultGradeDialogManager, CurveGradesDialogManager, GradebookApi, SubmissionCommentApi,
|
||||
FinalGradeOverrides, GradebookGrid, studentRowHeaderConstants, AssignmentRowCellPropFactory, GradebookMenu, ViewOptionsMenu, ActionMenu,
|
||||
AssignmentGroupFilter, GradingPeriodFilter, ModuleFilter, SectionFilter, GridColor, StatusesModal, SubmissionTray,
|
||||
GradebookSettingsModal, AnonymousSpeedGraderAlert, { statusColors }, StudentDatastore, PostGradesStore, PostGradesApp, SubmissionStateMap,
|
||||
DownloadSubmissionsDialogManager, ReuploadSubmissionsDialogManager, GradebookKeyboardNav,
|
||||
GradebookSettingsModal, AnonymousSpeedGraderAlert, { statusColors }, StudentDatastore, PostGradesStore, PostGradesApp,
|
||||
SubmissionStateMap, DownloadSubmissionsDialogManager, ReuploadSubmissionsDialogManager, GradebookKeyboardNav,
|
||||
AssignmentMuterDialogManager, assignmentHelper, TextMeasure, GradeInputHelper, { default: OutlierScoreHelper },
|
||||
LatePolicyApplicator, { default: Button }, { default: IconSettingsSolid }, FlashAlert) ->
|
||||
|
||||
|
@ -178,8 +178,6 @@ define [
|
|||
showEnrollments:
|
||||
concluded: false
|
||||
inactive: false
|
||||
showUnpublishedAssignments: true
|
||||
showFinalGradeOverrides: false
|
||||
sortRowsBy:
|
||||
columnId: sortRowsByColumnId # the column controlling the sort
|
||||
settingKey: sortRowsBySettingKey # the key describing the sort criteria
|
||||
|
@ -454,6 +452,7 @@ define [
|
|||
|
||||
@gridReady.then () =>
|
||||
@renderViewOptionsMenu()
|
||||
@renderGradebookSettingsModal()
|
||||
|
||||
# called from app/jsx/bundles/gradezilla.js
|
||||
onShow: ->
|
||||
|
@ -1297,11 +1296,12 @@ define [
|
|||
onSelect: onSelect
|
||||
selected: showingNotes
|
||||
|
||||
getOverridesViewOptionsMenuProps: ->
|
||||
disabled: @contentLoadStates.overridesColumnUpdating || @gridReady.state() != 'resolved'
|
||||
label: if @options.grading_period_set then I18n.t('Grading Period Overrides') else I18n.t('Overrides')
|
||||
onSelect: @toggleOverrides
|
||||
selected: @gridDisplaySettings.showFinalGradeOverrides
|
||||
getFinalGradeOverridesSettingsModalProps: ->
|
||||
disabled: !@options.final_grade_override_enabled ||
|
||||
@contentLoadStates.overridesColumnUpdating ||
|
||||
@gridReady.state() != 'resolved'
|
||||
onChange: @toggleOverrides
|
||||
defaultChecked: @gridDisplaySettings.showFinalGradeOverrides
|
||||
|
||||
getColumnSortSettingsViewOptionsMenuProps: ->
|
||||
storedSortOrder = @getColumnOrder()
|
||||
|
@ -1346,8 +1346,6 @@ define [
|
|||
|
||||
getViewOptionsMenuProps: ->
|
||||
teacherNotes: @getTeacherNotesViewOptionsMenuProps()
|
||||
overrides: @getOverridesViewOptionsMenuProps()
|
||||
finalGradeOverrideEnabled: @options.final_grade_override_enabled
|
||||
columnSortSettings: @getColumnSortSettingsViewOptionsMenuProps()
|
||||
filterSettings: @getFilterSettingsViewOptionsMenuProps()
|
||||
showUnpublishedAssignments: @gridDisplaySettings.showUnpublishedAssignments
|
||||
|
@ -1422,6 +1420,7 @@ define [
|
|||
onClose: => @gradebookSettingsModalButton.focus()
|
||||
onLatePolicyUpdate: @onLatePolicyUpdate
|
||||
gradedLateSubmissionsExist: @options.graded_late_submissions_exist
|
||||
overrides: @getFinalGradeOverridesSettingsModalProps()
|
||||
@gradebookSettingsModal = renderComponent(
|
||||
GradebookSettingsModal,
|
||||
gradebookSettingsModalMountPoint,
|
||||
|
@ -1652,6 +1651,7 @@ define [
|
|||
id: 'total_grade_override'
|
||||
maxWidth: columnWidths.total_grade_override.max
|
||||
minWidth: columnWidths.total_grade_override.min
|
||||
toolTip: label
|
||||
type: 'total_grade_override'
|
||||
width: totalWidth
|
||||
}
|
||||
|
@ -2103,6 +2103,10 @@ define [
|
|||
@updateColumns()
|
||||
@renderViewOptionsMenu()
|
||||
|
||||
updateColumnsAndRenderGradebookSettingsModal: =>
|
||||
@updateColumns()
|
||||
@renderGradebookSettingsModal()
|
||||
|
||||
## React Header Component Ref Methods
|
||||
|
||||
setHeaderComponentRef: (columnId, ref) =>
|
||||
|
@ -2408,15 +2412,15 @@ define [
|
|||
@gridDisplaySettings.showUnpublishedAssignments = showUnpublishedAssignments == 'true'
|
||||
|
||||
toggleUnpublishedAssignments: =>
|
||||
@gridDisplaySettings.showUnpublishedAssignments = !@gridDisplaySettings.showUnpublishedAssignments
|
||||
@updateColumnsAndRenderViewOptionsMenu()
|
||||
toggleableAction = =>
|
||||
@gridDisplaySettings.showUnpublishedAssignments = !@gridDisplaySettings.showUnpublishedAssignments
|
||||
@updateColumnsAndRenderViewOptionsMenu()
|
||||
toggleableAction()
|
||||
|
||||
@saveSettings(
|
||||
{ showUnpublishedAssignments: @gridDisplaySettings.showUnpublishedAssignments },
|
||||
() =>, # on success, do nothing since the render happened earlier
|
||||
() => # on failure, undo
|
||||
@gridDisplaySettings.showUnpublishedAssignments = !@gridDisplaySettings.showUnpublishedAssignments
|
||||
@updateColumnsAndRenderViewOptionsMenu()
|
||||
toggleableAction
|
||||
)
|
||||
|
||||
initShowOverrides: (showFinalGradeOverrides = 'false') =>
|
||||
|
@ -2425,17 +2429,18 @@ define [
|
|||
setShowFinalGradeOverrides: (show) =>
|
||||
@gridDisplaySettings.showFinalGradeOverrides = show
|
||||
|
||||
toggleOverrides: =>
|
||||
@setShowFinalGradeOverrides(!@gridDisplaySettings.showFinalGradeOverrides)
|
||||
@updateColumnsAndRenderViewOptionsMenu()
|
||||
toggleOverrides: () =>
|
||||
toggleableAction = =>
|
||||
@setShowFinalGradeOverrides(!@gridDisplaySettings.showFinalGradeOverrides)
|
||||
@updateColumnsAndRenderGradebookSettingsModal()
|
||||
|
||||
@saveSettings(
|
||||
{ showFinalGradeOverrides: @gridDisplaySettings.showFinalGradeOverrides },
|
||||
() =>, # on success, do nothing since the render happened earlier
|
||||
() => # on failure, undo
|
||||
@gridDisplaySettings.showFinalGradeOverrides = !@gridDisplaySettings.showFinalGradeOverrides
|
||||
@updateColumnsAndRenderViewOptionsMenu()
|
||||
)
|
||||
toggleableAction()
|
||||
new Promise (resolve, reject) =>
|
||||
@saveSettings(
|
||||
{ showFinalGradeOverrides: @gridDisplaySettings.showFinalGradeOverrides },
|
||||
() =>, # on success, do nothing since the render happened earlier
|
||||
toggleableAction
|
||||
).done(resolve).fail(reject)
|
||||
|
||||
setAssignmentsLoaded: (loaded) =>
|
||||
@contentLoadStates.assignmentsLoaded = loaded
|
||||
|
|
|
@ -77,7 +77,7 @@ export function createGradebook(options = {}) {
|
|||
}
|
||||
|
||||
export function setFixtureHtml($fixture) {
|
||||
$fixture.innerHTML = `
|
||||
return $fixture.innerHTML = `
|
||||
<div id="application">
|
||||
<div id="wrapper">
|
||||
<div data-component="GridColor"></div>
|
||||
|
@ -112,5 +112,5 @@ export function stubDataLoader() {
|
|||
gotSubmissions: $.Deferred()
|
||||
}
|
||||
|
||||
window.sandbox.stub(DataLoader, 'loadGradebookData').returns(dataLoaderPromises)
|
||||
return window.sandbox.stub(DataLoader, 'loadGradebookData').returns(dataLoaderPromises)
|
||||
}
|
||||
|
|
|
@ -65,10 +65,3 @@ export function updateLatePolicy (courseId, latePolicyData) {
|
|||
const data = { late_policy: underscore(latePolicyData) };
|
||||
return axios.patch(url, data);
|
||||
}
|
||||
|
||||
export default {
|
||||
DEFAULT_LATE_POLICY_DATA,
|
||||
fetchLatePolicy,
|
||||
createLatePolicy,
|
||||
updateLatePolicy
|
||||
};
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 {bool, func, shape} from 'prop-types'
|
||||
|
||||
import Checkbox from '@instructure/ui-forms/lib/components/Checkbox'
|
||||
import View from '@instructure/ui-layout/lib/components/View'
|
||||
|
||||
import I18n from 'i18n!gradebook'
|
||||
|
||||
export default function AdvancedTabPanel({overrides}) {
|
||||
return (
|
||||
<div id="AdvancedTabPanel__Container">
|
||||
<View as="div" margin="small">
|
||||
<Checkbox {...overrides} label={I18n.t('Allow final grade override')} />
|
||||
</View>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
AdvancedTabPanel.propTypes = {
|
||||
overrides: shape({
|
||||
defaultChecked: bool.isRequired,
|
||||
disabled: bool.isRequired,
|
||||
onChange: func.isRequired
|
||||
}).isRequired
|
||||
}
|
|
@ -16,17 +16,49 @@
|
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { bool, func, string } from 'prop-types';
|
||||
import _ from 'underscore';
|
||||
import Button from '@instructure/ui-buttons/lib/components/Button';
|
||||
import LatePoliciesTabPanel from '../../../gradezilla/default_gradebook/components/LatePoliciesTabPanel';
|
||||
import GradebookSettingsModalApi from '../../../gradezilla/default_gradebook/apis/GradebookSettingsModalApi';
|
||||
import Modal, { ModalBody, ModalFooter, ModalHeader } from '@instructure/ui-overlays/lib/components/Modal';
|
||||
import Heading from '@instructure/ui-elements/lib/components/Heading';
|
||||
import TabList, { TabPanel } from '@instructure/ui-tabs/lib/components/TabList';
|
||||
import I18n from 'i18n!gradebook';
|
||||
import { showFlashAlert } from '../../../shared/FlashAlert';
|
||||
import React from 'react'
|
||||
import {bool, func, shape, string} from 'prop-types'
|
||||
import _ from 'underscore'
|
||||
import I18n from 'i18n!gradebook'
|
||||
|
||||
import Button from '@instructure/ui-buttons/lib/components/Button'
|
||||
import Modal, {ModalBody, ModalFooter} from '@instructure/ui-overlays/lib/components/Modal'
|
||||
import TabList, {TabPanel} from '@instructure/ui-tabs/lib/components/TabList'
|
||||
|
||||
import AdvancedTabPanel from './AdvancedTabPanel'
|
||||
import {
|
||||
fetchLatePolicy,
|
||||
createLatePolicy,
|
||||
updateLatePolicy
|
||||
} from '../apis/GradebookSettingsModalApi'
|
||||
import LatePoliciesTabPanel from './LatePoliciesTabPanel'
|
||||
import {showFlashAlert} from '../../../shared/FlashAlert'
|
||||
|
||||
function isLatePolicySaveable({latePolicy: {changes, validationErrors}}) {
|
||||
return !_.isEmpty(changes) && _.isEmpty(validationErrors)
|
||||
}
|
||||
|
||||
function isOverridesChanged({
|
||||
props: {
|
||||
overrides: {defaultChecked}
|
||||
},
|
||||
state: {overrides}
|
||||
}) {
|
||||
return defaultChecked !== overrides
|
||||
}
|
||||
|
||||
function onSaveSettingsFailure() {
|
||||
const message = I18n.t('An error occurred while saving your settings')
|
||||
showFlashAlert({message, type: 'error'})
|
||||
return Promise.reject(new Error(message))
|
||||
}
|
||||
|
||||
function onUpdateSuccess({close}) {
|
||||
const message = I18n.t('Gradebook Settings updated')
|
||||
showFlashAlert({message, type: 'success'})
|
||||
close()
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
class GradebookSettingsModal extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -34,96 +66,117 @@ class GradebookSettingsModal extends React.Component {
|
|||
locale: string.isRequired,
|
||||
onClose: func.isRequired,
|
||||
gradedLateSubmissionsExist: bool.isRequired,
|
||||
onLatePolicyUpdate: func.isRequired
|
||||
onLatePolicyUpdate: func.isRequired,
|
||||
overrides: shape({
|
||||
disabled: bool.isRequired,
|
||||
onChange: func.isRequired,
|
||||
defaultChecked: bool.isRequired
|
||||
})
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpen: false,
|
||||
latePolicy: { changes: {}, validationErrors: {} }
|
||||
};
|
||||
state = {
|
||||
isOpen: false,
|
||||
latePolicy: {changes: {}, validationErrors: {}},
|
||||
overrides: this.props.overrides.defaultChecked,
|
||||
processingRequests: false
|
||||
}
|
||||
|
||||
onFetchLatePolicySuccess = ({ data }) => {
|
||||
this.changeLatePolicy({ ...this.state.latePolicy, data: data.latePolicy });
|
||||
onFetchLatePolicySuccess = ({data}) => {
|
||||
this.changeLatePolicy({...this.state.latePolicy, data: data.latePolicy})
|
||||
}
|
||||
|
||||
onFetchLatePolicyFailure = () => {
|
||||
const message = I18n.t('An error occurred while loading late policies');
|
||||
showFlashAlert({ message, type: 'error' });
|
||||
const message = I18n.t('An error occurred while loading late policies')
|
||||
showFlashAlert({message, type: 'error'})
|
||||
}
|
||||
|
||||
onUpdateLatePolicySuccess = () => {
|
||||
const message = I18n.t('Late policies updated');
|
||||
showFlashAlert({ message, type: 'success' });
|
||||
this.props.onLatePolicyUpdate({...this.state.latePolicy.data, ...this.state.latePolicy.changes});
|
||||
this.close();
|
||||
}
|
||||
|
||||
onUpdateLatePolicyFailure = () => {
|
||||
const message = I18n.t('An error occurred while updating late policies');
|
||||
showFlashAlert({ message, type: 'error' });
|
||||
}
|
||||
|
||||
handleUpdateButtonClicked = () => {
|
||||
if (this.state.latePolicy.data.newRecord) {
|
||||
this.createLatePolicy();
|
||||
} else {
|
||||
this.updateLatePolicy();
|
||||
}
|
||||
onSaveLatePolicyFailure = () => {
|
||||
const message = I18n.t('An error occurred while updating late policies')
|
||||
showFlashAlert({message, type: 'error'})
|
||||
return Promise.reject(new Error(message))
|
||||
}
|
||||
|
||||
fetchLatePolicy = () => {
|
||||
GradebookSettingsModalApi
|
||||
.fetchLatePolicy(this.props.courseId)
|
||||
fetchLatePolicy(this.props.courseId)
|
||||
.then(this.onFetchLatePolicySuccess)
|
||||
.catch(this.onFetchLatePolicyFailure);
|
||||
.catch(this.onFetchLatePolicyFailure)
|
||||
}
|
||||
|
||||
createLatePolicy = () => {
|
||||
GradebookSettingsModalApi
|
||||
.createLatePolicy(this.props.courseId, this.state.latePolicy.changes)
|
||||
.then(this.onUpdateLatePolicySuccess)
|
||||
.catch(this.onUpdateLatePolicyFailure);
|
||||
saveLatePolicy = () => {
|
||||
const createOrUpdate = this.state.latePolicy.data.newRecord
|
||||
? createLatePolicy
|
||||
: updateLatePolicy
|
||||
return createOrUpdate(this.props.courseId, this.state.latePolicy.changes)
|
||||
.then(() =>
|
||||
this.props.onLatePolicyUpdate({
|
||||
...this.state.latePolicy.data,
|
||||
...this.state.latePolicy.changes
|
||||
})
|
||||
)
|
||||
.catch(this.onSaveLatePolicyFailure)
|
||||
}
|
||||
|
||||
updateLatePolicy = () => {
|
||||
GradebookSettingsModalApi
|
||||
.updateLatePolicy(this.props.courseId, this.state.latePolicy.changes)
|
||||
.then(this.onUpdateLatePolicySuccess)
|
||||
.catch(this.onUpdateLatePolicyFailure);
|
||||
saveSettings = () => this.props.overrides.onChange().catch(onSaveSettingsFailure)
|
||||
|
||||
handleUpdateButtonClicked = () => {
|
||||
const promises = []
|
||||
|
||||
this.setState({processingRequests: true}, () => {
|
||||
if (isLatePolicySaveable(this.state)) {
|
||||
promises.push(this.saveLatePolicy())
|
||||
}
|
||||
if (isOverridesChanged(this)) {
|
||||
promises.push(this.saveSettings())
|
||||
}
|
||||
|
||||
// can't use finally() to remove the duplication because we need to
|
||||
// skip onUpdateSuccess if an earlier promise rejected and removing the
|
||||
// last catch will mean these rejected promises are uncaught, which
|
||||
// causes `Uncaught (in promise) Error` to be logged in the console
|
||||
Promise.all(promises)
|
||||
.then(() => onUpdateSuccess(this))
|
||||
.then(() => this.setState({processingRequests: false}))
|
||||
.catch(() => this.setState({processingRequests: false}))
|
||||
})
|
||||
}
|
||||
|
||||
changeLatePolicy = (latePolicy) => {
|
||||
this.setState({ latePolicy });
|
||||
changeLatePolicy = latePolicy => {
|
||||
this.setState({latePolicy})
|
||||
}
|
||||
|
||||
isUpdateButtonDisabled = () => {
|
||||
const { latePolicy: { changes, validationErrors } } = this.state;
|
||||
return _.isEmpty(changes) || !_.isEmpty(validationErrors);
|
||||
changeOverrides = ({target: {checked}}) => {
|
||||
this.setState({overrides: checked})
|
||||
}
|
||||
|
||||
isUpdateButtonEnabled = () => {
|
||||
if (this.state.processingRequests) return false
|
||||
return isOverridesChanged(this) || isLatePolicySaveable(this.state)
|
||||
}
|
||||
|
||||
open = () => {
|
||||
this.setState({ isOpen: true });
|
||||
this.setState({isOpen: true})
|
||||
}
|
||||
|
||||
close = () => {
|
||||
this.setState({ isOpen: false }, () => {
|
||||
const latePolicy = { changes: {}, data: undefined, validationErrors: {} };
|
||||
this.setState({isOpen: false}, () => {
|
||||
const latePolicy = {changes: {}, data: undefined, validationErrors: {}}
|
||||
// need to reset the latePolicy state _after_ the modal is closed, otherwise
|
||||
// the spinner will be visible for a brief moment before the modal closes.
|
||||
this.setState({ latePolicy });
|
||||
});
|
||||
this.setState({latePolicy})
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
const { isOpen, latePolicy } = this.state;
|
||||
render() {
|
||||
const overrides = {
|
||||
disabled: this.props.overrides.disabled,
|
||||
onChange: this.changeOverrides,
|
||||
defaultChecked: this.state.overrides
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="large"
|
||||
open={isOpen}
|
||||
open={this.state.isOpen}
|
||||
label={I18n.t('Gradebook Settings')}
|
||||
onOpen={this.fetchLatePolicy}
|
||||
onDismiss={this.close}
|
||||
|
@ -131,38 +184,37 @@ class GradebookSettingsModal extends React.Component {
|
|||
>
|
||||
<ModalBody>
|
||||
<TabList defaultSelectedIndex={0}>
|
||||
<TabPanel id="late-policies-tab" title={I18n.t('Late Policies')}>
|
||||
<TabPanel title={I18n.t('Late Policies')}>
|
||||
<LatePoliciesTabPanel
|
||||
latePolicy={latePolicy}
|
||||
latePolicy={this.state.latePolicy}
|
||||
changeLatePolicy={this.changeLatePolicy}
|
||||
locale={this.props.locale}
|
||||
showAlert={this.props.gradedLateSubmissionsExist}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel title={I18n.t('Advanced')}>
|
||||
<AdvancedTabPanel overrides={overrides} />
|
||||
</TabPanel>
|
||||
</TabList>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
id="gradebook-settings-cancel-button"
|
||||
onClick={this.close}
|
||||
margin="0 small"
|
||||
>
|
||||
<Button id="gradebook-settings-cancel-button" onClick={this.close} margin="0 small">
|
||||
{I18n.t('Cancel')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
id="gradebook-settings-update-button"
|
||||
onClick={this.handleUpdateButtonClicked}
|
||||
disabled={this.isUpdateButtonDisabled()}
|
||||
disabled={!this.isUpdateButtonEnabled()}
|
||||
variant="primary"
|
||||
>
|
||||
{I18n.t('Update')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default GradebookSettingsModal;
|
||||
export default GradebookSettingsModal
|
||||
|
|
|
@ -67,13 +67,6 @@ class ViewOptionsMenu extends React.Component {
|
|||
onSelect: func.isRequired,
|
||||
selected: bool.isRequired
|
||||
}).isRequired,
|
||||
overrides: shape({
|
||||
disabled: bool.isRequired,
|
||||
label: string.isRequired,
|
||||
onSelect: func.isRequired,
|
||||
selected: bool.isRequired
|
||||
}),
|
||||
finalGradeOverrideEnabled: bool.isRequired,
|
||||
onSelectShowStatusesModal: func.isRequired,
|
||||
showUnpublishedAssignments: bool.isRequired,
|
||||
onSelectShowUnpublishedAssignments: func.isRequired
|
||||
|
@ -240,16 +233,6 @@ class ViewOptionsMenu extends React.Component {
|
|||
>
|
||||
{I18n.t('Unpublished Assignments')}
|
||||
</MenuItem>
|
||||
|
||||
{this.props.finalGradeOverrideEnabled &&
|
||||
<MenuItem
|
||||
disabled={this.props.overrides.disabled}
|
||||
onSelect={this.props.overrides.onSelect}
|
||||
selected={this.props.overrides.selected}
|
||||
>
|
||||
<span data-menu-item-id="show-overrides-column">{this.props.overrides.label}</span>
|
||||
</MenuItem>
|
||||
}
|
||||
</MenuItemGroup>
|
||||
</Menu>
|
||||
);
|
||||
|
|
|
@ -231,7 +231,9 @@ export function showFlashAlert ({ message, err, type = err ? 'error' : 'info', s
|
|||
|
||||
export function destroyContainer () {
|
||||
const container = document.getElementById(messageHolderId)
|
||||
const liveRegion = document.getElementById(screenreaderMessageHolderId)
|
||||
if (container) container.remove()
|
||||
if (liveRegion) liveRegion.remove()
|
||||
}
|
||||
|
||||
export function showFlashError (message = I18n.t('An error occurred making a network request')) {
|
||||
|
|
|
@ -19,10 +19,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe GradebookSettingsController, type: :controller do
|
||||
let!(:teacher) do
|
||||
course_with_teacher
|
||||
@teacher
|
||||
end
|
||||
let(:teacher) { course_with_teacher.user }
|
||||
|
||||
before do
|
||||
user_session(teacher)
|
||||
|
@ -30,10 +27,8 @@ RSpec.describe GradebookSettingsController, type: :controller do
|
|||
end
|
||||
|
||||
describe "PUT update" do
|
||||
let(:json_response) { JSON.parse(response.body) }
|
||||
|
||||
context "given valid params" do
|
||||
let(:show_settings) do
|
||||
let(:gradebook_settings) do
|
||||
{
|
||||
"enter_grades_as" => {
|
||||
"2301" => "points"
|
||||
|
@ -65,122 +60,143 @@ RSpec.describe GradebookSettingsController, type: :controller do
|
|||
}
|
||||
end
|
||||
|
||||
let(:show_settings_massaged) do
|
||||
show_settings.merge('filter_rows_by' => { 'section_id' => nil })
|
||||
let(:gradebook_settings_massaged) do
|
||||
gradebook_settings.merge('filter_rows_by' => { 'section_id' => nil })
|
||||
end
|
||||
|
||||
let(:valid_params) do
|
||||
{
|
||||
"course_id" => @course.id,
|
||||
"gradebook_settings" => show_settings
|
||||
"gradebook_settings" => gradebook_settings
|
||||
}
|
||||
end
|
||||
|
||||
it "saves new gradebook_settings in preferences" do
|
||||
put :update, params: valid_params
|
||||
expect(response).to be_ok
|
||||
let(:expected_settings) do
|
||||
{
|
||||
@course.id => gradebook_settings_massaged.except("colors"),
|
||||
colors: gradebook_settings_massaged.fetch("colors")
|
||||
}.as_json
|
||||
end
|
||||
|
||||
expected_settings = {
|
||||
@course.id => show_settings_massaged.except("colors"),
|
||||
colors: show_settings_massaged.fetch("colors")
|
||||
}
|
||||
expect(teacher.preferences[:gradebook_settings]).to eq expected_settings
|
||||
expect(json_response["gradebook_settings"]).to eql expected_settings.as_json
|
||||
context 'given a valid PUT request' do
|
||||
subject { json_parse.fetch('gradebook_settings').fetch(@course.id.to_s) }
|
||||
|
||||
before { put :update, params: valid_params }
|
||||
|
||||
it { expect(response).to be_ok }
|
||||
it { is_expected.to include 'enter_grades_as' => {'2301' => 'points'} }
|
||||
it { is_expected.to include 'filter_columns_by' => {'grading_period_id' => '1401', 'assignment_group_id' => '888'} }
|
||||
it { is_expected.to include 'filter_rows_by' => {'section_id' => nil} }
|
||||
it { is_expected.to include 'selected_view_options_filters' => ['assignmentGroups'] }
|
||||
it { is_expected.to include 'show_inactive_enrollments' => 'true' }
|
||||
it { is_expected.to include 'show_concluded_enrollments' => 'false' }
|
||||
it { is_expected.to include 'show_unpublished_assignments' => 'true' }
|
||||
it { is_expected.to include 'show_final_grade_overrides' => 'false' }
|
||||
it { is_expected.to include 'student_column_display_as' => 'last_first' }
|
||||
it { is_expected.to include 'student_column_secondary_info' => 'login_id' }
|
||||
it { is_expected.to include 'sort_rows_by_column_id' => 'student' }
|
||||
it { is_expected.to include 'sort_rows_by_setting_key' => 'sortable_name' }
|
||||
it { is_expected.to include 'sort_rows_by_direction' => 'descending' }
|
||||
it { is_expected.not_to include 'colors' }
|
||||
it { is_expected.to have(13).items } # ensure we add specs for new additions
|
||||
|
||||
context 'colors' do
|
||||
subject { json_parse.fetch('gradebook_settings').fetch('colors') }
|
||||
|
||||
it { is_expected.to have(5).items } # ensure we add specs for new additions
|
||||
it do
|
||||
is_expected.to include({
|
||||
'late' => '#000000',
|
||||
'missing' => '#000001',
|
||||
'resubmitted' => '#000002',
|
||||
'dropped' => '#000003',
|
||||
'excused' => '#000004'
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "transforms 'null' string values to nil" do
|
||||
put :update, params: valid_params
|
||||
|
||||
expect(teacher.preferences[:gradebook_settings][@course.id]['filter_rows_by']['section_id']).to be_nil
|
||||
section_id = teacher.preferences.
|
||||
fetch(:gradebook_settings).
|
||||
fetch(@course.id).
|
||||
fetch('filter_rows_by').
|
||||
fetch('section_id')
|
||||
|
||||
expect(section_id).to be_nil
|
||||
end
|
||||
|
||||
it "allows saving gradebook settings for multiple courses" do
|
||||
previous_course = Course.create!(name: 'Previous Course')
|
||||
teacher.preferences[:gradebook_settings] = {
|
||||
previous_course.id => show_settings_massaged.except("colors"),
|
||||
colors: show_settings_massaged.fetch("colors")
|
||||
}
|
||||
teacher.save!
|
||||
|
||||
teacher.update!(preferences: {
|
||||
gradebook_settings: {
|
||||
previous_course.id => gradebook_settings_massaged.except("colors"),
|
||||
colors: gradebook_settings_massaged.fetch("colors")
|
||||
}
|
||||
})
|
||||
put :update, params: valid_params
|
||||
|
||||
expected_user_settings = {
|
||||
@course.id => show_settings_massaged.except("colors"),
|
||||
previous_course.id => show_settings_massaged.except("colors"),
|
||||
colors: show_settings_massaged.fetch("colors")
|
||||
}
|
||||
expected_response = {
|
||||
@course.id => show_settings_massaged.except("colors"),
|
||||
colors: show_settings_massaged.fetch("colors")
|
||||
}
|
||||
|
||||
expect(teacher.reload.preferences[:gradebook_settings]).to eq(expected_user_settings)
|
||||
expect(json_response["gradebook_settings"]).to eql(expected_response.as_json)
|
||||
expect(json_parse.fetch('gradebook_settings')).to eql expected_settings
|
||||
end
|
||||
|
||||
it "is allowed for courses in concluded enrollment terms" do
|
||||
term = teacher.account.enrollment_terms.create!(start_at: 2.months.ago, end_at: 1.month.ago)
|
||||
@course.enrollment_term = term # `update_attribute` with a term has unwanted side effects
|
||||
@course.save!
|
||||
|
||||
@course.update!(enrollment_term: teacher.account.enrollment_terms.create!(start_at: 2.months.ago, end_at: 1.month.ago))
|
||||
put :update, params: valid_params
|
||||
expect(response).to be_ok
|
||||
|
||||
expected_settings = {
|
||||
@course.id => show_settings_massaged.except("colors"),
|
||||
colors: show_settings_massaged.fetch("colors")
|
||||
}
|
||||
expect(teacher.preferences[:gradebook_settings]).to eq expected_settings
|
||||
expect(json_response["gradebook_settings"]).to eql expected_settings.as_json
|
||||
expect(json_parse.fetch('gradebook_settings')).to eql expected_settings
|
||||
end
|
||||
|
||||
it "is allowed for courses with concluded workflow state" do
|
||||
@course.workflow_state = "concluded"
|
||||
@course.save!
|
||||
|
||||
@course.update!(workflow_state: "concluded")
|
||||
put :update, params: valid_params
|
||||
expect(response).to be_ok
|
||||
|
||||
expected_settings = {
|
||||
@course.id => show_settings_massaged.except("colors"),
|
||||
colors: show_settings_massaged.fetch("colors")
|
||||
}
|
||||
expect(teacher.preferences[:gradebook_settings]).to eq expected_settings
|
||||
expect(json_response["gradebook_settings"]).to eql expected_settings.as_json
|
||||
expect(json_parse.fetch('gradebook_settings')).to eql expected_settings
|
||||
end
|
||||
|
||||
context "given invalid status colors (but otherwise valid params)" do
|
||||
subject { response }
|
||||
|
||||
let(:malevolent_color) { "; background: url(https://httpbin.org/basic-auth/user/passwd)" }
|
||||
let(:invalid_params) do
|
||||
{
|
||||
"course_id" => @course.id,
|
||||
"gradebook_settings" => {
|
||||
"colors" => {
|
||||
"dropped" => "#FEF0E5",
|
||||
"excused" => "#FEF7E5",
|
||||
"late" => "#cccccc",
|
||||
"missing" => malevolent_color,
|
||||
"resubmitted" => "#E5F7E5"
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
before { put :update, params: invalid_params }
|
||||
|
||||
it { is_expected.to be_ok }
|
||||
|
||||
it "does not store invalid status colors" do
|
||||
colors = json_parse.fetch("gradebook_settings").fetch("colors")
|
||||
expect(colors).not_to have_key "missing"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "given invalid params" do
|
||||
it "give an error response" do
|
||||
subject { response }
|
||||
|
||||
before do
|
||||
invalid_params = { "course_id" => @course.id }
|
||||
put :update, params: invalid_params
|
||||
|
||||
expect(response).not_to be_ok
|
||||
expect(json_response).to include(
|
||||
"errors" => [{
|
||||
"message" => "gradebook_settings is missing"
|
||||
}]
|
||||
)
|
||||
end
|
||||
|
||||
it "does not store invalid status colors" do
|
||||
malevolent_color = "; background: url(https://httpbin.org/basic-auth/user/passwd)"
|
||||
invalid_params = {
|
||||
"course_id" => @course.id,
|
||||
"gradebook_settings" => {
|
||||
"colors" => {
|
||||
"dropped" => "#FEF0E5",
|
||||
"excused" => "#FEF7E5",
|
||||
"late" => "#cccccc",
|
||||
"missing" => malevolent_color,
|
||||
"resubmitted" => "#E5F7E5"
|
||||
}
|
||||
}
|
||||
}
|
||||
put :update, params: invalid_params
|
||||
it { is_expected.to have_http_status :bad_request }
|
||||
|
||||
expect(response).to be_ok
|
||||
expect(json_response["gradebook_settings"]["colors"]).not_to have_key("missing")
|
||||
it "gives an error message" do
|
||||
expect(json_parse).to include "errors" => [{"message" => "gradebook_settings is missing"}]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,6 +22,8 @@ import {mount} from 'enzyme'
|
|||
import GradeSelect from 'jsx/assignments/GradeSummary/components/GradesGrid/GradeSelect'
|
||||
import {FAILURE, STARTED, SUCCESS} from 'jsx/assignments/GradeSummary/grades/GradeActions'
|
||||
|
||||
import {waitFor} from '../../../../support/Waiters'
|
||||
|
||||
function Container(props) {
|
||||
/*
|
||||
* This class exists because Enzyme does not update props of children, which
|
||||
|
@ -241,26 +243,6 @@ QUnit.module('GradeSummary GradeSelect', suiteHooks => {
|
|||
}
|
||||
}
|
||||
|
||||
async function waitFor(conditionFn, timeout = 200) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let timeoutId
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
const result = conditionFn()
|
||||
if (result) {
|
||||
clearInterval(intervalId)
|
||||
clearTimeout(timeoutId)
|
||||
resolve(result)
|
||||
}
|
||||
}, 10)
|
||||
|
||||
timeoutId = setTimeout(() => {
|
||||
clearInterval(intervalId)
|
||||
reject(new Error('Timeout waiting for condition'))
|
||||
}, timeout)
|
||||
})
|
||||
}
|
||||
|
||||
test('renders a text input', async () => {
|
||||
await mountComponent()
|
||||
const input = wrapper.find('input[type="text"]')
|
||||
|
|
|
@ -39,6 +39,7 @@ import SubmissionStateMap from 'jsx/gradezilla/SubmissionStateMap';
|
|||
import studentRowHeaderConstants from 'jsx/gradezilla/default_gradebook/constants/studentRowHeaderConstants';
|
||||
import { darken, statusColors, defaultColors } from 'jsx/gradezilla/default_gradebook/constants/colors';
|
||||
import ViewOptionsMenu from 'jsx/gradezilla/default_gradebook/components/ViewOptionsMenu';
|
||||
import GradebookSettingsModal from 'jsx/gradezilla/default_gradebook/components/GradebookSettingsModal';
|
||||
|
||||
import { createGradebook, stubDataLoader } from 'jsx/gradezilla/default_gradebook/__tests__/GradebookSpecHelper';
|
||||
import { createCourseGradesWithGradingPeriods as createGrades } from '../gradebook/GradeCalculatorSpecHelper';
|
||||
|
@ -254,40 +255,51 @@ test('updates partial .filterColumnsBy settings with the default values', functi
|
|||
strictEqual(gradebook.getFilterColumnsBySetting('gradingPeriodId'), null);
|
||||
});
|
||||
|
||||
QUnit.module('Gradebook#initialize', {
|
||||
setup () {
|
||||
stubDataLoader()
|
||||
$fixtures.innerHTML = `
|
||||
<div id="search-filter-container">
|
||||
<input type="text" />
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
QUnit.module('Gradebook#initialize', () => {
|
||||
QUnit.module('with dataloader stubs', (moduleHooks) => {
|
||||
moduleHooks.beforeEach(() => {
|
||||
stubDataLoader()
|
||||
$fixtures.innerHTML = `
|
||||
<div id="search-filter-container">
|
||||
<input type="text" />
|
||||
</div>
|
||||
`
|
||||
})
|
||||
|
||||
createInitializedGradebook (options) {
|
||||
const gradebook = createGradebook(options);
|
||||
gradebook.initialize();
|
||||
return gradebook;
|
||||
},
|
||||
moduleHooks.afterEach(() => {
|
||||
$fixtures.innerHTML = ''
|
||||
})
|
||||
|
||||
teardown () {
|
||||
$fixtures.innerHTML = '';
|
||||
}
|
||||
});
|
||||
function createInitializedGradebook (options) {
|
||||
const gradebook = createGradebook(options)
|
||||
gradebook.initialize()
|
||||
return gradebook
|
||||
}
|
||||
|
||||
test('stores the late policy with camelized keys, if one exists', function () {
|
||||
const gradebook = this.createInitializedGradebook({ late_policy: { late_submission_interval: 'hour' } });
|
||||
deepEqual(gradebook.courseContent.latePolicy, { lateSubmissionInterval: 'hour' });
|
||||
});
|
||||
test('stores the late policy with camelized keys, if one exists', () => {
|
||||
const gradebook = createInitializedGradebook({ late_policy: { late_submission_interval: 'hour' } })
|
||||
deepEqual(gradebook.courseContent.latePolicy, { lateSubmissionInterval: 'hour' })
|
||||
})
|
||||
|
||||
test('stores the late policy as undefined if the late_policy option is null', function () {
|
||||
const gradebook = this.createInitializedGradebook({ late_policy: null });
|
||||
strictEqual(gradebook.courseContent.latePolicy, undefined);
|
||||
});
|
||||
test('stores the late policy as undefined if the late_policy option is null', () => {
|
||||
const gradebook = createInitializedGradebook({ late_policy: null })
|
||||
strictEqual(gradebook.courseContent.latePolicy, undefined)
|
||||
})
|
||||
|
||||
test('sets assignmentGroupsLoaded to false', function () {
|
||||
const gradebook = this.createInitializedGradebook()
|
||||
strictEqual(gradebook.contentLoadStates.assignmentGroupsLoaded, false)
|
||||
test('sets assignmentGroupsLoaded to false', function () {
|
||||
const gradebook = createInitializedGradebook()
|
||||
strictEqual(gradebook.contentLoadStates.assignmentGroupsLoaded, false)
|
||||
})
|
||||
})
|
||||
|
||||
test('calls DataLoader.loadGradebookData with getFinalGradeOverrides', () => {
|
||||
const gradebook = createGradebook()
|
||||
const loadGradebookDataStub = stubDataLoader()
|
||||
gradebook.initialize()
|
||||
const {firstCall: {args: [{getFinalGradeOverrides}]}} = loadGradebookDataStub
|
||||
strictEqual(getFinalGradeOverrides, false)
|
||||
loadGradebookDataStub.restore()
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('Gradebook#gotChunkOfStudents', {
|
||||
|
@ -2363,16 +2375,6 @@ QUnit.module('Gradebook#getViewOptionsMenuProps', () => {
|
|||
consoleSpy.restore()
|
||||
})
|
||||
|
||||
test('finalGradeOverrideEnabled is false', () => {
|
||||
const {finalGradeOverrideEnabled} = createGradebook().getViewOptionsMenuProps()
|
||||
strictEqual(finalGradeOverrideEnabled, false)
|
||||
})
|
||||
|
||||
test('finalGradeOverrideEnabled is set via final_grade_override_enabled', () => {
|
||||
const {finalGradeOverrideEnabled} = createGradebook({final_grade_override_enabled: true}).getViewOptionsMenuProps()
|
||||
strictEqual(finalGradeOverrideEnabled, true)
|
||||
})
|
||||
|
||||
test('showUnpublishedAssignments is true', () => {
|
||||
const {showUnpublishedAssignments} = createGradebook().getViewOptionsMenuProps()
|
||||
strictEqual(showUnpublishedAssignments, true)
|
||||
|
@ -5242,6 +5244,30 @@ QUnit.module('Gradebook#updateColumnsAndRenderViewOptionsMenu', function (hooks)
|
|||
});
|
||||
});
|
||||
|
||||
QUnit.module('Gradebook#updateColumnsAndRenderGradebookSettingsModal', (moduleHooks) => {
|
||||
let gradebook
|
||||
|
||||
moduleHooks.beforeEach(() => {
|
||||
gradebook = createGradebook()
|
||||
sinon.stub(gradebook, 'updateColumns')
|
||||
sinon.stub(gradebook, 'renderGradebookSettingsModal')
|
||||
})
|
||||
|
||||
moduleHooks.afterEach(() => {
|
||||
gradebook.destroy()
|
||||
})
|
||||
|
||||
test('calls updateColumns', () => {
|
||||
gradebook.updateColumnsAndRenderGradebookSettingsModal()
|
||||
strictEqual(gradebook.updateColumns.callCount, 1)
|
||||
})
|
||||
|
||||
test('calls renderGradebookSettingsModal', () => {
|
||||
gradebook.updateColumnsAndRenderGradebookSettingsModal()
|
||||
strictEqual(gradebook.renderGradebookSettingsModal.callCount, 1)
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('Gradebook React Header Component References', {
|
||||
setup () {
|
||||
this.gradebook = createGradebook();
|
||||
|
@ -5468,8 +5494,8 @@ QUnit.module('Gradebook#toggleUnpublishedAssignments', () => {
|
|||
QUnit.module('Gradebook#toggleOverrides', () => {
|
||||
test('toggles showFinalGradeOverrides to true when currently false', function () {
|
||||
const gradebook = createGradebook();
|
||||
gradebook.gridDisplaySettings.showFinalGradeOverrides = false;
|
||||
sandbox.stub(gradebook, 'updateColumnsAndRenderViewOptionsMenu');
|
||||
gradebook.setShowFinalGradeOverrides(false);
|
||||
sandbox.stub(gradebook, 'updateColumnsAndRenderGradebookSettingsModal');
|
||||
sandbox.stub(gradebook, 'saveSettings');
|
||||
gradebook.toggleOverrides();
|
||||
|
||||
|
@ -5478,8 +5504,8 @@ QUnit.module('Gradebook#toggleOverrides', () => {
|
|||
|
||||
test('toggles showFinalGradeOverrides to false when currently true', function () {
|
||||
const gradebook = createGradebook();
|
||||
gradebook.gridDisplaySettings.showFinalGradeOverrides = true;
|
||||
sandbox.stub(gradebook, 'updateColumnsAndRenderViewOptionsMenu');
|
||||
gradebook.setShowFinalGradeOverrides(true);
|
||||
sandbox.stub(gradebook, 'updateColumnsAndRenderGradebookSettingsModal');
|
||||
sandbox.stub(gradebook, 'saveSettings');
|
||||
gradebook.toggleOverrides();
|
||||
|
||||
|
@ -5488,8 +5514,8 @@ QUnit.module('Gradebook#toggleOverrides', () => {
|
|||
|
||||
test('calls showFinalGradeOverrides after toggling', function () {
|
||||
const gradebook = createGradebook();
|
||||
gradebook.gridDisplaySettings.showFinalGradeOverrides = true;
|
||||
const stubFn = sandbox.stub(gradebook, 'updateColumnsAndRenderViewOptionsMenu').callsFake(function () {
|
||||
gradebook.setShowFinalGradeOverrides(true);
|
||||
const stubFn = sandbox.stub(gradebook, 'updateColumnsAndRenderGradebookSettingsModal').callsFake(function () {
|
||||
strictEqual(gradebook.gridDisplaySettings.showFinalGradeOverrides, false);
|
||||
});
|
||||
sandbox.stub(gradebook, 'saveSettings');
|
||||
|
@ -5501,7 +5527,7 @@ QUnit.module('Gradebook#toggleOverrides', () => {
|
|||
test('calls saveSettings with showFinalGradeOverrides', function () {
|
||||
const gradebookProps = {settings: {show_final_grade_overrides: 'true'}, final_grade_override_enabled: true}
|
||||
const gradebook = createGradebook(gradebookProps);
|
||||
sandbox.stub(gradebook, 'updateColumnsAndRenderViewOptionsMenu');
|
||||
sandbox.stub(gradebook, 'updateColumnsAndRenderGradebookSettingsModal');
|
||||
const saveSettingsStub = sandbox.stub(gradebook, 'saveSettings');
|
||||
gradebook.toggleOverrides();
|
||||
|
||||
|
@ -5517,8 +5543,8 @@ QUnit.module('Gradebook#toggleOverrides', () => {
|
|||
]);
|
||||
|
||||
const gradebook = createGradebook({ options });
|
||||
gradebook.gridDisplaySettings.showFinalGradeOverrides = true;
|
||||
sandbox.stub(gradebook, 'updateColumnsAndRenderViewOptionsMenu');
|
||||
gradebook.setShowFinalGradeOverrides(true);
|
||||
sandbox.stub(gradebook, 'updateColumnsAndRenderGradebookSettingsModal');
|
||||
const saveSettingsStub = sinon.spy(gradebook, 'saveSettings');
|
||||
gradebook.toggleOverrides();
|
||||
|
||||
|
@ -5534,8 +5560,9 @@ QUnit.module('Gradebook#toggleOverrides', () => {
|
|||
]);
|
||||
|
||||
const gradebook = createGradebook({ options });
|
||||
gradebook.gridDisplaySettings.showFinalGradeOverrides = true;
|
||||
const stubFn = sandbox.stub(gradebook, 'updateColumnsAndRenderViewOptionsMenu');
|
||||
gradebook.setShowFinalGradeOverrides(true);
|
||||
sandbox.stub(gradebook, 'renderGradebookSettingsModal')
|
||||
const stubFn = sandbox.stub(gradebook, 'updateColumns');
|
||||
stubFn.onFirstCall().callsFake(function () {
|
||||
strictEqual(gradebook.gridDisplaySettings.showFinalGradeOverrides, false);
|
||||
});
|
||||
|
@ -6122,59 +6149,110 @@ QUnit.module('Gradebook', () => {
|
|||
})
|
||||
})
|
||||
|
||||
QUnit.module('Gradebook#getOverridesViewOptionsMenuProps', () => {
|
||||
test('includes exactly what ViewOptionsMenu overrides props require', () => {
|
||||
const props = createGradebook().getOverridesViewOptionsMenuProps()
|
||||
const {propTypes: {overrides}} = ViewOptionsMenu
|
||||
QUnit.module('Gradebook#getFinalGradeOverridesSettingsModalProps', () => {
|
||||
test('includes the exact properties that GradebookSettingsModal overrides props require', () => {
|
||||
const props = createGradebook().getFinalGradeOverridesSettingsModalProps()
|
||||
const {propTypes: {overrides}} = GradebookSettingsModal
|
||||
const consoleSpy = sinon.spy(console, 'error')
|
||||
PropTypes.checkPropTypes({overrides}, props, 'prop', 'ViewOptionsMenu')
|
||||
PropTypes.checkPropTypes({overrides}, props, 'prop', 'GradebookSettingsModal')
|
||||
strictEqual(consoleSpy.called, false)
|
||||
consoleSpy.restore()
|
||||
})
|
||||
|
||||
test('disabled defaults to true', function () {
|
||||
const gradebook = createGradebook()
|
||||
const props = gradebook.getOverridesViewOptionsMenuProps()
|
||||
strictEqual(props.disabled, true)
|
||||
})
|
||||
|
||||
test('disabled is false when the grid is ready', function () {
|
||||
test('`disabled` defaults to true', () => {
|
||||
const gradebook = createGradebook()
|
||||
sinon.stub(gradebook.gridReady, 'state').returns('resolved')
|
||||
const props = gradebook.getOverridesViewOptionsMenuProps()
|
||||
strictEqual(props.disabled, false)
|
||||
const {disabled} = gradebook.getFinalGradeOverridesSettingsModalProps()
|
||||
strictEqual(disabled, true)
|
||||
})
|
||||
|
||||
test('disabled is true if the overrides column is updating', function () {
|
||||
const gradebook = createGradebook()
|
||||
test('`disabled` is false when final grades override is enabled and checked', () => {
|
||||
const final_grade_override_enabled = true
|
||||
const gradebook = createGradebook({final_grade_override_enabled})
|
||||
sinon.stub(gradebook.gridReady, 'state').returns('resolved')
|
||||
gradebook.setOverridesColumnUpdating(true)
|
||||
const props = gradebook.getOverridesViewOptionsMenuProps()
|
||||
strictEqual(props.disabled, true)
|
||||
const {disabled} = gradebook.getFinalGradeOverridesSettingsModalProps()
|
||||
strictEqual(disabled, !final_grade_override_enabled)
|
||||
})
|
||||
|
||||
test('disabled is false if the overrides column is not updating', function () {
|
||||
const gradebook = createGradebook()
|
||||
test('`disabled` is true when overrides column is updating', () => {
|
||||
const gradebook = createGradebook({final_grade_override_enabled: true})
|
||||
sinon.stub(gradebook.gridReady, 'state').returns('resolved')
|
||||
gradebook.setOverridesColumnUpdating(false)
|
||||
const props = gradebook.getOverridesViewOptionsMenuProps()
|
||||
strictEqual(props.disabled, false)
|
||||
const overridesColumnUpdating = true
|
||||
gradebook.setOverridesColumnUpdating(overridesColumnUpdating)
|
||||
const {disabled} = gradebook.getFinalGradeOverridesSettingsModalProps()
|
||||
strictEqual(disabled, overridesColumnUpdating)
|
||||
})
|
||||
|
||||
test('onSelect calls toggleOverrides', function () {
|
||||
const gradebook = createGradebook({ showFinalGradeOverrides: true })
|
||||
test('`onChange` calls `toggleOverrides`', () => {
|
||||
const gradebook = createGradebook({final_grade_override_enabled: true})
|
||||
sinon.stub(gradebook.gridReady, 'state').returns('resolved')
|
||||
sinon.stub(gradebook, 'toggleOverrides')
|
||||
const props = gradebook.getOverridesViewOptionsMenuProps()
|
||||
props.onSelect()
|
||||
const props = gradebook.getFinalGradeOverridesSettingsModalProps()
|
||||
props.onChange()
|
||||
strictEqual(gradebook.toggleOverrides.callCount, 1)
|
||||
gradebook.toggleOverrides.restore()
|
||||
})
|
||||
|
||||
test('selected reports showFinalGradeOverrides', function () {
|
||||
const show_final_grade_overrides = false
|
||||
test('`defaultChecked` defaults to false', () => {
|
||||
const gradebook = createGradebook()
|
||||
sinon.stub(gradebook.gridReady, 'state').returns('resolved')
|
||||
const {defaultChecked} = gradebook.getFinalGradeOverridesSettingsModalProps()
|
||||
strictEqual(defaultChecked, false)
|
||||
})
|
||||
|
||||
test('`defaultChecked` is false when final grades override is enabled', () => {
|
||||
const final_grade_override_enabled = true
|
||||
const gradebook = createGradebook({final_grade_override_enabled})
|
||||
sinon.stub(gradebook.gridReady, 'state').returns('resolved')
|
||||
const {defaultChecked} = gradebook.getFinalGradeOverridesSettingsModalProps()
|
||||
strictEqual(defaultChecked, !final_grade_override_enabled)
|
||||
})
|
||||
|
||||
test('`defaultChecked` is false when show final override setting is enabled', () => {
|
||||
const show_final_grade_overrides = 'true'
|
||||
const gradebook = createGradebook({settings: {show_final_grade_overrides}})
|
||||
const props = gradebook.getOverridesViewOptionsMenuProps()
|
||||
equal(props.selected, show_final_grade_overrides)
|
||||
sinon.stub(gradebook.gridReady, 'state').returns('resolved')
|
||||
const {defaultChecked} = gradebook.getFinalGradeOverridesSettingsModalProps()
|
||||
strictEqual(defaultChecked, !show_final_grade_overrides)
|
||||
})
|
||||
|
||||
test('`defaultChecked` is true when show final grades is enabled and the setting to show final grades is enabled', () => {
|
||||
const gradebook = createGradebook({
|
||||
final_grade_override_enabled: true,
|
||||
settings: {show_final_grade_overrides: 'true'}
|
||||
})
|
||||
sinon.stub(gradebook.gridReady, 'state').returns('resolved')
|
||||
const {defaultChecked} = gradebook.getFinalGradeOverridesSettingsModalProps()
|
||||
strictEqual(defaultChecked, true)
|
||||
})
|
||||
|
||||
test('`defaultChecked` is false when final grades is disabled and the setting to show final grades is enabled', () => {
|
||||
const gradebook = createGradebook({
|
||||
final_grade_override_enabled: false,
|
||||
settings: {show_final_grade_overrides: 'true'}
|
||||
})
|
||||
sinon.stub(gradebook.gridReady, 'state').returns('resolved')
|
||||
const {defaultChecked} = gradebook.getFinalGradeOverridesSettingsModalProps()
|
||||
strictEqual(defaultChecked, false)
|
||||
})
|
||||
|
||||
test('`defaultChecked` is false when final grades is enabled and the setting to show final grades is disabled', () => {
|
||||
const gradebook = createGradebook({
|
||||
final_grade_override_enabled: true,
|
||||
settings: {show_final_grade_overrides: 'false'}
|
||||
})
|
||||
sinon.stub(gradebook.gridReady, 'state').returns('resolved')
|
||||
const {defaultChecked} = gradebook.getFinalGradeOverridesSettingsModalProps()
|
||||
strictEqual(defaultChecked, false)
|
||||
})
|
||||
|
||||
test('`defaultChecked` is false when final grades is disabled and the setting to show final grades is disabled', () => {
|
||||
const gradebook = createGradebook({
|
||||
final_grade_override_enabled: false,
|
||||
settings: {show_final_grade_overrides: 'false'}
|
||||
})
|
||||
sinon.stub(gradebook.gridReady, 'state').returns('resolved')
|
||||
const {defaultChecked} = gradebook.getFinalGradeOverridesSettingsModalProps()
|
||||
strictEqual(defaultChecked, false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -8816,7 +8894,17 @@ QUnit.module('#renderGradebookSettingsModal', (hooks) => {
|
|||
gradebook.renderGradebookSettingsModal();
|
||||
strictEqual(gradebookSettingsModalProps().locale, 'de');
|
||||
});
|
||||
});
|
||||
|
||||
test('passes override props', () => {
|
||||
gradebook = createGradebook().renderGradebookSettingsModal()
|
||||
const expectedProps = {
|
||||
defaultChecked: false,
|
||||
disabled: true,
|
||||
onChange: {}
|
||||
}
|
||||
propEqual(gradebookSettingsModalProps().overrides, expectedProps)
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('Gradebook#renderAnonymousSpeedGraderAlert', (hooks) => {
|
||||
let gradebook;
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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 ReactDOM from 'react-dom'
|
||||
import AdvancedTabPanel from 'jsx/gradezilla/default_gradebook/components/AdvancedTabPanel'
|
||||
|
||||
const fixtures = document.getElementById('fixtures')
|
||||
const overridesOnChangeStub = sinon.stub()
|
||||
|
||||
function renderComponent({overrides, props} = {}) {
|
||||
const componentProps = {
|
||||
overrides: {
|
||||
defaultChecked: true,
|
||||
disabled: false,
|
||||
onChange: overridesOnChangeStub,
|
||||
...overrides
|
||||
},
|
||||
...props
|
||||
}
|
||||
ReactDOM.render(<AdvancedTabPanel {...componentProps} />, fixtures)
|
||||
return fixtures.children[0]
|
||||
}
|
||||
|
||||
function findCheckBox(label, scope = fixtures) {
|
||||
const labels = []
|
||||
scope.querySelectorAll('label').forEach(node => labels.push(node))
|
||||
const labelFor = labels.find(node => node.innerText.trim() === label).getAttribute('for')
|
||||
return scope.querySelector(`#${labelFor}`)
|
||||
}
|
||||
|
||||
function overridesCheckbox() {
|
||||
return findCheckBox('Allow final grade override')
|
||||
}
|
||||
|
||||
QUnit.module('AdvancedTabPanel', moduleHooks => {
|
||||
moduleHooks.beforeEach(() => {})
|
||||
|
||||
moduleHooks.afterEach(() => {
|
||||
ReactDOM.unmountComponentAtNode(fixtures)
|
||||
})
|
||||
|
||||
test('it renders', () => {
|
||||
renderComponent()
|
||||
const container = fixtures.querySelectorAll('#AdvancedTabPanel__Container')
|
||||
equal(container.length, 1)
|
||||
})
|
||||
|
||||
QUnit.module('Overrides', () => {
|
||||
test('checkbox is checked', () => {
|
||||
renderComponent()
|
||||
const {checked} = overridesCheckbox()
|
||||
strictEqual(checked, true)
|
||||
})
|
||||
|
||||
test('checkbox is not checked when `overrides.defaultChecked` is false', () => {
|
||||
renderComponent({overrides: {defaultChecked: false}})
|
||||
const {checked} = overridesCheckbox()
|
||||
strictEqual(checked, false)
|
||||
})
|
||||
|
||||
test('checkbox is not disabled', () => {
|
||||
renderComponent()
|
||||
const {disabled} = overridesCheckbox()
|
||||
strictEqual(disabled, false)
|
||||
})
|
||||
|
||||
test('checkbox is disabled when `overrides.disabled` is true', () => {
|
||||
renderComponent({overrides: {disabled: true}})
|
||||
const {disabled} = overridesCheckbox()
|
||||
strictEqual(disabled, true)
|
||||
})
|
||||
|
||||
test('onChange is called when checkbox is clicked', () => {
|
||||
renderComponent()
|
||||
overridesCheckbox().click()
|
||||
strictEqual(overridesOnChangeStub.callCount, 1)
|
||||
})
|
||||
})
|
||||
})
|
File diff suppressed because it is too large
Load Diff
|
@ -170,56 +170,6 @@ QUnit.module('ViewOptionsMenu - Overrides', (moduleHooks) => {
|
|||
const menuItem = getMenuItem(wrapper.instance().menuContent, 'Overrides')
|
||||
strictEqual(menuItem, undefined)
|
||||
})
|
||||
|
||||
QUnit.module('when "Final Grade Override" is enabled', hooks => {
|
||||
hooks.beforeEach(() => {
|
||||
props = {
|
||||
...defaultProps(),
|
||||
finalGradeOverrideEnabled: true
|
||||
}
|
||||
})
|
||||
|
||||
test('is not disabled', () => {
|
||||
wrapper = mountAndOpenOptions(props)
|
||||
const menuItem = getMenuItem(wrapper.instance().menuContent, 'Overrides')
|
||||
strictEqual(menuItem.getAttribute('aria-disabled'), null)
|
||||
})
|
||||
|
||||
test('can be optionally disabled', () => {
|
||||
props.overrides.disabled = true
|
||||
wrapper = mountAndOpenOptions(props)
|
||||
const menuItem = getMenuItem(wrapper.instance().menuContent, 'Overrides')
|
||||
strictEqual(menuItem.getAttribute('aria-disabled'), props.overrides.disabled.toString())
|
||||
})
|
||||
|
||||
test('triggers the onSelect when the "Overrides" option is clicked', () => {
|
||||
sandbox.stub(props.overrides, 'onSelect')
|
||||
wrapper = mountAndOpenOptions(props)
|
||||
getMenuItem(wrapper.instance().menuContent, 'Overrides').click()
|
||||
equal(props.overrides.onSelect.callCount, 1)
|
||||
})
|
||||
|
||||
test('is optionally not selected', () => {
|
||||
wrapper = mountAndOpenOptions(props)
|
||||
const menuItem = getMenuItem(wrapper.instance().menuContent, 'Overrides')
|
||||
strictEqual(menuItem.getAttribute('aria-checked'), props.overrides.selected.toString())
|
||||
})
|
||||
|
||||
test('is optionally selected', () => {
|
||||
props.overrides.selected = true
|
||||
wrapper = mountAndOpenOptions(props)
|
||||
const menuItem = getMenuItem(wrapper.instance().menuContent, 'Overrides')
|
||||
strictEqual(menuItem.getAttribute('aria-checked'), props.overrides.selected.toString())
|
||||
})
|
||||
|
||||
test('can be given a different label', () => {
|
||||
const someLabel = 'Grading Periods Label'
|
||||
props.overrides.label = someLabel
|
||||
wrapper = mountAndOpenOptions(props)
|
||||
const menuItem = getMenuItem(wrapper.instance().menuContent, someLabel)
|
||||
strictEqual(menuItem.textContent, someLabel)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('ViewOptionsMenu - Filters', {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (C) 2019 - 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/>.
|
||||
*/
|
||||
|
||||
export async function waitFor(conditionFn, timeout = 200) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let timeoutId
|
||||
|
||||
const interval = 10 // ms
|
||||
const intervalFn = () => {
|
||||
const result = conditionFn()
|
||||
if (result || result == null) {
|
||||
clearInterval(intervalId)
|
||||
clearTimeout(timeoutId)
|
||||
}
|
||||
if (result == null) {
|
||||
reject(
|
||||
new Error(
|
||||
"waitFor's criteria function returned null or undefined, did you mean for this function to return a boolean?"
|
||||
)
|
||||
)
|
||||
}
|
||||
if (result) {
|
||||
resolve(result)
|
||||
}
|
||||
}
|
||||
const intervalId = setInterval(intervalFn, interval)
|
||||
|
||||
const timeoutFn = () => {
|
||||
clearInterval(intervalId)
|
||||
reject(new Error('Timeout waiting for condition'))
|
||||
}
|
||||
timeoutId = setTimeout(timeoutFn, timeout)
|
||||
})
|
||||
}
|
|
@ -17,8 +17,7 @@
|
|||
|
||||
require_relative '../../common'
|
||||
require_relative '../pages/gradezilla_page'
|
||||
require_relative '../pages/gradezilla_advanced_options_page'
|
||||
require_relative '../pages/gradezilla_main_settings'
|
||||
require_relative '../pages/gradezilla/settings/advanced'
|
||||
require_relative '../pages/gradezilla_cells_page'
|
||||
require_relative '../pages/srgb_page'
|
||||
require_relative '../pages/student_grades_page'
|
||||
|
@ -27,7 +26,6 @@ describe 'Final Grade Override' do
|
|||
include_context 'in-process server selenium tests'
|
||||
|
||||
before(:once) do
|
||||
skip('Unskip in GRADE-1867')
|
||||
course_with_teacher(course_name: "Grade Override", active_course: true,active_enrollment: true,name: "Teacher Boss1",active_user: true)
|
||||
@students = create_users_in_course(@course, 5, return_type: :record, name_prefix: "Purple")
|
||||
Account.default.enable_feature!(:final_grades_override)
|
||||
|
@ -50,14 +48,12 @@ describe 'Final Grade Override' do
|
|||
user_session(@teacher)
|
||||
Gradezilla.visit(@course)
|
||||
Gradezilla.settings_cog_select
|
||||
#select option for override
|
||||
MainSettings::Advanced.grade_override_checkbox.click
|
||||
MainSettings::Controls.click_update_button
|
||||
Gradezilla::Settings.click_advanced_tab
|
||||
Gradezilla::Settings::Advanced.select_grade_override_checkbox
|
||||
Gradezilla::Settings.click_update_button
|
||||
end
|
||||
|
||||
it 'display override column in new gradebook', priority: '1', test_id: 3682130 do
|
||||
skip('Unskip in GRADE-1867')
|
||||
# TODO: verify new column on NG
|
||||
expect(f(".slick-header-column[title='Override']")).to be_displayed
|
||||
end
|
||||
|
||||
|
|
|
@ -17,13 +17,11 @@
|
|||
|
||||
require_relative '../pages/gradezilla_page'
|
||||
require_relative '../pages/gradezilla_cells_page'
|
||||
require_relative '../pages/gradezilla_late_policies_page'
|
||||
require_relative '../pages/gradezilla_main_settings'
|
||||
require_relative '../pages/gradezilla/settings/late_policies'
|
||||
|
||||
describe 'Late Policies:' do
|
||||
include_context "in-process server selenium tests"
|
||||
|
||||
|
||||
context 'when applied' do
|
||||
before(:once) do
|
||||
now = Time.zone.now
|
||||
|
@ -171,8 +169,8 @@ describe 'Late Policies:' do
|
|||
it 'saves late policy', test_id: 3196970, priority: '1' do
|
||||
percentage = 10
|
||||
increment = 'Day'
|
||||
MainSettings::LatePolicies.create_late_policy(percentage, increment)
|
||||
MainSettings::Controls.click_update_button
|
||||
Gradezilla::Settings::LatePolicies.create_late_policy(percentage, increment)
|
||||
Gradezilla::Settings.click_update_button
|
||||
|
||||
expect(@course.late_policy.late_submission_deduction_enabled).to be true
|
||||
expect(@course.late_policy.late_submission_deduction.to_i).to be percentage
|
||||
|
@ -181,8 +179,8 @@ describe 'Late Policies:' do
|
|||
|
||||
it 'saves missing policy', test_id: 3196968, priority: '1' do
|
||||
percentage = 50
|
||||
MainSettings::LatePolicies.create_missing_policy(percentage)
|
||||
MainSettings::Controls.click_update_button
|
||||
Gradezilla::Settings::LatePolicies.create_missing_policy(percentage)
|
||||
Gradezilla::Settings.click_update_button
|
||||
|
||||
expect(@course.late_policy.missing_submission_deduction_enabled).to be true
|
||||
expect(@course.late_policy.missing_submission_deduction.to_i).to be percentage
|
||||
|
@ -193,8 +191,8 @@ describe 'Late Policies:' do
|
|||
percentage = 10
|
||||
increment = 'Day'
|
||||
lowest_percentage = 50
|
||||
MainSettings::LatePolicies.create_late_policy(percentage, increment, lowest_percentage)
|
||||
MainSettings::Controls.click_update_button
|
||||
Gradezilla::Settings::LatePolicies.create_late_policy(percentage, increment, lowest_percentage)
|
||||
Gradezilla::Settings.click_update_button
|
||||
|
||||
expect(@course.late_policy.late_submission_minimum_percent_enabled).to be true
|
||||
expect(@course.late_policy.late_submission_minimum_percent.to_i).to be lowest_percentage
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
#
|
||||
# Copyright (C) 2017 - 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/>.
|
||||
|
||||
require_relative '../../../common'
|
||||
|
||||
module Gradezilla
|
||||
module Settings
|
||||
extend SeleniumDependencies
|
||||
|
||||
def self.tab(label:)
|
||||
# only works if not currently active
|
||||
ff('[data-ui-testable="TabList"] > [role="presentation"]').find do |el|
|
||||
el.text == label
|
||||
end
|
||||
end
|
||||
|
||||
def self.click_advanced_tab
|
||||
tab(label: 'Advanced').click
|
||||
end
|
||||
|
||||
def self.click_late_policy_tab
|
||||
tab(label: 'Late Policies').click
|
||||
end
|
||||
|
||||
def self.cancel_button
|
||||
f('#gradebook-settings-cancel-button')
|
||||
end
|
||||
|
||||
def self.update_button
|
||||
f('#gradebook-settings-update-button')
|
||||
end
|
||||
|
||||
def self.click_cancel_button
|
||||
cancel_button.click
|
||||
end
|
||||
|
||||
def self.click_update_button
|
||||
update_button.click
|
||||
wait_for_animations
|
||||
end
|
||||
end
|
||||
end
|
|
@ -15,17 +15,17 @@
|
|||
# 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/>.
|
||||
|
||||
require_relative '../../common'
|
||||
require_relative '../../../../common'
|
||||
require_relative '../settings'
|
||||
|
||||
module MainSettings
|
||||
class Advanced
|
||||
class << self
|
||||
include SeleniumDependencies
|
||||
module Gradezilla
|
||||
module Settings
|
||||
module Advanced
|
||||
extend SeleniumDependencies
|
||||
|
||||
def grade_override_checkbox
|
||||
# TODO: add locator for checkbox
|
||||
def self.select_grade_override_checkbox
|
||||
fj('label:contains("Allow final grade override")').click
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -15,64 +15,62 @@
|
|||
# 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/>.
|
||||
|
||||
require_relative '../../common'
|
||||
module MainSettings
|
||||
class LatePolicies
|
||||
class << self
|
||||
include SeleniumDependencies
|
||||
require_relative '../../../../common'
|
||||
require_relative '../settings'
|
||||
|
||||
def late_policy_tab
|
||||
# f('#late_policy_tab')
|
||||
end
|
||||
module Gradezilla
|
||||
module Settings
|
||||
module LatePolicies
|
||||
extend SeleniumDependencies
|
||||
|
||||
def missing_policy_checkbox
|
||||
def self.missing_policy_checkbox
|
||||
fj('label:contains("Automatically apply grade for missing submissions")')
|
||||
end
|
||||
|
||||
def missing_policy_percent_input
|
||||
def self.missing_policy_percent_input
|
||||
f('#missing-submission-grade')
|
||||
end
|
||||
|
||||
def late_policy_checkbox
|
||||
def self.late_policy_checkbox
|
||||
fj('label:contains("Automatically apply deduction to late submissions")')
|
||||
end
|
||||
|
||||
def late_policy_deduction_input
|
||||
def self.late_policy_deduction_input
|
||||
f('#late-submission-deduction')
|
||||
end
|
||||
|
||||
def late_policy_increment_combobox(increment)
|
||||
def self.late_policy_increment_combobox(increment)
|
||||
click_option(f('#late-submission-interval'), increment)
|
||||
end
|
||||
|
||||
def lowest_grade_percent_input
|
||||
def self.lowest_grade_percent_input
|
||||
f('#late-submission-minimum-percent')
|
||||
end
|
||||
|
||||
def select_late_policy_tab
|
||||
def self.select_late_policy_tab
|
||||
late_policy_tab.click
|
||||
end
|
||||
|
||||
def create_missing_policy(percent_per_assignment)
|
||||
def self.create_missing_policy(percent_per_assignment)
|
||||
unless missing_policy_checkbox.attribute('checked')
|
||||
missing_policy_checkbox.click
|
||||
end
|
||||
set_value(missing_policy_percent_input, percent_per_assignment)
|
||||
end
|
||||
|
||||
def disable_missing_policy
|
||||
def self.disable_missing_policy
|
||||
if missing_policy_checkbox.attribute('checked')
|
||||
missing_policy_checkbox.click
|
||||
end
|
||||
end
|
||||
|
||||
def disable_late_policy
|
||||
def self.disable_late_policy
|
||||
if late_policy_checkbox.attribute('checked')
|
||||
late_policy_checkbox.click
|
||||
end
|
||||
end
|
||||
|
||||
def create_late_policy(percentage, time_increment, lowest_percentage = nil)
|
||||
def self.create_late_policy(percentage, time_increment, lowest_percentage = nil)
|
||||
late_policy_checkbox.click
|
||||
set_value(late_policy_deduction_input, percentage)
|
||||
late_policy_increment_combobox(time_increment)
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
require_relative '../../common'
|
||||
|
||||
class Gradezilla
|
||||
module Gradezilla
|
||||
class Cells
|
||||
class << self
|
||||
include SeleniumDependencies
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
require_relative '../../common'
|
||||
class Gradezilla
|
||||
|
||||
module Gradezilla
|
||||
class GradeDetailTray
|
||||
class << self
|
||||
include SeleniumDependencies
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2017 - 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/>.
|
||||
|
||||
require_relative '../../common'
|
||||
module MainSettings
|
||||
class Controls
|
||||
class << self
|
||||
include SeleniumDependencies
|
||||
|
||||
def cancel_button
|
||||
f('#gradebook-settings-cancel-button')
|
||||
end
|
||||
|
||||
def update_button
|
||||
f('#gradebook-settings-update-button')
|
||||
end
|
||||
|
||||
def click_cancel_button
|
||||
cancel_button.click
|
||||
end
|
||||
|
||||
def click_update_button
|
||||
update_button.click
|
||||
wait_for_animations
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue