diff options
Diffstat (limited to 'tests/test_rst_compiler.py')
| -rw-r--r-- | tests/test_rst_compiler.py | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/tests/test_rst_compiler.py b/tests/test_rst_compiler.py new file mode 100644 index 0000000..c5db8dd --- /dev/null +++ b/tests/test_rst_compiler.py @@ -0,0 +1,369 @@ +# coding: utf8 +# Author: Rodrigo Bistolfi +# Date: 03/2013 + + +""" Test cases for Nikola ReST extensions. +A base class ReSTExtensionTestCase provides the tests basic behaivor. +Subclasses must override the "sample" class attribute with the ReST markup. +The sample will be rendered as HTML using publish_parts() by setUp(). +One method is provided for checking the resulting HTML: + + * assertHTMLContains(element, attributes=None, text=None) + +The HTML is parsed with lxml for checking against the data you provide. The +method takes an element argument, a string representing the *name* of an HTML +tag, like "script" or "iframe". We will try to find this tag in the document +and perform the tests on it. You can pass a dictionary to the attributes kwarg +representing the name and the value of the tag attributes. The text kwarg takes +a string argument, which will be tested against the contents of the HTML +element. +One last caveat: you need to url unquote your urls if you are going to test +attributes like "src" or "link", since the HTML rendered by docutils will be +always unquoted. + +""" + + +from __future__ import unicode_literals, absolute_import + +import codecs +try: + from io import StringIO +except ImportError: + from StringIO import StringIO # NOQA +import os +import sys +import tempfile + +import docutils +from lxml import html +from nose.plugins.skip import SkipTest +import unittest +from yapsy.PluginManager import PluginManager + +from nikola import utils +import nikola.plugins.compile.rest +from nikola.plugins.compile.rest import gist +from nikola.plugins.compile.rest import vimeo +import nikola.plugins.compile.rest.listing +from nikola.plugins.compile.rest.doc import Plugin as DocPlugin +from nikola.utils import _reload, STDERR_HANDLER +from nikola.plugin_categories import ( + Command, + Task, + LateTask, + TemplateSystem, + PageCompiler, + TaskMultiplier, + RestExtension, +) +from .base import BaseTestCase + + +class FakePost(object): + + def __init__(self, title, slug): + self._title = title + self._slug = slug + self._meta = {'slug': slug} + + def title(self): + return self._title + + def meta(self, key): + return self._meta[key] + + def permalink(self): + return '/posts/' + self._slug + + +class FakeSite(object): + def __init__(self): + self.template_system = self + self.config = { + 'DISABLED_PLUGINS': [], + 'EXTRA_PLUGINS': [], + 'DEFAULT_LANG': 'en', + } + self.EXTRA_PLUGINS = self.config['EXTRA_PLUGINS'] + self.plugin_manager = PluginManager(categories_filter={ + "Command": Command, + "Task": Task, + "LateTask": LateTask, + "TemplateSystem": TemplateSystem, + "PageCompiler": PageCompiler, + "TaskMultiplier": TaskMultiplier, + "RestExtension": RestExtension, + }) + self.loghandlers = [STDERR_HANDLER] + self.plugin_manager.setPluginInfoExtension('plugin') + if sys.version_info[0] == 3: + places = [ + os.path.join(os.path.dirname(utils.__file__), 'plugins'), + ] + else: + places = [ + os.path.join(os.path.dirname(utils.__file__), utils.sys_encode('plugins')), + ] + self.plugin_manager.setPluginPlaces(places) + self.plugin_manager.collectPlugins() + + self.timeline = [ + FakePost(title='Fake post', + slug='fake-post') + ] + + def render_template(self, name, _, context): + return('<img src="IMG.jpg">') + + +class ReSTExtensionTestCase(BaseTestCase): + """ Base class for testing ReST extensions """ + + sample = 'foo' + deps = None + + def setUp(self): + self.compiler = nikola.plugins.compile.rest.CompileRest() + self.compiler.set_site(FakeSite()) + return super(ReSTExtensionTestCase, self).setUp() + + def basic_test(self): + """ Parse cls.sample into a HTML document tree """ + self.setHtmlFromRst(self.sample) + + def setHtmlFromRst(self, rst): + """ Create html output from rst string """ + tmpdir = tempfile.mkdtemp() + inf = os.path.join(tmpdir, 'inf') + outf = os.path.join(tmpdir, 'outf') + depf = os.path.join(tmpdir, 'outf.dep') + with codecs.open(inf, 'wb+', 'utf8') as f: + f.write(rst) + self.html = self.compiler.compile_html(inf, outf) + with codecs.open(outf, 'r', 'utf8') as f: + self.html = f.read() + os.unlink(inf) + os.unlink(outf) + if os.path.isfile(depf): + with codecs.open(depf, 'r', 'utf8') as f: + self.assertEqual(self.deps, f.read()) + os.unlink(depf) + else: + self.assertEqual(self.deps, None) + os.rmdir(tmpdir) + self.html_doc = html.parse(StringIO(self.html)) + + def assertHTMLContains(self, element, attributes=None, text=None): + """ Test if HTML document includes an element with the given + attributes and text content + + """ + try: + tag = next(self.html_doc.iter(element)) + except StopIteration: + raise Exception("<{0}> not in {1}".format(element, self.html)) + else: + if attributes: + arg_attrs = set(attributes.items()) + tag_attrs = set(tag.items()) + self.assertTrue(arg_attrs.issubset(tag_attrs)) + if text: + self.assertIn(text, tag.text) + + +class ReSTExtensionTestCaseTestCase(ReSTExtensionTestCase): + """ Simple test for our base class :) """ + + sample = '.. raw:: html\n\n <iframe src="foo" height="bar">spam</iframe>' + + def test_test(self): + self.basic_test() + self.assertHTMLContains("iframe", attributes={"src": "foo"}, + text="spam") + self.assertRaises(Exception, self.assertHTMLContains, "eggs", {}) + + +class MathTestCase(ReSTExtensionTestCase): + sample = ':math:`e^{ix} = \cos x + i\sin x`' + + def test_mathjax(self): + """ Test that math is outputting MathJax.""" + self.basic_test() + self.assertHTMLContains("span", attributes={"class": "math"}, + text="\(e^{ix} = \cos x + i\sin x\)") + + +class GistTestCase(ReSTExtensionTestCase): + """ Test GitHubGist. + We will replace get_raw_gist() and get_raw_gist_with_filename() + monkeypatching the GitHubGist class for avoiding network dependency + + """ + gist_type = gist.GitHubGist + sample = '.. gist:: fake_id\n :file: spam.py' + sample_without_filename = '.. gist:: fake_id2' + + def setUp(self): + """ Patch GitHubGist for avoiding network dependency """ + super(GistTestCase, self).setUp() + self.gist_type.get_raw_gist_with_filename = lambda *_: 'raw_gist_file' + self.gist_type.get_raw_gist = lambda *_: "raw_gist" + _reload(nikola.plugins.compile.rest) + + def test_gist(self): + """ Test the gist directive with filename """ + raise SkipTest + self.setHtmlFromRst(self.sample) + output = 'https://gist.github.com/fake_id.js?file=spam.py' + self.assertHTMLContains("script", attributes={"src": output}) + self.assertHTMLContains("pre", text="raw_gist_file") + + def test_gist_without_filename(self): + """ Test the gist directive without filename """ + raise SkipTest + self.setHtmlFromRst(self.sample_without_filename) + output = 'https://gist.github.com/fake_id2.js' + self.assertHTMLContains("script", attributes={"src": output}) + self.assertHTMLContains("pre", text="raw_gist") + + +class GistIntegrationTestCase(ReSTExtensionTestCase): + """ Test requests integration. The gist plugin uses requests to fetch gist + contents and place it in a noscript tag. + + """ + sample = '.. gist:: 1812835' + + def test_gist_integration(self): + """ Fetch contents of the gist from GH and render in a noscript tag """ + self.basic_test() + text = ('Be alone, that is the secret of invention: be alone, that is' + ' when ideas are born. -- Nikola Tesla') + self.assertHTMLContains('pre', text=text) + + +class SlidesTestCase(ReSTExtensionTestCase): + """ Slides test case """ + + sample = '.. slides:: IMG.jpg\n' + + def test_slides(self): + """ Test the slides js generation and img tag creation """ + self.basic_test() + self.assertHTMLContains("img", attributes={"src": "IMG.jpg"}) + + +class SoundCloudTestCase(ReSTExtensionTestCase): + """ SoundCloud test case """ + + sample = '.. soundcloud:: SID\n :height: 400\n :width: 600' + + def test_soundcloud(self): + """ Test SoundCloud iframe tag generation """ + self.basic_test() + self.assertHTMLContains("iframe", + attributes={"src": ("https://w.soundcloud.com" + "/player/?url=http://" + "api.soundcloud.com/" + "tracks/SID"), + "height": "400", "width": "600"}) + + +class VimeoTestCase(ReSTExtensionTestCase): + """Vimeo test. + Set Vimeo.request_size to False for avoiding querying the Vimeo api + over the network + + """ + sample = '.. vimeo:: VID\n :height: 400\n :width: 600' + + def setUp(self): + """ Disable query of the vimeo api over the wire """ + vimeo.Vimeo.request_size = False + super(VimeoTestCase, self).setUp() + _reload(nikola.plugins.compile.rest) + + def test_vimeo(self): + """ Test Vimeo iframe tag generation """ + self.basic_test() + self.assertHTMLContains("iframe", + attributes={"src": ("http://player.vimeo.com/" + "video/VID"), + "height": "400", "width": "600"}) + + +class YoutubeTestCase(ReSTExtensionTestCase): + """ Youtube test case """ + + sample = '.. youtube:: YID\n :height: 400\n :width: 600' + + def test_youtube(self): + """ Test Youtube iframe tag generation """ + self.basic_test() + self.assertHTMLContains("iframe", + attributes={"src": ("http://www.youtube.com/" + "embed/YID?rel=0&hd=1&" + "wmode=transparent"), + "height": "400", "width": "600"}) + + +class ListingTestCase(ReSTExtensionTestCase): + """ Listing test case and CodeBlock alias tests """ + + deps = None + sample1 = '.. listing:: nikola.py python\n\n' + sample2 = '.. code-block:: python\n\n import antigravity' + sample3 = '.. sourcecode:: python\n\n import antigravity' + + #def test_listing(self): + ##""" Test that we can render a file object contents without errors """ + ##with cd(os.path.dirname(__file__)): + #self.deps = 'listings/nikola.py' + #self.setHtmlFromRst(self.sample1) + + def test_codeblock_alias(self): + """ Test CodeBlock aliases """ + self.deps = None + self.setHtmlFromRst(self.sample2) + self.setHtmlFromRst(self.sample3) + + +class DocTestCase(ReSTExtensionTestCase): + """ Ref role test case """ + + sample = 'Sample for testing my :doc:`doesnt-exist-post`' + sample1 = 'Sample for testing my :doc:`fake-post`' + sample2 = 'Sample for testing my :doc:`titled post <fake-post>`' + + def setUp(self): + # Initialize plugin, register role + self.plugin = DocPlugin() + self.plugin.set_site(FakeSite()) + # Hack to fix leaked state from integration tests + try: + f = docutils.parsers.rst.roles.role('doc', None, None, None)[0] + f.site = FakeSite() + except AttributeError: + pass + return super(DocTestCase, self).setUp() + + def test_doc_doesnt_exist(self): + self.assertRaises(Exception, self.assertHTMLContains, 'anything', {}) + + def test_doc(self): + self.setHtmlFromRst(self.sample1) + self.assertHTMLContains('a', + text='Fake post', + attributes={'href': '/posts/fake-post'}) + + def test_doc_titled(self): + self.setHtmlFromRst(self.sample2) + self.assertHTMLContains('a', + text='titled post', + attributes={'href': '/posts/fake-post'}) + + +if __name__ == "__main__": + unittest.main() |
