diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue
index 0fbeaf408b..1cebc6c074 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue
@@ -32,45 +32,80 @@
box
@focus="trackClick('Title')"
/>
-
-
-
-
-
-
-
-
- {{ $tr('noTagsFoundText', { text: tagText.trim() }) }}
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $tr('noTagsFoundText', { text: tagText.trim() }) }}
+
+
+
+
+
+
+
@@ -328,6 +363,10 @@
import SubtitlesList from '../../views/files/supplementaryLists/SubtitlesList';
import { isImportedContent, importedChannelLink } from '../../utils';
import AccessibilityOptions from './AccessibilityOptions.vue';
+ import LevelsOptions from './LevelsOptions.vue';
+ import ResourcesNeededOptions from './ResourcesNeededOptions.vue';
+ import LearningActivityOptions from './LearningActivityOptions.vue';
+
import {
getTitleValidators,
getCopyrightHolderValidators,
@@ -386,6 +425,7 @@
* - `grade_levels` (sometimes referred to as `content_levels`)
* - `learner_needs` (resources needed)
* - `accessibility_labels` (accessibility options)
+ * - `learning_activities` (learning activities)
*/
function generateNestedNodesGetterSetter(key) {
return {
@@ -416,6 +456,9 @@
ContentNodeThumbnail,
Checkbox,
AccessibilityOptions,
+ LevelsOptions,
+ ResourcesNeededOptions,
+ LearningActivityOptions,
},
mixins: [constantsTranslationMixin, metadataTranslationMixin],
props: {
@@ -495,6 +538,10 @@
role: generateGetterSetter('role_visibility'),
language: generateGetterSetter('language'),
accessibility: generateNestedNodesGetterSetter('accessibility_labels'),
+ contentLevel: generateNestedNodesGetterSetter('grade_levels'),
+ resourcesNeeded: generateNestedNodesGetterSetter('learner_needs'),
+ contentLearningActivities: generateNestedNodesGetterSetter('learning_activities'),
+
mastery_model() {
return this.getExtraFieldsValueFromNodes('mastery_model');
},
@@ -825,6 +872,18 @@
}
}
}
+
+ .basicInfoColumn {
+ display: flex;
+ /deep/ .v-input {
+ // Stretches the "Description" text area to fill the column vertically
+ align-items: stretch;
+ }
+ /deep/ .v-input__control {
+ // Makes sure that the character count does not get pushed to second column
+ flex-wrap: nowrap;
+ }
+ }
}
}
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/LearningActivityOptions.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/LearningActivityOptions.vue
new file mode 100644
index 0000000000..c9a7e33351
--- /dev/null
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/LearningActivityOptions.vue
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/LevelsOptions.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/LevelsOptions.vue
new file mode 100644
index 0000000000..d87a9f0f29
--- /dev/null
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/LevelsOptions.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/ResourcesNeededOptions.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/ResourcesNeededOptions.vue
new file mode 100644
index 0000000000..b866b1853f
--- /dev/null
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/ResourcesNeededOptions.vue
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/detailsTabView.spec.js b/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/detailsTabView.spec.js
index 50efa2b3cf..bda02c2d7e 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/detailsTabView.spec.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/detailsTabView.spec.js
@@ -61,6 +61,8 @@ describe.skip('detailsTabView', () => {
});
describe('on render', () => {
// TODO: add defaults for 'accessibility' field
+ // TODO: add defaults for 'grade_levels' field
+ // TODO: add defaults for 'learner_needs' field
it('all fields should match node field values', () => {
let keys = [
'language',
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/learningActivityOptions.spec.js b/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/learningActivityOptions.spec.js
new file mode 100644
index 0000000000..e0547bf13d
--- /dev/null
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/learningActivityOptions.spec.js
@@ -0,0 +1,57 @@
+import Vue from 'vue';
+import Vuetify from 'vuetify';
+import { shallowMount, mount } from '@vue/test-utils';
+import LearningActivityOptions from '../LearningActivityOptions.vue';
+import { LearningActivities } from 'shared/constants';
+
+Vue.use(Vuetify);
+
+function makeWrapper(value) {
+ return mount(LearningActivityOptions, {
+ propsData: {
+ value,
+ },
+ });
+}
+
+describe('LearningActivityOptions', () => {
+ it('smoke test', () => {
+ const wrapper = shallowMount(LearningActivityOptions);
+ expect(wrapper.isVueInstance()).toBe(true);
+ });
+
+ it('number of items in the dropdown should be equal to number of items available in ', () => {
+ const wrapper = shallowMount(LearningActivityOptions);
+ const numberOfDropdownItems = Object.keys(LearningActivities).length;
+ const dropdownItems = wrapper.attributes()['items'].split(',').length;
+
+ expect(dropdownItems).toBe(numberOfDropdownItems);
+ });
+
+ describe('updating state', () => {
+ it('should update learning_activity field with new values received from a parent', () => {
+ const learningActivity = ['activity_1', 'activity_2'];
+ const wrapper = makeWrapper(learningActivity);
+ const dropdown = wrapper.find({ name: 'v-select' });
+
+ expect(dropdown.props('value')).toEqual(learningActivity);
+
+ wrapper.setProps({
+ value: ['activity_4'],
+ });
+ expect(dropdown.props('value')).toEqual(['activity_4']);
+ });
+
+ it('should emit new input values', () => {
+ const learningActivity = ['activity_1', 'activity_2', 'activity_3'];
+ const wrapper = makeWrapper({});
+ const dropdown = wrapper.find({ name: 'v-select' });
+ dropdown.vm.$emit('input', learningActivity);
+
+ return Vue.nextTick().then(() => {
+ const emittedLevels = wrapper.emitted('input').pop()[0];
+ expect(emittedLevels).toEqual(learningActivity);
+ });
+ });
+ });
+});
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/levelsOptions.spec.js b/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/levelsOptions.spec.js
new file mode 100644
index 0000000000..a3541e494a
--- /dev/null
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/levelsOptions.spec.js
@@ -0,0 +1,58 @@
+import Vue from 'vue';
+import Vuetify from 'vuetify';
+import { shallowMount, mount } from '@vue/test-utils';
+import LevelsOptions from '../LevelsOptions.vue';
+import { ContentLevels } from 'shared/constants';
+
+Vue.use(Vuetify);
+
+function makeWrapper(value) {
+ return mount(LevelsOptions, {
+ propsData: {
+ value,
+ },
+ });
+}
+
+describe('LevelsOptions', () => {
+ it('smoke test', () => {
+ const wrapper = shallowMount(LevelsOptions);
+
+ expect(wrapper.isVueInstance()).toBe(true);
+ });
+
+ it('number of items in the dropdown should be equal to number of items available in ContentLevels', () => {
+ const wrapper = shallowMount(LevelsOptions);
+ const numberOfAvailableLevels = Object.keys(ContentLevels).length;
+ const dropdownItems = wrapper.attributes()['items'].split(',').length;
+
+ expect(dropdownItems).toBe(numberOfAvailableLevels);
+ });
+
+ describe('updating state', () => {
+ it('should update levels field with new values received from a parent', () => {
+ const levels = ['abc', 'gefo'];
+ const wrapper = makeWrapper(levels);
+ const dropdown = wrapper.find({ name: 'v-select' });
+
+ expect(dropdown.props('value')).toEqual(levels);
+
+ wrapper.setProps({
+ value: ['def'],
+ });
+ expect(dropdown.props('value')).toEqual(['def']);
+ });
+
+ it('should emit new input values', () => {
+ const levels = ['abc', 'gefo', '8hw'];
+ const wrapper = makeWrapper({});
+ const dropdown = wrapper.find({ name: 'v-select' });
+ dropdown.vm.$emit('input', levels);
+
+ return Vue.nextTick().then(() => {
+ const emittedLevels = wrapper.emitted('input').pop()[0];
+ expect(emittedLevels).toEqual(levels);
+ });
+ });
+ });
+});
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/resourcesNeededOptions.spec.js b/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/resourcesNeededOptions.spec.js
new file mode 100644
index 0000000000..28b6348ab0
--- /dev/null
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/__tests__/resourcesNeededOptions.spec.js
@@ -0,0 +1,66 @@
+import Vue from 'vue';
+import Vuetify from 'vuetify';
+import { shallowMount, mount } from '@vue/test-utils';
+import ResourcesNeededOptions, { updateResourcesDropdown } from '../ResourcesNeededOptions.vue';
+import { ResourcesNeededTypes } from 'shared/constants';
+
+Vue.use(Vuetify);
+
+function makeWrapper(value) {
+ return mount(ResourcesNeededOptions, {
+ propsData: {
+ value,
+ },
+ });
+}
+
+describe('ResourcesNeededOptions', () => {
+ it('smoke test', () => {
+ const wrapper = shallowMount(ResourcesNeededOptions);
+
+ expect(wrapper.isVueInstance()).toBe(true);
+ });
+
+ it('when there is a list of keys to remove from ResourcesNeededTypes, return updated map for ResourcesNeededTypes for dropdown', () => {
+ const list = ['FOR_BEGINNERS', 'INTERNET'];
+ const numberOfAvailableResources = Object.keys(ResourcesNeededTypes).length - list.length;
+ const dropdownItemsLength = Object.keys(updateResourcesDropdown(list)).length;
+
+ expect(dropdownItemsLength).toBe(numberOfAvailableResources);
+ });
+
+ it('when there are no keys to remove from ResourcesNeededTypes, dropdown should contain all resources', () => {
+ const list = [];
+ const numberOfAvailableResources = Object.keys(ResourcesNeededTypes).length - list.length;
+ const dropdownItemsLength = Object.keys(updateResourcesDropdown(list)).length;
+
+ expect(dropdownItemsLength).toBe(numberOfAvailableResources);
+ });
+
+ describe('updating state', () => {
+ it('should update resources field with new values received from a parent', () => {
+ const resourcesNeeded = ['person', 'book'];
+ const wrapper = makeWrapper(resourcesNeeded);
+ const dropdown = wrapper.find({ name: 'v-select' });
+
+ expect(dropdown.props('value')).toEqual(resourcesNeeded);
+
+ wrapper.setProps({
+ value: ['cat'],
+ });
+ expect(dropdown.props('value')).toEqual(['cat']);
+ });
+
+ it('should emit new input values', () => {
+ const resourcesNeeded = ['person', 'book', 'train'];
+ const wrapper = makeWrapper({});
+ const dropdown = wrapper.find({ name: 'v-select' });
+ dropdown.vm.$emit('input', resourcesNeeded);
+
+ return Vue.nextTick().then(() => {
+ const emittedLevels = wrapper.emitted('input').pop()[0];
+ expect(emittedLevels).toEqual(resourcesNeeded);
+ });
+ });
+ });
+});
diff --git a/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/__tests__/actions.spec.js b/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/__tests__/actions.spec.js
index 11e8c7d606..9067852d65 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/__tests__/actions.spec.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/__tests__/actions.spec.js
@@ -108,6 +108,7 @@ describe('contentNode actions', () => {
title: 'notatest',
description: 'very',
language: 'no',
+ learning_activities: { test: true },
})
.then(() => {
expect(updateSpy).toHaveBeenCalledWith(id, {
@@ -116,6 +117,7 @@ describe('contentNode actions', () => {
language: 'no',
changed: true,
complete: false,
+ learning_activities: { test: true },
});
updateSpy.mockRestore();
});
diff --git a/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/actions.js b/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/actions.js
index 9120c8e488..c608db3528 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/actions.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/actions.js
@@ -178,6 +178,9 @@ export function createContentNode(context, { parent, kind, ...payload }) {
...contentDefaults,
role_visibility: contentDefaults.role_visibility || RolesNames.LEARNER,
accessibility_labels: {},
+ grade_levels: {},
+ learner_needs: {},
+ learning_activities: {},
...payload,
};
@@ -211,6 +214,9 @@ function generateContentNodeData({
prerequisite = NOVALUE,
complete = NOVALUE,
accessibility_labels = NOVALUE,
+ grade_levels = NOVALUE,
+ learner_needs = NOVALUE,
+ learning_activities = NOVALUE,
} = {}) {
const contentNodeData = {};
if (title !== NOVALUE) {
@@ -252,6 +258,15 @@ function generateContentNodeData({
if (accessibility_labels !== NOVALUE) {
contentNodeData.accessibility_labels = accessibility_labels;
}
+ if (grade_levels !== NOVALUE) {
+ contentNodeData.grade_levels = grade_levels;
+ }
+ if (learner_needs !== NOVALUE) {
+ contentNodeData.learner_needs = learner_needs;
+ }
+ if (learning_activities !== NOVALUE) {
+ contentNodeData.learning_activities = learning_activities;
+ }
if (extra_fields !== NOVALUE) {
contentNodeData.extra_fields = contentNodeData.extra_fields || {};
diff --git a/contentcuration/contentcuration/frontend/shared/constants.js b/contentcuration/contentcuration/frontend/shared/constants.js
index 04a76f2059..c7af961529 100644
--- a/contentcuration/contentcuration/frontend/shared/constants.js
+++ b/contentcuration/contentcuration/frontend/shared/constants.js
@@ -5,6 +5,8 @@ export { default as CompletionCriteriaModels } from 'kolibri-constants/Completio
export { default as ContentLevel } from 'kolibri-constants/labels/Levels';
export { default as Categories } from 'kolibri-constants/labels/Subjects';
export { default as AccessibilityCategories } from 'kolibri-constants/labels/AccessibilityCategories';
+export { default as ContentLevels } from 'kolibri-constants/labels/Levels';
+export { default as ResourcesNeededTypes } from 'kolibri-constants/labels/Needs';
export const ContentDefaults = {
author: 'author',
@@ -159,6 +161,7 @@ export const ValidationErrors = {
INVALID_NUMBER_OF_CORRECT_ANSWERS: 'INVALID_NUMBER_OF_CORRECT_ANSWERS',
NO_VALID_PRIMARY_FILES: 'NO_VALID_PRIMARY_FILES',
INVALID_COMPLETION_CRITERIA_MODEL: 'INVALID_COMPLETION_CRITERIA_MODEL',
+ LEARNING_ACTIVITY_REQUIRED: 'LEARNING_ACTIVITY_REQUIRED',
...fileErrors,
};
diff --git a/contentcuration/contentcuration/frontend/shared/mixins.js b/contentcuration/contentcuration/frontend/shared/mixins.js
index 60e657accf..b7bedbaa60 100644
--- a/contentcuration/contentcuration/frontend/shared/mixins.js
+++ b/contentcuration/contentcuration/frontend/shared/mixins.js
@@ -669,13 +669,18 @@ const nonconformingKeys = {
PEOPLE: 'ToUseWithTeachersAndPeers',
PAPER_PENCIL: 'ToUseWithPaperAndPencil',
INTERNET: 'NeedsInternet',
- MATERIALS: 'NeedsMaterials',
FOR_BEGINNERS: 'ForBeginners',
digitalLiteracy: 'digitialLiteracy',
BASIC_SKILLS: 'allLevelsBasicSkills',
FOUNDATIONS: 'basicSkills',
- toolsAndSoftwareTraining: 'softwareToolsAndTraining',
foundationsLogicAndCriticalThinking: 'logicAndCriticalThinking',
+
+ /*
+ * TODO: the following are in ResourcesNeededTypes map from le-utils, but not in Kolibri,
+ * and should be tracked in the issue - https://github.com/learningequality/kolibri/issues/9245
+ */
+ OTHER_SUPPLIES: 'NeedsMaterials',
+ SPECIAL_SOFTWARE: 'softwareToolsAndTraining',
};
/**
diff --git a/contentcuration/contentcuration/frontend/shared/translator.js b/contentcuration/contentcuration/frontend/shared/translator.js
index a30ed48453..5188bc5f9c 100644
--- a/contentcuration/contentcuration/frontend/shared/translator.js
+++ b/contentcuration/contentcuration/frontend/shared/translator.js
@@ -14,6 +14,7 @@ const MESSAGES = {
masteryModelNGtZero: 'Must be at least 1',
masteryModelNWholeNumber: 'Must be a whole number',
confirmLogout: 'Changes you made may not be saved. Are you sure you want to leave this page?',
+ learningActivityRequired: 'Learning activity is required',
};
export default createTranslator('sharedVue', MESSAGES);
diff --git a/contentcuration/contentcuration/frontend/shared/utils/validation.js b/contentcuration/contentcuration/frontend/shared/utils/validation.js
index d89b416990..8ae07297e1 100644
--- a/contentcuration/contentcuration/frontend/shared/utils/validation.js
+++ b/contentcuration/contentcuration/frontend/shared/utils/validation.js
@@ -94,6 +94,10 @@ function _getMasteryModel(node) {
return node.extra_fields;
}
+function _getLearningActivity(node) {
+ return Object.keys(node.learning_activities);
+}
+
function _getErrorMsg(error) {
const messages = {
[ValidationErrors.TITLE_REQUIRED]: translator.$tr('titleRequired'),
@@ -108,6 +112,7 @@ function _getErrorMsg(error) {
[ValidationErrors.MASTERY_MODEL_N_REQUIRED]: translator.$tr('masteryModelNRequired'),
[ValidationErrors.MASTERY_MODEL_N_WHOLE_NUMBER]: translator.$tr('masteryModelNWholeNumber'),
[ValidationErrors.MASTERY_MODEL_N_GT_ZERO]: translator.$tr('masteryModelNGtZero'),
+ [ValidationErrors.LEARNING_ACTIVITY_REQUIRED]: translator.$tr('learningActivityRequired'),
};
return messages[error];
@@ -134,6 +139,10 @@ export function getCopyrightHolderValidators() {
return [value => Boolean(value && value.trim()) || ValidationErrors.COPYRIGHT_HOLDER_REQUIRED];
}
+export function getLearningActivityValidators() {
+ return [value => Boolean(value.length) || ValidationErrors.LEARNING_ACTIVITY_REQUIRED];
+}
+
export function getLicenseDescriptionValidators() {
return [value => Boolean(value && value.trim()) || ValidationErrors.LICENSE_DESCRIPTION_REQUIRED];
}
@@ -184,6 +193,13 @@ export function getNodeCopyrightHolderErrors(node) {
.filter(value => value !== true);
}
+export function getNodeLearningActivityErrors(node) {
+ const learningActivity = _getLearningActivity(node);
+ return getLearningActivityValidators()
+ .map(validator => validator(learningActivity))
+ .filter(value => value !== true);
+}
+
export function getNodeLicenseDescriptionErrors(node) {
const license = _getLicense(node);
if (!license || !license.is_custom) {
@@ -251,6 +267,14 @@ export function getNodeDetailsErrors(node) {
}
}
+ // learning activity is a required field for resources
+ if (node.kind !== ContentKindsNames.TOPIC) {
+ const learningActivityErrors = getNodeLearningActivityErrors(node);
+ if (learningActivityErrors.length) {
+ errors = errors.concat(learningActivityErrors);
+ }
+ }
+
// mastery is required on exercises
if (node.kind === ContentKindsNames.EXERCISE) {
const masteryModelErrors = getNodeMasteryModelErrors(node);
@@ -267,7 +291,6 @@ export function getNodeDetailsErrors(node) {
errors = errors.concat(masteryModelNErrors);
}
}
-
return errors;
}
diff --git a/contentcuration/contentcuration/frontend/shared/utils/validation.spec.js b/contentcuration/contentcuration/frontend/shared/utils/validation.spec.js
index 539426b741..9c2d4dbe24 100644
--- a/contentcuration/contentcuration/frontend/shared/utils/validation.spec.js
+++ b/contentcuration/contentcuration/frontend/shared/utils/validation.spec.js
@@ -18,6 +18,7 @@ import {
sanitizeAssessmentItemHints,
sanitizeAssessmentItem,
getAssessmentItemErrors,
+ getNodeLearningActivityErrors,
} from './validation';
import { MasteryModelsNames } from 'shared/leUtils/MasteryModels';
import { ContentKindsNames } from 'shared/leUtils/ContentKinds';
@@ -175,6 +176,23 @@ describe('channelEdit utils', () => {
});
});
+ describe('getNodeLearningActivityErrors', () => {
+ it(`returns an error for an empty learning activity input`, () => {
+ const node = {
+ learning_activities: {},
+ };
+ expect(getNodeLearningActivityErrors(node)).toEqual([
+ ValidationErrors.LEARNING_ACTIVITY_REQUIRED,
+ ]);
+ });
+ it('returns no errors when learning activity is specified', () => {
+ const node = {
+ learning_activities: { test: true },
+ };
+ expect(getNodeLearningActivityErrors(node)).toEqual([]);
+ });
+ });
+
describe('getNodeMasteryModelErrors', () => {
it('returns an error for an empty mastery model', () => {
const node = { extra_fields: null };
@@ -347,6 +365,7 @@ describe('channelEdit utils', () => {
title: 'Exercise',
kind: ContentKindsNames.EXERCISE,
license: { id: 8 },
+ learning_activities: { test: true },
extra_fields: {
mastery_model: MasteryModelsNames.DO_ALL,
options: {
@@ -457,6 +476,7 @@ describe('channelEdit utils', () => {
title: 'A node',
license: { id: 8 },
kind,
+ learning_activities: { test: true },
extra_fields: {
options: {
completion_criteria: {
@@ -548,6 +568,7 @@ describe('channelEdit utils', () => {
title: '',
kind: 'document',
license: 8,
+ learning_activities: { test: true },
})
).toEqual([ValidationErrors.TITLE_REQUIRED]);
});
@@ -558,6 +579,7 @@ describe('channelEdit utils', () => {
title: 'Title',
kind: 'document',
license: null,
+ learning_activities: { test: true },
},
[ValidationErrors.LICENSE_REQUIRED],
],
@@ -566,6 +588,7 @@ describe('channelEdit utils', () => {
title: 'Title',
kind: 'document',
license: 8,
+ learning_activities: { test: true },
},
[],
],
@@ -575,6 +598,7 @@ describe('channelEdit utils', () => {
title: 'Title',
kind: 'topic',
license: null,
+ learning_activities: { test: true },
},
[],
],
@@ -584,6 +608,7 @@ describe('channelEdit utils', () => {
title: 'Title',
freeze_authoring_data: true,
license: null,
+ learning_activities: { test: true },
},
[],
],
@@ -597,6 +622,7 @@ describe('channelEdit utils', () => {
{
title: 'Title',
license: 1,
+ learning_activities: { test: true },
},
[ValidationErrors.COPYRIGHT_HOLDER_REQUIRED],
],
@@ -605,6 +631,7 @@ describe('channelEdit utils', () => {
title: 'Title',
license: 1,
copyright_holder: 'Copyright holder',
+ learning_activities: { test: true },
},
[],
],
@@ -619,6 +646,7 @@ describe('channelEdit utils', () => {
title: 'Title',
license: 9,
copyright_holder: 'Copyright holder',
+ learning_activities: { test: true },
},
[ValidationErrors.LICENSE_DESCRIPTION_REQUIRED],
],
@@ -628,6 +656,7 @@ describe('channelEdit utils', () => {
license: 9,
copyright_holder: 'Copyright holder',
license_description: 'My custom license',
+ learning_activities: { test: true },
},
[],
],
@@ -641,6 +670,7 @@ describe('channelEdit utils', () => {
title: 'Title',
kind: 'exercise',
license: 8,
+ learning_activities: { test: true },
},
[ValidationErrors.MASTERY_MODEL_REQUIRED],
],
@@ -649,6 +679,7 @@ describe('channelEdit utils', () => {
title: 'Title',
kind: 'exercise',
license: 8,
+ learning_activities: { test: true },
extra_fields: {
mastery_model: 'do_all',
},
@@ -660,6 +691,7 @@ describe('channelEdit utils', () => {
title: 'Title',
kind: 'exercise',
license: 8,
+ learning_activities: { test: true },
extra_fields: {
mastery_model: 'm_of_n',
m: 3,
@@ -677,6 +709,7 @@ describe('channelEdit utils', () => {
title: 'Title',
kind: 'exercise',
license: 8,
+ learning_activities: { test: true },
extra_fields: {
mastery_model: 'm_of_n',
m: 3,
@@ -690,6 +723,7 @@ describe('channelEdit utils', () => {
title: 'Title',
kind: 'exercise',
license: 8,
+ learning_activities: { test: true },
extra_fields: {
mastery_model: 'm_of_n',
m: 2,