diff --git a/korona/html/construct.py b/korona/html/construct.py index 10c80ac..c560973 100644 --- a/korona/html/construct.py +++ b/korona/html/construct.py @@ -10,6 +10,7 @@ anchor_tag, abbr_tag, acronym_tag, + area_tag, bold_tag, base_tag ) @@ -98,7 +99,7 @@ def get_coords(self, shape, coords): """Returns coordinates after a series of validations. Args: - shape (str): Shape of a link (Ex. rect/circle/poly) + shape (str): Shape of a link (Ex. rect/circle/poly/etc.) coords (mixed): A list/tuple/str of coordinate values. Returns: @@ -143,6 +144,9 @@ def pre_validate(self, href, attribute_name, value): 'href attribute is set.' .format(attribute_name)) + if not isinstance(value, str): + raise ValueError(': {0} should be a string'.format(value)) + def validate_values(self, href, attribute_name, value): """Validates whether the given attribute value is a valid value or not. Some of the attributes have confined values. Even if we give some @@ -199,6 +203,167 @@ def construct_tag(self): return acronym_tag.render(self.values) +class Area(object): + """Class for constructing area tag. + + Args: + alt (str): Specifies an alternate text for the area. Required if the + href attribute is present. + coords (mixed): Specifies the coordinates of the area. + download (str): Specifies that the target will be downloaded when a + user clicks on the hyper link. + href (str): Specifies the hyperlink target for the area. + hreflang (str): Specifies the language of the target URL. + media (str): Specifies what media/device the target URL is optimized + for. + nohref(bool): Specifies that an area has no associated link. + rel (str): Specifies the relationship between the current document + and the target URL. + shape (str): Specifies the shape of the area. + target(str): Specifies where to open the target URL. + type (str): Specifies the media type of the target URL. + """ + def __init__(self, + alt=None, + coords=None, + download=None, + href=None, + hreflang=None, + media=None, + nohref=False, + rel=None, + shape=None, + target=None, + type=None): + self.tag = 'area' + self.validate_alt(href=href, attribute_name='alt', value=alt) + coordinates = self.get_coords(shape=shape, coords=coords) + self.pre_validate(href=href, attribute_name='download', value=download) + self.validate_string(value=href) + self.pre_validate(href=href, attribute_name='hreflang', value=hreflang) + self.pre_validate(href=href, attribute_name='media', value=media) + self.validate_values(href=href, attribute_name='rel', value=rel) + self.validate_values(href=href, attribute_name='shape', value=shape) + self.pre_validate(href=href, attribute_name='targte', value=target) + self.pre_validate(href=href, attribute_name='type', value=type) + self.values = {'alt': alt, + 'coords': coordinates, + 'download': download, + 'href': href, + 'hreflang': hreflang, + 'media': media, + 'nohref': nohref, + 'rel': rel, + 'shape': shape, + 'target': target, + 'type': type} + + def construct_tag(self): + """Returns the constructed area tag .""" + return area_tag.render(self.values) + + def validate_string(self, value): + """Validates whether the given value is a string or not.""" + if not value: + return + + # If the attribute value is not a string raise a ValueError. + if not isinstance(value, str): + raise ValueError(': {0} should be a string'.format(value)) + + def validate_alt(self, href, attribute_name, value): + """Validates area's alt attribute.""" + if href and not value: + raise AttributeError(': {0} attribute is required if the ' + 'href attribute is present.' + .format(attribute_name)) + + if not href and value: + raise AttributeError(': {0} attribute is only used when ' + 'href attribute is set.' + .format(attribute_name)) + + if value and not isinstance(value, str): + raise ValueError(': {0} should be a string'.format(value)) + + def pre_validate(self, href, attribute_name, value): + """Validates whether an attribute is dependant on another attribute or + not. Some of the attributes requires href attribute to be set. + """ + if not value: + return + + if not href: + raise AttributeError(': {0} attribute is only used when ' + 'href attribute is set.' + .format(attribute_name)) + + if not isinstance(value, str): + raise ValueError(': {0} should be a string'.format(value)) + + def get_coords(self, shape, coords): + """Returns coordinates after a series of validations. + + Args: + shape (str): Shape of a link (Ex. rect/circle/poly/etc.) + coords (mixed): A list/tuple/str of coordinate values. + + Returns: + str: A string of coordinate values. + """ + if not coords: + return + + if (coords and not shape) or (shape and not coords): + raise AttributeError(': shape attribute should be present ' + 'when coords are specified.') + + if isinstance(coords, str): + coords = coords.split(',') + + if isinstance(coords, dict): + raise ValueError(': {0} should be either list/tuple/str not ' + 'a dictionary' + .format(coords)) + + if shape == 'rect' and len(coords) != RECTANGLE_SHAPE_COORDINATES: + raise ValueError(': {0} coordinates should be given for ' + 'rectangle shape' + .format(RECTANGLE_SHAPE_COORDINATES)) + + if shape == 'circle' and len(coords) != CIRCLE_SHAPE_COORDINATES: + raise ValueError(': {0} coordinates should be given for ' + 'circle shape' + .format(CIRCLE_SHAPE_COORDINATES)) + + return ','.join(str(coord) for coord in coords) + + def validate_values(self, href, attribute_name, value): + """Validates whether the given attribute value is a valid value or not. + Some of the attributes have confined values. Even if we give some + other value, the html output would not be correct. + """ + if not value: + return + + if attribute_name == 'rel' and not href: + raise AttributeError(': {attribute_name} is only used when ' + 'href attribute is set.' + .format(attribute_name=attribute_name)) + + if not isinstance(value, str): + raise ValueError(': {0} should be a string value.' + .format(attribute_name)) + + attribute_values = TAG_ATTRIBUTES[self.tag][attribute_name]['values'] + + if value not in attribute_values: + raise AttributeError(': {attribute_name} attribute values ' + 'should be one of these: {values}' + .format(attribute_name=attribute_name, + values=','.join(attribute_values))) + + class B(object): """Class for constructing bold tag. diff --git a/korona/templates/html/__init__.py b/korona/templates/html/__init__.py index 80c799e..8517393 100644 --- a/korona/templates/html/__init__.py +++ b/korona/templates/html/__init__.py @@ -4,6 +4,7 @@ anchor_tag, abbr_tag, acronym_tag, + area_tag, bold_tag, base_tag ) diff --git a/korona/templates/html/tags.py b/korona/templates/html/tags.py index c199dfd..57528e0 100644 --- a/korona/templates/html/tags.py +++ b/korona/templates/html/tags.py @@ -26,6 +26,20 @@ {% if text -%} {{ text }} {% endif -%} """) +area_tag = env.from_string(""" + + {% if target %} target="{{ target }}" {% endif -%} +""") + bold_tag = env.from_string(""" {% if text -%} {{ text }} {% endif -%} """) diff --git a/tests/test_tags.py b/tests/test_tags.py index 953e2b6..a4b1e3d 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -6,6 +6,7 @@ A, Abbr, Acronym, + Area, B, Base ) @@ -13,6 +14,7 @@ anchor_tag, abbr_tag, acronym_tag, + area_tag, bold_tag, base_tag ) @@ -79,6 +81,8 @@ def test_construct_anchor_tag_coords(attributes): @parametrize('attributes,exception,error_msg', [ ({'rel': 1}, AttributeError, 'only used when href attribute is set.'), + ({'type': 1}, AttributeError, 'only used when href attribute is set.'), + ({'href': 'abc', 'type': 1}, ValueError, 'should be a string'), ({'charset': 1}, ValueError, 'should be a string'), ({'rel': 1, 'href': 'www.google.com'}, ValueError, 'should be a string'), ({'shape': 'circle', 'coords': [1, 2, 3, 4]}, @@ -144,6 +148,104 @@ def test_construct_acronym_tag(attributes): assert acronym.construct_tag() == acronym_tag.render(attributes) +@parametrize('attributes', [ + ({'href': 'www.google.com', 'alt': 'abc'}), + ({'href': 'www.google.com', 'alt': 'abc', 'rel': 'nofollow'}), + ({'download': 'abc', + 'href': 'www.google.com', + 'hreflang': 'en', + 'rel': 'nofollow', + 'target': '_blank', + 'type': 'text/html', + 'alt': 'abc'}), + ({'href': 'www.google.com', 'alt': 'abc'}) +]) +def test_construct_area_tag(attributes): + """Test for validating whether the area tag is constructed correctly or + not. + """ + area = Area(**attributes) + assert area.construct_tag() == area_tag.render(attributes) + + +@parametrize('attributes', [ + ({'shape': 'rect', 'coords': [1, 2, 3, 4]}), + ({'shape': 'circle', 'coords': [1, 2, 3]}), + ({'shape': 'poly', 'coords': [1, 2, 3, 4, 5, 6]}), + ({'shape': 'rect', 'coords': (1, 2, 3, 4)}), + ({'shape': 'circle', 'coords': (1, 2, 3)}), + ({'shape': 'poly', 'coords': (1, 2, 3, 4, 5, 6)}), + ({'shape': 'rect', 'coords': '1,2,3,4'}), + ({'shape': 'circle', 'coords': '1,2,3'}), + ({'shape': 'poly', 'coords': '1,2,3,4,5,6'}), +]) +def test_construct_area_tag_coords(attributes): + """Test for validating the shape and coordinate attributes given.""" + area = Area(**attributes) + + if not isinstance(attributes['coords'], str): + attributes['coords'] = (','.join(str(coord) + for coord in attributes['coords'])) + + assert area.construct_tag() == area_tag.render(attributes) + + +@parametrize('attributes,exception,error_msg', [ + ({'rel': 1}, AttributeError, 'only used when href attribute is set.'), + ({'rel': 1, 'href': 'www.google.com', 'alt': 'abc'}, + ValueError, + 'should be a string'), + ({'shape': 'circle', 'coords': [1, 2, 3, 4]}, + ValueError, + 'coordinates should be given for circle shape'), + ({'shape': 'rect', 'coords': [1, 2, 3, 4, 5]}, + ValueError, + 'coordinates should be given for rectangle shape'), + ({'shape': 'circle', 'coords': [1, 2]}, + ValueError, + 'coordinates should be given for circle shape'), + ({'shape': 'rect', 'coords': [1, 2, 3]}, + ValueError, + 'coordinates should be given for rectangle shape'), + ({'shape': 'rect', 'coords': {'x': 1, 'y': 2}}, + ValueError, + 'should be either list/tuple/str not a dictionary'), + ({'coords': [1, 2, 3]}, + AttributeError, + 'shape attribute should be present when coords are specified'), + ({'download': 'abc'}, + AttributeError, + 'only used when href attribute is set.'), + ({'href': 'abc', 'alt': 'abc', 'download': 123}, + ValueError, + 'should be a string'), + ({'href': 123, 'alt': 'abc'}, + ValueError, + 'should be a string'), + ({'href': 'www.google.com', 'alt': 'abc', 'rel': 'abc'}, + AttributeError, + 'attribute values should be one of these'), + ({'shape': 'abc'}, + AttributeError, + 'attribute values should be one of these'), + ({'alt': 'abc'}, + AttributeError, + 'attribute is only used when href attribute is set.'), + ({'href': 'abc'}, + AttributeError, + 'attribute is required if the href attribute is present'), + ({'href': 'www.google.com', 'alt': 123}, + ValueError, + 'should be a string') +]) +def test_construct_area_tag_error(attributes, exception, error_msg): + """Test for validating area tag's attributes.""" + with pytest.raises(exception) as exc: + Area(**attributes) + + assert error_msg in str(exc) + + @parametrize('attributes', [ ({'text': 'abc'}), ({'text': None})