Skip to content

Commit

Permalink
More test coverage: we weren't actually rendering but the failure was…
Browse files Browse the repository at this point in the history
… being hidden.
  • Loading branch information
jamadden committed Oct 14, 2017
1 parent 5915cdd commit 32a1ca7
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 51 deletions.
17 changes: 12 additions & 5 deletions src/nti/nikola_chameleon/feeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def options(self):

def _head_feed_link(self, link_type, link_name, link_postfix, classification, kind, language):
if len(self.options['translations']) > 1:
raise Exception("Translations not supported")
raise NotImplementedError("Translations not supported")

context = dict(self.options)
context.update(locals())
Expand All @@ -56,7 +56,7 @@ def _feed_head_rss(self, classification=None, kind='index', rss_override=True):
and not (options['rss_link'] and rss_override)
and kind != 'archive'):
if len(options['translations']) > 1:
raise Exception("Translations not supported")
raise NotImplementedError("Translations not supported")
for language in sorted(options['translations']):
if (classification or classification == '') and kind != 'index':
result += self._head_feed_link(
Expand Down Expand Up @@ -125,16 +125,23 @@ class HTMLFeedLinkViewlet(ViewletBase):
classification_name="author"
/>
"""
available = True
available = False
classification_name = None

def __init__(self, context, request, view, manager):
# There's a bug in the viewlet manager: it filters
# before doing the update(), so you can't set
# 'available' in update.
super(HTMLFeedLinkViewlet, self).__init__(context, request, view, manager)
self.update()

def update(self):
options = self.request.options
generate_atom = options['generate_atom']
generate_rss = options['generate_rss']

if not generate_atom and not generate_rss:
self.available = False
if generate_atom or generate_rss:
self.available = True

_html_feed_link_tmpl = ViewPageTemplate("""
<a tal:define="_link nocall:options/_link;
Expand Down
7 changes: 5 additions & 2 deletions src/nti/nikola_chameleon/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
from zope.interface.common.sequence import IReadSequence
from zope.viewlet.interfaces import IViewletManager

# pylint:disable=inherit-non-class

class IPost(interface.Interface):
"""
A post.
Expand Down Expand Up @@ -56,7 +58,8 @@ class IGallery(interface.Interface):
enable_comments = interface.Attribute("Whether comments are enabled.")
folders = interface.Attribute("List of folders (path, title)")
permalink = interface.Attribute("Permalink")
photo_array = interface.Attribute("List Photo array (contains dicts with image data: url, url_thumb, title, size{w, h}) ")
photo_array = interface.Attribute(
"List Photo array (contains dicts with image data: url, url_thumb, title, size{w, h}) ")
post = interface.Attribute("Optionally the post for this gallery")
thumbnail_size = interface.Attribute("THUMBNAIL_SIZE setting")

Expand Down Expand Up @@ -297,7 +300,7 @@ def _cleanUp():

try:
from zope.testing import cleanup
except ImportError:
except ImportError: # pragma: no cover
pass
else:
cleanup.addCleanUp(_cleanUp)
30 changes: 11 additions & 19 deletions src/nti/nikola_chameleon/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ class ChameleonTemplates(TemplateSystem):
def __init__(self):
super(ChameleonTemplates, self).__init__()
self._conf_context = xmlconfig.file('configure.zcml', nti.nikola_chameleon)
# A list of directories from least to most specific (or lowest to highest priority)
self._template_paths = []
self._injected_template_paths = []

def set_directories(self, directories, cache_folder):
"""Sets the list of folders where templates are located and cache."""
Expand All @@ -127,15 +127,11 @@ def set_directories(self, directories, cache_folder):
self._template_paths.extend(reversed(directories))

# We do set up the cache, though.

cache_dir = None
if not 'CHAMELEON_CACHE' in os.environ or \
not os.path.isdir(os.path.expanduser(os.environ['CHAMELEON_CACHE'])):
cache_dir = os.path.abspath(os.path.join(cache_folder, 'chameleon_cache'))
makedirs(cache_dir)
os.environ['CHAMELEON_CACHE'] = cache_dir
else:
cache_dir = os.environ['CHAMELEON_CACHE']
# We ignore any existing environment variable because it really
# really needs to go where we tell it.
cache_dir = os.path.abspath(os.path.join(cache_folder, 'chameleon_cache'))
makedirs(cache_dir)
os.environ['CHAMELEON_CACHE'] = cache_dir

conf_mod = dottedname.resolve('chameleon.config')
if conf_mod.CACHE_DIRECTORY != cache_dir:
Expand Down Expand Up @@ -190,7 +186,7 @@ def render_template(self, template_name, output_name, context):
so that the caller may do additional processing.
"""
result = self.render_template_to_string(template_name, context)
if output_name is not None:
if output_name is not None: # pragma: no cover (Nikola doesn't seem to use this)
makedirs(os.path.dirname(output_name))
with open(output_name, 'wb') as f:
f.write(result.encode('utf-8'))
Expand Down Expand Up @@ -230,7 +226,7 @@ def render_template_to_string(self, template, context):
context = _GalleryContext(options)
elif template == 'slides.tmpl':
context = _SlideContext(options)
else:
else: # pragma: no cover
# We shouldn't get here.
logger.warn("Unknown context type for template %r and options %r",
template, list(options))
Expand Down Expand Up @@ -301,19 +297,15 @@ def new_view_for_context(self, context, request):
interface.alsoProvides(view, interfaces.ICommentKindNone)
return view

def inject_directory(self, directory):
def inject_directory(self, directory): # pragma: no cover (Nikola seems not to call this)
"""Injects the directory with the lowest priority in the
template search mechanism."""

# This is called very early, that's the only reason that it gets
# set low in the chains. It's called for each template plugin?
self._injected_template_paths.append(directory)
self._template_paths.insert(0, directory)

def _provide_templates(self):
for d in self._injected_template_paths:
self._provide_templates_from_directory(d)
self._injected_template_paths = []

for d in self._template_paths:
self._provide_templates_from_directory(d)
self._template_paths = []
Expand All @@ -336,7 +328,7 @@ def _provide_templates_from_directory(self, directory):
template = NikolaPageFileTemplate(macro_file)
for name in template.macros.names:
factory = MacroFactory(macro_file, name, 'text/html')
if name in seen_macros:
if name in seen_macros: # pragma: no cover
logger.warning("Duplicate macro '%s' in %s and %s",
name, seen_macros[name], macro_file)
seen_macros[name].add(macro_file)
Expand Down
109 changes: 92 additions & 17 deletions src/nti/nikola_chameleon/tests/test_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,102 @@
# disable: accessing protected members, too many methods
# pylint: disable=W0212,R0904

from contextlib import contextmanager
import os
import unittest

from zope.component.testlayer import LayerBase
from zope.testing.cleanup import CleanUp

class TestRender(CleanUp,unittest.TestCase):
import nti.nikola_chameleon.tests
from nti.testing.layers import find_test

def test_render(self):
class RenderedLayer(CleanUp, LayerBase):

def __init__(self):
LayerBase.__init__(self, nti.nikola_chameleon.tests)
self.testsite = os.path.join(os.path.dirname(__file__), 'testsite')
self._rendered = False
self.test = None

@contextmanager
def current_site_directory(self):
cwd = os.getcwd()
try:
os.chdir(self.testsite)
yield
finally:
os.chdir(cwd)

def render(self):
from nikola.__main__ import main
import os
from nikola import nikola
with self.current_site_directory():
# Make it raise exceptions instead of swallowing
# them.
old_debug = nikola.DEBUG
nikola.DEBUG = True
try:
x = main(['build', '--quiet', '--strict', '--no-continue', '-v2'])
if x:
raise AssertionError("Build error")
finally:
nikola.DEBUG = old_debug
self._rendered = True

def testSetUp(self, test=None): # pylint:disable=arguments-differ
self.test = test or find_test()

def testTearDown(self):
self.test = None

def tearDown(self):
import shutil
cwd = os.getcwd()
testsite = os.path.join(os.path.dirname(__file__), 'testsite')
os.chdir(testsite)
self.addCleanup(os.chdir, cwd)

main(['build', '-q'])

shutil.rmtree('output')
# This gets different names under different versions
# of python, for some reason.
if os.path.exists('.doit.db.db'): # python 3
os.remove('.doit.db.db')
else:
os.remove(".doit.db")

with self.current_site_directory():
if os.path.exists('output'):
shutil.rmtree('output')
# This gets different names under different versions
# of python, for some reason.
for pname in ('.doit.db.db', 'doit.db'):
if os.path.exists(pname):
os.remove(pname)

CleanUp.cleanUp(self)

def _render_if_needed(self):
if not self._rendered:
self.render()

def assertOutputExists(self, *path):
self._render_if_needed()
fname = os.path.join(self.testsite, 'output', *path)
if not os.path.isfile(fname):
self.test.fail("No path %s" % (fname,)) # pragma: no cover
return fname

def assertInOutput(self, expected, *path):
self._render_if_needed()
fname = self.assertOutputExists(*path)
with open(fname, 'r') as f:
actual = f.read()

self.test.assertIn(expected, actual)

LAYER = RenderedLayer()


class TestRender(unittest.TestCase):

layer = LAYER

def test_render(self):
self.layer.assertOutputExists("index.html")
self.layer.assertOutputExists("index.atom")
self.layer.assertOutputExists("rss.xml")
self.layer.assertOutputExists("pages", "about-nikola.html")
self.layer.assertOutputExists("listings", "index.html")
self.layer.assertOutputExists("listings", "hello.py.html")

self.layer.assertInOutput('<div class="metadata"><p class="feedlink">',
'authors', 'jason-madden.html')
8 changes: 4 additions & 4 deletions src/nti/nikola_chameleon/tests/testsite/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@

# If ENABLE_AUTHOR_PAGES is set to True and there is more than one
# author, author pages are generated.
# ENABLE_AUTHOR_PAGES = True
ENABLE_AUTHOR_PAGES = True

# Path to author pages. Final locations are:
# output / TRANSLATION[lang] / AUTHOR_PATH / index.html (list of authors)
Expand Down Expand Up @@ -898,7 +898,7 @@
# )

# Show teasers (instead of full posts) in indexes? Defaults to False.
# INDEX_TEASERS = False
INDEX_TEASERS = True

# HTML fragments with the Read more... links.
# The following tags exist and are replaced for you:
Expand Down Expand Up @@ -1149,11 +1149,11 @@
# FEED_TEASER option. FEED_LINKS_APPEND_QUERY is also respected. Atom feeds
# are generated even for old indexes and have pagination link relations
# between each other. Old Atom feeds with no changes are marked as archived.
# GENERATE_ATOM = False
GENERATE_ATOM = True

# Only include teasers in Atom and RSS feeds. Disabling include the full
# content. Defaults to True.
# FEED_TEASERS = True
FEED_TEASERS = False

# Strip HTML from Atom and RSS feed summaries and content. Defaults to False.
# FEED_PLAIN = False
Expand Down
27 changes: 27 additions & 0 deletions src/nti/nikola_chameleon/tests/testsite/posts/2.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.. title: Test Page
.. slug: test-page
.. date: 2012-03-30 23:00:00 UTC-03:00
.. tags: nikola, python, demo, blog
.. author: Jason Madden
.. link: https://getnikola.com/
.. description:
.. category: nikola
.. figure:: https://farm1.staticflickr.com/138/352972944_4f9d568680.jpg
:target: https://farm1.staticflickr.com/138/352972944_4f9d568680_z.jpg?zz=1
:class: thumbnail
:alt: Nikola Tesla Corner by nicwest, on Flickr

If you can see this in a web browser, it means you managed to install Nikola,
and build a site using it. Congratulations!

Next steps:

* :doc:`Read the manual <handbook>`
* `Visit the Nikola website to learn more <https://getnikola.com>`__
* `See a demo photo gallery <link://gallery/demo>`__
* :doc:`See a demo listing <listings-demo>`
* :doc:`See a demo slideshow <slides-demo>`
* :doc:`See a demo of a longer text <dr-nikolas-vendetta>`

Send feedback to info@getnikola.com!
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
<nav metal:use-macro="macro:breadcrumbbar" />
<ul tal:condition="context/folders|context/files">
<li tal:repeat="name context/folders">
<metal:block metal:use-macro="folder-open"/>
<metal:block metal:use-macro="macro:folder_open"/>
<a href="${name}" class="listing-folder">${name}</a>
</li>
<li tal:repeat="name context/files">
<metal:block metal:use-macro="file"/>
<metal:block metal:use-macro="macro:file"/>
<a href="${name}.html" class="listing-file">${name}</a>
</li>
</ul>
Expand Down
6 changes: 4 additions & 2 deletions src/nti/nikola_chameleon/zcml.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ class IViewletManagerDirective(interface.Interface):

def viewletManagerDirective(_context, id):
id = str(id) # ensure native string
if id in dir(viewlets):
if id in dir(viewlets): # pragma: no cover
# The name functions as the identity, so this is
# idempotent.
return

# We must do our work right now so that it can
Expand Down Expand Up @@ -63,7 +65,7 @@ def _cleanUp():

try:
from zope.testing import cleanup
except ImportError:
except ImportError: # pragma: no cover
pass
else:
cleanup.addCleanUp(_cleanUp)

0 comments on commit 32a1ca7

Please sign in to comment.