From f4c3a77b8c93363ea83944755c488402cb1ced5f Mon Sep 17 00:00:00 2001 From: jarrekk Date: Tue, 6 Apr 2021 14:59:44 +0800 Subject: [PATCH] use gh actions, fix bugs --- .../CODE_OF_CONDUCT.md | 0 .github/workflows/release.yml | 29 ++ .github/workflows/unit_test.yml | 41 ++ .gitignore | 1 + .travis.yml | 39 -- AUTHORS.md | 11 - CHANGELOG.md | 50 ++ MANIFEST.io | 1 - README.md | 18 +- pyproject.toml | 7 + requirements.txt | 4 + setup.cfg | 35 ++ setup.py | 63 +-- {imgkit => src/imgkit}/__init__.py | 3 +- {imgkit => src/imgkit}/api.py | 0 {imgkit => src/imgkit}/config.py | 0 {imgkit => src/imgkit}/imgkit.py | 9 +- {imgkit => src/imgkit}/source.py | 0 test/imgkit_test.py | 426 ----------------- tests/__init__.py | 0 {test => tests}/fixtures/example.css | 0 {test => tests}/fixtures/example.html | 0 {test => tests}/fixtures/example2.css | 0 tests/imgkit_test.py | 446 ++++++++++++++++++ travis/init.sh | 7 - travis/pandoc.sh | 5 - 26 files changed, 637 insertions(+), 558 deletions(-) rename CODE_OF_CONDUCT.md => .github/CODE_OF_CONDUCT.md (100%) create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/unit_test.yml delete mode 100644 .travis.yml delete mode 100644 AUTHORS.md create mode 100644 CHANGELOG.md delete mode 100644 MANIFEST.io create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 setup.cfg rename {imgkit => src/imgkit}/__init__.py (77%) rename {imgkit => src/imgkit}/api.py (100%) rename {imgkit => src/imgkit}/config.py (100%) rename {imgkit => src/imgkit}/imgkit.py (98%) rename {imgkit => src/imgkit}/source.py (100%) delete mode 100755 test/imgkit_test.py create mode 100644 tests/__init__.py rename {test => tests}/fixtures/example.css (100%) rename {test => tests}/fixtures/example.html (100%) rename {test => tests}/fixtures/example2.css (100%) create mode 100755 tests/imgkit_test.py delete mode 100644 travis/init.sh delete mode 100644 travis/pandoc.sh diff --git a/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md similarity index 100% rename from CODE_OF_CONDUCT.md rename to .github/CODE_OF_CONDUCT.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..93d963c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,29 @@ +name: Release + +on: + push: + tags: + - 'V*' + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.7 + uses: actions/setup-python@v2 + with: + python-version: 3.7 + + - name: Install pypa/build + run: python -m pip install build --user + + - name: Build a binary wheel and a source tarball + run: python -m build --sdist --wheel --outdir dist/ . + + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@master + with: + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml new file mode 100644 index 0000000..145417e --- /dev/null +++ b/.github/workflows/unit_test.yml @@ -0,0 +1,41 @@ +name: Unit Test + +on: + push: + branches: + - master + tags-ignore: + - 'V*' + pull_request: + branches: + - master + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [2.7, 3.6, 3.7, 3.8, 3.9] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + sudo apt update + sudo apt install -y xvfb + wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.bionic_amd64.deb + sudo apt install -y ./wkhtmltox_0.12.6-1.bionic_amd64.deb + pip install -r requirements.txt + + - name: Run unit test + run: nosetests --with-coverage --cover-package=src/imgkit --where tests imgkit_test.py + + - name: Run code coverage + run: codecov diff --git a/.gitignore b/.gitignore index 802db70..71af159 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ build dist imgkit.egg-info +__pycache__ # Tests .tox diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ffbe460..0000000 --- a/.travis.yml +++ /dev/null @@ -1,39 +0,0 @@ -sudo: required - -language: python -matrix: - include: - - python: 2.7 - dist: bionic - - python: 3.6 - dist: bionic - - python: 3.7 - dist: bionic - - python: 3.8 - dist: bionic - - python: 3.9 - dist: bionic - -before_script: - - sh ./travis/init.sh - - pip install codecov - -script: - - nosetests --with-coverage --cover-package=imgkit --where test imgkit_test.py - - codecov - -before_deploy: sh ./travis/pandoc.sh - -deploy: - provider: pypi - user: jack614 - password: - secure: zzVWyvpe5oKzfBpm56bed6pruG7+DwW6KsG4zNACfrfR9CKYZUH4JxaTA6kEdiIZX9Syb+mBXg4yobiueJ2aMSQ78VQbUR9R/ULjDkBPyJjs3jqY3ekpbxOHAIDZ4q3cSfjt55veMy1eJ1ojhhwUkdzm2n0O+rsPd4njNxuVxQ2tgHSoId6gvv2X5f7m6qY5Kgs66A1x+Z8amaHeD16w5f33paYNTvIfQDvlQJrljtQ+b4zgeln8PfJucXkjU1BnEnBKZgc+Ii+mnFgIVFJA3WnuszmupG17Pbh2EhR/GWSlVZAalNIU/zzW1cgOG1h0hISJOl1J+Y4Fi5qfs7pWOv3yz0xnD1RJ8GlWnEhrP6VPbcBQSmjueJTkTN5lH1xOrAto0D1saZo2RHEp78uGTZlvjFTCWfkk4/V7QYtRVY70LqUYs6ay0KWZ2BAQvd0nk7C1UA3cc5vQt2XTHdr5DPRp+QV+g76vfjI03EzDYCxNXDNbg2CSn8wffux+YlOrqvLd1kjwyyRuDJSHrtkpzk6tygSY1UU541OYsl5y02zFZQARIoELsz6wx/IfAYwBlBT0SYH/a48aYh2bVq/0cXrXcUsR8haf2pPo2CzJZitcRAOis1qVM42b+cTBmO1mo1LW/91p26ldZtVqpXxdHQqGZQIBHunkE5SfD/iORLo= - on: - tags: true - distributions: sdist bdist_wheel - repo: jarrekk/imgkit - python: 3.7 - -notifications: - email: me@jarrekk.com diff --git a/AUTHORS.md b/AUTHORS.md deleted file mode 100644 index a92d22f..0000000 --- a/AUTHORS.md +++ /dev/null @@ -1,11 +0,0 @@ -## IMGKit author - -* **jarrekk** - -### Contributors - -* **v-hunt** -* **pprmint** -* **v-hunt** -* **arayate** -* **berkerboy** diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2d7f97f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,50 @@ + +# Change Log + +## [1.2.0] - 2018-04-07 + +### Added + +### Changed + +Use GH actions for CI/CD. +### Fixed + +Fix `six` package requirements. + +## [1.1.1] - 2018-04-06 + +### Added + +### Changed + +### Fixed + +Use `six` for `basestring` compatibility. + +## [1.1.0] - 2021-03-17 + +### Added + +### Changed + +- Refine code on get `wkhtmltoimage` & `xvfb-run` path. +- Refine code based on codacy suggestion. +- Use issue templates in GitHub. + +### Fixed + +## [1.0.5] - 2021-03-12 + +### Added + +### Changed + +- Fix travis-ci CI/CD error. +- Support new python version: 3.7, 3.8, 3.9. + +### Fixed + +## [1.0.2] - 2019-05-16 +## [1.0.1] - 2018-02-08 +## [1.0.0] - 2018-02-07 diff --git a/MANIFEST.io b/MANIFEST.io deleted file mode 100644 index aed7ede..0000000 --- a/MANIFEST.io +++ /dev/null @@ -1 +0,0 @@ -include README.md AUTHORS.md LICENSE diff --git a/README.md b/README.md index 14e3688..27dc129 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # IMGKit: Python library of HTML to IMG wrapper -[![Build Status](https://travis-ci.org/jarrekk/imgkit.svg?branch=master)](https://travis-ci.org/jarrekk/imgkit) +[![Unit Test](https://github.com/jarrekk/imgkit/actions/workflows/unit_test.yml/badge.svg?branch=master)](https://github.com/jarrekk/imgkit/actions/workflows/unit_test.yml) [![codecov](https://codecov.io/gh/jarrekk/imgkit/branch/master/graph/badge.svg?token=pNl4TtuAzz)](https://codecov.io/gh/jarrekk/imgkit) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/aa1f67f04ff24bb080b7f8c8a9b7b8b1)](https://www.codacy.com/app/jarrekk/imgkit?utm_source=github.com&utm_medium=referral&utm_content=jarrekk/imgkit&utm_campaign=Badge_Grade) +[![Release](https://github.com/jarrekk/imgkit/actions/workflows/release.yml/badge.svg)](https://github.com/jarrekk/imgkit/actions/workflows/release.yml) [![PyPI version](https://badge.fury.io/py/imgkit.svg)](https://badge.fury.io/py/imgkit) ``` text @@ -71,7 +72,7 @@ If you wish to further process generated IMG, you can read it to a variable: img = imgkit.from_url('http://google.com', False) ``` -You can find all wkhtmltoimage options by type `wkhtmltoimage` command or visit this [Manual](http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltoimage_0.10.0_rc2-doc.html). You can drop '--' in option name. If option without value, use *None, False* or *''* for dict value:. For repeatable options (incl. allow, cookie, custom-header, post, postfile, run-script, replace) you may use a list or a tuple. With option that need multiple values (e.g. --custom-header Authorization secret) we may use a 2-tuple (see example below). +You can find all wkhtmltoimage options by type `wkhtmltoimage` command or visit this [Manual](https://wkhtmltopdf.org/usage/wkhtmltopdf.txt). You can drop '--' in option name. If option without value, use *None, False* or *''* for dict value:. For repeatable options (incl. allow, cookie, custom-header, post, postfile, run-script, replace) you may use a list or a tuple. With option that need multiple values (e.g. --custom-header Authorization secret) we may use a 2-tuple (see example below). ``` python options = { @@ -190,3 +191,16 @@ imgkit.from_string(html_string, output_file, config=config) ## Credit [python PDFKit](https://github.com/JazzCore/python-pdfkit) + +## IMGKit author + +* **jarrekk** + +### Contributors + +* **v-hunt** +* **archydeberker** +* **arayate** +* **xtrntr** +* **mike1703** +* **themeewa** diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4ae8626 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[build-system] +build-backend = "setuptools.build_meta" +requires = [ + "setuptools", + "wheel", + "six" +] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c404b1f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +six +coverage +codecov +nose \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..78196a9 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,35 @@ +[metadata] +# replace with your username: +name = imgkit +version = 1.2.0 +author = Jarrekk +author_email = me@jarrekk.com +description = Wkhtmltopdf python wrapper to convert html to image using the webkit rendering engine and qt +long_description = file: README.md +long_description_content_type = text/markdown +url = https://github.com/jarrekk/imgkit +project_urls = + Bug Tracker = https://github.com/jarrekk/imgkit/issues +classifiers = + Programming Language :: Python + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + License :: OSI Approved :: MIT License + Operating System :: OS Independent + Topic :: Text Processing + Topic :: Text Processing :: General + Topic :: Text Processing :: Markup + Topic :: Text Processing :: Markup :: HTML + Topic :: Text Processing :: Markup :: XML + Topic :: Utilities + +[options] +package_dir = + = src +packages = find: + +[options.packages.find] +where = src \ No newline at end of file diff --git a/setup.py b/setup.py index 62aa094..2bbf5a6 100644 --- a/setup.py +++ b/setup.py @@ -1,61 +1,8 @@ -# -*- coding: utf-8 -*- -from distutils.core import setup -from setuptools.command.test import test -import os -import sys -import imgkit - - -class PyTest(test): - def finalize_options(self): - test.finalize_options(self) - self.test_args = ['imgkit_test.py'] - self.test_suite = True - - def run_tests(self): - # import here, cause outside the eggs aren't loaded - import pytest - os.chdir('test/') - err_no = pytest.main(self.test_args) - sys.exit(err_no) - - -def long_description(): - try: - import pypandoc - long_desc = pypandoc.convert_file('README.md', 'rst') - long_desc += '\n' + pypandoc.convert_file('AUTHORS.md', 'rst') - except Exception as e: - print(e) - long_desc = imgkit.__doc__.strip() - return long_desc - +from setuptools import setup setup( - name='imgkit', - version=imgkit.__version__, - description=imgkit.__doc__.strip(), - long_description=long_description(), - download_url='https://github.com/jarrekk/imgkit', - license=imgkit.__license__, - tests_require=['pytest'], - cmdclass={'test': PyTest}, - packages=['imgkit'], - author=imgkit.__author__, - author_email=imgkit.__contact__, - url=imgkit.__homepage__, - classifiers=[ - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Topic :: Text Processing', - 'Topic :: Text Processing :: General', - 'Topic :: Text Processing :: Markup', - 'Topic :: Text Processing :: Markup :: HTML', - 'Topic :: Text Processing :: Markup :: XML', - 'Topic :: Utilities' - ] + name="imgkit", + install_requires=[ + "six", + ], ) diff --git a/imgkit/__init__.py b/src/imgkit/__init__.py similarity index 77% rename from imgkit/__init__.py rename to src/imgkit/__init__.py index fa53f64..6beca1c 100644 --- a/imgkit/__init__.py +++ b/src/imgkit/__init__.py @@ -3,9 +3,8 @@ __author__ = "jarrekk" __contact__ = "me@jarrekk.com" -__version__ = "1.1.0" __homepage__ = "https://github.com/jarrekk/imgkit" __license__ = "MIT" +from .api import config, from_file, from_string, from_url from .imgkit import IMGKit -from .api import from_url, from_file, from_string, config diff --git a/imgkit/api.py b/src/imgkit/api.py similarity index 100% rename from imgkit/api.py rename to src/imgkit/api.py diff --git a/imgkit/config.py b/src/imgkit/config.py similarity index 100% rename from imgkit/config.py rename to src/imgkit/config.py diff --git a/imgkit/imgkit.py b/src/imgkit/imgkit.py similarity index 98% rename from imgkit/imgkit.py rename to src/imgkit/imgkit.py index 1aefa55..aa14f1b 100644 --- a/imgkit/imgkit.py +++ b/src/imgkit/imgkit.py @@ -6,12 +6,7 @@ from .config import Config import io import codecs - -# Python 2.x and 3.x support for checking string types -try: - assert basestring -except NameError: - basestring = str +from six import string_types class IMGKit(object): @@ -111,7 +106,7 @@ def _command(self, path=None): if self.source.isString() or self.source.isFileObj(): yield '-' else: - if isinstance(self.source.source, basestring): + if isinstance(self.source.source, string_types): yield self.source.to_s() else: for s in self.source.source: diff --git a/imgkit/source.py b/src/imgkit/source.py similarity index 100% rename from imgkit/source.py rename to src/imgkit/source.py diff --git a/test/imgkit_test.py b/test/imgkit_test.py deleted file mode 100755 index 1428a79..0000000 --- a/test/imgkit_test.py +++ /dev/null @@ -1,426 +0,0 @@ -# -*- coding: utf-8 -*- -import os -import io -import sys -import codecs -import unittest - -# Prepend ../ to PYTHONPATH so that we can import IMGKIT form there. -TEST_ROOT = os.path.abspath(os.path.dirname(__file__)) -sys.path.insert(0, os.path.realpath(os.path.join(TEST_ROOT, '..'))) - -import imgkit - - -class TestIMGKitInitialization(unittest.TestCase): - - def test_html_source(self): - r = imgkit.IMGKit('

