def test_get_raises_exception(self): with self.assertRaises(ConfigException): self.assertEqual( Options.get(Options.map({ 'BOLD': 'strong', }, 'FALLBACK'), 'ITALIC', 'FALLBACK'), Options('BOLD', 'strong'))
def test_map_works(self): self.assertEqual( Options.map( { "BOLD": "strong", "HIGHLIGHT": { "element": "strong", "props": { "style": { "textDecoration": "underline" } }, }, }, "FALLBACK", ), { "BOLD": Options("BOLD", "strong"), "HIGHLIGHT": Options( "HIGHLIGHT", "strong", props={"style": { "textDecoration": "underline" }}, ), }, )
def test_get_uses_fallback(self): self.assertEqual( Options.get( Options.map({ 'BOLD': 'strong', 'FALLBACK': 'span', }, 'FALLBACK'), 'ITALIC', 'FALLBACK'), Options('FALLBACK', 'span'))
def test_get_raises_exception(self): with self.assertRaises(ConfigException): self.assertEqual( Options.get( Options.map({"BOLD": "strong"}, "FALLBACK"), "ITALIC", "FALLBACK", ), Options("BOLD", "strong"), )
def test_create_uses_fallback(self): self.assertEqual( Options.create( { "header-one": "h1", "fallback": "div" }, "header-two", "fallback", ), Options("header-two", "div"), )
def test_get_uses_fallback(self): self.assertEqual( Options.get( Options.map({ "BOLD": "strong", "FALLBACK": "span" }, "FALLBACK"), "ITALIC", "FALLBACK", ), Options("FALLBACK", "span"), )
def __init__(self, config: Optional[Config] = None) -> None: if config is None: config = {} self.composite_decorators = config.get("composite_decorators", []) self.entity_options = Options.map_entities( config.get("entity_decorators", {})) self.block_options = Options.map_blocks( config.get("block_map", BLOCK_MAP)) self.style_options = Options.map_styles( config.get("style_map", STYLE_MAP)) DOM.use(config.get("engine", DOM.STRING))
def __init__(self, config=None): if config is None: config = {} self.composite_decorators = config.get('composite_decorators', []) self.has_decorators = len(self.composite_decorators) > 0 self.entity_options = Options.map_entities( config.get('entity_decorators', {})) self.block_options = Options.map_blocks( config.get('block_map', BLOCK_MAP)) self.style_options = Options.map_styles( config.get('style_map', STYLE_MAP)) DOM.use(config.get('engine', DOM.STRING))
def render_entities(self, style_node): # We have a complete (start, stop) entity to render. if self.completed_entity is not None: entity_details = self.get_entity_details(self.completed_entity) opts = Options.for_entity(self.entity_decorators, entity_details['type']) props = entity_details['data'].copy() props['entity'] = { 'type': entity_details['type'], } if len(self.element_stack) == 1: children = self.element_stack[0] else: children = DOM.create_element() for n in self.element_stack: DOM.append_child(children, n) self.completed_entity = None self.element_stack = [] # Is there still another entity? (adjacent) if so add the current style_node for it. if self.has_entity(): self.element_stack.append(style_node) return DOM.create_element(opts.element, props, children) if self.has_entity(): self.element_stack.append(style_node) return None return style_node
def render_entities(self, style_node): if self.completed_entity is not None: entity_details = self.get_entity_details(self.completed_entity) opts = Options.for_entity(self.entity_decorators, entity_details['type']) props = entity_details['data'].copy() props['entity'] = { 'type': entity_details['type'], } nodes = DOM.create_element() for n in self.element_stack: DOM.append_child(nodes, n) elt = DOM.create_element(opts.element, props, nodes) self.completed_entity = None self.element_stack = [] elif self.has_no_entity(): elt = style_node else: self.element_stack.append(style_node) elt = None return elt
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 test_render_entities_data(self): blocks = [{ "key": "5s7g9", "text": "test", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], }] def component(props): self.assertEqual(props["entity"]["blocks"], blocks) self.assertEqual(props["entity"]["block"], blocks[0]) self.assertEqual(props["entity"]["type"], "LINK") self.assertEqual(props["entity"]["mutability"], "MUTABLE") self.assertEqual(props["entity"]["entity_range"]["key"], "0") return None entity_state = EntityState(Options.map_entities({"LINK": component}), entity_map) entity_state.apply(Command("start_entity", 0, "0")) entity_state.render_entities("Test text", blocks[0], blocks) entity_state.apply(Command("stop_entity", 9, "0")) entity_state.render_entities("Test text", blocks[0], blocks)
def test_get_works(self): self.assertEqual( Options.get( Options.map( { 'BOLD': 'strong', 'HIGHLIGHT': { 'element': 'strong', 'props': { 'style': { 'textDecoration': 'underline' } }, }, }, 'FALLBACK'), 'BOLD', 'FALLBACK'), Options('BOLD', 'strong'))
def test_render_entities_data(self): blocks = [ { 'key': '5s7g9', 'text': 'test', 'type': 'unstyled', 'depth': 0, 'inlineStyleRanges': [], 'entityRanges': [], }, ] def component(props): self.assertEqual(props['entity']['blocks'], blocks) self.assertEqual(props['entity']['block'], blocks[0]) self.assertEqual(props['entity']['type'], 'LINK') self.assertEqual(props['entity']['mutability'], 'MUTABLE') self.assertEqual(props['entity']['entity_range']['key'], '0') return None entity_state = EntityState(Options.map_entities({ 'LINK': component, }), entity_map) entity_state.apply(Command('start_entity', 0, '0')) entity_state.render_entities('Test text', blocks[0], blocks) entity_state.apply(Command('stop_entity', 9, '0')) entity_state.render_entities('Test text', blocks[0], blocks)
def render_styles(self, text_node): node = text_node if not self.is_empty(): # Nest the tags. for s in sorted(self.styles, reverse=True): opt = Options.for_style(self.style_map, s) node = DOM.create_element(opt.element, opt.props, node) return node
def test_render_entities_data_no_mutability(self): def component(props): self.assertEqual(props["entity"]["mutability"], None) return None entity_state = EntityState(Options.map_entities({"LINK": component}), entity_map) entity_state.apply(Command("start_entity", 0, "2")) entity_state.render_entities("Test text", {}, []) entity_state.apply(Command("stop_entity", 9, "2")) entity_state.render_entities("Test text", {}, [])
def test_render_entities_data_no_mutability(self): def component(props): self.assertEqual(props['entity']['mutability'], None) return None entity_state = EntityState(Options.map_entities({ 'LINK': component, }), entity_map) entity_state.apply(Command('start_entity', 0, '2')) entity_state.render_entities('Test text', {}, []) entity_state.apply(Command('stop_entity', 9, '2')) entity_state.render_entities('Test text', {}, [])
def element_for(self, block, block_content): type_ = block['type'] depth = block['depth'] options = Options.for_block(self.block_map, type_) props = dict(options.props) props['block'] = block props['blocks'] = self.blocks # Make an element from the options specified in the block map. elt = DOM.create_element(options.element, props, block_content) parent = self.parent_for(options, depth, elt) return parent
def element_for(self, block, block_content): type_ = block['type'] if 'type' in block else 'unstyled' depth = block['depth'] if 'depth' in block else 0 options = Options.get(self.block_options, type_, BLOCK_TYPES.FALLBACK) props = dict(options.props) props['block'] = block props['blocks'] = self.blocks # Make an element from the options specified in the block map. elt = DOM.create_element(options.element, props, block_content) parent = self.parent_for(options, depth, elt) return parent
def setUp(self): DOM.use(DOM.STRING) self.wrapper_state = WrapperState( Options.map_blocks({ 'header-one': 'h1', 'unstyled': 'div', 'atomic': lambda props: props['children'], 'ignore': None, 'blockquote': blockquote, 'ordered-list-item': { 'element': list_item, 'wrapper': ordered_list }, }), [])
def render_styles(self, decorated_node, block, blocks): node = decorated_node if not self.is_empty(): # Nest the tags. for style in sorted(self.styles, reverse=True): opt = Options.for_style(self.style_map, style) props = dict(opt.props) props['block'] = block props['blocks'] = blocks props['inline_style_range'] = { 'style': style, } node = DOM.create_element(opt.element, props, node) return node
def render_entities(self, style_node: Element, block: Block, blocks: Sequence[Block]) -> Element: # We have a complete (start, stop) entity to render. if self.completed_entity is not None: entity_details = self.get_entity_details(self.completed_entity) options = Options.get( self.entity_options, entity_details["type"], ENTITY_TYPES.FALLBACK, ) props = entity_details["data"].copy() props["entity"] = { "type": entity_details["type"], "mutability": entity_details["mutability"] if "mutability" in entity_details else None, "block": block, "blocks": blocks, "entity_range": { "key": self.completed_entity }, } if len(self.element_stack) == 1: children = self.element_stack[0] else: children = DOM.create_element() for n in self.element_stack: DOM.append_child(children, n) self.completed_entity = None self.element_stack = [] # Is there still another entity? (adjacent) if so add the current style_node for it. if self.has_entity(): self.element_stack.append(style_node) return DOM.create_element(options.element, props, children) if self.has_entity(): self.element_stack.append(style_node) return None return style_node
def element_for( self, block: Block, block_content: Union[Element, Sequence[Element]]) -> Element: type_ = block["type"] if "type" in block else "unstyled" depth = block["depth"] if "depth" in block else 0 options = Options.get(self.block_options, type_, BLOCK_TYPES.FALLBACK) props = dict(options.props) props["block"] = block props["blocks"] = self.blocks # Make an element from the options specified in the block map. elt = DOM.create_element(options.element, props, block_content) parent = self.parent_for(options, depth, elt) return parent
def render_styles(self, decorated_node: Element, block: Block, blocks: Sequence[Block]) -> Element: node = decorated_node if not self.is_empty(): # This will mutate self.styles, but it’s going to be reset after rendering anyway. self.styles.sort(reverse=True) # Nest the tags. for style in self.styles: options = Options.get(self.style_options, style, INLINE_STYLES.FALLBACK) props = dict(options.props) props["block"] = block props["blocks"] = blocks props["inline_style_range"] = {"style": style} node = DOM.create_element(options.element, props, node) return node
def element_for(self, block, block_content): type_ = block['type'] depth = block['depth'] data = block.get('data', {}) options = Options.for_block(self.block_map, type_) props = dict(options.props) props['block'] = { 'type': type_, 'depth': depth, 'data': data, } # Make an element from the options specified in the block map. elt = DOM.create_element(options.element, props, block_content) parent = self.parent_for(options, depth, elt) return parent
def test_element_for_data(self): blocks = [ { 'key': '5s7g9', 'text': 'test', 'type': 'unstyled', 'depth': 0, 'inlineStyleRanges': [], 'entityRanges': [], }, ] def unstyled(props): self.assertEqual(props['blocks'], blocks) self.assertEqual(props['block'], blocks[0]) WrapperState(Options.map_blocks({'unstyled': unstyled}), blocks).element_for(blocks[0], 'test')
def test_element_for_data(self): blocks = [ { "key": "5s7g9", "text": "test", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], } ] def unstyled(props): self.assertEqual(props["blocks"], blocks) self.assertEqual(props["block"], blocks[0]) WrapperState( Options.map_blocks({"unstyled": unstyled}), blocks ).element_for(blocks[0], "test")
def setUp(self): DOM.use(DOM.STRING) self.wrapper_state = WrapperState( Options.map_blocks( { "header-one": "h1", "unstyled": "div", "atomic": lambda props: props["children"], "ignore": None, "blockquote": blockquote, "ordered-list-item": { "element": list_item, "wrapper": ordered_list, }, } ), [], )
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 render_entities(self, style_node: Element, block: Block, blocks: Sequence[Block]) -> Element: # We have a complete (start, stop) entity to render. if self.completed_entity is not None: entity_details = self.get_entity_details(self.completed_entity) options = Options.get(self.entity_options, entity_details['type'], ENTITY_TYPES.FALLBACK) props = entity_details['data'].copy() props['entity'] = { 'type': entity_details['type'], 'mutability': entity_details['mutability'] if 'mutability' in entity_details else None, 'block': block, 'blocks': blocks, 'entity_range': { 'key': self.completed_entity, }, } if len(self.element_stack) == 1: children = self.element_stack[0] else: children = DOM.create_element() for n in self.element_stack: DOM.append_child(children, n) self.completed_entity = None self.element_stack = [] # Is there still another entity? (adjacent) if so add the current style_node for it. if self.has_entity(): self.element_stack.append(style_node) return DOM.create_element(options.element, props, children) if self.has_entity(): self.element_stack.append(style_node) return None return style_node
def test_for_block_uses_fallback(self): self.assertEqual(Options.for_block({'header-one': 'h1', 'fallback': 'div'}, 'header-two'), Options('header-two', 'div'))
def test_for_block_raises_missing_type(self): with self.assertRaises(ConfigException): Options.for_block({'header-one': 'h1'}, 'header-two')
def test_for_block_simplest(self): self.assertEqual(Options.for_block({'unordered-list-item': 'li'}, 'unordered-list-item'), Options('unordered-list-item', 'li'))
def test_for_style_simplest(self): self.assertEqual(Options.for_style({'ITALIC': 'em'}, 'ITALIC'), Options('ITALIC', 'em'))
def test_for_block_raises_missing_element(self): with self.assertRaises(ConfigException): Options.for_block({'header-one': {}}, 'header-one')
def test_for_style_raises_missing_type(self): with self.assertRaises(ConfigException): Options.for_style({'BOLD': 'strong'}, 'CODE')
def test_for_style_uses_fallback(self): self.assertEqual(Options.for_style({'BOLD': 'strong', 'FALLBACK': 'span'}, 'CODE'), Options('CODE', 'span'))
def test_for_entity_raises_missing_element(self): with self.assertRaises(ConfigException): Options.for_entity({'HORIZONTAL_RULE': {}}, 'HORIZONTAL_RULE')
def test_for_entity_raises_missing_type(self): with self.assertRaises(ConfigException): Options.for_entity({'HORIZONTAL_RULE': 'hr'}, 'TEST')
def test_for_entity_uses_fallback(self): self.assertEqual(Options.for_entity({'HORIZONTAL_RULE': 'hr', 'FALLBACK': 'div'}, 'TEST'), Options('TEST', 'div'))
def test_for_entity_simplest(self): self.assertEqual(Options.for_entity({'HORIZONTAL_RULE': 'hr'}, 'HORIZONTAL_RULE'), Options('HORIZONTAL_RULE', 'hr'))
def test_for_style_raises_missing_element(self): with self.assertRaises(ConfigException): Options.for_style({'BOLD': {}}, 'BOLD')