feat: whole course translations (#1330)
* feat: add language selection chore: update tests so we have less error message test: update test * test: update tests * chore: remove duplicate translation * chore: lint for console * chore: remove comments * chore: make sure the affect url frame refresh after the language selection change * chore: add whole_course_translation and language to courseware meta (#1305) * feat: Add feedback widget UI mock Add unit tests Fix snapshot Clean Sequence component logEvent calls Clean unit test Put feedback widget behind whole course translation flag Fix useFeedbackWidget test * chore: add src and dest translation * feat: first iteration of plugin translation chore: update plugin instruction * feat: Connect FeedbackWidget with backend services (#1325) Connect FeedbackWidget with backend services Move feedback widget to unit translation plugin * feat: Add authentication to WCT feedback endpoints (#1329) * chore: add fetch config and move feedback widget for the plugin chore: rewrite and test the api request chore: rebase chore: update translation feedback chore: test chore: add more tests * chore: rebase * chore: update requested change * chore: update package * chore: upgrade frontend-lib-special-exams and frontend-lib-learning-assistant * chore: update tests * chore: remove unneeded package * chore: update example config * chore: add source-map-loader * fix: feedback widget render error after submit feedback (#1335) * fix: feedback widget render error after submit feedback * fix: widget logic --------- Co-authored-by: Rodrigo Martin <rodrigom_94@hotmail.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import React, {
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {
|
||||
sendTrackEvent,
|
||||
sendTrackingLogEvent,
|
||||
@@ -37,7 +38,10 @@ const Sequence = ({
|
||||
previousSequenceHandler,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const course = useModel('coursewareMeta', courseId);
|
||||
const {
|
||||
canAccessProctoredExams,
|
||||
license,
|
||||
} = useModel('coursewareMeta', courseId);
|
||||
const {
|
||||
isStaff,
|
||||
originalUserIsStaff,
|
||||
@@ -144,57 +148,60 @@ const Sequence = ({
|
||||
const gated = sequence && sequence.gatedContent !== undefined && sequence.gatedContent.gated;
|
||||
|
||||
const defaultContent = (
|
||||
<div className="sequence-container d-inline-flex flex-row w-100">
|
||||
<div className={classNames('sequence w-100', { 'position-relative': shouldDisplayNotificationTriggerInSequence })}>
|
||||
<div className="sequence-navigation-container">
|
||||
<SequenceNavigation
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
className="mb-4"
|
||||
nextHandler={() => {
|
||||
logEvent('edx.ui.lms.sequence.next_selected', 'top');
|
||||
handleNext();
|
||||
}}
|
||||
onNavigate={(destinationUnitId) => {
|
||||
logEvent('edx.ui.lms.sequence.tab_selected', 'top', destinationUnitId);
|
||||
handleNavigate(destinationUnitId);
|
||||
}}
|
||||
previousHandler={() => {
|
||||
logEvent('edx.ui.lms.sequence.previous_selected', 'top');
|
||||
handlePrevious();
|
||||
}}
|
||||
/>
|
||||
{shouldDisplayNotificationTriggerInSequence && (
|
||||
enableNewSidebar === 'true' ? <NewSidebarTriggers /> : <SidebarTriggers />
|
||||
)}
|
||||
</div>
|
||||
<>
|
||||
<div className="sequence-container d-inline-flex flex-row w-100">
|
||||
<div className={classNames('sequence w-100', { 'position-relative': shouldDisplayNotificationTriggerInSequence })}>
|
||||
<div className="sequence-navigation-container">
|
||||
<SequenceNavigation
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
className="mb-4"
|
||||
nextHandler={() => {
|
||||
logEvent('edx.ui.lms.sequence.next_selected', 'top');
|
||||
handleNext();
|
||||
}}
|
||||
onNavigate={(destinationUnitId) => {
|
||||
logEvent('edx.ui.lms.sequence.tab_selected', 'top', destinationUnitId);
|
||||
handleNavigate(destinationUnitId);
|
||||
}}
|
||||
previousHandler={() => {
|
||||
logEvent('edx.ui.lms.sequence.previous_selected', 'top');
|
||||
handlePrevious();
|
||||
}}
|
||||
/>
|
||||
{shouldDisplayNotificationTriggerInSequence && (
|
||||
enableNewSidebar === 'true' ? <NewSidebarTriggers /> : <SidebarTriggers />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="unit-container flex-grow-1">
|
||||
<SequenceContent
|
||||
courseId={courseId}
|
||||
gated={gated}
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
unitLoadedHandler={handleUnitLoaded}
|
||||
/>
|
||||
{unitHasLoaded && (
|
||||
<UnitNavigation
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
onClickPrevious={() => {
|
||||
logEvent('edx.ui.lms.sequence.previous_selected', 'bottom');
|
||||
handlePrevious();
|
||||
}}
|
||||
onClickNext={() => {
|
||||
logEvent('edx.ui.lms.sequence.next_selected', 'bottom');
|
||||
handleNext();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className="unit-container flex-grow-1">
|
||||
<SequenceContent
|
||||
courseId={courseId}
|
||||
gated={gated}
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
unitLoadedHandler={handleUnitLoaded}
|
||||
/>
|
||||
{unitHasLoaded && (
|
||||
<UnitNavigation
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
onClickPrevious={() => {
|
||||
logEvent('edx.ui.lms.sequence.previous_selected', 'bottom');
|
||||
handlePrevious();
|
||||
}}
|
||||
onClickNext={() => {
|
||||
logEvent('edx.ui.lms.sequence.next_selected', 'bottom');
|
||||
handleNext();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{enableNewSidebar === 'true' ? <NewSidebar /> : <Sidebar />}
|
||||
</div>
|
||||
{enableNewSidebar === 'true' ? <NewSidebar /> : <Sidebar />}
|
||||
</div>
|
||||
<div id="whole-course-translation-feedback-widget" />
|
||||
</>
|
||||
);
|
||||
|
||||
if (sequenceStatus === 'loaded') {
|
||||
@@ -205,11 +212,11 @@ const Sequence = ({
|
||||
courseId={courseId}
|
||||
isStaff={isStaff}
|
||||
originalUserIsStaff={originalUserIsStaff}
|
||||
canAccessProctoredExams={course.canAccessProctoredExams}
|
||||
canAccessProctoredExams={canAccessProctoredExams}
|
||||
>
|
||||
{defaultContent}
|
||||
</SequenceExamWrapper>
|
||||
<CourseLicense license={course.license || undefined} />
|
||||
<CourseLicense license={license || undefined} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ErrorPage } from '@edx/frontend-platform/react';
|
||||
import { StrictDict } from '@edx/react-unit-test-utils';
|
||||
import { ModalDialog, Modal } from '@openedx/paragon';
|
||||
|
||||
import PageLoading from '../../../../generic/PageLoading';
|
||||
import PageLoading from '@src/generic/PageLoading';
|
||||
import * as hooks from './hooks';
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ErrorPage } from '@edx/frontend-platform/react';
|
||||
import { ModalDialog, Modal } from '@openedx/paragon';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
|
||||
import PageLoading from '../../../../generic/PageLoading';
|
||||
import PageLoading from '@src/generic/PageLoading';
|
||||
|
||||
import * as hooks from './hooks';
|
||||
import ContentIFrame, { IFRAME_FEATURE_POLICY, testIDs } from './ContentIFrame';
|
||||
@@ -19,7 +19,7 @@ jest.mock('@openedx/paragon', () => jest.requireActual('@edx/react-unit-test-uti
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('../../../../generic/PageLoading', () => 'PageLoading');
|
||||
jest.mock('@src/generic/PageLoading', () => 'PageLoading');
|
||||
|
||||
jest.mock('./hooks', () => ({
|
||||
useIFrameBehavior: jest.fn(),
|
||||
|
||||
@@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { useModel } from '../../../../generic/model-store';
|
||||
import PageLoading from '../../../../generic/PageLoading';
|
||||
import { useModel } from '@src/generic/model-store';
|
||||
import PageLoading from '@src/generic/PageLoading';
|
||||
|
||||
import messages from '../messages';
|
||||
import HonorCode from '../honor-code';
|
||||
|
||||
@@ -2,8 +2,8 @@ import React from 'react';
|
||||
|
||||
import { formatMessage, shallow } from '@edx/react-unit-test-utils';
|
||||
|
||||
import { useModel } from '../../../../generic/model-store';
|
||||
import PageLoading from '../../../../generic/PageLoading';
|
||||
import { useModel } from '@src/generic/model-store';
|
||||
import PageLoading from '@src/generic/PageLoading';
|
||||
|
||||
import messages from '../messages';
|
||||
import HonorCode from '../honor-code';
|
||||
@@ -25,8 +25,8 @@ jest.mock('react', () => ({
|
||||
|
||||
jest.mock('../honor-code', () => 'HonorCode');
|
||||
jest.mock('../lock-paywall', () => 'LockPaywall');
|
||||
jest.mock('../../../../generic/model-store', () => ({ useModel: jest.fn() }));
|
||||
jest.mock('../../../../generic/PageLoading', () => 'PageLoading');
|
||||
jest.mock('@src/generic/model-store', () => ({ useModel: jest.fn() }));
|
||||
jest.mock('@src/generic/PageLoading', () => 'PageLoading');
|
||||
|
||||
jest.mock('./hooks', () => ({
|
||||
useShouldDisplayHonorCode: jest.fn(() => false),
|
||||
|
||||
@@ -20,11 +20,24 @@ exports[`Unit component output snapshot: not bookmarked, do not show content 1`]
|
||||
<div
|
||||
className="unit"
|
||||
>
|
||||
<h1
|
||||
className="mb-0 h3"
|
||||
<div
|
||||
className="mb-0"
|
||||
>
|
||||
unit-title
|
||||
</h1>
|
||||
<h3
|
||||
className="h3"
|
||||
>
|
||||
unit-title
|
||||
</h3>
|
||||
<PluginSlot
|
||||
id="unit_title_plugin"
|
||||
pluginProps={
|
||||
Object {
|
||||
"courseId": "test-course-id",
|
||||
"unitId": "test-props-id",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<h2
|
||||
className="sr-only"
|
||||
>
|
||||
|
||||
@@ -5,9 +5,9 @@ import { useDispatch } from 'react-redux';
|
||||
import { StrictDict, useKeyedState } from '@edx/react-unit-test-utils';
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
|
||||
import { fetchCourse } from '../../../../data';
|
||||
import { processEvent } from '../../../../../course-home/data/thunks';
|
||||
import { useEventListener } from '../../../../../generic/hooks';
|
||||
import { fetchCourse } from '@src/courseware/data';
|
||||
import { processEvent } from '@src/course-home/data/thunks';
|
||||
import { useEventListener } from '@src/generic/hooks';
|
||||
import { messageTypes } from '../constants';
|
||||
|
||||
import useLoadBearingHook from './useLoadBearingHook';
|
||||
|
||||
@@ -5,9 +5,9 @@ import { getEffects, mockUseKeyedState } from '@edx/react-unit-test-utils';
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { fetchCourse } from '../../../../data';
|
||||
import { processEvent } from '../../../../../course-home/data/thunks';
|
||||
import { useEventListener } from '../../../../../generic/hooks';
|
||||
import { fetchCourse } from '@src/courseware/data';
|
||||
import { processEvent } from '@src/course-home/data/thunks';
|
||||
import { useEventListener } from '@src/generic/hooks';
|
||||
|
||||
import { messageTypes } from '../constants';
|
||||
|
||||
@@ -33,13 +33,13 @@ jest.mock('@edx/frontend-platform/logging', () => ({
|
||||
logError: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../data', () => ({
|
||||
jest.mock('@src/courseware/data', () => ({
|
||||
fetchCourse: jest.fn(),
|
||||
}));
|
||||
jest.mock('../../../../../course-home/data/thunks', () => ({
|
||||
jest.mock('@src/course-home/data/thunks', () => ({
|
||||
processEvent: jest.fn((...args) => ({ processEvent: args })),
|
||||
}));
|
||||
jest.mock('../../../../../generic/hooks', () => ({
|
||||
jest.mock('@src/generic/hooks', () => ({
|
||||
useEventListener: jest.fn(),
|
||||
}));
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
|
||||
import { StrictDict, useKeyedState } from '@edx/react-unit-test-utils/dist';
|
||||
|
||||
import { useEventListener } from '../../../../../generic/hooks';
|
||||
import { useEventListener } from '@src/generic/hooks';
|
||||
|
||||
export const stateKeys = StrictDict({
|
||||
isOpen: 'isOpen',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { mockUseKeyedState } from '@edx/react-unit-test-utils';
|
||||
import { useEventListener } from '../../../../../generic/hooks';
|
||||
import { useEventListener } from '@src/generic/hooks';
|
||||
import { messageTypes } from '../constants';
|
||||
|
||||
import useModalIFrameData, { stateKeys, DEFAULT_HEIGHT } from './useModalIFrameData';
|
||||
@@ -8,7 +8,7 @@ jest.mock('react', () => ({
|
||||
...jest.requireActual('react'),
|
||||
useCallback: jest.fn((cb, prereqs) => ({ cb, prereqs })),
|
||||
}));
|
||||
jest.mock('../../../../../generic/hooks', () => ({
|
||||
jest.mock('@src/generic/hooks', () => ({
|
||||
useEventListener: jest.fn(),
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { StrictDict, useKeyedState } from '@edx/react-unit-test-utils/dist';
|
||||
import { useModel } from '../../../../../generic/model-store';
|
||||
import { useModel } from '@src/generic/model-store';
|
||||
|
||||
import { modelKeys } from '../constants';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { getEffects, mockUseKeyedState } from '@edx/react-unit-test-utils';
|
||||
import { useModel } from '../../../../../generic/model-store';
|
||||
import { useModel } from '@src/generic/model-store';
|
||||
|
||||
import { modelKeys } from '../constants';
|
||||
|
||||
@@ -11,7 +11,7 @@ jest.mock('react', () => ({
|
||||
...jest.requireActual('react'),
|
||||
useEffect: jest.fn(),
|
||||
}));
|
||||
jest.mock('../../../../../generic/model-store', () => ({
|
||||
jest.mock('@src/generic/model-store', () => ({
|
||||
useModel: jest.fn(),
|
||||
}));
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@ import React from 'react';
|
||||
import { AppContext } from '@edx/frontend-platform/react';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { useModel } from '../../../../generic/model-store';
|
||||
import { useModel } from '@src/generic/model-store';
|
||||
import { usePluginsCallback } from '@src/generic/plugin-store';
|
||||
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
import BookmarkButton from '../../bookmark/BookmarkButton';
|
||||
import messages from '../messages';
|
||||
import ContentIFrame from './ContentIFrame';
|
||||
@@ -28,16 +30,27 @@ const Unit = ({
|
||||
const isProcessing = unit.bookmarkedUpdateState === 'loading';
|
||||
const view = authenticatedUser ? views.student : views.public;
|
||||
|
||||
const iframeUrl = getIFrameUrl({
|
||||
const getUrl = usePluginsCallback('getIFrameUrl', () => getIFrameUrl({
|
||||
id,
|
||||
view,
|
||||
format,
|
||||
examAccess,
|
||||
});
|
||||
}));
|
||||
|
||||
const iframeUrl = getUrl();
|
||||
|
||||
return (
|
||||
<div className="unit">
|
||||
<h1 className="mb-0 h3">{unit.title}</h1>
|
||||
<div className="mb-0">
|
||||
<h3 className="h3">{unit.title}</h3>
|
||||
<PluginSlot
|
||||
id="unit_title_plugin"
|
||||
pluginProps={{
|
||||
courseId,
|
||||
unitId: id,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<h2 className="sr-only">{formatMessage(messages.headerPlaceholder)}</h2>
|
||||
<BookmarkButton
|
||||
unitId={unit.id}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import { when } from 'jest-when';
|
||||
import { formatMessage, shallow } from '@edx/react-unit-test-utils/dist';
|
||||
|
||||
import { useModel } from '../../../../generic/model-store';
|
||||
import { useModel } from '@src/generic/model-store';
|
||||
|
||||
import BookmarkButton from '../../bookmark/BookmarkButton';
|
||||
import UnitSuspense from './UnitSuspense';
|
||||
@@ -9,7 +10,7 @@ import ContentIFrame from './ContentIFrame';
|
||||
import Unit from '.';
|
||||
import messages from '../messages';
|
||||
import { getIFrameUrl } from './urls';
|
||||
import { views } from './constants';
|
||||
import { modelKeys, views } from './constants';
|
||||
import * as hooks from './hooks';
|
||||
|
||||
jest.mock('./hooks', () => ({ useUnitData: jest.fn() }));
|
||||
@@ -22,14 +23,14 @@ jest.mock('@edx/frontend-platform/i18n', () => {
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../generic/PageLoading', () => 'PageLoading');
|
||||
jest.mock('@src/generic/PageLoading', () => 'PageLoading');
|
||||
jest.mock('../../bookmark/BookmarkButton', () => 'BookmarkButton');
|
||||
jest.mock('./ContentIFrame', () => 'ContentIFrame');
|
||||
jest.mock('./UnitSuspense', () => 'UnitSuspense');
|
||||
jest.mock('../honor-code', () => 'HonorCode');
|
||||
jest.mock('../lock-paywall', () => 'LockPaywall');
|
||||
|
||||
jest.mock('../../../../generic/model-store', () => ({
|
||||
jest.mock('@src/generic/model-store', () => ({
|
||||
useModel: jest.fn(),
|
||||
}));
|
||||
|
||||
@@ -70,7 +71,14 @@ const unit = {
|
||||
bookmarked: false,
|
||||
bookmarkedUpdateState: 'pending',
|
||||
};
|
||||
useModel.mockReturnValue(unit);
|
||||
const mockCoursewareMetaFn = jest.fn(() => ({ wholeCourseTranslationEnabled: false }));
|
||||
const mockUnitsFn = jest.fn(() => unit);
|
||||
|
||||
when(useModel)
|
||||
.calledWith('courseHomeMeta', props.courseId)
|
||||
.mockImplementation(mockCoursewareMetaFn)
|
||||
.calledWith(modelKeys.units, props.id)
|
||||
.mockImplementation(mockUnitsFn);
|
||||
|
||||
let el;
|
||||
describe('Unit component', () => {
|
||||
@@ -113,7 +121,7 @@ describe('Unit component', () => {
|
||||
});
|
||||
describe('bookmarked, bookmark update pending', () => {
|
||||
beforeEach(() => {
|
||||
useModel.mockReturnValueOnce({ ...unit, bookmarked: true });
|
||||
mockUnitsFn.mockReturnValueOnce({ ...unit, bookmarked: true });
|
||||
renderComponent();
|
||||
});
|
||||
test('snapshot', () => {
|
||||
|
||||
@@ -19,24 +19,43 @@ const props = {
|
||||
examAccess: { blockAccess: false, accessToken: 'test-access-token' },
|
||||
};
|
||||
|
||||
describe('urls module', () => {
|
||||
describe('getIFrameUrl', () => {
|
||||
test('format provided, exam access and token available', () => {
|
||||
const params = stringify({
|
||||
...iframeParams,
|
||||
view: props.view,
|
||||
format: props.format,
|
||||
exam_access: props.examAccess.accessToken,
|
||||
});
|
||||
expect(getIFrameUrl(props)).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`);
|
||||
describe('urls module getIFrameUrl', () => {
|
||||
test('format provided, exam access and token available', () => {
|
||||
const params = stringify({
|
||||
...iframeParams,
|
||||
view: props.view,
|
||||
format: props.format,
|
||||
exam_access: props.examAccess.accessToken,
|
||||
});
|
||||
test('no format provided, exam access blocked', () => {
|
||||
const params = stringify({ ...iframeParams, view: props.view });
|
||||
expect(getIFrameUrl({
|
||||
id: props.id,
|
||||
view: props.view,
|
||||
examAccess: { blockAccess: true },
|
||||
})).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`);
|
||||
expect(getIFrameUrl(props)).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`);
|
||||
});
|
||||
test('no format provided, exam access blocked', () => {
|
||||
const params = stringify({ ...iframeParams, view: props.view });
|
||||
expect(getIFrameUrl({
|
||||
id: props.id,
|
||||
view: props.view,
|
||||
examAccess: { blockAccess: true },
|
||||
})).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`);
|
||||
});
|
||||
test('src and dest languages provided', () => {
|
||||
const params = stringify({
|
||||
...iframeParams,
|
||||
view: props.view,
|
||||
src_lang: 'test-src-lang',
|
||||
dest_lang: 'test-dest-lang',
|
||||
});
|
||||
expect(getIFrameUrl({
|
||||
...props,
|
||||
srcLanguage: 'test-src-lang',
|
||||
destLanguage: 'test-dest-lang',
|
||||
})).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`);
|
||||
});
|
||||
test('src and dest languages provided are the same', () => {
|
||||
const params = stringify({ ...iframeParams, view: props.view });
|
||||
expect(getIFrameUrl({
|
||||
...props,
|
||||
srcLanguage: 'test-lang',
|
||||
destLanguage: 'test-lang',
|
||||
})).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,8 +31,7 @@ const HiddenAfterDue = ({ courseId, intl }) => {
|
||||
<>
|
||||
<br />
|
||||
<FormattedMessage
|
||||
id="learn.hiddenAfterDue.gradeAvailable"
|
||||
defaultMessage="If you have completed this assignment, your grade is available on the {progressPage}."
|
||||
{...messages.gradeAvailable}
|
||||
values={{
|
||||
progressPage: progressLink,
|
||||
}}
|
||||
|
||||
@@ -79,7 +79,7 @@ const LockPaywall = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert variant="light" aria-live="off" icon={Locked} className="lock-paywall-container">
|
||||
<Alert variant="light" aria-live="off" icon={Locked} className="lock-paywall-container" data-testId="lock-paywall-test-id">
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<h4 aria-level="3">
|
||||
|
||||
@@ -76,9 +76,9 @@ describe('Lock Paywall', () => {
|
||||
it('does not display anything if course does not have verified mode', async () => {
|
||||
const courseHomeMetadata = Factory.build('courseHomeMetadata', { verified_mode: null });
|
||||
const testStore = await initializeTestStore({ courseHomeMetadata, excludeFetchSequence: true }, false);
|
||||
const { container } = render(<LockPaywall {...mockData} courseId={courseHomeMetadata.id} />, { store: testStore });
|
||||
render(<LockPaywall {...mockData} courseId={courseHomeMetadata.id} />, { store: testStore });
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
expect(screen.queryByTestId('lock-paywall-test-id')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays past expiration message if expiration date has expired', async () => {
|
||||
|
||||
@@ -110,7 +110,7 @@ const SequenceNavigation = ({
|
||||
};
|
||||
|
||||
return sequenceStatus === LOADED && (
|
||||
<nav id="courseware-sequenceNavigation" className={classNames('sequence-navigation', className, { 'mr-2': shouldDisplayNotificationTriggerInSequence })}>
|
||||
<nav id="courseware-sequence-navigation" data-testid="courseware-sequence-navigation" className={classNames('sequence-navigation', className, { 'mr-2': shouldDisplayNotificationTriggerInSequence })}>
|
||||
{renderPreviousButton()}
|
||||
{renderUnitButtons()}
|
||||
{renderNextButton()}
|
||||
|
||||
@@ -33,9 +33,9 @@ describe('Sequence Navigation', () => {
|
||||
|
||||
it('is empty while loading', async () => {
|
||||
const testStore = await initializeTestStore({ excludeFetchSequence: true }, false);
|
||||
const { container } = render(<SequenceNavigation {...mockData} />, { store: testStore, wrapWithRouter: true });
|
||||
render(<SequenceNavigation {...mockData} />, { store: testStore, wrapWithRouter: true });
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
expect(screen.queryByTestId('courseware-sequence-navigation')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders empty div without unitId', () => {
|
||||
|
||||
@@ -12,7 +12,7 @@ const SequenceNavigationDropdown = ({
|
||||
unitIds,
|
||||
}) => (
|
||||
<Dropdown className="sequence-navigation-dropdown">
|
||||
<Dropdown.Toggle variant="link" className="font-weight-normal w-100 border-right-0">
|
||||
<Dropdown.Toggle id="navigation-toggle" variant="link" className="font-weight-normal w-100 border-right-0">
|
||||
<FormattedMessage
|
||||
defaultMessage="{current} of {total}"
|
||||
description="The title of the mobile menu for sequence navigation of units"
|
||||
|
||||
Reference in New Issue
Block a user