2393 lines
93 KiB
Python
2393 lines
93 KiB
Python
import base64
|
||
import datetime
|
||
import json
|
||
import mock
|
||
import os
|
||
import re
|
||
import shutil
|
||
|
||
from django.core.files import File
|
||
from django.core.files.storage import default_storage
|
||
from django.core.serializers.base import DeserializationError
|
||
from django.urls import reverse
|
||
from django.conf import settings
|
||
from django.contrib.auth.models import Group
|
||
from django.db import connection
|
||
from django.template import TemplateSyntaxError
|
||
from django.test import override_settings
|
||
from django.test.client import RequestFactory
|
||
from django.test.utils import CaptureQueriesContext
|
||
from django.utils.encoding import force_bytes, force_str
|
||
from django.utils.http import urlencode
|
||
from django.utils.timezone import now
|
||
from django.utils.six import BytesIO
|
||
from django.utils.six.moves.urllib import parse as urlparse
|
||
|
||
import pytest
|
||
from webtest import Upload
|
||
|
||
from combo.data.forms import LinkCellForm
|
||
from combo.data.models import (
|
||
Page,
|
||
CellBase,
|
||
TextCell,
|
||
LinkCell,
|
||
ConfigJsonCell,
|
||
JsonCell,
|
||
PageSnapshot,
|
||
LinkListCell,
|
||
ParentContentCell,
|
||
MenuCell,
|
||
ValidityInfo,
|
||
)
|
||
from combo.manager.forms import PageAddForm
|
||
from combo.manager.forms import PageSelectTemplateForm
|
||
from combo.apps.assets.models import Asset
|
||
from combo.apps.maps.models import MapLayer
|
||
from combo.apps.family.models import FamilyInfosCell
|
||
from combo.apps.search.models import SearchCell
|
||
|
||
pytestmark = pytest.mark.django_db
|
||
|
||
TESTS_DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
|
||
|
||
|
||
def login(app, username='admin', password='admin'):
|
||
login_page = app.get('/login/')
|
||
login_form = login_page.forms[0]
|
||
login_form['username'] = username
|
||
login_form['password'] = password
|
||
resp = login_form.submit()
|
||
assert resp.status_int == 302
|
||
return app
|
||
|
||
|
||
def test_unlogged_access(app):
|
||
# connect while not being logged in
|
||
assert app.get('/manage/', status=302).location.endswith('/login/?next=/manage/')
|
||
|
||
|
||
def test_access(app, admin_user):
|
||
app = login(app)
|
||
resp = app.get('/manage/', status=200)
|
||
assert 'Pages' in resp.text
|
||
assert "This site doesn't have any page yet." in resp.text
|
||
|
||
|
||
@pytest.mark.parametrize('collapse', [True, False])
|
||
def test_pages_collapse(settings, app, admin_user, collapse):
|
||
Page.objects.create(title='One', slug='one')
|
||
settings.COMBO_MANAGE_HOME_COLLAPSE_PAGES = collapse
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/', status=200)
|
||
assert resp.context['collapse_pages'] is collapse
|
||
assert ('class="page level-0 untoggled"' in resp.text) is collapse
|
||
assert ('class="page level-0"' in resp.text) is not collapse
|
||
|
||
|
||
def test_add_page(app, admin_user):
|
||
app = login(app)
|
||
resp = app.get('/manage/', status=200)
|
||
resp = resp.click('New')
|
||
assert resp.forms[0]['title'].value == 'Home' # default title for first page
|
||
resp = resp.forms[0].submit()
|
||
page = Page.objects.latest('pk')
|
||
assert resp.location.endswith('/manage/pages/%s/' % page.pk)
|
||
assert page.slug == 'index'
|
||
assert page.title == 'Home'
|
||
assert page.template_name == 'standard' # default template
|
||
assert page.exclude_from_navigation is True
|
||
|
||
model_root_page = Page.objects.create(slug='modeles', title='Models')
|
||
model_a = Page.objects.create(
|
||
slug='model-a', title='Model A', template_name='two-columns', parent=model_root_page
|
||
)
|
||
TextCell.objects.create(page=model_a, placeholder='content', text='Foobar', order=1)
|
||
|
||
resp = app.get('/manage/pages/add/')
|
||
resp.forms[0]['title'].value = 'Page A'
|
||
resp.forms[0]['template_name'].value = 'model_model-a'
|
||
resp = resp.forms[0].submit()
|
||
new_page = Page.objects.latest('pk')
|
||
assert resp.location.endswith('/manage/pages/%s/' % new_page.pk)
|
||
assert new_page.slug == 'page-a'
|
||
assert new_page.title == 'Page A'
|
||
assert new_page.template_name == 'two-columns'
|
||
assert new_page.parent is None
|
||
assert TextCell.objects.filter(page=new_page).count() == 1
|
||
assert ParentContentCell.objects.filter(page=new_page).count() == 0
|
||
|
||
|
||
def test_add_second_page(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one')
|
||
page.save()
|
||
app = login(app)
|
||
resp = app.get('/manage/', status=200)
|
||
resp = resp.click('New')
|
||
# assert there's no defaul title for the second page
|
||
assert resp.forms[0]['title'].value == ''
|
||
resp.forms[0]['title'].value = 'Foobar'
|
||
resp = resp.forms[0].submit()
|
||
assert Page.objects.get(slug='foobar').title == 'Foobar'
|
||
|
||
|
||
def test_add_child_page(app, admin_user):
|
||
page = Page(title='One', slug='one')
|
||
page.save()
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.pk, status=200)
|
||
resp = resp.click(href='.*/add/')
|
||
resp.forms[0]['title'].value = 'Two'
|
||
resp = resp.forms[0].submit()
|
||
child = Page.objects.latest('pk')
|
||
assert resp.location.endswith('/manage/pages/%s/' % child.pk)
|
||
assert child.title == 'Two'
|
||
assert child.parent == page
|
||
|
||
app.get('/manage/pages/%s/add/' % child.pk, status=200)
|
||
app.get('/manage/pages/42/add/', status=404)
|
||
|
||
model_root_page = Page.objects.create(slug='modeles', title='Models')
|
||
model_a = Page.objects.create(
|
||
slug='model-a', title='Model A', template_name='two-columns', parent=model_root_page
|
||
)
|
||
TextCell.objects.create(page=model_a, placeholder='content', text='Foobar', order=1)
|
||
|
||
resp = app.get('/manage/pages/%s/add/' % page.pk)
|
||
resp.forms[0]['title'].value = 'Page A'
|
||
resp.forms[0]['template_name'].value = 'model_model-a'
|
||
resp = resp.forms[0].submit()
|
||
new_page = Page.objects.latest('pk')
|
||
assert resp.location.endswith('/manage/pages/%s/' % new_page.pk)
|
||
assert new_page.slug == 'page-a'
|
||
assert new_page.title == 'Page A'
|
||
assert new_page.template_name == 'two-columns'
|
||
assert new_page.parent == page
|
||
assert TextCell.objects.filter(page=new_page).count() == 1
|
||
assert ParentContentCell.objects.filter(page=new_page).count() == 0
|
||
|
||
|
||
def test_page_add_forms(settings):
|
||
settings.COMBO_PUBLIC_TEMPLATES = {
|
||
'one': {
|
||
'name': 'BBB',
|
||
'template': 'combo/page_template.html',
|
||
},
|
||
'two': {
|
||
'name': 'AAA',
|
||
'template': 'combo/page_template_sidebar.html',
|
||
},
|
||
'unknown': {
|
||
'name': 'Unknown',
|
||
'template': 'combo/unknown.html',
|
||
},
|
||
}
|
||
settings.COMBO_DEFAULT_PUBLIC_TEMPLATE = 'one'
|
||
request = RequestFactory().get('/')
|
||
form = PageAddForm(request=request)
|
||
assert form.fields['template_name'].widget.choices == [('two', 'AAA'), ('one', 'BBB')]
|
||
assert form['template_name'].initial == 'one'
|
||
|
||
model_root_page = Page.objects.create(slug='modeles', title='Models')
|
||
form = PageAddForm(request=request)
|
||
assert form.fields['template_name'].widget.choices == [('two', 'AAA'), ('one', 'BBB')]
|
||
model_a = Page.objects.create(slug='model-a', title='Model A', parent=model_root_page)
|
||
Page.objects.create(slug='model-aa', title='Model AA', parent=model_a)
|
||
Page.objects.create(slug='model-b', title='Model B', parent=model_root_page)
|
||
normal_page = Page.objects.create(slug='models', title='Wrong Models')
|
||
Page.objects.create(slug='model-c', title='Not a model (wrong parent)')
|
||
Page.objects.create(slug='model-d', title='Again not a model (wrong parent)', parent=normal_page)
|
||
form = PageAddForm(request=request)
|
||
assert form.fields['template_name'].widget.choices == [
|
||
('two', 'AAA'),
|
||
('one', 'BBB'),
|
||
('model_model-a', 'Model A'),
|
||
('model_model-aa', 'Model AA'),
|
||
('model_model-b', 'Model B'),
|
||
]
|
||
|
||
settings.COMBO_PUBLIC_TEMPLATES_ROOT_PAGE = None
|
||
form = PageAddForm(request=request)
|
||
assert form.fields['template_name'].widget.choices == [('two', 'AAA'), ('one', 'BBB')]
|
||
|
||
|
||
def test_edit_page(app, admin_user):
|
||
Page.objects.all().delete()
|
||
app = login(app)
|
||
resp = app.get('/manage/', status=200)
|
||
resp = resp.click('New')
|
||
resp = resp.forms[0].submit()
|
||
resp = resp.follow()
|
||
# title
|
||
resp = resp.click(href='.*/title')
|
||
resp.form['title'].value = 'Home 2'
|
||
resp = resp.form.submit()
|
||
resp = resp.follow()
|
||
assert Page.objects.all()[0].title == 'Home 2'
|
||
# description
|
||
resp = resp.click(href='.*/description')
|
||
resp.form['description'].value = 'second home page'
|
||
resp = resp.form.submit()
|
||
resp = resp.follow()
|
||
assert Page.objects.all()[0].description == 'second home page'
|
||
# template name
|
||
resp = resp.click(href='.*/template')
|
||
assert resp.form['template_name'].value == 'standard'
|
||
assert len(resp.form['template_name'].options) == 2 # by default there are 2 templates
|
||
resp.form['template_name'].value = 'standard-sidebar'
|
||
resp = resp.form.submit()
|
||
resp = resp.follow()
|
||
assert Page.objects.all()[0].template_name == 'standard-sidebar'
|
||
assert resp.click(href='.*/template').form['template_name'].value == 'standard-sidebar'
|
||
# redirection
|
||
resp = resp.click(href='.*/redirection')
|
||
resp.form['redirect_url'].value = 'http://www.example.net'
|
||
resp = resp.form.submit()
|
||
resp = resp.follow()
|
||
assert 'http://www.example.net' in resp.text
|
||
assert Page.objects.all()[0].redirect_url == 'http://www.example.net'
|
||
# exclude from nav
|
||
resp = resp.click(href='.*/include-in-navigation')
|
||
resp.form['include_in_navigation'].checked = False
|
||
resp = resp.form.submit()
|
||
resp = resp.follow()
|
||
assert Page.objects.all()[0].exclude_from_navigation is True
|
||
# include from nav
|
||
resp = resp.click(href='.*/include-in-navigation')
|
||
resp.form['include_in_navigation'].checked = True
|
||
resp = resp.form.submit()
|
||
resp = resp.follow()
|
||
assert Page.objects.all()[0].exclude_from_navigation is False
|
||
|
||
|
||
def test_page_edit_template_form(settings):
|
||
page = Page.objects.create(title='One', slug='one', template_name='two')
|
||
settings.COMBO_PUBLIC_TEMPLATES = {
|
||
'one': {
|
||
'name': 'BBB',
|
||
'template': 'combo/page_template.html',
|
||
},
|
||
'two': {
|
||
'name': 'AAA',
|
||
'template': 'combo/page_template_sidebar.html',
|
||
},
|
||
'unknown': {
|
||
'name': 'Unknown',
|
||
'template': 'combo/unknown.html',
|
||
},
|
||
}
|
||
settings.COMBO_DEFAULT_PUBLIC_TEMPLATE = 'one'
|
||
# model pages ignored
|
||
model_root_page = Page.objects.create(slug='modeles')
|
||
Page.objects.create(slug='model-a', title='Model A', parent=model_root_page)
|
||
|
||
form = PageSelectTemplateForm(instance=page)
|
||
assert form.fields['template_name'].widget.choices == [('two', 'AAA'), ('one', 'BBB')]
|
||
assert form['template_name'].initial == 'two'
|
||
|
||
|
||
def test_edit_page_cell_invalid_placeholder(freezer, app, admin_user):
|
||
freezer.move_to('2020-06-05 12:00:01')
|
||
page = Page.objects.create(title='One', slug='one', template_name='standard')
|
||
cell = TextCell.objects.create(page=page, placeholder='content', text='Foobar', order=1)
|
||
cell.mark_as_invalid('foo_bar_reason')
|
||
|
||
app = login(app)
|
||
ValidityInfo.objects.update(invalid_since=now() - datetime.timedelta(minutes=1))
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert '<span class="invalid">foo_bar_reason' in resp.text
|
||
assert 'This cell will no longer be displayed in 1 day, 23 hours' in resp.text
|
||
|
||
ValidityInfo.objects.update(invalid_since=now() - datetime.timedelta(minutes=47 * 60 - 1))
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert '<span class="invalid">foo_bar_reason' in resp.text
|
||
assert 'This cell will no longer be displayed in 1 hour, 1 minute.' in resp.text
|
||
|
||
ValidityInfo.objects.update(invalid_since=now() - datetime.timedelta(minutes=47 * 60 + 29))
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert '<span class="invalid">foo_bar_reason' in resp.text
|
||
assert 'This cell will no longer be displayed in 31 minutes.' in resp.text
|
||
|
||
ValidityInfo.objects.update(invalid_since=now() - datetime.timedelta(days=2))
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert '<span class="invalid">foo_bar_reason' in resp.text
|
||
assert 'This cell is no longer displayed since June 5, 2020, noon.' in resp.text
|
||
|
||
ValidityInfo.objects.update(invalid_since=now() - datetime.timedelta(days=2, minutes=10 * 60 + 2))
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert '<span class="invalid">foo_bar_reason' in resp.text
|
||
assert 'This cell is no longer displayed since June 5, 2020, 1:58 a.m.' in resp.text
|
||
|
||
cell.mark_as_valid()
|
||
assert ValidityInfo.objects.exists() is False
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert '<span class="invalid">foo_bar_reason' not in resp.text
|
||
|
||
cell2 = LinkListCell.objects.create(order=0, placeholder='content', page=page)
|
||
item = LinkCell.objects.create(page=page, placeholder=cell2.link_placeholder, order=0)
|
||
item.mark_as_invalid('foo_bar_reason')
|
||
cell2.check_validity()
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert '<span class="invalid">Invalid link</span>' in resp.text
|
||
assert '<span class="invalid">foo_bar_reason' in resp.text
|
||
assert 'This link will no longer be displayed in 2 days.' in resp.text
|
||
ValidityInfo.objects.update(invalid_since=now() - datetime.timedelta(days=2))
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert '<span class="invalid">Invalid link</span>' in resp.text
|
||
assert '<span class="invalid">foo_bar_reason' in resp.text
|
||
assert 'This link is no longer displayed since June 5, 2020, noon.' in resp.text
|
||
|
||
|
||
def test_edit_page_optional_placeholder(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page.objects.create(title='One', slug='one', template_name='standard')
|
||
app = login(app)
|
||
|
||
# empty placeholder footer
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content']
|
||
assert len(resp.context['optional_placeholders']) == 1
|
||
assert resp.context['optional_placeholders'][0]['key'] == 'footer'
|
||
assert resp.context['optional_placeholders'][0]['name'] == 'Footer'
|
||
assert resp.context['optional_placeholders'][0]['is_empty'] is True
|
||
assert '/manage/pages/%s/?include-section=footer' % page.pk in resp.text
|
||
assert 'Footer (empty)' in resp.text
|
||
assert '<div class="placeholder" data-placeholder-key="footer">' not in resp.text
|
||
|
||
# add a ParentContentCell
|
||
like_parent_cell = ParentContentCell.objects.create(page=page, placeholder='footer', order=0)
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content']
|
||
assert len(resp.context['optional_placeholders']) == 1
|
||
assert resp.context['optional_placeholders'][0]['key'] == 'footer'
|
||
assert resp.context['optional_placeholders'][0]['name'] == 'Footer'
|
||
assert resp.context['optional_placeholders'][0]['is_empty'] is False
|
||
assert '/manage/pages/%s/?include-section=footer' % page.pk in resp.text
|
||
assert 'Footer (like parent)' in resp.text
|
||
assert '<div class="placeholder" data-placeholder-key="footer">' not in resp.text
|
||
|
||
# click on the edit link
|
||
resp = resp.click(href='.*?include-section=footer')
|
||
assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content', 'footer']
|
||
assert len(resp.context['optional_placeholders']) == 0
|
||
assert '<div class="placeholder" data-placeholder-key="footer">' in resp.text
|
||
assert (
|
||
'<option data-add-url="/manage/pages/%s/add-cell-to-footer/data_textcell/default/"' % page.pk
|
||
in resp.text
|
||
)
|
||
|
||
# add a cell (so there is 2 cells)
|
||
resp = app.get('/manage/pages/%s/add-cell-to-footer/data_textcell/default/' % page.pk)
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content', 'footer']
|
||
assert len(resp.context['optional_placeholders']) == 0
|
||
assert '<div class="placeholder" data-placeholder-key="footer">' in resp.text
|
||
|
||
# remove parent content cell
|
||
like_parent_cell.delete()
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content', 'footer']
|
||
assert len(resp.context['optional_placeholders']) == 0
|
||
assert '<div class="placeholder" data-placeholder-key="footer">' in resp.text
|
||
|
||
|
||
def test_edit_page_unknown_template(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='broken')
|
||
page.save()
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert 'Unknown (broken)' in resp.text
|
||
|
||
|
||
def test_page_edit_slug(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
page = Page(title='Two', slug='two', template_name='standard')
|
||
page.save()
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click(href='.*/slug')
|
||
resp.form['slug'].value = 'two' # no change
|
||
resp = resp.form.submit()
|
||
resp = resp.follow()
|
||
assert Page.objects.get(id=page.id).slug == 'two'
|
||
|
||
resp = resp.click(href='.*/slug')
|
||
resp.form['slug'].value = 'two2' # valid change
|
||
resp = resp.form.submit()
|
||
resp = resp.follow()
|
||
assert Page.objects.get(id=page.id).slug == 'two2'
|
||
|
||
resp = resp.click(href='.*/slug')
|
||
resp.form['slug'].value = 'one' # duplicate
|
||
resp = resp.form.submit()
|
||
assert 'Slug must be unique' in resp.text
|
||
assert Page.objects.get(id=page.id).slug == 'two2'
|
||
|
||
|
||
def test_page_edit_sub_slug(app, admin_user):
|
||
page = Page.objects.create(title='One', slug='one', template_name='two')
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
|
||
resp = resp.click(href='.*/slug')
|
||
resp.form['slug'].value = 'foobar'
|
||
resp.form['sub_slug'].value = '(?P<card-foo_id>[0-9]+)'
|
||
resp = resp.form.submit().follow()
|
||
page.refresh_from_db()
|
||
assert page.slug == 'foobar'
|
||
assert page.sub_slug == '(?P<card-foo_id>[0-9]+)'
|
||
|
||
resp = app.get('/manage/pages/%s/slug' % page.pk)
|
||
resp.form['sub_slug'].value = 'card-foo_id'
|
||
resp = resp.form.submit().follow()
|
||
page.refresh_from_db()
|
||
assert page.slug == 'foobar'
|
||
assert page.sub_slug == 'card-foo_id'
|
||
|
||
# bad regexp
|
||
resp = app.get('/manage/pages/%s/slug' % page.pk)
|
||
resp.form['sub_slug'].value = '(?P< bad group name with spaces >[0-9]+)'
|
||
resp = resp.form.submit()
|
||
assert resp.context['form'].errors['sub_slug'] == ['Bad Regular expression.']
|
||
|
||
resp = app.get('/manage/pages/%s/slug' % page.pk)
|
||
resp.form['sub_slug'].value = ' bad group name with spaces '
|
||
resp = resp.form.submit()
|
||
assert resp.context['form'].errors['sub_slug'] == ['Bad Regular expression.']
|
||
|
||
# clear sub_slug
|
||
resp.form['sub_slug'].value = ''
|
||
resp = resp.form.submit().follow()
|
||
page.refresh_from_db()
|
||
assert page.slug == 'foobar'
|
||
assert page.sub_slug == ''
|
||
|
||
|
||
def test_page_edit_picture(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click(href='.*/picture/')
|
||
|
||
resp.form['picture'] = Upload(
|
||
'black.jpeg', open(os.path.join(TESTS_DATA_DIR, 'black.jpeg'), mode='rb').read(), 'image/jpeg'
|
||
)
|
||
resp = resp.form.submit()
|
||
assert resp.location.endswith('/manage/pages/%s/' % page.id)
|
||
resp = resp.follow()
|
||
assert '<h2>Page - One</h2>' in resp.text
|
||
assert '<img' in resp.text
|
||
assert 'src="/media/cache/' in resp.text
|
||
resp = resp.click(href='.*/remove-picture/')
|
||
resp = resp.follow()
|
||
assert '<h2>Page - One</h2>' in resp.text
|
||
assert not '<img' in resp.text
|
||
|
||
resp = resp.click(href='.*/picture/')
|
||
resp.form['picture'] = Upload('black.svg', b'<svg xmlns="http://www.w3.org/2000/svg"/>', 'image/svg+xml')
|
||
resp = resp.form.submit()
|
||
assert resp.location.endswith('/manage/pages/%s/' % page.id)
|
||
resp = resp.follow()
|
||
assert '<h2>Page - One</h2>' in resp.text
|
||
assert '<img' in resp.text
|
||
assert Page.objects.get(id=page.id).picture.url in resp.text
|
||
|
||
|
||
def test_page_placeholder_restricted_visibility(app, admin_user):
|
||
app = login(app)
|
||
|
||
page = Page.objects.create()
|
||
group = Group.objects.create(name='foobar')
|
||
|
||
resp = app.get(reverse('combo-manager-homepage'))
|
||
assert '<span class="visibility-summary"' not in resp.text
|
||
|
||
page.public = False
|
||
page.save()
|
||
resp = app.get(reverse('combo-manager-homepage'))
|
||
assert '<span class="visibility-summary" title="Restricted visibility">' in resp.text
|
||
assert 'foobar' not in resp.text
|
||
|
||
page.groups.set([group])
|
||
resp = app.get(reverse('combo-manager-homepage'))
|
||
assert '<span class="visibility-summary" title="Restricted visibility">' in resp.text
|
||
assert 'foobar' in resp.text
|
||
|
||
|
||
def test_edit_page_navigation(app, admin_user):
|
||
page1 = Page.objects.create(title='One', slug='one', parent=None, template_name='standard')
|
||
page2 = Page.objects.create(title='Two', slug='two', parent=page1, template_name='standard')
|
||
page3 = Page.objects.create(title='Three', slug='three', parent=page1, template_name='standard')
|
||
page4 = Page.objects.create(title='Four', slug='four', parent=None, template_name='standard')
|
||
|
||
app = login(app)
|
||
|
||
resp = app.get('/manage/pages/%s/' % page1.pk)
|
||
assert '<li class="nav-up"' not in resp.text
|
||
assert '<li class="nav-left"' not in resp.text
|
||
assert (
|
||
'<li class="nav-right"><a href="/manage/pages/%s/">%s</a></li>' % (page2.pk, page2.title) in resp.text
|
||
)
|
||
|
||
resp = app.get('/manage/pages/%s/' % page2.pk)
|
||
assert '<li class="nav-up"><a href="/manage/pages/%s/">%s</a></li>' % (page1.pk, page1.title) in resp.text
|
||
assert (
|
||
'<li class="nav-left"><a href="/manage/pages/%s/">%s</a></li>' % (page1.pk, page1.title) in resp.text
|
||
)
|
||
assert (
|
||
'<li class="nav-right"><a href="/manage/pages/%s/">%s</a></li>' % (page3.pk, page3.title) in resp.text
|
||
)
|
||
|
||
resp = app.get('/manage/pages/%s/' % page4.pk)
|
||
assert '<li class="nav-up"' not in resp.text
|
||
assert (
|
||
'<li class="nav-left"><a href="/manage/pages/%s/">%s</a></li>' % (page3.pk, page3.title) in resp.text
|
||
)
|
||
assert '<li class="nav-right"' not in resp.text
|
||
|
||
|
||
def test_edit_page_num_queries(settings, app, admin_user):
|
||
settings.COMBO_CELL_ASSET_SLOTS = {}
|
||
page = Page.objects.create(title='One', slug='one', parent=None, template_name='standard')
|
||
page2 = Page.objects.create(title='Two', slug='two', parent=page, template_name='standard')
|
||
MenuCell.objects.create(page=page, order=0)
|
||
TextCell.objects.create(page=page, placeholder='content', text='Foobar', order=1)
|
||
LinkCell.objects.create(page=page, placeholder='content', link_page=page2, order=2)
|
||
JsonCell.objects.create(page=page, placeholder='content', order=3, url='http://example.com')
|
||
SearchCell.objects.create(page=page, _search_services={'data': ['_text']}, order=4)
|
||
cell = LinkListCell.objects.create(order=5, placeholder='content', page=page)
|
||
for i in range(5):
|
||
LinkCell.objects.create(
|
||
page=page,
|
||
placeholder=cell.link_placeholder,
|
||
title='Foo %s' % i,
|
||
url='http://example.net/',
|
||
link_page=page,
|
||
order=i + 1,
|
||
)
|
||
|
||
app = login(app)
|
||
app.get('/manage/pages/%s/' % page.pk) # load once to populate caches
|
||
with CaptureQueriesContext(connection) as ctx:
|
||
app.get('/manage/pages/%s/' % page.pk)
|
||
assert len(ctx.captured_queries) == 32
|
||
|
||
|
||
def test_delete_page(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click('Delete')
|
||
assert '<button class="delete-button">Delete</button>' in resp.text
|
||
resp = resp.forms[0].submit()
|
||
assert resp.location.endswith('/manage/')
|
||
assert Page.objects.count() == 0
|
||
|
||
|
||
def test_delete_page_keep_child(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
page2 = Page(title='Two', slug='two', parent=page, template_name='standard')
|
||
page2.save()
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click('Delete')
|
||
assert '<button class="delete-button">Delete</button>' in resp.text
|
||
assert 'Delete only this page' in resp.text
|
||
assert 'Delete this page and all subpages' in resp.text
|
||
|
||
resp.form['choice'].value = 'delete-one'
|
||
resp = resp.forms[0].submit()
|
||
assert resp.location.endswith('/manage/')
|
||
assert Page.objects.count() == 1
|
||
assert Page.objects.get(id=page2.id) == page2
|
||
|
||
|
||
def test_delete_page_and_subpage(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
page2 = Page(title='Two', slug='two', parent=page, template_name='standard')
|
||
page2.save()
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click('Delete')
|
||
assert '<button class="delete-button">Delete</button>' in resp.text
|
||
assert 'Delete only this page' in resp.text
|
||
assert 'Delete this page and all subpages' in resp.text
|
||
|
||
resp.form['choice'].value = 'delete-all'
|
||
resp = resp.forms[0].submit()
|
||
assert resp.location.endswith('/manage/')
|
||
assert Page.objects.count() == 0
|
||
|
||
|
||
def test_page_reorder(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page1 = Page(title='One', slug='one', parent=None, order=0, template_name='standard')
|
||
page1.save()
|
||
page2 = Page(title='Two', slug='two', parent=None, order=1, template_name='standard')
|
||
page2.save()
|
||
page3 = Page(title='Three', slug='three', parent=page2, order=2, template_name='standard')
|
||
page3.save()
|
||
page4 = Page(title='Four', slug='four', parent=page2, order=3, template_name='standard')
|
||
page4.save()
|
||
|
||
ordered_ids = [x.id for x in Page.get_as_reordered_flat_hierarchy(Page.objects.all())]
|
||
ordered_ids = [page1.id, page2.id, page3.id, page4.id]
|
||
|
||
# move page4 before page3
|
||
app = login(app)
|
||
resp = app.get(
|
||
'/manage/pages/order',
|
||
params={
|
||
'moved-page-id': page4.id,
|
||
'moved-page-new-parent': page2.id,
|
||
'new-order': ','.join([str(x) for x in [page1.id, page2.id, page4.id, page3.id]]),
|
||
},
|
||
)
|
||
|
||
ordered_ids = [x.id for x in Page.get_as_reordered_flat_hierarchy(Page.objects.all())]
|
||
ordered_ids = [page1.id, page2.id, page4.id, page3.id]
|
||
|
||
# move page4 to level0
|
||
resp = app.get(
|
||
'/manage/pages/order',
|
||
params={
|
||
'moved-page-id': page4.id,
|
||
'moved-page-new-parent': '',
|
||
'new-order': ','.join([str(x) for x in [page1.id, page4.id, page2.id, page3.id]]),
|
||
},
|
||
)
|
||
ordered_ids = [x.id for x in Page.get_as_reordered_flat_hierarchy(Page.objects.all())]
|
||
ordered_ids = [page1.id, page4.id, page2.id, page3.id]
|
||
|
||
# change slug to check for autochange on duplicate
|
||
page4.slug = 'three'
|
||
page4.save()
|
||
# move it as a sibling of page3
|
||
resp = app.get(
|
||
'/manage/pages/order',
|
||
params={
|
||
'moved-page-id': page4.id,
|
||
'moved-page-new-parent': page2.id,
|
||
'new-order': ','.join([str(x) for x in [page1.id, page2.id, page4.id, page3.id]]),
|
||
},
|
||
)
|
||
assert Page.objects.get(id=page4.id).slug.startswith('three-')
|
||
|
||
# move a parent page as a child of its own child
|
||
for page in (page1, page2, page3, page4):
|
||
page.parent = None
|
||
page.save()
|
||
page2.parent = page1
|
||
page2.save()
|
||
page3.parent = page2
|
||
page3.save()
|
||
resp = app.get(
|
||
'/manage/pages/order',
|
||
params={
|
||
'moved-page-id': page1.id,
|
||
'moved-page-new-parent': page3.id,
|
||
'new-order': ','.join([str(x) for x in [page2.id, page3.id, page1.id, page4.id]]),
|
||
},
|
||
)
|
||
assert Page.objects.get(id=page2.id).parent_id is None
|
||
|
||
for page in (page1, page2, page3, page4):
|
||
page.parent = None
|
||
page.save()
|
||
page2.parent = page1
|
||
page2.save()
|
||
page3.parent = page2
|
||
page3.save()
|
||
page4.parent = page3
|
||
page4.save()
|
||
resp = app.get(
|
||
'/manage/pages/order',
|
||
params={
|
||
'moved-page-id': page2.id,
|
||
'moved-page-new-parent': page4.id,
|
||
'new-order': ','.join([str(x) for x in [page1.id, page3.id, page4.id, page2.id]]),
|
||
},
|
||
)
|
||
assert Page.objects.get(id=page1.id).parent_id is None
|
||
assert Page.objects.get(id=page3.id).parent_id == page1.id
|
||
|
||
|
||
def test_export_page(freezer, app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
freezer.move_to('2020-07-16')
|
||
resp = resp.click('Export')
|
||
assert resp.headers['content-type'] == 'application/json'
|
||
assert resp.headers['content-disposition'] == 'attachment; filename="export_page_one_20200716.json"'
|
||
assert resp.json['pages'][0].get('fields').get('slug') == 'one'
|
||
|
||
|
||
def test_export_page_without_child(freezer, app, admin_user):
|
||
page = Page.objects.create(title='One', slug='one', template_name='standard')
|
||
Page.objects.create(title='Two', slug='two', parent=page, template_name='standard')
|
||
app = login(app)
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
freezer.move_to('2020-07-16')
|
||
resp = resp.click('Export')
|
||
resp.form['with_subpages'].value = False
|
||
resp = resp.forms[0].submit()
|
||
assert resp.headers['content-type'] == 'application/json'
|
||
assert resp.headers['content-disposition'] == 'attachment; filename="export_page_one_20200716.json"'
|
||
assert len(resp.json['pages']) == 1
|
||
assert resp.json['pages'][0].get('fields').get('slug') == 'one'
|
||
|
||
|
||
def test_export_page_and_child(freezer, app, admin_user):
|
||
page1 = Page.objects.create(title='One', slug='one', template_name='standard')
|
||
page2 = Page.objects.create(title='Two', slug='two', parent=page1, template_name='standard')
|
||
Page.objects.create(title='Three', slug='three', parent=page2, template_name='standard')
|
||
Page.objects.create(title='Four', slug='four', parent=page1, template_name='standard')
|
||
freezer.move_to('2020-07-16')
|
||
app = login(app)
|
||
|
||
resp = app.get('/manage/pages/%s/' % page1.pk)
|
||
resp = resp.click('Export')
|
||
resp.form['with_subpages'].value = True
|
||
resp = resp.forms[0].submit()
|
||
assert resp.headers['content-type'] == 'application/json'
|
||
assert (
|
||
resp.headers['content-disposition']
|
||
== 'attachment; filename="export_page_one_and_subpages_20200716.json"'
|
||
)
|
||
assert len(resp.json['pages']) == 4
|
||
assert resp.json['pages'][0].get('fields').get('slug') == 'one'
|
||
assert resp.json['pages'][1].get('fields').get('slug') == 'two'
|
||
assert resp.json['pages'][2].get('fields').get('slug') == 'three'
|
||
assert resp.json['pages'][3].get('fields').get('slug') == 'four'
|
||
|
||
resp = app.get('/manage/pages/%s/' % page2.pk)
|
||
resp = resp.click('Export')
|
||
resp.form['with_subpages'].value = True
|
||
resp = resp.forms[0].submit()
|
||
assert resp.headers['content-type'] == 'application/json'
|
||
assert (
|
||
resp.headers['content-disposition']
|
||
== 'attachment; filename="export_page_two_and_subpages_20200716.json"'
|
||
)
|
||
assert len(resp.json['pages']) == 2
|
||
assert resp.json['pages'][0].get('fields').get('slug') == 'two'
|
||
assert resp.json['pages'][1].get('fields').get('slug') == 'three'
|
||
|
||
|
||
def test_export_page_order():
|
||
Page.objects.all().delete()
|
||
page1 = Page.objects.create(title='One', slug='one', template_name='standard')
|
||
page2 = Page.objects.create(title='Two', slug='two', parent=page1, template_name='standard')
|
||
page3 = Page.objects.create(title='Three', slug='three', parent=page2, template_name='standard')
|
||
page4 = Page.objects.create(title='Four', slug='four', parent=page1, template_name='standard')
|
||
random_list = [page3, page4, page1, page2]
|
||
ordered_list = Page.get_as_reordered_flat_hierarchy(random_list)
|
||
assert ordered_list[0] == page1
|
||
assert ordered_list[1] == page4
|
||
assert ordered_list[2] == page2
|
||
assert ordered_list[3] == page3
|
||
|
||
|
||
def test_site_export_import_json(app, admin_user):
|
||
page1 = Page.objects.create(title='One', slug='one', template_name='standard')
|
||
page2 = Page.objects.create(title='Two', slug='two', parent=page1, template_name='standard')
|
||
page3 = Page.objects.create(title='Three', slug='three', parent=page2, template_name='standard')
|
||
page4 = Page.objects.create(title='Four', slug='four', parent=page1, template_name='standard')
|
||
|
||
TextCell.objects.create(page=page1, placeholder='content', text='Foobar', order=0)
|
||
LinkCell.objects.create(page=page1, placeholder='content', link_page=page2, order=0)
|
||
LinkCell.objects.create(page=page2, placeholder='content', link_page=page1, order=0)
|
||
TextCell.objects.create(page=page3, placeholder='content', text='Foobar', order=0)
|
||
TextCell.objects.create(page=page3, placeholder='content', text='Foobar', order=0)
|
||
TextCell.objects.create(page=page4, placeholder='content', text='Foobar', order=0)
|
||
TextCell.objects.create(page=page4, placeholder='content', text='Foobar', order=0)
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Export Site')
|
||
resp = resp.form.submit()
|
||
assert resp.headers['content-type'] == 'application/json'
|
||
site_export = resp.body
|
||
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Import Site')
|
||
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
|
||
with CaptureQueriesContext(connection) as ctx:
|
||
resp = resp.form.submit()
|
||
assert len(ctx.captured_queries) in [1140, 1141]
|
||
|
||
Page.objects.all().delete()
|
||
assert LinkCell.objects.count() == 0
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Import Site')
|
||
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
|
||
with CaptureQueriesContext(connection) as ctx:
|
||
resp = resp.form.submit()
|
||
assert len(ctx.captured_queries) == 665
|
||
assert set(Page.objects.get(slug='one').related_cells['cell_types']) == set(
|
||
['data_textcell', 'data_linkcell']
|
||
)
|
||
assert Page.objects.count() == 4
|
||
assert LinkCell.objects.count() == 2
|
||
assert LinkCell.objects.get(page__slug='one').link_page.slug == 'two'
|
||
assert LinkCell.objects.get(page__slug='two').link_page.slug == 'one'
|
||
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Export Site')
|
||
resp.form['pages'] = False
|
||
resp = resp.form.submit()
|
||
site_export = resp.body
|
||
|
||
site_json = json.loads(site_export.decode())
|
||
assert 'pages' not in site_json
|
||
assert 'pwa' in site_json
|
||
|
||
Page.objects.all().delete()
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Import Site')
|
||
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
|
||
assert Page.objects.count() == 0
|
||
|
||
# check with invalid file
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Import Site')
|
||
resp.form['site_file'] = Upload('site-export.json', b'invalid content', 'application/json')
|
||
resp = resp.form.submit()
|
||
assert 'File is not in the expected TAR or JSON format.' in resp.text
|
||
|
||
|
||
def test_site_export_import_tar(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page1 = Page(title='One', slug='one', template_name='standard')
|
||
page1.save()
|
||
cell = TextCell(page=page1, placeholder='content', text='Foobar', order=0)
|
||
cell.save()
|
||
Asset(key='collectivity:banner', asset=File(BytesIO(b'test'), 'test.png')).save()
|
||
path = default_storage.path('')
|
||
assert open('%s/assets/test.png' % path, 'r').read() == 'test'
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Export Site')
|
||
resp.form['asset_files'] = True
|
||
resp = resp.form.submit()
|
||
assert resp.headers['content-type'] == 'application/x-tar'
|
||
site_export = resp.body
|
||
|
||
Page.objects.all().delete()
|
||
Asset.objects.filter(key='collectivity:banner').delete()
|
||
assert Page.objects.count() == 0
|
||
assert TextCell.objects.count() == 0
|
||
assert Asset.objects.filter(key='collectivity:banner').count() == 0
|
||
open('%s/assets/test.png' % path, 'w').write('foo')
|
||
app = login(app)
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Import Site')
|
||
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
|
||
resp = resp.form.submit()
|
||
assert Page.objects.count() == 1
|
||
assert TextCell.objects.count() == 1
|
||
assert Asset.objects.filter(key='collectivity:banner').count() == 1
|
||
assert open('%s/assets/test.png' % path, 'r').read() == 'foo'
|
||
|
||
os.remove('%s/assets/test.png' % path)
|
||
app = login(app)
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Import Site')
|
||
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
|
||
resp = resp.form.submit()
|
||
assert open('%s/assets/test.png' % path, 'r').read() == 'test'
|
||
|
||
|
||
def test_site_export_import_missing_group(app, admin_user):
|
||
Page.objects.all().delete()
|
||
group = Group.objects.create(name='foobar')
|
||
page1 = Page(title='One', slug='one', template_name='standard')
|
||
page1.save()
|
||
page1.groups.set([group])
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Export Site')
|
||
resp = resp.form.submit()
|
||
assert resp.headers['content-type'] == 'application/json'
|
||
site_export = resp.body
|
||
|
||
Page.objects.all().delete()
|
||
group.delete()
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Import Site')
|
||
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
|
||
resp = resp.form.submit()
|
||
assert 'Missing groups: foobar' in resp.text
|
||
|
||
|
||
def test_site_export_import_unknown_parent(app, admin_user):
|
||
Page.objects.create(title='One', slug='one', template_name='standard')
|
||
Page.objects.create(title='Two', slug='two', template_name='standard')
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Export Site')
|
||
resp = resp.form.submit()
|
||
payload = json.loads(force_str(resp.body))
|
||
payload['pages'][0]['fields']['exclude_from_navigation'] = False
|
||
payload['pages'][0]['fields']['parent'] = ['unknown-parent']
|
||
|
||
resp = app.get('/manage/')
|
||
resp = resp.click('Import Site')
|
||
resp.form['site_file'] = Upload('site-export.json', force_bytes(json.dumps(payload)), 'application/json')
|
||
resp = resp.form.submit().follow()
|
||
assert (
|
||
'Unknown parent for page "One"; parent has been reset and page was excluded from navigation.'
|
||
in resp.text
|
||
)
|
||
|
||
|
||
def test_site_export_import_unknown_page(app, admin_user):
|
||
page = Page.objects.create(title='One', slug='one')
|
||
page2 = Page.objects.create(title='Two', slug='two')
|
||
MenuCell.objects.create(root_page=page2, page=page, order=0, slug='carrousel', placeholder='content')
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/site-export')
|
||
resp = resp.form.submit()
|
||
payload = json.loads(force_str(resp.body))
|
||
payload['pages'][0]['cells'][0]['fields']['root_page'] = ['unknown-parent']
|
||
Page.objects.all().delete()
|
||
|
||
resp = app.get('/manage/site-import')
|
||
resp.form['site_file'] = Upload('site-export.json', force_bytes(json.dumps(payload)), 'application/json')
|
||
resp = resp.form.submit()
|
||
assert resp.context['form'].errors['site_file'] == ['Unknown page "unknown-parent".']
|
||
assert Page.objects.count() == 0
|
||
|
||
resp = app.get('/manage/site-import')
|
||
resp.form['site_file'] = Upload('site-export.json', force_bytes(json.dumps(payload)), 'application/json')
|
||
with mock.patch('combo.data.models.Page.load_serialized_pages') as mock_load:
|
||
mock_load.side_effect = DeserializationError('foo bar')
|
||
resp = resp.form.submit()
|
||
assert resp.context['form'].errors['site_file'] == ['foo bar']
|
||
|
||
resp = app.get('/manage/site-import')
|
||
resp.form['site_file'] = Upload('site-export.json', force_bytes(json.dumps(payload)), 'application/json')
|
||
with mock.patch('combo.data.models.Page.load_serialized_pages') as mock_load:
|
||
mock_load.side_effect = DeserializationError("Page matching query does not exist.")
|
||
resp = resp.form.submit()
|
||
assert resp.context['form'].errors['site_file'] == ['Page matching query does not exist.']
|
||
|
||
resp = app.get('/manage/site-import')
|
||
resp.form['site_file'] = Upload('site-export.json', force_bytes(json.dumps(payload)), 'application/json')
|
||
with mock.patch('combo.data.models.Page.load_serialized_pages') as mock_load:
|
||
mock_load.side_effect = DeserializationError("Page matching query does not exist. '['blah")
|
||
resp = resp.form.submit()
|
||
assert resp.context['form'].errors['site_file'] == ['Unknown page "blah".']
|
||
|
||
|
||
def test_invalid_cell_report(app, admin_user):
|
||
app = login(app)
|
||
resp = app.get('/manage/cells/invalid-report/')
|
||
assert resp.context['object_list'] == []
|
||
|
||
page = Page.objects.create(title='One', slug='one', template_name='standard')
|
||
cell = TextCell.objects.create(page=page, placeholder='content', text='Foobar', order=0)
|
||
resp = app.get('/manage/cells/invalid-report/')
|
||
assert resp.context['object_list'] == []
|
||
|
||
cell.mark_as_invalid('foo_bar_reason')
|
||
resp = app.get('/manage/cells/invalid-report/')
|
||
assert resp.context['object_list'] == [cell]
|
||
assert '<a href="/manage/pages/{}/">{}</a>'.format(page.pk, page.title) in resp.text
|
||
assert (
|
||
'<a href="/manage/pages/{}/#cell-{}">{}</a>'.format(page.pk, cell.get_reference(), cell.get_label())
|
||
in resp.text
|
||
)
|
||
|
||
# cells from snapshot are not reported
|
||
snapshot = PageSnapshot.objects.create(page=page)
|
||
page.snapshot = snapshot
|
||
page.save()
|
||
resp = app.get('/manage/cells/invalid-report/')
|
||
assert resp.context['object_list'] == []
|
||
|
||
# cells used in LinkListCell are not reported
|
||
page.snapshot = None
|
||
page.save()
|
||
cell2 = LinkListCell.objects.create(order=0, placeholder='content', page=page)
|
||
item = LinkCell.objects.create(page=page, placeholder=cell2.link_placeholder, order=0)
|
||
item.mark_as_invalid('foo_bar_reason')
|
||
resp = app.get('/manage/cells/invalid-report/')
|
||
assert resp.context['object_list'] == [cell]
|
||
|
||
# cells without placeholder are not reported
|
||
cell.placeholder = ''
|
||
cell.save()
|
||
resp = app.get('/manage/cells/invalid-report/')
|
||
assert resp.context['object_list'] == []
|
||
|
||
# cells with unknown placeholder are not reported
|
||
request = RequestFactory().get('/')
|
||
assert [p.key for p in page.get_placeholders(request, traverse_cells=True)] == ['content', 'footer']
|
||
cell.placeholder = 'foobar'
|
||
cell.save()
|
||
resp = app.get('/manage/cells/invalid-report/')
|
||
assert resp.context['object_list'] == []
|
||
cell.placeholder = 'footer'
|
||
cell.save()
|
||
resp = app.get('/manage/cells/invalid-report/')
|
||
assert resp.context['object_list'] == [cell]
|
||
|
||
|
||
def test_duplicate_page(app, admin_user):
|
||
page = Page.objects.create(
|
||
title='One', slug='one', template_name='standard', exclude_from_navigation=False
|
||
)
|
||
TextCell.objects.create(page=page, placeholder='content', text='Foobar', order=0)
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
resp = resp.click('Duplicate')
|
||
resp = resp.form.submit()
|
||
new_page = Page.objects.latest('pk')
|
||
assert resp.status_int == 302
|
||
assert resp.location.endswith('/manage/pages/%s/' % new_page.pk)
|
||
resp = resp.follow()
|
||
assert (
|
||
'Page %s has been duplicated, it has been marked as excluded from navigation.' % page.title
|
||
in resp.text
|
||
)
|
||
assert new_page.exclude_from_navigation is True
|
||
|
||
page.exclude_from_navigation = True
|
||
page.save()
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
resp = resp.click('Duplicate')
|
||
resp = resp.form.submit()
|
||
new_page = Page.objects.latest('pk')
|
||
assert resp.status_int == 302
|
||
assert resp.location.endswith('/manage/pages/%s/' % new_page.pk)
|
||
resp = resp.follow()
|
||
assert 'Page %s has been duplicated.' % page.title in resp.text
|
||
assert new_page.exclude_from_navigation is True
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
resp = resp.click('Duplicate')
|
||
resp.form['title'] = 'Two'
|
||
resp = resp.form.submit()
|
||
new_page = Page.objects.latest('pk')
|
||
assert resp.status_int == 302
|
||
assert resp.location.endswith('/manage/pages/%s/' % new_page.pk)
|
||
assert new_page.title == 'Two'
|
||
assert new_page.slug == 'two'
|
||
|
||
|
||
def test_add_edit_cell(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
# click on first option link, this should add a text cell
|
||
resp = app.get(resp.html.find('option').get('data-add-url'))
|
||
|
||
cells = CellBase.get_cells(page_id=page.id)
|
||
assert len(cells) == 1
|
||
assert isinstance(cells[0], TextCell)
|
||
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.id, cells[0].get_reference()))
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert ('data-cell-reference="%s"' % cells[0].get_reference()) in resp.text
|
||
resp.forms[0]['c%s-text' % cells[0].get_reference()].value = 'Hello world'
|
||
resp = resp.forms[0].submit()
|
||
assert resp.status_int == 302
|
||
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.id, cells[0].get_reference()))
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert resp.forms[0]['c%s-text' % cells[0].get_reference()].value == 'Hello world'
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert ('data-cell-reference="%s"' % cells[0].get_reference()) in resp.text
|
||
resp.forms[0]['c%s-text' % cells[0].get_reference()].value = 'World Hello'
|
||
resp = resp.forms[0].submit(xhr=True)
|
||
assert resp.status_int == 200
|
||
assert resp.text.strip().startswith('<p><label')
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert resp.forms[0]['c%s-text' % cells[0].get_reference()].value == 'World Hello'
|
||
|
||
|
||
def test_add_all_basic_cell_types(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
for option in resp.html.find_all('option'):
|
||
if not '/data_' in option.get('data-add-url'):
|
||
continue
|
||
resp = app.get(option.get('data-add-url'))
|
||
resp = resp.follow()
|
||
|
||
|
||
SEARCH_SERVICES = {
|
||
'search1': {
|
||
'label': 'Search 1',
|
||
'url': 'http://www.example.net/search/?q=%(q)s',
|
||
}
|
||
}
|
||
|
||
|
||
class SearchServices(object):
|
||
def __init__(self, search_services):
|
||
self.search_services = search_services
|
||
|
||
def __enter__(self):
|
||
settings.COMBO_SEARCH_SERVICES = self.search_services
|
||
|
||
def __exit__(self, *args, **kwargs):
|
||
settings.COMBO_SEARCH_SERVICES = {}
|
||
|
||
|
||
def test_search_cell(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
|
||
# add
|
||
app = login(app)
|
||
assert SearchCell.objects.count() == 0
|
||
with SearchServices(SEARCH_SERVICES):
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
for option in resp.html.find_all('option'):
|
||
if 'content/search_' in option.get('data-add-url'):
|
||
resp = app.get(option.get('data-add-url'))
|
||
resp = resp.follow()
|
||
assert SearchCell.objects.count() == 1
|
||
cell = SearchCell.objects.first()
|
||
|
||
# delete
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click(href='/search_searchcell-%s/delete' % cell.id)
|
||
resp = resp.form.submit()
|
||
assert SearchCell.objects.count() == 0
|
||
|
||
|
||
def test_delete_cell(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
cell = TextCell(page=page, placeholder='content', text='Foobar', order=0)
|
||
cell.save()
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click(href='/data_textcell-%s/delete' % cell.id)
|
||
resp = resp.form.submit()
|
||
assert TextCell.objects.count() == 0
|
||
|
||
|
||
def test_edit_cell_visibility(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
cell = TextCell(page=page, placeholder='content', text='Foobar', order=0)
|
||
cell.save()
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click(href='/data_textcell-%s/visibility' % cell.id)
|
||
assert resp.form['cdata_textcell-%s-visibility' % cell.id].value == 'all'
|
||
resp.form['cdata_textcell-%s-visibility' % cell.id] = 'logged'
|
||
resp = resp.form.submit('submit')
|
||
assert TextCell.objects.get(id=cell.id).public is False
|
||
assert TextCell.objects.get(id=cell.id).restricted_to_unlogged is False
|
||
assert TextCell.objects.get(id=cell.id).groups.count() == 0
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click(href='/data_textcell-%s/visibility' % cell.id)
|
||
assert resp.form['cdata_textcell-%s-visibility' % cell.id].value == 'logged'
|
||
resp.form['cdata_textcell-%s-visibility' % cell.id] = 'unlogged'
|
||
resp = resp.form.submit('submit')
|
||
assert TextCell.objects.get(id=cell.id).restricted_to_unlogged is True
|
||
assert TextCell.objects.get(id=cell.id).public is True
|
||
assert TextCell.objects.get(id=cell.id).groups.count() == 0
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click(href='/data_textcell-%s/visibility' % cell.id)
|
||
assert resp.form['cdata_textcell-%s-visibility' % cell.id].value == 'unlogged'
|
||
resp.form['cdata_textcell-%s-visibility' % cell.id] = 'all'
|
||
resp = resp.form.submit('submit')
|
||
assert TextCell.objects.get(id=cell.id).restricted_to_unlogged is False
|
||
assert TextCell.objects.get(id=cell.id).public is True
|
||
assert TextCell.objects.get(id=cell.id).groups.count() == 0
|
||
|
||
group1 = Group(name='A group')
|
||
group1.save()
|
||
group2 = Group(name='Another group')
|
||
group2.save()
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click(href='/data_textcell-%s/visibility' % cell.id)
|
||
resp.form['cdata_textcell-%s-visibility' % cell.id] = 'groups-any'
|
||
resp.form['cdata_textcell-%s-groups' % cell.id] = [group1.id]
|
||
resp = resp.form.submit('submit')
|
||
assert TextCell.objects.get(id=cell.id).restricted_to_unlogged is False
|
||
assert TextCell.objects.get(id=cell.id).public is False
|
||
assert TextCell.objects.get(id=cell.id).groups.count() == 1
|
||
assert TextCell.objects.get(id=cell.id).groups.all()[0].name == 'A group'
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click(href='/data_textcell-%s/visibility' % cell.id)
|
||
resp.form['cdata_textcell-%s-visibility' % cell.id] = 'groups-none'
|
||
resp.form['cdata_textcell-%s-groups' % cell.id] = [group2.id]
|
||
resp = resp.form.submit('submit')
|
||
assert TextCell.objects.get(id=cell.id).restricted_to_unlogged is True
|
||
assert TextCell.objects.get(id=cell.id).public is False
|
||
assert TextCell.objects.get(id=cell.id).groups.count() == 1
|
||
assert TextCell.objects.get(id=cell.id).groups.all()[0].name == 'Another group'
|
||
|
||
|
||
def test_edit_cell_options(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
cell = TextCell(page=page, placeholder='content', text='Foobar', order=0)
|
||
cell.save()
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click(href='/data_textcell-%s/options' % cell.id)
|
||
assert resp.form['cdata_textcell-%s-slug' % cell.id].value == ''
|
||
assert resp.form['cdata_textcell-%s-extra_css_class' % cell.id].value == ''
|
||
resp.form['cdata_textcell-%s-slug' % cell.id] = 'SLUG'
|
||
resp = resp.form.submit('submit')
|
||
assert TextCell.objects.get(id=cell.id).slug == 'SLUG'
|
||
assert 'SLUG' in app.get('/manage/pages/%s/' % page.id)
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click(href='/data_textcell-%s/options' % cell.id)
|
||
resp.form['cdata_textcell-%s-slug' % cell.id] = ''
|
||
resp = resp.form.submit('submit')
|
||
assert TextCell.objects.get(id=cell.id).slug == ''
|
||
assert not 'SLUG' in app.get('/manage/pages/%s/' % page.id)
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp = resp.click(href='/data_textcell-%s/options' % cell.id)
|
||
resp.form['cdata_textcell-%s-extra_css_class' % cell.id] = 'CSS'
|
||
resp = resp.form.submit('submit')
|
||
assert TextCell.objects.get(id=cell.id).extra_css_class == 'CSS'
|
||
assert '[CSS]' in app.get('/manage/pages/%s/' % page.id)
|
||
|
||
|
||
def test_edit_cell_order(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
cells = []
|
||
for i in range(5):
|
||
cells.append(TextCell(page=page, placeholder='content', text='Foobar%s' % i, order=i))
|
||
cells[-1].save()
|
||
|
||
params = []
|
||
new_order = [2, 3, 1, 4, 5]
|
||
for i, (cell, new_pos) in enumerate(zip(cells, new_order)):
|
||
params.append(('ph_data_textcell-%s' % cell.id, 'content')) # no placeholder change
|
||
params.append(('pos_data_textcell-%s' % cell.id, str(new_order[i])))
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/order?%s' % (page.id, urlencode(params)))
|
||
assert resp.status_code == 204
|
||
for i, cell in enumerate(cells):
|
||
assert TextCell.objects.get(id=cell.id).order == new_order[i]
|
||
|
||
|
||
def test_edit_text_cell(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
data_add_url = [x for x in resp.html.find_all('option') if x.text == 'Text'][0].get('data-add-url')
|
||
resp = app.get(data_add_url)
|
||
|
||
cells = CellBase.get_cells(page_id=page.id)
|
||
assert len(cells) == 1
|
||
assert isinstance(cells[0], TextCell)
|
||
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.id, cells[0].get_reference()))
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp.form['cdata_textcell-%s-text' % cells[0].id].value = 'Hello : World'
|
||
resp = resp.form.submit()
|
||
assert TextCell.objects.get(id=cells[0].id).text == 'Hello : World'
|
||
|
||
with override_settings(LANGUAGE_CODE='fr-fr'):
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp.form['cdata_textcell-%s-text' % cells[0].id].value = 'Hello : World'
|
||
resp = resp.form.submit()
|
||
assert TextCell.objects.get(id=cells[0].id).text == u'Hello\u00a0: World'
|
||
|
||
|
||
def test_edit_json_cell(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
data_add_url = [x for x in resp.html.find_all('option') if x.text == 'JSON Prototype'][0].get(
|
||
'data-add-url'
|
||
)
|
||
resp = app.get(data_add_url)
|
||
|
||
cells = CellBase.get_cells(page_id=page.id)
|
||
assert len(cells) == 1
|
||
assert isinstance(cells[0], JsonCell)
|
||
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.id, cells[0].get_reference()))
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp.form['cdata_jsoncell-%s-url' % cells[0].id].value = 'xxx'
|
||
resp = resp.form.submit()
|
||
assert 'Enter a valid URL.' in resp.text
|
||
assert JsonCell.objects.get(id=cells[0].id).url == ''
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp.form['cdata_jsoncell-%s-url' % cells[0].id].value = 'https://www.example.net/'
|
||
resp = resp.form.submit()
|
||
assert JsonCell.objects.get(id=cells[0].id).url == 'https://www.example.net/'
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp.form['cdata_jsoncell-%s-url' % cells[0].id].value = '{{url}}'
|
||
resp = resp.form.submit()
|
||
assert JsonCell.objects.get(id=cells[0].id).url == '{{url}}'
|
||
|
||
|
||
def test_edit_config_json_cell(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
options = [x.text for x in resp.html.find_all('option')]
|
||
assert not 'Foobar' in options
|
||
|
||
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://test/'}},
|
||
TEMPLATES=templates_settings,
|
||
):
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
options = [x.text for x in resp.html.find_all('option')]
|
||
assert 'Foobar' in options
|
||
data_add_url = [x for x in resp.html.find_all('option') if x.text == 'Foobar'][0].get('data-add-url')
|
||
resp = app.get(data_add_url)
|
||
|
||
cells = CellBase.get_cells(page_id=page.id)
|
||
assert len(cells) == 1
|
||
assert isinstance(cells[0], ConfigJsonCell)
|
||
assert cells[0].key == 'test-config-json-cell'
|
||
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.id, cells[0].get_reference()))
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert ('data-cell-reference="%s"' % cells[0].get_reference()) in resp.text
|
||
assert 'There are no options for this cell.' in resp.form.text
|
||
|
||
# make it configurable
|
||
with override_settings(
|
||
JSON_CELL_TYPES={
|
||
'test-config-json-cell': {
|
||
'name': 'Foobar',
|
||
'url': 'http://test/',
|
||
'form': [
|
||
{
|
||
'label': 'Test',
|
||
'type': 'string',
|
||
'varname': 'test',
|
||
},
|
||
{
|
||
'label': 'Test bool',
|
||
'type': 'bool',
|
||
'varname': 'test2',
|
||
'required': False,
|
||
},
|
||
{
|
||
'label': 'Test no type is string',
|
||
'varname': 'test3',
|
||
},
|
||
{
|
||
'label': 'Test text',
|
||
'type': 'text',
|
||
'varname': 'test4',
|
||
'required': False,
|
||
},
|
||
],
|
||
}
|
||
},
|
||
TEMPLATES=templates_settings,
|
||
):
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert 'There are no options for this cell.' not in resp.form.text
|
||
|
||
resp.form['c%s-test' % cells[0].get_reference()].value = 'Hello world'
|
||
resp.form['c%s-test3' % cells[0].get_reference()].value = 'Hello again'
|
||
resp = resp.form.submit()
|
||
assert resp.status_int == 302
|
||
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.id, cells[0].get_reference()))
|
||
|
||
# test form error
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp.form['c%s-test' % cells[0].get_reference()].value = ''
|
||
resp.form['c%s-test3' % cells[0].get_reference()].value = 'Hello'
|
||
resp = resp.form.submit()
|
||
assert resp.status_int == 200
|
||
assert resp.context['form'].errors['test'] == ['This field is required.']
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert resp.form['c%s-test' % cells[0].get_reference()].value == 'Hello world'
|
||
assert resp.form['c%s-test2' % cells[0].get_reference()].checked is False
|
||
assert resp.form['c%s-test3' % cells[0].get_reference()].value == 'Hello again'
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert ('data-cell-reference="%s"' % cells[0].get_reference()) in resp.text
|
||
resp.forms[0]['c%s-test' % cells[0].get_reference()].value = 'World Hello'
|
||
resp.forms[0]['c%s-test2' % cells[0].get_reference()].checked = True
|
||
assert resp.form['c%s-test4' % cells[0].get_reference()].tag == 'textarea'
|
||
resp.forms[0]['c%s-test4' % cells[0].get_reference()].value = 'Text Area'
|
||
resp = resp.form.submit(xhr=True)
|
||
assert resp.status_int == 200
|
||
assert resp.text.strip().startswith('<p><label')
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert resp.form['c%s-test' % cells[0].get_reference()].value == 'World Hello'
|
||
assert resp.form['c%s-test2' % cells[0].get_reference()].checked is True
|
||
assert resp.form['c%s-test3' % cells[0].get_reference()].value == 'Hello again'
|
||
assert resp.form['c%s-test4' % cells[0].get_reference()].value == 'Text Area'
|
||
|
||
|
||
def test_logout(app, admin_user):
|
||
app = login(app)
|
||
app.get('/logout/')
|
||
assert app.get('/manage/', status=302).location.endswith('/login/?next=/manage/')
|
||
|
||
|
||
def test_asset_management(app, admin_user):
|
||
app = login(app)
|
||
resp = app.get('/manage/assets/')
|
||
assert 'have any asset yet.' in resp.text
|
||
|
||
# test upload
|
||
resp = app.get('/manage/assets/')
|
||
resp = resp.click('Upload')
|
||
resp.form['upload'] = Upload(
|
||
'test.png',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
|
||
),
|
||
'image/png',
|
||
)
|
||
resp = resp.form.submit().follow()
|
||
|
||
# check file is now there
|
||
resp = app.get('/manage/assets/')
|
||
assert 'have any asset yet.' not in resp.text
|
||
|
||
# check there is also a thumbnail
|
||
thumbnail_filename = re.findall('src="/media/(.*thumb.*)"', resp.text)[0]
|
||
thumbnail_path = default_storage.path(thumbnail_filename)
|
||
assert os.path.exists(thumbnail_path)
|
||
thumbnail_contents = open(thumbnail_path, mode='rb').read()
|
||
|
||
# check overwriting
|
||
resp = resp.click('Overwrite')
|
||
# test with the same extension but uppercased
|
||
resp.form['upload'] = Upload(
|
||
'test.PNG',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAgAABAADRWoApgAA\nAABJRU5ErkJggg=='
|
||
),
|
||
'image/png',
|
||
)
|
||
resp = resp.form.submit().follow()
|
||
|
||
resp.click('test.png')
|
||
assert re.findall('src="/media/(.*thumb.*)"', resp.text)[0] == thumbnail_filename
|
||
assert os.path.exists(thumbnail_path)
|
||
thumbnail_contents_new = open(thumbnail_path, mode='rb').read()
|
||
assert thumbnail_contents_new != thumbnail_contents
|
||
|
||
# try to overwrite with a different mimetype
|
||
resp = resp.click('Overwrite')
|
||
resp.form['upload'] = Upload(
|
||
'test.pdf',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAgAABAADRWoApgAA\nAABJRU5ErkJggg=='
|
||
),
|
||
'application/pdf',
|
||
)
|
||
with mock.patch('combo.apps.assets.views.default_storage.delete') as mock_delete:
|
||
resp = resp.form.submit().follow()
|
||
# original file was not deleted
|
||
assert mock_delete.call_args_list == []
|
||
messages = resp.context['messages']
|
||
assert len(messages._loaded_messages) == 1
|
||
assert messages._loaded_messages[0].message == 'You have to upload a file with the same extension (.png).'
|
||
|
||
# test deletion
|
||
resp = resp.click('Delete')
|
||
assert 'Are you sure you want to delete' in resp.text
|
||
resp = resp.form.submit().follow()
|
||
assert 'have any asset yet.' in resp.text
|
||
|
||
|
||
def test_asset_management_anchor(app, admin_user):
|
||
app = login(app)
|
||
resp = app.get('/manage/assets/')
|
||
assert 'have any asset yet.' in resp.text
|
||
|
||
for i in range(25):
|
||
filepath = os.path.join(settings.CKEDITOR_UPLOAD_PATH, 'cc.png')
|
||
pix = b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
|
||
default_storage.save(filepath, BytesIO(base64.decodebytes(pix)))
|
||
|
||
# test upload
|
||
resp = app.get('/manage/assets/')
|
||
resp = resp.click('Upload')
|
||
resp.form['upload'] = Upload(
|
||
'test.png',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
|
||
),
|
||
'image/png',
|
||
)
|
||
resp = resp.form.submit()
|
||
assert resp.location.endswith('?page=3')
|
||
resp = resp.follow()
|
||
|
||
# overwrite
|
||
resp = resp.click(href=re.compile('overwrite.*test.png'))
|
||
resp.form['upload'] = Upload(
|
||
'aa.png',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAgAABAADRWoApgAA\nAABJRU5ErkJggg=='
|
||
),
|
||
'image/png',
|
||
)
|
||
resp = resp.form.submit()
|
||
assert resp.location.endswith('?page=3')
|
||
resp = resp.follow()
|
||
|
||
# delete
|
||
resp = resp.click(href=re.compile('delete.*test.png'))
|
||
resp = resp.form.submit()
|
||
resp = resp.follow()
|
||
assert 'test.png' not in resp.text
|
||
|
||
|
||
def test_asset_management_search(app, admin_user):
|
||
app = login(app)
|
||
|
||
filepath = os.path.join(settings.CKEDITOR_UPLOAD_PATH, 'test.png')
|
||
pix = b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
|
||
default_storage.save(filepath, BytesIO(base64.decodebytes(pix)))
|
||
assert os.path.exists(default_storage.path(filepath))
|
||
|
||
filepath = os.path.join(settings.CKEDITOR_UPLOAD_PATH, 'test.txt')
|
||
default_storage.save(filepath, BytesIO(b'hello world'))
|
||
|
||
filepath = os.path.join(settings.CKEDITOR_UPLOAD_PATH, 'foo.txt')
|
||
default_storage.save(filepath, BytesIO(b'foo bar baz'))
|
||
|
||
resp = app.get('/manage/assets/')
|
||
assert resp.text.count('<tr class="asset') == 3
|
||
|
||
resp.form['q'] = 'test'
|
||
resp = resp.form.submit()
|
||
assert resp.text.count('<tr class="asset') == 2
|
||
|
||
|
||
def test_assets_browse(app, admin_user):
|
||
app = login(app)
|
||
|
||
resp = app.get('/manage/ckeditor/browse/?CKEditor=foo&CKEditorFuncNum=0')
|
||
assert 'No files found' in resp.text
|
||
|
||
filepath = os.path.join(settings.CKEDITOR_UPLOAD_PATH, 'aa.png')
|
||
pix = b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
|
||
default_storage.save(filepath, BytesIO(base64.decodebytes(pix)))
|
||
filepath = os.path.join(settings.CKEDITOR_UPLOAD_PATH, 'bb.pdf')
|
||
pix = b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
|
||
default_storage.save(filepath, BytesIO(base64.decodebytes(pix)))
|
||
filepath = os.path.join(settings.CKEDITOR_UPLOAD_PATH, 'cc.png')
|
||
pix = b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
|
||
default_storage.save(filepath, BytesIO(base64.decodebytes(pix)))
|
||
|
||
resp = app.get('/manage/ckeditor/browse/?CKEditor=foo&CKEditorFuncNum=0')
|
||
assert 'No files found' not in resp.text
|
||
|
||
resp.form['q'] = 'test'
|
||
resp = resp.form.submit()
|
||
assert 'CKEditor=foo' in resp.request.url
|
||
assert 'CKEditorFuncNum=0' in resp.request.url
|
||
assert resp.text.count('<tr class="asset') == 0
|
||
|
||
resp.form['q'] = 'png'
|
||
resp = resp.form.submit()
|
||
assert 'CKEditor=foo' in resp.request.url
|
||
assert 'CKEditorFuncNum=0' in resp.request.url
|
||
assert resp.text.count('<tr class="asset') == 2
|
||
|
||
for i in range(25):
|
||
filepath = os.path.join(settings.CKEDITOR_UPLOAD_PATH, 'cc.png')
|
||
pix = b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
|
||
default_storage.save(filepath, BytesIO(base64.decodebytes(pix)))
|
||
resp = app.get('/manage/ckeditor/browse/?CKEditor=foo&CKEditorFuncNum=0')
|
||
resp = resp.click(href='.*page=2.*')
|
||
assert 'CKEditor=foo' in resp.request.url
|
||
assert 'CKEditorFuncNum=0' in resp.request.url
|
||
|
||
|
||
def test_asset_slots_management(app, admin_user):
|
||
app = login(app)
|
||
assert Asset.objects.count() == 0
|
||
|
||
with override_settings(COMBO_ASSET_SLOTS={'collectivity:banner': {'label': 'Banner'}}):
|
||
resp = app.get('/manage/assets/')
|
||
assert 'Banner <span class="not-defined">' in resp.text
|
||
assert '>Delete<' not in resp.text
|
||
|
||
resp = resp.click('Overwrite')
|
||
resp.form['upload'] = Upload(
|
||
'test.png',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAgAABAADRWoApgAA\nAABJRU5ErkJggg=='
|
||
),
|
||
'image/png',
|
||
)
|
||
resp = resp.form.submit()
|
||
assert resp.location.endswith('?page=1')
|
||
resp = resp.follow()
|
||
assert 'test.png' in resp.text
|
||
assert '>Delete<' in resp.text
|
||
assert Asset.objects.filter(key='collectivity:banner').count() == 1
|
||
|
||
assert app.get('/assets/collectivity:banner', status=302)['location'].endswith('test.png')
|
||
|
||
# upload a new version of image
|
||
resp = resp.click('Overwrite')
|
||
resp.form['upload'] = Upload(
|
||
'test2.png',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAgAABAADRWoApgAA\nAABJRU5ErkJggg=='
|
||
),
|
||
'image/png',
|
||
)
|
||
resp = resp.form.submit().follow()
|
||
assert 'test2.png' in resp.text
|
||
assert '<img data-href' in resp.text # thumbnail
|
||
assert '>Delete<' in resp.text
|
||
assert Asset.objects.filter(key='collectivity:banner').count() == 1
|
||
|
||
resp = resp.click('Delete')
|
||
resp = resp.form.submit()
|
||
assert resp.location.endswith('?page=1')
|
||
resp = resp.follow()
|
||
assert 'Banner <span class="not-defined">' in resp.text
|
||
assert '>Delete<' not in resp.text
|
||
assert Asset.objects.filter(key='collectivity:banner').count() == 0
|
||
|
||
resp = app.get('/assets/collectivity:banner', status=404)
|
||
|
||
with override_settings(
|
||
COMBO_ASSET_SLOTS={'collectivity:cgu': {'label': 'CGU', 'asset-type': 'document'}}
|
||
):
|
||
resp = app.get('/manage/assets/')
|
||
assert 'CGU <span class="not-defined">' in resp.text
|
||
assert '>Delete<' not in resp.text
|
||
|
||
resp = resp.click('Overwrite')
|
||
resp.form['upload'] = Upload('test.pdf', b'PDF-1.4 ...', 'application.pdf')
|
||
resp = resp.form.submit().follow()
|
||
assert 'test.pdf' in resp.text
|
||
assert '<img data-href' not in resp.text # no thumbnail
|
||
assert '>Delete<' in resp.text
|
||
assert Asset.objects.filter(key='collectivity:cgu').count() == 1
|
||
|
||
# badly remove the file, manager page is still visible
|
||
os.unlink(Asset.objects.filter(key='collectivity:cgu')[0].asset.path)
|
||
resp = app.get('/manage/assets/')
|
||
assert '>CGU<' in resp.text
|
||
|
||
|
||
def test_asset_slots_for_cells_management(settings, app, admin_user):
|
||
app = login(app)
|
||
assert Asset.objects.count() == 0
|
||
|
||
settings.COMBO_CELL_ASSET_SLOTS = {'data_textcell': {'picture': {'prefix': 'Picture'}}}
|
||
page = Page.objects.create(title='xxx', slug='test_cell_assets', template_name='standard')
|
||
cell = TextCell.objects.create(page=page, order=0, slug='foo')
|
||
|
||
resp = app.get('/manage/assets/slots/%s/' % cell.get_reference())
|
||
assert '>Overwrite<' in resp.text
|
||
assert '>Delete<' not in resp.text
|
||
|
||
resp = resp.click('Overwrite')
|
||
resp.form['upload'] = Upload(
|
||
'test.png',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAgAABAADRWoApgAA\nAABJRU5ErkJggg=='
|
||
),
|
||
'image/png',
|
||
)
|
||
resp = resp.form.submit()
|
||
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
|
||
assert Asset.objects.filter(key=cell.get_asset_slot_key('picture')).count() == 1
|
||
|
||
assert app.get('/assets/%s' % cell.get_asset_slot_key('picture'), status=302)['location'].endswith(
|
||
'test.png'
|
||
)
|
||
|
||
# upload a new version of image
|
||
resp = app.get('/manage/assets/slots/%s/' % cell.get_reference())
|
||
resp = resp.click('Overwrite')
|
||
resp.form['upload'] = Upload(
|
||
'test2.png',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAgAABAADRWoApgAA\nAABJRU5ErkJggg=='
|
||
),
|
||
'image/png',
|
||
)
|
||
resp = resp.form.submit()
|
||
resp = app.get('/manage/assets/slots/%s/' % cell.get_reference())
|
||
assert 'test2.png' in resp.text
|
||
assert '<img data-href' in resp.text # thumbnail
|
||
assert '>Delete<' in resp.text
|
||
assert Asset.objects.filter(key=cell.get_asset_slot_key('picture')).count() == 1
|
||
|
||
# delete the image
|
||
resp = app.get('/manage/assets/slots/%s/' % cell.get_reference())
|
||
resp = resp.click('Delete')
|
||
resp = resp.form.submit()
|
||
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
|
||
resp = app.get('/manage/assets/slots/%s/' % cell.get_reference())
|
||
assert '>Overwrite<' in resp.text
|
||
assert '>Delete<' not in resp.text
|
||
assert Asset.objects.filter(key=cell.get_asset_slot_key('picture')).count() == 0
|
||
|
||
assert app.get('/assets/%s' % cell.get_asset_slot_key('picture'), status=404)
|
||
|
||
|
||
def test_asset_slots_for_map_layers_management(app, admin_user):
|
||
app = login(app)
|
||
assert Asset.objects.count() == 0
|
||
|
||
with override_settings(COMBO_MAP_LAYER_ASSET_SLOTS={'marker': {'prefix': 'Marker'}}):
|
||
resp = app.get('/manage/assets/')
|
||
assert 'Marker — "bicycles" map layer <span class="not-defined">' not in resp
|
||
|
||
layer = MapLayer()
|
||
layer.label = 'bicycles'
|
||
layer.geojson_url = 'http://example.org/geojson'
|
||
layer.marker_colour = 'FF0000'
|
||
layer.icon = 'fa-bicycle'
|
||
layer.icon_colour = '0000FF'
|
||
layer.save()
|
||
|
||
resp = app.get('/manage/assets/')
|
||
assert 'Marker — "bicycles" map layer <span class="not-defined">' in resp
|
||
resp = resp.click('Overwrite')
|
||
assert resp.request.url.endswith('/maplayer:marker:bicycles/')
|
||
|
||
|
||
def test_asset_slots_export(app, admin_user):
|
||
app = login(app)
|
||
|
||
with override_settings(COMBO_ASSET_SLOTS={'collectivity:banner': {'label': 'Banner'}}):
|
||
resp = app.get('/manage/assets/')
|
||
resp = resp.click('Overwrite')
|
||
resp.form['upload'] = Upload(
|
||
'test.png',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAgAABAADRWoApgAA\nAABJRU5ErkJggg=='
|
||
),
|
||
'image/png',
|
||
)
|
||
resp = resp.form.submit().follow()
|
||
assert Asset.objects.filter(key='collectivity:banner').count() == 1
|
||
|
||
resp = resp.click('Export')
|
||
assert resp.content_type == 'application/x-tar'
|
||
content = resp.content
|
||
Asset.objects.filter(key='collectivity:banner').delete()
|
||
|
||
resp = app.get('/manage/assets/')
|
||
resp = resp.click('Import')
|
||
resp.form['assets_file'] = Upload('test.tar', content)
|
||
resp = resp.form.submit()
|
||
|
||
assert Asset.objects.filter(key='collectivity:banner').count() == 1
|
||
|
||
|
||
def test_serve_asset(settings, app, admin_user):
|
||
settings.COMBO_ASSET_SLOTS = {'collectivity:banner': {'label': 'Banner'}}
|
||
app = login(app)
|
||
|
||
resp = app.get('/manage/assets/')
|
||
assert 'Banner <span class="not-defined">' in resp.text
|
||
assert '>Delete<' not in resp.text
|
||
resp = resp.click('Overwrite')
|
||
resp.form['upload'] = Upload(
|
||
'test.png',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAgAABAADRWoApgAA\nAABJRU5ErkJggg=='
|
||
),
|
||
'image/png',
|
||
)
|
||
resp = resp.form.submit().follow()
|
||
assert Asset.objects.filter(key='collectivity:banner').count() == 1
|
||
asset = Asset.objects.latest('pk')
|
||
|
||
assert app.get('/assets/collectivity:banner', status=302)['location'].endswith('test.png')
|
||
assert app.get('/assets/collectivity:banner?width=200', status=302)['location'].startswith(
|
||
'/media/cache/'
|
||
)
|
||
assert app.get('/assets/collectivity:banner?height=200', status=302)['location'].startswith(
|
||
'/media/cache/'
|
||
)
|
||
assert app.get('/assets/collectivity:banner?crop=center', status=302)['location'].endswith('test.png')
|
||
assert app.get('/assets/collectivity:banner?width=200&crop=center', status=302)['location'].startswith(
|
||
'/media/cache/'
|
||
)
|
||
|
||
# file is missing
|
||
os.remove(asset.asset.path)
|
||
app.get('/assets/collectivity:banner', status=404)
|
||
|
||
# upload a SVG
|
||
resp = resp.click('Delete')
|
||
resp = resp.form.submit()
|
||
assert Asset.objects.filter(key='collectivity:banner').count() == 0
|
||
|
||
resp = app.get('/manage/assets/')
|
||
assert 'Banner <span class="not-defined">' in resp.text
|
||
assert '>Delete<' not in resp.text
|
||
resp = resp.click('Overwrite')
|
||
resp.form['upload'] = Upload(
|
||
'test.svg',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAgAABAADRWoApgAA\nAABJRU5ErkJggg=='
|
||
),
|
||
'image/svg+xml',
|
||
)
|
||
resp = resp.form.submit()
|
||
assert Asset.objects.filter(key='collectivity:banner').count() == 1
|
||
|
||
# SVG: no thumbnail !
|
||
assert app.get('/assets/collectivity:banner', status=302)['location'].endswith('test.svg')
|
||
assert app.get('/assets/collectivity:banner?width=200', status=302)['location'].endswith('test.svg')
|
||
assert app.get('/assets/collectivity:banner?height=200', status=302)['location'].endswith('test.svg')
|
||
assert app.get('/assets/collectivity:banner?crop=center', status=302)['location'].endswith('test.svg')
|
||
assert app.get('/assets/collectivity:banner?width=200&crop=center', status=302)['location'].endswith(
|
||
'test.svg'
|
||
)
|
||
|
||
# unknown Asset key
|
||
app.get('/assets/foo:bar', status=404)
|
||
|
||
|
||
def test_asset_export_import(app, admin_user):
|
||
for path in ('uploads', 'assets', 'cache'):
|
||
if os.path.exists(default_storage.path(path)):
|
||
shutil.rmtree(default_storage.path(path))
|
||
|
||
app = login(app)
|
||
|
||
# upload a file
|
||
resp = app.get('/manage/assets/')
|
||
resp = resp.click('Upload')
|
||
resp.form['upload'] = Upload(
|
||
'test.png',
|
||
base64.decodebytes(
|
||
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
|
||
),
|
||
'image/png',
|
||
)
|
||
resp = resp.form.submit()
|
||
|
||
resp = app.get('/manage/assets/')
|
||
resp = resp.click('Export')
|
||
assert resp.content_type == 'application/x-tar'
|
||
content = resp.content
|
||
|
||
for path in ('uploads', 'assets'):
|
||
if os.path.exists(default_storage.path(path)):
|
||
shutil.rmtree(default_storage.path(path))
|
||
|
||
resp = app.get('/manage/assets/')
|
||
assert 'have any asset yet.' in resp.text
|
||
resp = resp.click('Import')
|
||
resp.form['assets_file'] = Upload('test.tar', content)
|
||
resp = resp.form.submit()
|
||
assert sum([len(x[2]) for x in os.walk(default_storage.path(''))]) == 2
|
||
resp = resp.follow()
|
||
assert 'The assets file has been imported.' in resp.text
|
||
|
||
# test no overwrite
|
||
filename = re.findall('data-href="(.*?)"', resp.text)[0][7:] # strip /media/
|
||
with open(default_storage.path(filename), 'w') as fd:
|
||
fd.write('test') # 4 bytes
|
||
assert os.stat(default_storage.path(filename)).st_size == 4
|
||
|
||
resp = app.get('/manage/assets/')
|
||
resp = resp.click('Import')
|
||
resp.form['assets_file'] = Upload('test.tar', content)
|
||
resp = resp.form.submit()
|
||
resp = resp.follow()
|
||
assert 'The assets file has been imported.' in resp.text
|
||
|
||
assert os.stat(default_storage.path(filename)).st_size == 4
|
||
|
||
# test overwrite
|
||
resp = app.get('/manage/assets/')
|
||
resp = resp.click('Import')
|
||
resp.form['overwrite'] = True
|
||
resp.form['assets_file'] = Upload('test.tar', content)
|
||
resp = resp.form.submit()
|
||
resp = resp.follow()
|
||
assert 'The assets file has been imported.' in resp.text
|
||
|
||
assert os.stat(default_storage.path(filename)).st_size == 67
|
||
|
||
# test uploading garbage
|
||
for path in ('uploads', 'assets'):
|
||
if os.path.exists(default_storage.path(path)):
|
||
shutil.rmtree(default_storage.path(path))
|
||
|
||
resp = app.get('/manage/assets/')
|
||
resp = resp.click('Import')
|
||
resp.form['assets_file'] = Upload('test.tar', b'garbage')
|
||
resp = resp.form.submit()
|
||
assert sum([len(x[2]) for x in os.walk(default_storage.path(''))]) == 0
|
||
resp = resp.follow()
|
||
assert 'The assets file is not valid.' in resp.text
|
||
|
||
|
||
def test_menu_json(app, admin_user):
|
||
app.get('/manage/menu.json', status=302)
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/menu.json')
|
||
assert resp.headers['content-type'] == 'application/json'
|
||
assert resp.json[0]['label'] == 'Content Management'
|
||
|
||
resp = app.get('/manage/menu.json?callback=fooBar')
|
||
assert resp.headers['content-type'] == 'application/javascript'
|
||
assert resp.text.startswith('fooBar([{"')
|
||
|
||
|
||
def test_page_multiple_link_cells(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
for i in range(10):
|
||
Page(title='page %d' % i, slug='page%d' % i, template_name='standard').save()
|
||
cell = LinkCell(page=page, placeholder='content', order=0)
|
||
cell.save()
|
||
|
||
patched_orig = Page.get_as_reordered_flat_hierarchy
|
||
app = login(app)
|
||
with mock.patch('combo.data.models.Page.get_as_reordered_flat_hierarchy') as func:
|
||
func.side_effect = lambda *args, **kwargs: patched_orig(*args, **kwargs)
|
||
app.get('/manage/pages/%s/' % page.id)
|
||
# only 1 call for combo.data.forms.get_page_choices
|
||
# 1 call for get_previous_page and 1 call for get_next_page
|
||
assert func.call_count == 3
|
||
|
||
|
||
def test_page_cell_placeholder(app, admin_user):
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
cell = JsonCell(page=page, placeholder='content', order=0, url='http://example.com')
|
||
cell.save()
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content']
|
||
|
||
# check a placeholder within a cell is included
|
||
cell.template_string = '{% placeholder "foobar" name="Foobar" %}'
|
||
cell.save()
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content', 'foobar']
|
||
assert re.findall('<h2>(.*)</h2>', resp.text) == ['Page - One', 'Content', 'JSON Prototype / Foobar']
|
||
|
||
|
||
def test_page_familycell_placeholder(app, admin_user):
|
||
page = Page(title='My family', slug='my-family', template_name='standard')
|
||
page.save()
|
||
cell = FamilyInfosCell(page=page, placeholder='content', order=0)
|
||
cell.save()
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content', "family_unlinked_user"]
|
||
assert re.findall('<h2>(.*)</h2>', resp.text) == [
|
||
'Page - My family',
|
||
'Content',
|
||
'Family Information Cell / Top content for unlinked users',
|
||
]
|
||
|
||
|
||
def test_page_discover_placeholder_with_error_cells(app, admin_user):
|
||
page = Page(title='One', slug='one', template_name='standard')
|
||
page.save()
|
||
cell = JsonCell(page=page, placeholder='content', order=0, url='xx')
|
||
cell.template_string = '{% xxx %}'
|
||
cell.save()
|
||
|
||
app = login(app)
|
||
|
||
with mock.patch('combo.utils.requests.get') as requests_get:
|
||
data = {'data': [{'url': 'xxx', 'text': 'xxx'}]}
|
||
with pytest.raises(TemplateSyntaxError):
|
||
cell.render({})
|
||
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content']
|
||
|
||
|
||
def test_page_versionning(app, admin_user):
|
||
Page.objects.all().delete()
|
||
PageSnapshot.objects.all()
|
||
|
||
page = Page(title='One', slug='one')
|
||
page.save()
|
||
|
||
cell1 = TextCell(page=page, placeholder='content', text='Foobar1', order=0)
|
||
cell1.save()
|
||
cell2 = TextCell(page=page, placeholder='content', text='Foobar2', order=1)
|
||
cell2.save()
|
||
cell3 = TextCell(page=page, placeholder='content', text='Foobar3', order=2)
|
||
cell3.save()
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.id, status=200)
|
||
|
||
# update title
|
||
resp = resp.click(href='.*/title')
|
||
resp.form['title'].value = 'One Two'
|
||
resp = resp.form.submit()
|
||
resp = resp.follow()
|
||
assert Page.objects.all()[0].title == 'One Two'
|
||
assert PageSnapshot.objects.all().count() == 1
|
||
|
||
# change cell text
|
||
resp.forms[0]['c%s-text' % cell1.get_reference()].value = 'Hello world'
|
||
resp = resp.forms[0].submit().follow()
|
||
assert PageSnapshot.objects.all().count() == 2
|
||
|
||
# reorder cells
|
||
params = []
|
||
for i, cell in enumerate([cell3, cell1, cell2]):
|
||
params.append(('ph_data_textcell-%s' % cell.id, 'content')) # no placeholder change
|
||
params.append(('pos_data_textcell-%s' % cell.id, str(i)))
|
||
|
||
app.get('/manage/pages/%s/order?%s' % (page.id, urlencode(params)))
|
||
assert PageSnapshot.objects.all().count() == 3
|
||
|
||
resp = resp.click('History')
|
||
assert (
|
||
resp.text.index('reordered cells')
|
||
< resp.text.index('changed cell')
|
||
< resp.text.index('changed title')
|
||
)
|
||
|
||
with CaptureQueriesContext(connection) as ctx:
|
||
resp2 = resp.click('view', index=1)
|
||
assert len(ctx.captured_queries) == 71
|
||
assert Page.snapshots.latest('pk').related_cells == {'cell_types': ['data_textcell']}
|
||
assert resp2.text.index('Hello world') < resp2.text.index('Foobar3')
|
||
|
||
resp2 = resp.click('view', index=0)
|
||
assert resp2.text.index('Hello world') > resp2.text.index('Foobar3')
|
||
|
||
resp2 = resp.click('view', index=2)
|
||
assert 'Foobar1' in resp2.text
|
||
assert 'Hello world' not in resp2.text
|
||
|
||
assert Page.objects.all().count() == 1
|
||
assert PageSnapshot.objects.filter(page=page)[0].user.username == 'admin'
|
||
|
||
# check with asynchronous cells
|
||
resp = app.get('/manage/pages/%s/add-cell-to-content/data_jsoncell/default/' % page.id)
|
||
resp = resp.follow()
|
||
cell_id = JsonCell.objects.last().id
|
||
resp.forms[3]['cdata_jsoncell-%s-template_string' % cell_id].value = 'A{{json.data.0.text}}B'
|
||
resp.forms[3]['cdata_jsoncell-%s-url' % cell_id].value = 'http://example.com'
|
||
resp = resp.forms[3].submit().follow()
|
||
assert PageSnapshot.objects.all().count() == 5 # add + change
|
||
|
||
resp.forms[3]['cdata_jsoncell-%s-template_string' % cell_id].value = 'C{{json.data.0.text}}D'
|
||
resp = resp.forms[3].submit().follow()
|
||
assert PageSnapshot.objects.all().count() == 6
|
||
|
||
resp.forms[1]['c%s-text' % cell1.get_reference()].value = 'Foo back to 1'
|
||
resp = resp.forms[0].submit().follow()
|
||
|
||
resp = resp.click('History')
|
||
assert 'added cell' in resp.text
|
||
|
||
resp2 = resp.click('view', index=1)
|
||
json_cell_url = re.findall(r'/ajax/cell/.*/data_jsoncell-.*/', resp2.text)[0]
|
||
|
||
with mock.patch('combo.utils.requests.get') as requests_get:
|
||
data = {'data': [{'url': 'xxx', 'text': 'xxx'}]}
|
||
requests_get.return_value = mock.Mock(json=lambda: data, status_code=200)
|
||
resp3 = app.get(json_cell_url)
|
||
assert resp3.text.strip() == 'CxxxD'
|
||
|
||
# previous version should return AxxxB
|
||
resp2 = resp.click('view', index=2)
|
||
json_cell_url = re.findall(r'/ajax/cell/.*/data_jsoncell-.*/', resp2.text)[0]
|
||
|
||
with mock.patch('combo.utils.requests.get') as requests_get:
|
||
data = {'data': [{'url': 'xxx', 'text': 'xxx'}]}
|
||
requests_get.return_value = mock.Mock(json=lambda: data, status_code=200)
|
||
resp3 = app.get(json_cell_url)
|
||
assert resp3.text.strip() == 'AxxxB'
|
||
|
||
# check anonymous users can't get to cells from snapshots
|
||
app.get('/logout/')
|
||
resp3 = app.get(json_cell_url, status=403)
|
||
|
||
# restore a snapshot
|
||
app = login(app)
|
||
resp = resp.click('restore', index=6)
|
||
with CaptureQueriesContext(connection) as ctx:
|
||
resp = resp.form.submit().follow()
|
||
assert len(ctx.captured_queries) == 327
|
||
|
||
resp2 = resp.click('See online')
|
||
assert resp2.text.index('Foobar1') < resp2.text.index('Foobar2') < resp2.text.index('Foobar3')
|
||
|
||
# clean it up
|
||
Page.snapshots.all().delete()
|
||
assert Page.objects.count() == 1
|
||
assert TextCell.objects.count() == 3
|
||
assert JsonCell.objects.count() == 0
|
||
|
||
# page does not exists
|
||
app.get('/manage/pages/0/history', status=404)
|
||
|
||
|
||
def test_restore_page_attributes(app):
|
||
page1 = Page.objects.create(title='One', slug='one')
|
||
page2 = Page.objects.create(title='Two', slug='two', parent=page1, exclude_from_navigation=False)
|
||
page3 = Page.objects.create(title='Three', slug='three')
|
||
page4 = Page.objects.create(title='Four', slug='four')
|
||
|
||
assert page1.order == 1
|
||
assert page2.order == 2
|
||
assert page3.order == 3
|
||
assert page4.order == 4
|
||
|
||
PageSnapshot.take(page2)
|
||
snapshot = PageSnapshot.objects.latest('pk')
|
||
|
||
# move page2, remove page 1
|
||
page2.parent = page4
|
||
page2.order = 3
|
||
page2.save()
|
||
page1.delete()
|
||
page3.order = 1
|
||
page3.save()
|
||
page4.order = 2
|
||
page4.save()
|
||
|
||
assert page3.order == 1
|
||
assert page4.order == 2
|
||
assert page2.order == 3
|
||
|
||
# restore snapshot
|
||
page2 = snapshot.restore()
|
||
|
||
# check some attributes
|
||
assert page2.order == 3
|
||
assert page2.parent == page4
|
||
assert page2.exclude_from_navigation is False
|
||
|
||
PageSnapshot.take(page2)
|
||
snapshot = PageSnapshot.objects.latest('pk')
|
||
|
||
# exclude page2 from navigation
|
||
page2.exclude_from_navigation = True
|
||
page2.save()
|
||
|
||
# restore snapshot
|
||
page2 = snapshot.restore()
|
||
|
||
# check exclude_from_navigation param
|
||
assert page2.exclude_from_navigation is True
|
||
|
||
|
||
def test_view_old_snapshot(app, admin_user):
|
||
page = Page.objects.create(title='One', slug='one')
|
||
PageSnapshot.take(page)
|
||
snapshot = PageSnapshot.objects.get()
|
||
# delete creation_timestamp field
|
||
del snapshot.serialization['fields']['creation_timestamp']
|
||
snapshot.save()
|
||
# and try to visit the snapshot
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
resp = resp.click('History')
|
||
# should succeed
|
||
resp = resp.click('view')
|
||
|
||
|
||
def test_django_admin(app, admin_user):
|
||
resp = app.get('/admin/')
|
||
resp = resp.follow() # -> /admin/login/
|
||
resp = resp.follow() # -> /login/
|
||
resp.form['username'] = 'admin'
|
||
resp.form['password'] = 'admin'
|
||
resp = resp.form.submit()
|
||
resp = resp.follow() # -> /admin/
|
||
assert '/admin/logout/' in resp.text
|
||
resp = resp.click(href='/admin/logout/')
|
||
resp = resp.follow() # -> /logout/
|
||
assert urlparse.urlparse(resp.location).path == '/'
|
||
|
||
|
||
def test_json_cell_syntax_validation(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page(title='One', slug='one')
|
||
page.save()
|
||
app = login(app)
|
||
# syntax error
|
||
resp = app.get('/manage/pages/%s/add-cell-to-content/data_jsoncell/default/' % page.id)
|
||
resp = resp.follow()
|
||
cell_id = JsonCell.objects.last().id
|
||
resp.forms[0]['cdata_jsoncell-%s-template_string' % cell_id].value = '{% syntax|error %}'
|
||
resp.forms[0]['cdata_jsoncell-%s-url' % cell_id].value = 'http://example.com'
|
||
resp = resp.forms[0].submit()
|
||
assert 'syntax error: Invalid block tag' in resp.text
|
||
assert JsonCell.objects.count() == 1
|
||
assert JsonCell.objects.first().template_string is None
|
||
# valid syntax
|
||
resp = app.get('/manage/pages/%s/' % page.id)
|
||
resp.forms[0]['cdata_jsoncell-%s-template_string' % cell_id].value = '{{ ok }}'
|
||
resp.forms[0]['cdata_jsoncell-%s-url' % cell_id].value = 'http://example.com'
|
||
resp = resp.forms[0].submit().follow()
|
||
assert 'syntax error' not in resp.text
|
||
assert JsonCell.objects.count() == 1
|
||
assert JsonCell.objects.first().template_string == '{{ ok }}'
|
||
|
||
|
||
def test_link_cell_validation():
|
||
form = LinkCellForm(data={'url': 'http://example.com'})
|
||
assert form.is_valid() is True
|
||
|
||
form = LinkCellForm(data={'url': '{{test_url}}'})
|
||
assert form.is_valid() is True
|
||
|
||
form = LinkCellForm(data={'url': '{{{test_url}}'})
|
||
assert form.is_valid() is False
|
||
assert form.errors['url'] == ["syntax error: Could not parse the remainder: '{test_url' from '{test_url'"]
|
||
|
||
|
||
def test_add_edit_delete_list_link_item(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page.objects.create(title='One', slug='one', template_name='standard')
|
||
cell = LinkListCell.objects.create(order=0, placeholder='content', page=page)
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||
assert PageSnapshot.objects.count() == 0
|
||
|
||
resp = resp.click(href='.*/add-link/link$')
|
||
resp.forms[0]['title'] = 'Hello world'
|
||
resp.forms[0]['url'] = 'http://example.com'
|
||
resp = resp.forms[0].submit()
|
||
assert resp.status_int == 302
|
||
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
|
||
assert LinkCell.objects.count() == 1
|
||
item = LinkCell.objects.get()
|
||
assert item.title == 'Hello world'
|
||
assert item.url == 'http://example.com'
|
||
assert item.page == page
|
||
assert item.placeholder == cell.link_placeholder
|
||
assert PageSnapshot.objects.count() == 1
|
||
|
||
resp = resp.follow()
|
||
resp = resp.click(href='.*/link/%s/$' % item.get_reference())
|
||
resp.forms[0]['title'] = 'Hello world 2'
|
||
resp.forms[0]['url'] = 'http://example2.com'
|
||
resp = resp.forms[0].submit()
|
||
assert resp.status_int == 302
|
||
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
|
||
assert LinkCell.objects.count() == 1
|
||
item.refresh_from_db()
|
||
assert item.title == 'Hello world 2'
|
||
assert item.url == 'http://example2.com'
|
||
assert PageSnapshot.objects.count() == 2
|
||
|
||
resp = resp.follow()
|
||
resp = resp.click(href='.*/link/%s/delete' % item.get_reference())
|
||
resp = resp.forms[0].submit()
|
||
assert resp.status_int == 302
|
||
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
|
||
assert LinkCell.objects.count() == 0
|
||
assert PageSnapshot.objects.count() == 3
|
||
|
||
|
||
def test_edit_link_list_order(app, admin_user):
|
||
Page.objects.all().delete()
|
||
page = Page.objects.create(title='One', slug='one', template_name='standard')
|
||
cell = LinkListCell.objects.create(order=0, page=page)
|
||
items = []
|
||
for i in range(5):
|
||
items.append(
|
||
LinkCell.objects.create(
|
||
page=page,
|
||
placeholder=cell.link_placeholder,
|
||
title='Foo %s' % i,
|
||
url='http://example.net/',
|
||
link_page=page,
|
||
order=i + 1,
|
||
)
|
||
)
|
||
|
||
params = []
|
||
new_order = [2, 3, 1, 4, 5]
|
||
for i, (item, new_pos) in enumerate(zip(items, new_order)):
|
||
params.append(('pos_%s' % item.pk, str(new_pos)))
|
||
|
||
app = login(app)
|
||
resp = app.get('/manage/pages/%s/cell/%s/order?%s' % (page.pk, cell.get_reference(), urlencode(params)))
|
||
assert resp.status_code == 204
|
||
for i, item in enumerate(items):
|
||
item.refresh_from_db()
|
||
assert item.order == new_order[i]
|