Fix tests

Some parts of the code are not yet tested:
 * internal IP addresses feature
 * utils module
This commit is contained in:
Joost Cassee 2011-01-30 02:50:49 +01:00
parent 4bf0fcfcc5
commit cf7589bc20
41 changed files with 746 additions and 886 deletions

View file

@ -13,10 +13,3 @@ __email__ = "joost@cassee.net"
__version__ = "0.2.0alpha"
__copyright__ = "Copyright (C) 2011 Joost Cassee"
__license__ = "MIT License"
try:
from collections import namedtuple
except ImportError:
namedtuple = lambda name, fields: lambda *values: values
Property = namedtuple('Property', ['num', 'name', 'value'])

View file

@ -1,33 +0,0 @@
"""
Context processors for django-analytical.
"""
from django.conf import settings
IMPORT_SETTINGS = [
'ANALYTICAL_INTERNAL_IPS',
'ANALYTICAL_SERVICES',
'CHARTBEAT_USER_ID',
'CLICKY_SITE_ID',
'CRAZY_EGG_ACCOUNT_NUMBER',
'GOOGLE_ANALYTICS_PROPERTY_ID',
'KISS_INSIGHTS_ACCOUNT_NUMBER',
'KISS_INSIGHTS_SITE_CODE',
'KISS_METRICS_API_KEY',
'MIXPANEL_TOKEN',
'OPTIMIZELY_ACCOUNT_NUMBER',
]
def settings(request):
"""
Import all django-analytical settings into the template context.
"""
vars = {}
for setting in IMPORT_SETTINGS:
try:
vars[setting] = getattr(settings, setting)
except AttributeError:
pass
return vars

View file

@ -5,36 +5,24 @@ Analytical template tags and filters.
import logging
from django import template
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.template import Node, TemplateSyntaxError
from django.utils.importlib import import_module
from analytical.templatetags import chartbeat, clicky, crazy_egg, \
google_analytics, hubspot, kiss_insights, kiss_metrics, mixpanel, \
optimizely
TAG_NODES = {
'head_top': [
chartbeat.ChartbeatTopNode, # Chartbeat should come first
kiss_metrics.KissMetricsNode,
optimizely.OptimizelyNode,
],
'head_bottom': [
google_analytics.GoogleAnalyticsNode,
mixpanel.MixpanelNode,
],
'body_top': [
kiss_insights.KissInsightsNode,
],
'body_bottom': [
clicky.ClickyNode,
crazy_egg.CrazyEggNode,
hubspot.HubSpotNode,
chartbeat.ChartbeatBottomNode, # Chartbeat should come last
],
}
TAG_LOCATIONS = ['head_top', 'head_bottom', 'body_top', 'body_bottom']
TAG_POSITIONS = ['first', None, 'last']
TAG_MODULES = [
'analytical.chartbeat',
'analytical.clicky',
'analytical.crazy_egg',
'analytical.google_analytics',
'analytical.hubspot',
'analytical.kiss_insights',
'analytical.kiss_metrics',
'analytical.mixpanel',
'analytical.optimizely'
]
logger = logging.getLogger(__name__)
@ -49,29 +37,36 @@ def _location_tag(location):
return AnalyticalNode(location)
return analytical_tag
for loc in TAG_NODES.keys():
for loc in TAG_LOCATIONS:
register.tag('analytical_%s' % loc, _location_tag(loc))
class AnalyticalNode(Node):
def __init__(self, location):
self.nodes = template_nodes[location]
self.nodes = [node_cls() for node_cls in template_nodes[location]]
def render(self, context):
return "".join([node.render(context) for node in self.nodes])
def _load_template_nodes():
location_nodes = {}
for location, node_classes in TAG_NODES.items():
location_nodes[location] = []
for node_class in node_classes:
try:
node = node_class()
except ImproperlyConfigured, e:
logger.debug("not loading analytical service '%s': %s",
node_class.name, e)
location_nodes.append(node)
return location_nodes
template_nodes = dict((l, dict((p, []) for p in TAG_POSITIONS))
for l in TAG_LOCATIONS)
def add_node_cls(location, node, position=None):
template_nodes[location][position].append(node)
for path in TAG_MODULES:
module = _import_tag_module(path)
try:
module.contribute_to_analytical(add_node_cls)
except ImproperlyConfigured, e:
logger.debug("not loading tags from '%s': %s", path, e)
for location in TAG_LOCATIONS:
template_nodes[location] = sum((template_nodes[location][p]
for p in TAG_POSITIONS), [])
return template_nodes
def _import_tag_module(path):
app_name, lib_name = path.rsplit('.', 1)
return import_module("%s.templatetags.%s" % (app_name, lib_name))
template_nodes = _load_template_nodes()

View file

