feat!: remove "Create Zendesk Tickets for suspicious attempts" setting from Proctored Exam Settings (#2517)

BREAKING CHANGE: This PR removes the deprecated “Create Zendesk Tickets for suspicious attempts”
setting from the Proctored Exam Settings modal in the frontend-app-authoring
MFE. This option was previously used with PSI and Zendesk to generate support
tickets for suspicious exam attempts. Since both systems are retired, the
setting no longer serves a purpose and has been fully removed.

Part of: https://github.com/openedx/edx-platform/issues/36329
This commit is contained in:
Santhosh Kumar
2025-12-12 03:35:05 +05:30
committed by GitHub
parent 225c82d037
commit 42acb53201
6 changed files with 3 additions and 115 deletions

View File

@@ -175,10 +175,6 @@ Feature: New Proctoring Exams View
Requirements Requirements
------------ ------------
* ``edx-platform`` Django settings:
* ``ZENDESK_*``: necessary if automatic ZenDesk ticket creation is desired
* `edx-exams <https://github.com/edx/edx-exams>`_: for this feature to work, the ``edx-exams`` IDA must be deployed and its API accessible by the browser * `edx-exams <https://github.com/edx/edx-exams>`_: for this feature to work, the ``edx-exams`` IDA must be deployed and its API accessible by the browser
Configuration Configuration
@@ -196,7 +192,6 @@ In Studio, a new item ("Proctored Exam Settings") is added to "Other Course Sett
* Enable proctored exams for the course * Enable proctored exams for the course
* Allow opting out of proctored exams * Allow opting out of proctored exams
* Select a proctoring provider * Select a proctoring provider
* Enable automatic creation of Zendesk tickets for "suspicious" proctored exam attempts
Feature: Advanced Settings Feature: Advanced Settings
========================== ==========================

View File

@@ -33,7 +33,6 @@ const ProctoringSettings = ({ onClose }) => {
proctoringProvider: false, proctoringProvider: false,
escalationEmail: '', escalationEmail: '',
allowOptingOut: false, allowOptingOut: false,
createZendeskTickets: false,
}; };
const [formValues, setFormValues] = useState(initialFormValues); const [formValues, setFormValues] = useState(initialFormValues);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@@ -80,12 +79,11 @@ const ProctoringSettings = ({ onClose }) => {
const value = target.type === 'checkbox' ? target.checked : target.value; const value = target.type === 'checkbox' ? target.checked : target.value;
const { name } = target; const { name } = target;
if (['allowOptingOut', 'createZendeskTickets'].includes(name)) { if (['allowOptingOut'].includes(name)) {
// Form.Radio expects string values, so convert back to a boolean here // Form.Radio expects string values, so convert back to a boolean here
setFormValues({ ...formValues, [name]: value === 'true' }); setFormValues({ ...formValues, [name]: value === 'true' });
} else if (name === 'proctoringProvider') { } else if (name === 'proctoringProvider') {
const newFormValues = { ...formValues, proctoringProvider: value }; const newFormValues = { ...formValues, proctoringProvider: value };
if (requiresEscalationEmailProviders.includes(value)) { if (requiresEscalationEmailProviders.includes(value)) {
setFormValues({ ...newFormValues }); setFormValues({ ...newFormValues });
setShowEscalationEmail(true); setShowEscalationEmail(true);
@@ -115,7 +113,6 @@ const ProctoringSettings = ({ onClose }) => {
enable_proctored_exams: formValues.enableProctoredExams, enable_proctored_exams: formValues.enableProctoredExams,
// lti providers are managed outside edx-platform, lti_external indicates this // lti providers are managed outside edx-platform, lti_external indicates this
proctoring_provider: isLtiProviderSelected ? 'lti_external' : selectedProvider, proctoring_provider: isLtiProviderSelected ? 'lti_external' : selectedProvider,
create_zendesk_tickets: formValues.createZendeskTickets,
}, },
}; };
if (isEdxStaff) { if (isEdxStaff) {
@@ -386,29 +383,6 @@ const ProctoringSettings = ({ onClose }) => {
</Form.Group> </Form.Group>
</fieldset> </fieldset>
)} )}
{/* CREATE ZENDESK TICKETS */}
{ isEdxStaff && formValues.enableProctoredExams && !isLtiProviderSelected && (
<fieldset aria-describedby="createZendeskTicketsText">
<Form.Group controlId="formCreateZendeskTickets">
<Form.Label as="legend" className="font-weight-bold">
{intl.formatMessage(messages['authoring.proctoring.createzendesk.label'])}
</Form.Label>
<Form.RadioSet
name="createZendeskTickets"
value={formValues.createZendeskTickets.toString()}
onChange={handleChange}
>
<Form.Radio value="true" data-testid="createZendeskTicketsYes">
{intl.formatMessage(messages['authoring.proctoring.yes'])}
</Form.Radio>
<Form.Radio value="false" data-testid="createZendeskTicketsNo">
{intl.formatMessage(messages['authoring.proctoring.no'])}
</Form.Radio>
</Form.RadioSet>
</Form.Group>
</fieldset>
)}
</> </>
); );
} }
@@ -571,7 +545,6 @@ const ProctoringSettings = ({ onClose }) => {
proctoringProvider: selectedProvider, proctoringProvider: selectedProvider,
enableProctoredExams: proctoredExamSettings.enable_proctored_exams, enableProctoredExams: proctoredExamSettings.enable_proctored_exams,
allowOptingOut: proctoredExamSettings.allow_proctoring_opt_out, allowOptingOut: proctoredExamSettings.allow_proctoring_opt_out,
createZendeskTickets: proctoredExamSettings.create_zendesk_tickets,
// The backend API may return null for the proctoringEscalationEmail value, which is the default. // The backend API may return null for the proctoringEscalationEmail value, which is the default.
// In order to keep our email input component controlled, we use the empty string as the default // In order to keep our email input component controlled, we use the empty string as the default
// and perform this conversion during GETs and POSTs. // and perform this conversion during GETs and POSTs.

View File

@@ -78,7 +78,6 @@ describe('ProctoredExamSettings', () => {
allow_proctoring_opt_out: false, allow_proctoring_opt_out: false,
proctoring_provider: 'mockproc', proctoring_provider: 'mockproc',
proctoring_escalation_email: 'test@example.com', proctoring_escalation_email: 'test@example.com',
create_zendesk_tickets: true,
}, },
available_proctoring_providers: ['software_secure', 'mockproc', 'lti_external'], available_proctoring_providers: ['software_secure', 'mockproc', 'lti_external'],
requires_escalation_email_providers: ['test_lti'], requires_escalation_email_providers: ['test_lti'],
@@ -95,36 +94,6 @@ describe('ProctoredExamSettings', () => {
await act(async () => render(renderComponent(<ProctoredExamSettings {...defaultProps} />))); await act(async () => render(renderComponent(<ProctoredExamSettings {...defaultProps} />)));
}); });
it('Updates Zendesk ticket field if software_secure is provider', async () => {
await waitFor(() => {
screen.getByDisplayValue('mockproc');
});
const selectElement = screen.getByDisplayValue('mockproc');
fireEvent.change(selectElement, { target: { value: 'software_secure' } });
const zendeskTicketInput = screen.getByTestId('createZendeskTicketsYes');
expect(zendeskTicketInput.checked).toEqual(true);
});
it('Updates Zendesk ticket field if software_secure is provider', async () => {
await waitFor(() => {
screen.getByDisplayValue('mockproc');
});
const selectElement = screen.getByDisplayValue('mockproc');
fireEvent.change(selectElement, { target: { value: 'software_secure' } });
const zendeskTicketInput = screen.getByTestId('createZendeskTicketsYes');
expect(zendeskTicketInput.checked).toEqual(true);
});
it('Does not update zendesk ticket field for any other provider', async () => {
await waitFor(() => {
screen.getByDisplayValue('mockproc');
});
const selectElement = screen.getByDisplayValue('mockproc');
fireEvent.change(selectElement, { target: { value: 'mockproc' } });
const zendeskTicketInput = screen.getByTestId('createZendeskTicketsYes');
expect(zendeskTicketInput.checked).toEqual(true);
});
it('Hides all other fields when enabledProctorExam is false when first loaded', async () => { it('Hides all other fields when enabledProctorExam is false when first loaded', async () => {
cleanup(); cleanup();
// Overrides the handler defined in beforeEach. // Overrides the handler defined in beforeEach.
@@ -136,7 +105,6 @@ describe('ProctoredExamSettings', () => {
allow_proctoring_opt_out: false, allow_proctoring_opt_out: false,
proctoring_provider: 'mockproc', proctoring_provider: 'mockproc',
proctoring_escalation_email: 'test@example.com', proctoring_escalation_email: 'test@example.com',
create_zendesk_tickets: true,
}, },
available_proctoring_providers: ['software_secure', 'mockproc'], available_proctoring_providers: ['software_secure', 'mockproc'],
requires_escalation_email_providers: [], requires_escalation_email_providers: [],
@@ -152,8 +120,6 @@ describe('ProctoredExamSettings', () => {
expect(screen.queryByText('Allow Opting Out of Proctored Exams')).toBeNull(); expect(screen.queryByText('Allow Opting Out of Proctored Exams')).toBeNull();
expect(screen.queryByDisplayValue('mockproc')).toBeNull(); expect(screen.queryByDisplayValue('mockproc')).toBeNull();
expect(screen.queryByTestId('escalationEmail')).toBeNull(); expect(screen.queryByTestId('escalationEmail')).toBeNull();
expect(screen.queryByTestId('createZendeskTicketsYes')).toBeNull();
expect(screen.queryByTestId('createZendeskTicketsNo')).toBeNull();
}); });
it('Hides all other fields when enableProctoredExams toggled to false', async () => { it('Hides all other fields when enableProctoredExams toggled to false', async () => {
@@ -163,8 +129,6 @@ describe('ProctoredExamSettings', () => {
expect(screen.queryByText('Allow opting out of proctored exams')).toBeDefined(); expect(screen.queryByText('Allow opting out of proctored exams')).toBeDefined();
expect(screen.queryByDisplayValue('mockproc')).toBeDefined(); expect(screen.queryByDisplayValue('mockproc')).toBeDefined();
expect(screen.queryByTestId('escalationEmail')).toBeDefined(); expect(screen.queryByTestId('escalationEmail')).toBeDefined();
expect(screen.queryByTestId('createZendeskTicketsYes')).toBeDefined();
expect(screen.queryByTestId('createZendeskTicketsNo')).toBeDefined();
let enabledProctoredExamCheck = screen.getAllByLabelText('Proctored exams', { exact: false })[0]; let enabledProctoredExamCheck = screen.getAllByLabelText('Proctored exams', { exact: false })[0];
expect(enabledProctoredExamCheck.checked).toEqual(true); expect(enabledProctoredExamCheck.checked).toEqual(true);
@@ -174,8 +138,6 @@ describe('ProctoredExamSettings', () => {
expect(screen.queryByText('Allow opting out of proctored exams')).toBeNull(); expect(screen.queryByText('Allow opting out of proctored exams')).toBeNull();
expect(screen.queryByDisplayValue('mockproc')).toBeNull(); expect(screen.queryByDisplayValue('mockproc')).toBeNull();
expect(screen.queryByTestId('escalationEmail')).toBeNull(); expect(screen.queryByTestId('escalationEmail')).toBeNull();
expect(screen.queryByTestId('createZendeskTicketsYes')).toBeNull();
expect(screen.queryByTestId('createZendeskTicketsNo')).toBeNull();
}); });
it('Hides unsupported fields when lti provider is selected', async () => { it('Hides unsupported fields when lti provider is selected', async () => {
@@ -185,8 +147,6 @@ describe('ProctoredExamSettings', () => {
const selectElement = screen.getByDisplayValue('mockproc'); const selectElement = screen.getByDisplayValue('mockproc');
fireEvent.change(selectElement, { target: { value: 'test_lti' } }); fireEvent.change(selectElement, { target: { value: 'test_lti' } });
expect(screen.queryByTestId('allowOptingOutRadio')).toBeNull(); expect(screen.queryByTestId('allowOptingOutRadio')).toBeNull();
expect(screen.queryByTestId('createZendeskTicketsYes')).toBeNull();
expect(screen.queryByTestId('createZendeskTicketsNo')).toBeNull();
}); });
}); });
@@ -202,7 +162,6 @@ describe('ProctoredExamSettings', () => {
allow_proctoring_opt_out: false, allow_proctoring_opt_out: false,
proctoring_provider: 'lti_external', proctoring_provider: 'lti_external',
proctoring_escalation_email: 'test@example.com', proctoring_escalation_email: 'test@example.com',
create_zendesk_tickets: true,
}, },
available_proctoring_providers: ['software_secure', 'mockproc', 'lti_external'], available_proctoring_providers: ['software_secure', 'mockproc', 'lti_external'],
requires_escalation_email_providers: ['test_lti'], requires_escalation_email_providers: ['test_lti'],
@@ -382,7 +341,6 @@ describe('ProctoredExamSettings', () => {
allow_proctoring_opt_out: false, allow_proctoring_opt_out: false,
proctoring_provider: 'mockproc', proctoring_provider: 'mockproc',
proctoring_escalation_email: 'test@example.com', proctoring_escalation_email: 'test@example.com',
create_zendesk_tickets: true,
}, },
available_proctoring_providers: ['software_secure', 'mockproc'], available_proctoring_providers: ['software_secure', 'mockproc'],
requires_escalation_email_providers: [], requires_escalation_email_providers: [],
@@ -395,7 +353,6 @@ describe('ProctoredExamSettings', () => {
allow_proctoring_opt_out: false, allow_proctoring_opt_out: false,
proctoring_provider: 'mockproc', proctoring_provider: 'mockproc',
proctoring_escalation_email: 'test@example.com', proctoring_escalation_email: 'test@example.com',
create_zendesk_tickets: true,
}, },
available_proctoring_providers: ['software_secure', 'mockproc'], available_proctoring_providers: ['software_secure', 'mockproc'],
requires_escalation_email_providers: [], requires_escalation_email_providers: [],
@@ -529,18 +486,16 @@ describe('ProctoredExamSettings', () => {
}); });
describe('Toggles field visibility based on user permissions', () => { describe('Toggles field visibility based on user permissions', () => {
it('Hides opting out and zendesk tickets for non edX staff', async () => { it('Hides opting out for non edX staff', async () => {
setupApp(false); setupApp(false);
await act(async () => render(renderComponent(<ProctoredExamSettings {...defaultProps} />))); await act(async () => render(renderComponent(<ProctoredExamSettings {...defaultProps} />)));
expect(screen.queryByTestId('allowOptingOutYes')).toBeNull(); expect(screen.queryByTestId('allowOptingOutYes')).toBeNull();
expect(screen.queryByTestId('createZendeskTicketsYes')).toBeNull();
}); });
it('Shows opting out and zendesk tickets for edX staff', async () => { it('Shows opting out for edX staff', async () => {
setupApp(true); setupApp(true);
await act(async () => render(renderComponent(<ProctoredExamSettings {...defaultProps} />))); await act(async () => render(renderComponent(<ProctoredExamSettings {...defaultProps} />)));
expect(screen.queryByTestId('allowOptingOutYes')).not.toBeNull(); expect(screen.queryByTestId('allowOptingOutYes')).not.toBeNull();
expect(screen.queryByTestId('createZendeskTicketsYes')).not.toBeNull();
}); });
}); });
@@ -618,7 +573,6 @@ describe('ProctoredExamSettings', () => {
allow_proctoring_opt_out: false, allow_proctoring_opt_out: false,
proctoring_provider: 'mockproc', proctoring_provider: 'mockproc',
proctoring_escalation_email: 'test@example.com', proctoring_escalation_email: 'test@example.com',
create_zendesk_tickets: true,
}, },
available_proctoring_providers: ['software_secure', 'mockproc', 'lti_external'], available_proctoring_providers: ['software_secure', 'mockproc', 'lti_external'],
requires_escalation_email_providers: ['test_lti'], requires_escalation_email_providers: ['test_lti'],
@@ -642,7 +596,6 @@ describe('ProctoredExamSettings', () => {
allow_proctoring_opt_out: false, allow_proctoring_opt_out: false,
proctoring_provider: 'lti_external', proctoring_provider: 'lti_external',
proctoring_escalation_email: 'test_lti@example.com', proctoring_escalation_email: 'test_lti@example.com',
create_zendesk_tickets: true,
}, },
}); });
expect(axiosMock.history.patch.length).toBe(1); expect(axiosMock.history.patch.length).toBe(1);
@@ -674,7 +627,6 @@ describe('ProctoredExamSettings', () => {
enable_proctored_exams: true, enable_proctored_exams: true,
allow_proctoring_opt_out: false, allow_proctoring_opt_out: false,
proctoring_provider: 'mockproc', proctoring_provider: 'mockproc',
create_zendesk_tickets: true,
}, },
}); });
@@ -716,7 +668,6 @@ describe('ProctoredExamSettings', () => {
allow_proctoring_opt_out: false, allow_proctoring_opt_out: false,
proctoring_provider: 'lti_external', proctoring_provider: 'lti_external',
proctoring_escalation_email: 'test_lti@example.com', proctoring_escalation_email: 'test_lti@example.com',
create_zendesk_tickets: true,
}, },
}); });
@@ -746,7 +697,6 @@ describe('ProctoredExamSettings', () => {
enable_proctored_exams: true, enable_proctored_exams: true,
allow_proctoring_opt_out: false, allow_proctoring_opt_out: false,
proctoring_provider: 'mockproc', proctoring_provider: 'mockproc',
create_zendesk_tickets: true,
}, },
}); });
@@ -768,7 +718,6 @@ describe('ProctoredExamSettings', () => {
allow_proctoring_opt_out: false, allow_proctoring_opt_out: false,
proctoring_provider: 'mockproc', proctoring_provider: 'mockproc',
proctoring_escalation_email: 'test@example.com', proctoring_escalation_email: 'test@example.com',
create_zendesk_tickets: true,
}, },
available_proctoring_providers: ['software_secure', 'mockproc'], available_proctoring_providers: ['software_secure', 'mockproc'],
requires_escalation_email_providers: [], requires_escalation_email_providers: [],
@@ -787,7 +736,6 @@ describe('ProctoredExamSettings', () => {
enable_proctored_exams: true, enable_proctored_exams: true,
allow_proctoring_opt_out: false, allow_proctoring_opt_out: false,
proctoring_provider: 'mockproc', proctoring_provider: 'mockproc',
create_zendesk_tickets: true,
}, },
}); });
@@ -887,26 +835,5 @@ describe('ProctoredExamSettings', () => {
expect(document.activeElement).toEqual(successAlert); expect(document.activeElement).toEqual(successAlert);
}); });
}); });
it('Include Zendesk ticket in post request if user is not an admin', async () => {
// use non-admin user for test
const isAdmin = false;
setupApp(isAdmin);
await act(async () => render(renderComponent(<ProctoredExamSettings {...defaultProps} />)));
// Make a change to the proctoring provider
const selectElement = screen.getByDisplayValue('mockproc');
fireEvent.change(selectElement, { target: { value: 'software_secure' } });
const submitButton = screen.getByTestId('submissionButton');
fireEvent.click(submitButton);
expect(axiosMock.history.post.length).toBe(1);
expect(JSON.parse(axiosMock.history.post[0].data)).toEqual({
proctored_exam_settings: {
enable_proctored_exams: true,
proctoring_provider: 'software_secure',
create_zendesk_tickets: true,
},
});
});
}); });
}); });

