Escape xblock model field in string editor
Also add standalone spec STUD-1984
This commit is contained in:
@@ -227,6 +227,7 @@ define([
|
||||
"js/spec/views/unit_outline_spec",
|
||||
"js/spec/views/xblock_spec",
|
||||
"js/spec/views/xblock_editor_spec",
|
||||
"js/spec/views/xblock_string_field_editor_spec",
|
||||
|
||||
"js/spec/views/pages/container_spec",
|
||||
"js/spec/views/pages/container_subviews_spec",
|
||||
|
||||
@@ -95,7 +95,7 @@ define(["jquery", "underscore", "underscore.string", "js/spec_helpers/create_sin
|
||||
|
||||
describe("Editing the container", function() {
|
||||
var updatedDisplayName = 'Updated Test Container',
|
||||
expectEditCanceled, getDisplayNameWrapper;
|
||||
getDisplayNameWrapper;
|
||||
|
||||
afterEach(function() {
|
||||
edit_helpers.cancelModalIfShowing();
|
||||
@@ -105,24 +105,6 @@ define(["jquery", "underscore", "underscore.string", "js/spec_helpers/create_sin
|
||||
return containerPage.$('.wrapper-xblock-field');
|
||||
};
|
||||
|
||||
expectEditCanceled = function(test, options) {
|
||||
var initialRequests, displayNameWrapper, displayNameInput;
|
||||
renderContainerPage(test, mockContainerXBlockHtml);
|
||||
displayNameWrapper = getDisplayNameWrapper();
|
||||
initialRequests = requests.length;
|
||||
displayNameInput = edit_helpers.inlineEdit(displayNameWrapper, options.newTitle);
|
||||
if (options.pressEscape) {
|
||||
displayNameInput.simulate("keydown", { keyCode: $.simulate.keyCode.ESCAPE });
|
||||
displayNameInput.simulate("keyup", { keyCode: $.simulate.keyCode.ESCAPE });
|
||||
} else {
|
||||
displayNameInput.change();
|
||||
}
|
||||
// No requests should be made when the edit is cancelled client-side
|
||||
expect(initialRequests).toBe(requests.length);
|
||||
edit_helpers.verifyInlineEditChange(displayNameWrapper, initialDisplayName);
|
||||
expect(containerPage.model.get('display_name')).toBe(initialDisplayName);
|
||||
};
|
||||
|
||||
it('can edit itself', function() {
|
||||
var editButtons, displayNameElement;
|
||||
renderContainerPage(this, mockContainerXBlockHtml);
|
||||
@@ -173,46 +155,6 @@ define(["jquery", "underscore", "underscore.string", "js/spec_helpers/create_sin
|
||||
edit_helpers.verifyInlineEditChange(displayNameWrapper, updatedDisplayName);
|
||||
expect(containerPage.model.get('display_name')).toBe(updatedDisplayName);
|
||||
});
|
||||
|
||||
it('does not change the title when a display name update fails', function() {
|
||||
var initialRequests, displayNameInput, displayNameWrapper;
|
||||
renderContainerPage(this, mockContainerXBlockHtml);
|
||||
displayNameWrapper = getDisplayNameWrapper();
|
||||
displayNameInput = edit_helpers.inlineEdit(displayNameWrapper, updatedDisplayName);
|
||||
initialRequests = requests.length;
|
||||
displayNameInput.change();
|
||||
create_sinon.respondWithError(requests);
|
||||
// No fetch operation should occur.
|
||||
expect(initialRequests + 1).toBe(requests.length);
|
||||
edit_helpers.verifyInlineEditChange(displayNameWrapper, initialDisplayName, updatedDisplayName);
|
||||
expect(containerPage.model.get('display_name')).toBe(initialDisplayName);
|
||||
});
|
||||
|
||||
it('trims whitespace from the display name', function() {
|
||||
var displayNameInput, displayNameWrapper;
|
||||
renderContainerPage(this, mockContainerXBlockHtml);
|
||||
displayNameWrapper = getDisplayNameWrapper();
|
||||
displayNameInput = edit_helpers.inlineEdit(displayNameWrapper, updatedDisplayName + ' ');
|
||||
displayNameInput.change();
|
||||
// This is the response for the change operation.
|
||||
create_sinon.respondWithJson(requests, { });
|
||||
// This is the response for the subsequent fetch operation.
|
||||
create_sinon.respondWithJson(requests, {"display_name": updatedDisplayName});
|
||||
edit_helpers.verifyInlineEditChange(displayNameWrapper, updatedDisplayName);
|
||||
expect(containerPage.model.get('display_name')).toBe(updatedDisplayName);
|
||||
});
|
||||
|
||||
it('does not change the title when input is the empty string', function() {
|
||||
expectEditCanceled(this, {newTitle: ''});
|
||||
});
|
||||
|
||||
it('does not change the title when input is whitespace-only', function() {
|
||||
expectEditCanceled(this, {newTitle: ' '});
|
||||
});
|
||||
|
||||
it('can cancel an inline edit', function() {
|
||||
expectEditCanceled(this, {newTitle: updatedDisplayName, pressEscape: true});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Editing an xblock", function() {
|
||||
|
||||
155
cms/static/js/spec/views/xblock_string_field_editor_spec.js
Normal file
155
cms/static/js/spec/views/xblock_string_field_editor_spec.js
Normal file
@@ -0,0 +1,155 @@
|
||||
define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers", "js/spec_helpers/edit_helpers", "js/models/xblock_info", "js/views/xblock_string_field_editor"],
|
||||
function ($, create_sinon, view_helpers, edit_helpers, XBlockInfo, XBlockStringFieldEditor) {
|
||||
describe("XBlockStringFieldEditorView", function () {
|
||||
var initialDisplayName, updatedDisplayName, getXBlockInfo, getFieldEditorView;
|
||||
|
||||
getXBlockInfo = function (displayName) {
|
||||
return new XBlockInfo(
|
||||
{
|
||||
display_name: displayName,
|
||||
id: "my_xblock"
|
||||
},
|
||||
{ parse: true }
|
||||
);
|
||||
};
|
||||
|
||||
getFieldEditorView = function (xblockInfo) {
|
||||
if (xblockInfo === undefined) {
|
||||
xblockInfo = getXBlockInfo(initialDisplayName);
|
||||
}
|
||||
return new XBlockStringFieldEditor({
|
||||
model: xblockInfo,
|
||||
el: $('.wrapper-xblock-field')
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
initialDisplayName = "Default Display Name";
|
||||
updatedDisplayName = "Updated Display Name";
|
||||
view_helpers.installTemplate('xblock-string-field-editor');
|
||||
appendSetFixtures(
|
||||
'<div class="wrapper-xblock-field incontext-editor is-editable"' +
|
||||
'data-field="display_name" data-field-display-name="Display Name">' +
|
||||
'<h1 class="page-header-title xblock-field-value incontext-editor-value"><span class="title-value">' + initialDisplayName + '</span></h1>' +
|
||||
'</div>'
|
||||
);
|
||||
});
|
||||
|
||||
describe('Editing', function () {
|
||||
var expectPostedNewDisplayName, expectEditCanceled;
|
||||
|
||||
expectPostedNewDisplayName = function (requests, displayName) {
|
||||
create_sinon.expectJsonRequest(requests, 'POST', '/xblock/my_xblock', {
|
||||
metadata: {
|
||||
display_name: displayName
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
expectEditCanceled = function (test, fieldEditorView, options) {
|
||||
var requests, initialRequests, displayNameInput;
|
||||
requests = create_sinon.requests(test);
|
||||
initialRequests = requests.length;
|
||||
displayNameInput = edit_helpers.inlineEdit(fieldEditorView.$el, options.newTitle);
|
||||
if (options.pressEscape) {
|
||||
displayNameInput.simulate("keydown", { keyCode: $.simulate.keyCode.ESCAPE });
|
||||
displayNameInput.simulate("keyup", { keyCode: $.simulate.keyCode.ESCAPE });
|
||||
} else if (options.clickCancel) {
|
||||
fieldEditorView.$('button[name=cancel]').click();
|
||||
} else {
|
||||
displayNameInput.change();
|
||||
}
|
||||
// No requests should be made when the edit is cancelled client-side
|
||||
expect(initialRequests).toBe(requests.length);
|
||||
edit_helpers.verifyInlineEditChange(fieldEditorView.$el, initialDisplayName);
|
||||
expect(fieldEditorView.model.get('display_name')).toBe(initialDisplayName);
|
||||
};
|
||||
|
||||
it('can inline edit the display name', function () {
|
||||
var requests, fieldEditorView;
|
||||
requests = create_sinon.requests(this);
|
||||
fieldEditorView = getFieldEditorView().render();
|
||||
edit_helpers.inlineEdit(fieldEditorView.$el, updatedDisplayName);
|
||||
fieldEditorView.$('button[name=submit]').click();
|
||||
expectPostedNewDisplayName(requests, updatedDisplayName);
|
||||
// This is the response for the change operation.
|
||||
create_sinon.respondWithJson(requests, { });
|
||||
// This is the response for the subsequent fetch operation.
|
||||
create_sinon.respondWithJson(requests, {display_name: updatedDisplayName});
|
||||
edit_helpers.verifyInlineEditChange(fieldEditorView.$el, updatedDisplayName);
|
||||
});
|
||||
|
||||
it('does not change the title when a display name update fails', function () {
|
||||
var requests, fieldEditorView, initialRequests;
|
||||
requests = create_sinon.requests(this);
|
||||
initialRequests = requests.length;
|
||||
fieldEditorView = getFieldEditorView().render();
|
||||
edit_helpers.inlineEdit(fieldEditorView.$el, updatedDisplayName);
|
||||
fieldEditorView.$('button[name=submit]').click();
|
||||
expectPostedNewDisplayName(requests, updatedDisplayName);
|
||||
create_sinon.respondWithError(requests);
|
||||
// No fetch operation should occur.
|
||||
expect(initialRequests + 1).toBe(requests.length);
|
||||
edit_helpers.verifyInlineEditChange(fieldEditorView.$el, initialDisplayName, updatedDisplayName);
|
||||
});
|
||||
|
||||
it('trims whitespace from the display name', function () {
|
||||
var requests, fieldEditorView;
|
||||
requests = create_sinon.requests(this);
|
||||
fieldEditorView = getFieldEditorView().render();
|
||||
updatedDisplayName += ' ';
|
||||
edit_helpers.inlineEdit(fieldEditorView.$el, updatedDisplayName);
|
||||
fieldEditorView.$('button[name=submit]').click();
|
||||
expectPostedNewDisplayName(requests, updatedDisplayName.trim());
|
||||
// This is the response for the change operation.
|
||||
create_sinon.respondWithJson(requests, { });
|
||||
// This is the response for the subsequent fetch operation.
|
||||
create_sinon.respondWithJson(requests, {display_name: updatedDisplayName.trim()});
|
||||
edit_helpers.verifyInlineEditChange(fieldEditorView.$el, updatedDisplayName.trim());
|
||||
});
|
||||
|
||||
it('does not change the title when input is the empty string', function () {
|
||||
var fieldEditorView = getFieldEditorView().render();
|
||||
expectEditCanceled(this, fieldEditorView, {newTitle: ''});
|
||||
});
|
||||
|
||||
it('does not change the title when input is whitespace-only', function () {
|
||||
var fieldEditorView = getFieldEditorView().render();
|
||||
expectEditCanceled(this, fieldEditorView, {newTitle: ' '});
|
||||
});
|
||||
|
||||
it('can cancel an inline edit by pressing escape', function () {
|
||||
var fieldEditorView = getFieldEditorView().render();
|
||||
expectEditCanceled(this, fieldEditorView, {newTitle: updatedDisplayName, pressEscape: true});
|
||||
});
|
||||
|
||||
it('can cancel an inline edit by clicking cancel', function () {
|
||||
var fieldEditorView = getFieldEditorView().render();
|
||||
expectEditCanceled(this, fieldEditorView, {newTitle: updatedDisplayName, clickCancel: true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Rendering', function () {
|
||||
var expectInputMatchesModelDisplayName = function (displayName) {
|
||||
var fieldEditorView = getFieldEditorView(getXBlockInfo(displayName)).render();
|
||||
expect(fieldEditorView.$('.xblock-field-input').val()).toBe(displayName);
|
||||
};
|
||||
|
||||
it('renders single quotes in input field', function () {
|
||||
expectInputMatchesModelDisplayName('Updated \'Display Name\'');
|
||||
});
|
||||
|
||||
it('renders double quotes in input field', function () {
|
||||
expectInputMatchesModelDisplayName('Updated "Display Name"');
|
||||
});
|
||||
|
||||
it('renders open angle bracket in input field', function () {
|
||||
expectInputMatchesModelDisplayName(updatedDisplayName + '<');
|
||||
});
|
||||
|
||||
it('renders close angle bracket in input field', function () {
|
||||
expectInputMatchesModelDisplayName('>' + updatedDisplayName);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -11,7 +11,8 @@ define(["js/views/baseview", "js/views/utils/xblock_utils"],
|
||||
var XBlockStringFieldEditor = BaseView.extend({
|
||||
events: {
|
||||
'click .xblock-field-value-edit': 'showInput',
|
||||
'click button[type=submit]': 'onClickSubmit',
|
||||
'click button[name=submit]': 'onClickSubmit',
|
||||
'click button[name=cancel]': 'onClickCancel',
|
||||
'change .xblock-field-input': 'updateField',
|
||||
'focusout .xblock-field-input': 'onInputFocusLost',
|
||||
'keyup .xblock-field-input': 'handleKeyUp'
|
||||
@@ -29,7 +30,7 @@ define(["js/views/baseview", "js/views/utils/xblock_utils"],
|
||||
|
||||
render: function() {
|
||||
this.$el.append(this.template({
|
||||
value: this.model.get(this.fieldName),
|
||||
value: this.model.escape(this.fieldName),
|
||||
fieldName: this.fieldName,
|
||||
fieldDisplayName: this.fieldDisplayName
|
||||
}));
|
||||
@@ -56,6 +57,11 @@ define(["js/views/baseview", "js/views/utils/xblock_utils"],
|
||||
this.updateField();
|
||||
},
|
||||
|
||||
onClickCancel: function(event) {
|
||||
event.preventDefault();
|
||||
this.cancelInput();
|
||||
},
|
||||
|
||||
onChangeField: function() {
|
||||
var value = this.model.get(this.fieldName);
|
||||
this.getLabel().text(value);
|
||||
|
||||
Reference in New Issue
Block a user