add save button, confirmation modal for mastery calculation tab

closes OUT-4074

flag=account_level_mastery_scales

test-plan:
- Enable the FF, if not already
- Visit the Outcome > Calculation tab
- Verify that the save button is dimmed and is
not clickable until changes are made to the
calculation method
- After changes are made, verify that updating
works correctly and a confirmation modal appears
to confirm any changes
- Verify clicking 'Save' in the confirmation modal
dims the save button
- Verify canceling out of the confirmation modal
does not save the calculation method when reloading
- Verify that the wording in the modal within an
account is different than a course
- Verify that users who do not have permissions
to change the mastery calculation (can be changed
via permissions) do not see the save button
- Verify invalid integers/ non numbers cannot be saved
and that the error message correctly appears

Change-Id: I6b77caa2c0ff477221c6b3fb3865003ee620e503
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/253025
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Michael Brewer-Davis <mbd@instructure.com>
Reviewed-by: Augusto Callejas <acallejas@instructure.com>
QA-Review: Augusto Callejas <acallejas@instructure.com>
Product-Review: Jody Sailor
This commit is contained in:
Pat Renner 2020-11-17 15:43:23 -06:00
parent cc45c56cb3
commit 3a785a5280
8 changed files with 229 additions and 122 deletions

View File

@ -16,19 +16,20 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import I18n from 'i18n!confirmMasteryScaleEditModal'
import I18n from 'i18n!confirmMasteryModal'
import React, {Component} from 'react'
import {func, string, bool} from 'prop-types'
import {Button} from '@instructure/ui-buttons'
import Modal from '../shared/components/InstuiModal'
export default class ConfirmMasteryScaleEdit extends Component {
export default class ConfirmMasteryModal extends Component {
static propTypes = {
onConfirm: func.isRequired,
contextType: string.isRequired,
modalText: string.isRequired,
isOpen: bool.isRequired,
onClose: func.isRequired
onClose: func.isRequired,
title: string.isRequired
}
onConfirm = () => {
@ -39,28 +40,16 @@ export default class ConfirmMasteryScaleEdit extends Component {
this.props.onClose()
}
getModalText = () => {
const {contextType} = this.props
if (contextType === 'Course') {
return I18n.t(
'This will update all rubrics aligned to outcomes within this course that have not yet been assessed.'
)
}
return I18n.t(
'This will update all account and course level rubrics that are tied to the account level mastery scale and have not yet been assessed.'
)
}
render() {
return (
<Modal
label={I18n.t('Confirm Mastery Scale')}
label={this.props.title}
open={this.props.isOpen}
onDismiss={this.onClose}
size="small"
>
<Modal.Body>
<div>{this.getModalText()}</div>
<div>{this.props.modalText}</div>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.onClose}>{I18n.t('Cancel')}</Button>

View File

@ -16,13 +16,13 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, {useState, useEffect, useRef, useCallback} from 'react'
import React, {useState, useEffect} from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'
import $ from 'jquery'
import 'compiled/jquery.rails_flash_notifications'
import I18n from 'i18n!MasteryScale'
import numberHelper from 'jsx/shared/helpers/numberHelper'
import {Button} from '@instructure/ui-buttons'
import {FormFieldGroup} from '@instructure/ui-form-field'
import {Flex} from '@instructure/ui-flex'
import {Text} from '@instructure/ui-text'
@ -32,6 +32,7 @@ import {View} from '@instructure/ui-view'
import {NumberInput} from '@instructure/ui-number-input'
import {SimpleSelect} from '@instructure/ui-simple-select'
import CalculationMethodContent from 'compiled/models/grade_summary/CalculationMethodContent'
import ConfirmMasteryModal from 'jsx/outcomes/ConfirmMasteryModal'
const validInt = (method, value) => {
if (method.validRange) {
@ -43,42 +44,29 @@ const validInt = (method, value) => {
}
const CalculationIntInput = ({updateCalculationInt, calculationMethod, calculationInt}) => {
const [currentValue, setCurrentValue] = useState(calculationInt)
useEffect(() => {
setCurrentValue(calculationInt)
}, [calculationInt])
const handleChange = (_event, data) => {
if (data === '') {
setCurrentValue(null)
updateCalculationInt('')
} else {
const parsed = numberHelper.parse(data)
if (!Number.isNaN(parsed)) {
setCurrentValue(parsed)
if (validInt(calculationMethod, parsed)) {
updateCalculationInt(parsed)
}
updateCalculationInt(parsed)
}
}
}
const handleIncrement = () => {
if (validInt(calculationMethod, calculationInt + 1)) {
updateCalculationInt(calculationInt + 1)
}
updateCalculationInt(calculationInt !== '' ? calculationInt + 1 : 1)
}
const handleDecrement = () => {
if (validInt(calculationMethod, calculationInt - 1)) {
updateCalculationInt(calculationInt - 1)
}
updateCalculationInt(calculationInt - 1)
}
const errorMessages = []
if (currentValue === null) {
if (calculationInt === '') {
errorMessages.push({text: I18n.t('Must be a number'), type: 'error'})
} else if (!validInt(calculationMethod, currentValue)) {
} else if (!validInt(calculationMethod, calculationInt)) {
errorMessages.push({
text: I18n.t('Must be between %{lower} and %{upper}', {
lower: calculationMethod.validRange[0],
@ -90,8 +78,8 @@ const CalculationIntInput = ({updateCalculationInt, calculationMethod, calculati
return (
<NumberInput
renderLabel={I18n.t('Parameter')}
value={currentValue || ''}
renderLabel={() => I18n.t('Parameter')}
value={typeof calculationInt === 'number' ? calculationInt : ''}
messages={errorMessages}
onIncrement={handleIncrement}
onDecrement={handleDecrement}
@ -103,7 +91,7 @@ const CalculationIntInput = ({updateCalculationInt, calculationMethod, calculati
const Display = ({calculationInt, currentMethod}) => {
return (
<>
<Heading level="h4">{I18n.t('Proficiency Calculation')}</Heading>
<Heading level="h4">{I18n.t('Mastery Calculation')}</Heading>
<Text color="primary" weight="normal">
{currentMethod.friendlyCalculationMethod}
</Text>
@ -132,11 +120,11 @@ const Form = ({
return (
<FormFieldGroup
description={
<ScreenReaderContent>{I18n.t('Proficiency calculation parameters')}</ScreenReaderContent>
<ScreenReaderContent>{I18n.t('Mastery calculation parameters')}</ScreenReaderContent>
}
>
<SimpleSelect
renderLabel={I18n.t('Proficiency Calculation')}
renderLabel={I18n.t('Mastery Calculation')}
value={calculationMethodKey}
onChange={updateCalculationMethod}
>
@ -178,23 +166,23 @@ const Example = ({currentMethod}) => {
)
}
const ProficiencyCalculation = ({method, update: rawUpdate, updateError, canManage}) => {
const getModalText = contextType => {
if (contextType === 'Course') {
return I18n.t('This will update all student mastery results within this course.')
}
return I18n.t(
'This will update all student mastery results tied to the account level mastery calculation.'
)
}
const ProficiencyCalculation = ({method, update, updateError, canManage, contextType}) => {
const {calculationMethod: initialMethodKey, calculationInt: initialInt} = method
const [calculationMethodKey, setCalculationMethodKey] = useState(initialMethodKey)
const [calculationInt, setCalculationInt] = useState(initialInt)
const firstRender = useRef(true)
const update = useCallback(_.debounce(rawUpdate, 500), [rawUpdate])
useEffect(() => () => update.cancel(), [update]) // cancel on unmount
useEffect(() => {
if (!firstRender.current) {
update(calculationMethodKey, calculationInt)
}
firstRender.current = false
}, [calculationMethodKey, calculationInt, update])
const [allowSave, setAllowSave] = useState(false)
const [showConfirmation, setShowConfirmationModal] = useState(false)
useEffect(() => {
if (updateError) {
@ -210,12 +198,33 @@ const ProficiencyCalculation = ({method, update: rawUpdate, updateError, canMana
const updateCalculationMethod = (_event, data) => {
const newMethod = data.id
const newCalculationInt = calculationMethods[newMethod].defaultInt || null
if (newMethod !== calculationMethodKey) {
setCalculationMethodKey(newMethod)
setCalculationInt(calculationMethods[newMethod].defaultInt || null)
setCalculationInt(newCalculationInt)
if (initialMethodKey === newMethod && initialInt === newCalculationInt) {
setAllowSave(false)
} else {
setAllowSave(true)
}
}
}
const updateCalculationInt = newCalculationInt => {
setCalculationInt(newCalculationInt)
if (initialMethodKey === calculationMethodKey && initialInt === newCalculationInt) {
setAllowSave(false)
} else {
setAllowSave(true)
}
}
const saveCalculationMethod = () => {
update(calculationMethodKey, calculationInt)
setShowConfirmationModal(false)
setAllowSave(false)
}
return (
<View as="div">
<Flex alignItems="start" wrap="wrap">
@ -227,7 +236,7 @@ const ProficiencyCalculation = ({method, update: rawUpdate, updateError, canMana
calculationMethods={calculationMethods}
currentMethod={currentMethod}
updateCalculationMethod={updateCalculationMethod}
setCalculationInt={setCalculationInt}
setCalculationInt={updateCalculationInt}
/>
) : (
<Display currentMethod={currentMethod} calculationInt={calculationInt} />
@ -237,6 +246,28 @@ const ProficiencyCalculation = ({method, update: rawUpdate, updateError, canMana
<Example currentMethod={currentMethod} />
</Flex.Item>
</Flex>
{canManage && (
<div className="save">
<Button
variant="primary"
interaction={allowSave ? 'enabled' : 'disabled'}
onClick={() => {
if (validInt(currentMethod, calculationInt)) {
setShowConfirmationModal(true)
}
}}
>
{I18n.t('Save Mastery Calculation')}
</Button>
<ConfirmMasteryModal
isOpen={showConfirmation}
onConfirm={saveCalculationMethod}
modalText={getModalText(contextType)}
title={I18n.t('Confirm Mastery Calculation')}
onClose={() => setShowConfirmationModal(false)}
/>
</div>
)}
</View>
)
}
@ -248,7 +279,8 @@ ProficiencyCalculation.propTypes = {
}),
canManage: PropTypes.bool,
update: PropTypes.func.isRequired,
updateError: PropTypes.string
updateError: PropTypes.string,
contextType: PropTypes.string.isRequired
}
ProficiencyCalculation.defaultProps = {

View File

@ -96,6 +96,11 @@ describe('ProficiencyCalculation', () => {
expect(getByText('Example')).not.toBeNull()
expect(getByText(/most recent graded/)).not.toBeNull()
})
it('does not render the save button', () => {
const {queryByText} = render(<ProficiencyCalculation {...makeProps({canManage: false})} />)
expect(queryByText(/Save Mastery Calculation/)).not.toBeInTheDocument()
})
})
describe('unlocked', () => {
@ -138,20 +143,38 @@ describe('ProficiencyCalculation', () => {
expect(getByDisplayValue('5')).not.toBeNull()
})
it('debounces save', async () => {
it('calls save when the button is clicked', () => {
const update = jest.fn()
const {getByLabelText} = render(<ProficiencyCalculation {...makeProps({update})} />)
const {getByText, getByLabelText} = render(
<ProficiencyCalculation {...makeProps({update})} />
)
const parameter = getByLabelText('Parameter')
fireEvent.input(parameter, {target: {value: '22'}})
fireEvent.input(parameter, {target: {value: '40'}})
fireEvent.input(parameter, {target: {value: '44'}})
fireEvent.input(parameter, {target: {value: '41'}})
await wait(() => expect(update).toHaveBeenCalledTimes(1))
fireEvent.click(getByText('Save Mastery Calculation'))
fireEvent.click(getByText('Save'))
expect(update).toHaveBeenCalledTimes(1)
expect(update).toHaveBeenCalledWith('decaying_average', 41)
})
it('save button is initially disabled', () => {
const {getByText} = render(<ProficiencyCalculation {...makeProps()} />)
expect(getByText('Save Mastery Calculation').closest('button').disabled).toEqual(true)
})
it('save button geos back to disabled if changes are reverted', () => {
const {getByText, getByLabelText} = render(<ProficiencyCalculation {...makeProps()} />)
const parameter = getByLabelText('Parameter')
fireEvent.input(parameter, {target: {value: '22'}})
expect(getByText('Save Mastery Calculation').closest('button').disabled).toEqual(false)
fireEvent.input(parameter, {target: {value: '75'}})
expect(getByText('Save Mastery Calculation').closest('button').disabled).toEqual(true)
})
describe('highest', () => {
it('saves when method changed', async () => {
it('calls update with the correct arguments', () => {
const update = jest.fn()
const {getByDisplayValue, getByText} = render(
<ProficiencyCalculation {...makeProps({update})} />
@ -160,12 +183,14 @@ describe('ProficiencyCalculation', () => {
fireEvent.click(method)
const newMethod = getByText('Highest Score')
fireEvent.click(newMethod)
await wait(() => expect(update).toHaveBeenCalledWith('highest', null))
fireEvent.click(getByText('Save Mastery Calculation'))
fireEvent.click(getByText('Save'))
expect(update).toHaveBeenCalledWith('highest', null)
})
})
describe('latest', () => {
it('saves when method changed', async () => {
it('calls update with the correct arguments', () => {
const update = jest.fn()
const {getByDisplayValue, getByText} = render(
<ProficiencyCalculation {...makeProps({update})} />
@ -174,7 +199,9 @@ describe('ProficiencyCalculation', () => {
fireEvent.click(method)
const newMethod = getByText('Most Recent Score')
fireEvent.click(newMethod)
await wait(() => expect(update).toHaveBeenCalledWith('latest', null))
fireEvent.click(getByText('Save Mastery Calculation'))
fireEvent.click(getByText('Save'))
expect(update).toHaveBeenCalledWith('latest', null)
})
})
@ -200,19 +227,22 @@ describe('ProficiencyCalculation', () => {
expect(getByText('Must be between 1 and 99')).not.toBeNull()
})
it('saves only when int is valid', async () => {
it('renders the confirmation modal only when int is valid', async () => {
const update = jest.fn()
const {getByLabelText} = render(<ProficiencyCalculation {...makeProps({update})} />)
const {getByText, queryByText, getByLabelText} = render(
<ProficiencyCalculation {...makeProps({update})} />
)
const parameter = getByLabelText('Parameter')
fireEvent.input(parameter, {target: {value: '0'}})
fireEvent.input(parameter, {target: {value: '22'}})
fireEvent.input(parameter, {target: {value: '0'}})
await wait(() => expect(update).toHaveBeenCalledTimes(1))
fireEvent.input(parameter, {target: {value: '199'}})
fireEvent.click(getByText('Save Mastery Calculation'))
expect(queryByText('Save')).not.toBeInTheDocument()
fireEvent.input(parameter, {target: {value: '40'}})
await wait(() => expect(update).toHaveBeenCalledTimes(2))
expect(update).toHaveBeenCalledWith('decaying_average', 22)
expect(update).toHaveBeenCalledWith('decaying_average', 40)
fireEvent.click(getByText('Save Mastery Calculation'))
fireEvent.click(getByText('Save'))
await wait(() => {
expect(update).toHaveBeenCalledTimes(1)
expect(update).toHaveBeenCalledWith('decaying_average', 40)
})
})
})
@ -250,25 +280,54 @@ describe('ProficiencyCalculation', () => {
expect(getByText('Must be between 1 and 5')).not.toBeNull()
})
it('saves only when int is valid', async () => {
it('renders the confirmation modal only when int is valid', async () => {
const update = jest.fn()
const {getByLabelText} = render(
const {getByText, queryByText, getByLabelText} = render(
<ProficiencyCalculation
{...makeProps({update, method: {calculationMethod: 'n_mastery', calculationInt: 5}})}
/>
)
const parameter = getByLabelText('Parameter')
fireEvent.input(parameter, {target: {value: '0'}})
fireEvent.input(parameter, {target: {value: '2'}})
fireEvent.input(parameter, {target: {value: '0'}})
await wait(() => expect(update).toHaveBeenCalledTimes(1))
fireEvent.input(parameter, {target: {value: '6'}})
fireEvent.input(parameter, {target: {value: '3'}})
fireEvent.input(parameter, {target: {value: '9'}})
await wait(() => expect(update).toHaveBeenCalledTimes(2))
expect(update).toHaveBeenCalledWith('n_mastery', 2)
expect(update).toHaveBeenCalledWith('n_mastery', 3)
fireEvent.click(getByText('Save Mastery Calculation'))
expect(queryByText('Save')).not.toBeInTheDocument()
fireEvent.input(parameter, {target: {value: '3'}})
fireEvent.click(getByText('Save Mastery Calculation'))
fireEvent.click(getByText('Save'))
await wait(() => {
expect(update).toHaveBeenCalledTimes(1)
expect(update).toHaveBeenCalledWith('n_mastery', 3)
})
})
})
})
describe('confirmation modal', () => {
it('renders correct text for the Account context', () => {
const {getByDisplayValue, getByText} = render(<ProficiencyCalculation {...makeProps()} />)
const method = getByDisplayValue('Decaying Average')
fireEvent.click(method)
const newMethod = getByText('Most Recent Score')
fireEvent.click(newMethod)
fireEvent.click(getByText('Save Mastery Calculation'))
expect(getByText(/Confirm Mastery Calculation/)).not.toBeNull()
expect(
getByText(/all student mastery results tied to the account level mastery calculation/)
).not.toBeNull()
})
it('renders correct text for the Course context', () => {
const {getByDisplayValue, getByText} = render(
<ProficiencyCalculation {...makeProps()} contextType="Course" />
)
const method = getByDisplayValue('Decaying Average')
fireEvent.click(method)
const newMethod = getByText('Most Recent Score')
fireEvent.click(newMethod)
fireEvent.click(getByText('Save Mastery Calculation'))
expect(getByText(/Confirm Mastery Calculation/)).not.toBeNull()
expect(getByText(/all student mastery results within this course/)).not.toBeNull()
})
})
})

View File

@ -155,7 +155,7 @@ describe('MasteryCalculation', () => {
</MockedProvider>
)
await waitForElementToBeRemoved(() => queryByText('Loading'))
expect(getByText('Proficiency Calculation')).not.toBeNull()
expect(getByText('Mastery Calculation')).not.toBeNull()
})
describe('update outcomeCalculationMethod', () => {
@ -187,16 +187,19 @@ describe('MasteryCalculation', () => {
result: updateCall
}
]
it('submits a request when calculation method is updated', async () => {
const {findByLabelText} = render(
it('submits a request when calculation method is saved', async () => {
const {getByText, findByLabelText} = render(
<MockedProvider mocks={updateMocks} addTypename={false}>
<MasteryCalculation contextType="Account" contextId="11" />
</MockedProvider>
)
const parameter = await findByLabelText(/Parameter/)
fireEvent.input(parameter, {target: {value: '88'}})
await wait(() => expect(updateCall).toHaveBeenCalled())
fireEvent.click(getByText('Save Mastery Calculation'))
fireEvent.click(getByText('Save'))
await wait(() => {
expect(updateCall).toHaveBeenCalled()
})
})
})

View File

@ -16,6 +16,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import $ from 'jquery'
import React, {useCallback} from 'react'
import I18n from 'i18n!MasteryScale'
import {Spinner} from '@instructure/ui-spinner'
@ -39,11 +40,12 @@ const MasteryCalculation = ({contextType, contextId}) => {
const [setCalculationMethodQuery, {error: setCalculationMethodError}] = useMutation(
SET_OUTCOME_CALCULATION_METHOD
)
const setCalculationMethod = useCallback(
(calculationMethod, calculationInt) => {
setCalculationMethodQuery({
variables: {contextType, contextId, calculationMethod, calculationInt}
})
}).then(() => $.flashMessage(I18n.t('Mastery calculation saved')))
},
[contextType, contextId, setCalculationMethodQuery]
)
@ -58,7 +60,7 @@ const MasteryCalculation = ({contextType, contextId}) => {
if (error) {
return (
<Text color="danger">
{I18n.t('An error occurred while loading the outcome calculation: %{error}', {error})}
{I18n.t('An error occurred while loading the mastery calculation: %{error}', {error})}
</Text>
)
}

View File

@ -21,7 +21,6 @@ import PropTypes from 'prop-types'
import {Button} from '@instructure/ui-buttons'
import {Flex} from '@instructure/ui-flex'
import {IconPlusLine} from '@instructure/ui-icons'
import {capitalizeFirstLetter} from '@instructure/ui-utils'
import I18n from 'i18n!ProficiencyTable'
import {View} from '@instructure/ui-view'
import ProficiencyRating from './ProficiencyRating'
@ -30,7 +29,7 @@ import _ from 'lodash'
import {fromJS, List} from 'immutable'
import NumberHelper from '../../shared/helpers/numberHelper'
import WithBreakpoints, {breakpointsShape} from '../../shared/WithBreakpoints'
import ConfirmMasteryScaleEdit from 'jsx/outcomes/ConfirmMasteryScaleEdit'
import ConfirmMasteryModal from 'jsx/outcomes/ConfirmMasteryModal'
const ADD_DEFAULT_COLOR = 'EF4437'
@ -152,17 +151,10 @@ class ProficiencyTable extends React.Component {
() => {
this.props
.update(this.stateToConfig())
.then(() => {
$.flashMessage(
I18n.t(`%{context} mastery scale saved`, {
context: capitalizeFirstLetter(this.props.contextType)
})
)
})
.then(() => $.flashMessage(I18n.t(`Mastery scale saved`)))
.catch(e => {
$.flashError(
I18n.t('An error occurred while saving %{context} mastery scale: %{message}', {
context: capitalizeFirstLetter(this.props.contextType),
I18n.t('An error occurred while saving the mastery scale: %{message}', {
message: e.message
})
)
@ -318,9 +310,21 @@ class ProficiencyTable extends React.Component {
invalidDescription = description => !description || description.trim().length === 0
getModalText = () => {
const {contextType} = this.props
if (contextType === 'Course') {
return I18n.t(
'This will update all rubrics aligned to outcomes within this course that have not yet been assessed.'
)
}
return I18n.t(
'This will update all account and course level rubrics that are tied to the account level mastery scale and have not yet been assessed.'
)
}
render() {
const {showConfirmation} = this.state
const {breakpoints, canManage, contextType} = this.props
const {breakpoints, canManage} = this.props
const isMobileView = breakpoints.mobileOnly
return (
<>
@ -397,10 +401,11 @@ class ProficiencyTable extends React.Component {
{I18n.t('Save Mastery Scale')}
</Button>
</div>
<ConfirmMasteryScaleEdit
<ConfirmMasteryModal
isOpen={showConfirmation}
contextType={contextType}
onConfirm={this.handleSubmit}
modalText={this.getModalText()}
title={I18n.t('Confirm Mastery Scale')}
onClose={this.hideConfirmationModal}
/>
</>

View File

@ -146,7 +146,7 @@ describe('default proficiency', () => {
fireEvent.click(getByText('Save'))
await wait(() => {
expect(updateSpy).toHaveBeenCalled()
expect(flashMock).toHaveBeenCalledWith('Course mastery scale saved')
expect(flashMock).toHaveBeenCalledWith('Mastery scale saved')
})
})
@ -469,3 +469,25 @@ describe('custom proficiency', () => {
})
})
})
describe('confirmation modal', () => {
it('renders correct text for the Account context', () => {
const {getByDisplayValue, getByText} = render(<ProficiencyTable {...defaultProps} />)
const pointsInput = getByDisplayValue('3')
fireEvent.change(pointsInput, {target: {value: '1000'}})
fireEvent.click(getByText('Save Mastery Scale'))
expect(getByText(/Confirm Mastery Scale/)).not.toBeNull()
expect(getByText(/all account and course level rubrics/)).not.toBeNull()
})
it('renders correct text for the Course context', () => {
const {getByDisplayValue, getByText} = render(
<ProficiencyTable {...defaultProps} contextType="Course" />
)
const pointsInput = getByDisplayValue('3')
fireEvent.change(pointsInput, {target: {value: '1000'}})
fireEvent.click(getByText('Save Mastery Scale'))
expect(getByText(/Confirm Mastery Scale/)).not.toBeNull()
expect(getByText(/all rubrics aligned to outcomes within this course/)).not.toBeNull()
})
})

View File

@ -16,21 +16,22 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react'
import ConfirmMasteryScaleEdit from '../ConfirmMasteryScaleEdit'
import ConfirmMasteryModal from '../ConfirmMasteryModal'
import {render, fireEvent} from '@testing-library/react'
const defaultProps = () => ({
onConfirm: () => {},
contextType: 'account',
isOpen: true,
onClose: () => {}
onClose: () => {},
title: 'title',
modalText: 'body!!'
})
it('calls onClose and does not call onConfirm when canceled', () => {
const onConfirm = jest.fn()
const onClose = jest.fn()
const {getByText} = render(
<ConfirmMasteryScaleEdit {...defaultProps()} onConfirm={onConfirm} onClose={onClose} />
<ConfirmMasteryModal {...defaultProps()} onConfirm={onConfirm} onClose={onClose} />
)
fireEvent.click(getByText('Cancel'))
expect(onConfirm).not.toHaveBeenCalled()
@ -39,19 +40,13 @@ it('calls onClose and does not call onConfirm when canceled', () => {
it('does call onConfirm when saved', () => {
const onConfirm = jest.fn()
const {getByText} = render(<ConfirmMasteryScaleEdit {...defaultProps()} onConfirm={onConfirm} />)
const {getByText} = render(<ConfirmMasteryModal {...defaultProps()} onConfirm={onConfirm} />)
fireEvent.click(getByText('Save'))
expect(onConfirm).toHaveBeenCalled()
})
describe('modal text', () => {
it('renders correct text for an Account context', () => {
const {getByText} = render(<ConfirmMasteryScaleEdit {...defaultProps()} />)
expect(getByText(/all account and course level rubrics/)).not.toBeNull()
})
it('renders correct text for a Course context', () => {
const {getByText} = render(<ConfirmMasteryScaleEdit {...defaultProps()} contextType="Course" />)
expect(getByText(/all rubrics aligned to outcomes within this course/)).not.toBeNull()
})
it('renders the modalText and title provided as props', () => {
const {getByText} = render(<ConfirmMasteryModal {...defaultProps()} />)
expect(getByText(/title/)).not.toBeNull()
expect(getByText(/body!!/)).not.toBeNull()
})