1051 lines
38 KiB
Python
1051 lines
38 KiB
Python
import io
|
|
import os
|
|
import shutil
|
|
import xml.etree.ElementTree as ET
|
|
|
|
import pytest
|
|
from quixote.http_request import Upload
|
|
|
|
from wcs.blocks import BlockDef
|
|
from wcs.carddef import CardDef
|
|
from wcs.categories import Category
|
|
from wcs.data_sources import NamedDataSource
|
|
from wcs.fields import CommentField, ItemField, StringField
|
|
from wcs.formdef import FormDef
|
|
from wcs.mail_templates import MailTemplate
|
|
from wcs.qommon.form import UploadedFile
|
|
from wcs.qommon.misc import localstrftime
|
|
from wcs.wf.form import WorkflowFormFieldsFormDef
|
|
from wcs.workflows import Workflow, WorkflowVariablesFieldsFormDef
|
|
from wcs.wscalls import NamedWsCall
|
|
|
|
from .admin_pages.test_all import create_role, create_superuser
|
|
from .utilities import clean_temporary_pub, create_temporary_pub, get_app, login
|
|
|
|
|
|
@pytest.fixture
|
|
def pub(emails):
|
|
pub = create_temporary_pub(lazy_mode=True)
|
|
pub.cfg['identification'] = {'methods': ['password']}
|
|
pub.cfg['language'] = {'language': 'en'}
|
|
pub.write_cfg()
|
|
FormDef.wipe()
|
|
CardDef.wipe()
|
|
NamedDataSource.wipe()
|
|
pub.snapshot_class.wipe()
|
|
pub.user_class.wipe()
|
|
return pub
|
|
|
|
|
|
@pytest.fixture
|
|
def formdef_with_history(pub):
|
|
formdef = FormDef()
|
|
formdef.name = 'testform'
|
|
formdef.fields = [StringField(id='1', label='Test', type='string')]
|
|
formdef.store()
|
|
|
|
for i in range(5):
|
|
formdef.name = 'testform %s' % i
|
|
formdef.description = 'this is a description (%s)' % i
|
|
formdef.store()
|
|
|
|
return formdef
|
|
|
|
|
|
def teardown_module(module):
|
|
clean_temporary_pub()
|
|
|
|
|
|
def test_snapshot_basics(pub):
|
|
formdef = FormDef()
|
|
formdef.name = 'testform'
|
|
# start with a big content
|
|
formdef.fields = [CommentField(id='0', label='Test ' * 500, type='comment')]
|
|
formdef.store()
|
|
|
|
# first occurence, complete snapshot stored
|
|
assert pub.snapshot_class.count() == 1
|
|
snapshot1 = pub.snapshot_class.get_latest('formdef', formdef.id)
|
|
assert snapshot1.serialization is not None
|
|
assert '>testform<' in snapshot1.serialization
|
|
assert snapshot1.patch is None
|
|
assert snapshot1.instance # possible to restore
|
|
|
|
# no changes
|
|
formdef.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
|
|
# patch only
|
|
formdef.name = 'testform2'
|
|
formdef.store()
|
|
assert pub.snapshot_class.count() == 2
|
|
|
|
snapshot2 = pub.snapshot_class.get_latest('formdef', formdef.id)
|
|
assert snapshot2.serialization is None
|
|
assert '>testform2<' in snapshot2.patch
|
|
assert snapshot2.instance # possible to restore
|
|
|
|
# no diff with latest snap but label is given
|
|
pub.snapshot_class.snap(instance=formdef, label="foo bar")
|
|
assert pub.snapshot_class.count() == 3
|
|
snapshot3 = pub.snapshot_class.get_latest('formdef', formdef.id)
|
|
assert snapshot3.serialization is None
|
|
assert '>testform2<' in snapshot3.patch
|
|
assert snapshot2.patch == snapshot3.patch
|
|
assert snapshot3.instance # possible to restore
|
|
|
|
# patch is longer as serialization, store serialization
|
|
formdef.name = 'testform3'
|
|
formdef.fields += [StringField(id=str(i + 1), label='Test %s' % i, type='string') for i in range(0, 10)]
|
|
formdef.store()
|
|
assert pub.snapshot_class.count() == 4
|
|
snapshot4 = pub.snapshot_class.get_latest('formdef', formdef.id)
|
|
assert snapshot4.serialization is not None
|
|
assert '>testform3<' in snapshot4.serialization
|
|
assert snapshot4.patch is None
|
|
assert snapshot4.instance # possible to restore
|
|
|
|
# no diff with latest snap but label is given
|
|
pub.snapshot_class.snap(instance=formdef, label="foo bar")
|
|
assert pub.snapshot_class.count() == 5
|
|
snapshot5 = pub.snapshot_class.get_latest('formdef', formdef.id)
|
|
assert snapshot5.serialization is None
|
|
assert snapshot5.patch == '' # no difference with latest snap, which has a serialization
|
|
assert snapshot5.instance # possible to restore
|
|
|
|
# add snapshots with patches
|
|
snapshot6 = None
|
|
for i in range(10):
|
|
formdef.name = 'testform%s' % (i + 6)
|
|
formdef.fields.append(StringField(id=str(i + 11), label='Test %s' % (i + 10), type='string'))
|
|
formdef.store()
|
|
snapshot = pub.snapshot_class.get_latest('formdef', formdef.id)
|
|
assert snapshot.patch is None or len(snapshot.patch) < len(snapshot.get_serialization()) / 10
|
|
snapshot6 = snapshot6 or snapshot
|
|
assert pub.snapshot_class.count() == 15
|
|
|
|
# patch is longer as serialization, store serialization
|
|
formdef.name = 'testform16'
|
|
formdef.fields += [
|
|
StringField(id=str(i + 20), label='Test %s' % (i + 20), type='string') for i in range(0, 30)
|
|
]
|
|
formdef.store()
|
|
assert pub.snapshot_class.count() == 16
|
|
snapshot16 = pub.snapshot_class.get_latest('formdef', formdef.id)
|
|
assert snapshot16.serialization is not None
|
|
assert '>testform16<' in snapshot16.serialization
|
|
assert snapshot16.patch is None
|
|
assert snapshot16.instance # possible to restore
|
|
|
|
# check that snapshot6 restoration:
|
|
# don't take snapshot15 as latest_complete
|
|
latest_complete = snapshot6.get_latest(snapshot6.object_type, snapshot6.object_id, complete=True)
|
|
assert latest_complete.id == snapshot16.id
|
|
latest_complete = snapshot6.get_latest(
|
|
snapshot6.object_type, snapshot6.object_id, complete=True, max_timestamp=snapshot6.timestamp
|
|
)
|
|
assert latest_complete.id == snapshot4.id
|
|
assert snapshot6.instance # possible to restore
|
|
assert [int(f.id) for f in snapshot6.instance.fields] == list(range(0, 12))
|
|
|
|
|
|
def test_snapshot_diff(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
formdef = FormDef()
|
|
formdef.name = 'testform'
|
|
formdef.fields = []
|
|
formdef.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
snapshot1 = pub.snapshot_class.get_latest('formdef', formdef.id)
|
|
|
|
formdef.fields = [StringField(id=1, label='Test', type='string')]
|
|
formdef.store()
|
|
assert pub.snapshot_class.count() == 2
|
|
snapshot2 = pub.snapshot_class.get_latest('formdef', formdef.id)
|
|
|
|
formdef.fields += [StringField(id=2, label='Test bis', type='string')]
|
|
formdef.store()
|
|
assert pub.snapshot_class.count() == 3
|
|
snapshot3 = pub.snapshot_class.get_latest('formdef', formdef.id)
|
|
|
|
app = login(get_app(pub))
|
|
resp = app.get('/backoffice/forms/%s/history/' % formdef.id)
|
|
assert 'name="version1" value="%s"' % snapshot3.id in resp
|
|
assert 'name="version2" value="%s"' % snapshot3.id not in resp
|
|
assert 'name="version1" value="%s"' % snapshot2.id in resp
|
|
assert 'name="version2" value="%s"' % snapshot2.id in resp
|
|
assert 'name="version1" value="%s"' % snapshot1.id not in resp
|
|
assert 'name="version2" value="%s"' % snapshot1.id in resp
|
|
|
|
resp = app.get(
|
|
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s'
|
|
% (formdef.id, snapshot1.id, snapshot3.id)
|
|
)
|
|
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot1.id, snapshot1.id) in resp
|
|
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot3.id, snapshot3.id) in resp
|
|
assert resp.text.count('diff_sub') == 1
|
|
assert resp.text.count('diff_add') == 24
|
|
|
|
resp = app.get(
|
|
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s'
|
|
% (formdef.id, snapshot3.id, snapshot1.id)
|
|
)
|
|
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot1.id, snapshot1.id) in resp
|
|
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot3.id, snapshot3.id) in resp
|
|
assert resp.text.count('diff_sub') == 1
|
|
assert resp.text.count('diff_add') == 24
|
|
|
|
resp = app.get(
|
|
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s'
|
|
% (formdef.id, snapshot2.id, snapshot3.id)
|
|
)
|
|
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot2.id, snapshot2.id) in resp
|
|
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot3.id, snapshot3.id) in resp
|
|
assert resp.text.count('diff_sub') == 0
|
|
assert resp.text.count('diff_add') == 11
|
|
|
|
formdef.fields = [StringField(id=1, label='Test', type='string')]
|
|
formdef.store()
|
|
assert pub.snapshot_class.count() == 4
|
|
snapshot4 = pub.snapshot_class.get_latest('formdef', formdef.id)
|
|
|
|
resp = app.get(
|
|
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s'
|
|
% (formdef.id, snapshot3.id, snapshot4.id)
|
|
)
|
|
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot3.id, snapshot3.id) in resp
|
|
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot4.id, snapshot4.id) in resp
|
|
assert resp.text.count('diff_sub') == 11
|
|
assert resp.text.count('diff_add') == 0
|
|
|
|
resp = app.get('/backoffice/forms/%s/history/compare' % (formdef.id), status=404)
|
|
resp = app.get(
|
|
'/backoffice/forms/%s/history/compare?version1=%s' % (formdef.id, snapshot4.id), status=404
|
|
)
|
|
resp = app.get(
|
|
'/backoffice/forms/%s/history/compare?version2=%s' % (formdef.id, snapshot4.id), status=404
|
|
)
|
|
resp = app.get(
|
|
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s' % (formdef.id, snapshot3.id, 0),
|
|
status=404,
|
|
)
|
|
resp = app.get(
|
|
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s' % (formdef.id, 0, snapshot4.id),
|
|
status=404,
|
|
)
|
|
|
|
|
|
def test_snapshot_instance(pub):
|
|
formdef = FormDef()
|
|
formdef.name = 'testform'
|
|
formdef.fields = []
|
|
formdef.store()
|
|
|
|
carddef = CardDef()
|
|
carddef.name = 'testcard'
|
|
carddef.fields = []
|
|
carddef.store()
|
|
|
|
# remove existing snapshots as they may be duplicated if table_name was
|
|
# generated in a different second.
|
|
pub.snapshot_class.wipe()
|
|
|
|
carddef.name = 'testcard2'
|
|
carddef.store()
|
|
|
|
for i in range(10):
|
|
formdef.name = 'testform %s' % i
|
|
formdef.store()
|
|
|
|
assert pub.snapshot_class.count() == 11
|
|
|
|
snapshots = pub.snapshot_class.select_object_history(formdef)
|
|
assert len(snapshots) == 10
|
|
for i in range(10):
|
|
assert snapshots[i].serialization is None # not loaded
|
|
assert snapshots[i].patch is None # not loaded
|
|
assert pub.snapshot_class.get(snapshots[i].id).instance.name == 'testform %s' % (9 - i)
|
|
|
|
snapshots = pub.snapshot_class.select_object_history(carddef)
|
|
assert len(snapshots) == 1
|
|
|
|
|
|
def test_snapshot_user(pub):
|
|
user = pub.user_class()
|
|
user.name = 'User Name'
|
|
user.email = 'foo@localhost'
|
|
user.store()
|
|
|
|
carddef = CardDef()
|
|
carddef.name = 'testcard'
|
|
carddef.fields = []
|
|
carddef.store()
|
|
snapshot = pub.snapshot_class.select_object_history(carddef)[0]
|
|
assert snapshot.user is None
|
|
|
|
snapshot.user_id = user.id
|
|
snapshot.store()
|
|
snapshot = pub.snapshot_class.select_object_history(carddef)[0]
|
|
assert str(snapshot.user) == 'User Name'
|
|
|
|
snapshot.user_id = 'nope'
|
|
snapshot.store()
|
|
snapshot = pub.snapshot_class.select_object_history(carddef)[0]
|
|
assert str(snapshot.user) == 'unknown user'
|
|
|
|
|
|
def test_form_snapshot_comments(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
app = login(get_app(pub))
|
|
resp = app.get('/backoffice/forms/')
|
|
|
|
resp = resp.click('New Form')
|
|
resp.form['name'] = 'form title'
|
|
resp = resp.form.submit().follow()
|
|
# .store() then .disabled = True, then .store() again -> 2.
|
|
assert pub.snapshot_class.count() == 2
|
|
|
|
resp = resp.click('Confirmation Page')
|
|
assert resp.form['confirmation'].checked
|
|
resp.form['confirmation'].checked = False
|
|
resp = resp.form.submit().follow()
|
|
assert pub.snapshot_class.count() == 3
|
|
assert (
|
|
pub.snapshot_class.select(order_by='-timestamp')[0].comment
|
|
== 'Changed "Confirmation Page" parameters'
|
|
)
|
|
|
|
resp = resp.click(href='fields/')
|
|
resp.forms[0]['label'] = 'foobar'
|
|
resp.forms[0]['type'] = 'string'
|
|
resp = resp.forms[0].submit().follow()
|
|
assert pub.snapshot_class.select(order_by='-timestamp')[0].comment == 'New field "foobar"'
|
|
|
|
resp.forms[0]['label'] = 'foo' * 30
|
|
resp.forms[0]['type'] = 'string'
|
|
resp = resp.forms[0].submit().follow()
|
|
assert (
|
|
pub.snapshot_class.select(order_by='-timestamp')[0].comment
|
|
== 'New field "foofoofoofoofoofoofoofoofoo(…)"'
|
|
)
|
|
|
|
|
|
def test_form_snapshot_history(pub, formdef_with_history):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
app = login(get_app(pub))
|
|
resp = app.get('/backoffice/forms/%s/' % formdef_with_history.id)
|
|
resp = resp.click('History')
|
|
assert [x.attrib['class'] for x in resp.pyquery.find('ul.snapshots-list li')] == [
|
|
'new-day',
|
|
'collapsed',
|
|
'collapsed',
|
|
'collapsed',
|
|
'collapsed',
|
|
'collapsed',
|
|
]
|
|
|
|
|
|
def test_form_snapshot_export(pub, formdef_with_history):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
app = login(get_app(pub))
|
|
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
|
|
|
|
snapshot = pub.snapshot_class.select_object_history(formdef_with_history)[2]
|
|
resp_export = resp.click(href='%s/export' % snapshot.id)
|
|
assert resp_export.content_type == 'application/x-wcs-snapshot'
|
|
assert '>testform 2<' in resp_export.text
|
|
|
|
|
|
def test_form_snapshot_restore(pub, formdef_with_history):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
app = login(get_app(pub))
|
|
|
|
# restore as new
|
|
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
|
|
snapshot = pub.snapshot_class.select_object_history(formdef_with_history)[2]
|
|
resp = resp.click(href='%s/restore' % snapshot.id)
|
|
assert resp.form['action'].value == 'as-new'
|
|
resp = resp.form.submit('submit')
|
|
assert FormDef.count() == 2
|
|
formdef = FormDef.get(resp.location.split('/')[-2])
|
|
assert formdef.url_name != formdef_with_history.url_name
|
|
assert not hasattr(formdef, 'snapshot_object')
|
|
|
|
# restore over
|
|
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
|
|
snapshot = pub.snapshot_class.select_object_history(formdef_with_history)[2]
|
|
resp = resp.click(href='%s/restore' % snapshot.id)
|
|
resp.form['action'].value = 'overwrite'
|
|
resp = resp.form.submit('submit')
|
|
assert FormDef.count() == 2
|
|
formdef = FormDef.get(resp.location.split('/')[-2])
|
|
assert formdef.id == formdef_with_history.id
|
|
assert formdef.url_name == formdef_with_history.url_name
|
|
|
|
|
|
def test_form_snapshot_restore_with_import_error(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
app = login(get_app(pub))
|
|
|
|
formdef = FormDef()
|
|
formdef.name = 'testform'
|
|
formdef.fields = [ItemField(id='1', label='Test', type='item', data_source={'type': 'unknown'})]
|
|
formdef.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
snapshot = pub.snapshot_class.select_object_history(formdef)[0]
|
|
resp = app.get('/backoffice/forms/%s/history/%s/restore' % (formdef.id, snapshot.id))
|
|
resp = resp.form.submit('submit')
|
|
assert 'Can not restore snapshot (Unknown referenced objects [Unknown datasources: unknown])' in resp
|
|
|
|
|
|
def test_block_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
BlockDef.wipe()
|
|
blockdef = BlockDef()
|
|
blockdef.name = 'testblock'
|
|
blockdef.fields = []
|
|
blockdef.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
# check calling .store() without changes doesn't create snapshots
|
|
blockdef.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
|
|
app = login(get_app(pub))
|
|
|
|
resp = app.get('/backoffice/forms/blocks/%s/history/' % blockdef.id)
|
|
snapshot = pub.snapshot_class.select_object_history(blockdef)[0]
|
|
resp = resp.click(href='%s/view/' % snapshot.id)
|
|
assert 'This block of fields is readonly.' in resp.text
|
|
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
|
|
|
|
|
|
def test_card_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
CardDef.wipe()
|
|
carddef = CardDef()
|
|
carddef.name = 'testcard'
|
|
carddef.fields = []
|
|
carddef.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
# check calling .store() without changes doesn't create snapshots
|
|
carddef.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
|
|
pub.custom_view_class.wipe()
|
|
custom_view = pub.custom_view_class()
|
|
custom_view.title = 'shared form view'
|
|
custom_view.formdef = carddef
|
|
custom_view.columns = {'list': [{'id': 'id'}]}
|
|
custom_view.filters = {}
|
|
custom_view.visibility = 'any'
|
|
custom_view.store()
|
|
|
|
# new version has custom views
|
|
carddef.name = 'test 1'
|
|
carddef.store()
|
|
|
|
# delete custom views
|
|
pub.custom_view_class.wipe()
|
|
|
|
app = login(get_app(pub))
|
|
|
|
resp = app.get('/backoffice/cards/%s/history/' % carddef.id)
|
|
snapshot = pub.snapshot_class.select_object_history(carddef)[0]
|
|
resp = resp.click(href='%s/view/' % snapshot.id)
|
|
assert 'This card model is readonly' in resp.text
|
|
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
|
|
# check option dialogs only have a cancel button
|
|
resp = resp.click('User support')
|
|
assert [x[0].name for x in resp.form.fields.values() if x[0].tag == 'button'] == ['cancel']
|
|
assert pub.custom_view_class.count() == 0 # custom views are not restore on preview
|
|
|
|
|
|
def test_datasource_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
NamedDataSource.wipe()
|
|
datasource = NamedDataSource(name='test')
|
|
datasource.data_source = {'type': 'formula', 'value': repr([('1', 'un'), ('2', 'deux')])}
|
|
datasource.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
# check calling .store() without changes doesn't create snapshots
|
|
datasource.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
|
|
app = login(get_app(pub))
|
|
|
|
resp = app.get('/backoffice/forms/data-sources/%s/history/' % datasource.id)
|
|
snapshot = pub.snapshot_class.select_object_history(datasource)[0]
|
|
resp = resp.click(href='%s/view/' % snapshot.id)
|
|
assert 'This data source is readonly' in resp.text
|
|
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
|
|
with pytest.raises(IndexError):
|
|
resp = resp.click('Edit')
|
|
with pytest.raises(IndexError):
|
|
resp = resp.click('Duplicate')
|
|
|
|
|
|
def test_form_snapshot_browse(pub, formdef_with_history):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
app = login(get_app(pub))
|
|
|
|
pub.custom_view_class.wipe()
|
|
custom_view = pub.custom_view_class()
|
|
custom_view.title = 'shared form view'
|
|
custom_view.formdef = formdef_with_history
|
|
custom_view.columns = {'list': [{'id': 'id'}]}
|
|
custom_view.filters = {}
|
|
custom_view.visibility = 'any'
|
|
custom_view.store()
|
|
|
|
# version 5 has custom views
|
|
formdef_with_history.name = 'testform 5'
|
|
formdef_with_history.description = 'this is a description (5)'
|
|
formdef_with_history.store()
|
|
assert pub.snapshot_class.count() == 7
|
|
# check calling .store() without changes doesn't create snapshots
|
|
formdef_with_history.store()
|
|
assert pub.snapshot_class.count() == 7
|
|
|
|
# delete custom views
|
|
pub.custom_view_class.wipe()
|
|
|
|
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
|
|
snapshot = pub.snapshot_class.select_object_history(formdef_with_history)[0]
|
|
resp = resp.click(href='%s/view/' % snapshot.id)
|
|
assert 'This form is readonly' in resp.text
|
|
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
|
|
|
|
assert '<a class="button disabled" href="#">≪</a>' in resp.text
|
|
assert '<a class="button disabled" href="#"><</a>' in resp.text
|
|
assert '<a class="button" href="../../%s/view/">></a>' % (snapshot.id - 1) in resp.text
|
|
assert '<a class="button" href="../../%s/view/">≫</a>' % (snapshot.id - 6) in resp.text
|
|
|
|
resp = resp.click(href='../../%s/view/' % (snapshot.id - 1))
|
|
assert '<a class="button" href="../../%s/view/">≪</a>' % snapshot.id in resp.text
|
|
assert '<a class="button" href="../../%s/view/"><</a>' % snapshot.id in resp.text
|
|
assert '<a class="button" href="../../%s/view/">></a>' % (snapshot.id - 2) in resp.text
|
|
assert '<a class="button" href="../../%s/view/">≫</a>' % (snapshot.id - 6) in resp.text
|
|
|
|
resp = resp.click(href='../../%s/view/' % (snapshot.id - 6))
|
|
assert '<a class="button" href="../../%s/view/">≪</a>' % snapshot.id in resp.text
|
|
assert '<a class="button" href="../../%s/view/"><</a>' % (snapshot.id - 5) in resp.text
|
|
assert '<a class="button disabled" href="#">></a>' in resp.text
|
|
assert '<a class="button disabled" href="#">≫</a>' in resp.text
|
|
|
|
resp = resp.click(href='../../%s/view/' % snapshot.id)
|
|
resp = resp.click('Description')
|
|
assert resp.form['description'].value == 'this is a description (5)'
|
|
assert [x[0].name for x in resp.form.fields.values() if x[0].tag == 'button'] == ['cancel']
|
|
assert pub.custom_view_class.count() == 0 # custom views are not restore on preview
|
|
|
|
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
|
|
resp = resp.click(href='%s/view/' % (snapshot.id - 1))
|
|
resp = resp.click(href='fields/')
|
|
assert 'sortable readonly' in resp.text
|
|
assert '<a class="button" href="../../../%s/view/fields/">≪</a>' % snapshot.id in resp.text
|
|
assert '<a class="button" href="../../../%s/view/fields/"><</a>' % snapshot.id in resp.text
|
|
assert '<a class="button" href="../../../%s/view/fields/">></a>' % (snapshot.id - 2) in resp.text
|
|
assert '<a class="button" href="../../../%s/view/fields/">≫</a>' % (snapshot.id - 6) in resp.text
|
|
|
|
|
|
def test_form_snapshot_browse_with_import_error(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
app = login(get_app(pub))
|
|
|
|
formdef = FormDef()
|
|
formdef.name = 'testform'
|
|
formdef.fields = [ItemField(id='1', label='Test', type='item', data_source={'type': 'unknown'})]
|
|
formdef.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
snapshot = pub.snapshot_class.select_object_history(formdef)[0]
|
|
# no error for missing datasource
|
|
resp = app.get('/backoffice/forms/%s/history/%s/view/' % (formdef.id, snapshot.id), status=200)
|
|
|
|
# other FormdefImportError
|
|
formdef.fields = [ItemField(id='1', label='Test', type='foobar')]
|
|
formdef.store()
|
|
assert pub.snapshot_class.count() == 2
|
|
snapshot = pub.snapshot_class.select_object_history(formdef)[0]
|
|
resp = app.get('/backoffice/forms/%s/history/%s/view/' % (formdef.id, snapshot.id), status=302)
|
|
assert resp.location == 'http://example.net/backoffice/forms/%s/history/' % formdef.id
|
|
resp = resp.follow()
|
|
assert 'Can not display snapshot (Unknown referenced objects [Unknown field types: foobar])' in resp
|
|
|
|
|
|
def test_workflow_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
Workflow.wipe()
|
|
workflow = Workflow(name='test')
|
|
workflow.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
# check calling .store() without changes doesn't create snapshots
|
|
workflow.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
|
|
app = login(get_app(pub))
|
|
|
|
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
|
|
snapshot = pub.snapshot_class.select_object_history(workflow)[0]
|
|
resp = resp.click(href='%s/view/' % snapshot.id)
|
|
assert 'This workflow is readonly' in resp.text
|
|
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
|
|
|
|
|
|
def test_workflow_snapshot_browse_with_missing_role(pub):
|
|
create_superuser(pub)
|
|
app = login(get_app(pub))
|
|
|
|
pub.role_class.wipe()
|
|
Workflow.wipe()
|
|
wf = Workflow(name='status')
|
|
|
|
ac1 = wf.add_global_action('Action', 'ac1')
|
|
trigger = ac1.triggers[0]
|
|
assert trigger.key == 'manual'
|
|
trigger.roles = ['foobar']
|
|
wf.store()
|
|
|
|
assert pub.role_class.count() == 0
|
|
assert pub.snapshot_class.count() == 1
|
|
snapshot = pub.snapshot_class.select_object_history(wf)[0]
|
|
app.get('/backoffice/workflows/%s/history/%s/view/' % (wf.id, snapshot.id), status=200)
|
|
assert pub.role_class.count() == 1 # missing role was created
|
|
|
|
pub.cfg['sp'] = {'idp-manage-roles': True}
|
|
pub.write_cfg()
|
|
trigger.roles = ['foobarbaz']
|
|
wf.store()
|
|
assert pub.role_class.count() == 1
|
|
assert pub.snapshot_class.count() == 2
|
|
snapshot = pub.snapshot_class.select_object_history(wf)[0]
|
|
app.get('/backoffice/workflows/%s/history/%s/view/' % (wf.id, snapshot.id), status=200)
|
|
assert pub.role_class.count() == 1 # missing role was not created
|
|
|
|
|
|
def test_workflow_snapshot_restore(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
Workflow.wipe()
|
|
workflow = Workflow(name='test')
|
|
workflow.store()
|
|
|
|
app = login(get_app(pub))
|
|
|
|
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
|
|
snapshot = pub.snapshot_class.select_object_history(workflow)[0]
|
|
resp = resp.click(href='%s/view/' % snapshot.id)
|
|
|
|
# restore over
|
|
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
|
|
snapshot = pub.snapshot_class.select_object_history(workflow)[-1]
|
|
resp = resp.click(href='%s/restore' % snapshot.id)
|
|
resp.form['action'].value = 'overwrite'
|
|
resp = resp.form.submit('submit')
|
|
assert Workflow.count() == 1
|
|
assert not hasattr(Workflow.get(workflow.id), 'snapshot_object')
|
|
|
|
|
|
def test_workflow_snapshot_restore_with_import_error(pub):
|
|
create_superuser(pub)
|
|
app = login(get_app(pub))
|
|
|
|
Workflow.wipe()
|
|
wf = Workflow(name='status')
|
|
st1 = wf.add_status('Status1', 'st1')
|
|
|
|
display_form = st1.add_action('form', id='_x')
|
|
display_form.varname = 'xxx'
|
|
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
|
|
display_form.formdef.fields = [
|
|
ItemField(id='1', label='Test', type='item', data_source={'type': 'unknown'})
|
|
]
|
|
wf.store()
|
|
|
|
assert pub.snapshot_class.count() == 1
|
|
snapshot = pub.snapshot_class.select_object_history(wf)[0]
|
|
resp = app.get('/backoffice/workflows/%s/history/%s/restore' % (wf.id, snapshot.id))
|
|
resp = resp.form.submit('submit')
|
|
assert 'Can not restore snapshot (Unknown referenced objects [Unknown datasources: unknown])' in resp
|
|
|
|
|
|
def test_workflow_snapshot_restore_with_missing_role(pub):
|
|
create_superuser(pub)
|
|
app = login(get_app(pub))
|
|
|
|
pub.role_class.wipe()
|
|
Workflow.wipe()
|
|
wf = Workflow(name='status')
|
|
|
|
ac1 = wf.add_global_action('Action', 'ac1')
|
|
trigger = ac1.triggers[0]
|
|
assert trigger.key == 'manual'
|
|
trigger.roles = ['foobar']
|
|
wf.store()
|
|
|
|
assert pub.role_class.count() == 0
|
|
assert pub.snapshot_class.count() == 1
|
|
snapshot = pub.snapshot_class.select_object_history(wf)[0]
|
|
resp = app.get('/backoffice/workflows/%s/history/%s/restore' % (wf.id, snapshot.id), status=200)
|
|
resp.form['action'].value = 'overwrite'
|
|
resp = resp.form.submit('submit')
|
|
assert pub.role_class.count() == 1 # missing role was created
|
|
wf = Workflow.get(resp.location.split('/')[-2])
|
|
assert wf.global_actions[0].triggers[0].roles == ['1']
|
|
assert pub.snapshot_class.count() == 2
|
|
|
|
pub.cfg['sp'] = {'idp-manage-roles': True}
|
|
pub.write_cfg()
|
|
wf.global_actions[0].triggers[0].roles = ['foobarbaz']
|
|
wf.store()
|
|
assert pub.role_class.count() == 1
|
|
assert pub.snapshot_class.count() == 3
|
|
snapshot = pub.snapshot_class.select_object_history(wf)[0]
|
|
resp = app.get('/backoffice/workflows/%s/history/%s/restore' % (wf.id, snapshot.id), status=200)
|
|
resp.form['action'].value = 'overwrite'
|
|
resp = resp.form.submit('submit')
|
|
assert pub.role_class.count() == 1 # missing role was not created
|
|
wf = Workflow.get(resp.location.split('/')[-2])
|
|
assert wf.global_actions[0].triggers[0].roles == ['foobarbaz']
|
|
# no error raised due to unknown role
|
|
app.get(
|
|
'/backoffice/workflows/%s/global-actions/ac1/triggers/%s/'
|
|
% (wf.id, wf.global_actions[0].triggers[0].id)
|
|
)
|
|
|
|
|
|
def test_workflow_with_model_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
Workflow.wipe()
|
|
if os.path.exists(os.path.join(pub.app_dir, 'models')):
|
|
shutil.rmtree(os.path.join(pub.app_dir, 'models'))
|
|
workflow = Workflow(name='test')
|
|
st1 = workflow.add_status('Status1', 'st1')
|
|
export_to = st1.add_action('export_to_model')
|
|
export_to.label = 'test'
|
|
upload = Upload('/foo/bar', content_type='application/vnd.oasis.opendocument.text')
|
|
file_content = b'''PK\x03\x04\x14\x00\x00\x08\x00\x00\'l\x8eG^\xc62\x0c\'\x00'''
|
|
upload.fp = io.BytesIO()
|
|
upload.fp.write(file_content)
|
|
upload.fp.seek(0)
|
|
export_to.model_file = UploadedFile('models', 'tmp', upload)
|
|
|
|
# export/import to get models stored in the expected way
|
|
workflow.store()
|
|
workflow = Workflow.import_from_xml_tree(
|
|
ET.fromstring(ET.tostring(workflow.export_to_xml(include_id=True))), include_id=True
|
|
)
|
|
workflow.store()
|
|
assert len(os.listdir(os.path.join(pub.app_dir, 'models'))) == 2
|
|
|
|
workflow = Workflow.import_from_xml_tree(
|
|
ET.fromstring(ET.tostring(workflow.export_to_xml(include_id=True))), include_id=True
|
|
)
|
|
workflow.store()
|
|
assert len(os.listdir(os.path.join(pub.app_dir, 'models'))) == 2
|
|
|
|
app = login(get_app(pub))
|
|
|
|
for i in range(3):
|
|
# check document model is not overwritten
|
|
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
|
|
snapshot = pub.snapshot_class.select_object_history(workflow)[0]
|
|
resp = resp.click(href='%s/view/' % snapshot.id)
|
|
assert 'This workflow is readonly' in resp.text
|
|
filenames = os.listdir(os.path.join(pub.app_dir, 'models'))
|
|
assert len(filenames) == 3 + i
|
|
assert (
|
|
len([f for f in filenames if f.startswith('export_to_model-snapshot') and f.endswith('.upload')])
|
|
== 1 + i
|
|
)
|
|
|
|
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
|
|
resp = resp.click(href='%s/restore' % snapshot.id)
|
|
assert resp.form['action'].value == 'as-new'
|
|
resp = resp.form.submit('submit')
|
|
assert Workflow.count() == 2
|
|
Workflow.get(2)
|
|
assert list(workflow.get_all_items())[0].key == 'export_to_model'
|
|
assert list(workflow.get_all_items())[0].model_file.filename == 'export_to_model-1-st1-1.upload'
|
|
|
|
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
|
|
resp_export = resp.click(href='%s/export' % snapshot.id)
|
|
assert resp_export.content_type == 'application/x-wcs-snapshot'
|
|
workflow = Workflow.import_from_xml_tree(ET.fromstring(resp_export.text))
|
|
workflow.store()
|
|
assert list(workflow.get_all_items())[0].key == 'export_to_model'
|
|
assert list(workflow.get_all_items())[0].model_file.filename != 'export_to_model-1-st1-1.upload'
|
|
assert 'snapshot' not in list(workflow.get_all_items())[0].model_file.filename
|
|
|
|
|
|
def test_workflow_with_form_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
Workflow.wipe()
|
|
wf = Workflow(name='status')
|
|
st1 = wf.add_status('Status1', 'st1')
|
|
|
|
display_form = st1.add_action('form', id='_x')
|
|
display_form.varname = 'xxx'
|
|
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
|
|
display_form.formdef.fields.append(StringField(id='1', label='Test', type='string'))
|
|
wf.store()
|
|
snapshot = pub.snapshot_class.select_object_history(wf)[0]
|
|
|
|
app = login(get_app(pub))
|
|
resp = app.get(
|
|
'/backoffice/workflows/%s/history/%s/view/status/st1/items/_x/fields/' % (wf.id, snapshot.id)
|
|
)
|
|
assert 'The fields are readonly' in resp.text # ok, no error
|
|
assert 'sortable readonly' in resp.text
|
|
|
|
|
|
def test_wscall_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
NamedWsCall.wipe()
|
|
wscall = NamedWsCall(name='test')
|
|
wscall.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
# check calling .store() without changes doesn't create snapshots
|
|
wscall.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
|
|
app = login(get_app(pub))
|
|
|
|
resp = app.get('/backoffice/settings/wscalls/%s/history/' % wscall.id)
|
|
snapshot = pub.snapshot_class.select_object_history(wscall)[0]
|
|
resp = resp.click(href='%s/view/' % snapshot.id)
|
|
assert 'This webservice call is readonly' in resp.text
|
|
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
|
|
with pytest.raises(IndexError):
|
|
resp = resp.click('Edit')
|
|
|
|
|
|
def test_form_snapshot_save(pub, formdef_with_history):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
app = login(get_app(pub))
|
|
|
|
resp = app.get('/backoffice/forms/%s/' % formdef_with_history.id)
|
|
resp = resp.click('Save snapshot')
|
|
resp.form['label'] = 'test snapshot'
|
|
resp = resp.form.submit('submit')
|
|
|
|
# add more snapshots
|
|
formdef = FormDef.get(id=formdef_with_history.id)
|
|
for i in range(10, 15):
|
|
formdef.description = 'this is a description (%s)' % i
|
|
formdef.store()
|
|
|
|
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
|
|
assert [x.attrib['class'] for x in resp.pyquery.find('ul.snapshots-list li')] == [
|
|
'new-day',
|
|
'collapsed',
|
|
'collapsed',
|
|
'collapsed',
|
|
'collapsed',
|
|
'has-label',
|
|
'collapsed',
|
|
'collapsed',
|
|
'collapsed',
|
|
'collapsed',
|
|
'collapsed',
|
|
'collapsed',
|
|
]
|
|
|
|
|
|
def test_snaphost_workflow_status_item_comments(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
Workflow.wipe()
|
|
workflow = Workflow(name='test')
|
|
workflow.add_status(name='baz')
|
|
workflow.add_status(name='hop')
|
|
global_action = workflow.add_global_action('Action', 'ac1')
|
|
register_comment = global_action.add_action('register-comment')
|
|
workflow.store()
|
|
|
|
app = login(get_app(pub))
|
|
resp = app.get('/backoffice/workflows/1/')
|
|
resp = resp.click('baz')
|
|
|
|
resp.forms[0]['action-interaction'] = 'Webservice'
|
|
resp = resp.forms[0].submit()
|
|
resp = resp.follow()
|
|
|
|
resp = resp.click('Edit', href='items/1/')
|
|
resp.form['url'] = 'http://example.org'
|
|
resp = resp.forms[0].submit('submit')
|
|
resp = resp.follow()
|
|
resp = resp.follow()
|
|
|
|
resp = resp.click('Edit', href='items/1/')
|
|
resp.form['label'] = 'foo'
|
|
resp = resp.forms[0].submit('submit')
|
|
resp = resp.follow()
|
|
resp = resp.follow()
|
|
|
|
resp = resp.click(href='items/1/copy')
|
|
resp.form['status'] = 'hop'
|
|
resp = resp.forms[0].submit('submit')
|
|
resp = resp.follow()
|
|
|
|
resp = resp.click(href='items/1/delete')
|
|
resp = resp.form.submit('submit')
|
|
|
|
resp = app.get('/backoffice/workflows/1/global-actions/ac1/items/%s/' % register_comment.id)
|
|
resp.forms[0]['comment'] = 'xxx'
|
|
resp = resp.form.submit('submit')
|
|
|
|
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
|
|
comments = [
|
|
x.text[18 : x.text.find('\n')]
|
|
for x in resp.html.find('ul', {'class': 'snapshots-list'}).find_all('span', {'class': 'label'})
|
|
]
|
|
assert comments == [
|
|
'Change in action "History Message" in global action "Action"',
|
|
'Deletion of action "Webservice (foo)" in status "baz"',
|
|
'Copy of action "Webservice (foo)" from status "baz" to status "hop"',
|
|
'Change in action "Webservice (foo)" in status "baz"',
|
|
'Change in action "Webservice" in status "baz"',
|
|
'New action "Webservice" in status "baz"',
|
|
'',
|
|
]
|
|
|
|
|
|
def test_snapshot_workflow_variable(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
Workflow.wipe()
|
|
workflow = Workflow(name='test')
|
|
workflow.store()
|
|
workflow.variables_formdef = WorkflowVariablesFieldsFormDef(workflow=workflow)
|
|
workflow.variables_formdef.fields = [
|
|
StringField(id='0', label='foo', type='string', varname='foo'),
|
|
]
|
|
workflow.store()
|
|
|
|
app = login(get_app(pub))
|
|
snapshot = pub.snapshot_class.get_latest('workflow', workflow.id)
|
|
resp = app.get(
|
|
'/backoffice/workflows/%s/history/%s/view/variables/fields/0/' % (workflow.id, snapshot.id)
|
|
)
|
|
assert '>Submit<' not in resp
|
|
resp = app.get('/backoffice/workflows/%s/history/%s/view/variables/fields/' % (workflow.id, snapshot.id))
|
|
assert 'This workflow is readonly' in resp.text
|
|
assert 'sortable readonly' in resp.text
|
|
|
|
|
|
def test_pickle_erroneous_snapshot_object(pub):
|
|
# check snapshot object attribute is not restored
|
|
formdef = FormDef()
|
|
formdef.name = 'basic formdef'
|
|
formdef.snapshot_object = 'whatever'
|
|
formdef.store()
|
|
|
|
assert not hasattr(FormDef.get(formdef.id), 'snapshot_object')
|
|
|
|
|
|
def test_mail_template_snapshot_restore(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
app = login(get_app(pub))
|
|
mail_template = MailTemplate(name='test')
|
|
mail_template.store()
|
|
for i in range(2):
|
|
mail_template.name = 'test %s' % i
|
|
mail_template.store()
|
|
|
|
assert pub.snapshot_class.count() == 3
|
|
|
|
# restore as new
|
|
resp = app.get('/backoffice/workflows/mail-templates/%s/history/' % mail_template.id)
|
|
snapshot = pub.snapshot_class.select_object_history(mail_template)[2]
|
|
resp = resp.click(href='%s/restore' % snapshot.id)
|
|
assert resp.form['action'].value == 'as-new'
|
|
resp = resp.form.submit('submit')
|
|
assert MailTemplate.count() == 2
|
|
mail_template2 = MailTemplate.get(resp.location.split('/')[-2])
|
|
assert mail_template2.name == 'test'
|
|
assert mail_template2.id != mail_template.id
|
|
|
|
# restore over
|
|
resp = app.get('/backoffice/workflows/mail-templates/%s/history/' % mail_template.id)
|
|
snapshot = pub.snapshot_class.select_object_history(mail_template)[2]
|
|
resp = resp.click(href='%s/restore' % snapshot.id)
|
|
resp.form['action'].value = 'overwrite'
|
|
resp = resp.form.submit('submit')
|
|
assert MailTemplate.count() == 2
|
|
mail_template2 = MailTemplate.get(resp.location.split('/')[-2])
|
|
assert mail_template2.id == mail_template.id
|
|
|
|
|
|
def test_mail_template_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
MailTemplate.wipe()
|
|
mail_template = MailTemplate(name='test')
|
|
mail_template.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
# check calling .store() without changes doesn't create snapshots
|
|
mail_template.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
|
|
app = login(get_app(pub))
|
|
|
|
resp = app.get('/backoffice/workflows/mail-templates/%s/history/' % mail_template.id)
|
|
snapshot = pub.snapshot_class.select_object_history(mail_template)[0]
|
|
resp = resp.click(href='%s/view/' % snapshot.id)
|
|
assert 'This mail template is readonly' in resp.text
|
|
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
|
|
with pytest.raises(IndexError):
|
|
resp = resp.click('Edit')
|
|
|
|
|
|
def test_category_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role(pub)
|
|
|
|
Category.wipe()
|
|
category = Category(name='test')
|
|
category.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
# check calling .store() without changes doesn't create snapshots
|
|
category.store()
|
|
assert pub.snapshot_class.count() == 1
|
|
|
|
app = login(get_app(pub))
|
|
|
|
resp = app.get('/backoffice/forms/categories/%s/history/' % category.id)
|
|
snapshot = pub.snapshot_class.select_object_history(category)[0]
|
|
resp = resp.click(href='%s/view/' % snapshot.id)
|
|
assert 'This category is readonly' in resp.text
|
|
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
|
|
with pytest.raises(IndexError):
|
|
resp = resp.click('Edit')
|