diff --git a/ui/jstests/test_manage_taxonomies.jsx b/ui/jstests/test_manage_taxonomies.jsx
index 5f43eac8..7e079741 100644
--- a/ui/jstests/test_manage_taxonomies.jsx
+++ b/ui/jstests/test_manage_taxonomies.jsx
@@ -36,6 +36,37 @@ define(['QUnit', 'jquery', 'manage_taxonomies', 'react',
}
]
};
+ var learningResourceTypes = {
+ "count": 8,
+ "next": null,
+ "previous": null,
+ "results": [
+ {
+ "name": "course"
+ },
+ {
+ "name": "chapter"
+ },
+ {
+ "name": "sequential"
+ },
+ {
+ "name": "vertical"
+ },
+ {
+ "name": "html"
+ },
+ {
+ "name": "video"
+ },
+ {
+ "name": "discussion"
+ },
+ {
+ "name": "problem"
+ }
+ ]
+ };
/**
* Common assertions for Add terms, use in term and taxonomy
* components tests
@@ -63,39 +94,9 @@ define(['QUnit', 'jquery', 'manage_taxonomies', 'react',
assert.equal(itemList.length, 3);
return devItems;
}
+
QUnit.module('Test taxonomies panel', {
beforeEach: function() {
- var learningResourceTypes = {
- "count": 8,
- "next": null,
- "previous": null,
- "results": [
- {
- "name": "course"
- },
- {
- "name": "chapter"
- },
- {
- "name": "sequential"
- },
- {
- "name": "vertical"
- },
- {
- "name": "html"
- },
- {
- "name": "video"
- },
- {
- "name": "discussion"
- },
- {
- "name": "problem"
- }
- ]
- };
var listOfVocabularies = {
"count": 1,
"next": null,
@@ -126,11 +127,6 @@ define(['QUnit', 'jquery', 'manage_taxonomies', 'react',
"label": "test",
"weight": 1
};
- var duplicateVocabularyResponse = {
- "non_field_errors":[
- "The fields repository, name must make a unique set."
- ]
- };
TestUtils.setup();
TestUtils.initMockjax({
url: "/api/v1/repositories/repo/vocabularies/easy/terms/",
@@ -154,19 +150,6 @@ define(['QUnit', 'jquery', 'manage_taxonomies', 'react',
dataType: 'json',
type: "POST"
});
- TestUtils.initMockjax({
- url: "/api/v1/repositories/repo2/vocabularies/",
- type: "POST",
- status: 400
- });
- TestUtils.initMockjax({
- url: "/api/v1/repositories/repo3/vocabularies/",
- contentType: "application/json; charset=utf-8",
- responseText: duplicateVocabularyResponse,
- dataType: 'json',
- type: "POST",
- status: 400
- });
TestUtils.initMockjax({
url: "/api/v1/repositories/repo/vocabularies/",
contentType: "application/json; charset=utf-8",
@@ -476,12 +459,20 @@ define(['QUnit', 'jquery', 'manage_taxonomies', 'react',
var addTermCalled = 0;
var deleteVocabularyCalled = 0;
+ var editVocabularyButton;
+ var editVocabularyCalled = 0;
+
var addTerm = function() {
addTermCalled += 1;
};
var deleteVocabulary = function() {
deleteVocabularyCalled += 1;
};
+
+ var editVocabulary = function() {
+ editVocabularyCalled += 1;
+ };
+
var reportMessage = function() {};
var refreshCount = 0;
@@ -502,6 +493,12 @@ define(['QUnit', 'jquery', 'manage_taxonomies', 'react',
component,
'panel-body'
);
+ var buttons = React.addons.TestUtils.
+ scryRenderedDOMComponentsWithClass(
+ component,
+ 'fa-pencil'
+ );
+ editVocabularyButton = buttons[0];
var itemList = React.addons.TestUtils.
scryRenderedDOMComponentsWithTag(
devItems,
@@ -542,12 +539,26 @@ define(['QUnit', 'jquery', 'manage_taxonomies', 'react',
component.state.newTermLabel
);
React.addons.TestUtils.Simulate.keyUp(textbox, {key: "x"});
- assert.equal(addTermCalled, 0);
+ component.forceUpdate(function () {
+ assert.equal(addTermCalled, 0);
- React.addons.TestUtils.Simulate.keyUp(textbox, {key: "Enter"});
- waitForAjax(1, function () {
- assert.equal(addTermCalled, 1);
- done();
+ React.addons.TestUtils.Simulate.click(editVocabularyButton);
+ component.forceUpdate(function () {
+ assert.equal(editVocabularyCalled, 1);
+ node = React.findDOMNode(component);
+ var textbox = $(node).find("input")[0];
+ assert.equal(
+ 'test12',
+ component.state.newTermLabel
+ );
+ React.addons.TestUtils.Simulate.keyUp(
+ textbox, {key: "Enter"}
+ );
+ waitForAjax(1, function () {
+ assert.equal(addTermCalled, 1);
+ done();
+ });
+ });
});
});
});
@@ -556,6 +567,7 @@ define(['QUnit', 'jquery', 'manage_taxonomies', 'react',
React.addons.TestUtils.
renderIntoDocument(
+ );
+ }
+ );
+
+ QUnit.test('Assert that update vocabulary works',
+ function(assert) {
+ assert.ok(AddVocabulary, "class object not found");
+ var done = assert.async();
+ var removePanelVisibility = false;
+ var vocabularyUpdateResponse = {
+ "id": 1,
+ "slug": "difficulty",
+ "name": "TestB",
+ "description": "TestB",
+ "vocabulary_type": "f",
+ "required": false,
+ "weight": 2147483647,
+ "learning_resource_types": [
+ "course", "chapter"
+ ],
+ "multi_terms": true,
+ };
+
+ var refreshFromAPI = function() {};
+ var editVocabulary = function() {};
+ var closeTaxonomyPanel = function() {};
+ var updateParent = function(vocab) {
+ assert.equal(vocabulary.id, vocab.id);
+ assert.notEqual(vocabulary.name, vocab.name);
+ assert.notEqual(vocabulary.description, vocab.description);
+ assert.notEqual(
+ vocabulary.learning_resource_types.length,
+ vocab.learning_resource_types.length
+ );
+ };
+
+ //update vocabulary
+ TestUtils.initMockjax({
+ url: "/api/v1/repositories/repo4/vocabularies/" +
+ vocabulary.slug + "/",
+ contentType: "application/json; charset=utf-8",
+ responseText: vocabularyUpdateResponse,
+ dataType: 'json',
+ type: "PATCH"
+ });
+
+ var afterMount = function(component) {
+ var formNode = React.addons.TestUtils.
+ findRenderedDOMComponentWithClass(
+ component,
+ 'form-horizontal'
+ );
+ assert.ok(formNode);
+ //test form submission
+ var inputNodes = React.addons.TestUtils.
+ scryRenderedDOMComponentsWithTag(
+ formNode,
+ 'input'
+ );
+ var buttonNodes = React.addons.TestUtils.
+ scryRenderedDOMComponentsWithTag(
+ formNode,
+ 'button'
+ );
+ var updateButton = buttonNodes[0];
+ var inputVocabularyName = inputNodes[0];
+ var inputVocabularyDesc = inputNodes[1];
+ var checkboxCourse = inputNodes[2];
+
React.addons.TestUtils.Simulate.change(
- inputVocabularyDesc, {target: {value: "TestB"}}
+ inputVocabularyName, {target: {value: "TestB"}}
+ );
+ component.forceUpdate(function() {
+ assert.equal(component.state.name, "TestB");
+ React.addons.TestUtils.Simulate.change(
+ inputVocabularyDesc, {target: {value: "TestB"}}
+ );
+ component.forceUpdate(function () {
+ assert.equal(component.state.description, "TestB");
+ React.addons.TestUtils.Simulate.change(
+ checkboxCourse,
+ {target: {value: 'course', checked: true}}
+ );
+ component.forceUpdate(function () {
+ React.addons.TestUtils.Simulate.click(updateButton);
+ component.forceUpdate(function() {
+ waitForAjax(1, function() {
+ //testing state is reset
+ assert.equal(
+ component.state.name,
+ ''
+ );
+ assert.equal(
+ component.state.description,
+ ''
+ );
+ assert.equal(
+ component.state.vocabularyType,
+ 'm'
+ );
+ assert.equal(
+ component.state.learningResourceTypes.length,
+ 0
+ );
+ assert.equal(component.state.multiTerms, false);
+ done();
+ });
+ });
+ });
+ });
+ });
+ };
+ React.addons.TestUtils.
+ renderIntoDocument(
+
);
+ }
+ );
+
+ QUnit.test('Assert that update vocabulary fail with name ' +
+ 'or description blank',
+ function(assert) {
+ assert.ok(AddVocabulary, "class object not found");
+ var removePanelVisibility = false;
+ var refreshFromAPI = function() {};
+ var closeTaxonomyPanel = function () {
+ };
+ var editVocabulary = function () {
+ };
+ var updateParent = function () {
+ };
+ var afterMount = function(component) {
+ var formNode = React.addons.TestUtils.
+ findRenderedDOMComponentWithClass(
+ component,
+ 'form-horizontal'
+ );
+ assert.ok(formNode);
+ //test form submission
+ var inputNodes = React.addons.TestUtils.
+ scryRenderedDOMComponentsWithTag(
+ formNode,
+ 'input'
+ );
+ var buttonNodes = React.addons.TestUtils.
+ scryRenderedDOMComponentsWithTag(
+ formNode,
+ 'button'
+ );
+ var updateButton = buttonNodes[0];
+ var inputVocabularyName = inputNodes[0];
+ var inputVocabularyDesc = inputNodes[1];
+
React.addons.TestUtils.Simulate.change(
- checkboxCourse,
- {target: {value: 'course', checked: true}}
+ inputVocabularyName, {target: {value: ""}}
);
- React.addons.TestUtils.Simulate.submit(formNode);
- waitForAjax(1, function() {
- // Error is caused by a 400 status code
- assert.deepEqual(
- component.state.message,
- {
- error: 'A Vocabulary named "" already exists.' +
- ' Please choose a different name.'
- }
+ component.forceUpdate(function() {
+ assert.equal(component.state.name, "");
+ React.addons.TestUtils.Simulate.change(
+ inputVocabularyDesc, {target: {value: "TestB"}}
+ );
+ component.forceUpdate(function () {
+ assert.equal(component.state.description, "TestB");
+ React.addons.TestUtils.Simulate.click(updateButton);
+ component.forceUpdate(function () {
+ assert.deepEqual(
+ component.state.message,
+ {error: 'Please enter vocabulary name.'}
+ );
+ React.addons.TestUtils.Simulate.change(
+ inputVocabularyName, {target: {value: "Name of colours"}}
+ );
+ component.forceUpdate(function () {
+ assert.equal(component.state.name, "Name of colours");
+ React.addons.TestUtils.Simulate.change(
+ inputVocabularyDesc, {target: {value: ""}}
+ );
+ component.forceUpdate(function () {
+ assert.equal(component.state.description, "");
+ React.addons.TestUtils.Simulate.click(updateButton);
+ component.forceUpdate(function () {
+ assert.deepEqual(
+ component.state.message,
+ {error: 'Please enter vocabulary description.'}
+ );
+ });
+ });
+ });
+ });
+ });
+ });
+ };
+ React.addons.TestUtils.
+ renderIntoDocument(
+
+ );
+ }
+ );
+
+ QUnit.test('Assert that ajax call fail in update vocabulary',
+ function(assert) {
+ assert.ok(AddVocabulary, "class object not found");
+ var done = assert.async();
+ var closeTaxonomyPanel = function() {};
+ var editVocabulary = function() {};
+ var updateParent = function() {};
+ var refreshCount = 0;
+ var refreshFromAPI = function() {
+ refreshCount++;
+ };
+
+ TestUtils.initMockjax({
+ url: '/api/v1/repositories/repo' +
+ '/vocabularies/' + vocabulary.slug + "/",
+ type: "PATCH",
+ status: 400
+ });
+
+ var removePanelVisibility = false;
+ var afterMount = function(component) {
+ var formNode = React.addons.TestUtils.
+ findRenderedDOMComponentWithClass(
+ component,
+ 'form-horizontal'
+ );
+ assert.ok(formNode);
+ //test form submission
+ var inputNodes = React.addons.TestUtils.
+ scryRenderedDOMComponentsWithTag(
+ formNode,
+ 'input'
+ );
+ var buttonNodes = React.addons.TestUtils.
+ scryRenderedDOMComponentsWithTag(
+ formNode,
+ 'button'
+ );
+ var updateButton = buttonNodes[0];
+ var inputVocabularyName = inputNodes[0];
+ var inputVocabularyDesc = inputNodes[1];
+ var checkboxChapter = inputNodes[2];
+
+ React.addons.TestUtils.Simulate.change(
+ inputVocabularyName, {target: {value: "TestB"}}
+ );
+ component.forceUpdate(function() {
+ assert.equal(component.state.name, "TestB");
+ React.addons.TestUtils.Simulate.change(
+ inputVocabularyDesc, {target: {value: "TestB"}}
);
assert.equal(refreshCount, 0);
- done();
+ component.forceUpdate(function() {
+ assert.equal(component.state.description, "TestB");
+ React.addons.TestUtils.Simulate.change(
+ checkboxChapter,
+ {target: {value: 'course', checked: true}}
+ );
+ component.forceUpdate(function() {
+ React.addons.TestUtils.Simulate.click(updateButton);
+ component.forceUpdate(function() {
+ waitForAjax(1, function() {
+ // Error is caused by a 400 status code
+ assert.deepEqual(
+ component.state.message,
+ {error: "There was a problem with updating the Vocabulary."}
+ );
+ done();
+ });
+ });
+ });
+ });
});
};
React.addons.TestUtils.
renderIntoDocument(
+ );
+ }
+ );
+
+ QUnit.test('Assert that update vocabulary works in TaxonomyComponent',
+ function(assert) {
+ assert.ok(TaxonomyComponent, "class object not found");
+ var vocabularyUpdateResponse = {
+ "id": 1,
+ "slug": "difficulty",
+ "name": "TestA",
+ "description": "TestA",
+ "vocabulary_type": "f",
+ "required": false,
+ "weight": 2147483647,
+ "learning_resource_types": [
+ "course", "chapter"
+ ],
+ "multi_terms": true,
+ };
+ TestUtils.initMockjax({
+ url: "/api/v1/repositories/repo/vocabularies/" +
+ vocabulary.slug + "/",
+ contentType: "application/json; charset=utf-8",
+ responseText: vocabularyUpdateResponse,
+ dataType: 'json',
+ type: "PATCH"
+ });
+ var removePanelVisibility = false;
+ var setVocabularyActionTabName = function() {};
+ var closeTaxonomyPanel = function() {};
+ var refreshCount = 0;
+ var refreshFromAPI = function() {
+ refreshCount++;
+ };
+
+ var done = assert.async();
+ var afterMount = function(component) {
+ waitForAjax(2, function() {
+ assert.equal(
+ component.state.vocabularies.length,
+ 1
+ );
+ var buttons = React.addons.TestUtils.
+ scryRenderedDOMComponentsWithClass(
+ component,
+ 'fa-pencil'
+ );
+ var editVocabularyButton = buttons[0];
+ React.addons.TestUtils.Simulate.click(editVocabularyButton);
+ component.forceUpdate(function() {
+ var formNode = React.addons.TestUtils.
+ findRenderedDOMComponentWithClass(
+ component,
+ 'form-horizontal'
+ );
+ assert.ok(formNode);
+ //test form submission
+ var inputNodes = React.addons.TestUtils.
+ scryRenderedDOMComponentsWithTag(
+ formNode,
+ 'input'
+ );
+ var buttonForm = React.addons.TestUtils.
+ scryRenderedDOMComponentsWithTag(
+ formNode,
+ 'button'
+ );
+ var updateButton = buttonForm[0];
+ var inputVocabularyName = inputNodes[0];
+ var inputVocabularyDesc = inputNodes[1];
+ React.addons.TestUtils.Simulate.change(
+ inputVocabularyName, {target: {value: "TestA"}}
+ );
+ component.forceUpdate(function() {
+ React.addons.TestUtils.Simulate.change(
+ inputVocabularyDesc, {target: {value: "TestA"}}
+ );
+ component.forceUpdate(function() {
+ React.addons.TestUtils.Simulate.click(updateButton);
+ component.forceUpdate(function() {
+ waitForAjax(1, function() {
+ assert.equal(
+ component.state.vocabularies.length,
+ 1
+ );
+ assert.equal(
+ component.state.vocabularies[0].vocabulary.name,
+ vocabularyUpdateResponse.name
+ );
+ assert.equal(
+ component.state.vocabularies[0].vocabulary.id,
+ vocabularyUpdateResponse.id
+ );
+ assert.equal(
+ component.state.vocabularies[0].vocabulary.description,
+ vocabularyUpdateResponse.description
+ );
+ done();
+ });
+ });
+ });
+ });
+ });
+ });
+ };
+ React.addons.TestUtils.renderIntoDocument
+ (
+
+ );
+ }
+ );
+
+ QUnit.test('Assert that taxonomy panel close',
+ function(assert) {
+ assert.ok(TaxonomyComponent, "class object not found");
+ var done = assert.async();
+ var removePanelVisibility = true;
+ var closeTaxonomyPanelCount = 0;
+ var setVocabularyActionTabNameCount = 0;
+ var closeTaxonomyPanel = function() {
+ closeTaxonomyPanelCount += 1;
+ };
+ var setVocabularyActionTabName = function() {
+ setVocabularyActionTabNameCount += 1;
+ };
+ var refreshCount = 0;
+ var refreshFromAPI = function() {
+ refreshCount++;
+ };
+ var afterMount = function(component) {
+ assert.equal(closeTaxonomyPanelCount, 1);
+ waitForAjax(2, function() {
+ assert.equal(
+ component.state.vocabularies.length,
+ 1
+ );
+ var buttons = React.addons.TestUtils.
+ scryRenderedDOMComponentsWithClass(
+ component,
+ 'fa-pencil'
+ );
+ var editVocabularyButton = buttons[0];
+ React.addons.TestUtils.Simulate.click(editVocabularyButton);
+ component.forceUpdate(function() {
+ var formNode = React.addons.TestUtils.
+ findRenderedDOMComponentWithClass(
+ component,
+ 'form-horizontal'
+ );
+ var messageNode = React.addons.TestUtils.
+ findRenderedDOMComponentWithClass(
+ component,
+ 'alert-dismissible'
+ );
+ assert.ok(formNode);
+ //test form submission
+ var inputNodes = React.addons.TestUtils.
+ scryRenderedDOMComponentsWithTag(
+ formNode,
+ 'input'
+ );
+ var inputVocabularyName = inputNodes[0];
+ var inputVocabularyDesc = inputNodes[1];
+ // Test for user made changes on edit mode and try exit
+ // panel without saving changes. Assert that he will get a warning
+ // message to save changes or cancel edit mode.
+ React.addons.TestUtils.Simulate.change(
+ inputVocabularyName, {target: {value: "TestA"}}
+ );
+ component.forceUpdate(function() {
+ React.addons.TestUtils.Simulate.change(
+ inputVocabularyDesc, {target: {value: "TestA"}}
+ );
+ component.forceUpdate(function() {
+ assert.equal(
+ $(React.findDOMNode(messageNode)).html(),
+ 'Please save changes or cancel before exit.'
+ );
+ done();
+ });
+ });
+ });
+ });
+ };
+ React.addons.TestUtils.renderIntoDocument
+ (
+
);
@@ -1539,7 +2124,10 @@ define(['QUnit', 'jquery', 'manage_taxonomies', 'react',
function(assert) {
var container = document.createElement("div");
assert.equal(0, $(container).find("input").size());
- ManageTaxonomies.loader("repo", function() {}, function() {}, container);
+ ManageTaxonomies.loader(
+ "repo", container, function() {}, function() {}, function() {},
+ function() {}, function() {}
+ );
assert.equal(5, $(container).find("input").size());
}
);
diff --git a/ui/static/ui/css/mit-lore.css b/ui/static/ui/css/mit-lore.css
index 9601c055..e15d0ccc 100644
--- a/ui/static/ui/css/mit-lore.css
+++ b/ui/static/ui/css/mit-lore.css
@@ -591,3 +591,11 @@ input.repo-page-status {
-ms-user-select: none;
user-select: none;
}
+
+.hide-vocab-cancel {
+ display: none;
+}
+
+.show-vocab-cancel {
+ display: inline;
+}
diff --git a/ui/static/ui/js/listing.js b/ui/static/ui/js/listing.js
index bddcf23a..6d49b506 100644
--- a/ui/static/ui/js/listing.js
+++ b/ui/static/ui/js/listing.js
@@ -82,6 +82,30 @@ define('listing',
});
}
+ var hideTaxonomyPanel = function() {
+ $('.cd-panel-2').removeClass('is-visible');
+ };
+
+ var setVocabularyActionTabName = function(isEditMode) {
+ var actionTabName = 'Add Vocabulary';
+ if (isEditMode) {
+ actionTabName = 'Edit Vocabulary';
+ }
+ $('.add-edit-vocabulary-tab').html(actionTabName);
+ };
+
+ var loadManageTaxonomies = function (isHideTaxonomyPanel) {
+ ManageTaxonomies.loader(
+ repoSlug,
+ $('#taxonomy-component')[0],
+ isHideTaxonomyPanel,
+ hideTaxonomyPanel,
+ showConfirmationDialog,
+ setVocabularyActionTabName,
+ refreshFromAPI
+ );
+ };
+
$('[data-toggle=popover]').popover();
//Close panels on escape keypress
$(document).keyup(function(event) {
@@ -90,7 +114,7 @@ define('listing',
$('.cd-panel').removeClass('is-visible');
}
if ($('.cd-panel-2').hasClass('is-visible')) {
- $('.cd-panel-2').removeClass('is-visible');
+ loadManageTaxonomies(true);
}
if ($('.cd-panel-exports').hasClass('is-visible')) {
$('.cd-panel-exports').removeClass('is-visible');
@@ -114,6 +138,7 @@ define('listing',
//open the lateral panel
$('.btn-taxonomies').on('click', function (event) {
event.preventDefault();
+ loadManageTaxonomies(false);
$('.cd-panel-2').addClass('is-visible');
});
@@ -121,7 +146,7 @@ define('listing',
$('.cd-panel-2').on('click', function (event) {
if ($(event.target).is('.cd-panel-2') ||
$(event.target).is('.cd-panel-close')) {
- $('.cd-panel-2').removeClass('is-visible');
+ loadManageTaxonomies(true);
event.preventDefault();
}
});
@@ -493,11 +518,6 @@ define('listing',
// Initial refresh to populate page.
refreshFromAPI();
- ManageTaxonomies.loader(
- repoSlug,
- refreshFromAPI,
- showConfirmationDialog,
- $('#taxonomy-component')[0]);
};
return {
diff --git a/ui/static/ui/js/manage_taxonomies.jsx b/ui/static/ui/js/manage_taxonomies.jsx
index 7c627223..7f0cd418 100644
--- a/ui/static/ui/js/manage_taxonomies.jsx
+++ b/ui/static/ui/js/manage_taxonomies.jsx
@@ -140,7 +140,7 @@ define('manage_taxonomies', ['react', 'lodash', 'jquery', 'utils', 'bootstrap'],