diff --git a/common/lib/xmodule/xmodule/js/src/html/edit.js b/common/lib/xmodule/xmodule/js/src/html/edit.js index 4945b8878e..34de8437b7 100644 --- a/common/lib/xmodule/xmodule/js/src/html/edit.js +++ b/common/lib/xmodule/xmodule/js/src/html/edit.js @@ -50,6 +50,7 @@ this.saveImage = bind(this.saveImage, this); this.editImage = bind(this.editImage, this); this.setupTinyMCE = bind(this.setupTinyMCE, this); + this.cancelButton = bind(this.cancelButton, this); var tiny_mce_css_links; this.element = element; this.base_asset_url = this.element.find("#editor-tab").data('base-asset-url'); @@ -135,14 +136,16 @@ "alignleft aligncenter alignright alignjustify | " + "bullist numlist outdent indent blockquote | link unlink " + ((this.new_image_modal ? 'insertImage' : 'image') + " | code"), - block_formats: interpolate("%(paragraph)s=p;%(preformatted)s=pre;%(heading3)s=h3;%(heading4)s=h4;%(heading5)s=h5;%(heading6)s=h6", { - paragraph: gettext("Paragraph"), - preformatted: gettext("Preformatted"), - heading3: gettext("Heading 3"), - heading4: gettext("Heading 4"), - heading5: gettext("Heading 5"), - heading6: gettext("Heading 6") - }, true), + block_formats: edx.StringUtils.interpolate( + gettext("{paragraph}=p;{preformatted}=pre;{heading3}=h3;{heading4}=h4;{heading5}=h5;{heading6}=h6"), + { + paragraph: gettext("Paragraph"), + preformatted: gettext("Preformatted"), + heading3: gettext("Heading 3"), + heading4: gettext("Heading 4"), + heading5: gettext("Heading 5"), + heading6: gettext("Heading 6") + }), width: '100%', height: '400px', menubar: false, @@ -1227,6 +1230,7 @@ ed.on('EditLink', this.editLink); ed.on('ShowCodeEditor', this.showCodeEditor); ed.on('SaveCodeEditor', this.saveCodeEditor); + $(".action-cancel").on('click', this.cancelButton) this.imageModal.on('closeModal', this.closeImageModal); return this.imageModal.on('submitForm', this.editImageSubmit); @@ -1378,11 +1382,22 @@ if (text === void 0) { text = this.advanced_editor.getValue(); } + this.unbindSubmitEventFromImageEditor() return { data: text }; }; + HTMLEditingDescriptor.prototype.cancelButton = function () { + this.unbindSubmitEventFromImageEditor() + }; + + HTMLEditingDescriptor.prototype.unbindSubmitEventFromImageEditor = function () { + /* + Unbinds events on cancel/save button of image editor. + */ + if (this.imageModal) this.imageModal.off('submitForm') + }; return HTMLEditingDescriptor; })(); diff --git a/common/test/acceptance/pages/studio/html_component_editor.py b/common/test/acceptance/pages/studio/html_component_editor.py index 94e4234111..d77bc38b64 100644 --- a/common/test/acceptance/pages/studio/html_component_editor.py +++ b/common/test/acceptance/pages/studio/html_component_editor.py @@ -238,6 +238,27 @@ class HtmlXBlockEditorView(XBlockEditorView): """ click_css(self, '.action-save') + def open_image_modal(self): + """ + Clicks and in insert image button + """ + click_css(self, 'div i[class="mce-ico mce-i-image"]') + + def upload_image(self, file_name): + """ + Upload image and add description and click save to upload image via TinyMCE editor. + """ + file_input_css = "[type='file']" + + # select file input element and change visibility to add file. + self.browser.execute_script('$("{}").css("display","block");'.format(file_input_css)) + self.wait_for_element_visibility(file_input_css, "Input is visible") + self.q(css=file_input_css).results[0].send_keys(file_name) + self.wait_for_element_visibility('#imageDescription', 'Upload form is visible.') + + self.q(css='#imageDescription').results[0].send_keys('test image') + click_css(self, '.modal-footer .btn-primary') + class HTMLEditorIframe(XBlockEditorView): """ diff --git a/common/test/acceptance/tests/studio/test_studio_html_editor.py b/common/test/acceptance/tests/studio/test_studio_html_editor.py index a770d8926d..bef9262964 100644 --- a/common/test/acceptance/tests/studio/test_studio_html_editor.py +++ b/common/test/acceptance/tests/studio/test_studio_html_editor.py @@ -3,12 +3,17 @@ Acceptance tests for HTML component in studio """ from __future__ import absolute_import +import os + from common.test.acceptance.fixtures.course import XBlockFixtureDesc from common.test.acceptance.pages.studio.container import ContainerPage, XBlockWrapper from common.test.acceptance.pages.studio.html_component_editor import HTMLEditorIframe, HtmlXBlockEditorView from common.test.acceptance.pages.studio.utils import add_component, type_in_codemirror from common.test.acceptance.tests.studio.base_studio_test import ContainerBase +UPLOAD_SUFFIX = 'data/uploads/studio-uploads/' +UPLOAD_FILE_DIR = os.path.abspath(os.path.join(__file__, '../../../../', UPLOAD_SUFFIX)) + class HTMLComponentEditorTests(ContainerBase): """ @@ -365,3 +370,30 @@ class HTMLComponentEditorTests(ContainerBase): } self.html_editor.open_font_dropdown() self.assertDictContainsSubset(EXPECTED_FONTS, self.html_editor.font_dict()) + + def test_image_modal(self): + """ + Scenario: TinyMCE text editor allows to add multiple images. + + Given I have created a Blank text editor Page. + I add an image in TinyMCE text editor and hit save button. + I edit the component again. + I add another image in TinyMCE text editor and hit save button again. + Then it is expected that both images show up on page. + """ + image_file_names = [u'file-0.png', u'file-1.png'] + self._add_component('Text') + + for image in image_file_names: + image_path = os.path.join(UPLOAD_FILE_DIR, image) + self.container_page.edit() + self.html_editor.open_image_modal() + self.html_editor.upload_image(image_path) + self.html_editor.save_content() + self.html_editor.wait_for_ajax() + + self.container_page.edit() + self.html_editor.open_raw_editor() + editor_value = self.html_editor.editor_value + number_of_images = editor_value.count(u'img') + self.assertEqual(number_of_images, 2)