@instructure/quiz-taking
Version:
300 lines (260 loc) • 11.7 kB
JavaScript
import {sessionStore} from '@instructure/quiz-core/common/util/sessionStore'
import {TakeButton} from '../presenter'
import React from 'react'
import {describe, expect, it, vi} from 'vitest'
import userEvent from '@testing-library/user-event'
import {canvas} from '@instructure/ui-themes'
import {hexToRgb} from '../../../../tests/utils/hexToRgb'
import {render, screen} from '../../../../tests/utils/rtlRenderOverride'
describe('TakeButton presenter', () => {
const submitQuizStub = vi.fn()
const nextQuestionStub = vi.fn()
const onSubmitQuizSession = () => {}
const screenreaderNotificationStub = vi.fn()
const withConfirmStub = vi.fn()
const defaultProps = {
allowBacktracking: true,
currentSessionItemPosition: 3,
isOneQuestionAtATime: true,
currentQuestionIsLast: false,
itemId: '66',
largeFormatResponse: {value: 'goodbye'},
nextQuestion: nextQuestionStub,
quizSessionId: '22',
onSubmitQuizSession,
screenreaderNotification: screenreaderNotificationStub,
submitQuiz: submitQuizStub,
userResponse: {value: 'hello'},
withConfirm: withConfirmStub,
isPassage: false,
}
describe('one question at a time on', () => {
it('renders a next button if not on the last question', () => {
render(<TakeButton {...defaultProps} />)
const nextButton = screen.getByRole('button', {name: /next/i})
expect(nextButton).toBeInTheDocument()
})
it('renders a submit button if on the last question', () => {
render(<TakeButton {...defaultProps} currentQuestionIsLast />)
const submitButton = screen.queryByRole('button', {name: /submit/i})
expect(submitButton).toBeInTheDocument()
})
describe('when iceTopNavBarEnabled is true and isFooter is false', () => {
it('renders a next button if not on the last question', () => {
render(<TakeButton {...defaultProps} iceTopNavBarEnabled />)
const nextButton = screen.getByRole('button', {name: /next/i})
expect(nextButton).toBeInTheDocument()
expect(nextButton).toHaveAttribute('data-automation', 'sdk-oqaat-next-or-submit-button')
})
it('renders a submit button if on the last question', () => {
render(<TakeButton {...defaultProps} iceTopNavBarEnabled currentQuestionIsLast />)
const submitButton = screen.queryByRole('button', {name: /submit/i})
expect(submitButton).toBeInTheDocument()
})
})
describe('clicking the next button', () => {
it('triggers the correct callback', () => {
render(<TakeButton {...defaultProps} />)
const button = screen.getByRole('button', {name: /next/i})
userEvent.click(button)
const args = [
defaultProps.quizSessionId,
defaultProps.allowBacktracking,
defaultProps.currentSessionItemPosition === defaultProps.sessionItemsCount,
defaultProps.currentSessionItemPosition,
defaultProps.itemId,
defaultProps.userResponse,
defaultProps.largeFormatResponse,
]
expect(nextQuestionStub).toHaveBeenCalledWith(
...args,
expect.any(Function),
expect.any(Boolean),
)
})
it('confirms Next when backtracking disabled and no response', () => {
render(<TakeButton {...defaultProps} allowBacktracking={false} userResponse={{}} />)
const button = screen.getByRole('button', {name: /next/i})
userEvent.click(button)
expect(withConfirmStub).toHaveBeenCalled()
expect(nextQuestionStub).not.toHaveBeenCalled()
})
it('does not confirm Next when there is a response', () => {
render(
<TakeButton
{...defaultProps}
allowBacktracking={false}
userResponse={{value: 'value'}}
/>,
)
const button = screen.getByRole('button', {name: /next/i})
userEvent.click(button)
expect(withConfirmStub).not.toHaveBeenCalled()
expect(nextQuestionStub).toHaveBeenCalled()
})
it('does not confirm Next when backtracking enabled', () => {
render(<TakeButton {...defaultProps} allowBacktracking={true} userResponse={{}} />)
const button = screen.getByRole('button', {name: /next/i})
userEvent.click(button)
expect(withConfirmStub).not.toHaveBeenCalled()
expect(nextQuestionStub).toHaveBeenCalled()
})
it('does not confirm Next if user has asked us not to', () => {
render(<TakeButton {...defaultProps} allowBacktracking={false} userResponse={{}} />)
sessionStore.set(`quiz_session_${defaultProps.quizSessionId}_confirm_next`, 'false')
const button = screen.getByRole('button', {name: /next/i})
userEvent.click(button)
expect(withConfirmStub).not.toHaveBeenCalled()
expect(nextQuestionStub).toHaveBeenCalled()
})
describe('required question validation', () => {
it('shows required question alert when survey, no backtracking, required, and unanswered', () => {
render(
<TakeButton
{...defaultProps}
isSurvey={true}
allowBacktracking={false}
isRequiredItem={true}
userResponse={{}}
/>,
)
const button = screen.getByRole('button', {name: /next/i})
userEvent.click(button)
expect(withConfirmStub).toHaveBeenCalled()
expect(nextQuestionStub).not.toHaveBeenCalled()
const modalOptions = withConfirmStub.mock.calls[0][1]
expect(modalOptions.title).toBe('Required Question')
expect(modalOptions.continueText).toBe('Ok')
expect(modalOptions.hideCancelButton).toBe(true)
})
it('does not show required alert when not a survey (shows optional warning instead)', () => {
render(
<TakeButton
{...defaultProps}
isSurvey={false}
allowBacktracking={false}
isRequiredItem={true}
userResponse={{}}
/>,
)
const button = screen.getByRole('button', {name: /next/i})
userEvent.click(button)
expect(withConfirmStub).toHaveBeenCalled()
expect(nextQuestionStub).not.toHaveBeenCalled()
const modalOptions = withConfirmStub.mock.calls[0][1]
expect(modalOptions.title).toBe('Are you sure?')
expect(modalOptions.continueText).toBe('Confirm')
expect(modalOptions.hideCancelButton).toBeUndefined()
})
it('does not show alert when backtracking is enabled', () => {
render(
<TakeButton
{...defaultProps}
isSurvey={true}
allowBacktracking={true}
isRequiredItem={true}
userResponse={{}}
/>,
)
const button = screen.getByRole('button', {name: /next/i})
userEvent.click(button)
expect(withConfirmStub).not.toHaveBeenCalled()
expect(nextQuestionStub).toHaveBeenCalled()
})
it('does not show required alert when question is not required (shows optional warning instead)', () => {
render(
<TakeButton
{...defaultProps}
isSurvey={true}
allowBacktracking={false}
isRequiredItem={false}
userResponse={{}}
/>,
)
const button = screen.getByRole('button', {name: /next/i})
userEvent.click(button)
expect(withConfirmStub).toHaveBeenCalled()
expect(nextQuestionStub).not.toHaveBeenCalled()
const modalOptions = withConfirmStub.mock.calls[0][1]
expect(modalOptions.title).toBe('Are you sure?')
expect(modalOptions.continueText).toBe('Confirm')
expect(modalOptions.hideCancelButton).toBeUndefined()
})
})
it('reads nothing when backtracking is allowed on the last question', () => {
render(<TakeButton {...defaultProps} currentQuestionIsLast />)
const button = screen.getByRole('button', {name: /submit/i})
userEvent.click(button)
expect(screenreaderNotificationStub).not.toHaveBeenCalled()
})
it('reads the text when backtracking is allowed when not on the last question', () => {
render(<TakeButton {...defaultProps} allowBacktracking={true} />)
const button = screen.getByRole('button', {name: /next/i})
userEvent.click(button)
expect(screenreaderNotificationStub).toHaveBeenCalledWith('Moving to next question')
})
it('reads the text when backtracking is disabled', () => {
render(<TakeButton {...defaultProps} allowBacktracking={false} />)
const button = screen.getByRole('button', {name: /next/i})
userEvent.click(button)
expect(screenreaderNotificationStub).toHaveBeenCalledWith('Question submitted')
})
})
it('shows the submit button with primary light on non final question', () => {
render(<TakeButton {...defaultProps} />)
const submitButton = screen.getByRole('button', {name: /next/i})
// Note: This test was checking internal Button component props, which RTL discourages
// Consider testing the visual appearance or behavior instead
expect(submitButton).toBeInTheDocument()
})
it('shows the submit button with primary color on final question', () => {
render(<TakeButton {...defaultProps} currentQuestionIsLast={true} />)
const submitButton = screen.getByRole('button', {name: /submit/i})
expect(submitButton).toHaveStyle({color: hexToRgb(canvas.colors.contrasts.grey125125)})
})
it('disables button when disabled', () => {
render(<TakeButton {...defaultProps} disabled />)
const button = screen.getByRole('button', {name: /next/i})
expect(button).toBeDisabled()
})
describe('with no backtracking', () => {
it('does not render a previous button', () => {
render(<TakeButton {...defaultProps} allowBacktracking={false} />)
const button = screen.queryByRole('button', {name: /previous/i})
expect(button).not.toBeInTheDocument()
})
it('disables button when disabled', () => {
render(<TakeButton {...defaultProps} allowBacktracking={false} disabled />)
const button = screen.queryByRole('button', {name: /next/i})
expect(button).toBeDisabled()
})
})
})
describe('one question at a time off', () => {
it('shows the submit button with primary color', () => {
render(<TakeButton {...defaultProps} isOneQuestionAtATime={false} />)
const submitButton = screen.getByRole('button', {name: /submit/i})
expect(submitButton).toHaveStyle({color: hexToRgb(canvas.colors.contrasts.grey125125)})
})
describe('when iceTopNavBarEnabled is true and isFooter is false', () => {
it('render a submit button', () => {
render(
<TakeButton
{...defaultProps}
isOneQuestionAtATime={false}
iceTopNavBarEnabled={true}
isFooter={false}
/>,
)
const submitButton = screen.getByRole('button', {name: /submit/i})
expect(submitButton).toBeInTheDocument()
})
})
it('calls submit quiz when submit button clicked', () => {
render(<TakeButton {...defaultProps} isOneQuestionAtATime={false} />)
const submitButton = screen.getByRole('button', {name: /submit/i})
userEvent.click(submitButton)
expect(submitQuizStub).toHaveBeenCalledWith('22', onSubmitQuizSession)
})
})
})