├── .github └── workflows │ ├── pypi-deploy.yml │ └── tests.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── html5validator ├── __init__.py ├── cli.py ├── tests │ ├── README.rst │ ├── __init__.py │ ├── angularjs │ │ └── index.html │ ├── config_files │ │ ├── angularjs.yaml │ │ ├── angularjs_normal.yaml │ │ ├── extra.yaml │ │ ├── format_flags │ │ │ ├── gnu_invalid.yaml │ │ │ ├── gnu_valid.yaml │ │ │ ├── json_invalid.yaml │ │ │ ├── json_valid.yaml │ │ │ ├── text_invalid.yaml │ │ │ ├── text_valid.yaml │ │ │ ├── xml_invalid.yaml │ │ │ └── xml_valid.yaml │ │ ├── ignore_and_ignorere.yaml │ │ ├── invalid.yaml │ │ ├── invalid_css.yaml │ │ ├── invalid_css_only.yaml │ │ ├── invalid_single_file.yaml │ │ ├── log_file.yaml │ │ ├── multiple_ignores.yaml │ │ ├── no_files.yaml │ │ ├── return_254.yaml │ │ ├── return_255.yaml │ │ ├── return_255_256.yaml │ │ ├── skip.yaml │ │ ├── stack_size.yaml │ │ ├── valid.yaml │ │ ├── warning.yaml │ │ └── warning_pass.yaml │ ├── invalid │ │ ├── index.html │ │ └── style.css │ ├── multiple_ignores │ │ └── index.html │ ├── return_value │ │ ├── 254.html │ │ ├── 255.html │ │ └── 256.html │ ├── test_config.py │ ├── test_simple.py │ ├── valid │ │ └── index.html │ └── warning │ │ └── index.html └── validator.py ├── setup.cfg ├── setup.py └── vnujar ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __init__.py └── vnu.jar /.github/workflows/pypi-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and upload to PyPI 2 | 3 | # Build on every branch push, tag push, and pull request change: 4 | # on: [push, pull_request] 5 | # Alternatively, to publish when a (published) GitHub Release is created, use the following: 6 | on: 7 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | branches: 12 | - main 13 | release: 14 | types: 15 | - published 16 | 17 | jobs: 18 | build_sdist: 19 | name: Build source distribution 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v2 23 | - uses: actions/setup-python@v2 24 | name: Install Python 25 | with: 26 | python-version: '3.7' 27 | - run: pip install -e . 28 | - run: python -c "import html5validator; print(html5validator.__version__)" 29 | - run: git status 30 | - run: git diff 31 | - run: python -c "import html5validator; assert 'dirty' not in html5validator.__version__" 32 | - name: Build sdist 33 | run: python setup.py sdist 34 | - uses: actions/upload-artifact@v2 35 | with: 36 | path: dist/*.tar.gz 37 | 38 | upload_pypi: 39 | needs: [build_sdist] 40 | runs-on: ubuntu-latest 41 | # upload to PyPI on every tag starting with 'v' 42 | # if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') 43 | # alternatively, to publish when a GitHub Release is created, use the following rule: 44 | if: github.event_name == 'release' && github.event.action == 'published' 45 | steps: 46 | - uses: actions/download-artifact@v2 47 | with: 48 | name: artifact 49 | path: dist 50 | 51 | - uses: pypa/gh-action-pypi-publish@master 52 | with: 53 | user: __token__ 54 | password: ${{ secrets.pypi_password }} 55 | # To test: repository_url: https://test.pypi.org/legacy/ 56 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | include: 12 | - os: ubuntu-latest 13 | python: 3.6 14 | - os: ubuntu-latest 15 | python: 3.7 16 | - os: ubuntu-latest 17 | python: 3.8 18 | - os: ubuntu-latest 19 | python: 3.9 20 | - os: ubuntu-latest 21 | python: '3.10' 22 | - os: macos-latest 23 | python: 3.7 24 | - os: macos-latest 25 | python: 3.8 26 | conda: True 27 | - os: macos-latest 28 | python: 3.9 29 | - os: macos-latest 30 | python: '3.10' 31 | - os: windows-latest 32 | python: '3.10' 33 | 34 | steps: 35 | - uses: actions/checkout@v2 36 | - name: Set up Python ${{ matrix.python }} 37 | if: ${{ !matrix.conda }} 38 | uses: actions/setup-python@v2 39 | with: 40 | python-version: ${{ matrix.python }} 41 | - name: Set up Conda 42 | if: matrix.conda 43 | uses: s-weigand/setup-conda@v1 44 | with: 45 | update-conda: true 46 | python-version: ${{ matrix.python }} 47 | conda-channels: anaconda, conda-forge 48 | - run: conda --version 49 | if: matrix.conda 50 | - run: which python 51 | if: matrix.conda 52 | - run: python --version 53 | - name: Install 54 | run: python -m pip install -e ".[tests]" 55 | - name: Print environment 56 | run: | 57 | python -m pip freeze 58 | python --version 59 | python -c "import html5validator; print(html5validator.__version__)" 60 | - name: Lint html5validator 61 | # run: pylint html5validator --disable=fixme 62 | run: flake8 63 | # - name: pycodestyle html5validator 64 | # run: python -m pycodestyle html5validator 65 | - name: Test 66 | env: 67 | PYTHONDEVMODE: 1 68 | run: pytest -vv 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Python template 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | *.py[cod] 9 | venv* 10 | .DS_Store 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | share/python-wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 87 | __pypackages__/ 88 | 89 | # Celery stuff 90 | celerybeat-schedule 91 | celerybeat.pid 92 | 93 | # SageMath parsed files 94 | *.sage.py 95 | 96 | # Environments 97 | .env 98 | env/ 99 | venv/ 100 | ENV/ 101 | env.bak/ 102 | venv.bak/ 103 | 104 | # Spyder project settings 105 | .spyderproject 106 | .spyproject 107 | 108 | # Rope project settings 109 | .ropeproject 110 | 111 | # mkdocs documentation 112 | /site 113 | 114 | # mypy 115 | .mypy_cache/ 116 | .dmypy.json 117 | dmypy.json 118 | 119 | # Pyre type checker 120 | .pyre/ 121 | 122 | # pytype static type analyzer 123 | .pytype/ 124 | 125 | # Cython debug symbols 126 | cython_debug/ 127 | 128 | # vscode 129 | .vscode/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Sven Kreiss 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE vnujar/vnu.jar vnujar/LICENSE 2 | recursive-include tests * 3 | recursive-exclude tests *.pyc .DS_Store 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | HTML5 Validator 2 | =============== 3 | 4 | ``html5validator`` is a command line tool that tests files for 5 | HTML5 validity. This was written with static site generators like 6 | `Jekyll `_ and 7 | `Pelican `_ in mind. Dynamic html content 8 | (for example from JS template engines) can be crawled 9 | (e.g. with `localcrawl `_) 10 | and then validated. 11 | 12 | .. image:: https://github.com/svenkreiss/html5validator/actions/workflows/tests.yml/badge.svg?branch=main 13 | :target: https://github.com/svenkreiss/html5validator/actions/workflows/tests.yml 14 | .. image:: https://badge.fury.io/py/html5validator.svg 15 | :target: https://pypi.python.org/pypi/html5validator/ 16 | 17 | 18 | Install 19 | ------- 20 | 21 | This module requires Python 3.6, 3.7, 3.8, 3.9 or 3.10 and Java 8 (``openjdk8`` or ``oraclejdk8``). 22 | Install with ``pip install html5validator`` and run with 23 | 24 | .. code-block:: bash 25 | 26 | html5validator --root _build/ 27 | 28 | to validate all html files in the ``_build`` directory. 29 | Run ``html5validator --help`` to see the list of command line options:: 30 | 31 | usage: html5validator [-h] [--root ROOT] [--match MATCH [MATCH ...]] 32 | [--blacklist [BLACKLIST ...]] [--show-warnings] 33 | [--no-langdetect] [--no-vnu-stdout] [--no-asciiquotes] 34 | [--format {gnu,xml,json,text}] 35 | [--ignore [IGNORE ...]] [--ignore-re [IGNORE_RE ...]] 36 | [--config CONFIG] [-l] [-ll] [-lll] [--log LOG] 37 | [--log-file LOG_FILE] [--version] 38 | [files ...] 39 | 40 | [v0.4.2] Command line tool for HTML5 validation. Return code is 0 for valid 41 | HTML5. Arguments that are unknown to html5validator 42 | are passed as arguments to `vnu.jar`. 43 | 44 | positional arguments: 45 | files specify files to check 46 | 47 | optional arguments: 48 | -h, --help show this help message and exit 49 | --root ROOT start directory to search for files to validate 50 | --match MATCH [MATCH ...] 51 | match file pattern in search (default: "*.html" or 52 | "*.html *.css" if --also-check-css is used) 53 | --blacklist [BLACKLIST ...] 54 | directory names to skip in search 55 | --show-warnings show warnings and count them as errors 56 | --no-langdetect disable language detection 57 | --no-vnu-stdout do not use --stdout with vnu.jar 58 | --no-asciiquotes do not use --asciiquotes with vnu.jar 59 | --format {gnu,xml,json,text} 60 | output format 61 | --ignore [IGNORE ...] 62 | ignore messages containing the given strings 63 | --ignore-re [IGNORE_RE ...] 64 | regular expression of messages to ignore 65 | --config CONFIG Path to a config file for options 66 | -l run on larger files: sets Java stack size to 2048k 67 | -ll run on larger files: sets Java stack size to 8192k 68 | -lll run on larger files: sets Java stack size to 32768k 69 | --log LOG log level: DEBUG, INFO or WARNING (default: WARNING) 70 | --log-file LOG_FILE Name for log file. If no name supplied then no log 71 | file will be created 72 | --version show program's version number and exit 73 | 74 | This module uses the `validator.nu backend `_ 75 | which is written in Java. Therefore, a Java Runtime Environment must be 76 | available on your system. Since version 0.2, Java 8 is required. 77 | 78 | 79 | Checking CSS/SVG 80 | ---------------- 81 | 82 | .. code-block:: bash 83 | 84 | html5validator --root _build/ --also-check-css 85 | 86 | # checking only CSS 87 | html5validator --root _build/ --skip-non-css 88 | 89 | Replace ``css`` with ``svg`` for similar behavior with SVG files. 90 | 91 | 92 | Integration with CircleCI 1.0 93 | ----------------------------- 94 | 95 | Create a ``circle.yml`` file: 96 | 97 | .. code-block:: yaml 98 | 99 | machine: 100 | java: 101 | version: openjdk8 102 | dependencies: 103 | pre: 104 | - sudo pip install html5validator 105 | test: 106 | override: 107 | - html5validator --root _build/ 108 | 109 | in your repository with static html files and get HTML5 validation on every 110 | ``git push``. 111 | 112 | 113 | Integration with CircleCI 2.0 114 | ----------------------------- 115 | 116 | Simplified example ``circle.yml`` file from 117 | `pelican-jsmath `_: 118 | 119 | .. code-block:: yaml 120 | 121 | version: 2 122 | jobs: 123 | test-3.6: 124 | docker: 125 | - image: python:3.6-stretch 126 | steps: 127 | - run: 128 | name: install Java 129 | command: apt-get update && apt-get install -y openjdk-8-jre 130 | - checkout 131 | - run: 132 | name: install 133 | command: pip install '.[test]' 134 | - run: 135 | name: generate html 136 | working_directory: test/example_site 137 | command: pelican content -s pelicanconf.py 138 | - run: 139 | name: validate html 140 | command: html5validator --root test/example_site/output 141 | workflows: 142 | version: 2 143 | build_and_test: 144 | jobs: 145 | - test-3.6 146 | 147 | 148 | Integration with TravisCI 149 | ------------------------- 150 | 151 | Create a ``.travis.yml`` file. This is an example for a Python project: 152 | 153 | .. code-block:: yaml 154 | 155 | language: python 156 | python: 157 | - "2.7" 158 | addons: 159 | apt: 160 | packages: 161 | - openjdk-8-jre # install Java8 as required by vnu.jar 162 | 163 | branches: 164 | only: 165 | - gh-pages 166 | 167 | install: 168 | - pip install html5validator 169 | 170 | script: html5validator --root _build/ 171 | 172 | This is an example for Java project: 173 | 174 | .. code-block:: yaml 175 | 176 | language: java 177 | jdk: 178 | - oraclejdk8 # vnu.jar requires Java 8 179 | 180 | branches: 181 | only: 182 | - gh-pages 183 | 184 | install: 185 | - pip install --user html5validator 186 | 187 | script: html5validator --root _build/ 188 | 189 | 190 | Fix the ``html5validator`` version by using 191 | ``pip install --user html5validator==``. 192 | 193 | You can also use this for user pages (repositories of the form ``.github.io``) 194 | where the html files are in the master branch. You only have to remove: 195 | 196 | .. code-block:: yaml 197 | 198 | branches: 199 | only: 200 | - gh-pages 201 | 202 | from ``.travis.yml``. I am using this on 203 | `my own user page `_. 204 | 205 | 206 | Integration with CodeShip 207 | ------------------------- 208 | 209 | Add this lines to the ``Setup Commands``: 210 | 211 | .. code-block:: yaml 212 | 213 | jdk_switcher use oraclejdk8 214 | pip install html5validator 215 | 216 | 217 | This is an example for Ruby project: 218 | 219 | .. code-block:: yaml 220 | 221 | rvm use 2.2.0 --install 222 | bundle install 223 | bundle update 224 | export RAILS_ENV=test 225 | jdk_switcher use oraclejdk8 226 | pip install html5validator 227 | 228 | Integration with GitLab CI 229 | -------------------------------- 230 | 231 | There is a docker image available to be used with GitLab CI or stand alone. 232 | `Docker image `_, 233 | `Docker image repo `_. 234 | 235 | Example for html test `(Full) `_: 236 | 237 | .. code-block:: yaml 238 | 239 | html_test: 240 | stage: html_test 241 | image: cyb3rjak3/html5validator:latest 242 | script: 243 | - html5validator --root public/ --also-check-css --format text 244 | 245 | Integration with GitHub Actions 246 | --------------------------------- 247 | 248 | There is a Github Action that can be used to check repositories. `Marketplace Link `_. 249 | 250 | Example action: 251 | 252 | .. code-block:: yaml 253 | 254 | - name: HTML5 Validator 255 | uses: Cyb3r-Jak3/html5validator-action@master 256 | with: 257 | root: html/ 258 | 259 | Technical Notes 260 | --------------- 261 | 262 | * If you are using grunt already, maybe consider using the 263 | `grunt-html `_ plugin for grunt instead. 264 | * Use ``--ignore-re 'Attribute "ng-[a-z-]+" not allowed'`` with angular.js apps. 265 | * Example with multiple ignores: ``html5validator --root tests/multiple_ignores/ --ignore-re 'Attribute "ng-[a-z-]+" not allowed' 'Start tag seen without seeing a doctype first'`` 266 | 267 | 268 | Changelog 269 | --------- 270 | 271 | Install a particular version, for example ``0.1.14``, with ``pip install html5validator==0.1.14``. 272 | 273 | * `main `_ 274 | * `0.4.2 `_ (2022-05-29) 275 | * test with Python 3.10 276 | * vnu.jar updated to 20.6.30 277 | * compatibility restored with certain versions of Python (`os.errno` issue) 278 | * `0.4.0 `_ (2021-05-03) 279 | * update vnu jar to 21.4.9 280 | * use `--stdout` and `--asciiquotes` by default for vnu.jar 281 | * make `--format=json` parsable 282 | * better log file and config file tests 283 | * move tests to GitHub Actions and setup auto-deploy to PyPI from GitHub releases 284 | * `0.3.3 `_ (2019-12-07) 285 | * `PR#59 `_ 286 | * `0.3.2 `_ (2019-11-22) 287 | * update vnu jar to 18.11.5 288 | * better output check `PR#57 `_ by `@Cyb3r-Jak3 `_ 289 | * `0.3.1 `_ (2018-06-01) 290 | * update vnu jar to 18.3.0 291 | * pass remaining command line options to ``vnu.jar`` 292 | * allow to match multiple file patterns, e.g. ``--match *.html *.css`` 293 | * `0.3.0 `_ (2018-01-21) 294 | * update vnu jar to 17.11.1 295 | * support explicit list of files: ``html5validator file1.html file2.html`` 296 | * new command line options: ``--no-langdetect``, ``--format`` 297 | * new tests for ``--show-warnings`` flag 298 | * refactored internal API 299 | * bugfix: check existence of Java 300 | * bugfix: split Java and vnu.jar command line options 301 | * `0.2.8 `_ (2017-09-08) 302 | * update vnu jar to 17.9.0 303 | * suppress a warning from the JDK about picked up environment variables 304 | * `0.2.7 `_ (2017-04-09) 305 | * update vnu jar to 17.3.0 306 | * lint Python code 307 | * `0.2.5 `_ (2016-07-30) 308 | * clamp CLI return value at 255: `PR26 `_ 309 | * `0.2.4 `_ (2016-07-14) 310 | * a fix for Cygwin thanks to this `PR20 `_ 311 | * `0.2.3 `_ (2016-07-05) 312 | * ``vnu.jar`` updated to 16.6.29 thanks to this `PR `_ 313 | * `0.2.2 `_ (2016-04-30) 314 | * ``vnu.jar`` updated to 16.3.3 315 | * `0.2.1 `_ (2016-01-25) 316 | * ``--ignore``, ``--ignore-re``: ignore messages containing an exact pattern or 317 | matching a regular expression (migration from version 0.1.14: replace ``--ignore`` with ``--ignore-re``) 318 | * curly quotes and straight quotes can now be used interchangeably 319 | * change Java stack size handling (introduced the new command line options ``-l``, ``-ll`` and ``-lll``) 320 | * update vnu.jar to 16.1.1 (which now requires Java 8) 321 | * `0.1.14 `_ (2015-10-09) 322 | * change text encoding handling 323 | * adding command line arguments ``--log`` and ``--version`` 324 | * `0.1.12 `_ (2015-05-07) 325 | * document how to specify multiple regular expressions to be ignored 326 | * add ``--ignore`` as command line argument. Takes a regular expression 327 | for warnings and errors that should be ignored. 328 | * `0.1.9 `_ (2015-03-02) 329 | -------------------------------------------------------------------------------- /html5validator/__init__.py: -------------------------------------------------------------------------------- 1 | """Validate HTML5 files.""" 2 | # flake8: noqa 3 | 4 | __version__ = "0.4.2" 5 | 6 | from .validator import Validator, JavaNotFoundException 7 | -------------------------------------------------------------------------------- /html5validator/cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Command line tool for HTML5 validation. Return code is 0 for valid HTML5. 3 | 4 | Arguments that are unknown to html5validator are passed as arguments 5 | to `vnu.jar`. 6 | """ 7 | 8 | from .validator import Validator, all_files 9 | import argparse 10 | import logging 11 | import sys 12 | import yaml 13 | 14 | from . import __version__ as VERSION 15 | 16 | LOGGER = logging.getLogger(__name__) 17 | 18 | 19 | def parse_yaml(filename, starter): 20 | """Parses yaml config file""" 21 | converted_namespace = vars(starter) 22 | with open(filename, encoding='utf8') as yaml_file: 23 | yaml_contents = yaml.safe_load(yaml_file) 24 | LOGGER.debug(yaml_contents) 25 | for item in yaml_contents.keys(): 26 | if item == "vnu": 27 | pass 28 | converted_namespace[item] = yaml_contents[item] 29 | extras = [item for item in yaml_contents.get("vnu", [])] 30 | return argparse.Namespace(**converted_namespace), extras 31 | 32 | 33 | def main(): 34 | """Main function of html5validator""" 35 | vnu_help, _ = Validator().run_vnu(['--help']) 36 | 37 | parser = argparse.ArgumentParser( 38 | description='[v' + VERSION + '] ' + __doc__, 39 | prog='html5validator', 40 | formatter_class=argparse.RawDescriptionHelpFormatter, 41 | epilog=''' 42 | 43 | This html5validator uses vnu.jar to check the files. 44 | It has many options that are documented in the 45 | vnu.jar help below. 46 | 47 | ================= VNU help ======================== 48 | ''' + vnu_help, 49 | ) 50 | parser.add_argument('files', nargs='*', default=None, 51 | help='specify files to check') 52 | 53 | parser.add_argument('--root', default='.', 54 | help='start directory to search for files to validate') 55 | parser.add_argument('--match', nargs='+', 56 | help=('match file pattern in search ' 57 | '(default: "*.html" or ' 58 | '"*.html *.css" if --also-check-css is used)')) 59 | parser.add_argument('--blacklist', type=str, nargs='*', 60 | help='directory names to skip in search', default=[]) 61 | 62 | parser.add_argument('--show-warnings', dest='errors_only', 63 | action='store_false', default=True, 64 | help='show warnings and count them as errors') 65 | parser.add_argument('--no-langdetect', dest='detect_language', 66 | action='store_false', default=True, 67 | help='disable language detection') 68 | parser.add_argument('--no-vnu-stdout', dest='vnu_stdout', 69 | action='store_false', default=True, 70 | help='do not use --stdout with vnu.jar') 71 | parser.add_argument('--no-asciiquotes', dest='vnu_asciiquotes', 72 | action='store_false', default=True, 73 | help='do not use --asciiquotes with vnu.jar') 74 | parser.add_argument('--format', choices=['gnu', 'xml', 'json', 'text'], 75 | help='output format', default=None) 76 | 77 | parser.add_argument('--ignore', nargs='*', default=None, 78 | type=lambda s: (s.decode('utf-8') 79 | if isinstance(s, bytes) else s), 80 | help='ignore messages containing the given strings') 81 | parser.add_argument('--ignore-re', nargs='*', default=None, 82 | type=lambda s: (s.decode('utf-8') 83 | if isinstance(s, bytes) else s), 84 | dest='ignore_re', 85 | help='regular expression of messages to ignore') 86 | parser.add_argument("--config", help="Path to a config file for options") 87 | parser.add_argument('-l', action='store_const', const=2048, 88 | dest='stack_size', 89 | help=('run on larger files: sets Java ' 90 | 'stack size to 2048k')) 91 | parser.add_argument('-ll', action='store_const', const=8192, 92 | dest='stack_size', 93 | help=('run on larger files: sets Java ' 94 | 'stack size to 8192k')) 95 | parser.add_argument('-lll', action='store_const', const=32768, 96 | dest='stack_size', 97 | help=('run on larger files: sets Java ' 98 | 'stack size to 32768k')) 99 | 100 | parser.add_argument('--log', default='WARNING', 101 | help=('log level: DEBUG, INFO or WARNING ' 102 | '(default: WARNING)')) 103 | parser.add_argument('--log-file', dest="log_file", 104 | help=("Name for log file. If no name supplied then no " 105 | "log file will be created")) 106 | 107 | parser.add_argument('--version', action='version', 108 | version='%(prog)s ' + VERSION) 109 | args, extra_args = parser.parse_known_args() 110 | 111 | if args.config is not None: 112 | args, extra_args = parse_yaml(args.config, args) 113 | if args.vnu_stdout: 114 | extra_args.append('--stdout') 115 | if args.vnu_asciiquotes: 116 | extra_args.append('--asciiquotes') 117 | if args.match is None: 118 | args.match = ['*.html'] 119 | 120 | # append to match 121 | if '--also-check-css' in extra_args or '--css' in extra_args: 122 | args.match.append('*.css') 123 | if '--also-check-svg' in extra_args or '--svg' in extra_args: 124 | args.match.append('*.svg') 125 | 126 | # overwrite match 127 | if '--skip-non-css' in extra_args: 128 | args.match = ['*.css'] 129 | if '--skip-non-svg' in extra_args: 130 | args.match = ['*.svg'] 131 | 132 | if args.log_file is None: 133 | logging.basicConfig(level=getattr(logging, args.log)) 134 | 135 | else: 136 | logging.basicConfig(level=getattr(logging, args.log), 137 | handlers=[ 138 | logging.FileHandler(f"{args.log_file}.log", 139 | mode="w")]) 140 | 141 | validator = Validator(ignore=args.ignore, 142 | ignore_re=args.ignore_re, 143 | errors_only=args.errors_only, 144 | detect_language=args.detect_language, 145 | format=args.format, 146 | stack_size=args.stack_size, 147 | vnu_args=extra_args) 148 | if args.files: 149 | files = args.files 150 | else: 151 | files = all_files( 152 | directory=args.root, 153 | match=args.match, 154 | blacklist=args.blacklist) 155 | if len(files) == 0: 156 | LOGGER.error("There are no files to check") 157 | sys.exit(1) 158 | LOGGER.info('Files to validate: \n {0}'.format('\n '.join(files))) 159 | LOGGER.info(f'Number of files: {len(files)}') 160 | 161 | error_count = validator.validate(files) 162 | sys.exit(min(error_count, 255)) 163 | 164 | 165 | if __name__ == "__main__": 166 | main() 167 | -------------------------------------------------------------------------------- /html5validator/tests/README.rst: -------------------------------------------------------------------------------- 1 | Tests 2 | ===== 3 | 4 | Run the tests from the main directory with:: 5 | 6 | python setup.py test 7 | 8 | or:: 9 | 10 | nosetests tests 11 | 12 | For the integration with TravisCI, this repository contains two test branches: one that is valid HTML5 (``gh-pages``) and one that is not (``gh-pages-failing``). These buttons show the HTML5 validity of the two branches: 13 | 14 | .. image:: https://travis-ci.org/svenkreiss/html5validator.svg?branch=gh-pages 15 | :target: https://travis-ci.org/svenkreiss/html5validator) 16 | .. image:: https://travis-ci.org/svenkreiss/html5validator.svg?branch=gh-pages-failing 17 | :target: https://travis-ci.org/svenkreiss/html5validator) 18 | -------------------------------------------------------------------------------- /html5validator/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenkreiss/html5validator/2516f7ac9894917fe074d3cadf27eb2025b5c0e2/html5validator/tests/__init__.py -------------------------------------------------------------------------------- /html5validator/tests/angularjs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test 6 | 7 | 8 |

