428 lines
14 KiB
Python
428 lines
14 KiB
Python
import os
|
|
import shutil
|
|
import xml.etree.ElementTree as ET
|
|
|
|
import pytest
|
|
|
|
from django.utils.six import BytesIO
|
|
from quixote.http_request import Upload
|
|
|
|
from wcs.blocks import BlockDef
|
|
from wcs.carddef import CardDef
|
|
from wcs.data_sources import NamedDataSource
|
|
from wcs.formdef import FormDef
|
|
from wcs.qommon.form import UploadedFile
|
|
from wcs.workflows import Workflow
|
|
from wcs.workflows import ExportToModel
|
|
from wcs.wscalls import NamedWsCall
|
|
|
|
from utilities import get_app, login, create_temporary_pub, clean_temporary_pub
|
|
from test_admin_pages import create_superuser, create_role
|
|
|
|
|
|
@pytest.fixture
|
|
def pub(request, emails):
|
|
pub = create_temporary_pub(sql_mode=True, templates_mode=True, 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 = []
|
|
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'
|
|
formdef.fields = []
|
|
formdef.store()
|
|
|
|
carddef = CardDef()
|
|
carddef.name = 'testcard'
|
|
carddef.fields = []
|
|
carddef.store()
|
|
|
|
formdef.name = 'testform2'
|
|
formdef.store()
|
|
|
|
carddef.name = 'testcard2'
|
|
carddef.store()
|
|
|
|
data_source = NamedDataSource(name='foobar')
|
|
data_source.data_source = {'type': 'formula',
|
|
'value': repr([('1', 'un'), ('2', 'deux')])}
|
|
data_source.store()
|
|
assert pub.snapshot_class.count() == 5
|
|
|
|
# check calling .store() without changes doesn't create snapshots
|
|
# (this cannot be done on formdefs/carddefs as they embed a modification
|
|
# time)
|
|
data_source.store()
|
|
assert pub.snapshot_class.count() == 5
|
|
|
|
# check we got correct data in the serializations
|
|
snapshot = pub.snapshot_class.get_latest('formdef', formdef.id)
|
|
assert '>testform2<' in snapshot.serialization
|
|
|
|
snapshot = pub.snapshot_class.get_latest('carddef', carddef.id)
|
|
assert '>testcard2<' in snapshot.serialization
|
|
|
|
|
|
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
|
|
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()
|
|
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"'
|
|
|
|
|
|
def test_form_snapshot_history(pub, formdef_with_history):
|
|
create_superuser(pub)
|
|
create_role()
|
|
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()
|
|
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()
|
|
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
|
|
|
|
# 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_block_snapshot_browse(pub, blocks_feature):
|
|
create_superuser(pub)
|
|
create_role()
|
|
|
|
BlockDef.wipe()
|
|
blockdef = BlockDef()
|
|
blockdef.name = 'testblock'
|
|
blockdef.fields = []
|
|
blockdef.store()
|
|
|
|
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
|
|
|
|
|
|
def test_card_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role()
|
|
|
|
CardDef.wipe()
|
|
carddef = CardDef()
|
|
carddef.name = 'testcard'
|
|
carddef.fields = []
|
|
carddef.store()
|
|
|
|
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
|
|
resp = resp.click('Geolocation')
|
|
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()
|
|
|
|
NamedDataSource.wipe()
|
|
datasource = NamedDataSource(name='test')
|
|
datasource.data_source = {'type': 'formula',
|
|
'value': repr([('1', 'un'), ('2', 'deux')])}
|
|
datasource.store()
|
|
|
|
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
|
|
with pytest.raises(IndexError):
|
|
resp = resp.click('Edit')
|
|
|
|
|
|
def test_form_snapshot_browse(pub, formdef_with_history):
|
|
create_superuser(pub)
|
|
create_role()
|
|
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()
|
|
|
|
# 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
|
|
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
|
|
|
|
|
|
def test_workflow_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role()
|
|
|
|
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)
|
|
assert 'This workflow is readonly' in resp
|
|
|
|
|
|
def test_workflow_with_model_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role()
|
|
|
|
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 = ExportToModel()
|
|
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 = BytesIO()
|
|
upload.fp.write(file_content)
|
|
upload.fp.seek(0)
|
|
export_to.model_file = UploadedFile('models', 'tmp', upload)
|
|
st1.items.append(export_to)
|
|
export_to.parent = st1
|
|
|
|
# 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)
|
|
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)
|
|
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
|
|
assert len(os.listdir(os.path.join(pub.app_dir, 'models'))) == 3 + i
|
|
|
|
|
|
def test_wscall_snapshot_browse(pub):
|
|
create_superuser(pub)
|
|
create_role()
|
|
|
|
NamedWsCall.wipe()
|
|
wscall = NamedWsCall(name='test')
|
|
wscall.store()
|
|
|
|
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
|
|
with pytest.raises(IndexError):
|
|
resp = resp.click('Edit')
|
|
|
|
|
|
def test_form_snapshot_save(pub, formdef_with_history):
|
|
create_superuser(pub)
|
|
create_role()
|
|
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']
|