def test_inline_css_dev(tmpdir): mktree( tmpdir, { 'pages': { 'foo.html': '{{inline_css("theme/main.css")}}', 'bar.html': "{{ url('theme/main.css') }}", }, 'theme': { 'sass/main.scss': 'body {width: 10px + 10px;}', }, }) build(tmpdir, mode=Mode.development) assert gettree(tmpdir.join('dist')) == { 'foo': { 'index.html': ('body {\n' ' width: 20px; }\n' '\n' '/*# sourceMappingURL=/theme/main.css.map */\n'), }, 'bar': { 'index.html': RegexStr(r'/theme/main.css\?t=\d+\n'), }, 'theme': { 'main.css.map': RegexStr('{.*'), 'main.css': ('body {\n' ' width: 20px; }\n' '\n' '/*# sourceMappingURL=main.css.map */'), '.src': { 'main.scss': 'body {width: 10px + 10px;}', }, }, }
def test_ignore_no_template(tmpdir): mktree( tmpdir, { 'pages': { 'ignore_this.md': 'this file is ignored', 'normal.md': 'hello this is normal', 'no_template.md': 'this should be passed through as-is', 'normal_but_no_output.md': ('---\n' 'output: false\n' '---\n' 'hello this is normal\n') }, 'theme': { 'templates/foobar.jinja': 'rendered {{ content }}', }, 'harrier.yml': ('default_template: "foobar.jinja"\n' 'ignore:\n' '- "**/ignore*"\n' 'defaults:\n' ' "/no_temp*":\n' ' pass_through: true\n') }) build(tmpdir, mode=Mode.production) assert gettree(tmpdir.join('dist')) == { 'no_template': { 'index.html': 'this should be passed through as-is', }, 'normal': { 'index.html': 'rendered <p>hello this is normal</p>\n', }, }
def test_jinja_md_extensions(input, output, tmpdir): mktree(tmpdir, { 'pages/index.html': input, }) build(tmpdir, mode=Mode.production) # debug(tmpdir.join('dist/index.html').read_text('utf8')) assert tmpdir.join('dist/index.html').read_text('utf8') == output
def test_full_build(tmpdir): mktree( tmpdir, { 'pages': { 'foobar.html': ('{{ url("foobar.png") }}\n' '{{ resolve_url("theme/main.css") }}\n' '{{ resolve_url("another") }}\n'), 'another.md': '# Hello', 'third.yaml': 'content: "hello there"', }, 'theme': { 'sass/main.scss': 'body {width: 10px + 10px;}', 'assets/foobar.png': '*', }, }) build(tmpdir, mode=Mode.production) assert gettree(tmpdir.join('dist')) == { 'foobar': { 'index.html': ('/foobar.3389dae.png\n' '/theme/main.a1ac3a7.css\n' '/another/\n'), }, 'another': { 'index.html': '<h1 id="1-hello">Hello</h1>\n' }, 'theme': { 'main.a1ac3a7.css': 'body{width:20px}\n', }, 'third': { 'index.html': 'hello there\n' }, 'foobar.3389dae.png': '*', }
def test_markdown_extensions(input, output, tmpdir): mktree(tmpdir, { 'pages': { 'foobar.md': input, }, }) build(tmpdir, mode=Mode.production) assert tmpdir.join('dist/foobar/index.html').read_text('utf8') == output
def test_extensions_ok(tmpdir): mktree( tmpdir, { 'pages': { 'foo.md': '# foo', 'bar.html': '{{ 4|add_one }} {{ dynamic }}', 'spam.html': 'before', 'splat.html': '{{ 3 is two }} {{ 2 is two }}', }, 'theme/templates/main.jinja': '{{ content }}', 'extensions.py': """ from harrier.extensions import modify, template @modify.pages('/foo.*') def modify_foo(page, config): page['content'] += ' changed by extension' return page @modify.som def add_var(site): site['dynamic'] = 42 return site @modify.som def change_pages(site): site['pages']['/spam.html']['content'] = 'after' return site @template.filter def add_one(v): return v + 1 @template.test def two(v): return v == 2 """ }) build(str(tmpdir)) assert gettree(tmpdir.join('dist')) == { 'foo': { 'index.html': '<h1 id="1-foo-changed-by-extension">foo changed by extension</h1>\n', }, 'bar': { 'index.html': '5 42\n', }, 'spam': { 'index.html': 'after\n', }, 'splat': { 'index.html': 'False True\n', }, }
def test_build_no_templates(tmpdir): mktree(tmpdir, { 'pages': { 'foobar.md': ('### Whatever'), }, }) build(tmpdir, mode=Mode.production) assert gettree(tmpdir.join('dist')) == { 'foobar': { 'index.html': ('<h3 id="3-whatever">Whatever</h3>\n'), }, }
def test_bad_python(tmpdir): mktree( tmpdir, { 'pages': { 'foo.md': '# foo', }, 'theme/templates/main.jinja': '{{ content }}', 'extensions.py': 'xxx' }) with pytest.raises(ExtensionError): build(str(tmpdir))
def test_xml_no_front_matter(tmpdir): mktree( tmpdir, { 'pages': { 'foobar.xml': ('<x><y>{{ config.whatever }}</y></x>'), }, 'harrier.yml': 'whatever: 123' }) build(tmpdir, mode=Mode.production) assert gettree(tmpdir.join('dist')) == { 'foobar.xml': '<x><y>{{ config.whatever }}</y></x>' }
def test_pages_function(infile, outfile, tmpdir): mktree( tmpdir, { 'pages': { 'index.html': '1', 'foobar.md': '2', 'robots.txt': '3', 'testing.html': infile, } }) build(tmpdir, mode=Mode.production) # debug(tmpdir.join('dist/testing/index.html').read_text('utf8')) assert tmpdir.join('dist/testing/index.html').read_text('utf8') == outfile
def test_render_error(tmpdir, caplog): mktree( tmpdir, { 'pages': { 'foobar.html': '{{ 1/0 }}', }, 'theme': { 'templates/main.jinja': '{{ content }}', }, }) with pytest.raises(HarrierProblem): build(tmpdir, mode=Mode.production) assert 'ZeroDivisionError: division by zero' in caplog.text
def test_jinja_functions(input, output, tmpdir): mktree( tmpdir, { 'pages/index.html': input, 'theme/assets': {}, 'harrier.yml': 'paginate_by: 3', }) img = Image.new('RGB', (60, 30), (255, 255, 255)) img.save(str(tmpdir.join('theme/assets/image.png')), 'PNG') build(tmpdir, mode=Mode.production) # debug(tmpdir.join('dist/index.html').read_text('utf8')) assert tmpdir.join('dist/index.html').read_text('utf8') == output.replace( '{tmpdir}', str(tmpdir))
def test_render_code_lang(tmpdir): mktree(tmpdir, { 'pages': { 'foobar.md': ('testing\n\n' '```py\n' 'x = 4\n' '```\n'), }, }) build(tmpdir, mode=Mode.production) assert tmpdir.join('dist/foobar/index.html').read_text('utf8') == ( '<p>testing</p>\n' '<div class="hi"><pre><span></span><span class="n">x</span> ' '<span class="o">=</span> <span class="mi">4</span>\n' '</pre></div>\n')
def test_jinja_format(tmpdir): mktree( tmpdir, { 'pages': { 'index.html': '{{"{:0.2f}"|format(3.1415)}}', '2032-06-02-date.txt': '---\nx: 1\n---\n{{ "{:%b %d, %Y}"|format(page.created) }}', }, }) build(tmpdir, mode=Mode.production) assert gettree(tmpdir.join('dist')) == { 'index.html': '3.14\n', 'date.txt': 'Jun 02, 2032\n' }
def test_no_trailing_slash(tmpdir): mktree( tmpdir, { 'pages': { 'index.html': '<a href="{{ url("other") }}">link to other</a>', 'other.html': 'xxx' }, 'harrier.yml': 'apply_trailing_slash: false' }) build(tmpdir, mode=Mode.production) assert gettree(tmpdir.join('dist')) == { 'other': { 'index.html': 'xxx\n', }, 'index.html': '<a href="/other">link to other</a>\n', }
def test_uri_key_error(tmpdir, caplog): mktree( tmpdir, { 'pages': { 'foobar.html': ('---\n' 'uri: "{foo}/whatever"\n' '---\n' 'hello'), }, 'theme': { 'templates/main.jinja': '{{ content }}', }, }) with pytest.raises(KeyError): build(tmpdir, mode=Mode.production) assert 'missing format variable "foo" for "{foo}/whatever"' in caplog.text
def test_generate_pages(tmpdir): mktree( tmpdir, { 'pages': { 'index.html': 'hello', }, 'extensions.py': """ from pathlib import Path from harrier.extensions import modify THIS_DIR = Path(__file__).parent.resolve() @modify.generate_pages def add_extra_pages(som): config: Config = som['config'] yield { 'path': Path('extra/index.md'), 'content': '# this is a test\\n\\nwith of generating pages dynamically', } yield { 'path': Path('more/index.html'), 'content': 'testing {{ page.x }}', 'data': { 'uri': '/foo-bar-whatever', 'x': 123, } } (THIS_DIR / 'pages' / 'binary_file').write_bytes(b'xxx') yield { 'path': 'binary_file', 'content': None, } """ }) build(str(tmpdir)) assert gettree(tmpdir.join('dist')) == { 'extra': { 'index.html': ('<h1 id="1-this-is-a-test">this is a test</h1>\n' '\n' '<p>with of generating pages dynamically</p>\n'), }, 'foo-bar-whatever': { 'index.html': 'testing 123\n' }, 'index.html': 'hello\n', 'binary_file': 'xxx', }
def test_generate_pages_error(tmpdir): mktree( tmpdir, { 'pages': { 'index.html': 'hello', }, 'extensions.py': """ from pathlib import Path from harrier.extensions import modify @modify.generate_pages def add_extra_pages(som): raise RuntimeError('xx') """ }) with pytest.raises(ExtensionError): build(str(tmpdir))
def test_yaml_render(tmpdir): mktree( tmpdir, { 'pages': { 'test.yml': ('template: foobar.jinja\n' 'foo:\n' ' bar: 42\n'), }, 'theme': { 'templates/foobar.jinja': 'foo bar: {{ page.foo.bar }}', }, }) build(tmpdir, mode=Mode.production) assert gettree(tmpdir.join('dist')) == { 'test': { 'index.html': 'foo bar: 42\n' } }
def test_pages_error(tmpdir): mktree( tmpdir, { 'pages': { 'foo.md': '# foo', }, 'extensions.py': """ from harrier.extensions import modify @modify.pages('**/*') def modify_pages(data, config): raise ValueError('xxx') """ }) with pytest.raises(ExtensionError) as exc_info: build(str(tmpdir), steps={BuildSteps.pages}) assert exc_info.value.args[0] == 'xxx'
def test_inline_css_prod(tmpdir): mktree( tmpdir, { 'pages': { 'foobar.html': '{{inline_css("theme/main.css")}}' }, 'theme': { 'sass/main.scss': 'body {width: 10px + 10px;}', }, }) build(tmpdir, mode=Mode.production) assert gettree(tmpdir.join('dist')) == { 'foobar': { 'index.html': ('body{width:20px}\n'), }, 'theme': { 'main.a1ac3a7.css': 'body{width:20px}\n', }, }
def test_frontmatter_maybe(tmpdir): mktree( tmpdir, { 'pages': { 'foobar.xml': ('---\n' 'foo: bar\n' '---\n' '<x><y>{{ config.whatever }}</y></x>'), 'foobar.txt': ('---\n' 'x: 1\n' '---\n' '{{ page.x }}'), }, 'harrier.yml': 'whatever: 123' }) build(tmpdir, mode=Mode.production) assert gettree(tmpdir.join('dist')) == { 'foobar.xml': '<x><y>123</y></x>\n', 'foobar.txt': '1\n', }
def test_copy_extensions_error(tmpdir): mktree( tmpdir, { 'pages': { 'index.html': 'hello', }, 'theme/assets': { 'foo/bar.svg': 'b', }, 'extensions.py': """ from harrier.extensions import modify, template @modify.copy('/foo/*') def modify_foo(in_path, out_path, config): raise RuntimeError('x') """ }) with pytest.raises(ExtensionError): build(str(tmpdir))
def test_ext_error(tmpdir): mktree( tmpdir, { 'pages': { 'foo.md': '# foo', }, 'theme/templates/main.jinja': '{{ content }}', 'extensions.py': """ from harrier.extensions import modify @modify.config def before(site): raise RuntimeError('xxx') """ }) with pytest.raises(ExtensionError) as exc_info: build(str(tmpdir)) assert exc_info.value.args[0] == 'xxx'
def test_broken_ext(tmpdir): mktree( tmpdir, { 'pages': { 'foo.md': '# foo', }, 'theme/templates/main.jinja': '{{ content }}', 'extensions.py': """ from harrier.extensions import modify @modify.config def before(site): pass """ }) with pytest.raises(HarrierProblem) as exc_info: build(str(tmpdir)) assert exc_info.value.args[ 0] == 'extension "before" did not return a Config object as expected'
def test_generate_pages_invalid(tmpdir): mktree( tmpdir, { 'pages': { 'index.html': 'hello', }, 'extensions.py': """ from pathlib import Path from harrier.extensions import modify @modify.generate_pages def add_extra_pages(som): config: Config = som['config'] yield { 'path': Path('extra/index.md'), 'content': [1, 2, 3], } """ }) with pytest.raises(ExtensionError): build(str(tmpdir))
def test_pages_broken(tmpdir): mktree( tmpdir, { 'pages': { 'foo.md': '# foo', }, 'theme/templates/main.jinja': '{{ content }}', 'extensions.py': """ from harrier.extensions import modify @modify.pages('**/*') def modify_pages(data, config): return None """ }) with pytest.raises(ExtensionError) as exc_info: build(str(tmpdir)) assert exc_info.value.args[ 0] == 'extension "modify_pages" did not return a dict'
def test_copy_extensions(tmpdir): mktree( tmpdir, { 'pages': { 'index.html': 'hello', }, 'theme/assets': { 'image1.png': 'a', 'image2.png': 'c', 'foo/bar.svg': 'c', }, 'extensions.py': """ from harrier.extensions import modify, template @modify.copy('/foo/*') def modify_foo(in_path, out_path, config): out_path.write_text(f'{in_path.name} {in_path.read_text()} custom') return 1 # prevent default copy @modify.copy('/image2.png') def print_in_path(in_path, out_path, config): out_path.with_name(out_path.name + '.alt').write_bytes(in_path.read_bytes() + b'2') # return nothing so normal copy also happens """ }) build(str(tmpdir)) assert gettree(tmpdir.join('dist')) == { 'index.html': 'hello\n', 'foo': { 'bar.4a8a08f.svg': 'bar.svg c custom', }, 'image1.0cc175b.png': 'a', 'image2.4a8a08f.png': 'c', 'image2.4a8a08f.png.alt': 'c2', }
def test_build_multi_part(tmpdir): mktree( tmpdir, { 'pages': { 'multipart_list.md': ('---\n' 'uri: /list_md.html\n' 'template: list.jinja\n' '---\n' 'part 1\n' '--- . ---\n' 'part **2**\n' '---.---\n' 'this is part *3*\n'), 'multipart_dict.md': ('---\n' 'uri: /dict_md.html\n' 'template: dict.jinja\n' '---\n' 'the main **section**\n' '--- other ---\n' 'part *2*\n'), 'multipart_list.html': ('---\n' 'uri: /list_html.html\n' 'template: list.jinja\n' '---\n' 'part 1\n' '--- . ---\n' 'part 2\n' '---.---\n' 'this is part 3\n'), 'multipart_dict.html': ('---\n' 'uri: /dict_html.html\n' 'template: dict.jinja\n' '---\n' 'the main section\n' '--- other ---\n' 'part 2\n'), }, 'theme': { 'templates/': { 'list.jinja': ('{% for v in content %}\n' ' {{ v.content }}\n' '{% endfor %}\n'), 'dict.jinja': ('{{ content.main.content }}\n' '{{ content.other.content }}\n'), }, }, }) build(tmpdir, mode=Mode.production) assert gettree(tmpdir.join('dist')) == { 'list_md.html': ('\n' ' <p>part 1</p>\n' '\n\n' ' <p>part <strong>2</strong></p>\n' '\n\n' ' <p>this is part <em>3</em></p>\n'), 'dict_md.html': ('<p>the main <strong>section</strong></p>\n' '\n' '<p>part <em>2</em></p>\n'), 'list_html.html': ('\n' ' part 1\n' '\n' ' part 2\n' '\n' ' this is part 3\n'), 'dict_html.html': ('the main section\n' 'part 2\n'), }