Gracefully handle inline styles/entities at top-level of rich text (#4290)

pull/4302/head
Matt Westcott 2018-02-16 10:30:06 +00:00 zatwierdzone przez Matt Westcott
rodzic 4fc05fc79e
commit 6d32ed00cf
2 zmienionych plików z 70 dodań i 2 usunięć

Wyświetl plik

@ -103,7 +103,13 @@ class InlineStyleElementHandler:
self.style = style
def handle_starttag(self, name, attrs, state, contentstate):
assert state.current_block is not None, "%s element found at the top level" % name
if state.current_block is None:
# Inline style element encountered at the top level -
# start a new paragraph block to contain it
block = Block('unstyled', depth=state.list_depth)
contentstate.blocks.append(block)
state.current_block = block
state.leading_whitespace = STRIP_WHITESPACE
if state.leading_whitespace == FORCE_WHITESPACE:
# any pending whitespace should be output before handling this tag,
@ -131,7 +137,13 @@ class InlineEntityElementHandler:
self.entity_type = entity_type
def handle_starttag(self, name, attrs, state, contentstate):
assert state.current_block is not None, "%s element found at the top level" % name
if state.current_block is None:
# Inline entity element encountered at the top level -
# start a new paragraph block to contain it
block = Block('unstyled', depth=state.list_depth)
contentstate.blocks.append(block)
state.current_block = block
state.leading_whitespace = STRIP_WHITESPACE
if state.leading_whitespace == FORCE_WHITESPACE:
# any pending whitespace should be output before handling this tag,

Wyświetl plik

@ -144,6 +144,24 @@ class TestHtmlToContentState(TestCase):
]
})
def test_inline_styles_at_start_of_bare_block(self):
converter = ContentstateConverter(features=['bold', 'italic'])
result = json.loads(converter.from_database_format(
'''<b>Seriously</b>, stop talking about <i>Fight Club</i> already.'''
))
self.assertContentStateEqual(result, {
'entityMap': {},
'blocks': [
{
'inlineStyleRanges': [
{'offset': 0, 'length': 9, 'style': 'BOLD'},
{'offset': 30, 'length': 10, 'style': 'ITALIC'},
],
'text': 'Seriously, stop talking about Fight Club already.', 'depth': 0, 'type': 'unstyled', 'key': '00000', 'entityRanges': []
},
]
})
def test_inline_styles_depend_on_features(self):
converter = ContentstateConverter(features=['italic', 'just-made-it-up'])
result = json.loads(converter.from_database_format(
@ -237,6 +255,44 @@ class TestHtmlToContentState(TestCase):
]
})
def test_link_in_bare_text(self):
converter = ContentstateConverter(features=['link'])
result = json.loads(converter.from_database_format(
'''an <a href="http://wagtail.io">external</a> link'''
))
self.assertContentStateEqual(result, {
'entityMap': {
'0': {'mutability': 'MUTABLE', 'type': 'LINK', 'data': {'url': 'http://wagtail.io'}}
},
'blocks': [
{
'inlineStyleRanges': [], 'text': 'an external link', 'depth': 0, 'type': 'unstyled', 'key': '00000',
'entityRanges': [{'offset': 3, 'length': 8, 'key': 0}]
},
]
})
def test_link_at_start_of_bare_text(self):
converter = ContentstateConverter(features=['link'])
result = json.loads(converter.from_database_format(
'''<a href="http://wagtail.io">an external link</a> and <a href="http://torchbox.com">another</a>'''
))
self.assertContentStateEqual(result, {
'entityMap': {
'0': {'mutability': 'MUTABLE', 'type': 'LINK', 'data': {'url': 'http://wagtail.io'}},
'1': {'mutability': 'MUTABLE', 'type': 'LINK', 'data': {'url': 'http://torchbox.com'}},
},
'blocks': [
{
'inlineStyleRanges': [], 'text': 'an external link and another', 'depth': 0, 'type': 'unstyled', 'key': '00000',
'entityRanges': [
{'offset': 0, 'length': 16, 'key': 0},
{'offset': 21, 'length': 7, 'key': 1},
]
},
]
})
def test_page_link(self):
converter = ContentstateConverter(features=['link'])
result = json.loads(converter.from_database_format(