This is a boring test.

9 | 10 | 11 | -------------------------------------------------------------------------------- /html5validator/tests/config_files/angularjs.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/angularjs/ 2 | ignore_re: 3 | - 'Attribute “ng-[a-z-]+” not allowed' -------------------------------------------------------------------------------- /html5validator/tests/config_files/angularjs_normal.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/angularjs/ 2 | ignore_re: 3 | - 'Attribute "ng-[a-z-]+" not allowed' -------------------------------------------------------------------------------- /html5validator/tests/config_files/extra.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/valid/ 2 | vnu: 3 | - "--hello world" -------------------------------------------------------------------------------- /html5validator/tests/config_files/format_flags/gnu_invalid.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/invalid/ 2 | format: gnu -------------------------------------------------------------------------------- /html5validator/tests/config_files/format_flags/gnu_valid.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/valid/ 2 | format: gnu -------------------------------------------------------------------------------- /html5validator/tests/config_files/format_flags/json_invalid.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/invalid/ 2 | format: json -------------------------------------------------------------------------------- /html5validator/tests/config_files/format_flags/json_valid.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/valid/ 2 | format: json -------------------------------------------------------------------------------- /html5validator/tests/config_files/format_flags/text_invalid.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/invalid/ 2 | format: text -------------------------------------------------------------------------------- /html5validator/tests/config_files/format_flags/text_valid.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/valid/ 2 | format: text -------------------------------------------------------------------------------- /html5validator/tests/config_files/format_flags/xml_invalid.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/invalid/ 2 | format: xml -------------------------------------------------------------------------------- /html5validator/tests/config_files/format_flags/xml_valid.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/valid/ 2 | format: xml -------------------------------------------------------------------------------- /html5validator/tests/config_files/ignore_and_ignorere.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/multiple_ignores/ 2 | ignore_re: 3 | - 'Attribute “ng-[a-z-]+” not allowed' 4 | ignore: 5 | - 'Start tag seen without seeing a doctype first' -------------------------------------------------------------------------------- /html5validator/tests/config_files/invalid.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/invalid/ -------------------------------------------------------------------------------- /html5validator/tests/config_files/invalid_css.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/invalid/ 2 | match: 3 | - "*.html" 4 | - "*.css" -------------------------------------------------------------------------------- /html5validator/tests/config_files/invalid_css_only.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/invalid/ 2 | match: 3 | - "*.css" -------------------------------------------------------------------------------- /html5validator/tests/config_files/invalid_single_file.yaml: -------------------------------------------------------------------------------- 1 | files: 2 | - ./html5validator/tests/invalid/index.html 3 | -------------------------------------------------------------------------------- /html5validator/tests/config_files/log_file.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/valid/ 2 | log_file: test_config 3 | log: DEBUG -------------------------------------------------------------------------------- /html5validator/tests/config_files/multiple_ignores.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/multiple_ignores/ 2 | ignore_re: 3 | - 'Attribute “ng-[a-z-]+” not allowed' 4 | - 'Start tag seen without seeing a doctype first' -------------------------------------------------------------------------------- /html5validator/tests/config_files/no_files.yaml: -------------------------------------------------------------------------------- 1 | root: MISSING -------------------------------------------------------------------------------- /html5validator/tests/config_files/return_254.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/return_value/ 2 | match: 3 | - "254.html" -------------------------------------------------------------------------------- /html5validator/tests/config_files/return_255.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/return_value/ 2 | match: 3 | - "255.html" -------------------------------------------------------------------------------- /html5validator/tests/config_files/return_255_256.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/return_value/ 2 | match: 3 | - "256.html" -------------------------------------------------------------------------------- /html5validator/tests/config_files/skip.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/invalid/ 2 | blacklist: 3 | - "index.html" 4 | match: 5 | - "*.html" 6 | - "*.css" -------------------------------------------------------------------------------- /html5validator/tests/config_files/stack_size.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/valid/ 2 | stack_size: 32768 -------------------------------------------------------------------------------- /html5validator/tests/config_files/valid.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/valid/ 2 | -------------------------------------------------------------------------------- /html5validator/tests/config_files/warning.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/warning 2 | errors_only: False -------------------------------------------------------------------------------- /html5validator/tests/config_files/warning_pass.yaml: -------------------------------------------------------------------------------- 1 | root: ./html5validator/tests/warning 2 | -------------------------------------------------------------------------------- /html5validator/tests/invalid/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test 5 | 6 | 7 |

