Permalink
Browse files

bears/hypertext: Add HTMLHintBear

Add `HTMLHintBear` that was worked upon by @AsnelChristian.
This PR is a continuation of @AsnelChristian's PR with some
modifications.

Closes #635
  • Loading branch information...
yash-nisar committed Aug 6, 2017
1 parent 6bf0d61 commit b49553a8d62cae9989d0778728adb09fecb7fffb
@@ -0,0 +1,177 @@
import json
from coalib.bearlib.abstractions.Linter import linter
from dependency_management.requirements.NpmRequirement import NpmRequirement
@linter(executable='htmlhint',
output_format='regex',
output_regex=r'(?P<filename>.+):(?P<line>\d+):(?P<column>\d+):\s*'
r'(?P<message>.+) \[(?P<severity>error|warning).+\]')
class HTMLHintBear:
"""
Checks HTML code with ``htmlhint`` for possible problems. Attempts to catch
little mistakes and enforces a code style guide on HTML files.
"""
LANGUAGES = {'HTML'}
REQUIREMENTS = {NpmRequirement('htmlhint', '0.9.13')}
AUTHORS = {'The coala developers'}
AUTHORS_EMAILS = {'coala-devel@googlegroups.com'}
LICENSE = 'AGPL-3.0'
CAN_DETECT = {'Syntax', 'Formatting', 'Duplication', 'Code Simplification'}
SEE_MORE = 'https://github.com/yaniswang/HTMLHint'
@staticmethod
def generate_config(filename, file,
enforce_lowercase_tagname: bool=True,
enforce_lowercase_attribute: bool=True,
require_attribute_value_in_double_quotes: bool=False,
prohibit_empty_value_for_attribute: bool=False,
prohibit_attribute_duplication: bool=True,
require_doctype_at_beginning: bool=True,
enforce_tag_pair: bool=True,
enforce_self_close_empty_tag: bool=True,
require_escaped_special_characters: bool=False,
require_unique_attribute_id: bool=True,
require_title_tag: bool=True,
prohibit_script_in_head: bool=False,
require_alt_attribute: bool=True,
enforce_id_class_naming_convention: str=None,
prohibit_inline_style: bool=True,
require_relative_links_in_href: bool=None,
prohibit_unsafe_characters: bool=True,
prohibit_inline_script: bool=False,
prohibit_style_tag: bool=False,
htmlhint_config: str=''):
"""
:param enforce_lowercase_tagname:
Enforce the tagnames to be written in lowercase.
For example: If set to ``True``, prefer ``<span><div>`` over
``<SPAN><BR>``.
:param enforce_lowercase_attribute:
Enforce the attribute names to be written in lowercase.
For example: If set to ``True``, prefer
``<img src="test.png" alt="test">`` over
``<img SRC="test.png" ALT="test">``.
:param require_attribute_value_in_double_quotes:
Allow attribute values to be enclosed in double quotes.
For example: If set to ``True``, prefer ``<a href="" title="abc">``
over ``<a href='' title=abc>``.
:param prohibit_empty_value_for_attribute:
Disallow empty values for attributes.
For example: If set to ``True``, prefer
``<input type="button" disabled="disabled">`` over
``<input type="button" disabled>``.
:param prohibit_attribute_duplication:
Disallow defining of the same attribute more than once in
a tag. For example: If set to ``True``, prefer
``<img src="a.png" />`` over ``<img src="a.png" src="b.png" />``.
:param require_doctype_at_beginning:
Enforce the ``<!DOCTYPE>`` declaration at the beginning.
For example: If set to ``True``, prefer ``<!DOCTYPE HTML><html>``
over ``<!--comment--><!DOCTYPE HTML><html>``.
:param enforce_tag_pair:
Enforce the tags to be paired.
:param enforce_self_close_empty_tag:
Enforce the empty tag to be closed by self.
For example: If set to ``True``, prefer ``<br />`` over ``<br>``.
:param require_escaped_special_characters:
Require the special characters to be escaped.
For example: If set to ``True``, prefer
``<span>aaa&gt;bbb&lt;ccc</span>`` over
``<span>aaa>bbb<ccc</span>``.
:param require_unique_attribute_id:
Require the ID attributes to be unique in the document.
For example: If set to ``True``, prefer
``<div id="id1"></div><div id="id2"></div>`` over
``<div id="id1"></div><div id="id1"></div>``.
:param require_title_tag:
Require the ``<title>`` to be present in the ``<head>`` tag.
:param prohibit_script_in_head:
Prohibit the use of the ``<script>`` tag in the ``<head>`` tag.
:param require_alt_attribute:
Require ``alt`` attribute when using images (``img`` tag) and links
(``href`` tag).
For example: If set to ``True``, prefer this::
<img src="test.png" alt="test">
<input type="image" alt="test">
over this::
<img src="test.png">
<input type="image">
:param enforce_id_class_naming_convention:
Possible values are ``underline``, ``dash`` and ``hump``.
Require the ``id`` and ``class`` values to be set according to
the given rules.
For example: If set to ``underline``, prefer
``<div id="aaa_bbb">``.
For example: If set to ``dash``, prefer ``<div id="aaa-bbb">``.
:param prohibit_inline_style:
Disallow the use of inline ``style`` attribute.
For example: If set to ``True``, ``<div style="color:red"></div>``
will raise a warning.
:param require_relative_links_in_href:
If ``True``, enforce relative links in the ``href`` attribute and
if ``False``, enforce absolute links.
:param prohibit_unsafe_characters:
Prohibit the use of unsafe characters in attribute values.
For example: If set to ``True``,
``<li><a href="https://vimeo.com//56931059‎\u0009‎">2012</a></li>``
will raise a warning.
:param prohibit_inline_script:
Disallow the use of inline scripts.
For example: If set to ``True``, this will raise a warning::
<img src="test.gif" onclick="alert(1);">
<img src="javascript:alert(1)">
<a href="javascript:alert(1)">test1</a>
:param prohibit_style_tag:
Prohibit the use of ``style`` tag.
For example: If set to ``True``,
``<body><style type="text/css"></style></body>``
will raise a warning.
"""
if htmlhint_config:
return None
else:
options = {
'tagname-lowercase': enforce_lowercase_tagname,
'attr-lowercase': enforce_lowercase_attribute,
'attr-value-double-quotes':
require_attribute_value_in_double_quotes,
'attr-value-not_empty': prohibit_empty_value_for_attribute,
'attr-no-duplication': prohibit_attribute_duplication,
'doctype-first': require_doctype_at_beginning,
'tag-pair': enforce_tag_pair,
'tag-self-close': enforce_self_close_empty_tag,
'spec-char-escape': require_escaped_special_characters,
'id-unique': require_unique_attribute_id,
'title-require': require_title_tag,
'head-script-disabled': prohibit_script_in_head,
'alt-require': require_alt_attribute,
'id-class-value': enforce_id_class_naming_convention,
'inline-style-disabled': prohibit_inline_style,
'attr-unsafe-chars': prohibit_unsafe_characters,
'inline-script-disabled': prohibit_inline_script,
'style-disabled': prohibit_style_tag,
'href-abs-or-rel':
'false' if require_relative_links_in_href is None
else 'rel' if require_relative_links_in_href else 'abs'
}
return json.dumps(options)
@staticmethod
def create_arguments(filename, file, config_file, htmlhint_config: str=''):
"""
:param htmlhint_config:
The path to a custom ``.htmlhintrc`` config file.
"""
return (filename, '--config',
htmlhint_config if htmlhint_config else config_file,
'--format', 'unix')
View
@@ -15,6 +15,7 @@
"eslint": "~2",
"eslint-plugin-import": "~1",
"happiness": "~7.1.2",
"htmlhint": "~0.9.13",
"jshint": "~2",
"postcss-cli": "~2",
"pug-lint": "~2.4.0",
@@ -0,0 +1,209 @@
import os
from queue import Queue
from bears.hypertext.HTMLHintBear import HTMLHintBear
from coalib.results.Result import Result
from coalib.results.RESULT_SEVERITY import RESULT_SEVERITY
from coalib.settings.Section import Section
from coalib.testing.BearTestHelper import generate_skip_decorator
from coalib.testing.LocalBearTestHelper import LocalBearTestHelper
def get_testfile_path(name):
return os.path.join(os.path.dirname(__file__),
'htmlhint_test_files',
name)
def load_testfile(name):
with open(get_testfile_path(name)) as f:
return f.readlines()
@generate_skip_decorator(HTMLHintBear)
class HTMLHintBearTest(LocalBearTestHelper):
def setUp(self):
self.uut = HTMLHintBear(Section('name'), Queue())
def test_bad_lowercase_tagname(self):
filename = 'test_bad_lowercase_tagname.html'
file_contents = load_testfile(filename)
self.check_results(
self.uut,
file_contents,
[Result.from_values('HTMLHintBear',
message='The html element name of [ SPAN ] '
'must be in lowercase.',
file=get_testfile_path(filename),
line=2,
column=1,
end_line=2,
end_column=1,
severity=RESULT_SEVERITY.MAJOR),
Result.from_values('HTMLHintBear',
message='The html element name of [ SPAN ] '
'must be in lowercase.',
file=get_testfile_path(filename),
line=3,
column=1,
end_line=3,
end_column=1,
severity=RESULT_SEVERITY.MAJOR)],
filename=get_testfile_path(filename))
def test_bad_lowercase_attribute(self):
filename = 'test_bad_lowercase_attribute.html'
file_contents = load_testfile(filename)
self.check_results(
self.uut,
file_contents,
[Result.from_values('HTMLHintBear',
message='The attribute name of [ TYPE ] must '
'be in lowercase.',
file=get_testfile_path(filename),
line=2,
column=7,
end_line=2,
end_column=7,
severity=RESULT_SEVERITY.MAJOR)],
filename=get_testfile_path(filename))
def test_attribute_duplication(self):
filename = 'test_attribute_duplication.html'
file_contents = load_testfile(filename)
self.check_results(
self.uut,
file_contents,
[Result.from_values('HTMLHintBear',
message='Duplicate of attribute name [ type ] '
'was found.',
file=get_testfile_path(filename),
line=2,
column=36,
end_line=2,
end_column=36,
severity=RESULT_SEVERITY.MAJOR)],
filename=get_testfile_path(filename))
def test_bad_tag_pair(self):
filename = 'test_bad_tag_pair.html'
file_contents = load_testfile(filename)
self.check_results(
self.uut,
file_contents,
[Result.from_values('HTMLHintBear',
message='Tag must be paired, missing: '
'[ </li> ], start tag match failed '
'[ <li> ] on line 2.',
file=get_testfile_path(filename),
line=2,
column=9,
end_line=2,
end_column=9,
severity=RESULT_SEVERITY.MAJOR)],
filename=get_testfile_path(filename))
def test_doctype_at_beginning(self):
filename = 'test_doctype_at_beginning.html'
file_contents = load_testfile(filename)
self.check_results(
self.uut,
file_contents,
[Result.from_values('HTMLHintBear',
message='Doctype must be declared first.',
file=get_testfile_path(filename),
line=1,
column=1,
end_line=1,
end_column=1,
severity=RESULT_SEVERITY.MAJOR)],
filename=get_testfile_path(filename))
def test_bad_unique_attribute_id(self):
filename = 'test_bad_unique_attribute_id.html'
file_contents = load_testfile(filename)
self.check_results(
self.uut,
file_contents,
[Result.from_values('HTMLHintBear',
message='The id value [ id1 ] must be unique.',
file=get_testfile_path(filename),
line=2,
column=25,
end_line=2,
end_column=25,
severity=RESULT_SEVERITY.MAJOR)],
filename=get_testfile_path(filename))
def test_require_alt_attribute(self):
filename = 'test_require_alt_attribute.html'
file_contents = load_testfile(filename)
self.check_results(
self.uut,
file_contents,
[Result.from_values('HTMLHintBear',
message='An alt attribute must be present on '
'<img> elements.',
file=get_testfile_path(filename),
line=2,
column=5,
end_line=2,
end_column=5,
severity=RESULT_SEVERITY.NORMAL)],
filename=get_testfile_path(filename))
def test_config_file(self):
filename = 'test_bad_inline_style.html'
file_contents = load_testfile(filename)
self.check_results(
self.uut,
file_contents,
[Result.from_values('HTMLHintBear',
message='Inline style [ style="color:red" ] '
'cannot be use.',
file=get_testfile_path(filename),
line=2,
column=5,
end_line=2,
end_column=5,
severity=RESULT_SEVERITY.NORMAL)],
filename=get_testfile_path(filename),
settings={'htmlhint_config': get_testfile_path('.htmlhintrc')})
def test_absolute_links_in_href(self):
filename = 'test_links_in_href.html'
file_contents = load_testfile(filename)
self.check_results(
self.uut,
file_contents,
[Result.from_values('HTMLHintBear',
message='The value of the href attribute '
'[ test.html ] must be absolute.',
file=get_testfile_path(filename),
line=2,
column=3,
end_line=2,
end_column=3,
severity=RESULT_SEVERITY.NORMAL)],
filename=get_testfile_path(filename),
settings={'require_relative_links_in_href': False})
def test_relative_links_in_href(self):
filename = 'test_links_in_href.html'
file_contents = load_testfile(filename)
self.check_results(
self.uut,
file_contents,
[Result.from_values('HTMLHintBear',
message='The value of the href attribute '
'[ http://www.alibaba.com/ ] must be '
'relative.',
file=get_testfile_path(filename),
line=3,
column=3,
end_line=3,
end_column=3,
severity=RESULT_SEVERITY.NORMAL)],
filename=get_testfile_path(filename),
settings={'require_relative_links_in_href': True})
Oops, something went wrong.

0 comments on commit b49553a

Please sign in to comment.