combo/tests/test_manager.py

1102 lines
43 KiB
Python

import base64
import json
import os
import re
import shutil
import mock
from django.core.files.storage import default_storage
from django.core.urlresolvers import reverse
from django.conf import settings
from django.contrib.auth.models import User, Group
from django.template import TemplateSyntaxError
from django.test import override_settings
from django.utils.http import urlencode
from django.utils.six import BytesIO
from django.utils.six.moves.urllib import parse as urlparse
import pytest
from webtest import TestApp
from webtest import Upload
from combo.wsgi import application
from combo.data.models import Page, CellBase, TextCell, LinkCell, ConfigJsonCell, JsonCell, PageSnapshot
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
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()
assert resp.location.endswith('/manage/pages/1/')
assert Page.objects.get(slug='index').title == 'Home'
assert Page.objects.get(slug='index').template_name == 'standard' # first one was taken
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_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='.*/exclude-from-navigation')
resp.form['exclude_from_navigation'].checked = True
resp = resp.form.submit()
resp = resp.follow()
assert Page.objects.all()[0].exclude_from_navigation is True
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
resp = resp.click(href='.*/remove-picture/')
resp = resp.follow()
assert '<h2>Page - One</h2>' in resp.text
assert not '<img' in resp.text
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-')
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(title='One', slug='one', template_name='standard')
page2 = Page(title='Two', slug='two', parent=page1, template_name='standard')
page3 = Page(title='Three', slug='three', parent=page2, template_name='standard')
page4 = Page(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_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'))
assert resp.location.endswith('/manage/pages/1/')
cells = CellBase.get_cells(page_id=page.id)
assert len(cells) == 1
assert isinstance(cells[0], TextCell)
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/')
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)
assert resp.location.endswith('/manage/pages/%s/' % page.id)
cells = CellBase.get_cells(page_id=page.id)
assert len(cells) == 1
assert isinstance(cells[0], TextCell)
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_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)
assert resp.location.endswith('/manage/pages/%s/' % page.id)
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'
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 not 'There are no options for this cell.' 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/' % page.id)
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.decodestring(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')
resp.form['upload'] = Upload('test.png',
base64.decodestring(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
# 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.decodestring(pix)))
# test upload
resp = app.get('/manage/assets/')
resp = resp.click('Upload')
resp.form['upload'] = Upload('test.png',
base64.decodestring(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.decodestring(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.decodestring(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_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.decodestring(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.decodestring(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', '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
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.decodestring(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.return_value = []
resp = app.get('/manage/pages/%s/' % page.id)
assert func.call_count == 1
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', 'footer']
# check a placeholder within a cell is included
cell.template_string = '{% load combo %}{% placeholder "foobar" name="Foobar" %}'
cell.save()
resp = app.get('/manage/pages/%s/' % page.id)
assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content', 'foobar', 'footer']
assert re.findall('<h2>(.*)</h2>', resp.text) == [
'Page - One', 'Content', 'JSON Feed / Foobar', 'Footer']
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", "footer"]
assert re.findall('<h2>(.*)</h2>', resp.text) == [
'Page - My family', 'Content',
'Family Information Cell / Top content for unlinked users', 'Footer']
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', 'footer']
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(content=json.dumps(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(content=json.dumps(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
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 == '/'