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();
+ });
+ });
+});