From 0b764d2a082dcdceae1a8112025b9207471e5d9c Mon Sep 17 00:00:00 2001 From: Marcos Rigoli Date: Fri, 11 Oct 2024 09:58:50 -0300 Subject: [PATCH] Remove retry button in LTI Proctorio exams (#155) * fix: Remove retry button in LTI Proctorio exams * chore: Updated Instructions test to match the update --- src/instructions/Instructions.test.jsx | 3 +- .../SubmittedPracticeExamInstructions.jsx | 32 ++- ...SubmittedPracticeExamInstructions.test.jsx | 210 ++++++++++++++++++ 3 files changed, 233 insertions(+), 12 deletions(-) create mode 100644 src/instructions/practice_exam/SubmittedPracticeExamInstructions.test.jsx diff --git a/src/instructions/Instructions.test.jsx b/src/instructions/Instructions.test.jsx index 191591a2..83df8b96 100644 --- a/src/instructions/Instructions.test.jsx +++ b/src/instructions/Instructions.test.jsx @@ -502,7 +502,7 @@ describe('SequenceExamWrapper', () => { expect(screen.getByTestId('retry-exam-button')).toHaveTextContent('Retry my exam'); }); - it('Shows submitted practice exam instructions if exam is onboarding and attempt status is submitted', () => { + it('Shows submitted practice exam instructions if exam is onboarding and attempt status is submitted on legacy LTI exams', () => { store.getState = () => ({ specialExams: Factory.build('specialExams', { activeAttempt: {}, @@ -510,6 +510,7 @@ describe('SequenceExamWrapper', () => { is_proctored: true, type: ExamType.PRACTICE, attempt: Factory.build('attempt', { + use_legacy_attempt_api: true, attempt_status: ExamStatus.SUBMITTED, }), }), diff --git a/src/instructions/practice_exam/SubmittedPracticeExamInstructions.jsx b/src/instructions/practice_exam/SubmittedPracticeExamInstructions.jsx index 3113e4c6..b69201d7 100644 --- a/src/instructions/practice_exam/SubmittedPracticeExamInstructions.jsx +++ b/src/instructions/practice_exam/SubmittedPracticeExamInstructions.jsx @@ -1,12 +1,20 @@ import React from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { FormattedMessage } from '@edx/frontend-platform/i18n'; import { Button } from '@openedx/paragon'; import { resetExam } from '../../data'; +import { ExamStatus } from '../../constants'; const SubmittedPracticeExamInstructions = () => { const dispatch = useDispatch(); + const { exam } = useSelector(state => state.specialExams); + + // It does not show the reload button if the exam is submitted and not legacy + const showRetryButton = !( + exam.attempt?.attempt_status === ExamStatus.SUBMITTED + && !exam.attempt?.use_legacy_attempt_api + ); return (
@@ -23,16 +31,18 @@ const SubmittedPracticeExamInstructions = () => { + 'completed this practice exam and can continue with your course work.'} />

- + {showRetryButton ? ( + + ) : null}
); }; diff --git a/src/instructions/practice_exam/SubmittedPracticeExamInstructions.test.jsx b/src/instructions/practice_exam/SubmittedPracticeExamInstructions.test.jsx new file mode 100644 index 00000000..d5639dc1 --- /dev/null +++ b/src/instructions/practice_exam/SubmittedPracticeExamInstructions.test.jsx @@ -0,0 +1,210 @@ +import React from 'react'; +import { Factory } from 'rosie'; +import { SubmittedPracticeExamInstructions } from './index'; +import { + render, screen, initializeTestStore, fireEvent, +} from '../../setupTest'; +import { ExamStatus, ExamType } from '../../constants'; + +const mockresetReturn = {}; +const mockDispatch = jest.fn(); + +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useDispatch: () => mockDispatch, +})); + +jest.mock('../../data', () => ({ + ...jest.requireActual('../../data'), + resetExam: () => mockresetReturn, +})); + +describe('ExamTimerBlock', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('when the exam is not proctored', () => { + beforeEach(() => { + const preloadedState = { + specialExams: { + exam: Factory.build('exam', { + is_proctored: false, + type: ExamType.ONBOARDING, + attempt: Factory.build('attempt'), + }), + }, + }; + initializeTestStore(preloadedState); + + render( + , + ); + }); + + it('renders the component correctly', async () => { + expect(screen.getByText('You have submitted this practice proctored exam')).toBeInTheDocument(); + expect(screen.getByText( + 'Practice exams do not affect your grade. You have ' + + 'completed this practice exam and can continue with your course work.', + )).toBeInTheDocument(); + expect(screen.queryByTestId('retry-exam-button')).toBeInTheDocument(); + }); + + it('calls resetExam() when clicking the retry button', () => { + expect(mockDispatch).not.toHaveBeenCalled(); + + fireEvent.click(screen.getByTestId('retry-exam-button')); + + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith(mockresetReturn); + }); + }); + + describe('when the exam is not proctored', () => { + beforeEach(() => { + const preloadedState = { + specialExams: { + exam: Factory.build('exam', { + is_proctored: false, + type: ExamType.ONBOARDING, + attempt: Factory.build('attempt'), + }), + }, + }; + initializeTestStore(preloadedState); + + render( + , + ); + }); + + it('renders the component correctly', async () => { + expect(screen.getByText('You have submitted this practice proctored exam')).toBeInTheDocument(); + expect(screen.getByText( + 'Practice exams do not affect your grade. You have ' + + 'completed this practice exam and can continue with your course work.', + )).toBeInTheDocument(); + expect(screen.queryByTestId('retry-exam-button')).toBeInTheDocument(); + }); + + it('calls resetExam() when clicking the retry button', () => { + expect(mockDispatch).not.toHaveBeenCalled(); + + fireEvent.click(screen.getByTestId('retry-exam-button')); + + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith(mockresetReturn); + }); + }); + + describe('when a legacy proctoring attempt API is used', () => { + beforeEach(() => { + const preloadedState = { + specialExams: { + exam: Factory.build('exam', { + is_proctored: true, + type: ExamType.PROCTORED, + attempt: Factory.build('attempt', { + use_legacy_attempt_api: true, + }), + }), + }, + }; + initializeTestStore(preloadedState); + + render( + , + ); + }); + + it('renders the component correctly', async () => { + expect(screen.getByText('You have submitted this practice proctored exam')).toBeInTheDocument(); + expect(screen.getByText( + 'Practice exams do not affect your grade. You have ' + + 'completed this practice exam and can continue with your course work.', + )).toBeInTheDocument(); + expect(screen.queryByTestId('retry-exam-button')).toBeInTheDocument(); + }); + + it('calls resetExam() when clicking the retry button', () => { + expect(mockDispatch).not.toHaveBeenCalled(); + + fireEvent.click(screen.getByTestId('retry-exam-button')); + + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith(mockresetReturn); + }); + }); + + describe('when an LTI provider is used but it has an error', () => { + beforeEach(() => { + const preloadedState = { + specialExams: { + exam: Factory.build('exam', { + is_proctored: true, + type: ExamType.PROCTORED, + attempt: Factory.build('attempt', { + use_legacy_attempt_api: false, + attempt_status: ExamStatus.ERROR, + }), + }), + }, + }; + initializeTestStore(preloadedState); + + render( + , + ); + }); + + it('renders the component correctly', async () => { + expect(screen.getByText('You have submitted this practice proctored exam')).toBeInTheDocument(); + expect(screen.getByText( + 'Practice exams do not affect your grade. You have ' + + 'completed this practice exam and can continue with your course work.', + )).toBeInTheDocument(); + expect(screen.queryByTestId('retry-exam-button')).toBeInTheDocument(); + }); + + it('calls resetExam() when clicking the retry button', () => { + expect(mockDispatch).not.toHaveBeenCalled(); + + fireEvent.click(screen.getByTestId('retry-exam-button')); + + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith(mockresetReturn); + }); + }); + + describe('when an LTI provider is used and the exam is submitted', () => { + beforeEach(() => { + const preloadedState = { + specialExams: { + exam: Factory.build('exam', { + is_proctored: true, + type: ExamType.PROCTORED, + attempt: Factory.build('attempt', { + use_legacy_attempt_api: false, + attempt_status: ExamStatus.SUBMITTED, + }), + }), + }, + }; + initializeTestStore(preloadedState); + + render( + , + ); + }); + + it('doesn\'t show the button if it has an LTI provider', async () => { + expect(screen.getByText('You have submitted this practice proctored exam')).toBeInTheDocument(); + expect(screen.getByText( + 'Practice exams do not affect your grade. You have ' + + 'completed this practice exam and can continue with your course work.', + )).toBeInTheDocument(); + expect(screen.queryByTestId('retry-exam-button')).not.toBeInTheDocument(); + }); + }); +});