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 HTML(): """ Entry point of the exporter. Combines entity, wrapper and style state to generate the right HTML nodes. """ 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 render(self, content_state): """ Starts the export process on a given piece of content state. """ entity_map = content_state.get('entityMap', {}) for block in content_state.get('blocks', []): self.render_block(block, entity_map) return self.wrapper_state.to_string() def render_block(self, block, entity_map): element = self.wrapper_state.element_for(block) entity_state = EntityState(element, 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) self.style_state.add_node(entity_state.current_parent(), text) 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): 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'))