combo/tests/test_cells.py

1677 lines
63 KiB
Python

import datetime
import json
import logging
import os
import re
from unittest import mock
import pytest
import requests
from django.apps import apps
from django.conf import settings
from django.contrib.auth.models import User
from django.db import connection
from django.forms.widgets import Media
from django.template.exceptions import TemplateDoesNotExist
from django.test import override_settings
from django.test.client import RequestFactory
from django.test.utils import CaptureQueriesContext
from django.urls import reverse
from django.utils.encoding import force_bytes, force_text
from django.utils.timezone import now
from combo.data.library import get_cell_classes
from combo.data.models import (
CellBase,
ConfigJsonCell,
FeedCell,
JsonCell,
LinkCell,
LinkListCell,
MenuCell,
Page,
TextCell,
ValidityInfo,
)
from combo.utils import NothingInCacheException
from .test_manager import login
pytestmark = pytest.mark.django_db
@pytest.fixture
def context():
ctx = {'request': RequestFactory().get('/')}
ctx['request'].user = None
ctx['request'].session = {}
return ctx
def mock_json_response(content, **kwargs):
content = force_bytes(content)
text = force_text(content)
return mock.Mock(content=content, text=text, json=lambda: json.loads(text), **kwargs)
def test_cell_reference():
page = Page()
page.save()
cell = TextCell()
cell.page = page
cell.text = 'foobar'
cell.order = 0
cell.save()
assert CellBase.get_cell(cell.get_reference()) == cell
def test_media():
class TextCelleWithMedia(TextCell):
class Media:
js = ['coincoin.js']
class Meta:
# to prevent error in Models metaclass as the current module is not
# in a registered applicatoin
proxy = True
app_label = 'data'
cells = [TextCelleWithMedia() for i in range(3)]
assert (
force_text(sum((cell.media for cell in cells), Media()))
== '<script src="/static/coincoin.js"></script>'
)
def test_additional_label():
page = Page()
page.save()
cell = TextCell()
cell.page = page
cell.text = '<p>foobar</p>'
cell.order = 0
cell.save()
assert cell.get_additional_label() == 'foobar'
cell = TextCell()
cell.page = page
cell.text = '<p>%s</p>' % 'foo' * 30
cell.order = 0
cell.save()
assert len(cell.get_additional_label()) < 100
assert '...' in cell.get_additional_label()
def test_text_cell_variadic_url():
page = Page()
page.save()
cell = TextCell(page=page, order=0)
cell.text = '<a href="{{test_url}}">test</a> <a href="/plop">test2</a> <img src="{{test_url}}"'
cell.save()
with override_settings(TEMPLATE_VARS={'test_url': 'http://example.net'}):
ctx = {}
assert 'href="http://example.net"' in cell.render(ctx)
assert 'href="/plop"' in cell.render(ctx)
assert 'src="http://example.net"' in cell.render(ctx)
# consider unknown variables as empty
assert 'href=""' in cell.render(ctx)
assert 'href="/plop"' in cell.render(ctx)
def test_link_cell():
page = Page(title='example page', slug='example-page')
page.save()
cell = LinkCell()
cell.page = page
cell.title = 'Example Site'
cell.url = 'http://example.net/'
cell.order = 0
cell.save()
assert cell.get_additional_label() == 'Example Site'
ctx = {}
assert cell.render(ctx).strip() == '<a href="http://example.net/">Example Site</a>'
cell.title = ''
cell.save()
assert cell.render(ctx).strip() == '<a href="http://example.net/">http://example.net/</a>'
cell.link_page = page
cell.save()
assert cell.render(ctx).strip() == '<a href="/example-page/">example page</a>'
cell.title = 'altertitle'
cell.save()
assert cell.render(ctx).strip() == '<a href="/example-page/">altertitle</a>'
cell.anchor = 'anchor'
cell.save()
assert cell.render(ctx).strip() == '<a href="/example-page/#anchor">altertitle</a>'
cell.link_page = None
cell.save()
assert cell.render(ctx).strip() == '<a href="http://example.net/#anchor">altertitle</a>'
@pytest.mark.parametrize('bypass', [True, False])
def test_link_cell_validity(settings, bypass):
page = Page.objects.create(title='example page', slug='example-page')
cell = LinkCell.objects.create(
page=page,
title='Example Site',
order=0,
bypass_url_validity_check=bypass,
)
# no link defined
if bypass:
assert ValidityInfo.objects.exists() is False
else:
validity_info = ValidityInfo.objects.latest('pk')
assert validity_info.invalid_reason_code == 'data_url_not_defined'
assert validity_info.invalid_since is not None
# no link defined but anchor set
cell.anchor = 'plop'
cell.save()
assert ValidityInfo.objects.exists() is False
# internal link - no check
cell.link_page = page
with mock.patch('combo.data.models.requests.get') as requests_get:
mock_json = mock.Mock(status_code=404)
requests_get.return_value = mock_json
cell.save()
assert requests_get.call_args_list == []
assert ValidityInfo.objects.exists() is False
# external link
cell.link_page = None
cell.url = 'http://example.net/'
with mock.patch('combo.data.models.requests.get') as requests_get:
mock_json = mock.Mock(status_code=404)
requests_get.return_value = mock_json
cell.save()
if bypass:
assert ValidityInfo.objects.exists() is False
else:
validity_info = ValidityInfo.objects.latest('pk')
assert validity_info.invalid_reason_code == 'data_url_not_found'
assert validity_info.invalid_since is not None
with mock.patch('combo.data.models.requests.get') as requests_get:
mock_json = mock.Mock(status_code=200)
requests_get.return_value = mock_json
cell.save()
assert ValidityInfo.objects.exists() is False
with mock.patch('combo.data.models.requests.get') as requests_get:
mock_json = mock.Mock(status_code=500)
requests_get.return_value = mock_json
cell.save()
assert ValidityInfo.objects.exists() is False
with mock.patch('combo.data.models.requests.get') as requests_get:
mock_json = mock.Mock(status_code=400)
requests_get.return_value = mock_json
cell.save()
if bypass:
assert ValidityInfo.objects.exists() is False
else:
validity_info = ValidityInfo.objects.latest('pk')
assert validity_info.invalid_reason_code == 'data_url_invalid'
assert validity_info.invalid_since is not None
# external link with a single variable
cell.link_page = None
cell.anchor = ''
settings.TEMPLATE_VARS = {'var1': 'foo'}
cell.url = 'http://foo?varone={{var1}}'
with mock.patch('combo.data.models.requests.get') as requests_get:
mock_json = mock.Mock(status_code=200)
requests_get.return_value = mock_json
cell.save()
assert ValidityInfo.objects.exists() is False
with mock.patch('combo.data.models.requests.get') as requests_get:
mock_json = mock.Mock(status_code=404)
requests_get.return_value = mock_json
cell.save()
if bypass:
assert ValidityInfo.objects.exists() is False
else:
assert ValidityInfo.objects.exists() is True
# external link with two variables
settings.TEMPLATE_VARS = {'var1': 'foo', 'var2': 'bar'}
cell.url = 'http://foo?varone={{var1}}&vartwo={{var2}}'
with mock.patch('combo.data.models.requests.get') as requests_get:
mock_json = mock.Mock(status_code=404)
requests_get.return_value = mock_json
cell.save()
assert ValidityInfo.objects.exists() is False
def test_link_list_cell():
page = Page.objects.create(title='example page', slug='example-page')
cell = LinkListCell.objects.create(order=0, page=page)
item = LinkCell.objects.create(
page=page,
placeholder=cell.link_placeholder,
title='Example Site',
url='http://example.net/',
order=0,
extra_css_class='foobar',
)
ctx = {'page_cells': [item]}
assert '<ul><li class=" foobar"><a href="http://example.net/">Example Site</a></li></ul>' in cell.render(
ctx
)
item.title = ''
item.extra_css_class = ''
assert '<ul><li class=""><a href="http://example.net/">http://example.net/</a></li></ul>' in cell.render(
ctx
)
item.link_page = page
assert '<ul><li class=""><a href="/example-page/">example page</a></li></ul>' in cell.render(ctx)
item.title = 'altertitle'
assert '<ul><li class=""><a href="/example-page/">altertitle</a></li></ul>' in cell.render(ctx)
item.anchor = 'anchor'
assert '<ul><li class=""><a href="/example-page/#anchor">altertitle</a></li></ul>' in cell.render(ctx)
item.link_page = None
assert '<ul><li class=""><a href="http://example.net/#anchor">altertitle</a></li></ul>' in cell.render(
ctx
)
item2 = LinkCell.objects.create(
page=page,
placeholder=cell.link_placeholder,
title='Example Site',
url='http://example.net/',
order=1,
)
ctx = {'page_cells': [item, item2]}
assert 'class="add-more-items--button">+</a>' not in cell.render(ctx)
cell.limit = 1
assert 'class="add-more-items--button">+</a>' in cell.render(ctx)
def test_link_list_cell_validity():
page = Page.objects.create(title='example page', slug='example-page')
cell = LinkListCell.objects.create(order=0, page=page)
item = LinkCell.objects.create(page=page, placeholder=cell.link_placeholder, order=0)
item.mark_as_valid()
cell.check_validity()
assert ValidityInfo.objects.exists() is False
item.mark_as_invalid('foo_bar_reason')
cell.check_validity()
validity_info = ValidityInfo.objects.latest('pk')
assert validity_info.invalid_reason_code == 'data_link_invalid'
assert validity_info.invalid_since is not None
validity_info.invalid_since = now() - datetime.timedelta(days=2)
validity_info.save()
request = RequestFactory().get('/')
assert cell.is_visible(request) # particular case: cell is visible
def test_feed_cell_validity(context):
page = Page.objects.create(title='example page', slug='example-page')
cell = FeedCell.objects.create(page=page, placeholder='content', order=1)
cell.get_cell_extra_context(context)
validity_info = ValidityInfo.objects.latest('pk')
assert validity_info.invalid_reason_code == 'data_url_not_defined'
assert validity_info.invalid_since is not None
cell.url = 'http://example.net/'
cell.save()
with mock.patch('combo.data.models.requests.get') as requests_get:
mock_json = mock.Mock(status_code=404)
requests_get.return_value = mock_json
cell.get_cell_extra_context(context)
validity_info = ValidityInfo.objects.latest('pk')
assert validity_info.invalid_reason_code == 'data_url_not_found'
assert validity_info.invalid_since is not None
with mock.patch('combo.data.models.requests.get') as requests_get:
mock_json = mock.Mock(status_code=200, content='')
requests_get.return_value = mock_json
cell.get_cell_extra_context(context)
assert ValidityInfo.objects.exists() is False
with mock.patch('combo.data.models.requests.get') as requests_get:
mock_json = mock.Mock(status_code=500)
requests_get.return_value = mock_json
cell.get_cell_extra_context(context)
assert ValidityInfo.objects.exists() is False
with mock.patch('combo.data.models.requests.get') as requests_get:
mock_json = mock.Mock(status_code=400)
requests_get.return_value = mock_json
cell.get_cell_extra_context(context)
validity_info = ValidityInfo.objects.latest('pk')
assert validity_info.invalid_reason_code == 'data_url_invalid'
assert validity_info.invalid_since is not None
# check validity info is cleared when the cell is saved
cell.save()
assert ValidityInfo.objects.exists() is False
def test_manager_feed_cell_tabs(app, admin_user):
page = Page.objects.create(title='example page', slug='example-page')
FeedCell.objects.create(page=page, placeholder='content', order=1)
app = login(app)
resp = app.get('/manage/pages/%s/' % page.pk)
assert not resp.pyquery('[data-tab-slug="general"] input[name$="title"]')
assert resp.pyquery('[data-tab-slug="appearance"] input[name$="title"]')
def test_menu_cell():
Page.objects.all().delete()
parent = Page.objects.create(
title='Page1', slug='page1', template_name='standard', exclude_from_navigation=False
)
page = Page.objects.create(
title='Page2', slug='page2', template_name='standard', parent=parent, exclude_from_navigation=False
)
page = Page.objects.create(
title='Page3',
slug='page3',
template_name='standard',
parent=parent,
public=False,
exclude_from_navigation=False,
)
cell = MenuCell.objects.create(root_page=parent, order=0, page=parent)
request = RequestFactory().get('/page1/')
request.user = None
ctx = {'page': parent, 'request': request}
assert 'menu-page2' in cell.render(ctx)
assert 'menu-page3' not in cell.render(ctx)
request.user = User(username='foo', email='foo@example.net')
assert 'menu-page2' in cell.render(ctx)
assert 'menu-page3' in cell.render(ctx)
# check cell is deferred in skeletons if some pages are private
ctx = {'page': parent, 'request': request, 'render_skeleton': True}
with pytest.raises(NothingInCacheException):
cell.render(ctx)
# and that it's rendered directly if all pages are public
page.public = True
page.save()
ctx = {'page': parent, 'request': request, 'render_skeleton': True}
assert 'menu-page2' in cell.render(ctx)
assert 'menu-page3' in cell.render(ctx)
def test_variant_templates():
page = Page(title='example page', slug='example-page')
page.save()
cell = TextCell()
cell.page = page
cell.text = '<p>foobar</p>'
cell.order = 0
cell.save()
ctx = {}
assert cell.render(ctx).strip() == '<p>foobar</p>'
templates_settings = [settings.TEMPLATES[0].copy()]
templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))]
with override_settings(TEMPLATES=templates_settings):
assert cell.render(ctx).strip() == '<p>foobar</p>'
cell.slug = 'foobar'
cell.save()
assert cell.render(ctx).strip() == '<div class="XXX"><p>foobar</p></div>'
assert cell.render(ctx).strip() == '<p>foobar</p>'
def test_extra_template():
page = Page(title='example page', slug='example-page')
page.save()
cell = TextCell()
cell.page = page
cell.text = '<p>foobar</p>'
cell.order = 0
cell.save()
ctx = {}
templates_settings = [settings.TEMPLATES[0].copy()]
templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))]
with override_settings(
COMBO_CELL_TEMPLATES={
'data_textcell': {
'extra': {
'label': 'Extra',
'template': 'combo/cells/foobar/text-cell.html',
'extra-css-classes': 'plop',
}
}
},
TEMPLATES=templates_settings,
):
cell.slug = ''
cell.template_name = 'extra'
cell.save()
assert cell.render(ctx).strip() == '<div class="XXX"><p>foobar</p></div>'
assert 'plop' in cell.css_class_names
def mocked_request(*args, **kwargs):
pass
def test_json_cell(caplog):
caplog.set_level(logging.DEBUG)
page = Page(title='example page', slug='example-page')
page.save()
cell = JsonCell()
cell.page = page
cell.url = 'http://example.net/'
cell.title = 'Example Site'
cell.order = 0
cell.save()
cell._json_content = None
assert cell.default_template_name == 'combo/json-error-cell.html'
cell._json_content = {}
assert cell.default_template_name == 'combo/json-cell.html'
cell._json_content = {'data': []}
assert cell.default_template_name == 'combo/json-cell.html'
cell._json_content = {'data': [{'url': 'xxx', 'text': 'xxx'}]}
assert cell.default_template_name == 'combo/json-list-cell.html'
cell._json_content = {'data': [{'foo': 'xxx', 'bar': 'xxx'}]}
assert cell.default_template_name == 'combo/json-cell.html'
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': [{'url': 'xxx', 'text': 'xxx'}]}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
context = cell.get_cell_extra_context({})
assert context['json'] == data
assert context['json_url'] == 'http://example.net/'
assert context['json_status'] == 200
assert 'json_error' not in context
requests_get.return_value = mock_json_response(content='', status_code=204) # 204 : No Content
context = cell.get_cell_extra_context({})
assert context['json'] is None
assert context['json_url'] == 'http://example.net/'
assert context['json_status'] == 204
assert 'json_error' not in context
requests_get.return_value = mock_json_response(content='not found', status_code=404, headers={})
context = cell.get_cell_extra_context({})
assert context['json'] is None
assert context['json_url'] == 'http://example.net/'
assert context['json_status'] == 404
assert 'json_error' not in context
requests_get.return_value = mock_json_response(
content=json.dumps(data), status_code=404, headers={'content-type': 'application/json'}
)
context = cell.get_cell_extra_context({})
assert context['json'] is None
assert context['json_url'] == 'http://example.net/'
assert context['json_status'] == 404
assert context['json_error'] == data
def mocked_requests_connection_error(*args, **kwargs):
raise requests.ConnectionError('boom')
requests_get.side_effect = mocked_requests_connection_error
context = cell.get_cell_extra_context({})
assert context['json'] is None
assert context['json_status'] == -1
assert context['json_url'] == 'http://example.net/'
assert context['json_error'] == 'boom'
assert isinstance(context['json_exception'], requests.ConnectionError)
cell.url = '' # no URL -> no request, no data, no status
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
context = cell.get_cell_extra_context({})
assert context['json'] is None
assert context['json_url'] == ''
assert 'json_status' not in context
assert 'json_error' not in context
with pytest.raises(NothingInCacheException):
cell.url = 'http://test3'
cell.render({})
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': [{'url': 'http://a.b', 'text': 'xxx'}]}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
cell.url = 'http://test4'
result = cell.render({'synchronous': True})
assert 'http://a.b' in result
assert requests_get.call_count == 1
cell.template_string = '{{json.data.0.text}}'
result = cell.render({'synchronous': True})
assert result == 'xxx'
with mock.patch('combo.utils.requests.get') as requests_get:
requests_get.return_value = mock_json_response(content='garbage', status_code=200)
cell.url = 'http://test5'
result = cell.render({'synchronous': True})
assert result == ''
context = cell.get_cell_extra_context({})
assert context['json'] is None
assert context['json_url'] == 'http://test5'
assert context['json_status'] == 200
assert context['json_error'] == 'invalid_json'
# URL is a template, using [variables]
cell.cache_duration = 10
data = {'data': [{'url': 'xxx', 'text': 'xxx'}]}
cell.url = 'http://testuser?[foobar]'
with mock.patch('combo.utils.requests.get') as requests_get:
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
context = cell.get_cell_extra_context({'foobar': 'barfoo'})
assert context['json'] == data
assert context['json_url'] == 'http://testuser?barfoo'
assert context['json_status'] == 200
assert requests_get.call_args[0][0] == 'http://testuser?barfoo'
assert requests_get.call_args[1]['cache_duration'] == 10
assert requests_get.call_count == 1
caplog.clear()
with mock.patch('combo.utils.requests.get') as requests_get:
context = cell.get_cell_extra_context({})
# can't get URL, 'foobar' variable is missing
assert context['json'] is None
assert requests_get.call_count == 0
assert caplog.record_tuples == [
(
'combo.data.models',
logging.DEBUG,
'error in templated URL (http://testuser?[foobar]): unknown variable foobar',
)
]
request = RequestFactory().get('/')
request.user = User(username='foo', email='foo@example.net')
cell.url = 'http://testuser?email=[user_email]'
with mock.patch('combo.utils.requests.get') as requests_get:
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
context = cell.get_cell_extra_context({'request': request})
assert context['json'] == data
assert context['json_url'] == 'http://testuser?email=foo%40example.net'
assert requests_get.call_count == 1
assert requests_get.call_args[0][0] == 'http://testuser?email=foo%40example.net'
assert requests_get.call_args[1]['cache_duration'] == 10
# URL is a template
cell.url = 'http://testuser?{{ concerned_user|name_id }}'
caplog.clear()
with mock.patch('combo.utils.requests.get') as requests_get:
context = cell.get_cell_extra_context({})
# can't get URL, variable is missing
assert context['json'] is None
assert requests_get.call_count == 0
assert caplog.record_tuples == [
(
'combo.data.models',
logging.DEBUG,
'error in templated URL (http://testuser?{{ concerned_user|name_id }}): name_id',
)
]
cell.url = 'http://testuser?{% for %}'
caplog.clear()
with mock.patch('combo.utils.requests.get') as requests_get:
context = cell.get_cell_extra_context({})
# can't get URL, variable is missing
assert context['json'] is None
assert requests_get.call_count == 0
assert len(caplog.records) == 1
assert caplog.record_tuples == [
(
'combo.data.models',
logging.ERROR,
'error in templated URL (http://testuser?{% for %}): syntax error',
)
]
def test_json_cell_with_varnames(app):
page = Page(title='example page', slug='index')
page.save()
cell = JsonCell()
cell.page = page
cell.title = 'Example Site'
cell.order = 0
cell.varnames_str = 'var1, var2, '
cell.url = 'http://foo?varone=[var1]&vartwo=[var2]'
cell.template_string = '/var1={{var1}}/var2={{var2}}/'
cell.save()
assert cell.varnames == ['var1', 'var2']
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
url = reverse(
'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}
)
resp = app.get(url + '?var1=foo&var2=bar') # request query string is here
assert requests_get.call_count == 1
assert requests_get.call_args[0][0] == 'http://foo?varone=foo&vartwo=bar'
assert '/var1=foo/' in resp.text
assert '/var2=bar/' in resp.text
def test_json_cell_make_public_url(app):
page = Page(title='example page', slug='index')
page.save()
cell = JsonCell()
cell.page = page
cell.title = 'Example Site'
cell.order = 0
cell.url = 'https://example.net'
# url from known_services
cell.template_string = '{% make_public_url url="http://127.0.0.1:8999/" %}'
cell.save()
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
url = reverse(
'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}
)
resp = app.get(url)
assert '/api/wcs/file/' in resp.text
assert 'http://127.0.0.1:8999' not in resp.text
# url from unknown service
cell.template_string = '{% make_public_url url="https://example.net" %}'
cell.save()
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
url = reverse(
'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}
)
resp = app.get(url)
assert 'https://example.net' in resp.text
assert '/api/wcs/file/' not in resp.text
# url with empty string
cell.template_string = 'X{% make_public_url url="" %}Y'
cell.save()
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
url = reverse(
'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}
)
resp = app.get(url)
assert 'XY' in resp.text
assert '/api/wcs/file/' not in resp.text
# url as None
cell.template_string = 'X{% make_public_url url=None %}Y'
cell.save()
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
url = reverse(
'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}
)
resp = app.get(url)
assert 'XNoneY' in resp.text
assert '/api/wcs/file/' not in resp.text
# url as "None" as string
cell.template_string = '{% make_public_url url="None" %}'
cell.save()
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
url = reverse(
'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}
)
resp = app.get(url)
assert resp.text == 'None'
def test_json_cell_validity(context):
page = Page.objects.create(title='example page', slug='example-page')
cell = JsonCell.objects.create(
page=page,
placeholder='content',
order=1,
varnames_str='var1, var2, ',
url='http://foo?varone=[var1]&vartwo=[var2]',
)
with mock.patch('combo.data.models.requests.get') as requests_get:
cell.get_cell_extra_context(context)
assert requests_get.call_args_list == [] # invalid context
assert ValidityInfo.objects.exists() is False
context['synchronous'] = True # to get fresh content
cell.varnames_str = ''
cell.url = 'http://foo'
with mock.patch('combo.utils.requests.get') as requests_get:
mock_json = mock.Mock(status_code=404)
requests_get.side_effect = [mock_json]
cell.get_cell_extra_context(context)
validity_info = ValidityInfo.objects.latest('pk')
assert validity_info.invalid_reason_code == 'data_url_not_found'
assert validity_info.invalid_since is not None
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
cell.get_cell_extra_context(context)
assert ValidityInfo.objects.exists() is False
with mock.patch('combo.utils.requests.get') as requests_get:
mock_json = mock.Mock(status_code=500)
requests_get.return_value = mock_json
cell.get_cell_extra_context(context)
assert ValidityInfo.objects.exists() is False
with mock.patch('combo.utils.requests.get') as requests_get:
mock_json = mock.Mock(status_code=400)
requests_get.return_value = mock_json
cell.get_cell_extra_context(context)
validity_info = ValidityInfo.objects.latest('pk')
assert validity_info.invalid_reason_code == 'data_url_invalid'
assert validity_info.invalid_since is not None
# url with a single variable
context['var1'] = 'foo'
cell.varnames_str = 'var1'
cell.url = 'http://foo?varone={{var1}}'
with mock.patch('combo.utils.requests.get') as requests_get:
mock_json = mock.Mock(status_code=200)
requests_get.return_value = mock_json
cell.get_cell_extra_context(context)
assert ValidityInfo.objects.exists() is False
with mock.patch('combo.utils.requests.get') as requests_get:
mock_json = mock.Mock(status_code=404)
requests_get.side_effect = [mock_json]
cell.get_cell_extra_context(context)
assert ValidityInfo.objects.exists() is True
# check validity info is cleared when the cell is saved
cell.save()
assert ValidityInfo.objects.exists() is False
# url with two variables
context['var2'] = 'bar'
cell.varnames_str = 'var1, var2'
cell.url = 'http://foo?varone={{var1}}&vartwo={{var2}}'
with mock.patch('combo.utils.requests.get') as requests_get:
mock_json = mock.Mock(status_code=404)
requests_get.side_effect = [mock_json]
cell.get_cell_extra_context(context)
assert ValidityInfo.objects.exists() is False
def test_manager_json_cell_tabs(app, admin_user):
page = Page.objects.create(title='example page', slug='example-page')
JsonCell.objects.create(
page=page,
placeholder='content',
order=1,
varnames_str='var1, var2, ',
url='http://foo?varone=[var1]&vartwo=[var2]',
)
app = login(app)
resp = app.get('/manage/pages/%s/' % page.pk)
assert not resp.pyquery('[data-tab-slug="general"] input[name$="title"]')
assert resp.pyquery('[data-tab-slug="appearance"] input[name$="title"]')
def test_config_json_cell():
page = Page(title='example page', slug='example-page')
page.save()
request = RequestFactory().get('/')
with override_settings(JSON_CELL_TYPES={'foobar': {'name': 'Foobar', 'url': 'http://test/'}}):
cell = ConfigJsonCell()
cell.key = 'foobar'
cell.parameters = {'blah': 'plop'}
assert cell.get_label() == 'Foobar'
assert cell.url == 'http://test/'
assert cell.default_template_name == 'combo/json/foobar.html'
assert cell.css_class_names.split() == ['config-json-cell', 'configjsoncell', 'foobar']
with mock.patch('combo.utils.requests.get') as requests_get:
requests_get.return_value = mock_json_response(
content=json.dumps({'hello': 'world'}), status_code=200
)
context = cell.get_cell_extra_context({'request': request})
assert context['json'] == {'hello': 'world'}
assert context['json_url'] == 'http://test/'
assert context['json_status'] == 200
assert context['parameters'] == {'blah': 'plop'}
assert context['blah'] == 'plop'
with override_settings(
JSON_CELL_TYPES={'foobar': {'name': 'Foobar', 'url': 'http://test/', 'cache_duration': 10}}
):
cell = ConfigJsonCell()
cell.key = 'foobar'
cell.parameters = {'blah': 'plop'}
assert cell.get_label() == 'Foobar'
assert cell.url == 'http://test/'
assert cell.default_template_name == 'combo/json/foobar.html'
assert cell.cache_duration == 10
def test_config_json_cell_with_varnames(app):
page = Page(title='example page', slug='index')
page.save()
templates_settings = [settings.TEMPLATES[0].copy()]
templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))]
with override_settings(
JSON_CELL_TYPES={
'test-config-json-cell': {
'name': 'Foobar',
'url': 'http://foo?varone=[var1]&vartwo=[var2]',
'varnames': ['var1', 'var2'],
}
},
TEMPLATES=templates_settings,
):
cell = ConfigJsonCell()
cell.key = 'test-config-json-cell'
cell.page = page
cell.title = 'Example Site'
cell.order = 0
cell.save()
assert cell.varnames == ['var1', 'var2']
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
url = reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()},
)
resp = app.get(url + '?var1=foo&var2=bar') # request query string is here
assert requests_get.call_count == 1
assert requests_get.call_args[0][0] == 'http://foo?varone=foo&vartwo=bar'
assert '/var1=foo/' in resp.text
assert '/var2=bar/' in resp.text
resp = app.get(url + '?var2=plop') # use var1 default value
assert requests_get.call_count == 1 # no request, var1 is missing
assert '/var1=/' in resp.text
assert '/var2=plop/' in resp.text
cell.parameters = {'var1': 'defaultvalue1'}
cell.save()
resp = app.get(url + '?var2=plop') # use var1 default value
assert requests_get.call_count == 2
assert requests_get.call_args[0][0] == 'http://foo?varone=defaultvalue1&vartwo=plop'
assert '/var1=defaultvalue1/' in resp.text
assert '/var2=plop/' in resp.text
def test_config_json_cell_with_param_in_url(app):
page = Page(title='example page', slug='index')
page.save()
templates_settings = [settings.TEMPLATES[0].copy()]
templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))]
with override_settings(
JSON_CELL_TYPES={
'test-config-json-cell': {
'name': 'Foobar',
'url': 'http://foo?var=[identifier]',
'log_errors': False,
'timeout': 42,
'form': [{"varname": "identifier", "type": "string", "label": "Identifier"}],
}
},
TEMPLATES=templates_settings,
):
cell = ConfigJsonCell()
cell.key = 'test-config-json-cell'
cell.parameters = {'identifier': 'plop'}
cell.page = page
cell.title = 'Example Site'
cell.order = 0
cell.save()
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
url = reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()},
)
app.get(url)
assert requests_get.call_count == 1
assert requests_get.call_args[0][0] == 'http://foo?var=plop'
assert requests_get.call_args[-1]['log_errors'] is False
assert requests_get.call_args[-1]['timeout'] == 42
def test_config_json_cell_with_template_string(settings, context):
settings.JSON_CELL_TYPES = {
'test-config-json-cell': {
'name': 'Foobar',
'url': 'http://foo',
'form': [
{"varname": "identifier", "type": "string", "label": "Identifier"},
{"varname": "template_string", "type": "text", "label": "Template"},
],
},
}
page = Page.objects.create(title='example page', slug='example-page')
cell = ConfigJsonCell.objects.create(
page=page,
placeholder='content',
order=1,
key='test-config-json-cell',
parameters={'identifier': 'plop', 'template_string': 'Foo Bar {{ identifier }}'},
)
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
assert cell.render({}) == 'Foo Bar plop'
settings.JSON_CELL_TYPES = { # template_string is not defined anymore
'test-config-json-cell': {
'name': 'Foobar',
'url': 'http://foo',
'form': [
{"varname": "identifier", "type": "string", "label": "Identifier"},
],
},
}
cell = ConfigJsonCell.objects.get(pk=cell.pk) # reload cell
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
with pytest.raises(TemplateDoesNotExist):
cell.render({})
def test_config_json_cell_with_global(settings, app):
settings.JSON_CELL_TYPES = {
'test-config-json-cell': {
'name': 'Foobar',
'url': 'http://foo',
'make_global': 'new_global_context',
'form': [
{"varname": "template_string", "type": "text", "label": "Template"},
],
},
'test-config-json-cell-2': {
'name': 'Foobar 2',
'url': 'http://foo',
'form': [
{"varname": "template_string", "type": "text", "label": "Template"},
],
},
}
page = Page.objects.create(title='example page', slug='example-page')
cell1 = ConfigJsonCell.objects.create(
page=page,
placeholder='content',
order=1,
key='test-config-json-cell',
parameters={'template_string': 'Foo Bar'},
)
cell2 = ConfigJsonCell.objects.create(
page=page,
placeholder='content',
order=2,
key='test-config-json-cell-2',
parameters={'template_string': 'Foo Bar'},
)
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
url = reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.pk, 'cell_reference': cell1.get_reference()},
)
resp = app.get(url)
assert resp.context['new_global_context'] == data
url = reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.pk, 'cell_reference': cell2.get_reference()},
)
resp = app.get(url)
assert resp.context['new_global_context'] == data
def test_config_json_cell_with_repeat(settings, app):
settings.JSON_CELL_TYPES = {
'test-config-json-cell': {
'name': 'Foobar',
'url': 'http://foo',
'form': [
{"varname": "template_string", "type": "text", "label": "Template"},
],
},
'test-config-json-cell-2': {
'name': 'Foobar 2',
'url': 'http://foo',
'repeat': '3',
'form': [
{"varname": "template_string", "type": "text", "label": "Template"},
],
},
}
page = Page.objects.create(title='example page', slug='example-page')
cell1 = ConfigJsonCell.objects.create(
page=page,
placeholder='content',
order=1,
key='test-config-json-cell',
parameters={'template_string': 'Foo Bar'},
)
cell2 = ConfigJsonCell.objects.create(
page=page,
placeholder='content',
order=2,
key='test-config-json-cell-2',
parameters={'template_string': 'Foo Bar {{ repeat_index }}'},
)
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
resp = app.get(page.get_online_url())
assert len(resp.context['cells']) == 4
assert resp.context['cells'][0].pk == cell1.pk
extra_ctx = re.findall(r'data-extra-context="(.*)"', resp.text)
cell_url = reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.pk, 'cell_reference': cell2.get_reference()},
)
for i in range(0, 3):
assert resp.context['cells'][i + 1].pk == cell2.pk
assert resp.context['cells'][i + 1].repeat_index == i
cell_resp = app.get(cell_url + '?ctx=' + extra_ctx[i + 1])
assert cell_resp.context['repeat_index'] == i
assert cell_resp.text == 'Foo Bar %s' % i
# repeat: not an int
settings.JSON_CELL_TYPES['test-config-json-cell-2']['repeat'] = 'foo'
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
resp = app.get(page.get_online_url())
assert len(resp.context['cells']) == 2
# repeat: template error
settings.JSON_CELL_TYPES['test-config-json-cell-2']['repeat'] = '{% foo %}'
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
resp = app.get(page.get_online_url())
assert len(resp.context['cells']) == 2
def test_config_json_cell_validity(settings, context):
settings.JSON_CELL_TYPES = {
'test-config-json-cell': {
'name': 'Foobar',
'url': 'http://foo?varone=[var1]&vartwo=[var2]',
'varnames': ['var1', 'var2'],
},
}
templates_settings = [settings.TEMPLATES[0].copy()]
templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))]
settings.TEMPLATES = templates_settings
page = Page.objects.create(title='example page', slug='example-page')
cell = ConfigJsonCell.objects.create(
page=page,
placeholder='content',
order=1,
key='test-config-json-cell',
parameters={'identifier': 'plop'},
)
assert cell.varnames == ['var1', 'var2']
with mock.patch('combo.data.models.requests.get') as requests_get:
cell.get_cell_extra_context(context)
assert requests_get.call_args_list == [] # invalid context
assert ValidityInfo.objects.exists() is False
context['synchronous'] = True # to get fresh content
settings.JSON_CELL_TYPES['test-config-json-cell']['url'] = 'http://foo'
settings.JSON_CELL_TYPES['test-config-json-cell']['varnames'] = []
with mock.patch('combo.utils.requests.get') as requests_get:
mock_json = mock.Mock(status_code=404)
requests_get.side_effect = [mock_json]
cell.get_cell_extra_context(context)
validity_info = ValidityInfo.objects.latest('pk')
assert validity_info.invalid_reason_code == 'data_url_not_found'
assert validity_info.invalid_since is not None
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
cell.get_cell_extra_context(context)
assert ValidityInfo.objects.exists() is False
with mock.patch('combo.utils.requests.get') as requests_get:
mock_json = mock.Mock(status_code=500)
requests_get.return_value = mock_json
cell.get_cell_extra_context(context)
assert ValidityInfo.objects.exists() is False
with mock.patch('combo.utils.requests.get') as requests_get:
mock_json = mock.Mock(status_code=400)
requests_get.return_value = mock_json
cell.get_cell_extra_context(context)
validity_info = ValidityInfo.objects.latest('pk')
assert validity_info.invalid_reason_code == 'data_url_invalid'
assert validity_info.invalid_since is not None
# url with a single variable
context['var1'] = 'foo'
settings.JSON_CELL_TYPES['test-config-json-cell']['varnames'] = ['var1']
settings.JSON_CELL_TYPES['test-config-json-cell']['url'] = 'http://foo?varone={{var1}}'
with mock.patch('combo.utils.requests.get') as requests_get:
mock_json = mock.Mock(status_code=200)
requests_get.return_value = mock_json
cell.get_cell_extra_context(context)
assert ValidityInfo.objects.exists() is False
with mock.patch('combo.utils.requests.get') as requests_get:
mock_json = mock.Mock(status_code=404)
requests_get.side_effect = [mock_json]
cell.get_cell_extra_context(context)
assert ValidityInfo.objects.exists() is True
# url with two variables
context['var2'] = 'bar'
settings.JSON_CELL_TYPES['test-config-json-cell']['varnames'] = ['var1', 'var2']
settings.JSON_CELL_TYPES['test-config-json-cell']['url'] = 'http://foo?varone={{var1}}&vartwo={{var2}}'
with mock.patch('combo.utils.requests.get') as requests_get:
mock_json = mock.Mock(status_code=404)
requests_get.side_effect = [mock_json]
cell.get_cell_extra_context(context)
assert ValidityInfo.objects.exists() is False
def test_json_force_async():
cell = JsonCell()
cell.url = 'http://example.net/test-force-async'
cell.template_string = '{{json.hello}}'
cell.force_async = True
with mock.patch('combo.utils.requests_wrapper.RequestsSession.request') as requests_get:
requests_get.return_value = mock_json_response(
content=json.dumps({'hello': 'world'}), status_code=200
)
with pytest.raises(NothingInCacheException):
cell.render({})
assert cell.render({'synchronous': True}) == 'world'
# check force async is effective
with pytest.raises(NothingInCacheException):
cell.render({})
# disable force_async
cell.force_async = False
assert cell.render({}) == 'world'
cell = JsonCell()
cell.url = 'http://example.net/test-force-async-2'
cell.template_string = '{{json.hello}}'
cell.force_async = False
with mock.patch('combo.utils.requests_wrapper.RequestsSession.request') as requests_get:
requests_get.return_value = mock_json_response(
content=json.dumps({'hello': 'world2'}), status_code=200
)
# raise if nothing in cache
with pytest.raises(NothingInCacheException):
cell.render({})
# force stuff in cache
assert cell.render({'synchronous': True}) == 'world2'
# rerun with stuff in cache
assert cell.render({}) == 'world2'
def test_config_json_cell_additional_url(app):
page = Page(title='example page', slug='index')
page.save()
templates_settings = [settings.TEMPLATES[0].copy()]
templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))]
with override_settings(
JSON_CELL_TYPES={
'test-config-json-cell-2': {
'name': 'Foobar',
'url': 'http://foo',
'additional-data': [
{'key': 'plop', 'url': 'http://bar', 'log_errors': False, 'timeout': 42},
],
}
},
TEMPLATES=templates_settings,
):
cell = ConfigJsonCell()
cell.key = 'test-config-json-cell-2'
cell.page = page
cell.title = 'Example Site'
cell.order = 0
cell.save()
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': 'toto'}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
url = reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()},
)
resp = app.get(url)
assert resp.text.strip() == '/var1=toto/var2=toto/'
assert len(requests_get.mock_calls) == 2
assert requests_get.mock_calls[0][1][0] == 'http://foo'
assert requests_get.mock_calls[0][-1]['log_errors'] is True
assert requests_get.mock_calls[0][-1]['timeout'] is None
assert requests_get.mock_calls[1][1][0] == 'http://bar'
assert requests_get.mock_calls[1][-1]['log_errors'] is False
assert requests_get.mock_calls[1][-1]['timeout'] == 42
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': 'toto'}
requests_get.return_value = mock_json_response(
content=json.dumps(data), status_code=404, headers={'content-type': 'application/json'}
)
url = reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()},
)
resp = app.get(url)
assert resp.text.strip() == '/var1=/var2=/'
assert len(requests_get.mock_calls) == 2
assert requests_get.mock_calls[0][1][0] == 'http://foo'
assert requests_get.mock_calls[1][1][0] == 'http://bar'
context = cell.get_cell_extra_context({})
assert context['json'] is None
assert context['json_url'] == 'http://foo'
assert context['json_status'] == 404
assert context['json_error'] == data
assert context['plop'] is None
assert context['plop_url'] == 'http://bar'
assert context['plop_status'] == 404
assert context['plop_error'] == data
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': 'toto'}
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
context = cell.get_cell_extra_context({})
assert context['json'] == data
assert context['json_url'] == 'http://foo'
assert context['json_status'] == 200
assert 'json_error' not in context
assert context['plop'] == data
assert context['plop_url'] == 'http://bar'
assert context['plop_status'] == 200
assert 'plop_error' not in context
# additional-data url depends on others results, with Django-syntax URL
with override_settings(
JSON_CELL_TYPES={
'test-config-json-cell-2': {
'name': 'Foobar',
'url': 'http://foo',
'additional-data': [
{'key': 'plop', 'url': 'http://{{json.data}}', 'log_errors': False, 'timeout': 42},
{
'key': 'plop2',
'url': '{% if plop %}http://{{json.data}}/{{plop.data}}{% endif %}',
'log_errors': False,
'timeout': 10,
},
],
}
},
TEMPLATES=templates_settings,
):
cell = ConfigJsonCell()
cell.key = 'test-config-json-cell-2'
cell.page = page
cell.title = 'Example Site'
cell.order = 0
cell.save()
data = {'data': 'bar'}
with mock.patch('combo.utils.requests.get') as requests_get:
requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200)
url = reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()},
)
resp = app.get(url)
assert resp.text.strip() == '/var1=bar/var2=bar/'
assert len(requests_get.mock_calls) == 3
assert requests_get.mock_calls[0][1][0] == 'http://foo'
assert requests_get.mock_calls[0][-1]['log_errors'] is True
assert requests_get.mock_calls[0][-1]['timeout'] is None
assert requests_get.mock_calls[1][1][0] == 'http://bar'
assert requests_get.mock_calls[1][-1]['log_errors'] is False
assert requests_get.mock_calls[1][-1]['timeout'] == 42
assert requests_get.mock_calls[2][1][0] == 'http://bar/bar'
assert requests_get.mock_calls[2][-1]['log_errors'] is False
assert requests_get.mock_calls[2][-1]['timeout'] == 10
context = cell.get_cell_extra_context({})
assert context['json'] == data
assert context['json_url'] == 'http://foo'
assert context['json_status'] == 200
assert context['plop'] == data
assert context['plop_url'] == 'http://bar'
assert context['plop_status'] == 200
assert context['plop2'] == data
assert context['plop2_url'] == 'http://bar/bar'
assert context['plop2_status'] == 200
with mock.patch('combo.utils.requests.get') as requests_get:
requests_get.return_value = mock_json_response(
content=json.dumps(data), status_code=404, headers={'content-type': 'application/json'}
)
url = reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()},
)
resp = app.get(url)
assert resp.text.strip() == '/var1=/var2=/'
# can not create plop and plop2 url: only one request for "json"
assert len(requests_get.mock_calls) == 2
assert requests_get.mock_calls[0][1][0] == 'http://foo'
context = cell.get_cell_extra_context({})
assert context['json'] is None
assert context['json_url'] == 'http://foo'
assert context['json_status'] == 404
assert context['json_error'] == data
assert context['plop'] is None
assert context['plop_url'] == 'http://'
assert context['plop_status'] == 404
assert context['plop_error'] == data
# plop2 url is empty, no request: None value, no status
assert context['plop2'] is None
assert context['plop2_url'] == ''
assert 'plop2_status' not in context
assert 'plop2_error' not in context
def test_config_json_invalid_key_cell():
page = Page(title='example page', slug='example-page')
page.save()
with override_settings(JSON_CELL_TYPES={'foobar': {'name': 'Foobar', 'url': 'http://test/'}}):
cell = ConfigJsonCell()
cell.key = 'foobar'
cell.parameters = {'blah': 'plop'}
cell.page = page
cell.order = 0
cell.save()
assert len(page.get_cells()) == 1
assert len(page.get_cells()) == 0
def test_page_cell_placeholder_restricted_visibility(app, admin_user):
page = Page(title='Test', slug='test', template_name='standard')
page.save()
json_cell = JsonCell(page=page, placeholder='content', order=0, url='http://example.com')
json_cell.template_string = '{% placeholder "foobar" name="Foobar" %}'
json_cell.save()
TextCell(
page=page, placeholder='foobar', text='<p>Public text</p>', order=0, restricted_to_unlogged=True
).save()
TextCell(page=page, placeholder='foobar', text='<p>Private text</p>', order=1, public=False).save()
resp = app.get(
reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.pk, 'cell_reference': json_cell.get_reference()},
)
)
assert "<p>Public text</p>" in resp.text
assert "<p>Private text</p>" not in resp.text
app = login(app)
resp = app.get(
reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.pk, 'cell_reference': json_cell.get_reference()},
)
)
assert "<p>Public text</p>" not in resp.text
assert "<p>Private text</p>" in resp.text
def test_related_cell_types_tracking():
page = Page(title='example page', slug='example-page')
page.save()
assert page.related_cells['cell_types'] == []
TextCell(page=page, placeholder='content', order=0, text='hello').save()
assert Page.objects.get(id=page.id).related_cells['cell_types'] == ['data_textcell']
TextCell(page=page, placeholder='content', order=1, text='hello').save()
assert Page.objects.get(id=page.id).related_cells['cell_types'] == ['data_textcell']
LinkCell(page=page, placeholder='content', order=0, title='Test', url='http://example.net').save()
assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == {'data_textcell', 'data_linkcell'}
with CaptureQueriesContext(connection) as ctx:
assert len(CellBase.get_cells(page=Page.objects.get(id=page.id))) == 3
assert len(ctx.captured_queries) == 1 + 2
TextCell.objects.get(order=1).delete()
assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == {'data_textcell', 'data_linkcell'}
TextCell.objects.get(order=0).delete()
assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == {'data_linkcell'}
with CaptureQueriesContext(connection) as ctx:
assert len(CellBase.get_cells(page=Page.objects.get(id=page.id))) == 1
assert len(ctx.captured_queries) == 1 + 1
# remove tracker, check it is rebuilt correctly
page.related_cells = {}
page.save()
with CaptureQueriesContext(connection) as ctx:
assert len(CellBase.get_cells(page=Page.objects.get(id=page.id))) == 1
assert len(ctx.captured_queries) == len(get_cell_classes())
Page.objects.get(id=page.id).get_cells()
TextCell(page=page, placeholder='content', order=0, text='hello').save()
assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == {'data_textcell', 'data_linkcell'}
def test_link_list_cell_duplicate():
page = Page.objects.create(title='xxx', slug='new', template_name='standard')
cell = LinkListCell.objects.create(order=0, page=page)
item = LinkCell.objects.create(
page=page,
placeholder=cell.link_placeholder,
title='Example Site',
url='http://example.net/',
link_page=page,
order=1,
)
new_cell = cell.duplicate()
assert LinkCell.objects.count() == 2
assert len(new_cell.get_items()) == 1
new_item = new_cell.get_items()[0]
assert new_item.page == page
assert new_item.placeholder == new_cell.link_placeholder
assert new_item.pk != item.pk
assert new_item.title == item.title
assert new_item.url == item.url
assert new_item.link_page == item.link_page
def test_cell_is_visible():
request = RequestFactory().get('/')
page = Page.objects.create()
cell = TextCell.objects.create(page=page, order=0)
assert cell.is_visible(request) is True
# invalid cell since just now
validity_info = ValidityInfo.objects.create(content_object=cell)
validity_info.invalid_reason_code = 'FOO'
validity_info.invalid_since = now()
validity_info.save()
assert cell.is_visible(request) is True
# invalid cell since two days
validity_info.invalid_since = now() - datetime.timedelta(days=2)
validity_info.save()
assert cell.is_visible(request) is False
def test_cell_invalidity_marker():
page = Page.objects.create()
cell = TextCell.objects.create(page=page, order=0)
cell.mark_as_invalid('foo_bar_reason')
validity_info = ValidityInfo.objects.latest('pk')
old_reason = validity_info.invalid_reason_code
old_date = validity_info.invalid_since
cell.mark_as_invalid('another_foo_bar_reason', force=False)
validity_info.refresh_from_db()
assert old_reason == validity_info.invalid_reason_code
assert old_date == validity_info.invalid_since
cell.mark_as_invalid('another_foo_bar_reason')
validity_info.refresh_from_db()
assert validity_info.invalid_reason_code == 'another_foo_bar_reason'
assert old_date < validity_info.invalid_since
cell.mark_as_valid()
assert ValidityInfo.objects.exists() is False
def test_hourly():
appconfig = apps.get_app_config('data')
page = Page.objects.create(title='xxx', slug='test_current_forms_cell_render', template_name='standard')
cell_classes = [c for c in appconfig.get_models() if c in get_cell_classes()]
for klass in cell_classes:
klass.objects.create(page=page, placeholder='content', order=0)
for klass in cell_classes:
if klass in [LinkCell, LinkListCell]:
with mock.patch('combo.data.models.%s.check_validity' % klass.__name__) as check_validity:
appconfig.hourly()
assert check_validity.call_args_list == [mock.call()]
else:
assert hasattr(klass, 'check_validity') is False
def test_cell_assets(settings, app, admin_user):
page = Page.objects.create(title='xxx', slug='test_cell_assets', template_name='standard')
text_cell = TextCell.objects.create(page=page, order=0, slug='foo')
list_cell = LinkListCell.objects.create(page=page, order=2, slug='bar')
item = LinkCell.objects.create(
page=page,
placeholder=list_cell.link_placeholder,
title='Example Site',
link_page=page,
order=1,
)
app = login(app)
settings.COMBO_CELL_ASSET_SLOTS = {}
resp = app.get('/manage/assets/')
assert 'have any asset yet.' in resp.text
# only text cells are defined for assets
settings.COMBO_CELL_ASSET_SLOTS = {'data_textcell': {'picture': {'prefix': 'Picture'}}}
resp = app.get('/manage/assets/')
assert 'Picture — %s' % text_cell.get_label_for_asset() in resp.text
assert 'Picture — %s' % list_cell.get_label_for_asset() not in resp.text
assert 'Picture — %s' % item.get_label_for_asset() not in resp.text
# text and list link cells are defined for assets
settings.COMBO_CELL_ASSET_SLOTS = {
'data_textcell': {'picture': {'prefix': 'Picture'}},
'data_linklistcell': {'picture': {'prefix': 'Picture'}},
'data_linkcell': {'picture': {'prefix': 'Picture', 'suffix': 'test'}},
}
resp = app.get('/manage/assets/')
assert 'Picture — %s' % text_cell.get_label_for_asset() in resp.text
assert 'Picture — %s' % list_cell.get_label_for_asset() in resp.text
# but items are excluded
assert item.get_slug_for_asset() is None # slug for asset is always None for items
assert 'Picture — %s' % item.get_label_for_asset() not in resp.text
# test slugs
link_cell = LinkCell.objects.create(
page=page,
url='http://example.net/',
order=1,
)
resp = app.get('/manage/assets/')
assert link_cell.get_slug_for_asset() == ''
assert 'Picture — %s' % link_cell.get_label_for_asset() not in resp.text
link_cell.slug = 'foo'
link_cell.save()
resp = app.get('/manage/assets/')
assert link_cell.get_slug_for_asset() == 'foo'
assert 'Picture — %s (test)' % link_cell.get_label_for_asset() in resp.text
link_cell.slug = ''
link_cell.url = ''
link_cell.link_page = page
link_cell.save()
resp = app.get('/manage/assets/')
assert link_cell.get_slug_for_asset() == 'test_cell_assets'
assert 'Picture — %s (test)' % link_cell.get_label_for_asset() in resp.text