Add initial Hotjar tracking code support

This commit is contained in:
Pi Delport 2018-11-15 13:50:09 +02:00
parent 2d8cbd25bd
commit c2a11ce794
3 changed files with 150 additions and 0 deletions

View file

@ -24,6 +24,7 @@ TAG_MODULES = [
'analytical.gauges',
'analytical.google_analytics',
'analytical.gosquared',
'analytical.hotjar',
'analytical.hubspot',
'analytical.intercom',
'analytical.kiss_insights',

View file

@ -0,0 +1,65 @@
"""
Hotjar template tags and filters.
"""
from __future__ import absolute_import
import re
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import get_required_setting, is_internal_ip, disable_html
HOTJAR_TRACKING_CODE = """\
<script>
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:%(HOTJAR_SITE_ID)s,hjsv:6};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
</script>
"""
register = Library()
def _validate_no_args(token):
bits = token.split_contents()
if len(bits) > 1:
raise TemplateSyntaxError("'%s' takes no arguments" % bits[0])
@register.tag
def hotjar(parser, token):
"""
Hotjar template tag.
"""
_validate_no_args(token)
return HotjarNode()
class HotjarNode(Node):
def __init__(self):
self.site_id = get_required_setting(
'HOTJAR_SITE_ID',
re.compile(r'^\d+$'),
"must be (a string containing) a number",
)
def render(self, context):
html = HOTJAR_TRACKING_CODE % {'HOTJAR_SITE_ID': self.site_id}
if is_internal_ip(context, 'HOTJAR'):
return disable_html(html, 'Hotjar')
else:
return html
def contribute_to_analytical(add_node):
# ensure properly configured
HotjarNode()
add_node('head_bottom', HotjarNode)

View file

@ -0,0 +1,84 @@
"""
Tests for the Hotjar template tags.
"""
from django.http import HttpRequest
from django.template import Context, Template, TemplateSyntaxError
from django.test import override_settings
from analytical.templatetags.analytical import _load_template_nodes
from analytical.templatetags.hotjar import HotjarNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
expected_html = """\
<script>
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:123456789,hjsv:6};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
</script>
"""
@override_settings(HOTJAR_SITE_ID='123456789')
class HotjarTagTestCase(TagTestCase):
maxDiff = None
def test_tag(self):
html = self.render_tag('hotjar', 'hotjar')
self.assertEqual(expected_html, html)
def test_node(self):
html = HotjarNode().render(Context({}))
self.assertEqual(expected_html, html)
def test_tags_take_no_args(self):
self.assertRaisesRegexp(
TemplateSyntaxError,
r"^'hotjar' takes no arguments$",
lambda: (Template('{% load hotjar %}{% hotjar "arg" %}')
.render(Context({}))),
)
@override_settings(HOTJAR_SITE_ID=None)
def test_no_id(self):
expected_pattern = r'^HOTJAR_SITE_ID setting is not set$'
self.assertRaisesRegexp(AnalyticalException, expected_pattern, HotjarNode)
@override_settings(HOTJAR_SITE_ID='invalid')
def test_invalid_id(self):
expected_pattern = (
r"^HOTJAR_SITE_ID setting: must be \(a string containing\) a number: 'invalid'$")
self.assertRaisesRegexp(AnalyticalException, expected_pattern, HotjarNode)
@override_settings(ANALYTICAL_INTERNAL_IPS=['1.1.1.1'])
def test_render_internal_ip(self):
request = HttpRequest()
request.META['REMOTE_ADDR'] = '1.1.1.1'
context = Context({'request': request})
actual_html = HotjarNode().render(context)
disabled_html = '\n'.join([
'<!-- Hotjar disabled on internal IP address',
expected_html,
'-->',
])
self.assertEqual(disabled_html, actual_html)
def test_contribute_to_analytical(self):
"""
`hotjar.contribute_to_analytical` registers the head and body nodes.
"""
template_nodes = _load_template_nodes()
self.assertEqual({
'head_top': [],
'head_bottom': [HotjarNode],
'body_top': [],
'body_bottom': [],
}, template_nodes)