combo/tests/test_manager.py

1865 lines
75 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import base64
import datetime
import json
import mock
import os
import re
import shutil
from django.core.files.storage import default_storage
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.apps.assets.models import Asset
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' # first one was taken
assert page.exclude_from_navigation is True
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)
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'
# slug
resp = resp.click(href='.*/slug')
resp.form['slug'].value = 'foobar'
resp = resp.form.submit()
resp = resp.follow()
assert Page.objects.all()[0].slug == 'foobar'
# 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_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' in resp.text
assert 'This cell will no longer be displayed in 2 days.' in resp.text
assert '<span class="invalid">foo_bar_reason</span>' 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_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) == 31
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(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('Export')
assert resp.headers['content-type'] == 'application/json'
assert resp.json['pages'][0].get('fields').get('slug') == 'one'
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(app, admin_user):
Page.objects.all().delete()
page1 = Page(title='One', slug='one', template_name='standard')
page1.save()
page2 = Page(title='Two', slug='two', parent=page1, template_name='standard')
page2.save()
page3 = Page(title='Three', slug='three', parent=page2, template_name='standard')
page3.save()
page4 = Page(title='Four', slug='four', parent=page1, template_name='standard')
page4.save()
cell = TextCell(page=page1, placeholder='content', text='Foobar', order=0)
cell.save()
cell = LinkCell(page=page1, placeholder='content', link_page=page2, order=0)
cell.save()
cell = LinkCell(page=page2, placeholder='content', link_page=page1, order=0)
cell.save()
app = login(app)
resp = app.get('/manage/')
resp = resp.click('Export Site')
assert resp.headers['content-type'] == 'application/json'
site_export = resp.body
Page.objects.all().delete()
assert LinkCell.objects.count() == 0
app = login(app)
resp = app.get('/manage/')
resp = resp.click('Import Site')
resp.form['site_json'] = Upload('site-export.json', site_export, 'application/json')
resp = resp.form.submit()
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'
# check with invalid file
resp = app.get('/manage/')
resp = resp.click('Import Site')
resp.form['site_json'] = Upload('site-export.json', b'invalid content', 'application/json')
resp = resp.form.submit()
assert 'File is not in the expected JSON format.' in resp.text
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')
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_json'] = 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')
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_json'] = Upload('site-export.json', force_bytes(json.dumps(payload)), 'application/json')
resp = resp.form.submit().follow()
assert 'Unknown parent for page &quot;One&quot;; parent has been reset and page was excluded from navigation.' in resp.text
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/1/#cell-%s' % 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/1/#cell-%s' % 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<' 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<' 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<' 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_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.decodestring(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<' 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<' 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=1)
cell3.save()
anonymous_app = app
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')
resp2 = resp.click('view', index=1)
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 not 'Hello world' 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()
resp.forms[3]['cdata_jsoncell-1-template_string'].value = 'A{{json.data.0.text}}B'
resp.forms[3]['cdata_jsoncell-1-url'].value = 'http://example.com'
resp = resp.forms[3].submit().follow()
assert PageSnapshot.objects.all().count() == 5 # add + change
resp.forms[3]['cdata_jsoncell-1-template_string'].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)
resp = resp.form.submit()
resp = resp.follow()
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_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()
resp.forms[0]['cdata_jsoncell-1-template_string'].value = '{% syntax|error %}'
resp.forms[0]['cdata_jsoncell-1-url'].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-1-template_string'].value = '{{ ok }}'
resp.forms[0]['cdata_jsoncell-1-url'].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]