From 3a04fbf18ac5821fdcf738d427768729dad351f5 Mon Sep 17 00:00:00 2001 From: Mashal Malik <107556986+Mashal-m@users.noreply.github.com> Date: Tue, 31 Oct 2023 23:20:01 +0500 Subject: [PATCH 1/3] refactor: updated README file to reflect template changes (#124) * refactor: updated README file to reflect template changes * docs: fixed some typos --------- Co-authored-by: ilee2u --- README.rst | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 9c4c5e2a..61eb67cc 100644 --- a/README.rst +++ b/README.rst @@ -1,9 +1,18 @@ +########################## frontend-lib-special-exams -================================= +########################## + +******* +Purpose +******* This is a react library responsible for extending learning app with special exams functionality, e.g. proctored/timed exams. -Set up instructions --------------------------- +*************** +Getting Started +*************** + +Installation +============ 1. Clone your new repo: @@ -17,15 +26,69 @@ Set up instructions ``cd frontend-lib-special-exams && npm install`` +******************* Build Process Notes -------------------- +******************* **Production Build** The production build is created with ``npm run build``. +********************* Internationalization --------------------- +********************* Please see `edx/frontend-platform's i18n module `_ for documentation on internationalization. The documentation explains how to use it, and the `How To `_ has more detail. +************ +Contributing +************ + +Contributions are very welcome. Please read `How To Contribute`_ for details. + +.. _How To Contribute: https://openedx.org/r/how-to-contribute + +This project is currently accepting all types of contributions, bug fixes, +security fixes, maintenance work, or new features. However, please make sure +to have a discussion about your new feature idea with the maintainers prior to +beginning development to maximize the chances of your change being accepted. +You can start a conversation by creating a new issue on this repo summarizing +your idea. + +************ +Getting Help +************ + +If you're having trouble, we have discussion forums at +https://discuss.openedx.org where you can connect with others in the community. + +Our real-time conversations are on Slack. You can request a `Slack +invitation`_, then join our `community Slack workspace`_. Because this is a +frontend repository, the best place to discuss it would be in the `#wg-frontend +channel`_. + +For anything non-trivial, the best path is to open an issue in this repository +with as many details about the issue you are facing as you can provide. + +https://github.com/openedx/frontend-lib-special-exams/issues + +For more information about these options, see the `Getting Help`_ page. + +.. _Slack invitation: https://openedx.org/slack +.. _community Slack workspace: https://openedx.slack.com/ +.. _#wg-frontend channel: https://openedx.slack.com/archives/C04BM6YC7A6 +.. _Getting Help: https://openedx.org/community/connect + +**************************** +The Open edX Code of Conduct +**************************** + +All community members are expected to follow the `Open edX Code of Conduct`_. + +.. _Open edX Code of Conduct: https://openedx.org/code-of-conduct/ + +************************** +Reporting Security Issues +************************** + +Please do not report security issues in public. Please email security@openedx.org. \ No newline at end of file From 1d236a42afa237671217675e9f0f8577a77adc93 Mon Sep 17 00:00:00 2001 From: Zachary Hancock Date: Fri, 15 Dec 2023 14:55:17 -0500 Subject: [PATCH 2/3] feat: new checkApp origin requirement (#126) --- src/data/messages/proctorio.js | 4 +++- src/data/redux.test.jsx | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/data/messages/proctorio.js b/src/data/messages/proctorio.js index 935d53ae..a32aa2b3 100644 --- a/src/data/messages/proctorio.js +++ b/src/data/messages/proctorio.js @@ -5,10 +5,12 @@ * vendor-specific integrations long term. As of now these events * will trigger on ANY lti integration, not just Proctorio. */ +import { getConfig } from '@edx/frontend-platform'; + export async function checkAppStatus() { return new Promise((resolve, reject) => { const handleResponse = event => { - if (event.origin === window.location.origin) { + if (event.origin === getConfig().EXAMS_BASE_URL) { window.removeEventListener('message', handleResponse); if (event?.data?.active) { resolve(); diff --git a/src/data/redux.test.jsx b/src/data/redux.test.jsx index b5c0cd96..b56e0ee9 100644 --- a/src/data/redux.test.jsx +++ b/src/data/redux.test.jsx @@ -1152,7 +1152,7 @@ describe('Data layer integration tests', () => { await new Promise(process.nextTick); const handleResponseCb = mockAddEventListener.mock.calls[0][1]; axiosMock.onPut(`${createUpdateAttemptURL}/${proctoredAttempt.attempt_id}`).reply(200, { exam_attempt_id: proctoredAttempt.attempt_id }); - handleResponseCb({ origin: 'https://edx.example.com', data: { active: false } }); + handleResponseCb({ origin: 'http://localhost:18740', data: { active: false } }); await new Promise(process.nextTick); const request = axiosMock.history.put[0]; @@ -1171,7 +1171,7 @@ describe('Data layer integration tests', () => { await new Promise(process.nextTick); const handleResponseCb = mockAddEventListener.mock.calls[0][1]; - handleResponseCb({ origin: 'https://edx.example.com', data: { active: true } }); + handleResponseCb({ origin: 'http://localhost:18740', data: { active: true } }); await new Promise(process.nextTick); expect(axiosMock.history.put.length).toBe(0); From 8457727a0a2b33614c380497ad0fb0cd02b664a4 Mon Sep 17 00:00:00 2001 From: Michael Roytman Date: Wed, 20 Dec 2023 12:44:36 -0500 Subject: [PATCH 3/3] feat: add support for a technical support URL in the LTI-based provider download instructions (#127) This commit adds support for displaying a technical support URL for LTI-based providers on the download instructions interstitial. The download instructions will display a technical support URL when it is returned from the proctoring settings backend endpoint. If the technical support URL is not available, then the technical support email and technical support phone number will be used instead. --- src/data/__snapshots__/redux.test.jsx.snap | 5 ++ src/data/slice.js | 1 + src/instructions/Instructions.test.jsx | 42 ++++++++++++- .../LtiProviderInstructions.jsx | 63 +++++++++++++------ .../download-instructions/index.jsx | 2 + 5 files changed, 92 insertions(+), 21 deletions(-) diff --git a/src/data/__snapshots__/redux.test.jsx.snap b/src/data/__snapshots__/redux.test.jsx.snap index e1cb7ba9..69475b13 100644 --- a/src/data/__snapshots__/redux.test.jsx.snap +++ b/src/data/__snapshots__/redux.test.jsx.snap @@ -84,6 +84,7 @@ Object { "provider_name": "", "provider_tech_support_email": "", "provider_tech_support_phone": "", + "provider_tech_support_url": "", }, "timeIsOver": false, }, @@ -129,6 +130,7 @@ Object { "provider_name": "", "provider_tech_support_email": "", "provider_tech_support_phone": "", + "provider_tech_support_url": "", }, "timeIsOver": false, }, @@ -148,6 +150,7 @@ Object { "provider_name": "", "provider_tech_support_email": "", "provider_tech_support_phone": "", + "provider_tech_support_url": "", } `; @@ -302,6 +305,7 @@ Object { "provider_name": "", "provider_tech_support_email": "", "provider_tech_support_phone": "", + "provider_tech_support_url": "", }, "timeIsOver": false, }, @@ -370,6 +374,7 @@ Object { "provider_name": "", "provider_tech_support_email": "", "provider_tech_support_phone": "", + "provider_tech_support_url": "", }, "timeIsOver": false, }, diff --git a/src/data/slice.js b/src/data/slice.js index 39c038ed..0b633dcc 100644 --- a/src/data/slice.js +++ b/src/data/slice.js @@ -17,6 +17,7 @@ export const examSlice = createSlice({ }, provider_tech_support_email: '', provider_tech_support_phone: '', + provider_tech_support_url: '', provider_name: '', learner_notification_from_email: '', integration_specific_email: '', diff --git a/src/instructions/Instructions.test.jsx b/src/instructions/Instructions.test.jsx index 92458074..22cbba99 100644 --- a/src/instructions/Instructions.test.jsx +++ b/src/instructions/Instructions.test.jsx @@ -723,7 +723,7 @@ describe('SequenceExamWrapper', () => { expect(screen.getByText('You have submitted this proctored exam for review')).toBeInTheDocument(); }); - it('Shows correct download instructions for LTI provider if attempt status is created', () => { + it('Shows correct download instructions for LTI provider if attempt status is created, with support email and phone', () => { store.getState = () => ({ examState: Factory.build('examState', { activeAttempt: {}, @@ -760,6 +760,46 @@ describe('SequenceExamWrapper', () => { expect(screen.getByText('Start Exam')).toBeInTheDocument(); }); + it('Shows correct download instructions for LTI provider if attempt status is created with support URL', () => { + store.getState = () => ({ + examState: Factory.build('examState', { + activeAttempt: {}, + proctoringSettings: Factory.build('proctoringSettings', { + provider_name: 'LTI Provider', + provider_tech_support_email: 'ltiprovidersupport@example.com', + provider_tech_support_phone: '+123456789', + provider_tech_support_url: 'www.example.com', + }), + exam: Factory.build('exam', { + is_proctored: true, + type: ExamType.PROCTORED, + attempt: Factory.build('attempt', { + attempt_status: ExamStatus.CREATED, + }), + }), + }), + }); + + render( + + +
Sequence
+
+
, + { store }, + ); + + expect(screen.getByText( + 'If you have issues relating to proctoring, you can contact LTI Provider technical support by visiting', + { exact: false }, + )).toBeInTheDocument(); + expect(screen.getByRole('link', { name: 'www.example.com in a new tab' })).toHaveAttribute('href', 'www.example.com'); + + expect(screen.getByText('Set up and start your proctored exam.')).toBeInTheDocument(); + expect(screen.getByText('Start System Check')).toBeInTheDocument(); + expect(screen.getByText('Start Exam')).toBeInTheDocument(); + }); + it('Hides support contact info on download instructions for LTI provider if not provided', () => { store.getState = () => ({ examState: Factory.build('examState', { diff --git a/src/instructions/proctored_exam/download-instructions/LtiProviderInstructions.jsx b/src/instructions/proctored_exam/download-instructions/LtiProviderInstructions.jsx index e9cc2d84..a68e9fca 100644 --- a/src/instructions/proctored_exam/download-instructions/LtiProviderInstructions.jsx +++ b/src/instructions/proctored_exam/download-instructions/LtiProviderInstructions.jsx @@ -1,47 +1,70 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import { Hyperlink } from '@edx/paragon'; const LtiProviderExamInstructions = ({ - providerName, supportEmail, supportPhone, -}) => ( - <> -

- -

- {supportEmail && supportPhone && ( -

+ providerName, supportEmail, supportPhone, supportURL, +}) => { + const supportURLHyperlink = (chunks) => ( + + {chunks} + + ); + + const getSupportText = () => { + // We assume that an LTI-based provider will either have a supportURL or a supportEmail and supportPhone. + // In the unlikely event a provider has all three, we prioritize the supportURL. + if (supportURL) { + return ( {supportURL}.'} + values={{ + providerName, + supportURL, + a: supportURLHyperlink, + }} + /> + ); + } + if (supportEmail && supportPhone) { + return ( + -

- )} - -); + ); + } + return null; + }; + + return ( +

+ {getSupportText()} +

+ ); +}; LtiProviderExamInstructions.propTypes = { providerName: PropTypes.string, supportEmail: PropTypes.string, supportPhone: PropTypes.string, + supportURL: PropTypes.string, }; LtiProviderExamInstructions.defaultProps = { providerName: '', supportEmail: '', supportPhone: '', + supportURL: '', }; export default LtiProviderExamInstructions; diff --git a/src/instructions/proctored_exam/download-instructions/index.jsx b/src/instructions/proctored_exam/download-instructions/index.jsx index 868db55b..298d81c2 100644 --- a/src/instructions/proctored_exam/download-instructions/index.jsx +++ b/src/instructions/proctored_exam/download-instructions/index.jsx @@ -39,6 +39,7 @@ const DownloadSoftwareProctoredExamInstructions = ({ intl, skipProctoredExam }) provider_name: providerName, provider_tech_support_email: supportEmail, provider_tech_support_phone: supportPhone, + provider_tech_support_url: supportURL, exam_proctoring_backend: proctoringBackend, } = proctoringSettings; const examHasLtiProvider = !useLegacyAttemptApi; @@ -81,6 +82,7 @@ const DownloadSoftwareProctoredExamInstructions = ({ intl, skipProctoredExam }) providerName={providerName} supportEmail={supportEmail} supportPhone={supportPhone} + supportURL={supportURL} /> ); }