UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

265 lines (217 loc) 8.91 kB
import {vi} from 'vitest' import React from 'react' import {render, screen, fireEvent} from '@testing-library/react' import userEvent from '@testing-library/user-event' import EssayEdit from '../index' import runAxeCheck from '@instructure/ui-axe-check' const merge = (objA, objB) => { return Object.assign({}, objA, objB) } const testRender = (overrideProps, overrideInteractionData) => { const changeItemStateStub = vi.fn() const {container} = render( <EssayEdit {...merge( { changeItemState: changeItemStateStub, enableRichContentEditor: false, // disable to make testing easier itemBody: 'STEM', openImportModal: () => {}, scoringData: {value: 'there better be some text here'}, additionalOptions: [], interactionData: merge( { rce: false, spellCheck: false, wordCount: false, wordLimitEnabled: false, wordLimitMax: null, wordLimitMin: null, notes: '', }, overrideInteractionData, ), }, overrideProps, )} />, ) return { changeItemStateStub, container, } } const assertNewAttrEqual = (changeItemStateStub, attr, expectedVal) => { expect(changeItemStateStub).toHaveBeenCalled() const newVal = changeItemStateStub.mock.lastCall[0].interactionData[attr] expect(newVal).toBe(expectedVal) } describe('Essay Edit', () => { let defaultProps = {} let changeItemStateStub beforeEach(() => { changeItemStateStub = vi.fn() defaultProps = { ...merge({ changeItemState: changeItemStateStub, enableRichContentEditor: false, // disable to make testing easier itemBody: 'STEM', openImportModal: () => {}, scoringData: {value: 'there better be some text here'}, additionalOptions: [], interactionData: merge({ rce: false, spellCheck: false, wordCount: false, wordLimitEnabled: false, wordLimitMax: null, wordLimitMin: null, notes: '', }), }), } }) describe('rendering', () => { it('renders a basic essay', () => { const {container} = render(<EssayEdit {...defaultProps} enableRichContentEditor />) // Check the input fields expect(container.querySelector('[data-automation="sdk-question-stem"]')).toBeInTheDocument() expect(screen.queryByRole('textbox', {name: /rich content editor/i})).toBeInTheDocument() // Check the tabs expect(screen.queryByRole('button', {name: /options/i})).toBeInTheDocument() expect(screen.queryByRole('button', {name: /grading notes/i})).toBeInTheDocument() // Check the word limit inputs expect(screen.queryByRole('textbox', {name: /Minimum/i})).not.toBeInTheDocument() expect(screen.queryByRole('textbox', {name: /Maximum/i})).not.toBeInTheDocument() }) it('renders with the rce disabled', () => { const {container} = testRender({enableRichContentEditor: false}) expect(container).toBeDefined() const rces = container.querySelectorAll('[data-automation^="sdk-rce"]') expect(rces).toHaveLength(0) }) it('renders word limit inputs', () => { testRender( {}, { wordLimitEnabled: true, wordLimitMin: 0, wordLimitMax: 0, }, ) const minInput = screen.getByLabelText(/Minimum/i) expect(minInput).toBeDefined() expect(minInput.value).toBe('0') const maxInput = screen.getByLabelText(/Maximum/i) expect(maxInput).toBeDefined() expect(maxInput.value).toBe('0') }) it('overrideEditableForRegrading disables a bunch of things', () => { const {container} = render(<EssayEdit {...defaultProps} overrideEditableForRegrading />) const questionStemTextArea = container.querySelector( '[data-automation="sdk-question-stem"] textarea', ) expect(questionStemTextArea.getAttribute('disabled')).not.toBeNull() const checkboxes = screen.queryAllByRole('checkbox') for (const checkbox of checkboxes) { expect(checkbox.getAttribute('disabled')).not.toBeNull() } const gradingNotesTab = screen.getByRole('button', {name: /grading notes/i}) userEvent.click(gradingNotesTab) const gradingNotesTextArea = screen.getByRole('textbox', {name: /grading notes/i}) expect(gradingNotesTextArea.getAttribute('disabled')).not.toBeNull() }) }) describe('#changeItemState', () => { it('handles changes to itemBody', () => { const {changeItemStateStub, container} = testRender() const stem = container.querySelector('[data-automation="sdk-question-stem"] textarea') expect(stem).not.toBeNull() fireEvent.change(stem, {target: {value: 'new stem val'}}) expect(changeItemStateStub).toHaveBeenCalled() expect(changeItemStateStub.mock.calls[0][0].itemBody).toBe('new stem val') }) it('saves the updated notes with changeItemState', () => { render(<EssayEdit {...defaultProps} />) const gradingNotesTab = screen.getByRole('button', {name: /grading notes/i}) userEvent.click(gradingNotesTab) const gradingNotesTextArea = screen.getByRole('textbox', {name: /grading notes/i}) userEvent.clear(gradingNotesTextArea) userEvent.type(gradingNotesTextArea, 'new val') expect(changeItemStateStub).toHaveBeenCalledWith({scoringData: {value: 'new val'}}) }) // TODO: QUIZ-XXXXX — migrate "handles all the toggles" test // This test relies on @instructure/ui-test-utils' wrapper .label property // which maps checkbox labels to interactionData keys. Needs RTL migration. it.skip('handles all the toggles', () => {}) it('handles minimum word limit changes', () => { const {changeItemStateStub} = testRender({}, {wordLimitEnabled: true}) const minInput = screen.getByLabelText(/Minimum/i) expect(minInput).toBeDefined() fireEvent.change(minInput, {target: {value: '10'}}) fireEvent.blur(minInput) assertNewAttrEqual(changeItemStateStub, 'wordLimitMin', '10') // make sure blank sets it to null fireEvent.change(minInput, {target: {value: ''}}) fireEvent.blur(minInput) assertNewAttrEqual(changeItemStateStub, 'wordLimitMin', null) }) it('handles maximum word limit changes', () => { const {changeItemStateStub} = testRender({}, {wordLimitEnabled: true}) const maxInput = screen.getByLabelText(/Maximum/i) expect(maxInput).toBeDefined() fireEvent.change(maxInput, {target: {value: '100'}}) fireEvent.blur(maxInput) assertNewAttrEqual(changeItemStateStub, 'wordLimitMax', '100') // make sure blank sets it to null fireEvent.change(maxInput, {target: {value: ''}}) fireEvent.blur(maxInput) assertNewAttrEqual(changeItemStateStub, 'wordLimitMax', null) }) }) describe('calculator per question option', () => { it('does not render the calculator checkbox if showCalculatorOption is false', () => { render(<EssayEdit {...defaultProps} showCalculatorOption={false} />) expect(screen.queryByRole('checkbox', {name: /show on-screen calculator/i})).toBeNull() }) it('shows with basic option selected', () => { testRender({calculatorType: 'basic'}) const calculatorBasicRadioInput = screen.getByRole('radio', {name: /basic calculator/i}) const calculatorScientificRadioInput = screen.getByRole('radio', { name: /scientific calculator/i, }) expect(calculatorBasicRadioInput.checked).toBe(true) expect(calculatorScientificRadioInput.checked).toBe(false) }) it('switches from none to basic calculator', () => { const {changeItemStateStub} = testRender({calculatorType: 'none'}) const calculatorCheckbox = screen.getByRole('checkbox', { name: /show on-screen calculator/i, }) userEvent.click(calculatorCheckbox) expect(changeItemStateStub).toHaveBeenCalled() expect(changeItemStateStub.mock.lastCall[0].calculatorType).toBe('basic') }) it('enables one question at a time properly', () => { const setOQAATStub = vi.fn() testRender({ calculatorType: 'basic', oneQuestionAtATime: false, setOneQuestionAtATime: setOQAATStub, }) const toggle = screen.getByRole('checkbox', {name: /enable one question at a time/i}) userEvent.click(toggle) expect(setOQAATStub).toHaveBeenCalledTimes(1) }) }) describe('a11y tests', () => { it('should meet a11y standards', async () => { testRender() expect( await runAxeCheck(document.body, { ignores: ['region'], }), ).toBe(true) }) }) })