1334 lines
50 KiB
Python
1334 lines
50 KiB
Python
import datetime
|
|
import json
|
|
import os
|
|
import re
|
|
import urllib.parse
|
|
from io import StringIO
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from django.conf import settings
|
|
from django.contrib.auth.models import Group
|
|
from django.core.files import File
|
|
from django.db import connection
|
|
from django.test import override_settings
|
|
from django.test.utils import CaptureQueriesContext
|
|
from django.urls import reverse
|
|
from django.utils.http import quote
|
|
|
|
try:
|
|
import mellon # pylint: disable=unused-import
|
|
except ImportError:
|
|
mellon = None
|
|
import requests
|
|
|
|
from combo.apps.assets.models import Asset
|
|
from combo.data.models import (
|
|
ConfigJsonCell,
|
|
FeedCell,
|
|
JsonCell,
|
|
LinkCell,
|
|
LinkListCell,
|
|
Page,
|
|
ParentContentCell,
|
|
Redirect,
|
|
SiteSettings,
|
|
TextCell,
|
|
)
|
|
from combo.profile.models import Profile
|
|
|
|
from .test_manager import login
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
def test_missing_index(app):
|
|
Page.objects.all().delete()
|
|
resp = app.get('/', status=200)
|
|
assert 'Welcome' in resp.text
|
|
|
|
|
|
def test_index(app):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
resp = app.get('/index', status=301)
|
|
assert urllib.parse.urlparse(resp.location).path == '/'
|
|
resp = app.get('/', status=200)
|
|
|
|
# check {% now %} inside a skeleton_extra_placeholder is interpreted
|
|
assert str(datetime.datetime.now().year) in resp.text
|
|
|
|
|
|
def test_page_contents(app):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
cell = TextCell(page=page, placeholder='content', text='Foobar', order=0)
|
|
cell.save()
|
|
resp = app.get('/', status=200)
|
|
assert 'Foobar' in resp.text
|
|
|
|
|
|
def test_page_contents_unlogged_only(app, admin_user):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
cell = TextCell(page=page, placeholder='content', text='Foobar', order=0, restricted_to_unlogged=True)
|
|
cell.save()
|
|
resp = app.get('/', status=200)
|
|
assert 'Foobar' in resp.text
|
|
|
|
app = login(app)
|
|
resp = app.get('/', status=200)
|
|
assert not 'Foobar' in resp.text
|
|
|
|
|
|
@pytest.mark.skipif('mellon is None')
|
|
def test_mellon_login(app):
|
|
with mock.patch('combo.public.views.get_idps') as get_idps:
|
|
get_idps.return_value = ['xxx']
|
|
resp = app.get('/login/')
|
|
assert urllib.parse.urlparse(resp.location).path == '/accounts/mellon/login/'
|
|
resp = app.get('/login/?next=whatever')
|
|
assert urllib.parse.urlparse(resp.location).query == 'next=whatever'
|
|
|
|
|
|
def test_page_contents_group_presence(app, normal_user):
|
|
group = Group(name='plop')
|
|
group.save()
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
cell = TextCell(page=page, placeholder='content', text='Foobar', order=0, public=False)
|
|
cell.save()
|
|
cell.groups.set([group])
|
|
resp = app.get('/', status=200)
|
|
assert 'Foobar' not in resp.text
|
|
|
|
app = login(app, username='normal-user', password='normal-user')
|
|
resp = app.get('/', status=200)
|
|
assert 'Foobar' not in resp.text
|
|
|
|
normal_user.groups.set([group])
|
|
resp = app.get('/', status=200)
|
|
assert 'Foobar' in resp.text
|
|
|
|
|
|
def test_page_contents_group_absence(app, normal_user):
|
|
group = Group(name='plop')
|
|
group.save()
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
cell = TextCell(
|
|
page=page, placeholder='content', text='Foobar', order=0, public=False, restricted_to_unlogged=True
|
|
)
|
|
cell.save()
|
|
cell.groups.set([group])
|
|
resp = app.get('/', status=200)
|
|
assert 'Foobar' not in resp.text
|
|
|
|
app = login(app, username='normal-user', password='normal-user')
|
|
resp = app.get('/', status=200)
|
|
assert 'Foobar' in resp.text
|
|
|
|
normal_user.groups.set([group])
|
|
resp = app.get('/', status=200)
|
|
assert 'Foobar' not in resp.text
|
|
|
|
|
|
def test_page_contents_condition(app, normal_user):
|
|
page = Page.objects.create(title='Page', slug='page', template_name='standard')
|
|
cell1 = TextCell.objects.create(
|
|
page=page, placeholder='content', text='Foobar', order=0, condition='True'
|
|
)
|
|
cell2 = TextCell.objects.create(
|
|
page=page, placeholder='content', text='Foobaz', order=1, condition='False'
|
|
)
|
|
cell3 = LinkListCell.objects.create(order=1, page=page, placeholder='content')
|
|
LinkCell.objects.create(
|
|
page=page,
|
|
placeholder=cell3.link_placeholder,
|
|
title='Example Site',
|
|
url='http://example.net/',
|
|
order=0,
|
|
condition='True',
|
|
)
|
|
LinkCell.objects.create(
|
|
page=page,
|
|
placeholder=cell3.link_placeholder,
|
|
title='Other Site',
|
|
url='http://other.net/',
|
|
order=1,
|
|
condition='False',
|
|
)
|
|
|
|
app = login(app, username='normal-user', password='normal-user')
|
|
resp = app.get(page.get_online_url())
|
|
assert 'Foobar' in resp.text
|
|
assert 'Foobaz' not in resp.text
|
|
assert 'Example Site' in resp.text
|
|
assert 'Other Site' not in resp.text
|
|
|
|
page.sub_slug = 'foo_id'
|
|
page.save()
|
|
cell1.condition = 'foo_id'
|
|
cell1.save()
|
|
cell2.condition = 'not foo_id'
|
|
cell2.save()
|
|
resp = app.get(page.get_online_url() + '11/')
|
|
assert 'Foobar' in resp.text
|
|
assert 'Foobaz' not in resp.text
|
|
|
|
|
|
def test_page_footer_acquisition(app):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
page_index = page
|
|
cell = TextCell(page=page, placeholder='footer', text='BARFOO', order=0)
|
|
cell.save()
|
|
resp = app.get('/', status=200)
|
|
assert resp.text.count('BARFOO') == 1
|
|
|
|
# make sure a parent content cell in the home page doesn't duplicate
|
|
# cells
|
|
ParentContentCell(page=page, placeholder='footer', order=0).save()
|
|
resp = app.get('/', status=200)
|
|
assert resp.text.count('BARFOO') == 1
|
|
|
|
page = Page(title='Second', slug='second', template_name='standard')
|
|
page.save()
|
|
ParentContentCell(page=page, placeholder='footer', order=0).save()
|
|
TextCell(page=page, placeholder='footer', text='BAR2FOO', order=1).save()
|
|
resp = app.get('/second', status=301)
|
|
assert urllib.parse.urlparse(resp.location).path == '/second/'
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.get('/second/', status=200)
|
|
assert resp.text.count('BARFOO') == 1
|
|
assert resp.text.count('BAR2FOO') == 1
|
|
queries_count_second = len(ctx.captured_queries)
|
|
|
|
# deeper hierarchy
|
|
page3 = Page(title='Third', slug='third', template_name='standard', parent=page)
|
|
page3.save()
|
|
ParentContentCell(page=page3, placeholder='footer', order=0).save()
|
|
|
|
page4 = Page(title='Fourth', slug='fourth', template_name='standard', parent=page3)
|
|
page4.save()
|
|
ParentContentCell(page=page4, placeholder='footer', order=0).save()
|
|
|
|
page5 = Page(title='Fifth', slug='fifth', template_name='standard', parent=page4)
|
|
page5.save()
|
|
ParentContentCell(page=page5, placeholder='footer', order=0).save()
|
|
|
|
# check growth in SQL queries is limited
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.get('/second/third/', status=200)
|
|
assert resp.text.count('BARFOO') == 1
|
|
assert resp.text.count('BAR2FOO') == 1
|
|
queries_count_third = len(ctx.captured_queries)
|
|
# +2 for validity info of parent page
|
|
assert queries_count_third == queries_count_second + 1
|
|
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.get('/second/third/fourth/', status=200)
|
|
assert resp.text.count('BARFOO') == 1
|
|
assert resp.text.count('BAR2FOO') == 1
|
|
queries_count_fourth = len(ctx.captured_queries)
|
|
# +1 for get_parents_and_self()
|
|
assert queries_count_fourth == queries_count_second + 1 + 1
|
|
|
|
# check footer doesn't get duplicated in real index children
|
|
page6 = Page(title='Sixth', slug='sixth', template_name='standard', parent=page_index)
|
|
page6.save()
|
|
ParentContentCell(page=page6, placeholder='footer', order=0).save()
|
|
|
|
resp = app.get('/sixth/', status=200)
|
|
assert resp.text.count('BARFOO') == 1
|
|
|
|
|
|
def test_list_of_links_acquisition(app):
|
|
Page.objects.all().delete()
|
|
index_page = Page(title='Home', slug='index', template_name='standard')
|
|
index_page.save()
|
|
|
|
cell = LinkListCell.objects.create(order=0, page=index_page, placeholder='footer')
|
|
LinkCell.objects.create(
|
|
page=index_page,
|
|
placeholder=cell.link_placeholder,
|
|
title='Example Site',
|
|
url='http://example.net/',
|
|
order=0,
|
|
)
|
|
LinkCell.objects.create(
|
|
page=index_page,
|
|
placeholder=cell.link_placeholder,
|
|
title='Example2 Site',
|
|
url='http://example.org/',
|
|
order=1,
|
|
)
|
|
|
|
resp = app.get('/', status=200)
|
|
assert resp.text.count('Example Site') == 1
|
|
assert resp.text.count('Example2 Site') == 1
|
|
|
|
page = Page(title='Second', slug='second', template_name='standard')
|
|
page.save()
|
|
ParentContentCell(page=page, placeholder='footer', order=0).save()
|
|
resp = app.get('/second/', status=200)
|
|
assert resp.text.count('Example Site') == 1
|
|
assert resp.text.count('Example2 Site') == 1
|
|
|
|
|
|
def test_page_redirect(app):
|
|
Page.objects.all().delete()
|
|
page = Page(
|
|
title='Elsewhere', slug='elsewhere', template_name='standard', redirect_url='http://example.net'
|
|
)
|
|
page.save()
|
|
resp = app.get('/elsewhere/', status=302)
|
|
assert resp.location == 'http://example.net'
|
|
|
|
|
|
def test_page_templated_redirect(app):
|
|
Page.objects.all().delete()
|
|
with override_settings(TEMPLATE_VARS={'test_url': 'http://example.net'}):
|
|
page = Page(title='Elsewhere', slug='elsewhere', template_name='standard', redirect_url='[test_url]')
|
|
page.save()
|
|
resp = app.get('/elsewhere/', status=302)
|
|
assert resp.location == 'http://example.net'
|
|
|
|
page.redirect_url = '{{test_url}}/ok'
|
|
page.save()
|
|
resp = app.get('/elsewhere/', status=302)
|
|
assert resp.location == 'http://example.net/ok'
|
|
|
|
page.redirect_url = '{{unknown_variable}}'
|
|
page.save()
|
|
resp = app.get('/elsewhere/', status=200)
|
|
|
|
page.redirect_url = '{% if error %}'
|
|
page.save()
|
|
resp = app.get('/elsewhere/', status=404)
|
|
|
|
|
|
def test_page_private_unlogged(app):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard', public=False)
|
|
page.save()
|
|
resp = app.get('/', status=302)
|
|
assert resp.location.endswith('/login/?next=http%3A//testserver/')
|
|
|
|
|
|
def test_page_private_logged_in(app, admin_user):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard', public=False)
|
|
page.save()
|
|
app = login(app)
|
|
app.get('/', status=200)
|
|
|
|
|
|
def test_page_skeleton(app):
|
|
Page.objects.all().delete()
|
|
page = Page(
|
|
title='Elsewhere', slug='elsewhere', template_name='standard', redirect_url='http://example.net/foo/'
|
|
)
|
|
page.save()
|
|
|
|
# url prefix match
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://example.net/foo/bar'))
|
|
assert '{% block placeholder-content %}{% block content %}{% endblock %}{% endblock %}' in resp.text
|
|
assert '{% block placeholder-footer %}{% block footer %}{% endblock %}{% endblock %}' in resp.text
|
|
assert resp.text.startswith('{% with page_template_name="standard" %}<!DOCTYPE html>\n')
|
|
|
|
# url netloc match
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://example.net'))
|
|
assert '{% block placeholder-content %}{% block content %}{% endblock %}{% endblock %}' in resp.text
|
|
assert '{% block placeholder-footer %}{% block footer %}{% endblock %}{% endblock %}' in resp.text
|
|
|
|
# settings.KNOWN_SERVICES match
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://127.0.0.1:8999/'))
|
|
assert '{% block placeholder-content %}{% block content %}{% endblock %}{% endblock %}' in resp.text
|
|
|
|
# no match
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://example.com/foo/bar'), status=403)
|
|
|
|
# check with a footer cell
|
|
TextCell.objects.create(page=page, placeholder='footer', text='Foobar', order=0)
|
|
TextCell.objects.create(page=page, placeholder='content', text='Foobar', slug='unique', order=1)
|
|
TextCell.objects.create(page=page, placeholder='content', text='Foobar', slug='dup', order=2)
|
|
TextCell.objects.create(page=page, placeholder='content', text='Foobar', slug='dup', order=3)
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://example.net'))
|
|
assert '{% block placeholder-content %}{% block content %}{% endblock %}{% endblock %}' in resp.text
|
|
assert '{% block placeholder-footer %}{% block footer %}{% endblock %}{% endblock %}' in resp.text
|
|
assert 'Foobar' in resp.text
|
|
# check slugs
|
|
assert 'id="unique"' in resp.text
|
|
assert 'id="dup"' not in resp.text
|
|
|
|
# check {% now %} inside a skeleton_extra_placeholder is not interpreted
|
|
assert '{% now ' in resp.text
|
|
assert '{# generation time #}' in resp.text
|
|
|
|
# check cells in footer are present even if there's no redirection page
|
|
page.slug = 'index'
|
|
page.save()
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://127.0.0.1:8999/'))
|
|
assert '{% block placeholder-content %}{% block content %}{% endblock %}{% endblock %}' in resp.text
|
|
assert '{% block placeholder-footer %}{% block footer %}{% endblock %}{% endblock %}' in resp.text
|
|
assert 'Foobar' in resp.text
|
|
|
|
# check link cells provide a full URL
|
|
other_page = Page(title='Plop', slug='plop', template_name='standard')
|
|
other_page.save()
|
|
cell = LinkCell(page=page, placeholder='footer', link_page=other_page, order=0)
|
|
cell.save()
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://127.0.0.1:8999/'))
|
|
assert 'http://testserver/plop' in resp.text
|
|
|
|
# check images in text cell use full URL
|
|
cell = TextCell(page=page, placeholder='footer', order=0)
|
|
cell.text = '<img src="/test/foobar.png"> vs <img src="http://www.example.com/test.png">'
|
|
cell.save()
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://127.0.0.1:8999/'))
|
|
assert 'http://testserver/test/foobar.png' in resp.text
|
|
# check absolute URIs are not modified
|
|
assert 'src="http://www.example.com/test.png"' in resp.text
|
|
|
|
# check links in text cell use full URL (unless anchors and verbatim)
|
|
cell.text = '''<a href="/test/foobar"> vs
|
|
<a href="http://www.example.com/test"> vs'
|
|
<a href="#top">
|
|
<a href="{{ test_url }}">
|
|
<a href="{% verbatim %}{{ registration_url }}{% endverbatim %}">
|
|
'''
|
|
cell.save()
|
|
with override_settings(TEMPLATE_VARS={'test_url': 'http://example.net'}):
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://127.0.0.1:8999/'))
|
|
assert 'href="http://testserver/test/foobar"' in resp.text
|
|
assert 'href="http://www.example.com/test"' in resp.text
|
|
assert 'href="#top"' in resp.text
|
|
assert 'href="http://example.net"' in resp.text
|
|
assert 'href="{{ registration_url }}"' in resp.text
|
|
|
|
# add a bad redirection page (don't use it, do not crash)
|
|
page = Page(title='BadRedirection', slug='badredir', template_name='standard', redirect_url='[foo_bar]')
|
|
page.save()
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://example.net/foo/bar'))
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://example.net/badredir'))
|
|
|
|
# add a page with restricted visibility
|
|
page = Page.objects.create(
|
|
title='RestrictedVisibility',
|
|
slug='restrictedvisibilit',
|
|
template_name='standard',
|
|
public=False,
|
|
exclude_from_navigation=False,
|
|
)
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://127.0.0.1:8999/'))
|
|
assert 'RestrictedVisibility' in resp.text
|
|
|
|
# check 404 skeleton
|
|
resp = app.get('/__skeleton__/?source=404')
|
|
assert "This page doesn't exist" in resp.text
|
|
assert resp.status_code == 200
|
|
assert 'href="http://testserver/test/foobar"' in resp.text
|
|
assert 'href="http://www.example.com/test"' in resp.text
|
|
assert 'href="#top"' in resp.text
|
|
assert 'href="http://testserver/plop/"' in resp.text
|
|
|
|
# do not consider empty redirection
|
|
Page.objects.all().delete()
|
|
page = Page(
|
|
title='EmptyRedirection', slug='emptyredir', template_name='standard', redirect_url='{{ is_empty }}'
|
|
)
|
|
page.save()
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://127.0.0.1:8999/'))
|
|
assert resp.headers['x-combo-page-id'] == '__root'
|
|
|
|
# prefer last match
|
|
Page.objects.all().delete()
|
|
page = Page(
|
|
title='Elsewhere', slug='elsewhere', template_name='standard', redirect_url='http://example.net/foo/'
|
|
)
|
|
page.save()
|
|
cell = TextCell(page=page, placeholder='footer', text='Foobar1', order=0)
|
|
cell.save()
|
|
page = Page(
|
|
title='Elsewhere2',
|
|
slug='elsewhere2',
|
|
template_name='standard',
|
|
redirect_url='http://example.net/foo/',
|
|
order=2,
|
|
)
|
|
page.save()
|
|
cell = TextCell(page=page, placeholder='footer', text='Foobar2', order=0)
|
|
cell.save()
|
|
|
|
resp = app.get('/__skeleton__/?source=%s' % quote('http://example.net/foo/bar'))
|
|
assert "Foobar2" in resp.text
|
|
|
|
|
|
def test_subpage_location(app):
|
|
Page.objects.all().delete()
|
|
|
|
page_index = Page(title='Home Page', slug='index', template_name='standard')
|
|
page_index.save()
|
|
|
|
page_sibling = Page(title='Second top level page', slug='second', template_name='standard')
|
|
page_sibling.save()
|
|
|
|
page = Page(title='Child of Home', slug='child-home', template_name='standard', parent=page_index)
|
|
page.save()
|
|
|
|
page = Page(title='Grand child of home', slug='grand-child-home', template_name='standard', parent=page)
|
|
page.save()
|
|
|
|
page = Page(title='Child of second', slug='child-second', template_name='standard', parent=page_sibling)
|
|
page.save()
|
|
|
|
page = Page(
|
|
title='Grand child of second', slug='grand-child-second', template_name='standard', parent=page
|
|
)
|
|
page.save()
|
|
|
|
resp = app.get('/', status=200)
|
|
assert 'Home Page' in resp.text
|
|
|
|
resp = app.get('/child-home/', status=200)
|
|
assert 'Child of Home' in resp.text
|
|
|
|
resp = app.get('/child-home/grand-child-home/', status=200)
|
|
assert 'Grand child of home' in resp.text
|
|
|
|
assert (
|
|
urllib.parse.urlparse(app.get('/child-home/grand-child-home', status=301).location).path
|
|
== '/child-home/grand-child-home/'
|
|
)
|
|
app.get('/grand-child-home/', status=404)
|
|
|
|
resp = app.get('/second/', status=200)
|
|
assert 'Second top level page' in resp.text
|
|
|
|
resp = app.get('/second/child-second/', status=200)
|
|
assert 'Child of second' in resp.text
|
|
|
|
resp = app.get('/second/child-second/grand-child-second/', status=200)
|
|
assert 'Grand child of second' in resp.text
|
|
|
|
|
|
def test_repeated_slug(app):
|
|
Page.objects.all().delete()
|
|
|
|
page1 = Page(title='Home Page', slug='index', template_name='standard')
|
|
page1.save()
|
|
|
|
page2 = Page(title='Second top level page', slug='second', template_name='standard')
|
|
page2.save()
|
|
|
|
page3 = Page(title='Child of second', slug='child-second', template_name='standard', parent=page2)
|
|
page3.save()
|
|
|
|
page4 = Page(
|
|
title='Grand child of second (named third)', slug='third', template_name='standard', parent=page3
|
|
)
|
|
page4.save()
|
|
|
|
page5 = Page(title='Third top level page', slug='third', template_name='standard')
|
|
page5.save()
|
|
|
|
for i, page in enumerate((page1, page2, page3, page4, page5)):
|
|
page.order = i
|
|
page.save()
|
|
|
|
resp = app.get('/third/', status=200)
|
|
assert 'Third top level page' in resp.text
|
|
|
|
resp = app.get('/second/child-second/third/', status=200)
|
|
assert 'Grand child of second (named third)' in resp.text
|
|
|
|
# reverse page order, and expect same behaviour
|
|
for i, page in enumerate(reversed((page1, page2, page3, page4, page5))):
|
|
page.order = i
|
|
page.save()
|
|
|
|
resp = app.get('/third/', status=200)
|
|
assert 'Third top level page' in resp.text
|
|
|
|
resp = app.get('/second/child-second/third/', status=200)
|
|
assert 'Grand child of second (named third)' in resp.text
|
|
|
|
|
|
def test_menu(app):
|
|
Page.objects.all().delete()
|
|
page = Page.objects.create(
|
|
title='Page1', slug='index', template_name='standard', exclude_from_navigation=False
|
|
)
|
|
Page.objects.create(title='Page2', slug='page2', template_name='standard', exclude_from_navigation=False)
|
|
Page.objects.create(
|
|
title='Page3', slug='page3', template_name='standard', public=False, exclude_from_navigation=False
|
|
)
|
|
resp = app.get('/', status=200)
|
|
assert 'menu-index' in resp.text
|
|
assert 'data-menu-page-id="%s"' % page.id
|
|
assert 'menu-page2' in resp.text
|
|
assert 'menu-page3' in resp.text
|
|
|
|
|
|
def test_404(app):
|
|
Page.objects.all().delete()
|
|
resp = app.get('/foobar/', status=404)
|
|
assert "This page doesn't exist" in resp.text
|
|
|
|
# check footer cells are taken from homepage
|
|
index_page = Page(title='Home', slug='index', template_name='standard')
|
|
index_page.save()
|
|
cell = TextCell(page=index_page, placeholder='footer', text='FOOBAR', order=0)
|
|
cell.save()
|
|
resp = app.get('/foobar/', status=404)
|
|
assert "This page doesn't exist" in resp.text
|
|
assert "FOOBAR" in resp.text
|
|
index_page.delete()
|
|
|
|
# check decidated custom page
|
|
page = Page(title='Not Found', slug='404', template_name='standard')
|
|
page.save()
|
|
TextCell(page=page, placeholder='content', text='Custom 404 Text', order=0).save()
|
|
resp = app.get('/foobar/', status=404)
|
|
assert 'Custom 404 Text' in resp.text
|
|
|
|
with override_settings(DEBUG=True):
|
|
# check error page provides an hint when debugging
|
|
resp = app.get('/foobar/', status=404)
|
|
assert "can't find the requested page" in resp.text
|
|
|
|
# check native django handler is used if all pages are private
|
|
page.public = False
|
|
page.save()
|
|
resp = app.get('/foobar/', status=404)
|
|
assert 'Custom 404 Text' not in resp.text
|
|
assert "This page doesn't exist" not in resp.text
|
|
|
|
# check access using its /404/ slug
|
|
resp = app.get('/404', status=404)
|
|
|
|
# check 404 skeleton when all pages are private
|
|
resp = app.get('/__skeleton__/?source=404')
|
|
assert 'Custom 404 Text' not in resp.text
|
|
assert "This page doesn't exist" not in resp.text
|
|
assert resp.status_code == 200
|
|
|
|
|
|
def test_style_demo(app, admin_user):
|
|
TextCell.objects.all().delete()
|
|
Page.objects.all().delete()
|
|
app = login(app)
|
|
with override_settings(DEBUG=True):
|
|
resp = app.get('/__style__/', status=200)
|
|
assert 'class="success"' not in resp.text
|
|
assert 'class="info"' not in resp.text
|
|
assert 'class="warning"' not in resp.text
|
|
assert 'class="error"' not in resp.text
|
|
assert 'Lorem ipsum' in resp.text
|
|
assert TextCell.objects.count() == 0
|
|
assert Page.objects.count() == 0
|
|
|
|
resp = app.get('/__style__/?template=standard-sidebar&with_messages=on', status=200)
|
|
assert 'class="success"' in resp.text
|
|
assert 'class="info"' in resp.text
|
|
assert 'class="warning"' in resp.text
|
|
assert 'class="error"' in resp.text
|
|
assert 'Lorem ipsum' in resp.text
|
|
assert 'links-list' in resp.text
|
|
assert Page.objects.count() == 0
|
|
|
|
|
|
def test_page_async_cell(app, nocache):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
cell = FeedCell(page=page, placeholder='content', url='http://example.net', order=1)
|
|
cell.save()
|
|
resp = app.get('/', status=200)
|
|
assert 'data-ajax-cell-must-load="true"' in resp.text
|
|
|
|
|
|
def test_ajax_cell(app):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
page2 = Page(title='Other', slug='other', template_name='standard')
|
|
page2.save()
|
|
cell = TextCell(page=page, placeholder='content', text='<p>Foobar</p>', order=0)
|
|
cell.save()
|
|
resp = app.get(
|
|
reverse(
|
|
'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}
|
|
)
|
|
)
|
|
assert resp.text.strip() == '<p>Foobar</p>'
|
|
|
|
resp = app.get(
|
|
reverse(
|
|
'combo-public-ajax-page-cell',
|
|
kwargs={'page_pk': page2.id, 'cell_reference': cell.get_reference()},
|
|
),
|
|
status=404,
|
|
)
|
|
|
|
resp = app.get(
|
|
reverse(
|
|
'combo-public-ajax-page-cell', kwargs={'page_pk': '100', 'cell_reference': cell.get_reference()}
|
|
),
|
|
status=404,
|
|
)
|
|
|
|
page.public = False
|
|
page.save()
|
|
resp = app.get(
|
|
reverse(
|
|
'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}
|
|
),
|
|
status=403,
|
|
)
|
|
|
|
page.public = True
|
|
page.save()
|
|
cell.public = False
|
|
cell.save()
|
|
resp = app.get('/', status=200)
|
|
assert not 'FOOBAR' in resp.text
|
|
|
|
resp = app.get(
|
|
reverse(
|
|
'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}
|
|
),
|
|
status=403,
|
|
)
|
|
|
|
|
|
def test_initial_login_page(app, admin_user):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
page = Page(title='Initial Login', slug='initial-login', template_name='standard', public=False)
|
|
page.save()
|
|
|
|
site_settings = SiteSettings.get_singleton()
|
|
site_settings.initial_login_page_path = '/initial-login/'
|
|
site_settings.save()
|
|
|
|
resp = app.get('/', status=200)
|
|
|
|
# first visit
|
|
app = login(app)
|
|
resp = app.get('/', status=302)
|
|
assert urllib.parse.urlparse(resp.location).path == '/initial-login/'
|
|
|
|
# visit again
|
|
resp = app.get('/', status=200)
|
|
|
|
# delete initial login info
|
|
profile = Profile.objects.get(user=admin_user)
|
|
profile.initial_login_view_timestamp = None
|
|
profile.save(update_fields=['initial_login_view_timestamp'])
|
|
|
|
with override_settings(
|
|
TEMPLATE_VARS={
|
|
'portal_url': settings.SITE_BASE_URL,
|
|
'idp_url': settings.KNOWN_SERVICES['authentic']['idp']['url'],
|
|
}
|
|
):
|
|
# add templated login page path
|
|
site_settings.initial_login_page_path = '{{portal_url}}/foo/'
|
|
site_settings.save(update_fields=['initial_login_page_path'])
|
|
|
|
# "first" visit
|
|
app = login(app)
|
|
resp = app.get('/', status=302)
|
|
assert resp.location == 'http://localhost/foo/'
|
|
|
|
# delete initial login info, again
|
|
profile = Profile.objects.get(user=admin_user)
|
|
profile.initial_login_view_timestamp = None
|
|
profile.save(update_fields=['initial_login_view_timestamp'])
|
|
|
|
# add templated login page path to idp url
|
|
site_settings.initial_login_page_path = '{{idp_url}}/bar/'
|
|
site_settings.save(update_fields=['initial_login_page_path'])
|
|
|
|
# "first" visit
|
|
app = login(app)
|
|
resp = app.get('/', status=302)
|
|
assert resp.location == 'https://authentic.example.org/bar/'
|
|
|
|
profile.initial_login_view_timestamp = None
|
|
profile.save(update_fields=['initial_login_view_timestamp'])
|
|
|
|
site_settings.initial_login_page = page
|
|
site_settings.save()
|
|
|
|
# first visit
|
|
app = login(app)
|
|
resp = app.get('/', status=302)
|
|
assert urllib.parse.urlparse(resp.location).path == '/initial-login/'
|
|
|
|
# visit again
|
|
resp = app.get('/', status=200)
|
|
|
|
|
|
def test_welcome_page(app, admin_user):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
page = Page(title='Welcome', slug='welcome', template_name='standard')
|
|
page.save()
|
|
|
|
site_settings = SiteSettings.get_singleton()
|
|
site_settings.welcome_page_path = '/welcome/'
|
|
site_settings.save()
|
|
|
|
app.cookiejar.clear()
|
|
resp = app.get('/', status=302)
|
|
assert urllib.parse.urlparse(resp.location).path == '/welcome/'
|
|
|
|
resp = app.get('/', status=200)
|
|
|
|
app.cookiejar.clear()
|
|
resp = app.get('/', status=302)
|
|
assert urllib.parse.urlparse(resp.location).path == '/welcome/'
|
|
|
|
app.cookiejar.clear()
|
|
app = login(app)
|
|
resp = app.get('/', status=200)
|
|
|
|
with override_settings(
|
|
TEMPLATE_VARS={
|
|
'portal_url': settings.SITE_BASE_URL,
|
|
'idp_url': settings.KNOWN_SERVICES['authentic']['idp']['url'],
|
|
}
|
|
):
|
|
# add templated welcome page path
|
|
site_settings.welcome_page_path = '{{portal_url}}/foo/'
|
|
site_settings.save(update_fields=['welcome_page_path'])
|
|
|
|
app.cookiejar.clear()
|
|
resp = app.get('/', status=302)
|
|
assert resp.location == 'http://localhost/foo/'
|
|
|
|
# add templated login page path to idp url
|
|
site_settings.welcome_page_path = '{{idp_url}}/bar/'
|
|
site_settings.save(update_fields=['welcome_page_path'])
|
|
|
|
app.cookiejar.clear()
|
|
resp = app.get('/', status=302)
|
|
assert resp.location == 'https://authentic.example.org/bar/'
|
|
|
|
site_settings.welcome_page = page
|
|
site_settings.save()
|
|
|
|
app.cookiejar.clear()
|
|
resp = app.get('/', status=302)
|
|
assert urllib.parse.urlparse(resp.location).path == '/welcome/'
|
|
|
|
resp = app.get('/', status=200)
|
|
|
|
|
|
def test_post_cell(app):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
cell = TextCell(page=page, placeholder='content', text='<p>Foobar</p>', order=0)
|
|
cell.save()
|
|
|
|
# check it's not possible to post to cell that doesn't have POST support.
|
|
resp = app.post(
|
|
reverse(
|
|
'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}
|
|
),
|
|
params={'hello': 'world'},
|
|
status=403,
|
|
)
|
|
|
|
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-post-cell': {
|
|
'name': 'Foobar',
|
|
'url': 'http://test-post-cell/{{slug}}/',
|
|
'form': [
|
|
{'label': 'slug', 'varname': 'slug', 'required': True},
|
|
],
|
|
'actions': {
|
|
'create': {
|
|
'url': 'http://test-post-cell/{{slug}}/create/',
|
|
},
|
|
'update': {
|
|
'url': 'http://test-post-cell/{{slug}}/update/',
|
|
'method': 'PATCH',
|
|
},
|
|
'search': {
|
|
'url': 'http://test-post-cell/{{slug}}/search/',
|
|
'method': 'GET',
|
|
'response': 'raw',
|
|
},
|
|
'download': {
|
|
'url': 'http://test-post-cell/{{slug}}/download/',
|
|
'method': 'GET',
|
|
'response': 'raw',
|
|
},
|
|
},
|
|
}
|
|
},
|
|
TEMPLATES=templates_settings,
|
|
):
|
|
cell = ConfigJsonCell(page=page, placeholder='content', order=0)
|
|
cell.key = 'test-post-cell'
|
|
cell.parameters = {'slug': 'slug'}
|
|
cell.save()
|
|
|
|
with mock.patch('combo.utils.requests.get') as requests_get:
|
|
requests_get.return_value = mock.Mock(content=json.dumps({'hello': 'world'}), status_code=200)
|
|
|
|
resp = app.get('/', status=200)
|
|
resp.form['value'] = 'plop'
|
|
resp.form.fields['items[]'][0].checked = True
|
|
|
|
with mock.patch('combo.utils.requests.request') as requests_post:
|
|
requests_post.return_value = mock.Mock(content=json.dumps({'err': 0}), status_code=200)
|
|
resp2 = resp.form.submit()
|
|
assert requests_post.call_args[0][0] == 'POST'
|
|
assert requests_post.call_args[0][1] == 'http://test-post-cell/slug/create/'
|
|
assert requests_post.call_args[1]['json'] == {'value': 'plop', 'items': ['1']}
|
|
assert urllib.parse.urlparse(resp2.location).path == '/'
|
|
|
|
# check ajax call
|
|
with mock.patch('combo.utils.requests.request') as requests_post:
|
|
requests_post.return_value = mock.Mock(content=json.dumps({'err': 0}), status_code=200)
|
|
resp2 = resp.form.submit(headers={'x-requested-with': 'XMLHttpRequest'})
|
|
assert requests_post.call_args[0][0] == 'POST'
|
|
assert requests_post.call_args[0][1] == 'http://test-post-cell/slug/create/'
|
|
assert requests_post.call_args[1]['json'] == {'value': 'plop', 'items': ['1']}
|
|
assert resp2.text.startswith('<form')
|
|
|
|
# check error on POST
|
|
with mock.patch('combo.utils.requests.request') as requests_post:
|
|
requests_post.return_value = mock.Mock(content=json.dumps({'err': 0}), status_code=400)
|
|
resp2 = resp.form.submit()
|
|
assert urllib.parse.urlparse(resp2.location).path == '/'
|
|
resp2 = resp2.follow()
|
|
assert 'Error sending data.' in resp2.text
|
|
|
|
settings.JSON_CELL_TYPES['test-post-cell']['actions']['create'][
|
|
'error-message'
|
|
] = 'Failed to create stuff.'
|
|
resp2 = resp.form.submit()
|
|
assert urllib.parse.urlparse(resp2.location).path == '/'
|
|
resp2 = resp2.follow()
|
|
assert 'Failed to create stuff.' in resp2.text
|
|
|
|
with mock.patch('combo.utils.requests.request') as requests_post:
|
|
requests_post.return_value = mock.Mock(content=json.dumps({'err': 0}), status_code=400)
|
|
resp2 = resp.form.submit(headers={'x-requested-with': 'XMLHttpRequest'})
|
|
assert resp2.text.startswith('<form')
|
|
assert resp2.headers['x-error-message'] == 'Failed to create stuff.'
|
|
|
|
# check variable substitution in URL
|
|
with mock.patch('combo.utils.requests.request') as requests_post:
|
|
settings.JSON_CELL_TYPES['test-post-cell']['actions']['create'] = {
|
|
'url': 'http://test-post-cell/{{slug}}/{{value}}/delete'
|
|
}
|
|
resp2 = resp.form.submit()
|
|
assert requests_post.call_args[0][0] == 'POST'
|
|
assert requests_post.call_args[0][1] == 'http://test-post-cell/slug/plop/delete'
|
|
|
|
# check patch method
|
|
with mock.patch('combo.utils.requests.request') as requests_patch:
|
|
resp.form['action'] = 'update'
|
|
requests_patch.return_value = mock.Mock(content=json.dumps({'err': 0}), status_code=200)
|
|
resp2 = resp.form.submit()
|
|
assert requests_patch.call_args[0][0] == 'PATCH'
|
|
assert requests_patch.call_args[0][1] == 'http://test-post-cell/slug/update/'
|
|
assert requests_patch.call_args[1]['json'] == {'value': 'plop', 'items': ['1']}
|
|
assert urllib.parse.urlparse(resp2.location).path == '/'
|
|
|
|
# check raw result
|
|
with mock.patch('combo.utils.requests.request') as requests_search:
|
|
resp.form['action'] = 'search'
|
|
requests_search.return_value = mock.Mock(
|
|
content=json.dumps({'foo': 'bar'}),
|
|
status_code=200,
|
|
headers={'Content-Type': 'application/json'},
|
|
)
|
|
resp2 = resp.form.submit()
|
|
assert requests_search.call_args[0][0] == 'GET'
|
|
assert requests_search.call_args[0][1] == 'http://test-post-cell/slug/search/'
|
|
assert resp2.json == {'foo': 'bar'}
|
|
|
|
# check raw result with content-disposition
|
|
with mock.patch('combo.utils.requests.request') as requests_search:
|
|
resp.form['action'] = 'download'
|
|
requests_search.return_value = mock.Mock(
|
|
content=b'a,b\n1,2',
|
|
status_code=200,
|
|
headers={
|
|
'Content-Type': 'application/octet-stream',
|
|
'Content-Disposition': 'attachment; filename=table.csv',
|
|
},
|
|
)
|
|
resp2 = resp.form.submit()
|
|
assert requests_search.call_args[0][0] == 'GET'
|
|
assert requests_search.call_args[0][1] == 'http://test-post-cell/slug/download/'
|
|
assert resp2['Content-Type'] == 'application/octet-stream'
|
|
assert resp2['Content-Disposition'] == 'attachment; filename=table.csv'
|
|
assert resp2.content == b'a,b\n1,2'
|
|
|
|
|
|
def test_synchronous_placeholder(app):
|
|
page = Page(title='foo', slug='foo', template_name='standard', order=0)
|
|
page.save()
|
|
cell = FeedCell(page=page, placeholder='content', url='http://example.net', order=1)
|
|
cell.save()
|
|
|
|
templates_settings = [settings.TEMPLATES[0].copy()]
|
|
templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))]
|
|
with override_settings(
|
|
COMBO_PUBLIC_TEMPLATES={
|
|
'standard': {
|
|
'name': 'Test',
|
|
'template': 'combo/page_template_synchronous_placeholder.html',
|
|
}
|
|
},
|
|
TEMPLATES=templates_settings,
|
|
):
|
|
with mock.patch('requests.get') as requests_get:
|
|
requests_get.return_value = mock.Mock(content='', status_code=200)
|
|
resp = app.get('/foo/', status=200)
|
|
assert not 'data-ajax-cell-must-load="true"' in resp.text
|
|
|
|
cell = FeedCell(page=page, placeholder='sidebar', url='http://example.org', order=1)
|
|
cell.save()
|
|
|
|
resp = app.get('/foo/', status=200)
|
|
assert resp.text.count('data-ajax-cell-must-load="true"') == 1
|
|
|
|
requests_get.side_effect = requests.exceptions.ConnectionError()
|
|
resp = app.get('/foo/', status=200)
|
|
|
|
|
|
def test_outer_tag_placeholder(app):
|
|
page = Page(title='foo', slug='foo', template_name='standard', order=0)
|
|
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(
|
|
COMBO_PUBLIC_TEMPLATES={
|
|
'standard': {
|
|
'name': 'Test',
|
|
'template': 'combo/page_template_outer_tag_placeholder.html',
|
|
}
|
|
},
|
|
TEMPLATES=templates_settings,
|
|
):
|
|
resp = app.get('/foo/', status=200)
|
|
assert not resp.pyquery('div.combo-placeholder--content')
|
|
assert not resp.pyquery('aside.combo-placeholder--sidebar')
|
|
|
|
TextCell.objects.create(page=page, placeholder='content', text='Foobar', order=0)
|
|
TextCell.objects.create(page=page, placeholder='sidebar', text='Foobar', order=0)
|
|
resp = app.get('/foo/', status=200)
|
|
assert resp.pyquery('div.combo-placeholder--content')
|
|
assert resp.pyquery('aside.combo-placeholder--sidebar')
|
|
|
|
|
|
def test_redirects(app):
|
|
Redirect.objects.all().delete()
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
|
|
page2 = Page(title='Second', slug='second', template_name='standard')
|
|
page2.save()
|
|
|
|
page3 = Page(title='Third', slug='third', template_name='standard', parent=page2)
|
|
page3.save()
|
|
|
|
app.get('/whatever/', status=404)
|
|
|
|
redirect = Redirect(old_url='/whatever/', page=page3)
|
|
redirect.save()
|
|
|
|
assert urllib.parse.urlparse(app.get('/whatever/', status=302).location).path == '/second/third/'
|
|
assert urllib.parse.urlparse(app.get('/whatever', status=301).location).path == '/whatever/'
|
|
|
|
# check the most recent redirect is called
|
|
redirect = Redirect(old_url='/whatever/', page=page2)
|
|
redirect.save()
|
|
assert urllib.parse.urlparse(app.get('/whatever/', status=302).location).path == '/second/'
|
|
|
|
# rename page
|
|
page3.slug = 'third2'
|
|
page3.save()
|
|
assert app.get('/second/third2/', status=200)
|
|
assert urllib.parse.urlparse(app.get('/second/third/', status=302).location).path == '/second/third2/'
|
|
|
|
page2.slug = 'second2'
|
|
page2.save()
|
|
assert urllib.parse.urlparse(app.get('/second/third/', status=302).location).path == '/second2/third2/'
|
|
assert urllib.parse.urlparse(app.get('/second/third2/', status=302).location).path == '/second2/third2/'
|
|
assert urllib.parse.urlparse(app.get('/second/', status=302).location).path == '/second2/'
|
|
|
|
# change parent
|
|
page3.parent = None
|
|
page3.save()
|
|
assert urllib.parse.urlparse(app.get('/second/third/', status=302).location).path == '/third2/'
|
|
assert urllib.parse.urlparse(app.get('/second2/third2/', status=302).location).path == '/third2/'
|
|
|
|
|
|
def test_sub_slug(app, john_doe, jane_doe):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
|
|
page2 = Page(title='User', slug='users', sub_slug='(?P<blah>[a-z]+)', template_name='standard')
|
|
page2.save()
|
|
|
|
page3 = Page(title='Blah', slug='blah', parent=page2, template_name='standard')
|
|
page3.save()
|
|
|
|
# without passing sub slug
|
|
assert app.get('/users/', status=302).location in ('..', 'http://testserver/')
|
|
# (result vary between django versions)
|
|
|
|
# json cell so we can display the parameter value
|
|
cell = JsonCell(page=page2, url='http://example.net', order=0, placeholder='content')
|
|
cell.template_string = 'XX{{ blah }}YY'
|
|
cell.save()
|
|
|
|
cell2 = JsonCell(page=page3, url='http://example.net', order=0, placeholder='content')
|
|
cell2.template_string = 'AA{{ blah }}BB{{ absolute_uri }}CC'
|
|
cell2.save()
|
|
|
|
with mock.patch('combo.utils.requests.get') as requests_get:
|
|
data = {'data': [{'url': 'http://a.b', 'text': 'xxx'}]}
|
|
requests_get.return_value = mock.Mock(content=json.dumps(data), status_code=200)
|
|
resp = app.get('/users/whatever/', status=200)
|
|
assert 'XXwhateverYY' in resp.text
|
|
cell_url = re.findall(r'data-ajax-cell-url="(.*)"', resp.text)[0]
|
|
extra_ctx = re.findall(r'data-extra-context="(.*)"', resp.text)[0]
|
|
resp = app.get(cell_url + '?ctx=' + extra_ctx)
|
|
assert resp.text == 'XXwhateverYY'
|
|
|
|
# 404 on value that doesn't match the regex
|
|
resp = app.get('/users/WHATEVER/', status=404)
|
|
|
|
# check sub page
|
|
resp = app.get('/users/whatever/plop/', status=404)
|
|
resp = app.get('/users/whatever/blah/', status=200)
|
|
cell_url = re.findall(r'data-ajax-cell-url="(.*)"', resp.text)[0]
|
|
extra_ctx = re.findall(r'data-extra-context="(.*)"', resp.text)[0]
|
|
resp = app.get(cell_url + '?ctx=' + extra_ctx)
|
|
assert resp.text == 'AAwhateverBBhttp://testserver/users/whatever/blah/CC'
|
|
|
|
# custom behaviour for <user_id>, it will add the user to context
|
|
for sub_slug in ['(?P<user_id>[0-9a-z]+)', 'user_id']:
|
|
page2.sub_slug = sub_slug
|
|
page2.save()
|
|
|
|
cell.template_string = 'XX{{ selected_user.username }}YY'
|
|
cell.save()
|
|
|
|
resp = app.get('/users/%s/' % john_doe.id, status=200)
|
|
assert 'XXjohn.doeYY' in resp.text
|
|
|
|
# bad user id => no selected_user
|
|
resp = app.get('/users/9999999/', status=200)
|
|
assert 'XXYY' in resp.text
|
|
resp = app.get('/users/abc/', status=200)
|
|
assert 'XXYY' in resp.text
|
|
|
|
# custom behaviour for <name_id>, it will add the SAML user to context
|
|
with mock.patch('combo.profile.utils.UserSAMLIdentifier') as user_saml:
|
|
|
|
class DoesNotExist(Exception):
|
|
pass
|
|
|
|
user_saml.DoesNotExist = DoesNotExist
|
|
|
|
def side_effect(*args, **kwargs):
|
|
name_id = kwargs['name_id']
|
|
if name_id == 'foo':
|
|
raise user_saml.DoesNotExist
|
|
return mock.Mock(user=john_doe)
|
|
|
|
mocked_objects = mock.Mock()
|
|
mocked_objects.get = mock.Mock(side_effect=side_effect)
|
|
user_saml.objects = mocked_objects
|
|
|
|
page2.sub_slug = '(?P<name_id>[0-9a-z.]+)'
|
|
page2.save()
|
|
resp = app.get('/users/john.doe/', status=200)
|
|
assert 'XXjohn.doeYY' in resp.text
|
|
# unknown name id => no selected_user
|
|
resp = app.get('/users/foo/', status=200)
|
|
assert 'XXYY' in resp.text
|
|
|
|
# sub_slug can contain '-' (if card slug for example)
|
|
page3 = Page.objects.create(title='Card Foo', slug='foo', template_name='standard')
|
|
for sub_slug in ['(?P<card-foo-bar_id>[0-9]+)', 'card-foo-bar_id']:
|
|
page3.sub_slug = sub_slug
|
|
page3.save()
|
|
resp = app.get('/foo/42/', status=200)
|
|
assert resp.context['card-foo-bar_id'] == '42'
|
|
assert resp.context['card_foo_bar_id'] == '42'
|
|
|
|
|
|
def test_page_extra_variables(app):
|
|
page = Page.objects.create(
|
|
title='Home',
|
|
slug='page',
|
|
template_name='standard',
|
|
extra_variables={
|
|
'foo': 'bar',
|
|
'bar_id': '{{ 40|add:2 }}',
|
|
'syntax_error': '{% for %}',
|
|
'variable_error': '{{ "foo"|add:user.email }}',
|
|
},
|
|
)
|
|
cell = JsonCell.objects.create(
|
|
page=page,
|
|
url='http://example.net',
|
|
order=0,
|
|
placeholder='content',
|
|
template_string='XX{{ foo }}YY{{ bar_id }}ZZ{{ baz_user }}AA{{ syntax_error }}BB{{ variable_error }}CC',
|
|
)
|
|
|
|
with mock.patch('combo.utils.requests.get') as requests_get:
|
|
requests_get.return_value = mock.Mock(content='{}', status_code=200)
|
|
resp = app.get('/page/')
|
|
assert '<div>XXbarYY42ZZAABBCC</div>' in resp
|
|
|
|
with mock.patch('combo.utils.requests.get') as requests_get:
|
|
requests_get.return_value = mock.Mock(content='{}', status_code=200)
|
|
resp = app.get(
|
|
reverse(
|
|
'combo-public-ajax-page-cell',
|
|
kwargs={'page_pk': page.pk, 'cell_reference': cell.get_reference()},
|
|
)
|
|
)
|
|
assert resp.text.strip() == 'XXbarYY42ZZAABBCC'
|
|
|
|
# check sub_slug/extra_variables override
|
|
page.sub_slug = '(?P<fooo>[a-z]+)'
|
|
page.save()
|
|
cell.template_string = 'XX{{ foo }}YY{{ bar_id }}ZZ{{ fooo }}AA'
|
|
cell.save()
|
|
with mock.patch('combo.utils.requests.get') as requests_get:
|
|
requests_get.return_value = mock.Mock(content='{}', status_code=200)
|
|
resp = app.get('/page/baz/')
|
|
assert '<div>XXbarYY42ZZbazAA</div>' in resp
|
|
|
|
page.sub_slug = '(?P<foo>[a-z]+)'
|
|
page.save()
|
|
with mock.patch('combo.utils.requests.get') as requests_get:
|
|
requests_get.return_value = mock.Mock(content='{}', status_code=200)
|
|
resp = app.get('/page/baz/')
|
|
assert '<div>XXbarYY42ZZAA</div>' in resp
|
|
|
|
|
|
def test_cell_slugs(app):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
|
|
TextCell(page=page, placeholder='content', text='Foobar', order=0).save()
|
|
TextCell(page=page, placeholder='content', text='Foobar', slug='unique', order=1).save()
|
|
TextCell(page=page, placeholder='content', text='Foobar', slug='dup', order=2).save()
|
|
TextCell(page=page, placeholder='content', text='Foobar', slug='dup', order=3).save()
|
|
|
|
resp = app.get('/', status=200)
|
|
assert 'id="unique"' in resp.text
|
|
assert 'id="dup"' not in resp.text
|
|
|
|
|
|
def test_missing_trailing_slashes(app):
|
|
# redirect to path with slash
|
|
assert urllib.parse.urlparse(app.get('/login', status=301).location).path == '/login/'
|
|
assert urllib.parse.urlparse(app.get('/foo', status=301).location).path == '/foo/'
|
|
# don't be tricked by double slashes
|
|
assert urllib.parse.urlparse(app.get('//foo', status=301).location).path == '/foo/'
|
|
|
|
|
|
def test_cell_asset_css_classes(settings, app, admin_user):
|
|
settings.COMBO_CELL_ASSET_SLOTS = {}
|
|
page = Page.objects.create(title='Home', slug='index', template_name='standard')
|
|
cell = TextCell.objects.create(page=page, placeholder='content', order=0, slug='foo', text='foo')
|
|
|
|
# test direct access
|
|
assert cell.asset_css_classes == ''
|
|
# and test asset preload
|
|
resp = app.get('/', status=200)
|
|
assert 'class="cell text-cell textcell foo"' in re.sub(r' +', ' ', resp.text)
|
|
|
|
settings.COMBO_CELL_ASSET_SLOTS = {'data_textcell': {'picture': {'prefix': 'Picture'}}}
|
|
cell = TextCell.objects.get(pk=cell.pk)
|
|
assert cell.asset_css_classes == ''
|
|
resp = app.get('/', status=200)
|
|
assert 'class="cell text-cell textcell foo"' in re.sub(r' +', ' ', resp.text)
|
|
|
|
Asset.objects.create(key=cell.get_asset_slot_key('picture'), asset=File(StringIO('test'), 'test.png'))
|
|
cell = TextCell.objects.get(pk=cell.pk)
|
|
assert cell.asset_css_classes == 'has-asset-picture has-any-asset has-all-assets'
|
|
resp = app.get('/', status=200)
|
|
assert 'class="cell text-cell textcell has-asset-picture has-any-asset has-all-assets foo"' in re.sub(
|
|
r' +', ' ', resp.text
|
|
)
|
|
|
|
settings.COMBO_CELL_ASSET_SLOTS = {
|
|
'data_textcell': {'picture': {'prefix': 'Picture'}, 'foo': {'prefix': 'Foo'}}
|
|
}
|
|
cell = TextCell.objects.get(pk=cell.pk)
|
|
assert cell.asset_css_classes == 'has-asset-picture has-any-asset'
|
|
resp = app.get('/', status=200)
|
|
assert 'class="cell text-cell textcell has-asset-picture has-any-asset foo"' in re.sub(
|
|
r' +', ' ', resp.text
|
|
)
|
|
|
|
Asset.objects.create(key=cell.get_asset_slot_key('foo'), asset=File(StringIO('test'), 'test.png'))
|
|
cell = TextCell.objects.get(pk=cell.pk)
|
|
assert cell.asset_css_classes == 'has-asset-foo has-asset-picture has-any-asset has-all-assets'
|
|
resp = app.get('/', status=200)
|
|
assert (
|
|
'class="cell text-cell textcell has-asset-foo has-asset-picture has-any-asset has-all-assets foo"'
|
|
in re.sub(r' +', ' ', resp.text)
|
|
)
|
|
|
|
Asset.objects.all().delete()
|
|
cell = TextCell.objects.get(pk=cell.pk)
|
|
assert cell.asset_css_classes == ''
|
|
resp = app.get('/', status=200)
|
|
assert 'class="cell text-cell textcell foo"' in re.sub(r' +', ' ', resp.text)
|
|
|
|
|
|
def test_placeholder_grid(app):
|
|
Page.objects.all().delete()
|
|
page = Page(title='Home', slug='index', template_name='standard')
|
|
page.save()
|
|
|
|
TextCell.objects.create(page=page, placeholder='content', text='Foobar', order=1)
|
|
|
|
resp = app.get('/', status=200)
|
|
# no placeholder div if there's no grid layout
|
|
assert resp.pyquery('#content > .text-cell')
|
|
|
|
page.placeholder_options = {'content': {'fx_grid_layout': 'fx-grid'}}
|
|
page.save()
|
|
|
|
resp = app.get('/', status=200)
|
|
# placeholder div in between
|
|
assert resp.pyquery('#content > .fx-grid > .text-cell')
|