From d2d6bbf51ce01d5196061bc12945a25f9c056e85 Mon Sep 17 00:00:00 2001 From: Zachary Hancock Date: Tue, 31 Aug 2021 09:37:51 -0400 Subject: [PATCH] feat: support for content gating (#41) * feat: support content gating * fix: minor HTML validation issues --- src/exam/Exam.jsx | 7 ++++-- src/exam/ExamWrapper.jsx | 7 +++++- src/exam/ExamWrapper.test.jsx | 22 +++++++++++++++++++ .../DefaultInstructions.jsx | 4 ++-- .../download-instructions/index.jsx | 4 ++-- 5 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/exam/Exam.jsx b/src/exam/Exam.jsx index 5ccaa6db..5d58ca0b 100644 --- a/src/exam/Exam.jsx +++ b/src/exam/Exam.jsx @@ -18,7 +18,9 @@ import { ExamStatus, ExamType } from '../constants'; * @returns {JSX.Element} * @constructor */ -const Exam = ({ isTimeLimited, originalUserIsStaff, children }) => { +const Exam = ({ + isGated, isTimeLimited, originalUserIsStaff, children, +}) => { const state = useContext(ExamStateContext); const { isLoading, activeAttempt, showTimer, stopExam, exam, @@ -93,7 +95,7 @@ const Exam = ({ isTimeLimited, originalUserIsStaff, children }) => { /> )} {apiErrorMsg && } - {isTimeLimited && !originalUserIsStaff + {isTimeLimited && !originalUserIsStaff && !isGated ? {sequenceContent} : sequenceContent} @@ -102,6 +104,7 @@ const Exam = ({ isTimeLimited, originalUserIsStaff, children }) => { Exam.propTypes = { isTimeLimited: PropTypes.bool.isRequired, + isGated: PropTypes.bool.isRequired, originalUserIsStaff: PropTypes.bool.isRequired, children: PropTypes.element.isRequired, }; diff --git a/src/exam/ExamWrapper.jsx b/src/exam/ExamWrapper.jsx index 51b6d6a6..a644f95a 100644 --- a/src/exam/ExamWrapper.jsx +++ b/src/exam/ExamWrapper.jsx @@ -22,6 +22,8 @@ const ExamWrapper = ({ children, ...props }) => { await getAllowProctoringOptOut(sequence.allowProctoringOptOut); }; + const isGated = sequence && sequence.gatedContent !== undefined && sequence.gatedContent.gated; + // if the user is browsing public content (not logged in) they cannot be in an exam // if the user is staff they may view exam content without an exam attempt // any requests for exam state will 403 so just short circuit this component here @@ -34,7 +36,7 @@ const ExamWrapper = ({ children, ...props }) => { }, []); return ( - + {children} ); @@ -45,6 +47,9 @@ ExamWrapper.propTypes = { id: PropTypes.string.isRequired, isTimeLimited: PropTypes.bool, allowProctoringOptOut: PropTypes.bool, + gatedContent: PropTypes.shape({ + gated: PropTypes.bool, + }), }).isRequired, courseId: PropTypes.string.isRequired, children: PropTypes.element.isRequired, diff --git a/src/exam/ExamWrapper.test.jsx b/src/exam/ExamWrapper.test.jsx index c8ac46f3..c2430a6d 100644 --- a/src/exam/ExamWrapper.test.jsx +++ b/src/exam/ExamWrapper.test.jsx @@ -163,6 +163,28 @@ describe('SequenceExamWrapper', () => { expect(queryByTestId('masquerade-alert')).toBeInTheDocument(); }); + it('allows default content rendering for gated sections even for exams', () => { + sequence.gatedContent = { + gated: true, + }; + store.getState = () => ({ + examState: Factory.build('examState', { + exam: Factory.build('exam', { + type: ExamType.PROCTORED, + }), + }), + }); + const { queryByTestId } = render( + + +
children
+
+
, + { store }, + ); + expect(queryByTestId('sequence-content')).toHaveTextContent('children'); + }); + it('does not display masquerade alert if specified learner is in the middle of the exam', () => { store.getState = () => ({ examState: Factory.build('examState', { diff --git a/src/instructions/proctored_exam/download-instructions/DefaultInstructions.jsx b/src/instructions/proctored_exam/download-instructions/DefaultInstructions.jsx index 234ff4be..b4d201dd 100644 --- a/src/instructions/proctored_exam/download-instructions/DefaultInstructions.jsx +++ b/src/instructions/proctored_exam/download-instructions/DefaultInstructions.jsx @@ -11,9 +11,9 @@ const DefaultInstructions = ({ code }) => ( defaultMessage="Step 1." /> -

+

-

+

{!withProviderInstructions && ( -

+

-

+
)} {allowProctoringOptOut && }