Skip to content

Commit

Permalink
Improve Exam Start Failure (#54)
Browse files Browse the repository at this point in the history
* feat: start exam action fail after 2 seconds

* feat: pollAttempt log error

* feat: loading spinner while waiting for exam start

* style: use const for timeout
  • Loading branch information
zacharis278 authored Dec 22, 2021
1 parent 3019a63 commit 0c12634
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/data/redux.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ describe('Data layer integration tests', () => {

await executeThunk(thunks.getExamAttemptsData(courseId, contentId), store.dispatch);
await executeThunk(thunks.startProctoredExam(), store.dispatch, store.getState);
expect(loggingService.logInfo).toHaveBeenCalledWith(
expect(loggingService.logError).toHaveBeenCalledWith(
Error('test error'), {
attemptId: createdWorkerAttempt.attempt_id,
courseId: createdWorkerAttempt.course_id,
Expand Down
24 changes: 18 additions & 6 deletions src/data/thunks.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { logError, logInfo } from '@edx/frontend-platform/logging';
import { logError } from '@edx/frontend-platform/logging';
import {
fetchExamAttemptsData,
createExamAttempt,
Expand Down Expand Up @@ -35,6 +35,8 @@ function handleAPIError(error, dispatch) {
dispatch(setApiError({ errorMsg: message || detail }));
}

const EXAM_START_TIMEOUT_MILLISECONDS = 5000;

/**
* Fetch attempt data and update exam state after performing another action if it is provided.
* It is assumed that action somehow modifies attempt in the backend, that's why the state needs
Expand Down Expand Up @@ -146,13 +148,15 @@ export function startProctoredExam() {
const useWorker = window.Worker && workerUrl;

if (useWorker) {
workerPromiseForEventNames(actionToMessageTypesMap.start, exam.attempt.desktop_application_js_url)()
.then(() => updateAttemptAfter(
exam.course_id, exam.content_id, continueAttempt(attempt.attempt_id),
)(dispatch))
const startExamTimeoutMilliseconds = EXAM_START_TIMEOUT_MILLISECONDS;
workerPromiseForEventNames(actionToMessageTypesMap.start, exam.attempt.desktop_application_js_url)(
startExamTimeoutMilliseconds,
).then(() => updateAttemptAfter(
exam.course_id, exam.content_id, continueAttempt(attempt.attempt_id),
)(dispatch))
.catch(error => {
if (error) {
logInfo(
logError(
error,
{
attemptId: attempt.attempt_id,
Expand Down Expand Up @@ -381,6 +385,14 @@ export function pingAttempt(timeoutInSeconds, workerUrl) {
.catch(async (error) => {
const { exam, activeAttempt } = getState().examState;
const message = error ? error.message : 'Worker failed to respond.';
logError(
message,
{
attemptId: activeAttempt.attempt_id,
courseId: activeAttempt.course_id,
examId: activeAttempt.exam.id,
},
);
await updateAttemptAfter(
exam.course_id, exam.content_id, endExamWithFailure(activeAttempt.attempt_id, message),
)(dispatch);
Expand Down
60 changes: 60 additions & 0 deletions src/instructions/Instructions.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -891,4 +891,64 @@ describe('SequenceExamWrapper', () => {

expect(screen.getByTestId('unknown-status-error')).toBeInTheDocument();
});

it('Shows ready to start page when proctored exam is in ready_to_start status', () => {
store.getState = () => ({
examState: Factory.build('examState', {
proctoringSettings: Factory.build('proctoringSettings', {
platform_name: 'Your Platform',
}),
activeAttempt: {},
exam: Factory.build('exam', {
is_proctored: true,
type: ExamType.PROCTORED,
attempt: Factory.build('attempt', {
attempt_status: ExamStatus.READY_TO_START,
}),
}),
}),
});

render(
<ExamStateProvider>
<Instructions>
<div>Sequence</div>
</Instructions>
</ExamStateProvider>,
{ store },
);

expect(screen.getByText('You must adhere to the following rules while you complete this exam.')).toBeInTheDocument();
});

it('Shows loading spinner while waiting to start exam', () => {
store.getState = () => ({
examState: Factory.build('examState', {
proctoringSettings: Factory.build('proctoringSettings', {
platform_name: 'Your Platform',
}),
activeAttempt: {},
exam: Factory.build('exam', {
is_proctored: true,
type: ExamType.PROCTORED,
attempt: Factory.build('attempt', {
attempt_status: ExamStatus.READY_TO_START,
}),
}),
startProctoredExam: jest.fn(),
}),
});

render(
<ExamStateProvider>
<Instructions>
<div>Sequence</div>
</Instructions>
</ExamStateProvider>,
{ store },
);

fireEvent.click(screen.getByTestId('start-exam-button'));
expect(screen.getByTestId('exam-loading-spinner')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useContext, useEffect } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { Button, Container } from '@edx/paragon';
import { Button, Container, Spinner } from '@edx/paragon';
import ExamStateContext from '../../context';
import Footer from './Footer';

Expand All @@ -16,11 +16,17 @@ const ReadyToStartProctoredExamInstructions = () => {
const { total_time: examDuration } = attempt;
const { link_urls: linkUrls, platform_name: platformName } = proctoringSettings;
const rulesUrl = linkUrls && linkUrls.online_proctoring_rules;
const [beginExamClicked, setBeginExamClicked] = useState(false);

useEffect(() => {
getExamReviewPolicy();
}, []);

const handleStart = () => {
setBeginExamClicked(true);
startProctoredExam();
};

return (
<div>
<Container className="border py-5 mb-4">
Expand Down Expand Up @@ -113,8 +119,10 @@ const ReadyToStartProctoredExamInstructions = () => {
<Button
data-testid="start-exam-button"
variant="primary"
onClick={startProctoredExam}
onClick={handleStart}
disabled={beginExamClicked}
>
{ beginExamClicked && <Spinner data-testid="exam-loading-spinner" animation="border" /> }
<FormattedMessage
id="exam.startExamInstructions.startExamButtonText"
defaultMessage="Start exam"
Expand Down

0 comments on commit 0c12634

Please sign in to comment.