MST-334 Implement the settings post back functionality (#16)
* MST-334 Implement the settings post back functionality * fix * take in feedback * feedback
This commit is contained in:
@@ -8,11 +8,25 @@ ensureConfig([
|
||||
const studioBaseUrl = getConfig().STUDIO_BASE_URL;
|
||||
|
||||
class StudioApiService {
|
||||
static getProctoredExamSettingsUrl(courseID) {
|
||||
return `${studioBaseUrl}/api/contentstore/v1/proctored_exam_settings/${courseID}`;
|
||||
}
|
||||
|
||||
static getProctoredExamSettingsData(courseID) {
|
||||
const apiClient = getAuthenticatedHttpClient();
|
||||
const url = `${studioBaseUrl}/api/contentstore/v1/proctored_exam_settings/${courseID}`;
|
||||
const url = StudioApiService.getProctoredExamSettingsUrl(courseID);
|
||||
return apiClient.get(url);
|
||||
}
|
||||
|
||||
static saveProctoredExamSettingsData(courseID, dataToSave) {
|
||||
const apiClient = getAuthenticatedHttpClient();
|
||||
const url = StudioApiService.getProctoredExamSettingsUrl(courseID);
|
||||
return apiClient.post(url, dataToSave);
|
||||
}
|
||||
|
||||
static getStudioCourseRunUrl(courseID) {
|
||||
return `${studioBaseUrl}/course/${courseID}`;
|
||||
}
|
||||
}
|
||||
|
||||
export default StudioApiService;
|
||||
|
||||
@@ -24,6 +24,8 @@ function ExamSettings(props) {
|
||||
const [createZendeskTickets, setCreateZendeskTickets] = useState(false);
|
||||
const [proctortrackEscalationEmailError, setProctortrackEscalationEmailError] = useState('');
|
||||
const [courseStartDate, setCourseStartDate] = useState('');
|
||||
const [saveSuccess, setSaveSuccess] = useState(false);
|
||||
const [saveError, setSaveError] = useState(false);
|
||||
|
||||
function onEnableProctoredExamsChange(event) {
|
||||
setEnableProctoredExams(event.target.checked);
|
||||
@@ -52,13 +54,32 @@ function ExamSettings(props) {
|
||||
setProctortrackEscalationEmail(event.target.value);
|
||||
}
|
||||
|
||||
function postSettingsBackToServer() {
|
||||
const dataToPostBack = {
|
||||
proctored_exam_settings: {
|
||||
enable_proctored_exams: enableProctoredExams,
|
||||
allow_proctoring_opt_out: allowOptingOut,
|
||||
proctoring_provider: proctoringProvider,
|
||||
proctoring_escalation_email: proctortrackEscalationEmail,
|
||||
create_zendesk_tickets: createZendeskTickets,
|
||||
},
|
||||
};
|
||||
StudioApiService.saveProctoredExamSettingsData(props.courseId, dataToPostBack).then(() => {
|
||||
setSaveSuccess(true);
|
||||
setSaveError(false);
|
||||
}).catch(() => {
|
||||
setSaveSuccess(false);
|
||||
setSaveError(true);
|
||||
});
|
||||
}
|
||||
|
||||
function onButtonClick() {
|
||||
if (proctoringProvider === 'proctortrack' && !EmailValidator.validate(proctortrackEscalationEmail)) {
|
||||
setProctortrackEscalationEmailError('A valid escalation email must be provided if '
|
||||
+ 'Proctortrack is the selected provider.');
|
||||
} else {
|
||||
setProctortrackEscalationEmailError('');
|
||||
// TODO: implement POST
|
||||
postSettingsBackToServer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +186,7 @@ function ExamSettings(props) {
|
||||
<Form.Label>Proctortrack Escalation Email</Form.Label>
|
||||
<Form.Control
|
||||
type="email"
|
||||
data-test-id="escalationEmail"
|
||||
onChange={onProctortrackEscalationEmailChange}
|
||||
value={proctortrackEscalationEmail}
|
||||
isInvalid={!!proctortrackEscalationEmailError}
|
||||
@@ -208,6 +230,7 @@ function ExamSettings(props) {
|
||||
</fieldset>
|
||||
<Button
|
||||
className="btn-primary mb-3"
|
||||
data-test-id="submissionButton"
|
||||
onClick={onButtonClick}
|
||||
>
|
||||
Submit
|
||||
@@ -237,7 +260,8 @@ function ExamSettings(props) {
|
||||
<Alert variant="danger" data-test-id="connectionError">
|
||||
We encountered a technical error when loading this page.
|
||||
This might be a temporary issue, so please try again in a few minutes.
|
||||
If the problem persists, please go to <a href="https://support.edx.org/hc/en-us">edX Support Page</a> for help.
|
||||
If the problem persists,
|
||||
please go to <Alert.Link href="https://support.edx.org/hc/en-us">edX Support Page</Alert.Link> for help.
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
@@ -251,6 +275,37 @@ function ExamSettings(props) {
|
||||
);
|
||||
}
|
||||
|
||||
function renderSaveSuccess() {
|
||||
const studioCourseRunURL = StudioApiService.getStudioCourseRunUrl(props.courseId);
|
||||
return (
|
||||
<Alert
|
||||
variant="success"
|
||||
dismissible
|
||||
data-test-id="saveSuccess"
|
||||
onClose={() => setSaveSuccess(false)}
|
||||
>
|
||||
Proctored exam settings saved successfully.
|
||||
You can go back to your course in Studio <Alert.Link href={studioCourseRunURL}>here</Alert.Link>.
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
function renderSaveError() {
|
||||
return (
|
||||
<Alert
|
||||
variant="danger"
|
||||
dismissible
|
||||
data-test-id="saveError"
|
||||
onClose={() => setSaveError(false)}
|
||||
>
|
||||
We encountered a technical error while trying to save proctored exam settings.
|
||||
This might be a temporary issue, so please try again in a few minutes.
|
||||
If the problem persists,
|
||||
please go to <Alert.Link href="https://support.edx.org/hc/en-us">edX Support Page</Alert.Link> for help.
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
StudioApiService.getProctoredExamSettingsData(props.courseId)
|
||||
@@ -288,6 +343,8 @@ function ExamSettings(props) {
|
||||
</h2>
|
||||
<div>
|
||||
{loading ? renderLoading() : null}
|
||||
{saveSuccess ? renderSaveSuccess() : null}
|
||||
{saveError ? renderSaveError() : null}
|
||||
{loaded ? renderContent() : null}
|
||||
{loadingConnectionError ? renderConnectionError() : null}
|
||||
{loadingPermissionError ? renderPermissionError() : null}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
} from '@testing-library/react';
|
||||
import * as auth from '@edx/frontend-platform/auth';
|
||||
import ProctoredExamSettings from './ProctoredExamSettings';
|
||||
import StudioApiService from '../data/services/StudioApiService';
|
||||
|
||||
const defaultProps = {
|
||||
courseId: 'course-v1%3AedX%2BDemoX%2BDemo_Course',
|
||||
@@ -123,3 +124,85 @@ describe('ProctoredExamSettings connection states tests', () => {
|
||||
cleanup();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ProctoredExamSettings save settings tests', () => {
|
||||
const mockGetData = {
|
||||
data: {
|
||||
proctored_exam_settings: {
|
||||
enable_proctored_exams: true,
|
||||
allow_proctoring_opt_out: false,
|
||||
proctoring_provider: 'mockproc',
|
||||
proctoring_escalation_email: 'test@example.com',
|
||||
create_zendesk_tickets: true,
|
||||
},
|
||||
available_proctoring_providers: ['software_secure', 'proctortrack', 'mockproc'],
|
||||
},
|
||||
};
|
||||
|
||||
function mockAPI(getData, postResult) {
|
||||
const mockClientGet = jest.fn(async () => (getData));
|
||||
const mockClientPost = postResult ? jest.fn(async () => (postResult)) : jest.fn(async () => { throw new Error(); });
|
||||
auth.getAuthenticatedHttpClient = jest.fn(() => ({
|
||||
get: mockClientGet,
|
||||
post: mockClientPost,
|
||||
}));
|
||||
auth.getAuthenticatedUser = jest.fn(() => ({ userId: 3 }));
|
||||
return { mockClientGet, mockClientPost };
|
||||
}
|
||||
|
||||
it('Makes API call successfully', async () => {
|
||||
const mockedFunctions = mockAPI(mockGetData, { data: 'success' });
|
||||
await act(async () => render(<ProctoredExamSettings {...defaultProps} />));
|
||||
// Make a change to the provider to proctortrack and set the email
|
||||
const selectElement = screen.getByDisplayValue('mockproc');
|
||||
await act(async () => {
|
||||
fireEvent.change(selectElement, { target: { value: 'proctortrack' } });
|
||||
});
|
||||
const escalationEmail = screen.getByTestId('escalationEmail');
|
||||
expect(escalationEmail.value).toEqual('test@example.com');
|
||||
await act(async () => {
|
||||
fireEvent.change(escalationEmail, { target: { value: 'proctortrack@example.com' } });
|
||||
});
|
||||
expect(escalationEmail.value).toEqual('proctortrack@example.com');
|
||||
const submitButton = screen.getByTestId('submissionButton');
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
expect(mockedFunctions.mockClientPost).toHaveBeenCalled();
|
||||
expect(mockedFunctions.mockClientPost).toHaveBeenCalledWith(
|
||||
StudioApiService.getProctoredExamSettingsUrl(defaultProps.courseId),
|
||||
{
|
||||
proctored_exam_settings: {
|
||||
enable_proctored_exams: true,
|
||||
allow_proctoring_opt_out: false,
|
||||
proctoring_provider: 'proctortrack',
|
||||
proctoring_escalation_email: 'proctortrack@example.com',
|
||||
create_zendesk_tickets: false,
|
||||
},
|
||||
},
|
||||
);
|
||||
const errorAlert = screen.getByTestId('saveSuccess');
|
||||
expect(errorAlert.textContent).toEqual(
|
||||
expect.stringContaining('Proctored exam settings saved successfully.'),
|
||||
);
|
||||
});
|
||||
|
||||
it('Makes API call generated error', async () => {
|
||||
const mockedFunctions = mockAPI(mockGetData, false);
|
||||
await act(async () => render(<ProctoredExamSettings {...defaultProps} />));
|
||||
// Make a change to the provider to proctortrack and set the email
|
||||
const submitButton = screen.getByTestId('submissionButton');
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
expect(mockedFunctions.mockClientPost).toHaveBeenCalled();
|
||||
const errorAlert = screen.getByTestId('saveError');
|
||||
expect(errorAlert.textContent).toEqual(
|
||||
expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user