This is a boring test.

8 | 9 | -------------------------------------------------------------------------------- /html5validator/tests/invalid/style.css: -------------------------------------------------------------------------------- 1 | p { maxwidth: 60em; } 2 | -------------------------------------------------------------------------------- /html5validator/tests/multiple_ignores/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test 5 | 6 | 7 |

This is a boring test.

8 | 9 | 10 | -------------------------------------------------------------------------------- /html5validator/tests/return_value/254.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test 6 | 7 | 8 |

This is a boring test. 9 |

This is a boring test. 10 |

This is a boring test. 11 |

This is a boring test. 12 |

This is a boring test. 13 |

This is a boring test. 14 |

This is a boring test. 15 |

This is a boring test. 16 |

This is a boring test. 17 |

This is a boring test. 18 |

This is a boring test. 19 |

This is a boring test. 20 |

This is a boring test. 21 |

This is a boring test. 22 |

This is a boring test. 23 |

This is a boring test. 24 |

This is a boring test. 25 |

This is a boring test. 26 |

This is a boring test. 27 |

This is a boring test. 28 |

This is a boring test. 29 |

This is a boring test. 30 |

This is a boring test. 31 |

This is a boring test. 32 |

This is a boring test. 33 |

This is a boring test. 34 |

This is a boring test. 35 |

This is a boring test. 36 |

This is a boring test. 37 |

This is a boring test. 38 |

This is a boring test. 39 |

This is a boring test. 40 |

This is a boring test. 41 |

This is a boring test. 42 |

This is a boring test. 43 |

This is a boring test. 44 |

This is a boring test. 45 |

This is a boring test. 46 |

This is a boring test. 47 |

This is a boring test. 48 |

This is a boring test. 49 |

This is a boring test. 50 |

This is a boring test. 51 |

This is a boring test. 52 |

This is a boring test. 53 |

This is a boring test. 54 |

This is a boring test. 55 |

This is a boring test. 56 |

This is a boring test. 57 |

This is a boring test. 58 |

This is a boring test. 59 |

This is a boring test. 60 |

This is a boring test. 61 |

This is a boring test. 62 |

This is a boring test. 63 |

This is a boring test. 64 |

This is a boring test. 65 |

This is a boring test. 66 |

This is a boring test. 67 |

This is a boring test. 68 |

This is a boring test. 69 |

This is a boring test. 70 |

This is a boring test. 71 |

This is a boring test. 72 |

This is a boring test. 73 |

This is a boring test. 74 |

This is a boring test. 75 |

This is a boring test. 76 |

This is a boring test. 77 |

This is a boring test. 78 |

This is a boring test. 79 |

This is a boring test. 80 |

This is a boring test. 81 |

This is a boring test. 82 |

This is a boring test. 83 |

This is a boring test. 84 |

This is a boring test. 85 |

This is a boring test. 86 |

This is a boring test. 87 |

