diff options
Diffstat (limited to 'tests/test_shortcodes.py')
| -rw-r--r-- | tests/test_shortcodes.py | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/tests/test_shortcodes.py b/tests/test_shortcodes.py new file mode 100644 index 0000000..8383176 --- /dev/null +++ b/tests/test_shortcodes.py @@ -0,0 +1,200 @@ +"""Test shortcodes.""" + +import pytest + +import nikola.utils +from nikola import shortcodes + + +@pytest.mark.parametrize( + "template, expected_result", + [ + ("test({{% noargs %}})", "test(noargs success!)"), + ( + "test({{% noargs %}}\\hello world/{{% /noargs %}})", + "test(noargs \\hello world/ success!)", + ), + ], +) +def test_noargs(site, template, expected_result): + applied_shortcode = shortcodes.apply_shortcodes(template, site.shortcode_registry)[0] + assert applied_shortcode == expected_result + + +@pytest.mark.parametrize( + "template, expected_result", + [ + ("test({{% arg 1 %}})", "test(arg ('1',)/[]/)"), + ("test({{% arg 1 2aa %}})", "test(arg ('1', '2aa')/[]/)"), + ('test({{% arg "hello world" %}})', "test(arg ('hello world',)/[]/)"), + ("test({{% arg back\\ slash arg2 %}})", "test(arg ('back slash', 'arg2')/[]/)"), + ('test({{% arg "%}}" %}})', "test(arg ('%}}',)/[]/)"), + ], +) +def test_positional_arguments(site, template, expected_result): + applied_shortcode = shortcodes.apply_shortcodes(template, site.shortcode_registry)[0] + assert applied_shortcode == expected_result + + +@pytest.mark.parametrize( + "template, expected_result", + [ + ("test({{% arg 1a=2b %}})", "test(arg ()/[('1a', '2b')]/)"), + ( + 'test({{% arg 1a="2b 3c" 4d=5f %}})', + "test(arg ()/[('1a', '2b 3c'), ('4d', '5f')]/)", + ), + ( + 'test({{% arg 1a="2b 3c" 4d=5f back=slash\\ slash %}})', + "test(arg ()/[('1a', '2b 3c'), ('4d', '5f'), ('back', 'slash slash')]/)", + ), + ], +) +def test_arg_keyword(site, template, expected_result): + applied_shortcode = shortcodes.apply_shortcodes(template, site.shortcode_registry)[0] + assert applied_shortcode == expected_result + + +@pytest.mark.parametrize( + "template, expected_result", + [ + ("test({{% arg 123 %}}Hello!{{% /arg %}})", "test(arg ('123',)/[]/Hello!)"), + ( + "test({{% arg 123 456 foo=bar %}}Hello world!{{% /arg %}})", + "test(arg ('123', '456')/[('foo', 'bar')]/Hello world!)", + ), + ( + 'test({{% arg 123 456 foo=bar baz="quotes rock." %}}Hello test suite!{{% /arg %}})', + "test(arg ('123', '456')/[('baz', 'quotes rock.'), ('foo', 'bar')]/Hello test suite!)", + ), + ( + 'test({{% arg "123 foo" foobar foo=bar baz="quotes rock." %}}Hello test suite!!{{% /arg %}})', + "test(arg ('123 foo', 'foobar')/[('baz', 'quotes rock.'), ('foo', 'bar')]/Hello test suite!!)", + ), + ], +) +def test_data(site, template, expected_result): + applied_shortcode = shortcodes.apply_shortcodes(template, site.shortcode_registry)[0] + assert applied_shortcode == expected_result + + +@pytest.mark.parametrize( + "template, expected_error_pattern", + [ + ( + "{{% start", + "^Shortcode 'start' starting at .* is not terminated correctly with '%}}'!", + ), + ( + "{{% wrong ending %%}", + "^Syntax error in shortcode 'wrong' at .*: expecting whitespace!", + ), + ( + "{{% start %}} {{% /end %}}", + "^Found shortcode ending '{{% /end %}}' which isn't closing a started shortcode", + ), + ('{{% start "asdf %}}', "^Unexpected end of unquoted string"), + ("{{% start =b %}}", "^String starting at .* must be non-empty!"), + ('{{% start "a\\', "^Unexpected end of data while escaping"), + ("{{% start a\\", "^Unexpected end of data while escaping"), + ('{{% start a"b" %}}', "^Unexpected quotation mark in unquoted string"), + ( + '{{% start "a"b %}}', + "^Syntax error in shortcode 'start' at .*: expecting whitespace!", + ), + ("{{% %}}", "^Syntax error: '{{%' must be followed by shortcode name"), + ("{{%", "^Syntax error: '{{%' must be followed by shortcode name"), + ("{{% ", "^Syntax error: '{{%' must be followed by shortcode name"), + ( + "{{% / %}}", + "^Found shortcode ending '{{% / %}}' which isn't closing a started shortcode", + ), + ("{{% / a %}}", "^Syntax error: '{{% /' must be followed by ' %}}'"), + ( + "==> {{% <==", + "^Shortcode '<==' starting at .* is not terminated correctly with '%}}'!", + ), + ], +) +def test_errors(site, template, expected_error_pattern): + with pytest.raises(shortcodes.ParsingError, match=expected_error_pattern): + shortcodes.apply_shortcodes( + template, site.shortcode_registry, raise_exceptions=True + ) + + +@pytest.mark.parametrize( + "input, expected", + [ + ("{{% foo %}}", (u"SC1", {u"SC1": u"{{% foo %}}"})), + ( + "{{% foo %}} bar {{% /foo %}}", + (u"SC1", {u"SC1": u"{{% foo %}} bar {{% /foo %}}"}), + ), + ( + "AAA{{% foo %}} bar {{% /foo %}}BBB", + (u"AAASC1BBB", {u"SC1": u"{{% foo %}} bar {{% /foo %}}"}), + ), + ( + "AAA{{% foo %}} {{% bar %}} {{% /foo %}}BBB", + (u"AAASC1BBB", {u"SC1": u"{{% foo %}} {{% bar %}} {{% /foo %}}"}), + ), + ( + "AAA{{% foo %}} {{% /bar %}} {{% /foo %}}BBB", + (u"AAASC1BBB", {u"SC1": u"{{% foo %}} {{% /bar %}} {{% /foo %}}"}), + ), + ( + "AAA{{% foo %}} {{% bar %}} quux {{% /bar %}} {{% /foo %}}BBB", + ( + u"AAASC1BBB", + {u"SC1": u"{{% foo %}} {{% bar %}} quux {{% /bar %}} {{% /foo %}}"}, + ), + ), + ( + "AAA{{% foo %}} BBB {{% bar %}} quux {{% /bar %}} CCC", + ( + u"AAASC1 BBB SC2 CCC", + {u"SC1": u"{{% foo %}}", u"SC2": u"{{% bar %}} quux {{% /bar %}}"}, + ), + ), + ], +) +def test_extract_shortcodes(input, expected, monkeypatch): + i = iter("SC%d" % i for i in range(1, 100)) + monkeypatch.setattr(shortcodes, "_new_sc_id", i.__next__) + extracted = shortcodes.extract_shortcodes(input) + assert extracted == expected + + +@pytest.fixture(scope="module") +def site(): + s = FakeSiteWithShortcodeRegistry() + s.register_shortcode("noargs", noargs) + s.register_shortcode("arg", arg) + return s + + +class FakeSiteWithShortcodeRegistry: + def __init__(self): + self.shortcode_registry = {} + self.debug = True + + # this code duplicated in nikola/nikola.py + def register_shortcode(self, name, f): + """Register function f to handle shortcode "name".""" + if name in self.shortcode_registry: + nikola.utils.LOGGER.warn("Shortcode name conflict: %s", name) + return + self.shortcode_registry[name] = f + + +def noargs(site, data="", lang=""): + return "noargs {0} success!".format(data) + + +def arg(*args, **kwargs): + # don’t clutter the kwargs dict + kwargs.pop("site") + data = kwargs.pop("data") + kwargs.pop("lang") + return "arg {0}/{1}/{2}".format(args, sorted(kwargs.items()), data) |
