feat: add social share widget (#323)

This commit is contained in:
Kristin Aoki
2023-05-03 09:55:31 -04:00
committed by GitHub
parent d68bdf9dc6
commit 6aaedfc500
13 changed files with 540 additions and 100 deletions

View File

@@ -0,0 +1,95 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SocialShareWidget rendered with videoSharingEnabled true and allowVideoSharing value equals false should have subtitle with text that reads Enabled 1`] = `
<injectIntl(ShimmedIntlComponent)
fontSize="x-small"
subtitle="Disabled"
title="Social Sharing"
>
<div>
<FormattedMessage
defaultMessage="Allow this video to be shareable to social media"
description="Description for sociail sharing setting"
id="authoring.videoeditor.socialShare.description"
/>
</div>
<Form.Checkbox
checked={false}
className="mt-3"
disabled={false}
onChange={[Function]}
>
<div
className="small text-gray-700"
>
This video is shareable to social media
</div>
</Form.Checkbox>
<div>
<FormattedMessage
defaultMessage="Note: This setting is overridden by the course outline page."
description="Message that the setting can be overriden in the course outline"
id="authoring.videoeditor.socialShare.overrideNote"
/>
</div>
<div
className="mt-3"
>
<Hyperlink
className="text-primary-500"
destination="sOMeURl.cOM"
target="_blank"
>
Learn more about social sharing
</Hyperlink>
</div>
</injectIntl(ShimmedIntlComponent)>
`;
exports[`SocialShareWidget rendered with videoSharingEnabled true and allowVideoSharing value equals true should have subtitle with text that reads Enabled 1`] = `
<injectIntl(ShimmedIntlComponent)
fontSize="x-small"
subtitle="Enabled"
title="Social Sharing"
>
<div>
<FormattedMessage
defaultMessage="Allow this video to be shareable to social media"
description="Description for sociail sharing setting"
id="authoring.videoeditor.socialShare.description"
/>
</div>
<Form.Checkbox
checked={true}
className="mt-3"
disabled={false}
onChange={[Function]}
>
<div
className="small text-gray-700"
>
This video is shareable to social media
</div>
</Form.Checkbox>
<div>
<FormattedMessage
defaultMessage="Note: This setting is overridden by the course outline page."
description="Message that the setting can be overriden in the course outline"
id="authoring.videoeditor.socialShare.overrideNote"
/>
</div>
<div
className="mt-3"
>
<Hyperlink
className="text-primary-500"
destination="sOMeURl.cOM"
target="_blank"
>
Learn more about social sharing
</Hyperlink>
</div>
</injectIntl(ShimmedIntlComponent)>
`;
exports[`SocialShareWidget rendered with with videoSharingEnabled false should return null 1`] = `""`;

View File

@@ -0,0 +1,110 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {
FormattedMessage,
injectIntl,
intlShape,
} from '@edx/frontend-platform/i18n';
import {
Hyperlink,
Form,
} from '@edx/paragon';
import { selectors, actions } from '../../../../../../data/redux';
import CollapsibleFormWidget from '../CollapsibleFormWidget';
import messages from './messages';
/**
* Collapsible Form widget controlling video thumbnail
*/
export const SocialShareWidget = ({
// injected
intl,
// redux
allowVideoSharing,
videoSharingEnabledForCourse,
videoSharingLearnMoreLink,
updateField,
}) => {
const isSetByCourse = allowVideoSharing.level === 'course';
const getSubtitle = () => {
if (allowVideoSharing.value) {
return intl.formatMessage(messages.enabledSubtitle);
}
return intl.formatMessage(messages.disabledSubtitle);
};
return (videoSharingEnabledForCourse ? (
<CollapsibleFormWidget
fontSize="x-small"
title={intl.formatMessage(messages.title)}
subtitle={getSubtitle()}
>
<div>
<FormattedMessage {...messages.socialSharingDescription} />
</div>
<Form.Checkbox
className="mt-3"
checked={allowVideoSharing.value}
disabled={isSetByCourse}
onChange={(e) => updateField({
allowVideoSharing: {
...allowVideoSharing,
value: e.target.checked,
},
})}
>
<div className="small text-gray-700">
{intl.formatMessage(messages.socialSharingCheckboxLabel)}
</div>
</Form.Checkbox>
<div>
<FormattedMessage {...messages.overrideSocialSharingNote} />
</div>
{isSetByCourse && (
<div>
<FormattedMessage {...messages.disclaimerSettingLocation} />
</div>
)}
<div className="mt-3">
<Hyperlink className="text-primary-500" destination={videoSharingLearnMoreLink} target="_blank">
{intl.formatMessage(messages.learnMoreLinkLabel)}
</Hyperlink>
</div>
</CollapsibleFormWidget>
) : null);
};
SocialShareWidget.defaultProps = {
allowVideoSharing: {
level: 'block',
value: false,
},
videoSharingEnabledForCourse: false,
};
SocialShareWidget.propTypes = {
// injected
intl: intlShape.isRequired,
// redux
allowVideoSharing: PropTypes.shape({
level: PropTypes.string.isRequired,
value: PropTypes.bool.isRequired,
}),
videoSharingEnabledForCourse: PropTypes.bool,
videoSharingLearnMoreLink: PropTypes.string.isRequired,
updateField: PropTypes.func.isRequired,
};
export const mapStateToProps = (state) => ({
allowVideoSharing: selectors.video.allowVideoSharing(state),
videoSharingLearnMoreLink: selectors.video.videoSharingLearnMoreLink(state),
videoSharingEnabledForCourse: selectors.video.videoSharingEnabledForCourse(state),
});
export const mapDispatchToProps = (dispatch) => ({
updateField: (stateUpdate) => dispatch(actions.video.updateField(stateUpdate)),
});
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SocialShareWidget));

View File

@@ -0,0 +1,185 @@
import React from 'react';
import { shallow } from 'enzyme';
import { formatMessage } from '../../../../../../../testUtils';
import { actions, selectors } from '../../../../../../data/redux';
import { SocialShareWidget, mapStateToProps, mapDispatchToProps } from '.';
import messages from './messages';
jest.mock('react', () => {
const updateState = jest.fn();
return {
...jest.requireActual('react'),
updateState,
useContext: jest.fn(() => ({ license: ['error.license', jest.fn().mockName('error.setLicense')] })),
};
});
jest.mock('../../../../../../data/redux', () => ({
actions: {
video: {
updateField: jest.fn().mockName('actions.video.updateField'),
},
},
selectors: {
video: {
allowVideoSharing: jest.fn(state => ({ allowVideoSharing: state })),
videoSharingEnabledForCourse: jest.fn(state => ({ videoSharingEnabledForCourse: state })),
videoSharingLearnMoreLink: jest.fn(state => ({ videoSharingLearnMoreLink: state })),
},
},
}));
describe('SocialShareWidget', () => {
const props = {
title: 'tiTLE',
intl: { formatMessage },
videoSharingEnabledForCourse: false,
allowVideoSharing: {
level: 'block',
value: false,
},
videoSharingLearnMoreLink: 'sOMeURl.cOM',
updateField: jest.fn().mockName('args.updateField'),
};
describe('rendered with with videoSharingEnabled false', () => {
it('should return null', () => {
const wrapper = shallow(<SocialShareWidget {...props} />);
expect(wrapper).toMatchSnapshot();
});
});
describe('rendered with videoSharingEnabled true', () => {
describe('and allowVideoSharing value equals true', () => {
describe(' with level equal to course', () => {
const wrapper = shallow(<SocialShareWidget
{...props}
videoSharingEnabledForCourse
allowVideoSharing={{
level: 'course',
value: true,
}}
/>);
it('should have setting location message', () => {
const settingLocationDisclaimer = wrapper.find('FormattedMessage').at(2).prop('defaultMessage');
expect(settingLocationDisclaimer).toEqual(messages.disclaimerSettingLocation.defaultMessage);
});
it('should have checkbox disabled prop equal true', () => {
const disabledCheckbox = wrapper.children().at(1).prop('disabled');
expect(disabledCheckbox).toEqual(true);
});
});
describe(' with level equal to block', () => {
const wrapper = shallow(<SocialShareWidget
{...props}
videoSharingEnabledForCourse
allowVideoSharing={{
level: 'block',
value: true,
}}
/>);
it('should not have setting location message', () => {
const formattedMessages = wrapper.find('FormattedMessage');
expect(formattedMessages.length).toEqual(2);
expect(formattedMessages.at(0)).not.toEqual(messages.disclaimerSettingLocation.defaultMessage);
expect(formattedMessages.at(1)).not.toEqual(messages.disclaimerSettingLocation.defaultMessage);
});
it('should have checkbox disabled prop equal false', () => {
const disabledCheckbox = wrapper.children().at(1).prop('disabled');
expect(disabledCheckbox).toEqual(false);
});
});
it('should have subtitle with text that reads Enabled', () => {
const wrapper = shallow(<SocialShareWidget
{...props}
videoSharingEnabledForCourse
allowVideoSharing={{
level: 'block',
value: true,
}}
/>);
const subtitle = wrapper.prop('subtitle');
expect(wrapper).toMatchSnapshot();
expect(subtitle).toEqual('Enabled');
});
});
describe('and allowVideoSharing value equals false', () => {
describe(' with level equal to course', () => {
const wrapper = shallow(<SocialShareWidget
{...props}
videoSharingEnabledForCourse
allowVideoSharing={{
level: 'course',
value: false,
}}
/>);
it('should have setting location message', () => {
const settingLocationDisclaimer = wrapper.find('FormattedMessage').at(2).prop('defaultMessage');
expect(settingLocationDisclaimer).toEqual(messages.disclaimerSettingLocation.defaultMessage);
});
it('should have checkbox disabled prop equal true', () => {
const disabledCheckbox = wrapper.children().at(1).prop('disabled');
expect(disabledCheckbox).toEqual(true);
});
});
describe(' with level equal to block', () => {
const wrapper = shallow(<SocialShareWidget
{...props}
videoSharingEnabledForCourse
allowVideoSharing={{
level: 'block',
value: false,
}}
/>);
it('should not have setting location message', () => {
const formattedMessages = wrapper.find('FormattedMessage');
expect(formattedMessages.length).toEqual(2);
expect(formattedMessages.at(0)).not.toEqual(messages.disclaimerSettingLocation.defaultMessage);
expect(formattedMessages.at(1)).not.toEqual(messages.disclaimerSettingLocation.defaultMessage);
});
it('should have checkbox disabled prop equal false', () => {
const disabledCheckbox = wrapper.children().at(1).prop('disabled');
expect(disabledCheckbox).toEqual(false);
});
});
it('should have subtitle with text that reads Enabled', () => {
const wrapper = shallow(<SocialShareWidget
{...props}
videoSharingEnabledForCourse
allowVideoSharing={{
level: 'block',
value: false,
}}
/>);
const subtitle = wrapper.prop('subtitle');
expect(wrapper).toMatchSnapshot();
expect(subtitle).toEqual('Disabled');
});
});
});
describe('mapStateToProps', () => {
const testState = { A: 'pple', B: 'anana', C: 'ucumber' };
test('allowVideoSharing from video.allowVideoSharing', () => {
expect(
mapStateToProps(testState).allowVideoSharing,
).toEqual(selectors.video.allowVideoSharing(testState));
});
test('videoSharingEnabledForCourse from video.videoSharingEnabledForCourse', () => {
expect(
mapStateToProps(testState).videoSharingEnabledForCourse,
).toEqual(selectors.video.videoSharingEnabledForCourse(testState));
});
test('videoSharingLearnMoreLink from video.videoSharingLearnMoreLink', () => {
expect(
mapStateToProps(testState).videoSharingLearnMoreLink,
).toEqual(selectors.video.videoSharingLearnMoreLink(testState));
});
});
describe('mapDispatchToProps', () => {
const dispatch = jest.fn();
test('updateField from actions.video.updateField', () => {
expect(mapDispatchToProps.updateField).toEqual(dispatch(actions.video.updateField));
});
});
});

View File

@@ -0,0 +1,46 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
title: {
id: 'authoring.videoeditor.socialShare.title',
defaultMessage: 'Social Sharing',
description: 'Title for socialShare widget',
},
disabledSubtitle: {
id: 'authoring.videoeditor.socialShare.disabled.subtitle',
defaultMessage: 'Disabled',
description: 'Subtitle for unavailable socialShare widget',
},
enabledSubtitle: {
id: 'authoring.videoeditor.socialShare.enabled.subtitle',
defaultMessage: 'Enabled',
description: 'Subtitle for when thumbnail has been uploaded to the widget',
},
learnMoreLinkLabel: {
id: 'authoring.videoeditor.socialShare.learnMore.link',
defaultMessage: 'Learn more about social sharing',
description: 'Text for link to learn more about social sharing',
},
socialSharingDescription: {
id: 'authoring.videoeditor.socialShare.description',
defaultMessage: 'Allow this video to be shareable to social media',
description: 'Description for sociail sharing setting',
},
socialSharingCheckboxLabel: {
id: 'authoring.videoeditor.socialShare.checkbox.label',
defaultMessage: 'This video is shareable to social media',
description: 'Label for checkbox for allowing video to be share',
},
overrideSocialSharingNote: {
id: 'authoring.videoeditor.socialShare.overrideNote',
defaultMessage: 'Note: This setting is overridden by the course outline page.',
description: 'Message that the setting can be overriden in the course outline',
},
disclaimerSettingLocation: {
id: 'authoring.videoeditor.socialShare.settingsDisclaimer',
defaultMessage: 'Change this setting on the course outline page.',
description: 'Message for disabled checkbox that notifies user that setting can be modified in course outline',
},
});
export default messages;

View File

@@ -287,53 +287,6 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with videoSh
</OverlayTrigger>
<ActionRow.Spacer />
</ActionRow>
<ActionRow
className="mt-4.5"
>
<Form.Checkbox
checked={false}
className="decorative-control-label"
onChange={[MockFunction]}
>
<div
className="small text-gray-700"
>
<FormattedMessage
defaultMessage="Allow this video to be shared on social media."
description="Label for allow shareable video checkbox"
id="authoring.videoeditor.videoSource.allowVideoSharingCheckboxLabel"
/>
</div>
</Form.Checkbox>
<OverlayTrigger
key="top-allow-sharing"
overlay={
<Tooltip
id="tooltip-top-allow-sharing"
>
<FormattedMessage
defaultMessage="Allow learners to share this video publicly on social media.
The video will be viewable by anyone, they will not need to enroll in the course
or even have an edX account. Links to the course about page and to enroll in the
course will appear alongside the video."
description="Message for allow shareable video checkbox"
id="authoring.videoeditor.videoSource.allowVideoSharingTooltipMessage"
/>
</Tooltip>
}
placement="top"
>
<Icon
style={
Object {
"height": "16px",
"width": "16px",
}
}
/>
</OverlayTrigger>
<ActionRow.Spacer />
</ActionRow>
</Form.Group>
<div
className="my-4 border-primary-100 border-bottom"

View File

@@ -1,6 +1,5 @@
import React from 'react';
import { connect, useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import { useDispatch } from 'react-redux';
import {
Form,
@@ -21,7 +20,6 @@ import {
import * as widgetHooks from '../hooks';
import * as hooks from './hooks';
import messages from './messages';
import { selectors } from '../../../../../../data/redux';
import { ErrorAlert } from '../../../../../../sharedComponents/ErrorAlerts/ErrorAlert';
import CollapsibleFormWidget from '../CollapsibleFormWidget';
@@ -32,8 +30,6 @@ import CollapsibleFormWidget from '../CollapsibleFormWidget';
export const VideoSourceWidget = ({
// injected
intl,
// redux
videoSharingEnabledForCourse,
}) => {
const dispatch = useDispatch();
const {
@@ -41,7 +37,6 @@ export const VideoSourceWidget = ({
videoSource: source,
fallbackVideos,
allowVideoDownloads: allowDownload,
allowVideoSharing: allowSharing,
} = widgetHooks.widgetValues({
dispatch,
fields: {
@@ -49,7 +44,6 @@ export const VideoSourceWidget = ({
[widgetHooks.selectorKeys.videoId]: widgetHooks.genericWidget,
[widgetHooks.selectorKeys.fallbackVideos]: widgetHooks.arrayWidget,
[widgetHooks.selectorKeys.allowVideoDownloads]: widgetHooks.genericWidget,
[widgetHooks.selectorKeys.allowVideoSharing]: widgetHooks.genericWidget,
},
});
const { videoIdChangeAlert } = hooks.videoIdChangeAlert();
@@ -144,31 +138,6 @@ export const VideoSourceWidget = ({
</OverlayTrigger>
<ActionRow.Spacer />
</ActionRow>
{videoSharingEnabledForCourse && (
<ActionRow className="mt-4.5">
<Form.Checkbox
checked={allowSharing.local}
className="decorative-control-label"
onChange={allowSharing.onCheckedChange}
>
<div className="small text-gray-700">
<FormattedMessage {...messages.allowVideoSharingCheckboxLabel} />
</div>
</Form.Checkbox>
<OverlayTrigger
key="top-allow-sharing"
placement="top"
overlay={(
<Tooltip id="tooltip-top-allow-sharing">
<FormattedMessage {...messages.allowVideoSharingTooltipMessage} />
</Tooltip>
)}
>
<Icon src={InfoOutline} style={{ height: '16px', width: '16px' }} />
</OverlayTrigger>
<ActionRow.Spacer />
</ActionRow>
)}
</Form.Group>
<div className="my-4 border-primary-100 border-bottom" />
<Button
@@ -186,12 +155,6 @@ export const VideoSourceWidget = ({
VideoSourceWidget.propTypes = {
// injected
intl: intlShape.isRequired,
// redux
videoSharingEnabledForCourse: PropTypes.bool.isRequired,
};
export const mapStateToProps = (state) => ({
videoSharingEnabledForCourse: selectors.video.videoSharingEnabledForCourse(state),
});
export default injectIntl(connect(mapStateToProps, {})(VideoSourceWidget));
export default injectIntl(VideoSourceWidget);

View File

@@ -10,12 +10,14 @@ import TranscriptWidget from './components/TranscriptWidget';
import VideoSourceWidget from './components/VideoSourceWidget';
import VideoPreviewWidget from './components/VideoPreviewWidget';
import './index.scss';
import SocialShareWidget from './components/SocialShareWidget';
export const VideoSettingsModal = () => (
<>
<ErrorSummary />
<VideoPreviewWidget />
<VideoSourceWidget />
<SocialShareWidget />
<ThumbnailWidget />
<TranscriptWidget />
<DurationWidget />

View File

@@ -8,8 +8,9 @@ import { parseYoutubeId } from '../../services/cms/api';
export const loadVideoData = () => (dispatch, getState) => {
const state = getState();
const rawVideoData = state.app.blockValue.data.metadata ? state.app.blockValue.data.metadata : {};
const courseLicenseData = state.app.courseDetails.data ? state.app.courseDetails.data : {};
const blockValueData = state.app.blockValue.data;
const rawVideoData = blockValueData.metadata ? blockValueData.metadata : {};
const courseData = state.app.courseDetails.data ? state.app.courseDetails.data : {};
const studioView = state.app.studioView?.data?.html;
const {
videoId,
@@ -23,16 +24,21 @@ export const loadVideoData = () => (dispatch, getState) => {
const [licenseType, licenseOptions] = module.parseLicense({ licenseData: studioView, level: 'block' });
const transcripts = module.parseTranscripts({ transcriptsData: studioView });
const [courseLicenseType, courseLicenseDetails] = module.parseLicense({
licenseData: courseLicenseData.license,
licenseData: courseData.license,
level: 'course',
});
const allowVideoSharing = module.parseVideoSharingSetting({
courseSetting: blockValueData?.video_sharing_options,
blockSetting: rawVideoData.public_access,
});
dispatch(actions.video.load({
videoSource: videoUrl || '',
videoId,
fallbackVideos,
allowVideoDownloads: rawVideoData.download_video,
allowVideoSharing: rawVideoData.public_access,
allowVideoSharing,
videoSharingLearnMoreLink: blockValueData?.video_sharing_doc_url,
videoSharingEnabledForCourse: blockValueData?.video_sharing_enabled,
transcripts,
allowTranscriptDownloads: rawVideoData.download_track,
showTranscriptByDefault: rawVideoData.show_captions,
@@ -61,7 +67,6 @@ export const loadVideoData = () => (dispatch, getState) => {
dispatch(requests.fetchVideoFeatures({
onSuccess: (response) => dispatch(actions.video.updateField({
allowThumbnailUpload: response.data.allowThumbnailUpload,
videoSharingEnabledForCourse: response.data.videoSharingEnabled,
})),
}));
const youTubeId = parseYoutubeId(videoUrl);
@@ -100,6 +105,19 @@ export const determineVideoSources = ({
};
};
export const parseVideoSharingSetting = ({ courseSetting, blockSetting }) => {
switch (courseSetting) {
case 'all-on':
return { level: 'course', value: true };
case 'all-off':
return { level: 'course', value: false };
case 'per-video':
return { level: 'block', value: blockSetting };
default:
return { level: 'block', value: blockSetting };
}
};
export const parseTranscripts = ({ transcriptsData }) => {
if (!transcriptsData) {
return [];

View File

@@ -53,7 +53,6 @@ const mockAllowTranscriptImport = { data: { command: 'import' } };
const mockVideoFeatures = {
data: {
allowThumbnailUpload: 'soMEbOolEAn',
videoSharingEnabled: 'someBOoOoOlean',
},
};
@@ -70,6 +69,10 @@ const testMetadata = {
transcripts: ['do', 're', 'mi'],
thumbnail: 'thuMBNaIl',
};
const videoSharingData = {
video_sharing_doc_url: 'SomEUrL.Com',
video_sharing_options: 'OpTIOns',
};
const testState = {
transcripts: ['la'],
thumbnail: 'sOMefILE',
@@ -92,7 +95,7 @@ describe('video thunkActions', () => {
getState = jest.fn(() => ({
app: {
blockId: 'soMEBloCk',
blockValue: { data: { metadata: { ...testMetadata } } },
blockValue: { data: { metadata: { ...testMetadata }, ...videoSharingData } },
studioEndpointUrl: 'soMEeNDPoiNT',
courseDetails: { data: { license: null } },
studioView: { data: { html: 'sOMeHTml' } },
@@ -110,6 +113,10 @@ describe('video thunkActions', () => {
videoId: 'videOiD',
fallbackVideos: 'fALLbACKvIDeos',
});
jest.spyOn(thunkActions, thunkActionsKeys.parseVideoSharingSetting).mockReturnValue({
level: 'course',
value: true,
});
jest.spyOn(thunkActions, thunkActionsKeys.parseLicense).mockReturnValue([
'liCENSEtyPe',
{
@@ -146,6 +153,12 @@ describe('video thunkActions', () => {
videoId: 'videOiD',
fallbackVideos: 'fALLbACKvIDeos',
allowVideoDownloads: testMetadata.download_video,
allowVideoSharing: {
level: 'course',
value: true,
},
videoSharingLearnMoreLink: videoSharingData.video_sharing_doc_url,
videoSharingEnableForCourse: videoSharingData.video_sharing_enabled,
transcripts: testMetadata.transcripts,
allowTranscriptDownloads: testMetadata.download_track,
showTranscriptByDefault: testMetadata.show_captions,
@@ -177,10 +190,8 @@ describe('video thunkActions', () => {
dispatchedAction1.fetchVideoFeatures.onSuccess(mockVideoFeatures);
expect(dispatch).toHaveBeenCalledWith(actions.video.updateField({
allowThumbnailUpload: mockVideoFeatures.data.allowThumbnailUpload,
videoSharingEnabledForCourse: mockVideoFeatures.data.videoSharingEnabled,
}));
dispatch.mockClear();
dispatchedAction2.checkTranscriptsForImport.onSuccess(mockAllowTranscriptImport);
expect(dispatch).toHaveBeenCalledWith(actions.video.updateField({
allowTranscriptImport: true,
@@ -349,6 +360,55 @@ describe('video thunkActions', () => {
]);
});
});
describe('parseVideoShareSetting', () => {
describe('has no course setting or block setting for video sharing', () => {
it('should return an object with level equal to block and value equal to null', () => {
const videoSharingSetting = thunkActions.parseVideoSharingSetting({
courseSetting: null,
blockSetting: null,
});
expect(videoSharingSetting).toEqual({ level: 'block', value: null });
});
});
describe('has no course setting and block setting defined for video sharing', () => {
it('should return an object with level equal to block and value equal to true', () => {
const videoSharingSetting = thunkActions.parseVideoSharingSetting({
courseSetting: null,
blockSetting: true,
});
expect(videoSharingSetting).toEqual({ level: 'block', value: true });
});
});
describe('has course setting defined for video sharing', () => {
describe('course setting equals all-on', () => {
it('should return an object with level equal to course and value equal to true', () => {
const videoSharingSetting = thunkActions.parseVideoSharingSetting({
courseSetting: 'all-on',
blockSetting: true,
});
expect(videoSharingSetting).toEqual({ level: 'course', value: true });
});
});
describe('course setting equals all-off', () => {
it('should return an object with level equal to course and value equal to false', () => {
const videoSharingSetting = thunkActions.parseVideoSharingSetting({
courseSetting: 'all-off',
blockSetting: true,
});
expect(videoSharingSetting).toEqual({ level: 'course', value: false });
});
});
describe('course setting equals per-video', () => {
it('should return an object with level equal to block and value equal to block setting', () => {
const videoSharingSetting = thunkActions.parseVideoSharingSetting({
courseSetting: 'per-video',
blockSetting: false,
});
expect(videoSharingSetting).toEqual({ level: 'block', value: false });
});
});
});
});
describe('uploadHandout', () => {
beforeEach(() => {
thunkActions.uploadHandout({ file: mockFilename })(dispatch);

View File

@@ -10,8 +10,12 @@ const initialState = {
'',
],
allowVideoDownloads: false,
allowVideoSharing: false,
allowVideoSharing: {
level: 'block',
value: false,
},
videoSharingEnabledForCourse: false,
videoSharingLearnMoreLink: '',
thumbnail: null,
transcripts: [],
allowTranscriptDownloads: false,

View File

@@ -18,6 +18,7 @@ export const simpleSelectors = [
stateKeys.fallbackVideos,
stateKeys.allowVideoDownloads,
stateKeys.videoSharingEnabledForCourse,
stateKeys.videoSharingLearnMoreLink,
stateKeys.allowVideoSharing,
stateKeys.thumbnail,
stateKeys.transcripts,

View File

@@ -166,7 +166,7 @@ export const apiMethods = {
metadata: {
display_name: title,
download_video: content.allowVideoDownloads,
public_access: content.allowVideoSharing,
public_access: content.allowVideoSharing.value,
edx_video_id: edxVideoId,
html5_sources: html5Sources,
youtube_id_1_0: youtubeId,

View File

@@ -125,7 +125,10 @@ describe('cms api', () => {
videoSource: 'viDeOSouRCE',
fallbackVideos: 'FalLBacKVidEOs',
allowVideoDownloads: 'alLOwViDeodownLOads',
allowVideoSharing: 'alloWviDeOshArinG',
allowVideoSharing: {
level: 'sOMeStRInG',
value: 'alloWviDeOshArinG',
},
thumbnail: 'THUmbNaIL',
transcripts: 'traNScRiPts',
allowTranscriptDownloads: 'aLloWTRaNScriPtdoWnlOADS',
@@ -162,7 +165,7 @@ describe('cms api', () => {
metadata: {
display_name: title,
download_video: content.allowVideoDownloads,
public_access: content.allowVideoSharing,
public_access: content.allowVideoSharing.value,
edx_video_id: edxVideoId,
html5_sources: html5Sources,
youtube_id_1_0: youtubeId,