@ -12,7 +12,8 @@ from django.core.exceptions import ImproperlyConfigured
from django.template import Library, Node, TemplateSyntaxError
from django.utils import simplejson
from analytical.utils import is_internal_ip, disable_html
from analytical.utils import is_internal_ip, disable_html, validate_setting, \
get_required_setting
USER_ID_RE = re.compile(r'^\d{5}$')
@ -56,8 +57,6 @@ def chartbeat_top(parser, token):
return ChartbeatTopNode()
class ChartbeatTopNode(Node):
name = 'Chartbeat top code'
def render(self, context):
if is_internal_ip(context):
return disable_html(INIT_CODE, self.name)
@ -79,10 +78,8 @@ def chartbeat_bottom(parser, token):
return ChartbeatBottomNode()
class ChartbeatBottomNode(Node):
name = 'Chartbeat bottom code'
def __init__(self):
self.user_id = self.get_required_setting(
self.user_id = get_required_setting(
'CHARTBEAT_USER_ID', USER_ID_RE,
"must be a string containing an five-digit number")
@ -97,6 +94,13 @@ class ChartbeatBottomNode(Node):
if domain is not None:
config['domain'] = domain
html = SETUP_CODE % {'config': simplejson.dumps(config)}
if is_internal_ip(context):
html = disable_html(html, self.name)
if is_internal_ip(context, 'CHARTBEAT'):
html = disable_html(html, 'Chartbeat')
return html
def contribute_to_analytical(add_node):
validate_setting('CHARTBEAT_USER_ID', USER_ID_RE,
"must be a string containing an five-digit number")
add_node('head_top', ChartbeatTopNode, 'first')
add_node('body_bottom', ChartbeatBottomNode, 'last')

View file

@ -9,8 +9,8 @@ import re
from django.template import Library, Node, TemplateSyntaxError
from django.utils import simplejson
from analytical.utils import get_required_setting, get_identity, \
is_internal_ip, disable_html
from analytical.utils import get_identity, is_internal_ip, disable_html, \
validate_setting, get_required_setting
SITE_ID_RE = re.compile(r'^\d{8}$')
@ -49,24 +49,29 @@ def clicky(parser, token):
return ClickyNode()
class ClickyNode(Node):
name = 'Clicky'
def __init__(self):
self.site_id = get_required_setting('CLICKY_SITE_ID', SITE_ID_RE,
"must be a string containing an eight-digit number")
def render(self, context):
custom = {}
for var, value in context.items():
if var.startswith('clicky_'):
custom[var[7:]] = value
for dict_ in context:
for var, val in dict_.items():
if var.startswith('clicky_'):
custom[var[7:]] = val
if 'username' not in custom.get('session', {}):
identity = get_identity(context)
identity = get_identity(context, 'clicky')
if identity is not None:
custom.setdefault('session', {})['username'] = identity
html = TRACKING_CODE % {'site_id': self.site_id,
'custom': simplejson.dumps(custom)}
if is_internal_ip(context):
html = disable_html(html, self.name)
if is_internal_ip(context, 'CLICKY'):
html = disable_html(html, 'Clicky')
return html
def contribute_to_analytical(add_node):
validate_setting('CLICKY_SITE_ID', SITE_ID_RE,
"must be a string containing an eight-digit number")
add_node('body_bottom', ClickyNode)

View file

@ -8,7 +8,8 @@ import re
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import is_internal_ip, disable_html
from analytical.utils import is_internal_ip, disable_html, validate_setting, \
get_required_setting
ACCOUNT_NUMBER_RE = re.compile(r'^\d{8}$')
@ -34,10 +35,8 @@ def crazy_egg(parser, token):
return CrazyEggNode()
class CrazyEggNode(Node):
name = 'Crazy Egg'
def __init__(self):
self.account_nr = self.get_required_setting('CRAZY_EGG_ACCOUNT_NUMBER',
self.account_nr = get_required_setting('CRAZY_EGG_ACCOUNT_NUMBER',
ACCOUNT_NUMBER_RE,
"must be a string containing an eight-digit number")
@ -51,6 +50,12 @@ class CrazyEggNode(Node):
for (varnr, value) in vars)
html = '%s\n<script type="text/javascript">%s</script>' \
% (html, js)
if is_internal_ip(context):
html = disable_html(html, self.name)
if is_internal_ip(context, 'CRAZY_EGG'):
html = disable_html(html, 'Crazy Egg')
return html
def contribute_to_analytical(add_node):
validate_setting('CRAZY_EGG_ACCOUNT_NUMBER', ACCOUNT_NUMBER_RE,
"must be a string containing an eight-digit number")
add_node('body_bottom', CrazyEggNode)

View file

@ -8,7 +8,8 @@ import re
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import is_internal_ip, disable_html
from analytical.utils import is_internal_ip, disable_html, validate_setting, \
get_required_setting
SCOPE_VISITOR = 1
SCOPE_SESSION = 2
@ -30,8 +31,8 @@ SETUP_CODE = """
</script>
"""
CUSTOM_VAR_CODE = "_gaq.push(['_setCustomVar', %(index)d, '%(name)s', " \
"'%(value)s', %(scope)d]);"
CUSTOM_VAR_CODE = "_gaq.push(['_setCustomVar', %(index)s, '%(name)s', " \
"'%(value)s', %(scope)s]);"
register = Library()
@ -52,10 +53,8 @@ def google_analytics(parser, token):
return GoogleAnalyticsNode()
class GoogleAnalyticsNode(Node):
name = 'Google Analytics'
def __init__(self):
self.property_id = self.get_required_setting(
self.property_id = get_required_setting(
'GOOGLE_ANALYTICS_PROPERTY_ID', PROPERTY_ID_RE,
"must be a string looking like 'UA-XXXXXX-Y'")
@ -63,21 +62,27 @@ class GoogleAnalyticsNode(Node):
commands = self._get_custom_var_commands(context)
html = SETUP_CODE % {'property_id': self.property_id,
'commands': " ".join(commands)}
if is_internal_ip(context):
html = disable_html(html, self.name)
if is_internal_ip(context, 'GOOGLE_ANALYTICS'):
html = disable_html(html, 'Google Analytics')
return html
def _get_custom_var_commands(self, context):
values = (context.get('google_analytics_var%d' % i)
values = (context.get('google_analytics_var%s' % i)
for i in range(1, 6))
vars = [(i, v) for i, v in enumerate(values, 1) if v is not None]
commands = []
for index, value in vars:
name = value[0]
value = value[1]
for index, var in vars:
name = var[0]
value = var[1]
try:
scope = value[2]
scope = var[2]
except IndexError:
scope = SCOPE_PAGE
commands.append(CUSTOM_VAR_CODE % locals())
return commands
def contribute_to_analytical(add_node):
validate_setting('GOOGLE_ANALYTICS_PROPERTY_ID', PROPERTY_ID_RE,
"must be a string looking like 'UA-XXXXXX-Y'")
add_node('head_bottom', GoogleAnalyticsNode)

View file

@ -8,7 +8,8 @@ import re
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import get_required_setting, is_internal_ip, disable_html
from analytical.utils import is_internal_ip, disable_html, validate_setting, \
get_required_setting
PORTAL_ID_RE = re.compile(r'^\d+$')
@ -18,7 +19,7 @@ TRACKING_CODE = """
var hs_portalid = %(portal_id)s;
var hs_salog_version = "2.00";
var hs_ppa = "%(domain)s";
document.write(unescape("%3Cscript src='" + document.location.protocol + "//" + hs_ppa + "/salog.js.aspx' type='text/javascript'%3E%3C/script%3E"));
document.write(unescape("%%3Cscript src='" + document.location.protocol + "//" + hs_ppa + "/salog.js.aspx' type='text/javascript'%%3E%%3C/script%%3E"));
</script>
"""
@ -41,17 +42,23 @@ def hubspot(parser, token):
return HubSpotNode()
class HubSpotNode(Node):
name = 'HubSpot'
def __init__(self):
self.site_id = get_required_setting('HUPSPOT_PORTAL_ID',
self.portal_id = get_required_setting('HUBSPOT_PORTAL_ID',
PORTAL_ID_RE, "must be a (string containing a) number")
self.domain = get_required_setting('HUPSPOT_DOMAIN',
self.domain = get_required_setting('HUBSPOT_DOMAIN',
DOMAIN_RE, "must be an internet domain name")
def render(self, context):
html = TRACKING_CODE % {'portal_id': self.portal_id,
'domain': self.domain}
if is_internal_ip(context):
if is_internal_ip(context, 'HUBSPOT'):
html = disable_html(html, self.name)
return html
def contribute_to_analytical(add_node):
validate_setting('HUBSPOT_PORTAL_ID', PORTAL_ID_RE,
"must be a (string containing a) number")
validate_setting('HUBSPOT_DOMAIN', DOMAIN_RE,
"must be an internet domain name")
add_node('body_bottom', HubSpotNode)

View file

@ -8,6 +8,9 @@ import re
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import validate_setting, get_identity, \
get_required_setting
ACCOUNT_NUMBER_RE = re.compile(r'^\d+$')
SITE_CODE_RE = re.compile(r'^[\w]{3}$')
@ -39,18 +42,16 @@ def kiss_insights(parser, token):
return KissInsightsNode()
class KissInsightsNode(Node):
name = 'KISSinsights'
def __init__(self):
self.account_number = self.get_required_setting(
self.account_number = get_required_setting(
'KISS_INSIGHTS_ACCOUNT_NUMBER', ACCOUNT_NUMBER_RE,
"must be (a string containing) a number")
self.site_code = self.get_required_setting('KISS_INSIGHTS_SITE_CODE',
self.site_code = get_required_setting('KISS_INSIGHTS_SITE_CODE',
SITE_CODE_RE, "must be a string containing three characters")
def render(self, context):
commands = []
identity = self.get_identity(context)
identity = get_identity(context, 'kiss_insights')
if identity is not None:
commands.append(IDENTIFY_CODE % identity)
try:
@ -61,3 +62,9 @@ class KissInsightsNode(Node):
html = SETUP_CODE % {'account_number': self.account_number,
'site_code': self.site_code, 'commands': " ".join(commands)}
return html
def contribute_to_analytical(add_node):
validate_setting('KISS_INSIGHTS_ACCOUNT_NUMBER', ACCOUNT_NUMBER_RE,
"must be (a string containing) a number")
add_node('body_top', KissInsightsNode)

View file

@ -9,7 +9,8 @@ import re
from django.template import Library, Node, TemplateSyntaxError
from django.utils import simplejson
from analytical.utils import is_internal_ip, disable_html
from analytical.utils import is_internal_ip, disable_html, get_identity, \
validate_setting, get_required_setting
API_KEY_RE = re.compile(r'^[0-9a-f]{40}$')
@ -53,16 +54,14 @@ def kiss_metrics(parser, token):
return KissMetricsNode()
class KissMetricsNode(Node):
name = 'KISSmetrics'
def __init__(self):
self.api_key = self.get_required_setting('KISS_METRICS_API_KEY',
self.api_key = get_required_setting('KISS_METRICS_API_KEY',
API_KEY_RE,
"must be a string containing a 40-digit hexadecimal number")
def render(self, context):
commands = []
identity = self.get_identity(context)
identity = get_identity(context, 'kiss_metrics')
if identity is not None:
commands.append(IDENTIFY_CODE % identity)
try:
@ -73,6 +72,12 @@ class KissMetricsNode(Node):
pass
html = TRACKING_CODE % {'api_key': self.api_key,
'commands': " ".join(commands)}
if is_internal_ip(context):
html = disable_html(html, self.name)
if is_internal_ip(context, 'KISS_METRICS'):
html = disable_html(html, 'KISSmetrics')
return html
def contribute_to_analytical(add_node):
validate_setting('KISS_METRICS_API_KEY', API_KEY_RE,
"must be a string containing a 40-digit hexadecimal number")
add_node('head_top', KissMetricsNode)

View file

@ -9,7 +9,8 @@ import re
from django.template import Library, Node, TemplateSyntaxError
from django.utils import simplejson
from analytical.utils import is_internal_ip, disable_html
from analytical.utils import is_internal_ip, disable_html, get_identity, \
validate_setting, get_required_setting
MIXPANEL_TOKEN_RE = re.compile(r'^[0-9a-f]{32}$')
@ -27,7 +28,7 @@ TRACKING_CODE = """
"""
IDENTIFY_CODE = "mpq.push(['identify', '%s']);"
EVENT_CODE = "mpq.push(['track', '%(name)s', %(properties)s]);"
EVENT_CONTEXT_KEY = 'mixpanel_metrics_event'
EVENT_CONTEXT_KEY = 'mixpanel_event'
register = Library()
@ -46,16 +47,14 @@ def mixpanel(parser, token):
return MixpanelNode()
class MixpanelNode(Node):
name = 'Mixpanel'
def __init__(self):
self.token = self.get_required_setting('MIXPANEL_TOKEN',
self.token = get_required_setting('MIXPANEL_TOKEN',
MIXPANEL_TOKEN_RE,
"must be a string containing a 32-digit hexadecimal number")
def render(self, context):
commands = []
identity = self.get_identity(context)
identity = get_identity(context, 'mixpanel')
if identity is not None:
commands.append(IDENTIFY_CODE % identity)
try:
@ -66,6 +65,12 @@ class MixpanelNode(Node):
pass
html = TRACKING_CODE % {'token': self.token,
'commands': " ".join(commands)}
if is_internal_ip(context):
html = disable_html(html, self.name)
if is_internal_ip(context, 'MIXPANEL'):
html = disable_html(html, 'Mixpanel')
return html
def contribute_to_analytical(add_node):
validate_setting('MIXPANEL_TOKEN', MIXPANEL_TOKEN_RE,
"must be a string containing a 32-digit hexadecimal number")
add_node('head_bottom', MixpanelNode)

View file

@ -8,7 +8,8 @@ import re
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import is_internal_ip, disable_html
from analytical.utils import is_internal_ip, disable_html, validate_setting, \
get_required_setting
ACCOUNT_NUMBER_RE = re.compile(r'^\d{7}$')
@ -33,15 +34,19 @@ def optimizely(parser, token):
return OptimizelyNode()
class OptimizelyNode(Node):
name = 'Optimizely'
def __init__(self):
self.account_number = self.get_required_setting(
self.account_number = get_required_setting(
'OPTIMIZELY_ACCOUNT_NUMBER', ACCOUNT_NUMBER_RE,
"must be a string containing an seven-digit number")
def render(self, context):
html = SETUP_CODE % {'account_number': self.account_number}
if is_internal_ip(context):
if is_internal_ip(context, 'OPTIMIZELY'):
html = disable_html(html, self.name)
return html
def contribute_to_analytical(add_node):
validate_setting('OPTIMIZELY_ACCOUNT_NUMBER', ACCOUNT_NUMBER_RE,
"must be a string containing an seven-digit number")
add_node('head_top', OptimizelyNode)

View file

@ -2,7 +2,14 @@
Tests for django-analytical.
"""
from analytical.tests.test_services import *
from analytical.tests.test_template_tags import *
from analytical.tests.services import *
from analytical.tests.test_tag_analytical import *
from analytical.tests.test_tag_chartbeat import *
from analytical.tests.test_tag_clicky import *
from analytical.tests.test_tag_crazy_egg import *
from analytical.tests.test_tag_google_analytics import *
from analytical.tests.test_tag_hubspot import *
from analytical.tests.test_tag_kiss_insights import *
from analytical.tests.test_tag_kiss_metrics import *
from analytical.tests.test_tag_mixpanel import *
from analytical.tests.test_tag_optimizely import *
from analytical.tests.test_utils import *

View file

@ -1,14 +0,0 @@
"""
Tests for the Analytical analytics services.
"""
from analytical.tests.services.test_base import *
from analytical.tests.services.test_chartbeat import *
from analytical.tests.services.test_clicky import *
from analytical.tests.services.test_console import *
from analytical.tests.services.test_crazy_egg import *
from analytical.tests.services.test_google_analytics import *
from analytical.tests.services.test_kiss_insights import *
from analytical.tests.services.test_kiss_metrics import *
from analytical.tests.services.test_mixpanel import *
from analytical.tests.services.test_optimizely import *

View file

@ -1,74 +0,0 @@
"""
Tests for the base service.
"""
import re
from django.contrib.auth.models import User, AnonymousUser
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpRequest
from django.test import TestCase
from analytical.services.base import AnalyticalService
from analytical.tests.utils import TestSettingsManager
class DummyService(AnalyticalService):
def render_test(self, context):
return context
class BaseServiceTestCase(TestCase):
"""
Tests for the base service.
"""
def setUp(self):
self.settings_manager = TestSettingsManager()
self.service = DummyService()
def tearDown(self):
self.settings_manager.revert()
def test_render(self):
r = self.service.render('test', 'foo')
self.assertEqual('foo', r)
def test_get_required_setting(self):
self.settings_manager.set(TEST='test')
r = self.service.get_required_setting('TEST', re.compile('es'), 'fail')
self.assertEqual('test', r)
def test_get_required_setting_missing(self):
self.settings_manager.delete('TEST')
self.assertRaises(ImproperlyConfigured,
self.service.get_required_setting, 'TEST', re.compile('es'),
'fail')
def test_get_required_setting_wrong(self):
self.settings_manager.set(TEST='test')
self.assertRaises(ImproperlyConfigured,
self.service.get_required_setting, 'TEST', re.compile('foo'),
'fail')
def test_get_identity_none(self):
context = {}
self.assertEqual(None, self.service.get_identity(context))
def test_get_identity_authenticated(self):
context = {'user': User(username='test')}
self.assertEqual('test', self.service.get_identity(context))
def test_get_identity_authenticated_request(self):
req = HttpRequest()
req.user = User(username='test')
context = {'request': req}
self.assertEqual('test', self.service.get_identity(context))
def test_get_identity_anonymous(self):
context = {'user': AnonymousUser()}
self.assertEqual(None, self.service.get_identity(context))
def test_get_identity_non_user(self):
context = {'user': object()}
self.assertEqual(None, self.service.get_identity(context))

View file

@ -1,60 +0,0 @@
"""
Tests for the Clicky service.
"""
import re
from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from analytical.services.clicky import ClickyService
from analytical.tests.utils import TestSettingsManager
class ClickyTestCase(TestCase):
"""
Tests for the Clicky service.
"""
def setUp(self):
self.settings_manager = TestSettingsManager()
self.settings_manager.set(CLICKY_SITE_ID='12345678')
self.service = ClickyService()
def tearDown(self):
self.settings_manager.revert()
def test_empty_locations(self):
self.assertEqual(self.service.render_head_top({}), "")
self.assertEqual(self.service.render_head_bottom({}), "")
self.assertEqual(self.service.render_body_top({}), "")
def test_no_site_id(self):
self.settings_manager.delete('CLICKY_SITE_ID')
self.assertRaises(ImproperlyConfigured, ClickyService)
def test_wrong_site_id(self):
self.settings_manager.set(CLICKY_SITE_ID='1234567')
self.assertRaises(ImproperlyConfigured, ClickyService)
self.settings_manager.set(CLICKY_SITE_ID='123456789')
self.assertRaises(ImproperlyConfigured, ClickyService)
def test_rendering(self):
r = self.service.render_body_bottom({})
self.assertTrue('var clicky_site_id = 12345678;' in r, r)
self.assertTrue('src="http://in.getclicky.com/12345678ns.gif"' in r,
r)
def test_identify(self):
self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True)
r = self.service.render_body_bottom({'user': User(username='test')})
self.assertTrue(
'var clicky_custom = {"session": {"username": "test"}};' in r,
r)
def test_custom(self):
custom = {'var1': 'val1', 'var2': 'val2'}
r = self.service.render_body_bottom({'clicky_custom': custom})
self.assertTrue(re.search('var clicky_custom = {.*'
'"var1": "val1", "var2": "val2".*};', r), r)

View file

@ -1,45 +0,0 @@
"""
Tests for the console debugging service.
"""
from django.contrib.auth.models import User
from django.test import TestCase
from analytical.services.console import ConsoleService
class ConsoleTestCase(TestCase):
"""
Tests for the console debugging service.
"""
def setUp(self):
self.service = ConsoleService()
def test_render_head_top(self):
r = self.service.render_head_top({})
self.assertTrue('rendering analytical_head_top tag' in r, r)
r = self.service.render_head_top({'user': User(username='test')})
self.assertTrue('rendering analytical_head_top tag for user test'
in r, r)
def test_render_head_bottom(self):
r = self.service.render_head_bottom({})
self.assertTrue('rendering analytical_head_bottom tag' in r, r)
r = self.service.render_head_bottom({'user': User(username='test')})
self.assertTrue('rendering analytical_head_bottom tag for user test'
in r, r)
def test_render_body_top(self):
r = self.service.render_body_top({})
self.assertTrue('rendering analytical_body_top tag' in r, r)
r = self.service.render_body_top({'user': User(username='test')})
self.assertTrue('rendering analytical_body_top tag for user test'
in r, r)
def test_render_body_bottom(self):
r = self.service.render_body_bottom({})
self.assertTrue('rendering analytical_body_bottom tag' in r, r)
r = self.service.render_body_bottom({'user': User(username='test')})
self.assertTrue('rendering analytical_body_bottom tag for user test'
in r, r)

View file

@ -1,48 +0,0 @@
"""
Tests for the Crazy Egg service.
"""
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from analytical.services.crazy_egg import CrazyEggService
from analytical.tests.utils import TestSettingsManager
class CrazyEggTestCase(TestCase):
"""
Tests for the Crazy Egg service.
"""
def setUp(self):
self.settings_manager = TestSettingsManager()
self.settings_manager.set(CRAZY_EGG_ACCOUNT_NUMBER='12345678')
self.service = CrazyEggService()
def tearDown(self):
self.settings_manager.revert()
def test_empty_locations(self):
self.assertEqual(self.service.render_head_top({}), "")
self.assertEqual(self.service.render_head_bottom({}), "")
self.assertEqual(self.service.render_body_top({}), "")
def test_no_account_number(self):
self.settings_manager.delete('CRAZY_EGG_ACCOUNT_NUMBER')
self.assertRaises(ImproperlyConfigured, CrazyEggService)
def test_wrong_account_number(self):
self.settings_manager.set(CRAZY_EGG_ACCOUNT_NUMBER='1234567')
self.assertRaises(ImproperlyConfigured, CrazyEggService)
self.settings_manager.set(CRAZY_EGG_ACCOUNT_NUMBER='123456789')
self.assertRaises(ImproperlyConfigured, CrazyEggService)
def test_rendering(self):
r = self.service.render_body_bottom({})
self.assertTrue('/1234/5678.js' in r, r)
def test_uservars(self):
context = {'crazy_egg_uservars': {1: 'foo', 2: 'bar'}}
r = self.service.render_body_bottom(context)
self.assertTrue("CE2.set(1, 'foo');" in r, r)
self.assertTrue("CE2.set(2, 'bar');" in r, r)

View file

@ -1,56 +0,0 @@
"""
Tests for the Google Analytics service.
"""
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from analytical.services.google_analytics import GoogleAnalyticsService
from analytical.tests.utils import TestSettingsManager
class GoogleAnalyticsTestCase(TestCase):
"""
Tests for the Google Analytics service.
"""
def setUp(self):
self.settings_manager = TestSettingsManager()
self.settings_manager.set(GOOGLE_ANALYTICS_PROPERTY_ID='UA-123456-7')
self.service = GoogleAnalyticsService()
def tearDown(self):
self.settings_manager.revert()
def test_empty_locations(self):
self.assertEqual(self.service.render_head_top({}), "")
self.assertEqual(self.service.render_body_top({}), "")
self.assertEqual(self.service.render_body_bottom({}), "")
def test_no_property_id(self):
self.settings_manager.delete('GOOGLE_ANALYTICS_PROPERTY_ID')
self.assertRaises(ImproperlyConfigured, GoogleAnalyticsService)
def test_wrong_property_id(self):
self.settings_manager.set(GOOGLE_ANALYTICS_PROPERTY_ID='wrong')
self.assertRaises(ImproperlyConfigured, GoogleAnalyticsService)
def test_rendering(self):
r = self.service.render_head_bottom({})
self.assertTrue("_gaq.push(['_setAccount', 'UA-123456-7']);" in r, r)
self.assertTrue("_gaq.push(['_trackPageview']);" in r, r)
def test_custom_vars(self):
context = {'google_analytics_custom_vars': [
(1, 'test1', 'foo'),
(5, 'test2', 'bar', 1),
]}
r = self.service.render_head_bottom(context)
self.assertTrue("_gaq.push(['_setCustomVar', 1, 'test1', 'foo', 2]);"
in r, r)
self.assertTrue("_gaq.push(['_setCustomVar', 5, 'test2', 'bar', 1]);"
in r, r)
self.assertRaises(ValueError, self.service.render_head_bottom,
{'google_analytics_custom_vars': [(0, 'test', 'test')]})
self.assertRaises(ValueError, self.service.render_head_bottom,
{'google_analytics_custom_vars': [(6, 'test', 'test')]})

View file

@ -1,63 +0,0 @@
"""
Tests for the KISSinsights service.
"""
from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from analytical.services.kiss_insights import KissInsightsService
from analytical.tests.utils import TestSettingsManager
class KissInsightsTestCase(TestCase):
"""
Tests for the KISSinsights service.
"""
def setUp(self):
self.settings_manager = TestSettingsManager()
self.settings_manager.set(KISS_INSIGHTS_ACCOUNT_NUMBER='12345')
self.settings_manager.set(KISS_INSIGHTS_SITE_CODE='abc')
self.service = KissInsightsService()
def tearDown(self):
self.settings_manager.revert()
def test_empty_locations(self):
self.assertEqual(self.service.render_head_top({}), "")
self.assertEqual(self.service.render_head_bottom({}), "")
self.assertEqual(self.service.render_body_bottom({}), "")
def test_no_account_number(self):
self.settings_manager.delete('KISS_INSIGHTS_ACCOUNT_NUMBER')
self.assertRaises(ImproperlyConfigured, KissInsightsService)
def test_no_site_code(self):
self.settings_manager.delete('KISS_INSIGHTS_SITE_CODE')
self.assertRaises(ImproperlyConfigured, KissInsightsService)
def test_wrong_account_number(self):
self.settings_manager.set(KISS_INSIGHTS_ACCOUNT_NUMBER='1234')
self.assertRaises(ImproperlyConfigured, KissInsightsService)
self.settings_manager.set(KISS_INSIGHTS_ACCOUNT_NUMBER='123456')
self.assertRaises(ImproperlyConfigured, KissInsightsService)
def test_wrong_site_id(self):
self.settings_manager.set(KISS_INSIGHTS_SITE_CODE='ab')
self.assertRaises(ImproperlyConfigured, KissInsightsService)
self.settings_manager.set(KISS_INSIGHTS_SITE_CODE='abcd')
self.assertRaises(ImproperlyConfigured, KissInsightsService)
def test_rendering(self):
r = self.service.render_body_top({})
self.assertTrue("//s3.amazonaws.com/ki.js/12345/abc.js" in r, r)
def test_identify(self):
self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True)
r = self.service.render_body_top({'user': User(username='test')})
self.assertTrue("_kiq.push(['identify', 'test']);" in r, r)
def test_show_survey(self):
r = self.service.render_body_top({'kiss_insights_show_survey': 1234})
self.assertTrue("_kiq.push(['showSurvey', 1234]);" in r, r)

View file

@ -1,58 +0,0 @@
"""
Tests for the KISSmetrics service.
"""
from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from analytical.services.kiss_metrics import KissMetricsService
from analytical.tests.utils import TestSettingsManager
class KissMetricsTestCase(TestCase):
"""
Tests for the KISSmetrics service.
"""
def setUp(self):
self.settings_manager = TestSettingsManager()
self.settings_manager.set(KISS_METRICS_API_KEY='0123456789abcdef012345'
'6789abcdef01234567')
self.service = KissMetricsService()
def tearDown(self):
self.settings_manager.revert()
def test_empty_locations(self):
self.assertEqual(self.service.render_head_bottom({}), "")
self.assertEqual(self.service.render_body_top({}), "")
self.assertEqual(self.service.render_body_bottom({}), "")
def test_no_api_key(self):
self.settings_manager.delete('KISS_METRICS_API_KEY')
self.assertRaises(ImproperlyConfigured, KissMetricsService)
def test_wrong_api_key(self):
self.settings_manager.set(KISS_METRICS_API_KEY='0123456789abcdef012345'
'6789abcdef0123456')
self.assertRaises(ImproperlyConfigured, KissMetricsService)
self.settings_manager.set(KISS_METRICS_API_KEY='0123456789abcdef012345'
'6789abcdef012345678')
self.assertRaises(ImproperlyConfigured, KissMetricsService)
def test_rendering(self):
r = self.service.render_head_top({})
self.assertTrue("//doug1izaerwt3.cloudfront.net/0123456789abcdef012345"
"6789abcdef01234567.1.js" in r, r)
def test_identify(self):
self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True)
r = self.service.render_head_top({'user': User(username='test')})
self.assertTrue("_kmq.push(['identify', 'test']);" in r, r)
def test_event(self):
r = self.service.render_event('test_event', {'prop1': 'val1',
'prop2': 'val2'})
self.assertEqual(r, "_kmq.push(['record', 'test_event', "
'{"prop1": "val1", "prop2": "val2"}]);')

View file

@ -1,59 +0,0 @@
"""
Tests for the Mixpanel service.
"""
from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from analytical.services.mixpanel import MixpanelService
from analytical.tests.utils import TestSettingsManager
class MixpanelTestCase(TestCase):
"""
Tests for the Mixpanel service.
"""
def setUp(self):
self.settings_manager = TestSettingsManager()
self.settings_manager.set(
MIXPANEL_TOKEN='0123456789abcdef0123456789abcdef')
self.service = MixpanelService()
def tearDown(self):
self.settings_manager.revert()
def test_empty_locations(self):
self.assertEqual(self.service.render_head_top({}), "")
self.assertEqual(self.service.render_body_top({}), "")
self.assertEqual(self.service.render_body_bottom({}), "")
def test_no_token(self):
self.settings_manager.delete('MIXPANEL_TOKEN')
self.assertRaises(ImproperlyConfigured, MixpanelService)
def test_wrong_token(self):
self.settings_manager.set(
MIXPANEL_TOKEN='0123456789abcdef0123456789abcde')
self.assertRaises(ImproperlyConfigured, MixpanelService)
self.settings_manager.set(
MIXPANEL_TOKEN='0123456789abcdef0123456789abcdef0')
self.assertRaises(ImproperlyConfigured, MixpanelService)
def test_rendering(self):
r = self.service.render_head_bottom({})
self.assertTrue(
"mpq.push(['init', '0123456789abcdef0123456789abcdef']);" in r,
r)
def test_identify(self):
self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True)
r = self.service.render_head_bottom({'user': User(username='test')})
self.assertTrue("mpq.push(['identify', 'test']);" in r, r)
def test_event(self):
r = self.service.render_event('test_event', {'prop1': 'val1',
'prop2': 'val2'})
self.assertEqual(r, "mpq.push(['track', 'test_event', "
'{"prop1": "val1", "prop2": "val2"}]);')

View file

@ -1,42 +0,0 @@
"""
Tests for the Optimizely service.
"""
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from analytical.services.optimizely import OptimizelyService
from analytical.tests.utils import TestSettingsManager
class OptimizelyTestCase(TestCase):
"""
Tests for the Optimizely service.
"""
def setUp(self):
self.settings_manager = TestSettingsManager()
self.settings_manager.set(OPTIMIZELY_ACCOUNT_NUMBER='1234567')
self.service = OptimizelyService()
def tearDown(self):
self.settings_manager.revert()
def test_empty_locations(self):
self.assertEqual(self.service.render_head_bottom({}), "")
self.assertEqual(self.service.render_body_top({}), "")
self.assertEqual(self.service.render_body_bottom({}), "")
def test_no_account_number(self):
self.settings_manager.delete('OPTIMIZELY_ACCOUNT_NUMBER')
self.assertRaises(ImproperlyConfigured, OptimizelyService)
def test_wrong_account_number(self):
self.settings_manager.set(OPTIMIZELY_ACCOUNT_NUMBER='123456')
self.assertRaises(ImproperlyConfigured, OptimizelyService)
self.settings_manager.set(OPTIMIZELY_ACCOUNT_NUMBER='12345678')
self.assertRaises(ImproperlyConfigured, OptimizelyService)
def test_rendering(self):
self.assertEqual(self.service.render_head_top({}),
'<script src="//cdn.optimizely.com/js/1234567.js"></script>')

View file

@ -0,0 +1,38 @@
"""
Dummy testing template tags and filters.
"""
from __future__ import absolute_import
from django.template import Library, Node, TemplateSyntaxError
from analytical.templatetags.analytical import TAG_LOCATIONS
register = Library()
def _location_node(location):
class DummyNode(Node):
def render(self, context):
return "<!-- dummy_%s -->" % location
return DummyNode
_location_nodes = dict((l, _location_node(l)) for l in TAG_LOCATIONS)
def _location_tag(location):
def dummy_tag(parser, token):
bits = token.split_contents()
if len(bits) > 1:
raise TemplateSyntaxError("'%s' tag takes no arguments" % bits[0])
return _location_nodes[location]
return dummy_tag
for loc in TAG_LOCATIONS:
register.tag('dummy_%s' % loc, _location_tag(loc))
def contribute_to_analytical(add_node_cls):
for location in TAG_LOCATIONS:
add_node_cls(location, _location_nodes[location])

View file

@ -1,80 +0,0 @@
"""
Tests for the services package.
"""
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from analytical import services
from analytical.services import load_services
from analytical.tests.utils import TestSettingsManager
from analytical.services.google_analytics import GoogleAnalyticsService
class GetEnabledServicesTestCase(TestCase):
"""
Tests for get_enabled_services.
"""
def setUp(self):
services.enabled_services = None
services.load_services = lambda: 'test'
def tearDown(self):
services.enabled_services = None
services.load_services = load_services
def test_get_enabled_services(self):
result = services.get_enabled_services()
self.assertEqual(result, 'test')
services.load_services = lambda: 'test2'
result = services.get_enabled_services()
self.assertEqual(result, 'test')
class LoadServicesTestCase(TestCase):
"""
Tests for load_services.
"""
def setUp(self):
self.settings_manager = TestSettingsManager()
self.settings_manager.delete('ANALYTICAL_SERVICES')
self.settings_manager.delete('CLICKY_SITE_ID')
self.settings_manager.delete('CHARTBEAT_USER_ID')
self.settings_manager.delete('CRAZY_EGG_ACCOUNT_NUMBER')
self.settings_manager.delete('GOOGLE_ANALYTICS_PROPERTY_ID')
self.settings_manager.delete('KISSINSIGHTS_ACCOUNT_NUMBER')
self.settings_manager.delete('KISSINSIGHTS_SITE_CODE')
self.settings_manager.delete('KISSMETRICS_API_KEY')
self.settings_manager.delete('MIXPANEL_TOKEN')
self.settings_manager.delete('OPTIMIZELY_ACCOUNT_NUMBER')
services.enabled_services = None
def tearDown(self):
self.settings_manager.revert()
services.enabled_services = None
def test_no_services(self):
self.assertEqual(load_services(), [])
def test_enabled_service(self):
self.settings_manager.set(GOOGLE_ANALYTICS_PROPERTY_ID='UA-1234567-8')
results = load_services()
self.assertEqual(len(results), 1, results)
self.assertTrue(isinstance(results[0], GoogleAnalyticsService),
results)
def test_explicit_service(self):
self.settings_manager.set(ANALYTICAL_SERVICES=[
'analytical.services.google_analytics.GoogleAnalyticsService'])
self.settings_manager.set(GOOGLE_ANALYTICS_PROPERTY_ID='UA-1234567-8')
results = load_services()
self.assertEqual(len(results), 1, results)
self.assertTrue(isinstance(results[0], GoogleAnalyticsService),
results)
def test_explicit_service_misconfigured(self):
self.settings_manager.set(ANALYTICAL_SERVICES=[
'analytical.services.google_analytics.GoogleAnalyticsService'])
self.assertRaises(ImproperlyConfigured, load_services)

View file

@ -0,0 +1,37 @@
"""
Tests for the generic template tags and filters.
"""
from django.template import Context, Template
from analytical.templatetags import analytical
from analytical.tests.utils import TagTestCase
class AnalyticsTagTestCase(TagTestCase):
"""
Tests for the ``analytical`` template tags.
"""
def setUp(self):
super(AnalyticsTagTestCase, self).setUp()
self._tag_modules = analytical.TAG_MODULES
analytical.TAG_MODULES = ['analytical.tests.dummy']
analytical.template_nodes = analytical._load_template_nodes()
def tearDown(self):
analytical.TAG_MODULES = self._tag_modules
analytical.template_nodes = analytical._load_template_nodes()
super(AnalyticsTagTestCase, self).tearDown()
def render_location_tag(self, location, vars=None):
if vars is None:
vars = {}
t = Template("{%% load analytical %%}{%% analytical_%s %%}"
% location)
return t.render(Context(vars))
def test_location_tags(self):
for l in ['head_top', 'head_bottom', 'body_top', 'body_bottom']:
r = self.render_location_tag(l)
self.assertTrue('dummy_%s' % l in r, r)

View file

@ -1,62 +1,69 @@
"""
Tests for the Chartbeat service.
Tests for the Chartbeat template tags and filters.
"""
import re
from django.conf import settings
from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpRequest
from django.test import TestCase
from django.template import Context
from analytical.services.chartbeat import ChartbeatService
from analytical.tests.utils import TestSettingsManager
from analytical.templatetags.chartbeat import ChartbeatTopNode, \
ChartbeatBottomNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
class ChartbeatTestCase(TestCase):
class ChartbeatTagTestCase(TagTestCase):
"""
Tests for the Chartbeat service.
Tests for the ``chartbeat`` template tag.
"""
def setUp(self):
self.settings_manager = TestSettingsManager()
super(ChartbeatTagTestCase, self).setUp()
self.settings_manager.set(CHARTBEAT_USER_ID='12345')
self.service = ChartbeatService()
def tearDown(self):
self.settings_manager.revert()
def test_empty_locations(self):
self.assertEqual(self.service.render_head_bottom({}), "")
self.assertEqual(self.service.render_body_top({}), "")
def test_no_user_id(self):
self.settings_manager.delete('CHARTBEAT_USER_ID')
self.assertRaises(ImproperlyConfigured, ChartbeatService)
def test_wrong_user_id(self):
self.settings_manager.set(CHARTBEAT_USER_ID='1234')
self.assertRaises(ImproperlyConfigured, ChartbeatService)
self.settings_manager.set(CHARTBEAT_USER_ID='123456')
self.assertRaises(ImproperlyConfigured, ChartbeatService)
def test_rendering_init(self):
r = self.service.render_head_top({})
def test_top_tag(self):
r = self.render_tag('chartbeat', 'chartbeat_top',
{'chartbeat_domain': "test.com"})
self.assertTrue('var _sf_startpt=(new Date()).getTime()' in r, r)
def test_rendering_setup(self):
r = self.service.render_body_bottom({'chartbeat_domain': "test.com"})
def test_bottom_tag(self):
r = self.render_tag('chartbeat', 'chartbeat_bottom',
{'chartbeat_domain': "test.com"})
self.assertTrue(re.search(
'var _sf_async_config={.*"uid": "12345".*};', r), r)
self.assertTrue(re.search(
'var _sf_async_config={.*"domain": "test.com".*};', r), r)
def test_top_node(self):
r = ChartbeatTopNode().render(
Context({'chartbeat_domain': "test.com"}))
self.assertTrue('var _sf_startpt=(new Date()).getTime()' in r, r)
def test_bottom_node(self):
r = ChartbeatBottomNode().render(
Context({'chartbeat_domain': "test.com"}))
self.assertTrue(re.search(
'var _sf_async_config={.*"uid": "12345".*};', r), r)
self.assertTrue(re.search(
'var _sf_async_config={.*"domain": "test.com".*};', r), r)
def test_no_user_id(self):
self.settings_manager.delete('CHARTBEAT_USER_ID')
self.assertRaises(AnalyticalException, ChartbeatBottomNode)
def test_wrong_user_id(self):
self.settings_manager.set(CHARTBEAT_USER_ID='1234')
self.assertRaises(AnalyticalException, ChartbeatBottomNode)
self.settings_manager.set(CHARTBEAT_USER_ID='123456')
self.assertRaises(AnalyticalException, ChartbeatBottomNode)
def test_rendering_setup_no_site(self):
installed_apps = [a for a in settings.INSTALLED_APPS
if a != 'django.contrib.sites']
self.settings_manager.set(INSTALLED_APPS=installed_apps)
r = self.service.render_body_bottom({})
r = ChartbeatBottomNode().render(Context())
self.assertTrue('var _sf_async_config={"uid": "12345"};' in r, r)
def test_rendering_setup_site(self):
@ -65,7 +72,7 @@ class ChartbeatTestCase(TestCase):
self.settings_manager.set(INSTALLED_APPS=installed_apps)
site = Site.objects.create(domain="test.com", name="test")
self.settings_manager.set(SITE_ID=site.id)
r = self.service.render_body_bottom({})
r = ChartbeatBottomNode().render(Context())
self.assertTrue(re.search(
'var _sf_async_config={.*"uid": "12345".*};', r), r)
self.assertTrue(re.search(

View file

@ -0,0 +1,57 @@
"""
Tests for the Clicky template tags and filters.
"""
import re
from django.contrib.auth.models import User
from django.template import Context
from analytical.templatetags.clicky import ClickyNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
class ClickyTagTestCase(TagTestCase):
"""
Tests for the ``clicky`` template tag.
"""
def setUp(self):
super(ClickyTagTestCase, self).setUp()
self.settings_manager.set(CLICKY_SITE_ID='12345678')
def test_tag(self):
r = self.render_tag('clicky', 'clicky')
self.assertTrue('var clicky_site_id = 12345678;' in r, r)
self.assertTrue('src="http://in.getclicky.com/12345678ns.gif"' in r,
r)
def test_node(self):
r = ClickyNode().render(Context({}))
self.assertTrue('var clicky_site_id = 12345678;' in r, r)
self.assertTrue('src="http://in.getclicky.com/12345678ns.gif"' in r,
r)
def test_no_site_id(self):
self.settings_manager.delete('CLICKY_SITE_ID')
self.assertRaises(AnalyticalException, ClickyNode)
def test_wrong_site_id(self):
self.settings_manager.set(CLICKY_SITE_ID='1234567')
self.assertRaises(AnalyticalException, ClickyNode)
self.settings_manager.set(CLICKY_SITE_ID='123456789')
self.assertRaises(AnalyticalException, ClickyNode)
def test_identify(self):
self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True)
r = ClickyNode().render(Context({'user': User(username='test')}))
self.assertTrue(
'var clicky_custom = {"session": {"username": "test"}};' in r,
r)
def test_custom(self):
r = ClickyNode().render(Context({'clicky_var1': 'val1',
'clicky_var2': 'val2'}))
self.assertTrue(re.search('var clicky_custom = {.*'
'"var1": "val1", "var2": "val2".*};', r), r)

View file

@ -0,0 +1,43 @@
"""
Tests for the Crazy Egg template tags and filters.
"""
from django.template import Context
from analytical.templatetags.crazy_egg import CrazyEggNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
class CrazyEggTagTestCase(TagTestCase):
"""
Tests for the ``crazy_egg`` template tag.
"""
def setUp(self):
super(CrazyEggTagTestCase, self).setUp()
self.settings_manager.set(CRAZY_EGG_ACCOUNT_NUMBER='12345678')
def test_tag(self):
r = self.render_tag('crazy_egg', 'crazy_egg')
self.assertTrue('/1234/5678.js' in r, r)
def test_node(self):
r = CrazyEggNode().render(Context())
self.assertTrue('/1234/5678.js' in r, r)
def test_no_account_number(self):
self.settings_manager.delete('CRAZY_EGG_ACCOUNT_NUMBER')
self.assertRaises(AnalyticalException, CrazyEggNode)
def test_wrong_account_number(self):
self.settings_manager.set(CRAZY_EGG_ACCOUNT_NUMBER='1234567')
self.assertRaises(AnalyticalException, CrazyEggNode)
self.settings_manager.set(CRAZY_EGG_ACCOUNT_NUMBER='123456789')
self.assertRaises(AnalyticalException, CrazyEggNode)
def test_uservars(self):
context = Context({'crazy_egg_var1': 'foo', 'crazy_egg_var2': 'bar'})
r = CrazyEggNode().render(context)
self.assertTrue("CE2.set(1, 'foo');" in r, r)
self.assertTrue("CE2.set(2, 'bar');" in r, r)

View file

@ -0,0 +1,46 @@
"""
Tests for the Google Analytics template tags and filters.
"""
from django.template import Context
from analytical.templatetags.google_analytics import GoogleAnalyticsNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
class GoogleAnalyticsTagTestCase(TagTestCase):
"""
Tests for the ``google_analytics`` template tag.
"""
def setUp(self):
super(GoogleAnalyticsTagTestCase, self).setUp()
self.settings_manager.set(GOOGLE_ANALYTICS_PROPERTY_ID='UA-123456-7')
def test_tag(self):
r = self.render_tag('google_analytics', 'google_analytics')
self.assertTrue("_gaq.push(['_setAccount', 'UA-123456-7']);" in r, r)
self.assertTrue("_gaq.push(['_trackPageview']);" in r, r)
def test_node(self):
r = GoogleAnalyticsNode().render(Context())
self.assertTrue("_gaq.push(['_setAccount', 'UA-123456-7']);" in r, r)
self.assertTrue("_gaq.push(['_trackPageview']);" in r, r)
def test_no_property_id(self):
self.settings_manager.delete('GOOGLE_ANALYTICS_PROPERTY_ID')
self.assertRaises(AnalyticalException, GoogleAnalyticsNode)
def test_wrong_property_id(self):
self.settings_manager.set(GOOGLE_ANALYTICS_PROPERTY_ID='wrong')
self.assertRaises(AnalyticalException, GoogleAnalyticsNode)
def test_custom_vars(self):
context = Context({'google_analytics_var1': ('test1', 'foo'),
'google_analytics_var5': ('test2', 'bar', 1)})
r = GoogleAnalyticsNode().render(context)
self.assertTrue("_gaq.push(['_setCustomVar', 1, 'test1', 'foo', 3]);"
in r, r)
self.assertTrue("_gaq.push(['_setCustomVar', 5, 'test2', 'bar', 1]);"
in r, r)

View file

@ -0,0 +1,46 @@
"""
Tests for the HubSpot template tags and filters.
"""
from django.template import Context
from analytical.templatetags.hubspot import HubSpotNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
class HubSpotTagTestCase(TagTestCase):
"""
Tests for the ``hubspot`` template tag.
"""
def setUp(self):
super(HubSpotTagTestCase, self).setUp()
self.settings_manager.set(HUBSPOT_PORTAL_ID='1234')
self.settings_manager.set(HUBSPOT_DOMAIN='example.com')
def test_tag(self):
r = self.render_tag('hubspot', 'hubspot')
self.assertTrue('var hs_portalid = 1234;' in r, r)
self.assertTrue('var hs_ppa = "example.com";' in r, r)
def test_node(self):
r = HubSpotNode().render(Context())
self.assertTrue('var hs_portalid = 1234;' in r, r)
self.assertTrue('var hs_ppa = "example.com";' in r, r)
def test_no_portal_id(self):
self.settings_manager.delete('HUBSPOT_PORTAL_ID')
self.assertRaises(AnalyticalException, HubSpotNode)
def test_wrong_portal_id(self):
self.settings_manager.set(HUBSPOT_PORTAL_ID='wrong')
self.assertRaises(AnalyticalException, HubSpotNode)
def test_no_domain(self):
self.settings_manager.delete('HUBSPOT_DOMAIN')
self.assertRaises(AnalyticalException, HubSpotNode)
def test_wrong_domain(self):
self.settings_manager.set(HUBSPOT_DOMAIN='wrong domain')
self.assertRaises(AnalyticalException, HubSpotNode)

View file

@ -0,0 +1,57 @@
"""
Tests for the KISSinsights template tags and filters.
"""
from django.contrib.auth.models import User
from django.template import Context
from analytical.templatetags.kiss_insights import KissInsightsNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
class KissInsightsTagTestCase(TagTestCase):
"""
Tests for the ``kiss_insights`` template tag.
"""
def setUp(self):
super(KissInsightsTagTestCase, self).setUp()
self.settings_manager.set(KISS_INSIGHTS_ACCOUNT_NUMBER='12345')
self.settings_manager.set(KISS_INSIGHTS_SITE_CODE='abc')
def test_tag(self):
r = self.render_tag('kiss_insights', 'kiss_insights')
self.assertTrue("//s3.amazonaws.com/ki.js/12345/abc.js" in r, r)
def test_node(self):
r = KissInsightsNode().render(Context())
self.assertTrue("//s3.amazonaws.com/ki.js/12345/abc.js" in r, r)
def test_no_account_number(self):
self.settings_manager.delete('KISS_INSIGHTS_ACCOUNT_NUMBER')
self.assertRaises(AnalyticalException, KissInsightsNode)
def test_no_site_code(self):
self.settings_manager.delete('KISS_INSIGHTS_SITE_CODE')
self.assertRaises(AnalyticalException, KissInsightsNode)
def test_wrong_account_number(self):
self.settings_manager.set(KISS_INSIGHTS_ACCOUNT_NUMBER='abcde')
self.assertRaises(AnalyticalException, KissInsightsNode)
def test_wrong_site_id(self):
self.settings_manager.set(KISS_INSIGHTS_SITE_CODE='ab')
self.assertRaises(AnalyticalException, KissInsightsNode)
self.settings_manager.set(KISS_INSIGHTS_SITE_CODE='abcd')
self.assertRaises(AnalyticalException, KissInsightsNode)
def test_identify(self):
self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True)
r = KissInsightsNode().render(Context({'user': User(username='test')}))
self.assertTrue("_kiq.push(['identify', 'test']);" in r, r)
def test_show_survey(self):
r = KissInsightsNode().render(
Context({'kiss_insights_show_survey': 1234}))
self.assertTrue("_kiq.push(['showSurvey', 1234]);" in r, r)

View file

@ -0,0 +1,54 @@
"""
Tests for the KISSmetrics tags and filters.
"""
from django.contrib.auth.models import User
from django.template import Context
from analytical.templatetags.kiss_metrics import KissMetricsNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
class KissMetricsTagTestCase(TagTestCase):
"""
Tests for the ``kiss_metrics`` template tag.
"""
def setUp(self):
super(KissMetricsTagTestCase, self).setUp()
self.settings_manager.set(KISS_METRICS_API_KEY='0123456789abcdef012345'
'6789abcdef01234567')
def test_tag(self):
r = self.render_tag('kiss_metrics', 'kiss_metrics')
self.assertTrue("//doug1izaerwt3.cloudfront.net/0123456789abcdef012345"
"6789abcdef01234567.1.js" in r, r)
def test_node(self):
r = KissMetricsNode().render(Context())
self.assertTrue("//doug1izaerwt3.cloudfront.net/0123456789abcdef012345"
"6789abcdef01234567.1.js" in r, r)
def test_no_api_key(self):
self.settings_manager.delete('KISS_METRICS_API_KEY')
self.assertRaises(AnalyticalException, KissMetricsNode)
def test_wrong_api_key(self):
self.settings_manager.set(KISS_METRICS_API_KEY='0123456789abcdef012345'
'6789abcdef0123456')
self.assertRaises(AnalyticalException, KissMetricsNode)
self.settings_manager.set(KISS_METRICS_API_KEY='0123456789abcdef012345'
'6789abcdef012345678')
self.assertRaises(AnalyticalException, KissMetricsNode)
def test_identify(self):
self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True)
r = KissMetricsNode().render(Context({'user': User(username='test')}))
self.assertTrue("_kmq.push(['identify', 'test']);" in r, r)
def test_event(self):
r = KissMetricsNode().render(Context({'kiss_metrics_event':
('test_event', {'prop1': 'val1', 'prop2': 'val2'})}))
self.assertTrue("_kmq.push(['record', 'test_event', "
'{"prop1": "val1", "prop2": "val2"}]);' in r, r)

View file

@ -0,0 +1,57 @@
"""
Tests for the Mixpanel tags and filters.
"""
from django.contrib.auth.models import User
from django.template import Context
from analytical.templatetags.mixpanel import MixpanelNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
class MixpanelTagTestCase(TagTestCase):
"""
Tests for the ``mixpanel`` template tag.
"""
def setUp(self):
super(MixpanelTagTestCase, self).setUp()
self.settings_manager.set(OPTIMIZELY_ACCOUNT_NUMBER='1234567')
self.settings_manager.set(
MIXPANEL_TOKEN='0123456789abcdef0123456789abcdef')
def test_tag(self):
r = self.render_tag('mixpanel', 'mixpanel')
self.assertTrue(
"mpq.push(['init', '0123456789abcdef0123456789abcdef']);" in r,
r)
def test_node(self):
r = MixpanelNode().render(Context())
self.assertTrue(
"mpq.push(['init', '0123456789abcdef0123456789abcdef']);" in r,
r)
def test_no_token(self):
self.settings_manager.delete('MIXPANEL_TOKEN')
self.assertRaises(AnalyticalException, MixpanelNode)
def test_wrong_token(self):
self.settings_manager.set(
MIXPANEL_TOKEN='0123456789abcdef0123456789abcde')
self.assertRaises(AnalyticalException, MixpanelNode)
self.settings_manager.set(
MIXPANEL_TOKEN='0123456789abcdef0123456789abcdef0')
self.assertRaises(AnalyticalException, MixpanelNode)
def test_identify(self):
self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True)
r = MixpanelNode().render(Context({'user': User(username='test')}))
self.assertTrue("mpq.push(['identify', 'test']);" in r, r)
def test_event(self):
r = MixpanelNode().render(Context({'mixpanel_event':
('test_event', {'prop1': 'val1', 'prop2': 'val2'})}))
self.assertTrue("mpq.push(['track', 'test_event', "
'{"prop1": "val1", "prop2": "val2"}]);' in r, r)

View file

@ -0,0 +1,39 @@
"""
Tests for the Optimizely template tags and filters.
"""
from django.template import Context
from analytical.templatetags.optimizely import OptimizelyNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
class OptimizelyTagTestCase(TagTestCase):
"""
Tests for the ``optimizely`` template tag.
"""
def setUp(self):
super(OptimizelyTagTestCase, self).setUp()
self.settings_manager.set(OPTIMIZELY_ACCOUNT_NUMBER='1234567')
def test_tag(self):
self.assertEqual(
'<script src="//cdn.optimizely.com/js/1234567.js"></script>',
self.render_tag('optimizely', 'optimizely'))
def test_node(self):
self.assertEqual(
'<script src="//cdn.optimizely.com/js/1234567.js"></script>',
OptimizelyNode().render(Context()))
def test_no_account_number(self):
self.settings_manager.delete('OPTIMIZELY_ACCOUNT_NUMBER')
self.assertRaises(AnalyticalException, OptimizelyNode)
def test_wrong_account_number(self):
self.settings_manager.set(OPTIMIZELY_ACCOUNT_NUMBER='123456')
self.assertRaises(AnalyticalException, OptimizelyNode)
self.settings_manager.set(OPTIMIZELY_ACCOUNT_NUMBER='12345678')
self.assertRaises(AnalyticalException, OptimizelyNode)

View file

@ -1,90 +0,0 @@
"""
Tests for the template tags.
"""
from django.http import HttpRequest
from django import template
from django.test import TestCase
from analytical import services
from analytical.tests.utils import TestSettingsManager
class TemplateTagsTestCase(TestCase):
"""
Tests for the template tags.
"""
def setUp(self):
self.settings_manager = TestSettingsManager()
self.settings_manager.set(ANALYTICAL_SERVICES=[
'analytical.services.console.ConsoleService'])
services.enabled_services = None
def tearDown(self):
self.settings_manager.revert()
services.enabled_services = None
def render_location_tag(self, location, context=None):
if context is None: context = {}
t = template.Template(
"{%% load analytical %%}{%% analytical_setup_%s %%}"
% location)
return t.render(template.Context(context))
def test_location_tags(self):
for l in ['head_top', 'head_bottom', 'body_top', 'body_bottom']:
r = self.render_location_tag(l)
self.assertTrue('rendering analytical_%s tag' % l in r, r)
def test_render_internal_ip(self):
self.settings_manager.set(ANALYTICAL_INTERNAL_IPS=['1.1.1.1'])
req = HttpRequest()
req.META['REMOTE_ADDR'] = '1.1.1.1'
for l in ['head_top', 'head_bottom', 'body_top', 'body_bottom']:
r = self.render_location_tag(l, {'request': req})
self.assertTrue('<!-- Analytical disabled on internal IP address'
in r, r)
def test_render_internal_ip_fallback(self):
self.settings_manager.set(INTERNAL_IPS=['1.1.1.1'])
req = HttpRequest()
req.META['REMOTE_ADDR'] = '1.1.1.1'
for l in ['head_top', 'head_bottom', 'body_top', 'body_bottom']:
r = self.render_location_tag(l, {'request': req})
self.assertTrue('<!-- Analytical disabled on internal IP address'
in r, r)
def test_render_internal_ip_forwarded_for(self):
self.settings_manager.set(ANALYTICAL_INTERNAL_IPS=['1.1.1.1'])
req = HttpRequest()
req.META['HTTP_X_FORWARDED_FOR'] = '1.1.1.1'
for l in ['head_top', 'head_bottom', 'body_top', 'body_bottom']:
r = self.render_location_tag(l, {'request': req})
self.assertTrue('<!-- Analytical disabled on internal IP address'
in r, r)
def test_render_different_internal_ip(self):
self.settings_manager.set(ANALYTICAL_INTERNAL_IPS=['1.1.1.1'])
req = HttpRequest()
req.META['REMOTE_ADDR'] = '2.2.2.2'
for l in ['head_top', 'head_bottom', 'body_top', 'body_bottom']:
r = self.render_location_tag(l, {'request': req})
self.assertFalse('<!-- Analytical disabled on internal IP address'
in r, r)
def test_render_internal_ip_empty(self):
self.settings_manager.set(ANALYTICAL_INTERNAL_IPS=['1.1.1.1'])
self.settings_manager.delete('ANALYTICAL_SERVICES')
self.settings_manager.delete('CLICKY_SITE_ID')
self.settings_manager.delete('CRAZY_EGG_ACCOUNT_NUMBER')
self.settings_manager.delete('GOOGLE_ANALYTICS_PROPERTY_ID')
self.settings_manager.delete('KISSINSIGHTS_ACCOUNT_NUMBER')
self.settings_manager.delete('KISSINSIGHTS_SITE_CODE')
self.settings_manager.delete('KISSMETRICS_API_KEY')
self.settings_manager.delete('MIXPANEL_TOKEN')
self.settings_manager.delete('OPTIMIZELY_ACCOUNT_NUMBER')
req = HttpRequest()
req.META['REMOTE_ADDR'] = '1.1.1.1'
for l in ['head_top', 'head_bottom', 'body_top', 'body_bottom']:
self.assertEqual(self.render_location_tag(l, {'request': req}), "")

View file

@ -0,0 +1,3 @@
"""
Tests for the analytical.utils module.
"""

View file

@ -5,14 +5,40 @@ Testing utilities.
from django.conf import settings
from django.core.management import call_command
from django.db.models import loading
from django.template import Template, Context, RequestContext
from django.test.simple import run_tests as django_run_tests
from django.test.testcases import TestCase
def run_tests():
def run_tests(labels=()):
"""
Use the Django test runner to run the tests.
"""
django_run_tests([], verbosity=1, interactive=True)
django_run_tests(labels, verbosity=1, interactive=True)
class TagTestCase(TestCase):
"""
Tests for a template tag.
Includes the settings manager.
"""
def setUp(self):
self.settings_manager = TestSettingsManager()
def tearDown(self):
self.settings_manager.revert()
def render_tag(self, library, tag, vars=None, request=None):
if vars is None:
vars = {}
t = Template("{%% load %s %%}{%% %s %%}" % (library, tag))
if request is not None:
context = RequestContext(request, vars)
else:
context = Context(vars)
return t.render(context)
class TestSettingsManager(object):

View file

@ -6,25 +6,36 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
IDENTITY_CONTEXT_KEY = 'analytical_identity'
HTML_COMMENT = "<!-- %(service)s disabled on internal IP address\n%(html)\n-->"
def get_required_setting(self, setting, value_re, invalid_msg):
def validate_setting(setting, value_re, invalid_msg):
try:
get_required_setting(setting, value_re, invalid_msg)
except AnalyticalException, e:
raise ImproperlyConfigured(e)
def get_required_setting(setting, value_re, invalid_msg):
try:
value = getattr(settings, setting)
except AttributeError:
raise ImproperlyConfigured("%s setting: not found" % setting)
raise AnalyticalException("%s setting: not found" % setting)
value = str(value)
if not value_re.search(value):
raise ImproperlyConfigured("%s setting: %s: '%s'"
raise AnalyticalException("%s setting: %s: '%s'"
% (setting, invalid_msg, value))
return value
def get_identity(context):
def get_identity(context, prefix=None):
if prefix is not None:
try:
return context['%s_identity' % prefix]
except KeyError:
pass
try:
return context[IDENTITY_CONTEXT_KEY]
return context['analytical_identity']
except KeyError:
pass
if getattr(settings, 'ANALYTICAL_AUTO_IDENTIFY', True):
@ -41,16 +52,33 @@ def get_identity(context):
return None
def is_internal_ip(context):
def is_internal_ip(context, prefix=None):
try:
request = context['request']
remote_ip = request.META.get('HTTP_X_FORWARDED_FOR',
request.META.get('REMOTE_ADDR', ''))
return remote_ip in getattr(settings, 'ANALYTICAL_INTERNAL_IPS',
getattr(settings, 'INTERNAL_IPS', []))
remote_ip = request.META.get('HTTP_X_FORWARDED_FOR', '')
if not remote_ip:
remote_ip = request.META.get('HTTP_X_FORWARDED_FOR', '')
internal_ips = ''
if prefix is not None:
internal_ips = getattr(settings, '%s_INTERNAL_IPS' % prefix, '')
if not internal_ips:
internal_ips = getattr(settings, 'ANALYTICAL_INTERNAL_IPS', '')
if not internal_ips:
internal_ips = getattr(settings, 'INTERNAL_IPS', '')
return remote_ip in internal_ips
except KeyError, AttributeError:
return False
def disable_html(self, html, service):
return HTML_COMMENT % locals()
class AnalyticalException(Exception):
"""
Raised when an exception occurs in any django-analytical code that should
be silenced in templates.
"""
silent_variable_failure = True

View file

@ -17,6 +17,7 @@ except ImportError:
class TestCommand(Command):
description = "run package tests"
user_options = []
def initialize_options(self):