Merge pull request #19594 from edx/awais/lett_2_bc_html_editor_II
Remaining bokchoy tests for HTML editor(II)
This commit is contained in:
@@ -206,6 +206,18 @@ class ContainerPage(PageObject, HelpMixin):
|
||||
"""
|
||||
return self.q(css='.wrapper-xblock .level-element .header-details').text
|
||||
|
||||
@property
|
||||
def content_html(self):
|
||||
"""
|
||||
Gets the html of HTML module
|
||||
Returns:
|
||||
list: A list containing inner HTMl
|
||||
"""
|
||||
self.wait_for_element_visibility('.xmodule_HtmlModule', 'Xblock content is visible')
|
||||
html = self.q(css='.xmodule_HtmlModule').html
|
||||
html = html[0].strip()
|
||||
return html
|
||||
|
||||
@property
|
||||
def is_staff_locked(self):
|
||||
""" Returns True if staff lock is currently enabled, False otherwise """
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
from common.test.acceptance.pages.studio.utils import type_in_codemirror
|
||||
from xblock_editor import XBlockEditorView
|
||||
"""
|
||||
HTML component editor in studio
|
||||
"""
|
||||
from common.test.acceptance.pages.studio.utils import type_in_codemirror, get_codemirror_value
|
||||
from common.test.acceptance.pages.studio.xblock_editor import XBlockEditorView
|
||||
from common.test.acceptance.pages.common.utils import click_css
|
||||
|
||||
|
||||
@@ -12,6 +15,62 @@ class HtmlXBlockEditorView(XBlockEditorView):
|
||||
settings_tab = '.editor-modes .settings-button'
|
||||
save_settings_button = '.action-save'
|
||||
|
||||
@property
|
||||
def toolbar_dropdown_titles(self):
|
||||
"""
|
||||
Returns the titles of dropdowns present on the toolbar
|
||||
"""
|
||||
return self.q(css='.mce-listbox').text
|
||||
|
||||
@property
|
||||
def toolbar_button_titles(self):
|
||||
"""
|
||||
Returns the titles of the buttons present on the toolbar
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.q(css='.mce-ico').attrs('class')
|
||||
|
||||
@property
|
||||
def fonts(self):
|
||||
"""
|
||||
Available fonts in the font dropdown
|
||||
Returns:
|
||||
(list): A list of font names
|
||||
"""
|
||||
return self.q(css='.mce-text').text
|
||||
|
||||
@property
|
||||
def font_families(self):
|
||||
"""
|
||||
Available font families against each font
|
||||
Returns:
|
||||
(list): A list of font families
|
||||
"""
|
||||
return self.q(css='.mce-text').attrs('style')
|
||||
|
||||
def open_font_dropdown(self):
|
||||
"""
|
||||
Clicks and waits for font dropdown to open
|
||||
"""
|
||||
self.q(css='#mce_2-open').first.click()
|
||||
self.wait_for_element_visibility('.mce-floatpanel', 'Dropdown is Visible')
|
||||
|
||||
def font_dict(self):
|
||||
"""
|
||||
Creates a dictionary with font labels and font families
|
||||
Returns:
|
||||
font_dict(dict): A dictionary of font labels as keys and font families as values
|
||||
"""
|
||||
font_labels = self.fonts
|
||||
font_families = self.font_families
|
||||
for index, font in enumerate(font_families):
|
||||
font = font.replace('font-family: ', '').replace(';', '')
|
||||
font_families[index] = font.split(',')
|
||||
font_families[index] = [x.lstrip() for x in font_families[index]]
|
||||
font_dict = dict(zip(font_labels, font_families))
|
||||
return font_dict
|
||||
|
||||
def set_content_and_save(self, content, raw=False):
|
||||
"""Types content into the html component and presses Save.
|
||||
|
||||
@@ -74,6 +133,13 @@ class HtmlXBlockEditorView(XBlockEditorView):
|
||||
"""
|
||||
click_css(self, self.save_settings_button)
|
||||
|
||||
def open_raw_editor(self):
|
||||
"""
|
||||
Clicks and waits for raw editor to open
|
||||
"""
|
||||
self.q(css='[aria-label="Edit HTML"]').click()
|
||||
self.wait_for_element_visibility('.mce-title', 'Wait for CodeMirror editor')
|
||||
|
||||
def open_link_plugin(self):
|
||||
"""
|
||||
Opens up the link plugin on editor
|
||||
@@ -95,6 +161,13 @@ class HtmlXBlockEditorView(XBlockEditorView):
|
||||
"""
|
||||
return self.q(css="#tinymce>p>a").attrs('href')[0]
|
||||
|
||||
@property
|
||||
def editor_value(self):
|
||||
"""
|
||||
Returns codemirror value from raw HTMl editor
|
||||
"""
|
||||
return get_codemirror_value(self, 0)
|
||||
|
||||
def switch_to_iframe(self):
|
||||
"""
|
||||
Switches to the editor iframe
|
||||
@@ -109,6 +182,23 @@ class HtmlXBlockEditorView(XBlockEditorView):
|
||||
self.open_link_plugin()
|
||||
return self.browser.execute_script('return $(".mce-textbox").val();')
|
||||
|
||||
def set_text_and_select(self, text):
|
||||
"""
|
||||
Sets and selects text from html editor
|
||||
"""
|
||||
script = """
|
||||
var editor = tinyMCE.activeEditor;
|
||||
editor.setContent(arguments[0]);
|
||||
editor.selection.select(editor.dom.select('p')[0]);"""
|
||||
self.browser.execute_script(script, str(text))
|
||||
self.wait_for_ajax()
|
||||
|
||||
def click_code_toolbar_button(self):
|
||||
"""
|
||||
Clicks on the code plugin on the toolbar
|
||||
"""
|
||||
self.q(css='.mce-i-none').first.click()
|
||||
|
||||
def get_default_settings(self):
|
||||
"""
|
||||
Returns default display name and editor
|
||||
@@ -138,6 +228,12 @@ class HtmlXBlockEditorView(XBlockEditorView):
|
||||
"""
|
||||
click_css(self, '.save-button')
|
||||
|
||||
def save_content(self):
|
||||
"""
|
||||
Click save button
|
||||
"""
|
||||
click_css(self, '.action-save')
|
||||
|
||||
|
||||
class HTMLEditorIframe(XBlockEditorView):
|
||||
"""
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Acceptance tests for HTML component in studio
|
||||
"""
|
||||
from common.test.acceptance.pages.studio.utils import type_in_codemirror
|
||||
from common.test.acceptance.tests.studio.base_studio_test import ContainerBase
|
||||
from common.test.acceptance.fixtures.course import XBlockFixtureDesc
|
||||
from common.test.acceptance.pages.studio.container import ContainerPage, XBlockWrapper
|
||||
@@ -8,7 +9,7 @@ from common.test.acceptance.pages.studio.utils import add_component
|
||||
from common.test.acceptance.pages.studio.html_component_editor import HtmlXBlockEditorView, HTMLEditorIframe
|
||||
|
||||
|
||||
class HTMLComponentEditor(ContainerBase):
|
||||
class HTMLComponentEditorTests(ContainerBase):
|
||||
"""
|
||||
Feature: CMS.Component Adding
|
||||
As a course author, I want to be able to add and edit HTML component
|
||||
@@ -17,17 +18,13 @@ class HTMLComponentEditor(ContainerBase):
|
||||
"""
|
||||
Create a course with a section, subsection, and unit to which to add the component.
|
||||
"""
|
||||
super(HTMLComponentEditor, self).setUp(is_staff=is_staff)
|
||||
self.component = 'Text'
|
||||
super(HTMLComponentEditorTests, self).setUp(is_staff=is_staff)
|
||||
self.unit = self.go_to_unit_page()
|
||||
self.container_page = ContainerPage(self.browser, None)
|
||||
self.xblock_wrapper = XBlockWrapper(self.browser, None)
|
||||
# Add HTML component
|
||||
add_component(self.container_page, 'html', self.component)
|
||||
self.component = self.unit.xblocks[1]
|
||||
self.container_page.edit()
|
||||
self.html_editor = HtmlXBlockEditorView(self.browser, self.component.locator)
|
||||
self.iframe = HTMLEditorIframe(self.browser, self.component.locator)
|
||||
self.component = None
|
||||
self.html_editor = None
|
||||
self.iframe = None
|
||||
|
||||
def populate_course_fixture(self, course_fixture):
|
||||
"""
|
||||
@@ -41,6 +38,29 @@ class HTMLComponentEditor(ContainerBase):
|
||||
)
|
||||
)
|
||||
|
||||
def _add_content(self, content):
|
||||
"""
|
||||
Set and save content in editor and assert its presence in container page's html
|
||||
|
||||
Args:
|
||||
content(str): Verifiable content
|
||||
"""
|
||||
self.html_editor.set_raw_content(content)
|
||||
self.html_editor.save_content()
|
||||
self.container_page.wait_for_page()
|
||||
|
||||
def _add_component(self, sub_type):
|
||||
"""
|
||||
Add sub-type of HTML component in studio
|
||||
|
||||
Args:
|
||||
sub_type(str): Sub-type of HTML component
|
||||
"""
|
||||
add_component(self.container_page, 'html', sub_type)
|
||||
self.component = self.unit.xblocks[1]
|
||||
self.html_editor = HtmlXBlockEditorView(self.browser, self.component.locator)
|
||||
self.iframe = HTMLEditorIframe(self.browser, self.component.locator)
|
||||
|
||||
def test_user_can_view_metadata(self):
|
||||
"""
|
||||
Scenario: User can view metadata
|
||||
@@ -48,6 +68,10 @@ class HTMLComponentEditor(ContainerBase):
|
||||
And I edit and select Settings
|
||||
Then I see the HTML component settings
|
||||
"""
|
||||
|
||||
# Add HTML Text type component
|
||||
self._add_component('Text')
|
||||
self.container_page.edit()
|
||||
self.html_editor.open_settings_tab()
|
||||
display_name_value = self.html_editor.get_default_settings()[0]
|
||||
display_name_key = self.html_editor.keys[0]
|
||||
@@ -72,6 +96,9 @@ class HTMLComponentEditor(ContainerBase):
|
||||
Then I can modify the display name
|
||||
And my display name change is persisted on save
|
||||
"""
|
||||
# Add HTML Text type component
|
||||
self._add_component('Text')
|
||||
self.container_page.edit()
|
||||
self.html_editor.open_settings_tab()
|
||||
self.html_editor.set_field_val('Display Name', 'New Name')
|
||||
self.html_editor.save_settings()
|
||||
@@ -88,6 +115,10 @@ class HTMLComponentEditor(ContainerBase):
|
||||
And the link is shown as "/static/image.jpg" in the Link Plugin
|
||||
"""
|
||||
static_link = '/static/image.jpg'
|
||||
|
||||
# Add HTML Text type component
|
||||
self._add_component('Text')
|
||||
self.container_page.edit()
|
||||
self.html_editor.open_link_plugin()
|
||||
self.html_editor.save_static_link(static_link)
|
||||
self.html_editor.switch_to_iframe()
|
||||
@@ -100,3 +131,234 @@ class HTMLComponentEditor(ContainerBase):
|
||||
static_link,
|
||||
"URL in the link plugin is different"
|
||||
)
|
||||
|
||||
def test_tinymce_and_codemirror_preserve_style_tags(self):
|
||||
"""
|
||||
Scenario: TinyMCE and CodeMirror preserve style tags
|
||||
Given I have created a Blank HTML Page
|
||||
When I edit the page
|
||||
And type "<p class='title'>pages</p><style><!-- .title { color: red; } --></style>" in the code editor and
|
||||
press OK
|
||||
And I save the page
|
||||
Then the page text contains:
|
||||
""
|
||||
<p class="title">pages</p>
|
||||
<style><!--
|
||||
.title { color: red; }
|
||||
--></style>
|
||||
""
|
||||
"""
|
||||
content = '<p class="title">pages</p><style><!-- .title { color: red; } --></style>'
|
||||
|
||||
# Add HTML Text type component
|
||||
self._add_component('Text')
|
||||
self.container_page.edit()
|
||||
self._add_content(content)
|
||||
html = self.container_page.content_html
|
||||
self.assertIn(content, html)
|
||||
|
||||
def test_tinymce_and_codemirror_preserve_span_tags(self):
|
||||
"""
|
||||
Scenario: TinyMCE and CodeMirror preserve span tags
|
||||
Given I have created a Blank HTML Page
|
||||
When I edit the page
|
||||
And type "<span>Test</span>" in the code editor and press OK
|
||||
And I save the page
|
||||
Then the page text contains:
|
||||
""
|
||||
<span>Test</span>
|
||||
""
|
||||
"""
|
||||
content = "<span>Test</span>"
|
||||
|
||||
# Add HTML Text type component
|
||||
self._add_component('Text')
|
||||
self.container_page.edit()
|
||||
self._add_content(content)
|
||||
html = self.container_page.content_html
|
||||
self.assertIn(content, html)
|
||||
|
||||
def test_tinymce_and_codemirror_preserve_math_tags(self):
|
||||
"""
|
||||
Scenario: TinyMCE and CodeMirror preserve math tags
|
||||
Given I have created a Blank HTML Page
|
||||
When I edit the page
|
||||
And type "<math><msup><mi>x</mi><mn>2</mn></msup></math>" in the code editor and press OK
|
||||
And I save the page
|
||||
Then the page text contains:
|
||||
""
|
||||
<math><msup><mi>x</mi><mn>2</mn></msup></math>
|
||||
""
|
||||
"""
|
||||
content = "<math><msup><mi>x</mi><mn>2</mn></msup></math>"
|
||||
|
||||
# Add HTML Text type component
|
||||
self._add_component('Text')
|
||||
self.container_page.edit()
|
||||
self._add_content(content)
|
||||
html = self.container_page.content_html
|
||||
self.assertIn(content, html)
|
||||
|
||||
def test_code_format_toolbar_wraps_text_with_code_tags(self):
|
||||
"""
|
||||
Scenario: Code format toolbar button wraps text with code tags
|
||||
Given I have created a Blank HTML Page
|
||||
When I edit the page
|
||||
And I set the text to "display as code" and I select the text
|
||||
And I save the page
|
||||
Then the page text contains:
|
||||
""
|
||||
<p><code>display as code</code></p>
|
||||
""
|
||||
"""
|
||||
# Add HTML Text type component
|
||||
self._add_component('Text')
|
||||
self.container_page.edit()
|
||||
self.html_editor.set_text_and_select("display as code")
|
||||
self.html_editor.click_code_toolbar_button()
|
||||
self.html_editor.save_content()
|
||||
html = self.container_page.content_html
|
||||
self.assertIn(html, '<p><code>display as code</code></p>')
|
||||
|
||||
def test_raw_html_component_does_not_change_text(self):
|
||||
"""
|
||||
Scenario: Raw HTML component does not change text
|
||||
Given I have created a raw HTML component
|
||||
When I edit the page
|
||||
And type "<li>zzzz<ol> " into the Raw Editor
|
||||
And I save the page
|
||||
Then the page text contains:
|
||||
""
|
||||
<li>zzzz<ol>
|
||||
""
|
||||
And I edit the page
|
||||
Then the Raw Editor contains exactly:
|
||||
""
|
||||
<li>zzzz<ol>
|
||||
""
|
||||
"""
|
||||
content = "<li>zzzz</li>"
|
||||
|
||||
# Add Raw HTML type component
|
||||
self._add_component('Raw HTML')
|
||||
self.container_page.edit()
|
||||
|
||||
# Set content in tinymce editor
|
||||
type_in_codemirror(self.html_editor, 0, content)
|
||||
self.html_editor.save_content()
|
||||
|
||||
# The HTML of the content added through tinymce editor
|
||||
html = self.container_page.content_html
|
||||
# The text content should be present with its tag preserved
|
||||
self.assertIn(content, html)
|
||||
|
||||
self.container_page.edit()
|
||||
editor_value = self.html_editor.editor_value
|
||||
# The tinymce editor value should not be different from the content added in the start
|
||||
self.assertEqual(content, editor_value)
|
||||
|
||||
def test_tinymce_toolbar_buttons_are_as_expected(self):
|
||||
"""
|
||||
Scenario: TinyMCE toolbar buttons are as expected
|
||||
Given I have created a Blank HTML Page
|
||||
When I edit the page
|
||||
Then the expected toolbar buttons are displayed
|
||||
"""
|
||||
# Add HTML Text type component
|
||||
self._add_component('Text')
|
||||
self.container_page.edit()
|
||||
|
||||
expected_buttons = [
|
||||
u'bold',
|
||||
u'italic',
|
||||
u'underline',
|
||||
u'forecolor',
|
||||
# This is our custom "code style" button, which uses an image instead of a class.
|
||||
u'none',
|
||||
u'alignleft',
|
||||
u'aligncenter',
|
||||
u'alignright',
|
||||
u'alignjustify',
|
||||
u'bullist',
|
||||
u'numlist',
|
||||
u'outdent',
|
||||
u'indent',
|
||||
u'blockquote',
|
||||
u'link',
|
||||
u'unlink',
|
||||
u'image'
|
||||
]
|
||||
toolbar_dropdowns = self.html_editor.toolbar_dropdown_titles
|
||||
# The toolbar is divided in two sections: drop-downs and all other formatting buttons
|
||||
# The assertions under asserts for the drop-downs
|
||||
self.assertEqual(len(toolbar_dropdowns), 2)
|
||||
self.assertEqual(['Paragraph', 'Font Family'], toolbar_dropdowns)
|
||||
|
||||
toolbar_buttons = self.html_editor.toolbar_button_titles
|
||||
# The assertions under asserts for all the remaining formatting buttons
|
||||
self.assertEqual(len(toolbar_buttons), len(expected_buttons))
|
||||
|
||||
for index, button in enumerate(expected_buttons):
|
||||
class_name = toolbar_buttons[index]
|
||||
self.assertEqual("mce-ico mce-i-" + button, class_name)
|
||||
|
||||
def test_static_links_converted(self):
|
||||
"""
|
||||
Scenario: Static links are converted when switching between code editor and WYSIWYG views
|
||||
Given I have created a Blank HTML Page
|
||||
When I edit the page
|
||||
And type "<img src="/static/image.jpg">" in the code editor and press OK
|
||||
Then the src link is rewritten to the asset link /asset-v1:(course_id)+type@asset+block/image.jpg
|
||||
And the code editor displays "<p><img src="/static/image.jpg" /></p>"
|
||||
"""
|
||||
value = '<img src="/static/image.jpg">'
|
||||
|
||||
# Add HTML Text type component
|
||||
self._add_component('Text')
|
||||
self.container_page.edit()
|
||||
self.html_editor.set_raw_content(value)
|
||||
self.html_editor.save_content()
|
||||
html = self.container_page.content_html
|
||||
src = "/asset-v1:{}+type@asset+block/image.jpg".format(self.course_id.strip('course-v1:'))
|
||||
self.assertIn(src, html)
|
||||
self.container_page.edit()
|
||||
self.html_editor.open_raw_editor()
|
||||
editor_value = self.html_editor.editor_value
|
||||
self.assertEqual(value, editor_value)
|
||||
|
||||
def test_font_selection_dropdown(self):
|
||||
"""
|
||||
Scenario: Font selection dropdown contains Default font and tinyMCE builtin fonts
|
||||
Given I have created a Blank HTML Page
|
||||
When I edit the page
|
||||
And I click font selection dropdown
|
||||
Then I should see a list of available fonts
|
||||
And "Default" fonts should be available
|
||||
And all standard tinyMCE fonts should be available
|
||||
"""
|
||||
# Add HTML Text type component
|
||||
self._add_component('Text')
|
||||
self.container_page.edit()
|
||||
EXPECTED_FONTS = {
|
||||
u"Default": [u'"Open Sans"', u'Verdana', u'Arial', u'Helvetica', u'sans-serif'],
|
||||
u"Andale Mono": [u'andale mono', u'times'],
|
||||
u"Arial": [u'arial', u'helvetica', u'sans-serif'],
|
||||
u"Arial Black": [u'arial black', u'avant garde'],
|
||||
u"Book Antiqua": [u'book antiqua', u'palatino'],
|
||||
u"Comic Sans MS": [u'comic sans ms', u'sans-serif'],
|
||||
u"Courier New": [u'courier new', u'courier'],
|
||||
u"Georgia": [u'georgia', u'palatino'],
|
||||
u"Helvetica": [u'helvetica'],
|
||||
u"Impact": [u'impact', u'chicago'],
|
||||
u"Symbol": [u'symbol'],
|
||||
u"Tahoma": [u'tahoma', u'arial', u'helvetica', u'sans-serif'],
|
||||
u"Terminal": [u'terminal', u'monaco'],
|
||||
u"Times New Roman": [u'times new roman', u'times'],
|
||||
u"Trebuchet MS": [u'trebuchet ms', u'geneva'],
|
||||
u"Verdana": [u'verdana', u'geneva'],
|
||||
# tinyMCE does not set font-family on dropdown span for these two fonts
|
||||
u"Webdings": [u""], # webdings
|
||||
u"Wingdings": [u""] # wingdings
|
||||
}
|
||||
self.html_editor.open_font_dropdown()
|
||||
self.assertDictContainsSubset(EXPECTED_FONTS, self.html_editor.font_dict())
|
||||
|
||||
Reference in New Issue
Block a user