diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad68842..887e4a1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,16 +3,22 @@
> All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+## [[v0.7.0]](https://github.com/springload/draftjs_exporter/releases/tag/v0.7.0) - 2017-02-15
+
+### Added
+
+- Add support for decorators thanks to @su27 (#16, #17).
+
## [[v0.6.2]](https://github.com/springload/draftjs_exporter/releases/tag/v0.6.2) - 2017-01-18
### Added
-- Add profiling tooling thanks to [@su27](https://github.com/su27) [#31](https://github.com/springload/draftjs_exporter/issues/31).
-- Add more common entity types in constants (#34)
+- Add profiling tooling thanks to @su27 (#31).
+- Add more common entity types in constants (#34).
### Fixed
-- Stop mutating entity data when rendering entities (#36)
+- Stop mutating entity data when rendering entities (#36).
## [[v0.6.1]](https://github.com/springload/draftjs_exporter/releases/tag/v0.6.1) - 2016-12-21
diff --git a/README.rst b/README.rst
index f2a901c..e4e09cc 100644
--- a/README.rst
+++ b/README.rst
@@ -106,6 +106,7 @@ This project adheres to `Semantic Versioning`_, and measures performance and `co
* Convert line breaks to ``
`` elements.
* Define any attribute in the block map – custom class names for elements.
* React-like API to create custom entity decorators.
+* React-like API to create composite decorators for text.
* Automatic conversion of entity data to HTML attributes (int & boolean to string, ``className`` to ``class``).
* Wrapped blocks (``
`` elements go inside ````).
* Nested wrapped blocks (multiple list levels, arbitrary type and depth).
diff --git a/draftjs_exporter/composite_decorators.py b/draftjs_exporter/composite_decorators.py
new file mode 100644
index 0000000..2927a93
--- /dev/null
+++ b/draftjs_exporter/composite_decorators.py
@@ -0,0 +1,49 @@
+from __future__ import absolute_import, unicode_literals
+
+from operator import itemgetter
+from draftjs_exporter.dom import DOM
+
+
+def get_decorations(decorators, text):
+ occupied = {}
+ decorations = []
+
+ for decorator in decorators:
+ for match in decorator.SEARCH_RE.finditer(text):
+ begin, end = match.span()
+ if not any(occupied.get(i) for i in range(begin, end)):
+ for i in range(begin, end):
+ occupied[i] = 1
+ decorations.append((begin, end, match, decorator))
+
+ decorations.sort(key=itemgetter(0))
+
+ return decorations
+
+
+def apply_decorators(decorators, text, block_type):
+ decorations = get_decorations(decorators, text)
+
+ pointer = 0
+ for begin, end, match, decorator in decorations:
+ if pointer < begin:
+ yield DOM.create_text_node(text[pointer:begin])
+
+ yield decorator.render({
+ 'children': match.group(0),
+ 'match': match,
+ 'block_type': block_type,
+ })
+ pointer = end
+
+ if pointer < len(text):
+ yield DOM.create_text_node(text[pointer:])
+
+
+def render_decorators(decorators, text, block_type):
+ decorated_node = DOM.create_document_fragment()
+
+ for decorated_child in apply_decorators(decorators, text, block_type):
+ DOM.append_child(decorated_node, decorated_child)
+
+ return decorated_node
diff --git a/draftjs_exporter/entity_state.py b/draftjs_exporter/entity_state.py
index 4a57353..34ee8b4 100644
--- a/draftjs_exporter/entity_state.py
+++ b/draftjs_exporter/entity_state.py
@@ -21,6 +21,9 @@ def apply(self, command):
elif command.name == 'stop_entity':
self.stop_command(command)
+ def is_empty(self):
+ return not self.entity_stack
+
def get_entity_details(self, command):
key = str(command.data)
details = self.entity_map.get(key)
@@ -53,15 +56,15 @@ def stop_command(self, command):
self.entity_stack.pop()
def render_entitities(self, root_element, style_node):
- stack_start = DOM.create_document_fragment()
- DOM.append_child(root_element, stack_start)
-
- element_stack = [stack_start]
- new_element = stack_start
-
- if len(self.entity_stack) == 0:
+ if self.is_empty():
DOM.append_child(root_element, style_node)
else:
+ stack_start = DOM.create_document_fragment()
+ DOM.append_child(root_element, stack_start)
+
+ element_stack = [stack_start]
+ new_element = stack_start
+
for entity_details in self.entity_stack:
decorator = self.get_entity_decorator(entity_details)
props = entity_details.copy()
@@ -71,5 +74,3 @@ def render_entitities(self, root_element, style_node):
new_element = decorator.render(props)
DOM.append_child(element_stack[-1], new_element)
element_stack.append(new_element)
-
- return new_element
diff --git a/draftjs_exporter/html.py b/draftjs_exporter/html.py
index b1e7ba9..9e8309c 100644
--- a/draftjs_exporter/html.py
+++ b/draftjs_exporter/html.py
@@ -1,7 +1,9 @@
from __future__ import absolute_import, unicode_literals
from draftjs_exporter.command import Command
+from draftjs_exporter.composite_decorators import render_decorators
from draftjs_exporter.defaults import BLOCK_MAP, STYLE_MAP
+from draftjs_exporter.dom import DOM
from draftjs_exporter.entity_state import EntityState
from draftjs_exporter.style_state import StyleState
from draftjs_exporter.wrapper_state import WrapperState
@@ -17,6 +19,7 @@ def __init__(self, config=None):
config = {}
self.entity_decorators = config.get('entity_decorators', {})
+ self.composite_decorators = config.get('composite_decorators', [])
self.block_map = config.get('block_map', BLOCK_MAP)
self.style_map = config.get('style_map', STYLE_MAP)
@@ -44,8 +47,14 @@ def render_block(self, block, entity_map):
entity_state.apply(command)
self.style_state.apply(command)
- style_node = self.style_state.create_node(text)
- entity_state.render_entitities(element, style_node)
+ # Decorators are not rendered inside entities.
+ if entity_state.is_empty() and len(self.composite_decorators) > 0:
+ decorated_node = render_decorators(self.composite_decorators, text, block.get('type', None))
+ else:
+ decorated_node = DOM.create_text_node(text)
+
+ styled_node = self.style_state.render_styles(decorated_node)
+ entity_state.render_entitities(element, styled_node)
def build_command_groups(self, block):
"""
diff --git a/draftjs_exporter/style_state.py b/draftjs_exporter/style_state.py
index f708a99..f89ecc6 100644
--- a/draftjs_exporter/style_state.py
+++ b/draftjs_exporter/style_state.py
@@ -32,7 +32,7 @@ def apply(self, command):
elif command.name == 'stop_inline_style':
self.styles.remove(command.data)
- def is_unstyled(self):
+ def is_empty(self):
return not self.styles
def get_style_tags(self):
@@ -55,28 +55,11 @@ def get_style_value(self):
return ''.join(sorted(rules))
- def replace_linebreaks(self, text):
- lines = text.split('\n')
-
- if len(lines) > 1:
- wrapper = DOM.create_document_fragment()
-
- DOM.append_child(wrapper, DOM.create_text_node(lines[0]))
-
- for l in lines[1:]:
- DOM.append_child(wrapper, DOM.create_element('br'))
- DOM.append_child(wrapper, DOM.create_text_node(l))
- else:
- wrapper = DOM.create_text_node(text)
-
- return wrapper
-
- def create_node(self, text):
- text_lines = self.replace_linebreaks(text)
-
- if self.is_unstyled():
- node = text_lines
+ def render_styles(self, text_node):
+ if self.is_empty():
+ node = text_node
else:
+ style = self.get_style_value()
tags = self.get_style_tags()
node = DOM.create_element(tags[0])
child = node
@@ -88,10 +71,9 @@ def create_node(self, text):
DOM.append_child(child, new_child)
child = new_child
- style_value = self.get_style_value()
- if style_value:
- DOM.set_attribute(child, 'style', style_value)
+ if style:
+ DOM.set_attribute(child, 'style', style)
- DOM.append_child(child, text_lines)
+ DOM.append_child(child, text_node)
return node
diff --git a/example.py b/example.py
index 7a6c0f1..d733bc5 100644
--- a/example.py
+++ b/example.py
@@ -3,6 +3,7 @@
from __future__ import absolute_import, unicode_literals
import codecs
+import re
from draftjs_exporter.constants import BLOCK_TYPES, ENTITY_TYPES
from draftjs_exporter.defaults import BLOCK_MAP, STYLE_MAP
@@ -35,12 +36,44 @@ def render(self, props):
return DOM.create_element('a', {'href': href}, props['children'])
+class BR:
+ """
+ Replace line breaks (\n) with br tags.
+ """
+ SEARCH_RE = re.compile(r'\n')
+
+ def render(self, props):
+ # Do not process matches inside code blocks.
+ if props['block_type'] == BLOCK_TYPES.CODE:
+ return props['children']
+
+ return DOM.create_element('br')
+
+
+class Hashtag:
+ """
+ Wrap hashtags in spans with a specific class.
+ """
+ SEARCH_RE = re.compile(r'#\w+')
+
+ def render(self, props):
+ # Do not process matches inside code blocks.
+ if props['block_type'] == BLOCK_TYPES.CODE:
+ return props['children']
+
+ return DOM.create_element('span', {'class': 'hashtag'}, props['children'])
+
+
config = {
'entity_decorators': {
ENTITY_TYPES.LINK: Link(),
ENTITY_TYPES.IMAGE: Image(),
ENTITY_TYPES.HORIZONTAL_RULE: HR(),
},
+ 'composite_decorators': [
+ BR(),
+ Hashtag(),
+ ],
# Extend/override the default block map.
'block_map': dict(BLOCK_MAP, **{
BLOCK_TYPES.HEADER_TWO: {
@@ -104,7 +137,7 @@ def render(self, props):
},
{
'key': '5384u',
- 'text': 'Everyone 🍺 Springload applies the best principles of UX to their work.',
+ 'text': 'Everyone 🍺 Springload applies the best #principles of UX to their work.',
'type': 'blockquote',
'depth': 0,
'inlineStyleRanges': [],
@@ -112,7 +145,7 @@ def render(self, props):
},
{
'key': 'eelkd',
- 'text': 'The design decisions we make building tools and services for your customers are based on empathy for what your customers need.',
+ 'text': 'The design decisions we make building #tools and #services for your customers are based on empathy for what your customers need.',
'type': 'unstyled',
'depth': 0,
'inlineStyleRanges': [],
@@ -186,8 +219,8 @@ def render(self, props):
'inlineStyleRanges': [],
'entityRanges': [
{
- 'offset': 0,
- 'length': 71,
+ 'offset': 53,
+ 'length': 12,
'key': 1
}
]
diff --git a/tests/test_composite_decorators.py b/tests/test_composite_decorators.py
new file mode 100644
index 0000000..d229197
--- /dev/null
+++ b/tests/test_composite_decorators.py
@@ -0,0 +1,144 @@
+
+from __future__ import absolute_import, unicode_literals
+
+import re
+import unittest
+
+from draftjs_exporter.composite_decorators import render_decorators
+from draftjs_exporter.constants import BLOCK_TYPES
+from draftjs_exporter.dom import DOM
+
+
+class Linkify:
+ """
+ Wrap plain URLs with link tags.
+ See http://pythex.org/?regex=(http%3A%2F%2F%7Chttps%3A%2F%2F%7Cwww%5C.)(%5Ba-zA-Z0-9%5C.%5C-%25%2F%5C%3F%26_%3D%5C%2B%23%3A~!%2C%5C%27%5C*%5C%5E%24%5D%2B)&test_string=search%20http%3A%2F%2Fa.us%20or%20https%3A%2F%2Fyahoo.com%20or%20www.google.com%20for%20%23github%20and%20%23facebook&ignorecase=0&multiline=0&dotall=0&verbose=0
+ for an example.
+ """
+ SEARCH_RE = re.compile(r'(http://|https://|www\.)([a-zA-Z0-9\.\-%/\?&_=\+#:~!,\'\*\^$]+)')
+
+ def render(self, props):
+ match = props.get('match')
+ protocol = match.group(1)
+ url = match.group(2)
+ href = protocol + url
+
+ if props['block_type'] == BLOCK_TYPES.CODE:
+ return href
+
+ link_props = {
+ 'href': href,
+ }
+
+ if href.startswith('www'):
+ link_props['href'] = 'http://' + href
+
+ return DOM.create_element('a', link_props, href)
+
+
+class Hashtag:
+ """
+ Wrap hashtags in spans with a specific class.
+ """
+ SEARCH_RE = re.compile(r'#\w+')
+
+ def render(self, props):
+ # Do not process matches inside code blocks.
+ if props['block_type'] == BLOCK_TYPES.CODE:
+ return props['children']
+
+ return DOM.create_element('span', {'class': 'hashtag'}, props['children'])
+
+
+class BR:
+ """
+ Replace line breaks (\n) with br tags.
+ """
+ SEARCH_RE = re.compile(r'\n')
+
+ def render(self, props):
+ # Do not process matches inside code blocks.
+ if props['block_type'] == BLOCK_TYPES.CODE:
+ return props['children']
+
+ return DOM.create_element('br')
+
+
+class TestLinkify(unittest.TestCase):
+ def test_init(self):
+ self.assertIsInstance(Linkify(), Linkify)
+
+ def test_render(self):
+ match = next(Linkify.SEARCH_RE.finditer('test https://www.example.com'))
+
+ self.assertEqual(DOM.render(DOM.create_element(Linkify, {
+ 'block_type': BLOCK_TYPES.UNSTYLED,
+ 'match': match,
+ 'children': match.group(0),
+ })), 'https://www.example.com')
+
+ def test_render_www(self):
+ match = next(Linkify.SEARCH_RE.finditer('test www.example.com'))
+
+ self.assertEqual(DOM.render(DOM.create_element(Linkify, {
+ 'block_type': BLOCK_TYPES.UNSTYLED,
+ 'match': match,
+ 'children': match.group(0),
+ })), 'www.example.com')
+
+ def test_render_code_block(self):
+ match = next(Linkify.SEARCH_RE.finditer('test https://www.example.com'))
+
+ self.assertEqual(DOM.render(DOM.create_element(Linkify, {
+ 'block_type': BLOCK_TYPES.CODE,
+ 'match': match,
+ 'children': match.group(0),
+ })), match.group(0))
+
+
+class TestHashtag(unittest.TestCase):
+ def test_init(self):
+ self.assertIsInstance(Hashtag(), Hashtag)
+
+ def test_render(self):
+ self.assertEqual(DOM.render(DOM.create_element(Hashtag, {
+ 'block_type': BLOCK_TYPES.UNSTYLED,
+ 'children': '#hashtagtest',
+ })), '#hashtagtest')
+
+ def test_render_code_block(self):
+ self.assertEqual(DOM.render(DOM.create_element(Hashtag, {
+ 'block_type': BLOCK_TYPES.CODE,
+ 'children': '#hashtagtest',
+ })), '#hashtagtest')
+
+
+class TestBR(unittest.TestCase):
+ def test_init(self):
+ self.assertIsInstance(BR(), BR)
+
+ def test_render(self):
+ self.assertEqual(DOM.render(DOM.create_element(BR, {
+ 'block_type': BLOCK_TYPES.UNSTYLED,
+ 'children': '\n',
+ })), '
')
+
+ def test_render_code_block(self):
+ self.assertEqual(DOM.create_element(BR, {
+ 'block_type': BLOCK_TYPES.CODE,
+ 'children': '\n',
+ }), '\n')
+
+
+class TestCompositeDecorators(unittest.TestCase):
+ def test_render_decorators_empty(self):
+ self.assertEqual(DOM.render(render_decorators([], 'test https://www.example.com#hash #hashtagtest', BLOCK_TYPES.UNSTYLED)), 'test https://www.example.com#hash #hashtagtest')
+
+ def test_render_decorators_single(self):
+ self.assertEqual(DOM.render(render_decorators([Linkify()], 'test https://www.example.com#hash #hashtagtest', BLOCK_TYPES.UNSTYLED)), 'test https://www.example.com#hash #hashtagtest')
+
+ def test_render_decorators_conflicting_order_one(self):
+ self.assertEqual(DOM.render(render_decorators([Linkify(), Hashtag()], 'test https://www.example.com#hash #hashtagtest', BLOCK_TYPES.UNSTYLED)), 'test https://www.example.com#hash #hashtagtest')
+
+ def test_render_decorators_conflicting_order_two(self):
+ self.assertEqual(DOM.render(render_decorators([Hashtag(), Linkify()], 'test https://www.example.com#hash #hashtagtest', BLOCK_TYPES.UNSTYLED)), 'test https://www.example.com#hash #hashtagtest')
diff --git a/tests/test_entity_state.py b/tests/test_entity_state.py
index c132e76..d450dd3 100644
--- a/tests/test_entity_state.py
+++ b/tests/test_entity_state.py
@@ -45,6 +45,13 @@ def test_apply_stop_entity(self):
self.entity_state.apply(Command('stop_entity', 5, 0))
self.assertEqual(len(self.entity_state.entity_stack), 0)
+ def test_is_empty_default(self):
+ self.assertEqual(self.entity_state.is_empty(), True)
+
+ def test_is_empty_styled(self):
+ self.entity_state.apply(Command('start_entity', 0, 0))
+ self.assertEqual(self.entity_state.is_empty(), False)
+
def test_get_entity_details(self):
self.assertEqual(self.entity_state.get_entity_details(Command('start_entity', 0, 0)), {
'data': {
diff --git a/tests/test_output.py b/tests/test_output.py
index 2881ad1..6ce053d 100644
--- a/tests/test_output.py
+++ b/tests/test_output.py
@@ -7,6 +7,7 @@
from draftjs_exporter.defaults import BLOCK_MAP
from draftjs_exporter.entity_state import EntityException
from draftjs_exporter.html import HTML
+from tests.test_composite_decorators import BR, Hashtag, Linkify
from tests.test_entities import HR, Link
config = {
@@ -14,6 +15,11 @@
ENTITY_TYPES.LINK: Link(),
ENTITY_TYPES.HORIZONTAL_RULE: HR(),
},
+ 'composite_decorators': [
+ Linkify(),
+ Hashtag(),
+ BR(),
+ ],
'block_map': dict(BLOCK_MAP, **{
BLOCK_TYPES.UNORDERED_LIST_ITEM: {
'element': 'li',
@@ -887,7 +893,7 @@ def test_render_with_default_config(self):
}), 'some paragraph text
')
def test_render_with_line_breaks(self):
- self.assertEqual(HTML().render({
+ self.assertEqual(self.exporter.render({
'entityMap': {},
'blocks': [
{
@@ -908,7 +914,7 @@ def test_render_with_line_breaks(self):
}), 'some paragraph text
split in half
')
def test_render_with_many_line_breaks(self):
- self.assertEqual(HTML().render({
+ self.assertEqual(self.exporter.render({
'entityMap': {},
'blocks': [
{
@@ -927,3 +933,69 @@ def test_render_with_many_line_breaks(self):
}
]
}), '
some paragraph text
split in half
')
+
+ def test_render_with_entity_and_decorators(self):
+ """
+ The composite decorator should never render text in any entities.
+ """
+ self.assertEqual(self.exporter.render({
+ 'entityMap': {
+ '1': {
+ 'type': 'LINK',
+ 'mutability': 'MUTABLE',
+ 'data': {
+ 'url': 'http://amazon.us'
+ }
+ }
+ },
+ 'blocks': [
+ {
+ 'key': '5s7g9',
+ 'text': 'search http://a.us or https://yahoo.com or www.google.com for #github and #facebook',
+ 'type': 'unstyled',
+ 'depth': 0,
+ 'inlineStyleRanges': [],
+ 'entityRanges': [
+ {
+ 'offset': 7,
+ 'length': 11,
+ 'key': 1
+ }
+ ],
+ },
+ {
+ 'key': '34a12',
+ 'text': '#check www.example.com',
+ 'type': 'code-block',
+ 'inlineStyleRanges': [],
+ },
+ ]
+ }),
+ 'search http://a.us or '
+ 'https://yahoo.com or '
+ 'www.google.com for '
+ '#github and '
+ '#facebook
'
+ '
#check www.example.com
')
+
+ def test_render_with_multiple_decorators(self):
+ """
+ When multiple decorators match the same part of text,
+ only the first one should perform the replacement.
+ """
+ self.assertEqual(self.exporter.render({
+ 'entityMap': {},
+ 'blocks': [
+ {
+ 'key': '5s7g9',
+ 'text': 'search http://www.google.com#world for the #world',
+ 'type': 'unstyled',
+ 'depth': 0,
+ 'inlineStyleRanges': [],
+ 'entityRanges': [],
+ },
+ ]
+ }),
+ 'search '
+ 'http://www.google.com#world for the '
+ '#world
')
diff --git a/tests/test_style_state.py b/tests/test_style_state.py
index dcb1cb7..dfb5db7 100644
--- a/tests/test_style_state.py
+++ b/tests/test_style_state.py
@@ -31,12 +31,12 @@ def test_apply_stop_inline_style(self):
self.style_state.apply(Command('stop_inline_style', 0, 'ITALIC'))
self.assertEqual(self.style_state.styles, [])
- def test_is_unstyled_default(self):
- self.assertEqual(self.style_state.is_unstyled(), True)
+ def test_is_empty_default(self):
+ self.assertEqual(self.style_state.is_empty(), True)
- def test_is_unstyled_styled(self):
+ def test_is_empty_styled(self):
self.style_state.apply(Command('start_inline_style', 0, 'ITALIC'))
- self.assertEqual(self.style_state.is_unstyled(), False)
+ self.assertEqual(self.style_state.is_empty(), False)
def test_get_style_value_empty(self):
self.assertEqual(self.style_state.get_style_value(), '')
@@ -49,34 +49,23 @@ def test_get_style_value_multiple(self):
self.style_state.apply(Command('start_inline_style', 0, 'HIGHLIGHT'))
self.assertEqual(self.style_state.get_style_value(), 'text-decoration: underline;')
- def test_replace_linebreaks_no_break(self):
- self.assertEqual(DOM.get_tag_name(self.style_state.replace_linebreaks('Test text')), 'textnode')
+ def test_render_styles_unstyled(self):
+ self.assertEqual(DOM.get_tag_name(self.style_state.render_styles(DOM.create_text_node('Test text'))), 'textnode')
+ self.assertEqual(DOM.get_text_content(self.style_state.render_styles(DOM.create_text_node('Test text'))), 'Test text')
- def test_replace_linebreaks_one_break(self):
- self.assertEqual(DOM.get_tag_name(self.style_state.replace_linebreaks('Test\ntext')), 'fragment')
- self.assertEqual(DOM.get_tag_name(DOM.get_children(self.style_state.replace_linebreaks('Test\ntext'))[1]), 'br')
+ def test_render_styles_unicode(self):
+ self.assertEqual(DOM.get_text_content(self.style_state.render_styles(DOM.create_text_node('🍺'))), '🍺')
- def test_replace_linebreaks_multiple_breaks(self):
- self.assertEqual(DOM.get_tag_name(self.style_state.replace_linebreaks('\nTest\nte\nxt\n')), 'fragment')
- self.assertEqual(DOM.render(self.style_state.replace_linebreaks('\nTest\nte\nxt\n')), '
Test
te
xt
')
-
- def test_create_node_unstyled(self):
- self.assertEqual(DOM.get_tag_name(self.style_state.create_node('Test text')), 'textnode')
- self.assertEqual(DOM.get_text_content(self.style_state.create_node('Test text')), 'Test text')
-
- def test_create_node_unicode(self):
- self.assertEqual(DOM.get_text_content(self.style_state.create_node('🍺')), '🍺')
-
- def test_create_node_styled(self):
+ def test_render_styles_styled(self):
self.style_state.apply(Command('start_inline_style', 0, 'ITALIC'))
- self.assertEqual(DOM.get_tag_name(self.style_state.create_node('Test text')), 'em')
- self.assertEqual(self.style_state.create_node('Test text').get('style'), None)
- self.assertEqual(DOM.get_text_content(self.style_state.create_node('Test text')), 'Test text')
+ self.assertEqual(DOM.get_tag_name(self.style_state.render_styles(DOM.create_text_node('Test text'))), 'em')
+ self.assertEqual(self.style_state.render_styles(DOM.create_text_node('Test text')).get('style'), None)
+ self.assertEqual(DOM.get_text_content(self.style_state.render_styles(DOM.create_text_node('Test text'))), 'Test text')
self.style_state.apply(Command('stop_inline_style', 9, 'ITALIC'))
- def test_create_node_styled_multiple(self):
+ def test_render_styles_styled_multiple(self):
self.style_state.apply(Command('start_inline_style', 0, 'BOLD'))
self.style_state.apply(Command('start_inline_style', 0, 'ITALIC'))
self.assertEqual(self.style_state.get_style_tags(), ['em', 'strong'])
- self.assertEqual(DOM.get_tag_name(self.style_state.create_node('wow')), 'em')
- self.assertEqual(DOM.get_tag_name(DOM.get_children(self.style_state.create_node('wow'))[0]), 'strong')
+ self.assertEqual(DOM.get_tag_name(self.style_state.render_styles(DOM.create_text_node('Test text'))), 'em')
+ self.assertEqual(DOM.get_tag_name(DOM.get_children(self.style_state.render_styles(DOM.create_text_node('Test text')))[0]), 'strong')