This is a boring test. 88 |

This is a boring test. 89 |

This is a boring test. 90 |

This is a boring test. 91 |

This is a boring test. 92 |

This is a boring test. 93 |

This is a boring test. 94 |

This is a boring test. 95 |

This is a boring test. 96 |

This is a boring test. 97 |

This is a boring test. 98 |

This is a boring test. 99 |

This is a boring test. 100 |

This is a boring test. 101 |

This is a boring test. 102 |

This is a boring test. 103 |

This is a boring test. 104 |

This is a boring test. 105 |

This is a boring test. 106 |

This is a boring test. 107 |

This is a boring test. 108 |

This is a boring test. 109 |

This is a boring test. 110 |

This is a boring test. 111 |

This is a boring test. 112 |

This is a boring test. 113 |

This is a boring test. 114 |

This is a boring test. 115 |

This is a boring test. 116 |

This is a boring test. 117 |

This is a boring test. 118 |

This is a boring test. 119 |

This is a boring test. 120 |

This is a boring test. 121 |

This is a boring test. 122 |

This is a boring test. 123 |

This is a boring test. 124 |

This is a boring test. 125 |

This is a boring test. 126 |

This is a boring test. 127 |

This is a boring test. 128 |

This is a boring test. 129 |

This is a boring test. 130 |

This is a boring test. 131 |

This is a boring test. 132 |

This is a boring test. 133 |

This is a boring test. 134 |

This is a boring test. 135 |

This is a boring test. 136 |

This is a boring test. 137 |

This is a boring test. 138 |

This is a boring test. 139 |

This is a boring test. 140 |

This is a boring test. 141 |

This is a boring test. 142 |

This is a boring test. 143 |

This is a boring test. 144 |

This is a boring test. 145 |

This is a boring test. 146 |

This is a boring test. 147 |

This is a boring test. 148 |

This is a boring test. 149 |

This is a boring test. 150 |

This is a boring test. 151 |

This is a boring test. 152 |

This is a boring test. 153 |

This is a boring test. 154 |

This is a boring test. 155 |

This is a boring test. 156 |

This is a boring test. 157 |

This is a boring test. 158 |

This is a boring test. 159 |

This is a boring test. 160 |

This is a boring test. 161 |

This is a boring test. 162 |

This is a boring test. 163 |

This is a boring test. 164 |

This is a boring test. 165 |

This is a boring test. 166 |

This is a boring test. 167 |

This is a boring test. 168 |

This is a boring test. 169 |

This is a boring test. 170 |

This is a boring test. 171 |

This is a boring test. 172 |

This is a boring test. 173 |

This is a boring test. 174 |

This is a boring test. 175 |

This is a boring test. 176 |

This is a boring test. 177 |

This is a boring test. 178 |

This is a boring test. 179 |

This is a boring test. 180 |

This is a boring test. 181 |

This is a boring test. 182 |

This is a boring test. 183 |

This is a boring test. 184 |

This is a boring test. 185 |

This is a boring test. 186 |

This is a boring test. 187 |

This is a boring test. 188 |

This is a boring test. 189 |

This is a boring test. 190 |

This is a boring test. 191 |

This is a boring test. 192 |

This is a boring test. 193 |

This is a boring test. 194 |

This is a boring test. 195 |

This is a boring test. 196 |

This is a boring test. 197 |

This is a boring test. 198 |

This is a boring test. 199 |

This is a boring test. 200 |

This is a boring test. 201 |

This is a boring test. 202 |

This is a boring test. 203 |

This is a boring test. 204 |

This is a boring test. 205 |

This is a boring test. 206 |

This is a boring test. 207 |

This is a boring test. 208 |

This is a boring test. 209 |

This is a boring test. 210 |

This is a boring test. 211 |

This is a boring test. 212 |

This is a boring test. 213 |

This is a boring test. 214 |

This is a boring test. 215 |

This is a boring test. 216 |

This is a boring test. 217 |

This is a boring test. 218 |

This is a boring test. 219 |

This is a boring test. 220 |

This is a boring test. 221 |

This is a boring test. 222 |

This is a boring test. 223 |

This is a boring test. 224 |

This is a boring test. 225 |

This is a boring test. 226 |

This is a boring test. 227 |

This is a boring test. 228 |

This is a boring test. 229 |

This is a boring test. 230 |

This is a boring test. 231 |

This is a boring test. 232 |

This is a boring test. 233 |

This is a boring test. 234 |

This is a boring test. 235 |

This is a boring test. 236 |

This is a boring test. 237 |

This is a boring test. 238 |

This is a boring test. 239 |

This is a boring test. 240 |

This is a boring test. 241 |

This is a boring test. 242 |

This is a boring test. 243 |

This is a boring test. 244 |

This is a boring test. 245 |

This is a boring test. 246 |

This is a boring test. 247 |

This is a boring test. 248 |

This is a boring test. 249 |

This is a boring test. 250 |

This is a boring test. 251 |

This is a boring test. 252 |

This is a boring test. 253 |

This is a boring test. 254 |

This is a boring test. 255 |

This is a boring test. 256 |

This is a boring test. 257 |

This is a boring test. 258 |

This is a boring test. 259 |

This is a boring test. 260 |

This is a boring test. 261 |

This is a boring test. 262 | 263 | 264 | -------------------------------------------------------------------------------- /html5validator/tests/return_value/255.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test 6 | 7 | 8 |

This is a boring test. 9 |

This is a boring test. 10 |

This is a boring test. 11 |

This is a boring test. 12 |

This is a boring test. 13 |

This is a boring test. 14 |

This is a boring test. 15 |

This is a boring test. 16 |

This is a boring test. 17 |

This is a boring test. 18 |

This is a boring test. 19 |

This is a boring test. 20 |

This is a boring test. 21 |

This is a boring test. 22 |

This is a boring test. 23 |

This is a boring test. 24 |

This is a boring test. 25 |

This is a boring test. 26 |

This is a boring test. 27 |

This is a boring test. 28 |

This is a boring test. 29 |

This is a boring test. 30 |

This is a boring test. 31 |

This is a boring test. 32 |

This is a boring test. 33 |

This is a boring test. 34 |

This is a boring test. 35 |

This is a boring test. 36 |

This is a boring test. 37 |

This is a boring test. 38 |

This is a boring test. 39 |

This is a boring test. 40 |

This is a boring test. 41 |

This is a boring test. 42 |

This is a boring test. 43 |

This is a boring test. 44 |

This is a boring test. 45 |

This is a boring test. 46 |

This is a boring test. 47 |

This is a boring test. 48 |

This is a boring test. 49 |

This is a boring test. 50 |

This is a boring test. 51 |

This is a boring test. 52 |

This is a boring test. 53 |

This is a boring test. 54 |

This is a boring test. 55 |

This is a boring test. 56 |

This is a boring test. 57 |

This is a boring test. 58 |

This is a boring test. 59 |

This is a boring test. 60 |

This is a boring test. 61 |

This is a boring test. 62 |

This is a boring test. 63 |

This is a boring test. 64 |

This is a boring test. 65 |

This is a boring test. 66 |

This is a boring test. 67 |

This is a boring test. 68 |

This is a boring test. 69 |

This is a boring test. 70 |

This is a boring test. 71 |

This is a boring test. 72 |

This is a boring test. 73 |

This is a boring test. 74 |

This is a boring test. 75 |

This is a boring test. 76 |

This is a boring test. 77 |

This is a boring test. 78 |

This is a boring test. 79 |

This is a boring test. 80 |

This is a boring test. 81 |

This is a boring test. 82 |

This is a boring test. 83 |

This is a boring test. 84 |

This is a boring test. 85 |

This is a boring test. 86 |

This is a boring test. 87 |

This is a boring test. 88 |

This is a boring test. 89 |

This is a boring test. 90 |

This is a boring test. 91 |

This is a boring test. 92 |

This is a boring test. 93 |

This is a boring test. 94 |

This is a boring test. 95 |

This is a boring test. 96 |

This is a boring test. 97 |

This is a boring test. 98 |

This is a boring test. 99 |

This is a boring test. 100 |

This is a boring test. 101 |

This is a boring test. 102 |

This is a boring test. 103 |

This is a boring test. 104 |

This is a boring test. 105 |

This is a boring test. 106 |

This is a boring test. 107 |

This is a boring test. 108 |

This is a boring test. 109 |

This is a boring test. 110 |

This is a boring test. 111 |

This is a boring test. 112 |

This is a boring test. 113 |

This is a boring test. 114 |

This is a boring test. 115 |

This is a boring test. 116 |

This is a boring test. 117 |

This is a boring test. 118 |

This is a boring test. 119 |

This is a boring test. 120 |

This is a boring test. 121 |

This is a boring test. 122 |

This is a boring test. 123 |

This is a boring test. 124 |

This is a boring test. 125 |

This is a boring test. 126 |

This is a boring test. 127 |

This is a boring test. 128 |

This is a boring test. 129 |

This is a boring test. 130 |

This is a boring test. 131 |

This is a boring test. 132 |

This is a boring test. 133 |

This is a boring test. 134 |

This is a boring test. 135 |

This is a boring test. 136 |

This is a boring test. 137 |

This is a boring test. 138 |

This is a boring test. 139 |

This is a boring test. 140 |

This is a boring test. 141 |

This is a boring test. 142 |

This is a boring test. 143 |

This is a boring test. 144 |

This is a boring test. 145 |

This is a boring test. 146 |

This is a boring test. 147 |

This is a boring test. 148 |

This is a boring test. 149 |

This is a boring test. 150 |

This is a boring test. 151 |

This is a boring test. 152 |

This is a boring test. 153 |

This is a boring test. 154 |

This is a boring test. 155 |

This is a boring test. 156 |

This is a boring test. 157 |

This is a boring test. 158 |

This is a boring test. 159 |

This is a boring test. 160 |

This is a boring test. 161 |

This is a boring test. 162 |

This is a boring test. 163 |

This is a boring test. 164 |

This is a boring test. 165 |

This is a boring test. 166 |

This is a boring test. 167 |

This is a boring test. 168 |

This is a boring test. 169 |

This is a boring test. 170 |

This is a boring test. 171 |

This is a boring test. 172 |

This is a boring test. 173 |

This is a boring test. 174 |

This is a boring test. 175 |

This is a boring test. 176 |

This is a boring test. 177 |

This is a boring test. 178 |

This is a boring test. 179 |

This is a boring test. 180 |

This is a boring test. 181 |

This is a boring test. 182 |

This is a boring test. 183 |

This is a boring test. 184 |

This is a boring test. 185 |

This is a boring test. 186 |

This is a boring test. 187 |

This is a boring test. 188 |

This is a boring test. 189 |

This is a boring test. 190 |

This is a boring test. 191 |

This is a boring test. 192 |

This is a boring test. 193 |

This is a boring test. 194 |

This is a boring test. 195 |

This is a boring test. 196 |

This is a boring test. 197 |

This is a boring test. 198 |

This is a boring test. 199 |

This is a boring test. 200 |

This is a boring test. 201 |

This is a boring test. 202 |

This is a boring test. 203 |

This is a boring test. 204 |

This is a boring test. 205 |

This is a boring test. 206 |

This is a boring test. 207 |

