Skip to content

Commit

Permalink
fix: pollAttempt call on download page
Browse files Browse the repository at this point in the history
  • Loading branch information
zacharis278 committed Aug 4, 2023
1 parent fbe0094 commit 7cb040b
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 9 deletions.
22 changes: 17 additions & 5 deletions src/data/api.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { logError } from '@edx/frontend-platform/logging';
import { ExamAction } from '../constants';
import { generateHumanizedTime } from '../helpers';

const BASE_API_URL = '/api/edx_proctoring/v1/proctored_exam/attempt';

async function fetchActiveAttempt() {
// fetch 'active' (timer is running) attempt if it exists
const activeAttemptUrl = new URL(`${getConfig().EXAMS_BASE_URL}/api/v1/exams/attempt/latest`);
const activeAttemptResponse = await getAuthenticatedHttpClient().get(activeAttemptUrl.href);
return activeAttemptResponse.data;
}

async function fetchLatestExamAttempt(sequenceId) {
// fetch lastest attempt for a specific exam
const attemptUrl = new URL(`${getConfig().EXAMS_BASE_URL}/api/v1/exams/attempt/latest`);
attemptUrl.searchParams.append('content_id', sequenceId);
const response = await getAuthenticatedHttpClient().get(attemptUrl.href);
return response.data;
}

export async function fetchExamAttemptsData(courseId, sequenceId) {
let data;
if (!getConfig().EXAMS_BASE_URL) {
Expand Down Expand Up @@ -56,22 +66,24 @@ export async function fetchLatestAttempt(courseId) {
return data;
}

export async function pollExamAttempt(url) {
export async function pollExamAttempt(pollUrl, sequenceId) {
let data;
if (!getConfig().EXAMS_BASE_URL) {
if (pollUrl) {
const edxProctoringURL = new URL(
`${getConfig().LMS_BASE_URL}${url}`,
`${getConfig().LMS_BASE_URL}${pollUrl}`,
);
const urlResponse = await getAuthenticatedHttpClient().get(edxProctoringURL.href);
data = urlResponse.data;
} else {
data = await fetchActiveAttempt();
} else if (getConfig().EXAMS_BASE_URL) {
data = await fetchLatestExamAttempt(sequenceId);

// Update dictionaries returned by edx-exams to have correct status key for legacy compatibility
if (data.attempt_status) {
data.status = data.attempt_status;
delete data.attempt_status;
}
} else {
logError('pollExamAttempt called but no pollUrl is set');
}
return data;
}
Expand Down
30 changes: 30 additions & 0 deletions src/data/redux.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import MockAdapter from 'axios-mock-adapter';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { getConfig, mergeConfig } from '@edx/frontend-platform';

import * as api from './api';
import * as thunks from './thunks';

import executeThunk from '../utils';
Expand Down Expand Up @@ -958,6 +959,35 @@ describe('Data layer integration tests', () => {
const state = store.getState();
expect(state.examState.activeAttempt).toMatchSnapshot();
});

describe('pollAttempt api called directly', () => {
// in the download view we call this function directly withough invoking the wrapping thunk
it('should call pollUrl if one is provided', async () => {
const pollExamAttemptUrl = `${getConfig().LMS_BASE_URL}${attempt.exam_started_poll_url}`;
axiosMock.onGet(pollExamAttemptUrl).reply(200, {
time_remaining_seconds: 1739.9,
accessibility_time_string: 'you have 29 minutes remaining',
attempt_status: ExamStatus.STARTED,
});
await api.pollExamAttempt(attempt.exam_started_poll_url);
expect(axiosMock.history.get[0].url).toEqual(pollExamAttemptUrl);
});
it('should call the latest attempt for a sequence if a sequence id is provided instead of a pollUrl', async () => {
const sequenceId = 'block-v1:edX+Test+123';
const expectedUrl = `${getConfig().EXAMS_BASE_URL}/api/v1/exams/attempt/latest?content_id=${encodeURIComponent(sequenceId)}`;
axiosMock.onGet(expectedUrl).reply(200, {
time_remaining_seconds: 1739.9,
status: ExamStatus.STARTED,
});
await api.pollExamAttempt(null, sequenceId);
expect(axiosMock.history.get[0].url).toEqual(expectedUrl);
});
test.only('pollUrl is required if edx-exams in not enabled, an error should be logged', async () => {
mergeConfig({ EXAMS_BASE_URL: null });
api.pollExamAttempt(null, null);
expect(loggingService.logError).toHaveBeenCalled();
});
});
});

describe('Test pingAttempt', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/data/thunks.js
Original file line number Diff line number Diff line change
Expand Up @@ -475,9 +475,9 @@ export function pingAttempt(timeoutInSeconds, workerUrl) {
},
);

// eslint-disable-next-line function-paren-newline
await updateAttemptAfter(

Check failure on line 478 in src/data/thunks.js

View workflow job for this annotation

GitHub Actions / tests

Unexpected newline after '('

Check failure on line 478 in src/data/thunks.js

View workflow job for this annotation

GitHub Actions / tests

Unexpected newline after '('
exam.course_id, exam.content_id, endExamWithFailure(activeAttempt.attempt_id, message))(dispatch);
exam.course_id, exam.content_id, endExamWithFailure(activeAttempt.attempt_id, message),
)(dispatch);

Check failure on line 480 in src/data/thunks.js

View workflow job for this annotation

GitHub Actions / tests

Unexpected newline before ')'

Check failure on line 480 in src/data/thunks.js

View workflow job for this annotation

GitHub Actions / tests

Unexpected newline before ')'
});
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const DownloadSoftwareProctoredExamInstructions = ({ intl, skipProctoredExam })
? `${getConfig().EXAMS_BASE_URL}/lti/start_proctoring/${attemptId}` : downloadUrl;

const handleDownloadClick = () => {
pollExamAttempt(`${pollUrl}?sourceid=instructions`)
pollExamAttempt(pollUrl, sequenceId)
.then((data) => {
if (data.status === ExamStatus.READY_TO_START) {
setSystemCheckStatus('success');
Expand All @@ -66,7 +66,7 @@ const DownloadSoftwareProctoredExamInstructions = ({ intl, skipProctoredExam })
};

const handleStartExamClick = () => {
pollExamAttempt(`${pollUrl}?sourceid=instructions`)
pollExamAttempt(pollUrl, sequenceId)
.then((data) => (
data.status === ExamStatus.READY_TO_START
? getExamAttemptsData(courseId, sequenceId)
Expand Down

0 comments on commit 7cb040b

Please sign in to comment.