feat: fetch exam access token (#1083)
* feat: fetch exam access token * build: update frontend lib special exams version
This commit is contained in:
14
package-lock.json
generated
14
package-lock.json
generated
@@ -12,7 +12,7 @@
|
||||
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
|
||||
"@edx/frontend-component-footer": "11.6.3",
|
||||
"@edx/frontend-component-header": "3.6.4",
|
||||
"@edx/frontend-lib-special-exams": "2.10.0",
|
||||
"@edx/frontend-lib-special-exams": "2.12.0",
|
||||
"@edx/frontend-platform": "3.4.1",
|
||||
"@edx/paragon": "20.28.4",
|
||||
"@fortawesome/fontawesome-svg-core": "1.3.0",
|
||||
@@ -3272,9 +3272,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-lib-special-exams": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-2.10.0.tgz",
|
||||
"integrity": "sha512-YHTYlmHIVM6KRTklbM3ERHmu1adRoMIpYTWwn/KhEQ/a6YQfRM8SJt8uawj4ceSRftJ7E9QWMGy/21KfnIYOxg==",
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-2.12.0.tgz",
|
||||
"integrity": "sha512-VltUW9bZ+Ha9Gw4xplEJjfZi4zjUJJWsAFOVeX8n+ZJLko4qDU46tL7EOLAJyMaiAtjvHent8MNIfcGBbqgcFQ==",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.34",
|
||||
"@fortawesome/free-brands-svg-icons": "5.11.2",
|
||||
@@ -28482,9 +28482,9 @@
|
||||
}
|
||||
},
|
||||
"@edx/frontend-lib-special-exams": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-2.10.0.tgz",
|
||||
"integrity": "sha512-YHTYlmHIVM6KRTklbM3ERHmu1adRoMIpYTWwn/KhEQ/a6YQfRM8SJt8uawj4ceSRftJ7E9QWMGy/21KfnIYOxg==",
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-2.12.0.tgz",
|
||||
"integrity": "sha512-VltUW9bZ+Ha9Gw4xplEJjfZi4zjUJJWsAFOVeX8n+ZJLko4qDU46tL7EOLAJyMaiAtjvHent8MNIfcGBbqgcFQ==",
|
||||
"requires": {
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.34",
|
||||
"@fortawesome/free-brands-svg-icons": "5.11.2",
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
|
||||
"@edx/frontend-component-footer": "11.6.3",
|
||||
"@edx/frontend-component-header": "3.6.4",
|
||||
"@edx/frontend-lib-special-exams": "2.10.0",
|
||||
"@edx/frontend-lib-special-exams": "2.12.0",
|
||||
"@edx/frontend-platform": "3.4.1",
|
||||
"@edx/paragon": "20.28.4",
|
||||
"@fortawesome/fontawesome-svg-core": "1.3.0",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
import { AppContext, ErrorPage } from '@edx/frontend-platform/react';
|
||||
import { getExamAccess, fetchExamAccess, isExam } from '@edx/frontend-lib-special-exams';
|
||||
import { Modal } from '@edx/paragon';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, {
|
||||
@@ -65,6 +67,16 @@ function useLoadBearingHook(id) {
|
||||
}, [id]);
|
||||
}
|
||||
|
||||
function addExamAccessToIframeUrl(accessToken, iframeUrl) {
|
||||
let url = iframeUrl;
|
||||
if (isExam()) {
|
||||
if (accessToken) {
|
||||
url += `&exam_access=${accessToken}`;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
export function sendUrlHashToFrame(frame) {
|
||||
const { hash } = window.location;
|
||||
if (hash) {
|
||||
@@ -83,6 +95,7 @@ const Unit = ({
|
||||
}) => {
|
||||
const { authenticatedUser } = useContext(AppContext);
|
||||
const view = authenticatedUser ? 'student_view' : 'public_view';
|
||||
|
||||
let iframeUrl = `${getConfig().LMS_BASE_URL}/xblock/${id}?show_title=0&show_bookmark_button=0&recheck_access=1&view=${view}`;
|
||||
if (format) {
|
||||
iframeUrl += `&format=${format}`;
|
||||
@@ -93,6 +106,8 @@ const Unit = ({
|
||||
const [showError, setShowError] = useState(false);
|
||||
const [modalOptions, setModalOptions] = useState({ open: false });
|
||||
const [shouldDisplayHonorCode, setShouldDisplayHonorCode] = useState(false);
|
||||
const [examAccessToken, setExamAccessToken] = useState('');
|
||||
const [blockExamAccess, setBlockExamAccess] = useState(isExam());
|
||||
|
||||
const unit = useModel('units', id);
|
||||
const course = useModel('coursewareMeta', courseId);
|
||||
@@ -140,6 +155,16 @@ const Unit = ({
|
||||
sendUrlHashToFrame(document.getElementById('unit-iframe'));
|
||||
}, [id, setIframeHeight, hasLoaded, iframeHeight, setHasLoaded, onLoaded]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isExam()) {
|
||||
fetchExamAccess().finally(() => {
|
||||
const examAccess = getExamAccess();
|
||||
setExamAccessToken(examAccess);
|
||||
setBlockExamAccess(false);
|
||||
}).catch((error) => logError(error));
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<div className="unit">
|
||||
<h1 className="mb-0 h3">{unit.title}</h1>
|
||||
@@ -175,7 +200,7 @@ const Unit = ({
|
||||
<HonorCode courseId={courseId} />
|
||||
</Suspense>
|
||||
)}
|
||||
{!shouldDisplayHonorCode && !hasLoaded && !showError && (
|
||||
{(!shouldDisplayHonorCode || blockExamAccess) && !hasLoaded && !showError && (
|
||||
<PageLoading
|
||||
srMessage={intl.formatMessage(messages.loadingSequence)}
|
||||
/>
|
||||
@@ -208,12 +233,12 @@ const Unit = ({
|
||||
dialogClassName="modal-lti"
|
||||
/>
|
||||
)}
|
||||
{!shouldDisplayHonorCode && (
|
||||
{!shouldDisplayHonorCode && !blockExamAccess && (
|
||||
<div className="unit-iframe-wrapper">
|
||||
<iframe
|
||||
id="unit-iframe"
|
||||
title={unit.title}
|
||||
src={iframeUrl}
|
||||
src={addExamAccessToIframeUrl(examAccessToken, iframeUrl)}
|
||||
allow={IFRAME_FEATURE_POLICY}
|
||||
allowFullScreen
|
||||
height={iframeHeight}
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
import React from 'react';
|
||||
import { Factory } from 'rosie';
|
||||
import { fetchExamAccess, getExamAccess, isExam } from '@edx/frontend-lib-special-exams';
|
||||
import {
|
||||
initializeTestStore, loadUnit, messageEvent, render, screen, waitFor,
|
||||
} from '../../../setupTest';
|
||||
import Unit, { sendUrlHashToFrame } from './Unit';
|
||||
|
||||
const originalIsExam = jest.requireActual('@edx/frontend-lib-special-exams').isExam();
|
||||
const originalFetchExamAccess = jest.requireActual('@edx/frontend-lib-special-exams').fetchExamAccess();
|
||||
const originalGetExamAccess = jest.requireActual('@edx/frontend-lib-special-exams').getExamAccess();
|
||||
jest.mock('@edx/frontend-lib-special-exams', () => ({
|
||||
...jest.requireActual('@edx/frontend-lib-special-exams'),
|
||||
isExam: jest.fn(),
|
||||
fetchExamAccess: jest.fn(),
|
||||
getExamAccess: jest.fn(),
|
||||
}));
|
||||
isExam.mockImplementation(() => originalIsExam).mockReturnValue(false);
|
||||
fetchExamAccess.mockImplementation(() => originalFetchExamAccess).mockResolvedValue();
|
||||
const examAccessToken = 'EXAMACCESSTOKEN';
|
||||
getExamAccess.mockImplementation(() => originalGetExamAccess).mockReturnValue(examAccessToken);
|
||||
|
||||
describe('Unit', () => {
|
||||
let mockData;
|
||||
const courseMetadata = Factory.build(
|
||||
@@ -174,4 +189,31 @@ describe('Unit', () => {
|
||||
expect(React.useEffect).toHaveBeenCalled();
|
||||
expect(mockHashCheck).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('updates url if exam and exam access granted', async () => {
|
||||
isExam.mockReturnValue(true);
|
||||
|
||||
render(<Unit {...mockData} />);
|
||||
expect(isExam).toHaveBeenCalled();
|
||||
await waitFor(() => expect(screen.getByTitle(unit.display_name)).toHaveAttribute('src', `http://localhost:18000/xblock/${mockData.id}?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view&format=${mockData.format}&exam_access=${examAccessToken}`));
|
||||
});
|
||||
|
||||
it('does not update url if exam and exam access not granted', async () => {
|
||||
isExam.mockReturnValue(true);
|
||||
fetchExamAccess.mockRejectedValue();
|
||||
getExamAccess.mockReturnValue('');
|
||||
|
||||
render(<Unit {...mockData} />);
|
||||
expect(isExam).toHaveBeenCalled();
|
||||
await waitFor(() => expect(screen.getByTitle(unit.display_name)).toHaveAttribute('src', `http://localhost:18000/xblock/${mockData.id}?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view&format=${mockData.format}`));
|
||||
});
|
||||
|
||||
it('does not update url if not exam', async () => {
|
||||
isExam.mockReturnValue(false);
|
||||
|
||||
render(<Unit {...mockData} />);
|
||||
expect(isExam).toHaveBeenCalled();
|
||||
const renderedUnit = screen.getByTitle(unit.display_name);
|
||||
expect(renderedUnit).toHaveAttribute('src', `http://localhost:18000/xblock/${mockData.id}?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view&format=${mockData.format}`);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user