This is a boring test. 208 |

This is a boring test. 209 |

This is a boring test. 210 |

This is a boring test. 211 |

This is a boring test. 212 |

This is a boring test. 213 |

This is a boring test. 214 |

This is a boring test. 215 |

This is a boring test. 216 |

This is a boring test. 217 |

This is a boring test. 218 |

This is a boring test. 219 |

This is a boring test. 220 |

This is a boring test. 221 |

This is a boring test. 222 |

This is a boring test. 223 |

This is a boring test. 224 |

This is a boring test. 225 |

This is a boring test. 226 |

This is a boring test. 227 |

This is a boring test. 228 |

This is a boring test. 229 |

This is a boring test. 230 |

This is a boring test. 231 |

This is a boring test. 232 |

This is a boring test. 233 |

This is a boring test. 234 |

This is a boring test. 235 |

This is a boring test. 236 |

This is a boring test. 237 |

This is a boring test. 238 |

This is a boring test. 239 |

This is a boring test. 240 |

This is a boring test. 241 |

This is a boring test. 242 |

This is a boring test. 243 |

This is a boring test. 244 |

This is a boring test. 245 |

This is a boring test. 246 |

This is a boring test. 247 |

This is a boring test. 248 |

This is a boring test. 249 |

This is a boring test. 250 |

This is a boring test. 251 |

This is a boring test. 252 |

This is a boring test. 253 |

This is a boring test. 254 |

This is a boring test. 255 |

This is a boring test. 256 |

This is a boring test. 257 |

This is a boring test. 258 |

This is a boring test. 259 |

This is a boring test. 260 |

This is a boring test. 261 |

This is a boring test. 262 |

This is a boring test. 263 | 264 | 265 | -------------------------------------------------------------------------------- /html5validator/tests/return_value/256.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test 6 | 7 | 8 |

This is a boring test. 9 |

This is a boring test. 10 |

This is a boring test. 11 |

This is a boring test. 12 |

This is a boring test. 13 |

This is a boring test. 14 |

This is a boring test. 15 |

This is a boring test. 16 |

This is a boring test. 17 |

This is a boring test. 18 |

This is a boring test. 19 |

This is a boring test. 20 |

This is a boring test. 21 |

This is a boring test. 22 |

This is a boring test. 23 |

This is a boring test. 24 |

This is a boring test. 25 |

This is a boring test. 26 |

This is a boring test. 27 |

This is a boring test. 28 |

This is a boring test. 29 |

This is a boring test. 30 |

This is a boring test. 31 |

This is a boring test. 32 |

This is a boring test. 33 |

This is a boring test. 34 |

This is a boring test. 35 |

This is a boring test. 36 |

This is a boring test. 37 |

This is a boring test. 38 |

This is a boring test. 39 |

This is a boring test. 40 |

This is a boring test. 41 |

This is a boring test. 42 |

This is a boring test. 43 |

This is a boring test. 44 |

This is a boring test. 45 |

This is a boring test. 46 |

This is a boring test. 47 |

This is a boring test. 48 |

This is a boring test. 49 |

This is a boring test. 50 |

This is a boring test. 51 |

This is a boring test. 52 |

This is a boring test. 53 |

This is a boring test. 54 |

This is a boring test. 55 |

This is a boring test. 56 |

This is a boring test. 57 |

This is a boring test. 58 |

This is a boring test. 59 |

This is a boring test. 60 |

This is a boring test. 61 |

This is a boring test. 62 |

This is a boring test. 63 |

This is a boring test. 64 |

This is a boring test. 65 |

This is a boring test. 66 |

This is a boring test. 67 |

This is a boring test. 68 |

This is a boring test. 69 |

This is a boring test. 70 |

This is a boring test. 71 |

This is a boring test. 72 |

This is a boring test. 73 |

This is a boring test. 74 |

This is a boring test. 75 |

This is a boring test. 76 |

This is a boring test. 77 |

This is a boring test. 78 |

This is a boring test. 79 |

This is a boring test. 80 |

This is a boring test. 81 |

This is a boring test. 82 |

This is a boring test. 83 |

This is a boring test. 84 |

This is a boring test. 85 |

This is a boring test. 86 |

This is a boring test. 87 |

This is a boring test. 88 |

This is a boring test. 89 |

This is a boring test. 90 |

This is a boring test. 91 |

This is a boring test. 92 |

This is a boring test. 93 |

This is a boring test. 94 |

This is a boring test. 95 |

This is a boring test. 96 |

This is a boring test. 97 |

This is a boring test. 98 |

This is a boring test. 99 |

This is a boring test. 100 |

This is a boring test. 101 |

This is a boring test. 102 |

This is a boring test. 103 |

This is a boring test. 104 |

This is a boring test. 105 |

This is a boring test. 106 |

This is a boring test. 107 |

This is a boring test. 108 |

This is a boring test. 109 |

This is a boring test. 110 |

This is a boring test. 111 |

This is a boring test. 112 |

This is a boring test. 113 |

This is a boring test. 114 |

This is a boring test. 115 |

This is a boring test. 116 |

This is a boring test. 117 |

This is a boring test. 118 |

This is a boring test. 119 |

This is a boring test. 120 |

This is a boring test. 121 |

This is a boring test. 122 |

This is a boring test. 123 |

This is a boring test. 124 |

This is a boring test. 125 |

This is a boring test. 126 |

This is a boring test. 127 |

This is a boring test. 128 |

This is a boring test. 129 |

This is a boring test. 130 |

This is a boring test. 131 |

This is a boring test. 132 |

This is a boring test. 133 |

This is a boring test. 134 |

This is a boring test. 135 |

This is a boring test. 136 |

This is a boring test. 137 |

This is a boring test. 138 |

This is a boring test. 139 |

This is a boring test. 140 |

This is a boring test. 141 |

This is a boring test. 142 |

This is a boring test. 143 |

This is a boring test. 144 |

This is a boring test. 145 |

This is a boring test. 146 |

This is a boring test. 147 |

This is a boring test. 148 |

This is a boring test. 149 |

This is a boring test. 150 |

This is a boring test. 151 |

This is a boring test. 152 |

This is a boring test. 153 |

This is a boring test. 154 |

This is a boring test. 155 |

This is a boring test. 156 |

This is a boring test. 157 |

This is a boring test. 158 |

This is a boring test. 159 |

This is a boring test. 160 |

This is a boring test. 161 |

This is a boring test. 162 |

This is a boring test. 163 |

This is a boring test. 164 |

This is a boring test. 165 |

This is a boring test. 166 |

This is a boring test. 167 |

This is a boring test. 168 |

This is a boring test. 169 |

This is a boring test. 170 |

This is a boring test. 171 |

This is a boring test. 172 |

This is a boring test. 173 |

This is a boring test. 174 |

This is a boring test. 175 |

This is a boring test. 176 |

This is a boring test. 177 |

This is a boring test. 178 |

This is a boring test. 179 |

This is a boring test. 180 |

This is a boring test. 181 |

This is a boring test. 182 |

This is a boring test. 183 |

This is a boring test. 184 |

This is a boring test. 185 |

This is a boring test. 186 |

This is a boring test. 187 |

This is a boring test. 188 |

This is a boring test. 189 |

This is a boring test. 190 |

This is a boring test. 191 |

This is a boring test. 192 |

This is a boring test. 193 |

This is a boring test. 194 |

This is a boring test. 195 |

This is a boring test. 196 |

This is a boring test. 197 |

This is a boring test. 198 |

This is a boring test. 199 |

This is a boring test. 200 |

This is a boring test. 201 |

This is a boring test. 202 |

This is a boring test. 203 |

This is a boring test. 204 |

This is a boring test. 205 |

This is a boring test. 206 |

This is a boring test. 207 |

This is a boring test. 208 |

This is a boring test. 209 |

This is a boring test. 210 |

This is a boring test. 211 |

This is a boring test. 212 |

This is a boring test. 213 |

This is a boring test. 214 |

This is a boring test. 215 |

This is a boring test. 216 |

This is a boring test. 217 |

This is a boring test. 218 |

This is a boring test. 219 |

This is a boring test. 220 |

This is a boring test. 221 |

This is a boring test. 222 |

This is a boring test. 223 |

This is a boring test. 224 |

This is a boring test. 225 |

This is a boring test. 226 |

This is a boring test. 227 |

This is a boring test. 228 |

This is a boring test. 229 |

This is a boring test. 230 |

This is a boring test. 231 |

This is a boring test. 232 |

This is a boring test. 233 |

This is a boring test. 234 |

This is a boring test. 235 |

This is a boring test. 236 |

This is a boring test. 237 |

This is a boring test. 238 |

This is a boring test. 239 |

This is a boring test. 240 |

This is a boring test. 241 |

This is a boring test. 242 |

This is a boring test. 243 |

This is a boring test. 244 |

This is a boring test. 245 |

This is a boring test. 246 |

This is a boring test. 247 |

This is a boring test. 248 |

This is a boring test. 249 |

This is a boring test. 250 |

This is a boring test. 251 |

This is a boring test. 252 |

This is a boring test. 253 |

This is a boring test. 254 |

This is a boring test. 255 |

This is a boring test. 256 |

This is a boring test. 257 |

This is a boring test. 258 |

This is a boring test. 259 |

This is a boring test. 260 |

This is a boring test. 261 |

This is a boring test. 262 |

This is a boring test. 263 |