Oh hai

', 'string') - self.assertTrue(r.source.isString()) - - def test_url_source(self): - r = imgkit.IMGKit('http://ya.ru', 'url') - self.assertTrue(r.source.isUrl()) - - def test_file_source(self): - r = imgkit.IMGKit('fixtures/example.html', 'file') - self.assertTrue(r.source.isFile()) - - def test_file_object_source(self): - with open('fixtures/example.html') as fl: - r = imgkit.IMGKit(fl, 'file') - self.assertTrue(r.source.isFileObj()) - - def test_file_source_with_path(self): - r = imgkit.IMGKit('test', 'string') - with io.open('fixtures/example.css') as f: - self.assertTrue(r.source.isFile(path=f)) - with codecs.open('fixtures/example.css', encoding='UTF-8') as f: - self.assertTrue(r.source.isFile(path=f)) - - def test_options_parsing(self): - r = imgkit.IMGKit('html', 'string', options={'format': 'jpg'}) - test_command = r.command('test') - idx = test_command.index('--format') # Raise exception in case of not found - self.assertTrue(test_command[idx + 1] == 'jpg') - - def test_options_parsing_with_dashes(self): - r = imgkit.IMGKit('html', 'string', options={'--format': 'jpg'}) - - test_command = r.command('test') - idx = test_command.index('--format') # Raise exception in case of not found - self.assertTrue(test_command[idx + 1] == 'jpg') - - def test_options_parsing_with_tuple(self): - options = { - '--custom-header': [ - ('Accept-Encoding', 'gzip') - ] - } - r = imgkit.IMGKit('html', 'string', options=options) - command = r.command() - idx1 = command.index('--custom-header') # Raise exception in case of not found - self.assertTrue(command[idx1 + 1] == 'Accept-Encoding') - self.assertTrue(command[idx1 + 2] == 'gzip') - - def test_options_parsing_with_tuple_no_dashes(self): - options = { - 'custom-header': [ - ('Accept-Encoding', 'gzip') - ] - } - r = imgkit.IMGKit('html', 'string', options=options) - command = r.command() - idx1 = command.index('--custom-header') # Raise exception in case of not found - self.assertTrue(command[idx1 + 1] == 'Accept-Encoding') - self.assertTrue(command[idx1 + 2] == 'gzip') - - def test_repeatable_options(self): - roptions = { - '--format': 'jpg', - 'cookies': [ - ('test_cookie1', 'cookie_value1'), - ('test_cookie2', 'cookie_value2'), - ] - } - - r = imgkit.IMGKit('html', 'string', options=roptions) - - test_command = r.command('test') - - idx1 = test_command.index('--format') # Raise exception in case of not found - self.assertTrue(test_command[idx1 + 1] == 'jpg') - - self.assertTrue(test_command.count('--cookies') == 2) - - idx2 = test_command.index('--cookies') - self.assertTrue(test_command[idx2 + 1] == 'test_cookie1') - self.assertTrue(test_command[idx2 + 2] == 'cookie_value1') - - idx3 = test_command.index('--cookies', idx2 + 2) - self.assertTrue(test_command[idx3 + 1] == 'test_cookie2') - self.assertTrue(test_command[idx3 + 2] == 'cookie_value2') - - def test_custom_config(self): - conf = imgkit.config() - self.assertEqual('imgkit-', conf.meta_tag_prefix) - conf = imgkit.config(meta_tag_prefix='prefix-') - self.assertEqual('prefix-', conf.meta_tag_prefix) - with self.assertRaises(IOError): - imgkit.config(wkhtmltoimage='wrongpath') - - -class TestIMGKitCommandGeneration(unittest.TestCase): - - def test_command_construction(self): - r = imgkit.IMGKit('html', 'string', options={'format': 'jpg', 'toc-l1-font-size': 12}) - command = r.command() - self.assertEqual(command[0], r.wkhtmltoimage) - self.assertEqual(command[command.index('--format') + 1], 'jpg') - self.assertEqual(command[command.index('--toc-l1-font-size') + 1], '12') - - def test_lists_of_input_args(self): - urls = ['http://ya.ru', 'http://google.com'] - paths = ['fixtures/example.html', 'fixtures/example.html'] - r = imgkit.IMGKit(urls, 'url') - r2 = imgkit.IMGKit(paths, 'file') - cmd = r.command() - cmd2 = r2.command() - self.assertEqual(cmd[-3:], ['http://ya.ru', 'http://google.com', '-']) - self.assertEqual(cmd2[-3:], ['fixtures/example.html', 'fixtures/example.html', '-']) - - def test_read_source_from_stdin(self): - r = imgkit.IMGKit('html', 'string') - self.assertEqual(r.command()[-2:], ['-', '-']) - - def test_url_in_command(self): - r = imgkit.IMGKit('http://ya.ru', 'url') - self.assertEqual(r.command()[-2:], ['http://ya.ru', '-']) - - def test_file_path_in_command(self): - path = 'fixtures/example.html' - r = imgkit.IMGKit(path, 'file') - self.assertEqual(r.command()[-2:], [path, '-']) - - def test_output_path(self): - out = '/test/test2/out.jpg' - r = imgkit.IMGKit('html', 'string') - self.assertEqual(r.command(out)[-1:], ['/test/test2/out.jpg']) - - def test_imgkit_meta_tags(self): - body = """ - - - - - - """ - - r = imgkit.IMGKit(body, 'string') - command = r.command() - self.assertEqual(command[command.index('--format') + 1], 'jpg') - self.assertEqual(command[command.index('--orientation') + 1], 'Landscape') - - def test_imgkit_meta_tags_in_bad_markup(self): - body = """ - - - - - -
- - """ - - r = imgkit.IMGKit(body, 'string') - command = r.command() - self.assertEqual(command[command.index('--format') + 1], 'jpg') - self.assertEqual(command[command.index('--orientation') + 1], 'Landscape') - - def test_skip_nonimgkit_tags(self): - body = """ - - - - - -
- - """ - - r = imgkit.IMGKit(body, 'string') - command = r.command() - self.assertEqual(command[command.index('--orientation') + 1], 'Landscape') - - def test_toc_handling_without_options(self): - r = imgkit.IMGKit('hmtl', 'string', toc={'xsl-style-sheet': 'test.xsl'}) - self.assertEqual(r.command()[1], 'toc') - self.assertEqual(r.command()[2], '--xsl-style-sheet') - - def test_toc_with_options(self): - options = { - 'format': 'jpg', - 'margin-top': '0.75in', - 'margin-right': '0.75in', - 'margin-bottom': '0.75in', - 'margin-left': '0.75in', - 'encoding': "UTF-8" - } - r = imgkit.IMGKit('html', 'string', options=options, toc={'xsl-style-sheet': 'test.xsl'}) - - command = r.command() - - self.assertEqual(command[1 + len(options) * 2], 'toc') - self.assertEqual(command[1 + len(options) * 2 + 1], '--xsl-style-sheet') - - def test_cover_without_options(self): - r = imgkit.IMGKit('html', 'string', cover='test.html') - - command = r.command() - - self.assertEqual(command[1], 'cover') - self.assertEqual(command[2], 'test.html') - - def test_cover_with_options(self): - options = { - 'format': 'jpg', - 'margin-top': '0.75in', - 'margin-right': '0.75in', - 'margin-bottom': '0.75in', - 'margin-left': '0.75in', - 'encoding': "UTF-8" - } - r = imgkit.IMGKit('html', 'string', options=options, cover='test.html') - - command = r.command() - - self.assertEqual(command[1 + len(options) * 2], 'cover') - self.assertEqual(command[1 + len(options) * 2 + 1], 'test.html') - - def test_cover_and_toc(self): - options = { - 'format': 'jpg', - 'margin-top': '0.75in', - 'margin-right': '0.75in', - 'margin-bottom': '0.75in', - 'margin-left': '0.75in', - 'encoding': "UTF-8" - } - r = imgkit.IMGKit('html', 'string', options=options, toc={'xsl-style-sheet': 'test.xsl'}, cover='test.html') - command = r.command() - self.assertEqual(command[-7:], ['toc', '--xsl-style-sheet', 'test.xsl', 'cover', 'test.html', '-', '-']) - - def test_cover_and_toc_cover_first(self): - options = { - 'format': 'jpg', - 'margin-top': '0.75in', - 'margin-right': '0.75in', - 'margin-bottom': '0.75in', - 'margin-left': '0.75in', - 'encoding': "UTF-8" - } - r = imgkit.IMGKit('html', 'string', options=options, toc={'xsl-style-sheet': 'test.xsl'}, cover='test.html', - cover_first=True) - command = r.command() - self.assertEqual(command[-7:], ['cover', 'test.html', 'toc', '--xsl-style-sheet', 'test.xsl', '-', '-']) - - def test_outline_options(self): - options = { - 'outline': None, - 'outline-depth': 1 - } - - r = imgkit.IMGKit('ya.ru', 'url', options=options) - cmd = r.command() - # self.assertEqual(cmd[1:], ['--outline', '--outline-depth', '1', 'ya.ru', '-']) - self.assertIn('--outline', cmd) - self.assertEqual(cmd[cmd.index('--outline-depth') + 1], '1') - - def test_filter_empty_and_none_values_in_opts(self): - options = { - 'outline': '', - 'footer-line': None, - 'quiet': False - } - - r = imgkit.IMGKit('html', 'string', options=options) - cmd = r.command() - self.assertEqual(len(cmd), 6) - - -class TestIMGKitGeneration(unittest.TestCase): - - def setUp(self): - pass - - def tearDown(self): - if os.path.exists('out.jpg'): - os.remove('out.jpg') - - def test_img_generation(self): - r = imgkit.IMGKit('html', 'string', options={'format': 'jpg'}) - pic = r.to_img('out.jpg') - self.assertTrue(pic) - - def test_img_generation_xvfb(self): - r = imgkit.IMGKit('html', 'string', options={'format': 'jpg', 'xvfb': ''}) - pic = r.to_img('out.jpg') - self.assertTrue(pic) - - def test_raise_error_with_invalid_url(self): - r = imgkit.IMGKit('wrongurl', 'url') - with self.assertRaises(IOError): - r.to_img('out.jpg') - - def test_raise_error_with_invalid_file_path(self): - paths = ['frongpath.html', 'wrongpath2.html'] - with self.assertRaises(IOError): - imgkit.IMGKit('wrongpath.html', 'file') - with self.assertRaises(IOError): - imgkit.IMGKit(paths, 'file') - - def test_stylesheet_adding_to_the_head(self): - r = imgkit.IMGKit('Hai!', 'string', - css='fixtures/example.css') - - with open('fixtures/example.css') as f: - css = f.read() - - r._prepend_css('fixtures/example.css') - self.assertIn(''.format(css), r.source.to_s()) - - def test_stylesheet_adding_without_head_tag(self): - r = imgkit.IMGKit('Hai!', 'string', - options={'quiet': None}, css='fixtures/example.css') - - with open('fixtures/example.css') as f: - css = f.read() - - r._prepend_css('fixtures/example.css') - self.assertIn(''.format(css), r.source.to_s()) - - def test_multiple_stylesheets_adding_to_the_head(self): - css_files = ['fixtures/example.css', 'fixtures/example2.css'] - r = imgkit.IMGKit('Hai!', 'string', - css=css_files) - - css = [] - for css_file in css_files: - with open(css_file) as f: - css.append(f.read()) - - r._prepend_css(css_files) - self.assertIn(''.format("\n".join(css)), r.source.to_s()) - - def test_multiple_stylesheet_adding_without_head_tag(self): - css_files = ['fixtures/example.css', 'fixtures/example2.css'] - r = imgkit.IMGKit('Hai!', 'string', - options={'quiet': None}, css=css_files) - - css = [] - for css_file in css_files: - with open(css_file) as f: - css.append(f.read()) - - r._prepend_css(css_files) - self.assertIn(''.format("\n".join(css)), r.source.to_s()) - - def test_stylesheet_throw_error_when_url(self): - r = imgkit.IMGKit('http://ya.ru', 'url', css='fixtures/example.css') - - with self.assertRaises(r.SourceError): - r.to_img() - - def test_stylesheet_adding_to_file_with_option(self): - css = 'fixtures/example.css' - r = imgkit.IMGKit('fixtures/example.html', 'file', css=css) - self.assertEqual(r.css, css) - r._prepend_css(css) - self.assertIn('font-size', r.source.to_s()) - - def test_wkhtmltoimage_error_handling(self): - r = imgkit.IMGKit('clearlywrongurl.asdf', 'url') - with self.assertRaises(IOError): - r.to_img() - - def test_image_generation_from_file(self): - with open('fixtures/example.html', 'r') as f: - r = imgkit.IMGKit(f, 'file') - output = r.to_img() - self.assertEqual(output[:4], b'\xff\xd8\xff\xe0') - - def test_raise_error_with_wrong_css_path(self): - css = 'fixtures/wrongpath.css' - r = imgkit.IMGKit('fixtures/example.html', 'file', css=css) - with self.assertRaises(IOError): - r.to_img() - - def test_raise_error_if_bad_wkhtmltoimage_option(self): - r = imgkit.IMGKit('Hai!', 'string', - options={'bad-option': None}) - with self.assertRaises(IOError) as cm: - r.to_img() - - raised_exception = cm.exception - self.assertRegexpMatches(str(raised_exception), - '^wkhtmltoimage exited with non-zero code 1. error:\nUnknown long argument ' - '--bad-option\r?\n') - - -class TestIMGKitAPI(unittest.TestCase): - - def test_from_string(self): - pic = imgkit.from_string('hello imgkit!', 'out.jpg') - self.assertTrue(pic) - - def test_from_url(self): - pic = imgkit.from_url('https://github.com', 'out.jpg') - self.assertTrue(pic) - - def test_from_file(self): - pic = imgkit.from_file('fixtures/example.html', 'out.jpg') - self.assertTrue(pic) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/example.css b/tests/fixtures/example.css similarity index 100% rename from test/fixtures/example.css rename to tests/fixtures/example.css diff --git a/test/fixtures/example.html b/tests/fixtures/example.html similarity index 100% rename from test/fixtures/example.html rename to tests/fixtures/example.html diff --git a/test/fixtures/example2.css b/tests/fixtures/example2.css similarity index 100% rename from test/fixtures/example2.css rename to tests/fixtures/example2.css diff --git a/tests/imgkit_test.py b/tests/imgkit_test.py new file mode 100755 index 0000000..5c6d3d1 --- /dev/null +++ b/tests/imgkit_test.py @@ -0,0 +1,446 @@ +# -*- coding: utf-8 -*- +import codecs +import io +import os +import sys +import unittest + +# Prepend ../ to PYTHONPATH so that we can import IMGKIT form there. +TEST_ROOT = os.path.abspath(os.path.dirname(__file__)) +sys.path.insert(0, os.path.realpath(os.path.join(TEST_ROOT, "../src"))) + +import imgkit + + +class TestIMGKitInitialization(unittest.TestCase): + def test_html_source(self): + r = imgkit.IMGKit("

Oh hai

", "string") + self.assertTrue(r.source.isString()) + + def test_url_source(self): + r = imgkit.IMGKit("http://ya.ru", "url") + self.assertTrue(r.source.isUrl()) + + def test_file_source(self): + r = imgkit.IMGKit("fixtures/example.html", "file") + self.assertTrue(r.source.isFile()) + + def test_file_object_source(self): + with open("fixtures/example.html") as fl: + r = imgkit.IMGKit(fl, "file") + self.assertTrue(r.source.isFileObj()) + + def test_file_source_with_path(self): + r = imgkit.IMGKit("test", "string") + with io.open("fixtures/example.css") as f: + self.assertTrue(r.source.isFile(path=f)) + with codecs.open("fixtures/example.css", encoding="UTF-8") as f: + self.assertTrue(r.source.isFile(path=f)) + + def test_options_parsing(self): + r = imgkit.IMGKit("html", "string", options={"format": "jpg"}) + test_command = r.command("test") + idx = test_command.index("--format") # Raise exception in case of not found + self.assertTrue(test_command[idx + 1] == "jpg") + + def test_options_parsing_with_dashes(self): + r = imgkit.IMGKit("html", "string", options={"--format": "jpg"}) + + test_command = r.command("test") + idx = test_command.index("--format") # Raise exception in case of not found + self.assertTrue(test_command[idx + 1] == "jpg") + + def test_options_parsing_with_tuple(self): + options = {"--custom-header": [("Accept-Encoding", "gzip")]} + r = imgkit.IMGKit("html", "string", options=options) + command = r.command() + idx1 = command.index("--custom-header") # Raise exception in case of not found + self.assertTrue(command[idx1 + 1] == "Accept-Encoding") + self.assertTrue(command[idx1 + 2] == "gzip") + + def test_options_parsing_with_tuple_no_dashes(self): + options = {"custom-header": [("Accept-Encoding", "gzip")]} + r = imgkit.IMGKit("html", "string", options=options) + command = r.command() + idx1 = command.index("--custom-header") # Raise exception in case of not found + self.assertTrue(command[idx1 + 1] == "Accept-Encoding") + self.assertTrue(command[idx1 + 2] == "gzip") + + def test_repeatable_options(self): + roptions = { + "--format": "jpg", + "cookies": [ + ("test_cookie1", "cookie_value1"), + ("test_cookie2", "cookie_value2"), + ], + } + + r = imgkit.IMGKit("html", "string", options=roptions) + + test_command = r.command("test") + + idx1 = test_command.index("--format") # Raise exception in case of not found + self.assertTrue(test_command[idx1 + 1] == "jpg") + + self.assertTrue(test_command.count("--cookies") == 2) + + idx2 = test_command.index("--cookies") + self.assertTrue(test_command[idx2 + 1] == "test_cookie1") + self.assertTrue(test_command[idx2 + 2] == "cookie_value1") + + idx3 = test_command.index("--cookies", idx2 + 2) + self.assertTrue(test_command[idx3 + 1] == "test_cookie2") + self.assertTrue(test_command[idx3 + 2] == "cookie_value2") + + def test_custom_config(self): + conf = imgkit.config() + self.assertEqual("imgkit-", conf.meta_tag_prefix) + conf = imgkit.config(meta_tag_prefix="prefix-") + self.assertEqual("prefix-", conf.meta_tag_prefix) + with self.assertRaises(IOError): + imgkit.config(wkhtmltoimage="wrongpath") + + +class TestIMGKitCommandGeneration(unittest.TestCase): + def test_command_construction(self): + r = imgkit.IMGKit( + "html", "string", options={"format": "jpg", "toc-l1-font-size": 12} + ) + command = r.command() + self.assertEqual(command[0], r.wkhtmltoimage) + self.assertEqual(command[command.index("--format") + 1], "jpg") + self.assertEqual(command[command.index("--toc-l1-font-size") + 1], "12") + + def test_lists_of_input_args(self): + urls = ["http://ya.ru", "http://google.com"] + paths = ["fixtures/example.html", "fixtures/example.html"] + r = imgkit.IMGKit(urls, "url") + r2 = imgkit.IMGKit(paths, "file") + cmd = r.command() + cmd2 = r2.command() + self.assertEqual(cmd[-3:], ["http://ya.ru", "http://google.com", "-"]) + self.assertEqual( + cmd2[-3:], ["fixtures/example.html", "fixtures/example.html", "-"] + ) + + def test_read_source_from_stdin(self): + r = imgkit.IMGKit("html", "string") + self.assertEqual(r.command()[-2:], ["-", "-"]) + + def test_url_in_command(self): + r = imgkit.IMGKit("http://ya.ru", "url") + self.assertEqual(r.command()[-2:], ["http://ya.ru", "-"]) + + def test_file_path_in_command(self): + path = "fixtures/example.html" + r = imgkit.IMGKit(path, "file") + self.assertEqual(r.command()[-2:], [path, "-"]) + + def test_output_path(self): + out = "/test/test2/out.jpg" + r = imgkit.IMGKit("html", "string") + self.assertEqual(r.command(out)[-1:], ["/test/test2/out.jpg"]) + + def test_imgkit_meta_tags(self): + body = """ + + + + + + """ + + r = imgkit.IMGKit(body, "string") + command = r.command() + self.assertEqual(command[command.index("--format") + 1], "jpg") + self.assertEqual(command[command.index("--orientation") + 1], "Landscape") + + def test_imgkit_meta_tags_in_bad_markup(self): + body = """ + + + + + +
+ + """ + + r = imgkit.IMGKit(body, "string") + command = r.command() + self.assertEqual(command[command.index("--format") + 1], "jpg") + self.assertEqual(command[command.index("--orientation") + 1], "Landscape") + + def test_skip_nonimgkit_tags(self): + body = """ + + + + + +
+ + """ + + r = imgkit.IMGKit(body, "string") + command = r.command() + self.assertEqual(command[command.index("--orientation") + 1], "Landscape") + + def test_toc_handling_without_options(self): + r = imgkit.IMGKit("hmtl", "string", toc={"xsl-style-sheet": "test.xsl"}) + self.assertEqual(r.command()[1], "toc") + self.assertEqual(r.command()[2], "--xsl-style-sheet") + + def test_toc_with_options(self): + options = { + "format": "jpg", + "margin-top": "0.75in", + "margin-right": "0.75in", + "margin-bottom": "0.75in", + "margin-left": "0.75in", + "encoding": "UTF-8", + } + r = imgkit.IMGKit( + "html", "string", options=options, toc={"xsl-style-sheet": "test.xsl"} + ) + + command = r.command() + + self.assertEqual(command[1 + len(options) * 2], "toc") + self.assertEqual(command[1 + len(options) * 2 + 1], "--xsl-style-sheet") + + def test_cover_without_options(self): + r = imgkit.IMGKit("html", "string", cover="test.html") + + command = r.command() + + self.assertEqual(command[1], "cover") + self.assertEqual(command[2], "test.html") + + def test_cover_with_options(self): + options = { + "format": "jpg", + "margin-top": "0.75in", + "margin-right": "0.75in", + "margin-bottom": "0.75in", + "margin-left": "0.75in", + "encoding": "UTF-8", + } + r = imgkit.IMGKit("html", "string", options=options, cover="test.html") + + command = r.command() + + self.assertEqual(command[1 + len(options) * 2], "cover") + self.assertEqual(command[1 + len(options) * 2 + 1], "test.html") + + def test_cover_and_toc(self): + options = { + "format": "jpg", + "margin-top": "0.75in", + "margin-right": "0.75in", + "margin-bottom": "0.75in", + "margin-left": "0.75in", + "encoding": "UTF-8", + } + r = imgkit.IMGKit( + "html", + "string", + options=options, + toc={"xsl-style-sheet": "test.xsl"}, + cover="test.html", + ) + command = r.command() + self.assertEqual( + command[-7:], + ["toc", "--xsl-style-sheet", "test.xsl", "cover", "test.html", "-", "-"], + ) + + def test_cover_and_toc_cover_first(self): + options = { + "format": "jpg", + "margin-top": "0.75in", + "margin-right": "0.75in", + "margin-bottom": "0.75in", + "margin-left": "0.75in", + "encoding": "UTF-8", + } + r = imgkit.IMGKit( + "html", + "string", + options=options, + toc={"xsl-style-sheet": "test.xsl"}, + cover="test.html", + cover_first=True, + ) + command = r.command() + self.assertEqual( + command[-7:], + ["cover", "test.html", "toc", "--xsl-style-sheet", "test.xsl", "-", "-"], + ) + + def test_outline_options(self): + options = {"outline": None, "outline-depth": 1} + + r = imgkit.IMGKit("ya.ru", "url", options=options) + cmd = r.command() + # self.assertEqual(cmd[1:], ['--outline', '--outline-depth', '1', 'ya.ru', '-']) + self.assertIn("--outline", cmd) + self.assertEqual(cmd[cmd.index("--outline-depth") + 1], "1") + + def test_filter_empty_and_none_values_in_opts(self): + options = {"outline": "", "footer-line": None, "quiet": False} + + r = imgkit.IMGKit("html", "string", options=options) + cmd = r.command() + self.assertEqual(len(cmd), 6) + + +class TestIMGKitGeneration(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + if os.path.exists("out.jpg"): + os.remove("out.jpg") + + def test_img_generation(self): + r = imgkit.IMGKit("html", "string", options={"format": "jpg"}) + pic = r.to_img("out.jpg") + self.assertTrue(pic) + + def test_img_generation_xvfb(self): + r = imgkit.IMGKit("html", "string", options={"format": "jpg", "xvfb": ""}) + pic = r.to_img("out.jpg") + self.assertTrue(pic) + + def test_raise_error_with_invalid_url(self): + r = imgkit.IMGKit("wrongurl", "url") + with self.assertRaises(IOError): + r.to_img("out.jpg") + + def test_raise_error_with_invalid_file_path(self): + paths = ["frongpath.html", "wrongpath2.html"] + with self.assertRaises(IOError): + imgkit.IMGKit("wrongpath.html", "file") + with self.assertRaises(IOError): + imgkit.IMGKit(paths, "file") + + def test_stylesheet_adding_to_the_head(self): + r = imgkit.IMGKit( + "Hai!", + "string", + css="fixtures/example.css", + ) + + with open("fixtures/example.css") as f: + css = f.read() + + r._prepend_css("fixtures/example.css") + self.assertIn("".format(css), r.source.to_s()) + + def test_stylesheet_adding_without_head_tag(self): + r = imgkit.IMGKit( + "Hai!", + "string", + options={"quiet": None}, + css="fixtures/example.css", + ) + + with open("fixtures/example.css") as f: + css = f.read() + + r._prepend_css("fixtures/example.css") + self.assertIn("".format(css), r.source.to_s()) + + def test_multiple_stylesheets_adding_to_the_head(self): + css_files = ["fixtures/example.css", "fixtures/example2.css"] + r = imgkit.IMGKit( + "Hai!", "string", css=css_files + ) + + css = [] + for css_file in css_files: + with open(css_file) as f: + css.append(f.read()) + + r._prepend_css(css_files) + self.assertIn("".format("\n".join(css)), r.source.to_s()) + + def test_multiple_stylesheet_adding_without_head_tag(self): + css_files = ["fixtures/example.css", "fixtures/example2.css"] + r = imgkit.IMGKit( + "Hai!", + "string", + options={"quiet": None}, + css=css_files, + ) + + css = [] + for css_file in css_files: + with open(css_file) as f: + css.append(f.read()) + + r._prepend_css(css_files) + self.assertIn("".format("\n".join(css)), r.source.to_s()) + + def test_stylesheet_throw_error_when_url(self): + r = imgkit.IMGKit("http://ya.ru", "url", css="fixtures/example.css") + + with self.assertRaises(r.SourceError): + r.to_img() + + def test_stylesheet_adding_to_file_with_option(self): + css = "fixtures/example.css" + r = imgkit.IMGKit("fixtures/example.html", "file", css=css) + self.assertEqual(r.css, css) + r._prepend_css(css) + self.assertIn("font-size", r.source.to_s()) + + def test_wkhtmltoimage_error_handling(self): + r = imgkit.IMGKit("clearlywrongurl.asdf", "url") + with self.assertRaises(IOError): + r.to_img() + + def test_image_generation_from_file(self): + with open("fixtures/example.html", "r") as f: + r = imgkit.IMGKit(f, "file") + output = r.to_img() + self.assertEqual(output[:4], b"\xff\xd8\xff\xe0") + + def test_raise_error_with_wrong_css_path(self): + css = "fixtures/wrongpath.css" + r = imgkit.IMGKit("fixtures/example.html", "file", css=css) + with self.assertRaises(IOError): + r.to_img() + + def test_raise_error_if_bad_wkhtmltoimage_option(self): + r = imgkit.IMGKit( + "Hai!", "string", options={"bad-option": None} + ) + with self.assertRaises(IOError) as cm: + r.to_img() + + raised_exception = cm.exception + self.assertRegexpMatches( + str(raised_exception), + "^wkhtmltoimage exited with non-zero code 1. error:\nUnknown long argument " + "--bad-option\r?\n", + ) + + +class TestIMGKitAPI(unittest.TestCase): + def test_from_string(self): + pic = imgkit.from_string("hello imgkit!", "out.jpg") + self.assertTrue(pic) + + def test_from_url(self): + pic = imgkit.from_url("https://github.com", "out.jpg") + self.assertTrue(pic) + + def test_from_file(self): + pic = imgkit.from_file("fixtures/example.html", "out.jpg") + self.assertTrue(pic) + + +if __name__ == "__main__": + unittest.main() diff --git a/travis/init.sh b/travis/init.sh deleted file mode 100644 index bf8faba..0000000 --- a/travis/init.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -sudo apt update -sudo apt install -y xvfb -wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.bionic_amd64.deb -sudo apt install -y ./wkhtmltox_0.12.6-1.bionic_amd64.deb -pip install coverage diff --git a/travis/pandoc.sh b/travis/pandoc.sh deleted file mode 100644 index 4652d46..0000000 --- a/travis/pandoc.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -sudo wget https://github.com/jgm/pandoc/releases/download/1.19.2.1/pandoc-1.19.2.1-1-amd64.deb -sudo dpkg -i pandoc-1.19.2.1-1-amd64.deb -pip install pypandoc \ No newline at end of file