1674 lines
63 KiB
Python
1674 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_str
|
|
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_str(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 '/static/coincoin.js' in force_str(sum((cell.media for cell in cells), Media()))
|
|
|
|
|
|
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
|