This is a boring test. 264 | 265 | 266 | -------------------------------------------------------------------------------- /html5validator/tests/test_config.py: -------------------------------------------------------------------------------- 1 | """Do an integration test for config file usage.""" 2 | 3 | import os 4 | import subprocess 5 | 6 | HTML_TEST_FILES = os.path.abspath(os.path.dirname(__file__)) 7 | 8 | 9 | def test_config_valid(): 10 | """Config test for valid HTML""" 11 | assert subprocess.call([ 12 | 'html5validator', 13 | f'--config={HTML_TEST_FILES}/config_files/valid.yaml']) == 0 14 | 15 | 16 | def test_config_invalid(): 17 | """Config test for invalid HTML""" 18 | assert subprocess.call([ 19 | 'html5validator', 20 | f'--config={HTML_TEST_FILES}/config_files/invalid.yaml']) == 1 21 | 22 | 23 | def test_config_skip(): 24 | """Config test for skipping files""" 25 | assert subprocess.call([ 26 | 'html5validator', 27 | f'--config={HTML_TEST_FILES}/config_files/skip.yaml']) == 2 28 | 29 | 30 | def test_config_invalid_with_css(): 31 | """Config test for CSS and HTML""" 32 | assert subprocess.call([ 33 | 'html5validator', 34 | f'--config={HTML_TEST_FILES}/config_files/invalid_css.yaml' 35 | ]) == 3 36 | 37 | 38 | def test_config_invalid_css_only(): 39 | """Config test for CSSS only""" 40 | assert subprocess.call([ 41 | 'html5validator', 42 | f"--config={HTML_TEST_FILES}/config_files/invalid_css_only.yaml"]) == 2 43 | 44 | 45 | def test_config_invalid_single_file(): 46 | """Config test for invalid single file""" 47 | assert subprocess.call([ 48 | 'html5validator', 49 | f"--config={HTML_TEST_FILES}/config_files/invalid_single_file.yaml" 50 | ]) == 1 51 | 52 | 53 | def test_config_warning(): 54 | """Config test for warnings""" 55 | assert subprocess.call([ 56 | 'html5validator', 57 | f"--config={HTML_TEST_FILES}/config_files/warning.yaml"]) == 1 58 | 59 | 60 | def test_config_warning_but_pass(): 61 | """Config test for allowed warnings""" 62 | assert subprocess.call([ 63 | 'html5validator', 64 | f'--config={HTML_TEST_FILES}/config_files/warning_pass.yaml']) == 0 65 | 66 | 67 | def test_config_return_value(): 68 | """Config test for error code return value""" 69 | assert subprocess.call([ 70 | 'html5validator', 71 | f'--config={HTML_TEST_FILES}/config_files/return_254.yaml']) == 254 72 | assert subprocess.call([ 73 | 'html5validator', 74 | f'--config={HTML_TEST_FILES}/config_files/return_255.yaml']) == 255 75 | assert subprocess.call([ 76 | 'html5validator', 77 | f'--config={HTML_TEST_FILES}/config_files/return_255_256.yaml']) == 255 78 | 79 | 80 | def test_config_angularjs(): 81 | """Config test for angularjs""" 82 | assert subprocess.call([ 83 | 'html5validator', 84 | f'--config={HTML_TEST_FILES}/config_files/angularjs.yaml']) == 0 85 | 86 | 87 | def test_config_angularjs_no_output_with_ignore(): 88 | """Make sure there is no spurious output when messages are ignored.""" 89 | assert subprocess.check_output([ 90 | 'html5validator', 91 | f'--config={HTML_TEST_FILES}/config_files/angularjs.yaml']) == b'' 92 | 93 | 94 | def test_config_angularjs_normal_quotes(): 95 | """Config test for normal angularjs""" 96 | assert subprocess.call([ 97 | 'html5validator', 98 | f'--config={HTML_TEST_FILES}/config_files/angularjs_normal.yaml']) == 0 99 | 100 | 101 | def test_config_multiple_ignoreres(): 102 | """Config test for multiple regex ignores""" 103 | assert subprocess.call([ 104 | 'html5validator', 105 | f'--config={HTML_TEST_FILES}/config_files/multiple_ignores.yaml']) == 0 106 | 107 | 108 | def test_config_ignore_and_ignorere(): 109 | """Config test for ignore and regex ignore""" 110 | assert subprocess.call([ 111 | 'html5validator', 112 | f'--config={HTML_TEST_FILES}/config_files/ignore_and_ignorere.yaml' 113 | ]) == 0 114 | 115 | 116 | def test_config_stack_size(): 117 | """Config test for stack size""" 118 | assert subprocess.call([ 119 | 'html5validator', 120 | f'--config={HTML_TEST_FILES}/config_files/stack_size.yaml']) == 0 121 | 122 | 123 | def test_config_valid_format_flags(): 124 | """Config test for output format for valid files""" 125 | assert subprocess.call([ 126 | 'html5validator', 127 | f'--config={HTML_TEST_FILES}/config_files/format_flags/text_valid.yaml' 128 | ]) == 0 129 | assert subprocess.call([ 130 | 'html5validator', 131 | f'--config={HTML_TEST_FILES}/config_files/format_flags/gnu_valid.yaml' 132 | ]) == 0 133 | assert subprocess.call([ 134 | 'html5validator', 135 | f'--config={HTML_TEST_FILES}/config_files/format_flags/json_valid.yaml' 136 | ]) == 0 137 | assert subprocess.call([ 138 | 'html5validator', 139 | f'--config={HTML_TEST_FILES}/config_files/format_flags/xml_valid.yaml' 140 | ]) == 0 141 | 142 | 143 | def test_config_invalid_format_flags(): 144 | """Config test for output format with invalid files""" 145 | assert subprocess.call([ 146 | 'html5validator', 147 | f'--config={HTML_TEST_FILES}/config_files/format_flags/' 148 | 'text_invalid.yaml' 149 | ]) == 3 150 | assert subprocess.call([ 151 | 'html5validator', 152 | f'--config={HTML_TEST_FILES}/config_files/format_flags/' 153 | 'gnu_invalid.yaml' 154 | ]) == 1 155 | assert subprocess.call([ 156 | 'html5validator', 157 | f'--config={HTML_TEST_FILES}/config_files/format_flags/' 158 | 'json_invalid.yaml' 159 | ]) == 1 160 | assert subprocess.call([ 161 | 'html5validator', 162 | f'--config={HTML_TEST_FILES}/config_files/format_flags/' 163 | 'xml_invalid.yaml' 164 | ]) == 8 165 | 166 | 167 | def test_config_log_file(): 168 | """Config test for log file""" 169 | assert subprocess.call([ 170 | 'html5validator', 171 | f'--config={HTML_TEST_FILES}/config_files/log_file.yaml']) == 0 172 | 173 | 174 | def test_config_extra(): 175 | """Config test for vnu extra arguments""" 176 | assert subprocess.call([ 177 | 'html5validator', 178 | f'--config={HTML_TEST_FILES}/config_files/extra.yaml']) == 0 179 | 180 | 181 | def test_config_nofiles(): 182 | """Command line test for missing files""" 183 | assert subprocess.call([ 184 | "html5validator", 185 | f'--config={HTML_TEST_FILES}/config_files/no_files.yaml' 186 | ]) == 1 187 | 188 | 189 | if __name__ == '__main__': 190 | test_config_valid() 191 | test_config_invalid() 192 | test_config_return_value() 193 | test_config_angularjs() 194 | test_config_multiple_ignoreres() 195 | test_config_ignore_and_ignorere() 196 | test_config_stack_size() 197 | test_config_valid_format_flags() 198 | test_config_invalid_format_flags() 199 | test_config_log_file() 200 | test_config_extra() 201 | -------------------------------------------------------------------------------- /html5validator/tests/test_simple.py: -------------------------------------------------------------------------------- 1 | """Do an integration test. Only use simple html files.""" 2 | 3 | import json 4 | import os 5 | import subprocess 6 | 7 | HTML_TEST_FILES = os.path.abspath(os.path.dirname(__file__)) 8 | 9 | 10 | def test_valid(): 11 | """Command line test for valid files""" 12 | assert subprocess.call(['html5validator', 13 | f'--root={HTML_TEST_FILES}/valid/']) == 0 14 | 15 | 16 | def test_invalid(): 17 | """Command line test for invalid files""" 18 | assert subprocess.call(['html5validator', 19 | f'--root={HTML_TEST_FILES}/invalid/']) == 1 20 | 21 | 22 | def test_invalid_with_css(): 23 | """Command line test for invalid CSS and HTML""" 24 | assert subprocess.call([ 25 | 'html5validator', 26 | f'--root={HTML_TEST_FILES}/invalid/', 27 | '--also-check-css', 28 | ]) == 2 29 | 30 | 31 | def test_invalid_css_only(): 32 | """Command line test for invalid CSS only""" 33 | assert subprocess.call([ 34 | 'html5validator', 35 | '--root', f'{HTML_TEST_FILES}/invalid/', 36 | '--skip-non-css', 37 | ]) == 1 38 | 39 | 40 | def test_invalid_single_file(): 41 | """Command line test for invalid single file""" 42 | assert subprocess.call([ 43 | 'html5validator', 44 | f'{HTML_TEST_FILES}/invalid/index.html', 45 | ]) == 1 46 | 47 | 48 | def test_warning(): 49 | """Command line test for warnings""" 50 | assert subprocess.call(['html5validator', 51 | f'--root={HTML_TEST_FILES}/warning/', 52 | '--show-warnings']) == 1 53 | 54 | 55 | def test_warning_but_pass(): 56 | """Command line test for passing warnings""" 57 | assert subprocess.call(['html5validator', 58 | f'--root={HTML_TEST_FILES}/warning/']) == 0 59 | 60 | 61 | def test_return_value(): 62 | """Command line test for error code return value""" 63 | assert subprocess.call(['html5validator', 64 | f'--root={HTML_TEST_FILES}/return_value/', 65 | '--match=254.html']) == 254 66 | assert subprocess.call(['html5validator', 67 | f'--root={HTML_TEST_FILES}/return_value/', 68 | '--match=255.html']) == 255 69 | assert subprocess.call(['html5validator', 70 | f'--root={HTML_TEST_FILES}/return_value/', 71 | '--match=256.html']) == 255 72 | 73 | 74 | def test_angularjs(): 75 | """Command line test for angularjs""" 76 | assert subprocess.call([ 77 | 'html5validator', 78 | f'--root={HTML_TEST_FILES}/angularjs/', 79 | '--ignore-re=Attribute “ng-[a-z-]+” not allowed', 80 | ]) == 0 81 | 82 | 83 | def test_angularjs_no_output_with_ignore(): 84 | """Make sure there is no spurious output when messages are ignored.""" 85 | 86 | output = subprocess.check_output([ 87 | 'html5validator', 88 | f'--root={HTML_TEST_FILES}/angularjs/', 89 | '--ignore-re=Attribute “ng-[a-z-]+” not allowed', 90 | ]) 91 | assert output == b'' 92 | 93 | 94 | def test_angularjs_normal_quotes(): 95 | """Command line test for passing angularjs""" 96 | assert subprocess.call([ 97 | 'html5validator', 98 | f'--root={HTML_TEST_FILES}/angularjs/', 99 | '--ignore-re=Attribute "ng-[a-z-]+" not allowed', 100 | ]) == 0 101 | 102 | 103 | def test_multiple_ignoreres(): 104 | """Command line test for multiple regex ignores""" 105 | assert subprocess.call([ 106 | 'html5validator', 107 | f'--root={HTML_TEST_FILES}/multiple_ignores/', 108 | '--ignore-re', 'Attribute “ng-[a-z-]+” not allowed', 109 | 'Start tag seen without seeing a doctype first', 110 | ]) == 0 111 | 112 | 113 | def test_ignore_and_ignorere(): 114 | """Command line test for ignore and regex ignores""" 115 | assert subprocess.call([ 116 | 'html5validator', 117 | f'--root={HTML_TEST_FILES}/multiple_ignores/', 118 | '--ignore-re', 'Attribute “ng-[a-z-]+” not allowed', 119 | '--ignore', 'Start tag seen without seeing a doctype first', 120 | ]) == 0 121 | 122 | 123 | def test_stack_size(): 124 | """Command line test for stack size""" 125 | assert subprocess.call(['html5validator', 126 | f'--root={HTML_TEST_FILES}/valid/', 127 | '-lll']) == 0 128 | 129 | 130 | def test_valid_format_flags(): 131 | """Command line test for output format for valid files""" 132 | assert subprocess.call(['html5validator', 133 | f'--root={HTML_TEST_FILES}/valid/', 134 | '--format', 'text']) == 0 135 | assert subprocess.call(['html5validator', 136 | f'--root={HTML_TEST_FILES}/valid/', 137 | '--format', 'gnu']) == 0 138 | assert subprocess.call(['html5validator', 139 | f'--root={HTML_TEST_FILES}/valid/', 140 | '--format', 'json']) == 0 141 | assert subprocess.call(['html5validator', 142 | f'--root={HTML_TEST_FILES}/valid/', 143 | '--format', 'xml']) == 0 144 | 145 | 146 | def test_invalid_format_flags(): 147 | """Command line test for output format for invalid files""" 148 | assert subprocess.call(['html5validator', 149 | f'--root={HTML_TEST_FILES}/invalid/', 150 | '--format', 'text']) == 3 151 | assert subprocess.call(['html5validator', 152 | f'--root={HTML_TEST_FILES}/invalid/', 153 | '--format', 'gnu']) == 1 154 | assert subprocess.call(['html5validator', 155 | f'--root={HTML_TEST_FILES}/invalid/', 156 | '--format', 'json']) == 1 157 | assert subprocess.call(['html5validator', 158 | f'--root={HTML_TEST_FILES}/invalid/', 159 | '--format', 'xml']) == 8 160 | 161 | 162 | def test_json_parseable(): 163 | out = subprocess.run(['html5validator', 164 | f'--root={HTML_TEST_FILES}/invalid/', 165 | '--format=json'], stdout=subprocess.PIPE).stdout 166 | assert out 167 | for line in out.decode('utf-8').splitlines(): 168 | print(line) 169 | json.loads(line) 170 | 171 | 172 | def test_log_file(): 173 | """Command line test for log file""" 174 | assert subprocess.call(['html5validator', 175 | f'--root={HTML_TEST_FILES}/valid/', 176 | '--log-file', 'test_command_line', 177 | '--log', 'DEBUG']) == 0 178 | 179 | 180 | def test_skip(): 181 | """Command line test for skipping file""" 182 | assert subprocess.call(['html5validator', 183 | '--blacklist', 'index.html', 184 | '--also-check-css', 185 | f'--root={HTML_TEST_FILES}/invalid/' 186 | ]) == 1 187 | 188 | 189 | def test_nofiles(): 190 | """Command line test for missing files""" 191 | assert subprocess.call([ 192 | "html5validator", "--root=MISSING" 193 | ]) == 1 194 | 195 | 196 | if __name__ == '__main__': 197 | test_valid() 198 | test_invalid() 199 | test_return_value() 200 | test_angularjs() 201 | test_multiple_ignoreres() 202 | test_ignore_and_ignorere() 203 | test_stack_size() 204 | test_valid_format_flags() 205 | test_invalid_format_flags() 206 | test_json_parseable() 207 | test_log_file() 208 | test_skip() 209 | test_nofiles() 210 | -------------------------------------------------------------------------------- /html5validator/tests/valid/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test 6 | 7 | 8 |

