def render_block(self, block, entity_map, wrapper_state): if block['inlineStyleRanges'] or block['entityRanges']: content = DOM.create_element() entity_state = EntityState(self.entity_decorators, entity_map) style_state = StyleState(self.style_map) for (text, commands) in self.build_command_groups(block): for command in commands: entity_state.apply(command) style_state.apply(command) # Decorators are not rendered inside entities. if entity_state.has_no_entity() and self.has_decorators: decorated_node = render_decorators(self.composite_decorators, text, block, wrapper_state.blocks) else: decorated_node = text styled_node = style_state.render_styles(decorated_node, block, wrapper_state.blocks) entity_node = entity_state.render_entities(styled_node) if entity_node is not None: DOM.append_child(content, entity_node) # Check whether there actually are two different nodes, confirming we are not inserting an upcoming entity. if styled_node != entity_node and entity_state.has_no_entity(): DOM.append_child(content, styled_node) # Fast track for blocks which do not contain styles nor entities, which is very common. elif self.has_decorators: content = render_decorators(self.composite_decorators, block['text'], block, wrapper_state.blocks) else: content = block['text'] return wrapper_state.element_for(block, content)
def render_block(self, block, entity_map, wrapper_state): content = DOM.create_element() entity_state = EntityState(self.entity_decorators, entity_map) style_state = StyleState(self.style_map) for (text, commands) in self.build_command_groups(block): for command in commands: entity_state.apply(command) style_state.apply(command) # Decorators are not rendered inside entities. if text and entity_state.has_no_entity() and len( self.composite_decorators) > 0: decorated_node = render_decorators(self.composite_decorators, text, block) else: decorated_node = text styled_node = style_state.render_styles(decorated_node) entity_node = entity_state.render_entities(styled_node) if entity_node is not None: DOM.append_child(content, entity_node) if styled_node != entity_node: DOM.append_child(content, styled_node) return wrapper_state.element_for(block, content)
def render_block(self, block: Block, entity_map: EntityMap, wrapper_state: WrapperState) -> Element: has_styles = "inlineStyleRanges" in block and block["inlineStyleRanges"] has_entities = "entityRanges" in block and block["entityRanges"] has_decorators = should_render_decorators(self.composite_decorators, block["text"]) if has_styles or has_entities: content = DOM.create_element() entity_state = EntityState(self.entity_options, entity_map) style_state = StyleState( self.style_options) if has_styles else None for (text, commands) in self.build_command_groups(block): for command in commands: entity_state.apply(command) if style_state: style_state.apply(command) # Decorators are not rendered inside entities. if has_decorators and entity_state.has_no_entity(): decorated_node = render_decorators( self.composite_decorators, text, block, wrapper_state.blocks, ) else: decorated_node = text if style_state: styled_node = style_state.render_styles( decorated_node, block, wrapper_state.blocks) else: styled_node = decorated_node entity_node = entity_state.render_entities( styled_node, block, wrapper_state.blocks) if entity_node is not None: DOM.append_child(content, entity_node) # Check whether there actually are two different nodes, confirming we are not inserting an upcoming entity. if (styled_node != entity_node and entity_state.has_no_entity()): DOM.append_child(content, styled_node) # Fast track for blocks which do not contain styles nor entities, which is very common. elif has_decorators: content = render_decorators( self.composite_decorators, block["text"], block, wrapper_state.blocks, ) else: content = block["text"] return wrapper_state.element_for(block, content)
def test_render_styles_data(self): blocks = [ { 'key': '5s7g9', 'text': 'test', 'type': 'unstyled', 'depth': 0, 'inlineStyleRanges': [], 'entityRanges': [], }, ] def component(props): self.assertEqual(props['blocks'], blocks) self.assertEqual(props['block'], blocks[0]) self.assertEqual(props['inline_style_range']['style'], 'ITALIC') return None style_state = StyleState({ 'ITALIC': component, }) style_state.apply(Command('start_inline_style', 0, 'ITALIC')) style_state.render_styles('Test text', blocks[0], blocks) style_state.apply(Command('stop_inline_style', 9, 'ITALIC'))
def render(self, content_state): """ Starts the export process on a given piece of content state. """ self.wrapper_state = WrapperState(self.block_map) self.style_state = StyleState(self.style_map) entity_map = content_state.get('entityMap', {}) for block in content_state.get('blocks', []): self.render_block(block, entity_map) self.wrapper_state.clean_up() return self.wrapper_state.to_string()
def render_block(self, block, entity_map, wrapper_state): content = DOM.create_element() if block['inlineStyleRanges'] or block['entityRanges']: entity_state = EntityState(self.entity_decorators, entity_map) style_state = StyleState(self.style_map) for (text, commands) in self.build_command_groups(block): for command in commands: entity_state.apply(command) style_state.apply(command) # Decorators are not rendered inside entities. if text and entity_state.has_no_entity() and len( self.composite_decorators) > 0: decorated_node = render_decorators( self.composite_decorators, text, block, wrapper_state.blocks) else: decorated_node = text styled_node = style_state.render_styles( decorated_node, block, wrapper_state.blocks) entity_node = entity_state.render_entities(styled_node) if entity_node is not None: DOM.append_child(content, entity_node) if styled_node != entity_node: DOM.append_child(content, styled_node) # Fast track for blocks which do not contain styles nor entities, which is very common. else: if len(self.composite_decorators) > 0: decorated_node = render_decorators(self.composite_decorators, block['text'], block, wrapper_state.blocks) else: decorated_node = block['text'] DOM.append_child(content, decorated_node) return wrapper_state.element_for(block, content)
def render_block(self, block, entity_map, wrapper_state): if 'inlineStyleRanges' in block and block[ 'inlineStyleRanges'] or 'entityRanges' in block and block[ 'entityRanges']: content = DOM.create_element() entity_state = EntityState(self.entity_options, entity_map) style_state = StyleState(self.style_options) for (text, commands) in self.build_command_groups(block): for command in commands: entity_state.apply(command) style_state.apply(command) # Decorators are not rendered inside entities. if entity_state.has_no_entity() and self.has_decorators: decorated_node = render_decorators( self.composite_decorators, text, block, wrapper_state.blocks) else: decorated_node = text styled_node = style_state.render_styles( decorated_node, block, wrapper_state.blocks) entity_node = entity_state.render_entities(styled_node) if entity_node is not None: DOM.append_child(content, entity_node) # Check whether there actually are two different nodes, confirming we are not inserting an upcoming entity. if styled_node != entity_node and entity_state.has_no_entity( ): DOM.append_child(content, styled_node) # Fast track for blocks which do not contain styles nor entities, which is very common. elif self.has_decorators: content = render_decorators(self.composite_decorators, block['text'], block, wrapper_state.blocks) else: content = block['text'] return wrapper_state.element_for(block, content)
def test_render_styles_data(self): blocks = [{ "key": "5s7g9", "text": "test", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], }] def component(props): self.assertEqual(props["blocks"], blocks) self.assertEqual(props["block"], blocks[0]) self.assertEqual(props["inline_style_range"]["style"], "ITALIC") return None style_state = StyleState(Options.map_styles({"ITALIC": component})) style_state.apply(Command("start_inline_style", 0, "ITALIC")) style_state.render_styles("Test text", blocks[0], blocks) style_state.apply(Command("stop_inline_style", 9, "ITALIC"))
def setUp(self): self.style_state = StyleState(style_map)
class TestStyleState(unittest.TestCase): def setUp(self): self.style_state = StyleState(style_map) def test_init(self): self.assertIsInstance(self.style_state, StyleState) def test_apply_start_inline_style(self): self.assertEqual(self.style_state.styles, []) self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.styles, ['ITALIC']) def test_apply_stop_inline_style(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) 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_unstyled_styled(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.is_unstyled(), False) def test_get_style_value_empty(self): self.assertEqual(self.style_state.get_style_value(), '') def test_get_style_value_single(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.get_style_value(), '') 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_create_node_unstyled(self): self.assertEqual(DOM.get_tag_name(self.style_state.create_nodes('Test text')[0]), 'textnode') self.assertEqual(DOM.get_text_content(self.style_state.create_nodes('Test text')[0]), 'Test text') def test_create_node_unicode(self): self.assertEqual(DOM.get_text_content(self.style_state.create_nodes('🍺')[0]), '🍺') def test_create_node_styled(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(DOM.get_tag_name(self.style_state.create_nodes('Test text')[0]), 'em') self.assertEqual(self.style_state.create_nodes('Test text')[0].get('style'), None) self.assertEqual(DOM.get_text_content(self.style_state.create_nodes('Test text')[0]), 'Test text') self.style_state.apply(Command('stop_inline_style', 9, 'ITALIC')) def test_create_node_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_nodes('wow')[0]), 'em') self.assertEqual(DOM.get_tag_name(DOM.get_children(self.style_state.create_nodes('wow')[0])[0]), 'strong')
def setUp(self): DOM.use(DOM.STRING) self.style_state = StyleState(style_map)
class HTML: """ Entry point of the exporter. Combines entity, wrapper and style state to generate the right HTML nodes. """ def __init__(self, config=None): if config is None: config = {} self.entity_decorators = config.get('entity_decorators', {}) self.block_map = config.get('block_map', BLOCK_MAP) self.style_map = config.get('style_map', STYLE_MAP) def render(self, content_state): """ Starts the export process on a given piece of content state. """ self.wrapper_state = WrapperState(self.block_map) self.style_state = StyleState(self.style_map) entity_map = content_state.get('entityMap', {}) for block in content_state.get('blocks', []): self.render_block(block, entity_map) self.wrapper_state.clean_up() return self.wrapper_state.to_string() def render_block(self, block, entity_map): element = self.wrapper_state.element_for(block) entity_state = EntityState(self.entity_decorators, entity_map) for (text, commands) in self.build_command_groups(block): for command in commands: entity_state.apply(command) self.style_state.apply(command) style_node = self.style_state.create_node(text) entity_state.render_entitities(element, style_node) def build_command_groups(self, block): """ Creates block modification commands, grouped by start index, with the text to apply them on. """ text = block.get('text') commands = self.build_commands(block) # Tried using itertools.tee but for some reason that failed. Oh well. grouped = Command.grouped_by_index(commands) listed = list(Command.grouped_by_index(commands)) sliced = [] i = 0 for start_index, commands in grouped: next_group = listed[i + 1] if i + 1 < len(listed) else False stop_index = next_group[0] if next_group else 0 sliced.append((text[start_index:stop_index], list(commands))) i += 1 return sliced def build_commands(self, block): """ Build all of the manipulation commands for a given block. - One pair to set the text. - Multiple pairs for styles. - Multiple pairs for entities. """ text_commands = Command.start_stop('text', 0, len(block.get('text'))) style_commands = self.build_style_commands(block) entity_commands = self.build_entity_commands(block) return text_commands + style_commands + entity_commands def build_style_commands(self, block): ranges = block.get('inlineStyleRanges', []) return Command.from_ranges(ranges, 'inline_style', 'style') def build_entity_commands(self, block): ranges = block.get('entityRanges', []) return Command.from_ranges(ranges, 'entity', 'key')
class TestStyleState(unittest.TestCase): def setUp(self): DOM.use(DOM.STRING) self.style_state = StyleState(style_map) def test_init(self): self.assertIsInstance(self.style_state, StyleState) def test_apply_start_inline_style(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.styles, ['ITALIC']) def test_apply_stop_inline_style(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.style_state.apply(Command('stop_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.styles, []) def test_is_empty_default(self): self.assertEqual(self.style_state.is_empty(), True) def test_is_empty_styled(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.is_empty(), False) def test_render_styles_unstyled(self): self.assertEqual(self.style_state.render_styles('Test text'), 'Test text') def test_render_styles_unicode(self): self.assertEqual(self.style_state.render_styles('🍺'), '🍺') def test_render_styles_styled(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(DOM.render_debug(self.style_state.render_styles('Test text')), '<em>Test text</em>') self.style_state.apply(Command('stop_inline_style', 9, 'ITALIC')) 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(DOM.render_debug(self.style_state.render_styles('Test text')), '<strong><em>Test text</em></strong>') def test_render_styles_attributes(self): self.style_state.apply(Command('start_inline_style', 0, 'KBD')) self.assertEqual(DOM.render_debug(self.style_state.render_styles('Test text')), '<kbd class="o-keyboard-shortcut">Test text</kbd>') self.style_state.apply(Command('stop_inline_style', 9, 'KBD')) def test_render_styles_component(self): self.style_state.apply(Command('start_inline_style', 0, 'IMPORTANT')) self.assertEqual(DOM.render_debug(self.style_state.render_styles('Test text')), '<strong style="color: red;">Test text</strong>') self.style_state.apply(Command('stop_inline_style', 9, 'IMPORTANT')) def test_render_styles_component_multiple(self): self.style_state.apply(Command('start_inline_style', 0, 'IMPORTANT')) self.style_state.apply(Command('start_inline_style', 0, 'SHOUT')) self.assertEqual(DOM.render_debug(self.style_state.render_styles('Test text')), '<strong style="color: red;"><span style="text-transform: uppercase;">Test text</span></strong>') self.style_state.apply(Command('stop_inline_style', 9, 'IMPORTANT')) self.style_state.apply(Command('stop_inline_style', 9, 'SHOUT')) def test_render_styles_component_multiple_invert(self): self.style_state.apply(Command('start_inline_style', 0, 'SHOUT')) self.style_state.apply(Command('start_inline_style', 0, 'IMPORTANT')) self.assertEqual(DOM.render_debug(self.style_state.render_styles('Test text')), '<strong style="color: red;"><span style="text-transform: uppercase;">Test text</span></strong>') self.style_state.apply(Command('stop_inline_style', 9, 'SHOUT')) self.style_state.apply(Command('stop_inline_style', 9, 'IMPORTANT'))
class TestStyleState(unittest.TestCase): def setUp(self): self.style_state = StyleState(style_map) def test_init(self): self.assertIsInstance(self.style_state, StyleState) def test_apply_start_inline_style(self): self.assertEqual(self.style_state.styles, []) self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.styles, ['ITALIC']) def test_apply_stop_inline_style(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) 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_unstyled_styled(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.is_unstyled(), False) def test_get_style_value_empty(self): self.assertEqual(self.style_state.get_style_value(), '') def test_get_style_value_single(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.get_style_value(), '') 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_add_node_unstyled(self): self.assertEqual(self.style_state.add_node(etree.Element('p'), 'Test text').tag, 'textnode') self.assertEqual(self.style_state.add_node(etree.Element('p'), 'Test text').text, 'Test text') def test_add_node_unicode(self): self.assertEqual(self.style_state.add_node(etree.Element('p'), '🍺').text, '🍺') def test_add_node_styled(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.add_node(etree.Element('p'), 'Test text').tag, 'em') self.assertEqual(self.style_state.add_node(etree.Element('p'), 'Test text').get('style'), None) self.assertEqual(self.style_state.add_node(etree.Element('p'), 'Test text').text, 'Test text') self.style_state.apply(Command('stop_inline_style', 9, 'ITALIC'))
class TestStyleState(unittest.TestCase): def setUp(self): DOM.use(DOM.STRING) self.style_state = StyleState(style_map) def test_init(self): self.assertIsInstance(self.style_state, StyleState) def test_apply_start_inline_style(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.styles, ['ITALIC']) def test_apply_stop_inline_style(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.style_state.apply(Command('stop_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.styles, []) def test_is_empty_default(self): self.assertEqual(self.style_state.is_empty(), True) def test_is_empty_styled(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.is_empty(), False) def test_render_styles_unstyled(self): self.assertEqual(self.style_state.render_styles('Test text', {}, []), 'Test text') def test_render_styles_unicode(self): self.assertEqual(self.style_state.render_styles('🍺', {}, []), '🍺') def test_render_styles_styled(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(DOM.render_debug(self.style_state.render_styles('Test text', {}, [])), '<em>Test text</em>') self.style_state.apply(Command('stop_inline_style', 9, 'ITALIC')) 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(DOM.render_debug(self.style_state.render_styles('Test text', {}, [])), '<strong><em>Test text</em></strong>') def test_render_styles_attributes(self): self.style_state.apply(Command('start_inline_style', 0, 'KBD')) self.assertEqual(DOM.render_debug(self.style_state.render_styles('Test text', {}, [])), '<kbd class="o-keyboard-shortcut">Test text</kbd>') self.style_state.apply(Command('stop_inline_style', 9, 'KBD')) def test_render_styles_component(self): self.style_state.apply(Command('start_inline_style', 0, 'IMPORTANT')) self.assertEqual(DOM.render_debug(self.style_state.render_styles('Test text', {}, [])), '<strong style="color: red;">Test text</strong>') self.style_state.apply(Command('stop_inline_style', 9, 'IMPORTANT')) def test_render_styles_component_multiple(self): self.style_state.apply(Command('start_inline_style', 0, 'IMPORTANT')) self.style_state.apply(Command('start_inline_style', 0, 'SHOUT')) self.assertEqual(DOM.render_debug(self.style_state.render_styles('Test text', {}, [])), '<strong style="color: red;"><span style="text-transform: uppercase;">Test text</span></strong>') self.style_state.apply(Command('stop_inline_style', 9, 'IMPORTANT')) self.style_state.apply(Command('stop_inline_style', 9, 'SHOUT')) def test_render_styles_component_multiple_invert(self): self.style_state.apply(Command('start_inline_style', 0, 'SHOUT')) self.style_state.apply(Command('start_inline_style', 0, 'IMPORTANT')) self.assertEqual(DOM.render_debug(self.style_state.render_styles('Test text', {}, [])), '<strong style="color: red;"><span style="text-transform: uppercase;">Test text</span></strong>') self.style_state.apply(Command('stop_inline_style', 9, 'SHOUT')) self.style_state.apply(Command('stop_inline_style', 9, 'IMPORTANT')) def test_render_styles_data(self): blocks = [ { 'key': '5s7g9', 'text': 'test', 'type': 'unstyled', 'depth': 0, 'inlineStyleRanges': [], 'entityRanges': [], }, ] def component(props): self.assertEqual(props['blocks'], blocks) self.assertEqual(props['block'], blocks[0]) self.assertEqual(props['inline_style_range']['style'], 'ITALIC') return None style_state = StyleState({ 'ITALIC': component, }) style_state.apply(Command('start_inline_style', 0, 'ITALIC')) style_state.render_styles('Test text', blocks[0], blocks) style_state.apply(Command('stop_inline_style', 9, 'ITALIC'))
class TestStyleState(unittest.TestCase): def setUp(self): DOM.use(DOM.STRING) self.style_state = StyleState( Options.map_styles(style_map) # type: ignore ) def test_init(self): self.assertIsInstance(self.style_state, StyleState) def test_apply_start_inline_style(self): self.style_state.apply(Command("start_inline_style", 0, "ITALIC")) self.assertEqual(self.style_state.styles, ["ITALIC"]) def test_apply_stop_inline_style(self): self.style_state.apply(Command("start_inline_style", 0, "ITALIC")) self.style_state.apply(Command("stop_inline_style", 0, "ITALIC")) self.assertEqual(self.style_state.styles, []) def test_is_empty_default(self): self.assertEqual(self.style_state.is_empty(), True) def test_is_empty_styled(self): self.style_state.apply(Command("start_inline_style", 0, "ITALIC")) self.assertEqual(self.style_state.is_empty(), False) def test_render_styles_unstyled(self): self.assertEqual(self.style_state.render_styles("Test text", {}, []), "Test text") def test_render_styles_unicode(self): self.assertEqual(self.style_state.render_styles("🍺", {}, []), "🍺") def test_render_styles_styled(self): self.style_state.apply(Command("start_inline_style", 0, "ITALIC")) self.assertEqual( DOM.render_debug( self.style_state.render_styles("Test text", {}, [])), "<em>Test text</em>", ) self.style_state.apply(Command("stop_inline_style", 9, "ITALIC")) 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( DOM.render_debug( self.style_state.render_styles("Test text", {}, [])), "<strong><em>Test text</em></strong>", ) def test_render_styles_attributes(self): self.style_state.apply(Command("start_inline_style", 0, "KBD")) self.assertEqual( DOM.render_debug( self.style_state.render_styles("Test text", {}, [])), '<kbd class="o-keyboard-shortcut">Test text</kbd>', ) self.style_state.apply(Command("stop_inline_style", 9, "KBD")) def test_render_styles_component(self): self.style_state.apply(Command("start_inline_style", 0, "IMPORTANT")) self.assertEqual( DOM.render_debug( self.style_state.render_styles("Test text", {}, [])), '<strong style="color: red;">Test text</strong>', ) self.style_state.apply(Command("stop_inline_style", 9, "IMPORTANT")) def test_render_styles_component_multiple(self): self.style_state.apply(Command("start_inline_style", 0, "IMPORTANT")) self.style_state.apply(Command("start_inline_style", 0, "SHOUT")) self.assertEqual( DOM.render_debug( self.style_state.render_styles("Test text", {}, [])), '<strong style="color: red;"><span style="text-transform: uppercase;">Test text</span></strong>', ) self.style_state.apply(Command("stop_inline_style", 9, "IMPORTANT")) self.style_state.apply(Command("stop_inline_style", 9, "SHOUT")) def test_render_styles_component_multiple_invert(self): self.style_state.apply(Command("start_inline_style", 0, "SHOUT")) self.style_state.apply(Command("start_inline_style", 0, "IMPORTANT")) self.assertEqual( DOM.render_debug( self.style_state.render_styles("Test text", {}, [])), '<strong style="color: red;"><span style="text-transform: uppercase;">Test text</span></strong>', ) self.style_state.apply(Command("stop_inline_style", 9, "SHOUT")) self.style_state.apply(Command("stop_inline_style", 9, "IMPORTANT")) def test_render_styles_data(self): blocks = [{ "key": "5s7g9", "text": "test", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], }] def component(props): self.assertEqual(props["blocks"], blocks) self.assertEqual(props["block"], blocks[0]) self.assertEqual(props["inline_style_range"]["style"], "ITALIC") return None style_state = StyleState(Options.map_styles({"ITALIC": component})) style_state.apply(Command("start_inline_style", 0, "ITALIC")) style_state.render_styles("Test text", blocks[0], blocks) style_state.apply(Command("stop_inline_style", 9, "ITALIC"))
class TestStyleState(unittest.TestCase): def setUp(self): DOM.use(DOM.STRING) self.style_state = StyleState(style_map) def test_init(self): self.assertIsInstance(self.style_state, StyleState) def test_apply_start_inline_style(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.styles, ['ITALIC']) def test_apply_stop_inline_style(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.style_state.apply(Command('stop_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.styles, []) def test_is_empty_default(self): self.assertEqual(self.style_state.is_empty(), True) def test_is_empty_styled(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.is_empty(), False) def test_render_styles_unstyled(self): self.assertEqual(self.style_state.render_styles('Test text', {}, []), 'Test text') def test_render_styles_unicode(self): self.assertEqual(self.style_state.render_styles('🍺', {}, []), '🍺') def test_render_styles_styled(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual( DOM.render_debug( self.style_state.render_styles('Test text', {}, [])), '<em>Test text</em>') self.style_state.apply(Command('stop_inline_style', 9, 'ITALIC')) 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( DOM.render_debug( self.style_state.render_styles('Test text', {}, [])), '<strong><em>Test text</em></strong>') def test_render_styles_attributes(self): self.style_state.apply(Command('start_inline_style', 0, 'KBD')) self.assertEqual( DOM.render_debug( self.style_state.render_styles('Test text', {}, [])), '<kbd class="o-keyboard-shortcut">Test text</kbd>') self.style_state.apply(Command('stop_inline_style', 9, 'KBD')) def test_render_styles_component(self): self.style_state.apply(Command('start_inline_style', 0, 'IMPORTANT')) self.assertEqual( DOM.render_debug( self.style_state.render_styles('Test text', {}, [])), '<strong style="color: red;">Test text</strong>') self.style_state.apply(Command('stop_inline_style', 9, 'IMPORTANT')) def test_render_styles_component_multiple(self): self.style_state.apply(Command('start_inline_style', 0, 'IMPORTANT')) self.style_state.apply(Command('start_inline_style', 0, 'SHOUT')) self.assertEqual( DOM.render_debug( self.style_state.render_styles('Test text', {}, [])), '<strong style="color: red;"><span style="text-transform: uppercase;">Test text</span></strong>' ) self.style_state.apply(Command('stop_inline_style', 9, 'IMPORTANT')) self.style_state.apply(Command('stop_inline_style', 9, 'SHOUT')) def test_render_styles_component_multiple_invert(self): self.style_state.apply(Command('start_inline_style', 0, 'SHOUT')) self.style_state.apply(Command('start_inline_style', 0, 'IMPORTANT')) self.assertEqual( DOM.render_debug( self.style_state.render_styles('Test text', {}, [])), '<strong style="color: red;"><span style="text-transform: uppercase;">Test text</span></strong>' ) self.style_state.apply(Command('stop_inline_style', 9, 'SHOUT')) self.style_state.apply(Command('stop_inline_style', 9, 'IMPORTANT')) def test_render_styles_data(self): blocks = [ { 'key': '5s7g9', 'text': 'test', 'type': 'unstyled', 'depth': 0, 'inlineStyleRanges': [], 'entityRanges': [], }, ] def component(props): self.assertEqual(props['blocks'], blocks) self.assertEqual(props['block'], blocks[0]) self.assertEqual(props['inline_style_range']['style'], 'ITALIC') return None style_state = StyleState({ 'ITALIC': component, }) style_state.apply(Command('start_inline_style', 0, 'ITALIC')) style_state.render_styles('Test text', blocks[0], blocks) style_state.apply(Command('stop_inline_style', 9, 'ITALIC'))
def __init__(self, config={}): self.entity_decorators = config.get('entity_decorators', {}) self.wrapper_state = WrapperState(config.get('block_map', BLOCK_MAP)) self.style_state = StyleState(config.get('style_map', STYLE_MAP))
def setUp(self): DOM.use(DOM.STRING) self.style_state = StyleState( Options.map_styles(style_map) # type: ignore )
class TestStyleState(unittest.TestCase): def setUp(self): self.style_state = StyleState(style_map) def test_init(self): self.assertIsInstance(self.style_state, StyleState) def test_apply_start_inline_style(self): self.assertEqual(self.style_state.styles, []) self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.styles, ['ITALIC']) def test_apply_stop_inline_style(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) 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_unstyled_styled(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.is_unstyled(), False) def test_get_style_value_empty(self): self.assertEqual(self.style_state.get_style_value(), '') def test_get_style_value_single(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual(self.style_state.get_style_value(), '') 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_get_class_value(self): self.style_state.apply(Command('start_inline_style', 0, 'OUTLINE')) self.assertEqual(self.style_state.get_class_value(), 'outline') def test_add_node_unstyled(self): self.assertEqual( DOM.get_tag_name( self.style_state.add_node(DOM.create_element('p'), 'Test text')), 'textnode') self.assertEqual( DOM.get_text_content( self.style_state.add_node(DOM.create_element('p'), 'Test text')), 'Test text') def test_add_node_unicode(self): self.assertEqual( DOM.get_text_content( self.style_state.add_node(DOM.create_element('p'), '🍺')), '🍺') def test_add_node_styled(self): self.style_state.apply(Command('start_inline_style', 0, 'ITALIC')) self.assertEqual( DOM.get_tag_name( self.style_state.add_node(DOM.create_element('p'), 'Test text')), 'em') self.assertEqual( self.style_state.add_node(DOM.create_element('p'), 'Test text').get('style'), None) self.assertEqual( DOM.get_text_content( self.style_state.add_node(DOM.create_element('p'), 'Test text')), 'Test text') self.style_state.apply(Command('stop_inline_style', 9, 'ITALIC'))