View File

@@ -81,11 +81,6 @@ const messages = defineMessages({
defaultMessage: 'Allow learners to opt out of proctoring on proctored exams', defaultMessage: 'Allow learners to opt out of proctoring on proctored exams',
description: 'Label for radio selection allowing proctored exam opt out', description: 'Label for radio selection allowing proctored exam opt out',
}, },
'authoring.proctoring.createzendesk.label': {
id: 'authoring.proctoring.createzendesk.label',
defaultMessage: 'Create Zendesk tickets for suspicious attempts',
description: 'Label for Zendesk ticket creation radio select.',
},
'authoring.proctoring.error.single': { 'authoring.proctoring.error.single': {
id: 'authoring.proctoring.error.single', id: 'authoring.proctoring.error.single',
defaultMessage: 'There is 1 error in this form.', defaultMessage: 'There is 1 error in this form.',

View File

@@ -62,7 +62,6 @@ module.exports = {
highlightsPreviewOnly: false, highlightsPreviewOnly: false,
highlightsDocUrl: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_course_highlight_emails.html', highlightsDocUrl: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_course_highlight_emails.html',
enableProctoredExams: true, enableProctoredExams: true,
createZendeskTickets: true,
enableTimedExams: true, enableTimedExams: true,
childInfo: { childInfo: {
category: 'chapter', category: 'chapter',

View File

@@ -1405,7 +1405,6 @@ module.exports = {
highlights_preview_only: false, highlights_preview_only: false,
highlights_doc_url: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_course_highlight_emails.html', highlights_doc_url: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_course_highlight_emails.html',
enable_proctored_exams: false, enable_proctored_exams: false,
create_zendesk_tickets: true,
enable_timed_exams: true, enable_timed_exams: true,
ancestor_has_staff_lock: false, ancestor_has_staff_lock: false,
user_partition_info: { user_partition_info: {