This is a boring test.

9 | 10 | 11 | -------------------------------------------------------------------------------- /html5validator/tests/warning/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test 5 | 6 | 7 | 8 | This is a boring test. 9 | 10 | 11 | -------------------------------------------------------------------------------- /html5validator/validator.py: -------------------------------------------------------------------------------- 1 | """The main validator class.""" 2 | 3 | 4 | import errno 5 | import fnmatch 6 | import logging 7 | import os 8 | import re 9 | from typing import List, Tuple, Optional 10 | import subprocess 11 | import sys 12 | import vnujar 13 | 14 | LOGGER = logging.getLogger(__name__) 15 | 16 | DEFAULT_IGNORE_RE: List[str] = [ 17 | r'\APicked up _JAVA_OPTIONS:.*', 18 | r'\ADocument checking completed. No errors found.*', 19 | ] 20 | 21 | DEFAULT_IGNORE: List[str] = [ 22 | '{"messages":[]}' 23 | ] 24 | 25 | DEFAULT_IGNORE_XML: List[str] = [ 26 | '', 27 | '', 28 | '' 29 | ] 30 | 31 | 32 | class JavaNotFoundException(Exception): 33 | """Error raised is there is no Java found""" 34 | def __str__(self): 35 | return ('Missing Java Runtime Environment on this system. ' 36 | 'The command "java" must be available.') 37 | 38 | 39 | def all_files( 40 | directory: str = '.', 41 | match: str = '*.html', 42 | blacklist: Optional[List[str]] = None, 43 | skip_invisible: bool = True) -> List: 44 | if blacklist is None: 45 | blacklist = [] 46 | if not isinstance(match, list): 47 | match = [match] 48 | 49 | files = [] 50 | for root, dirnames, filenames in os.walk(directory): 51 | # filter out blacklisted directory names 52 | for b in blacklist: 53 | if b in dirnames: 54 | dirnames.remove(b) 55 | if b in filenames: 56 | filenames.remove(b) 57 | 58 | if skip_invisible: 59 | # filter out directory names starting with '.' 60 | invisible_dirs = [d for d in dirnames if d[0] == '.'] 61 | for d in invisible_dirs: 62 | dirnames.remove(d) 63 | 64 | for pattern in match: 65 | for filename in fnmatch.filter(filenames, pattern): 66 | if skip_invisible and filename[0] == '.': 67 | # filter out invisible files 68 | continue 69 | files.append(os.path.join(root, filename)) 70 | 71 | return files 72 | 73 | 74 | def _cygwin_path_convert(filepath) -> str: 75 | return subprocess.check_output( 76 | ['cygpath', '-w', filepath], shell=False).strip().decode('utf8') 77 | 78 | 79 | def _normalize_string(s) -> str: 80 | s = s.replace('“', '"') 81 | s = s.replace('”', '"') 82 | return s 83 | 84 | 85 | class Validator: 86 | 87 | def __init__(self, 88 | ignore=None, ignore_re=None, 89 | errors_only=False, detect_language=True, format=None, 90 | stack_size=None, vnu_args=None): 91 | self.ignore = ignore if ignore else [] 92 | self.ignore_re = ignore_re if ignore_re else [] 93 | 94 | # java options 95 | self.stack_size = stack_size 96 | 97 | # vnu options 98 | self.errors_only = errors_only 99 | self.detect_language = detect_language 100 | self.format = format 101 | self.vnu_args = vnu_args 102 | 103 | # add default ignore_re 104 | self.ignore_re += DEFAULT_IGNORE_RE 105 | 106 | # add default ignore 107 | self.ignore += DEFAULT_IGNORE 108 | 109 | # process fancy quotes in ignore 110 | self.ignore = [_normalize_string(s) for s in self.ignore] 111 | self.ignore_re = [_normalize_string(s) for s in self.ignore_re] 112 | 113 | # Determine jar location. 114 | self.vnu_jar_location = (vnujar.__file__ 115 | .replace('__init__.pyc', 'vnu.jar') 116 | .replace('__init__.py', 'vnu.jar')) 117 | if sys.platform == 'cygwin': 118 | self.vnu_jar_location = _cygwin_path_convert( 119 | self.vnu_jar_location) 120 | 121 | def _java_options(self) -> List[str]: 122 | java_options = [] 123 | 124 | if self.stack_size is not None: 125 | java_options.append(f'-Xss{self.stack_size}k') 126 | 127 | return java_options 128 | 129 | def _vnu_options(self) -> List[str]: 130 | vnu_options = [] 131 | 132 | if self.errors_only: 133 | vnu_options.append('--errors-only') 134 | if not self.detect_language: 135 | vnu_options.append('--no-langdetect') 136 | if self.format is not None: 137 | vnu_options.append('--format') 138 | vnu_options.append(self.format) 139 | if self.vnu_args is not None: 140 | vnu_options += self.vnu_args 141 | 142 | return vnu_options 143 | 144 | def run_vnu(self, arguments) -> Tuple[str, str]: 145 | try: 146 | cmd = (['java'] + self._java_options() 147 | + ['-jar', self.vnu_jar_location] 148 | + arguments) 149 | LOGGER.debug(cmd) 150 | p = subprocess.Popen( 151 | cmd, 152 | stdout=subprocess.PIPE, 153 | stderr=subprocess.PIPE 154 | ) 155 | stdout, stderr = p.communicate() 156 | except OSError as e: 157 | if e.errno == errno.ENOENT: 158 | raise JavaNotFoundException() 159 | else: 160 | raise 161 | except subprocess.CalledProcessError as error: 162 | raise (error.output.decode('utf-8')) 163 | 164 | return stdout.decode('utf-8'), stderr.decode('utf-8') 165 | 166 | def validate(self, files): 167 | if sys.platform == 'cygwin': 168 | files = [_cygwin_path_convert(f) for f in files] 169 | 170 | stdout, stderr = self.run_vnu(self._vnu_options() + files) 171 | 172 | # process fancy quotes into standard quotes 173 | stdout = _normalize_string(stdout) 174 | stderr = _normalize_string(stderr) 175 | 176 | err = stdout.splitlines() + stderr.splitlines() 177 | 178 | # Removes any empty items in the list 179 | err = list(filter(None, err)) 180 | 181 | # Prevents removal of xml tags if there are errors 182 | if self.format == "xml" and len(err) < 4: 183 | self.ignore = DEFAULT_IGNORE_XML 184 | 185 | LOGGER.debug(err) 186 | 187 | for ignored in self.ignore: 188 | err = [line for line in err if ignored not in line] 189 | for ignored in self.ignore_re: 190 | regex = re.compile(ignored) 191 | err = [line for line in err if not regex.search(line)] 192 | 193 | if err: 194 | for line in err: 195 | print(line) 196 | else: 197 | LOGGER.info('All good.') 198 | return len(err) 199 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = H301,W503 3 | exclude = venv*/,docs,build,dist,pypy 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | from html5validator import __version__ 4 | 5 | 6 | setup( 7 | name='html5validator', 8 | version=__version__, 9 | packages=['html5validator', 'html5validator.tests', 'vnujar'], 10 | license='MIT', 11 | description='Validate HTML5 files.', 12 | long_description=open('README.rst').read(), 13 | long_description_content_type='text/x-rst', 14 | author='Sven Kreiss', 15 | author_email='me@svenkreiss.com', 16 | url='https://github.com/svenkreiss/html5validator', 17 | 18 | include_package_data=True, 19 | zip_safe=False, 20 | python_reqires=">=3.6", 21 | install_requires=[ 22 | 'PyYAML', 23 | ], 24 | extras_require={ 25 | 'tests': [ 26 | 'hacking', 27 | 'pytest', 28 | ], 29 | }, 30 | entry_points={ 31 | 'console_scripts': [ 32 | 'html5validator = html5validator.cli:main', 33 | ] 34 | }, 35 | 36 | classifiers=[ 37 | 'Development Status :: 3 - Alpha', 38 | 'Intended Audience :: Developers', 39 | 'Intended Audience :: Science/Research', 40 | 'Natural Language :: English', 41 | 'License :: OSI Approved :: MIT License', 42 | 'Operating System :: OS Independent', 43 | 'Programming Language :: Python', 44 | 'Programming Language :: Python :: 3.6', 45 | "Programming Language :: Python :: 3.7", 46 | "Programming Language :: Python :: 3.8", 47 | "Programming Language :: Python :: 3.9", 48 | "Programming Language :: Python :: 3.10", 49 | 'Programming Language :: Python :: Implementation :: PyPy', 50 | ] 51 | ) 52 | -------------------------------------------------------------------------------- /vnujar/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | With a few exceptions, this is a record of mainly just user-facing 2 | changes — that is, either changes to the actual behavior of the checker, 3 | or changes to any options/interfaces the checker exposes for developers. 4 | 5 | # 20.6.30 6 | 30 June 2020 7 | - CLI: Add new `--stdout` option, to report all messages to stdout 8 | - CLI: Ensure the `--version` option reports the actual version 9 | - CLI: Actually check SVG files, rather than just seeming to (bug fix) 10 | - CSS: Improvements in `calc()`, `min()`/`max()` checking, more 11 | - Allow `height` and `width` attributes for SVG `symbol` element 12 | - Allow `capture` attribute for the `input[type=file]` element 13 | - Allow `disabled` attribute for the `link[rel=stylesheet]` element 14 | - Treat custom elements as labelable elements for `label[for]` handling 15 | - ARIA: Fix bug that disallowed implicit combobox/listbox for `select` 16 | - ARIA: Improve error message for `alt`-less `img` with ARIA attributes 17 | - ARIA: Allow `section[role=doc-glossary]` 18 | 19 | # 20.3.16 20 | 16 March 2020 21 | - Disallow `accept-charset` values other than `UTF-8` 22 | - Disallow object[typemustmatch] 23 | - Allow SVG feDropShadow element (from Filter Effects spec) 24 | - Allow `loading` attribute for the `img` element (lazy loading) 25 | - Allow `rel=modulepreload` for the `link` element 26 | - Allow `integrity` attribute on link[rel=preload|modulepreload] 27 | - Allow `integrity` attribute on script[type=module] 28 | - Allow `autofocus` as a global attribute 29 | - Allow `inputmode` as a global attribute 30 | - Allow `nonce` as a global attribute 31 | - Allow `allow-downloads` in iframe[sandbox] 32 | - Allow heading content within `legend` element 33 | - Allow negated media features in `media` and `sizes` attributes 34 | - Align `autocomplete` checking with current spec 35 | - CSS: Improve support for color values 36 | - ARIA: Allow implicit roles to satisfy owned-by requirements 37 | - ARIA: Add proper ARIA checking for the `math` element 38 | - ARIA: Align all role checking with current HTML-ARIA spec 39 | - Add option to specify additional script for Web-based checker 40 | - CLI: Make --errors-only option take precedence over --Werror 41 | - CLI: Enable checking standard input as SVG or CSS 42 | - Include binary runtime images in release (alternative to jar/war) 43 | - Dockerfile: Switch to using binary runtime image rather than jar 44 | - Add checker.py script to repo (for building/testing/running checker) 45 | - Add option to bind to specific IP address (rather than all interfaces) 46 | 47 | # 18.11.5 48 | 05 November 2018 49 | - Fix bugs that can cause the command-line checker to emit broken JSON output 50 | - Allow `dppx` and `x` and units in media queries 51 | 52 | # 18.8.29 53 | 29 August 2018 54 | - CSS: Allow unit-less values for stroke-width and other from-SVG props 55 | - CSS: Bring checking up to date w/ CSS Align3; support font-display 56 | 57 | # 18.7.23 58 | 23 July 2018 59 | - Disable logging in the language detector 60 | 61 | # 18.7.22 62 | 22 July 2018 63 | - Allow the `decoding` attribute for the `img` element 64 | - Allow the `allow` attribute for the `iframe` element (initial support) 65 | - Align ARIA checking further with ARIA in HTML spec requirements 66 | - Restore the language-detection feature to vnu.jar command-line checker 67 | - Ensure vnu.jar is always runnable under Java8, even if built under Java9 68 | 69 | # 18.3.0 70 | 24 March 2018 71 | - Add new major (optional) feature to command-line checker, Web-based checker, 72 | and network API to check CSS documents (in addition to HTML documents) 73 | - Add new major feature to check that `style` element contents and `style` 74 | attribute values in HTML documents are valid CSS 75 | - Add new (optional) feature to command-line checker to check SVG documents 76 | - Add new option to command-line checker for specifying User-Agent string 77 | - Add Dockerfile to Docker Hub https://hub.docker.com/r/validator/validator/ 78 | - Add ability to Web-based checker to check SVG documents by file upload 79 | - Emit error (not warning) for HTML4/XHTML1 strict doctypes 80 | - Further restrict `script[type]` and `style[type]` 81 | - Allow the `autocapitalize` global attribute 82 | - Allow the `slot` attribute (for Shadow DOM interaction) 83 | - Allow the `allowpaymentrequest` attribute for the `iframe` element 84 | - Allow only one non-hidden `main` element per document 85 | - Allow only `html`, `body`, `div`, `form`, custom elements as `main` ancestor 86 | - Allow `` end tag to be omitted 87 | - Allow `role=none` where `role=presentation` is allowed 88 | - Allow `role=rowgroup` element to be contained in `role=table` element 89 | - Allow `role=row` element to be contained in `role=table` element 90 | - Allow more values for `aria-haspopup` per current ARIA spec 91 | - Allow more ARIA states & properties for `role=menuitem` 92 | - Allow CSP `prefetch-src` directive (updated to Salvation 2.4.0) 93 | - Disallow all character encodings other than UTF-8 94 | - Disallow `script[charset]` 95 | - Disallow nested interactive ARIA roles 96 | - Disallow the `dropzone` attribute 97 | - Disallow the `menuitem` element 98 | - Fix bug that caused the checker to incorrectly treat `input[type]` values 99 | case-sensitively when doing particular checks 100 | 101 | # 17.11.1 102 | 07 October 2017 103 | - Fix bug that made the vnu.jar `--Werror` option not work as expected 104 | - Make vnu.jar exit 0 if all errors have been filtered out 105 | 106 | # 17.11.0 107 | 06 October 2017 108 | - Allow DPUB roles on more elements (per ARIA in HTML spec updates) 109 | - Add `--Werror` option to the vnu.jar command-line checker. The option 110 | causes the checker to exit non-zero if any warnings are encountered 111 | (even if there are no errors). 112 | - Fix mismatch that caused message-filtering failures 113 | - Fix memory leak in language detector (patch from @tgyurci) 114 | - Stop reporting HTML4-specific parse errors for HTML4-doctype docs 115 | 116 | # 17.9.0 117 | 20 August 2017 118 | - Allow `script[nomodule]` 119 | - Allow `hover`, `any-hover`, `pointer`, and `any-pointer` media features 120 | - Allow `@scope`, `@updateviacache`, `@workertype` for `link[rel=serviceworker]` 121 | - Allow `&;` (don’t report it as “`&` did not start a character reference”) 122 | - Add `acceptlanguage` query parameter, to specify an Accept-Language 123 | request-header value for checker to send when fetching remote documents 124 | - Update CSP checking to Salvation 2.3.0 125 | https://github.com/shapesecurity/salvation/releases/tag/v2.3.0 126 | 127 | # 17.7.0 128 | 26 June 2017 129 | - Add new major feature to filter out (drop/ignore/suppress) errors/warnings 130 | by regex. See https://github.com/validator/validator/wiki/Message-filtering 131 | - Replace a case of “Attribute "foo" not allowed on element "param" **in this 132 | context**” wording in error message with “Attribute "foo" not allowed on 133 | element "param" **at this point**” (for consistent working for that error 134 | between the command-line checker and the web-based checker). 135 | - Disallow the `contextmenu` attribute and `type=contextmenu` and `type=toolbar` 136 | for the `menu` element. 137 | - Allow `link[rel=serviceworker]` 138 | - Allow floating-point numbers in attribute values to start with decimal point 139 | - Allow `a[href]` in SVG wherever `a[xlink:href]` is allowed 140 | - Allow the `focusable` and `tabindex` attributes on SVG elements 141 | - Fix bug that disallowed `progress` & `meter` as `label[for]` targets 142 | - Default to text/html for checking file uploads 143 | - Emit warnings for use of `rel={copyright,previous}` 144 | - Prevent Bulgarian ➡ Russian misidentifications in language detector 145 | - Skip figcaption elements when running the language detector 146 | 147 | # 17.3.0 148 | 26 March 2017 149 | - Allow `color` attribute with `link[rel="mask-icon"]` 150 | - Allow `meta[name]` to have `itemref`/`itemscope`/`itemtype`/`itemid` 151 | - Allow `allow-top-navigation-by-user-activation` in `iframe[sandbox]` 152 | - Stop hiding “sectioning roots” headings in “Heading-level outline” 153 | - Change error for `role=none` with `img[alt=""]` to warning 154 | - Fix from @xfq for longstanding bug in “Show source” behavior in Web UI 155 | - Fix from @xfq for controlling some runtime params for HTTP behavior 156 | - Fix from @zcorpan to drop unneeded warning for `` 157 | - Make “Corrupt GZIP trailer” a non-error 158 | - Add `--asciiquotes` option to vnu.jar command-line checker 159 | - Skip lang detection of elements w/ lang attributes not matching `html[lang]` 160 | - Drop Bulgarian lang detection, to prevent Russian misidentification 161 | - Update Estonian/Catalan lang profiles, to prevent Russian misidentification 162 | - Update ICU4J to 58.2 163 | 164 | # 17.2.1 165 | 06 February 2017 166 | - Fix bug in language detector that when running the vnu.jar command-line 167 | checker on a list of documents caused it to sometimes misidentify the 168 | language of the 2nd, 3rd, 4th, etc., documents. The bug also caused the 169 | memory used by the checker to increase as the number of documents 170 | checked at the same time increased, and caused performance to degrade. 171 | - Allow `aria-required` attribute everywhere `required` attribute is allowed 172 | - Add `--exit-zero-always` option to vnu.jar command-line checker 173 | - Fix longstanding bug around code for identifying overlapping cells in 174 | table-integrity checker (the bug somewhat frequently gets hit when checking 175 | Wikipedia pages but otherwise in the wild gets hit only extremely rarely) 176 | 177 | # 17.2.0 178 | 30 January 2017 179 | - Fix bug that broke vnu.jar command-line checking of URLs 180 | - Fix bug in `rel="shortcut icon"` checking 181 | - Add `nu.client.EmbeddedValidator` for use as library by other Java apps 182 | - Disallow `tfoot` before `tbody` 183 | 184 | # 17.1.0 185 | 15 January 2017 186 | This release adds the following changes to the vnu.jar command-line 187 | checker that had already been made available in the Web-based checker in 188 | the 17.0.1 release. 189 | - Allow **custom elements** (names containing “-”; e.g., ``) 190 | - Allow anything in `template` element subtrees (exclude from checking) 191 | 192 | # 17.0.1 193 | 08 January 2017 194 | - New language-detection feature; warns for missing/wrong `html[lang]` 195 | - New option `--no-langdetect` for `vnu.jar` disables language detection 196 | - Allow **custom elements** (names containing “-”; e.g., ``) 197 | - Allow the `is` attribute (for custom elements) 198 | - Allow **ARIA 1.1** roles/states/properties 199 | - Warn for viewport values that restrict resizing 200 | - Allow `div` in `dl`, to group `dt`+`dd` sets 201 | - Allow anything in `template` element subtrees (exclude from checking) 202 | - Allow `link[rel=preload]` in body 203 | - Disallow `sizes` attribute on non-icon `link` 204 | - Allow `` 205 | - Allow comments before doctype (warning dropped) 206 | - Allow `