wcs/tests/form_pages/test_all.py

6153 lines
213 KiB
Python

import hashlib
import json
import os
import re
import time
import urllib.parse
import xml.etree.ElementTree as ET
import zipfile
from unittest import mock
import pytest
import responses
from django.utils.encoding import force_bytes, force_str
from webtest import Hidden, Upload
from wcs import fields
from wcs.admin.settings import UserFieldsFormDef
from wcs.blocks import BlockDef
from wcs.carddef import CardDef
from wcs.categories import Category
from wcs.data_sources import NamedDataSource
from wcs.formdef import FormDef
from wcs.forms.root import PublicFormStatusPage
from wcs.qommon.emails import docutils
from wcs.qommon.ident.password_accounts import PasswordAccount
from wcs.qommon.template import Template
from wcs.roles import logged_users_role
from wcs.sql import TransientData
from wcs.sql_criterias import Equal
from wcs.wf.create_formdata import JournalAssignationErrorPart, Mapping
from wcs.wf.form import WorkflowFormFieldsFormDef
from wcs.wf.wscall import JournalWsCallErrorPart
from wcs.workflow_traces import WorkflowTrace
from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef, WorkflowVariablesFieldsFormDef
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app, login
def assert_equal_zip(stream1, stream2):
with zipfile.ZipFile(stream1) as z1, zipfile.ZipFile(stream2) as z2:
assert set(z1.namelist()) == set(z2.namelist())
for name in z1.namelist():
if name == 'styles.xml':
continue
if name in ['content.xml', 'meta.xml']:
t1, t2 = ET.tostring(ET.XML(z1.read(name))), ET.tostring(ET.XML(z2.read(name)))
try:
# >= python 3.8: tostring preserves attribute order; use canonicalize to sort them
t1, t2 = ET.canonicalize(t1), ET.canonicalize(t2)
except AttributeError:
pass
else:
t1, t2 = z1.read(name), z2.read(name)
assert t1 == t2, 'file "%s" differs' % name
def pytest_generate_tests(metafunc):
if 'pub' in metafunc.fixturenames:
metafunc.parametrize('pub', ['sql', 'sql-lazy'], indirect=True)
@pytest.fixture
def pub(request):
pub = create_temporary_pub(lazy_mode=bool('lazy' in request.param))
pub.cfg['identification'] = {'methods': ['password']}
pub.cfg['language'] = {'language': 'en'}
pub.cfg['users'] = {
'field_phone': '_phone',
}
pub.write_cfg()
formdef = UserFieldsFormDef(pub)
formdef.fields = [
fields.StringField(id='_phone', label='phone', varname='phone', validation={'type': 'phone'})
]
formdef.store()
Category.wipe()
cat = Category(name='foobar')
cat.store()
return pub
def teardown_module(module):
clean_temporary_pub()
def create_formdef():
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = []
formdef.store()
return formdef
def create_user(pub):
pub.user_class.wipe()
PasswordAccount.wipe()
user = pub.user_class()
user.name = 'User Name'
user.email = 'foo@localhost'
user.form_data = {'_phone': '+33123456789'}
user.store()
account = PasswordAccount(id='foo')
account.set_password('foo')
account.user_id = user.id
account.store()
return user
def create_user_and_admin(pub):
pub.user_class.wipe()
PasswordAccount.wipe()
user = pub.user_class()
user.email = 'foo@localhost'
user.store()
account = PasswordAccount(id='foo')
account.set_password('foo')
account.user_id = user.id
account.store()
admin = pub.user_class()
admin.email = 'admin@localhost'
admin.is_admin = True
admin.store()
account = PasswordAccount(id='admin')
account.set_password('admin')
account.user_id = admin.id
account.store()
return user, admin
def get_displayed_tracking_code(resp):
tracking_code = None
for a_tag in resp.html.findAll('a'):
if 'code/' in a_tag['href']:
tracking_code = a_tag.text
break
return tracking_code
def test_home(pub):
create_formdef()
home = get_app(pub).get('/')
assert 'category-misc' in home.text
assert '<a class="" href="test/">test</a>' in home.text
def test_home_with_user_forms(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.enable_tracking_codes = True
formdef.category_id = '1'
wf = Workflow(name='status')
st1 = wf.add_status('Status1', 'st1')
wf.store()
formdef.workflow_id = wf.id
formdef.store()
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.user_id = user.id
formdata.status = 'wf-st1'
formdata.data = {}
formdata.store()
draft = formdef.data_class()()
draft.user_id = user.id
draft.status = 'draft'
draft.data = {}
draft.store()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/')
assert 'Your Current Forms' not in resp
assert 'Your Past Forms' in resp
assert '<a href="/test/%s/"' % formdata.id in resp
assert 'Draft' in resp
assert '<a href="/test/%s/"' % draft.id in resp
resp = app.get('/test/%s' % formdata.id)
assert resp.location == 'http://example.net/test/1/'
resp = app.get('/test/%s/' % draft.id, status=302)
assert resp.location.startswith('http://example.net/foobar/test/%s/' % draft.id)
resp = resp.follow(status=302)
assert resp.location.startswith('http://example.net/foobar/test/?mt=')
resp = resp.follow(status=200)
# add action -> pending
st1.add_action('choice')
wf.store()
resp = app.get('/')
assert 'Your Current Forms' in resp
assert 'Your Past Forms' not in resp
assert '<a href="/test/%s/"' % formdata.id in resp
# disable formdef: formdatas are still visible and accessible, drafts are not
formdef.disabled = True
formdef.store()
resp = app.get('/')
assert 'Your Current Forms' in resp
assert 'Your Past Forms' not in resp
assert '<a href="/test/%s/"' % formdata.id in resp
assert 'Draft' not in resp
assert '<a href="test/%s"' % draft.id not in resp
resp = app.get('/test/%s/' % draft.id, status=302)
assert resp.location.startswith('http://example.net/foobar/test/%s/' % draft.id)
resp = resp.follow(status=302)
assert resp.location.startswith('http://example.net/foobar/test/?mt=')
resp = resp.follow(status=403)
def test_home_category(pub):
formdef = create_formdef()
formdef.category_id = '1'
formdef.store()
home = get_app(pub).get('/')
assert 'category-foobar' in home.text
assert 'category-misc' not in home.text
assert '<a class="" href="foobar/test/">test</a>' in home.text
def test_home_two_categories(pub):
formdef = create_formdef()
formdef.category_id = '1'
formdef.store()
formdef2 = FormDef()
formdef2.name = 'foobar'
formdef2.fields = []
formdef2.store()
resp = get_app(pub).get('/')
assert 'category-foobar' in resp.text # 1st formdef
assert 'category-misc' in resp.text # 2nd formdef, fake category
assert '<a class="" href="foobar/test/">test</a>' in resp.text
cat2 = Category(name='barfoo')
cat2.store()
formdef2.category_id = cat2.id
formdef2.store()
resp = get_app(pub).get('/')
assert 'category-foobar' in resp.text # 1st formdef
assert 'category-barfoo' in resp.text # 2nd formdef
assert 'category-misc' not in resp.text # no more "misc" category
def test_home_keywords(pub):
formdef = create_formdef()
formdef.category_id = '1'
formdef.keywords = 'hello, world'
formdef.store()
home = get_app(pub).get('/')
assert home.html.find('div', {'data-keywords': 'hello world'}) or home.html.find(
'div', {'data-keywords': 'world hello'}
)
assert home.html.find('li', {'data-keywords': 'hello world'}) or home.html.find(
'li', {'data-keywords': 'world hello'}
)
def test_home_formdef_description(pub):
formdef = create_formdef()
formdef.description = 'HELLO WORLD'
formdef.store()
home = get_app(pub).get('/')
assert 'HELLO WORLD' in home.text
assert '<a class="" href="test/">test</a>' in home.text
def test_home_disabled(pub):
formdef = create_formdef()
formdef.disabled = True
formdef.store()
home = get_app(pub).get('/')
assert '<a href="test/">test</a>' not in home.text
# check access is denied
get_app(pub).get('/test/', status=403)
def test_home_disabled_with_redirect(pub):
formdef = create_formdef()
formdef.disabled = True
formdef.disabled_redirection = 'http://example.org'
formdef.store()
resp = get_app(pub).get('/')
assert '<a class="redirection" href="test/">test</a>' in resp.text
resp = resp.click('test')
assert resp.location == 'http://example.org'
def test_home_inaccessible(pub):
formdef = create_formdef()
formdef.roles = ['xxx']
formdef.store()
home = get_app(pub).get('/')
assert home.status_int == 302
assert home.location == 'http://example.net/login/?next=http%3A%2F%2Fexample.net%2F'
def test_home_always_advertise(pub):
formdef = create_formdef()
formdef.roles = ['xxx']
formdef.always_advertise = True
formdef.store()
home = get_app(pub).get('/')
assert '<a href="test/">test</a>' in home.text
assert '<a href="test/">test</a><span> (authentication required)</span>' in home.text
def test_home_redirect(pub):
pub.cfg['misc']['homepage-redirect-url'] = 'http://www.example.com/'
pub.write_cfg()
create_formdef()
home = get_app(pub).get('/')
assert home.status_int == 302
assert home.location == 'http://www.example.com/'
def test_home_redirect_var(pub):
pub.cfg['misc']['homepage-redirect-url'] = 'http://www.example.com/[site_lang]/'
pub.write_cfg()
create_formdef()
home = get_app(pub).get('/')
assert home.status_int == 302
assert home.location == 'http://www.example.com/en/'
def test_category_page(pub):
formdef = create_formdef()
formdef.category_id = '1'
formdef.store()
resp = get_app(pub).get('/foobar/', status=302)
assert resp.location == 'http://example.net/'
def test_category_page_redirect(pub):
formdef = create_formdef()
formdef.category_id = '1'
formdef.store()
cat = Category.get(1)
cat.redirect_url = 'http://www.example.com/'
cat.store()
resp = get_app(pub).get('/foobar/')
assert resp.status_int == 302
assert resp.location == 'http://www.example.com/'
def test_category_page_redirect_var(pub):
formdef = create_formdef()
formdef.category_id = '1'
formdef.store()
cat = Category.get(1)
cat.redirect_url = 'http://www.example.com/[site_lang]/[category_slug]/'
cat.store()
resp = get_app(pub).get('/foobar/')
assert resp.status_int == 302
assert resp.location == 'http://www.example.com/en/foobar/'
def test_form_access(pub):
formdef = create_formdef()
get_app(pub).get('/test/', status=200)
pub.role_class.wipe()
role = pub.role_class(name='xxx')
role.store()
# check a formdef protected by a role cannot be accessed
formdef.roles = [role.id]
formdef.store()
# an unlogged user will ge ta redirect to login
resp = get_app(pub).get('/test/', status=302)
assert '/login' in resp.location
# while a logged-in user will get a 403
user = create_user(pub)
login(get_app(pub), username='foo', password='foo').get('/test/', status=403)
# unless the user has the right role
user = create_user(pub)
user.roles = [role.id]
user.store()
login(get_app(pub), username='foo', password='foo').get('/test/', status=200)
# check admin has access, even without specific roles
user = create_user(pub)
user.roles = []
user.is_admin = True
user.store()
login(get_app(pub), username='foo', password='foo').get('/test/', status=200)
# check special "logged users" role
formdef.roles = [logged_users_role().id]
formdef.store()
user = create_user(pub)
login(get_app(pub), username='foo', password='foo').get('/test/', status=200)
resp = get_app(pub).get('/test/', status=302) # redirect to login
# check "receiver" can also access the formdef
formdef = create_formdef()
formdef.roles = [-2]
formdef.workflow_roles = {'_receiver': role.id}
formdef.store()
user = create_user(pub)
user.roles = [role.id]
user.store()
login(get_app(pub), username='foo', password='foo').get('/test/', status=200)
def test_form_access_auth_context(pub):
create_user(pub)
pub.load_site_options()
if not pub.site_options.has_section('options'):
pub.site_options.add_section('options')
pub.site_options.set('options', 'auth-contexts', 'fedict')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
formdef = create_formdef()
get_app(pub).get('/test/', status=200)
formdef.required_authentication_contexts = ['fedict']
formdef.roles = [logged_users_role().id]
formdef.store()
# an unlogged user will get a redirect to login
resp = get_app(pub).get('/test/', status=302)
assert '/login' in resp.location
# a user logged in with a simple username/password tuple will get a page
# to relogin with a stronger auth
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/test/')
assert 'You need a stronger authentication level to fill this form.' in resp.text
for session in pub.session_manager.values():
session.saml_authn_context = 'urn:oasis:names:tc:SAML:2.0:ac:classes:SmartcardPKI'
session.store()
resp = app.get('/test/')
assert 'You need a stronger authentication level to fill this form.' not in resp.text
assert resp.form
def test_form_cancelurl(pub):
formdef = create_formdef()
formdef.data_class().wipe()
# path
resp = get_app(pub).get('/test/?cancelurl=/plop/')
resp = resp.form.submit('cancel')
assert resp.location == 'http://example.net/plop/'
# full URL
resp = get_app(pub).get('/test/?cancelurl=http://example.net/plop/')
resp = resp.form.submit('cancel')
assert resp.location == 'http://example.net/plop/'
# remote site
get_app(pub).get('/test/?cancelurl=http://example.org/plop/', status=400)
pub.site_options.add_section('api-secrets')
pub.site_options.set('api-secrets', 'example.org', 'xyz')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = get_app(pub).get('/test/?cancelurl=http://example.org/plop/')
resp = resp.form.submit('cancel')
assert resp.location == 'http://example.org/plop/'
pub.site_options.remove_section('api-secrets')
if not pub.site_options.has_section('options'):
pub.site_options.add_section('options')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
get_app(pub).get('/test/?cancelurl=http://example.org/plop/', status=400)
pub.site_options.set('options', 'relatable-hosts', 'example.com')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
get_app(pub).get('/test/?cancelurl=http://example.org/plop/', status=400)
pub.site_options.set('options', 'relatable-hosts', 'example.com, example.org')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = get_app(pub).get('/test/?cancelurl=http://example.org/plop/')
resp = resp.form.submit('cancel')
assert resp.location == 'http://example.org/plop/'
def test_form_submit(pub):
formdef = create_formdef()
formdef.data_class().wipe()
page = get_app(pub).get('/test/')
next_page = page.forms[0].submit('submit')
assert 'Check values then click submit.' in next_page.text
next_page = next_page.forms[0].submit('submit')
assert next_page.status_int == 302
next_page = next_page.follow()
assert 'The form has been recorded' in next_page.text
assert 'None' not in next_page.text
assert formdef.data_class().count() == 1
assert '<div class="section foldable folded" id="summary">' in next_page.text
assert next_page.pyquery('#summary .disclose-message')
assert formdef.data_class().select()[0].submission_context['language'] == 'en'
def test_form_submit_no_confirmation(pub):
formdef = create_formdef()
formdef.confirmation = False
formdef.store()
page = get_app(pub).get('/test/')
formdef.data_class().wipe()
next_page = page.forms[0].submit('submit')
assert next_page.status_int == 302
next_page = next_page.follow()
assert 'The form has been recorded' in next_page.text
assert formdef.data_class().count() == 1
def test_form_string_field_submit(pub):
formdef = create_formdef()
formdef.fields = [fields.StringField(id='0', label='string')]
formdef.store()
page = get_app(pub).get('/test/')
formdef.data_class().wipe()
next_page = page.forms[0].submit('submit') # but the field is required
assert next_page.pyquery('#form_error_f0').text() == 'required field'
next_page.forms[0]['f0'] = 'foobar'
next_page = next_page.forms[0].submit('submit')
assert 'Check values then click submit.' in next_page.text
next_page = next_page.forms[0].submit('submit')
assert next_page.status_int == 302
next_page = next_page.follow()
assert 'The form has been recorded' in next_page.text
assert formdef.data_class().count() == 1
data_id = formdef.data_class().select()[0].id
data = formdef.data_class().get(data_id)
assert data.data == {'0': 'foobar'}
def test_form_string_with_invalid_xml_chars(pub):
formdef = create_formdef()
formdef.fields = [fields.StringField(id='0', label='string')]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.form['f0'] = 'hello\x0b\x0cworld'
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
assert formdef.data_class().count() == 1
data = formdef.data_class().select()[0]
assert data.data == {'0': 'helloworld'}
def test_form_submit_handling_role_info(pub):
role = pub.role_class(name='xxx')
role.details = 'Managing service'
role.store()
formdef = create_formdef()
formdef.workflow_roles = {'_receiver': role.id}
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp
resp = resp.form.submit('submit').follow()
assert 'The form has been recorded' in resp
assert 'Your case is handled by' in resp
assert 'Managing service' in resp
formdata = formdef.data_class().select()[0]
formdata.jump_status('rejected')
formdata.store()
resp = resp.test_app.get(resp.request.url)
assert 'Your case has been handled by' in resp
def assert_current_page(resp, page_label):
for li_tag in resp.html.findAll('li'):
if 'current' in li_tag.attrs['class']:
assert li_tag.find_all('span')[-1].text == page_label
def test_form_multi_page(pub):
for initial_condition in (None, 'True'):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page'),
fields.StringField(id='3', label='string 2'),
]
formdef.fields[0].condition = {'type': 'python', 'value': initial_condition}
formdef.store()
page = get_app(pub).get('/test/')
formdef.data_class().wipe()
page.forms[0]['f1'] = 'foo'
assert page.forms[0].fields['submit'][0].value_if_submitted() == 'Next'
next_page = page.forms[0].submit('submit')
assert_current_page(next_page, '2nd page')
assert next_page.forms[0]['previous']
next_page.forms[0]['f3'] = 'bar'
next_page = next_page.forms[0].submit('submit')
assert_current_page(next_page, 'Validating')
assert 'Check values then click submit.' in next_page.text
next_page = next_page.forms[0].submit('submit')
assert next_page.status_int == 302
next_page = next_page.follow()
assert 'The form has been recorded' in next_page.text
assert formdef.data_class().count() == 1
data_id = formdef.data_class().select()[0].id
data = formdef.data_class().get(data_id)
assert data.data == {'1': 'foo', '3': 'bar'}
def test_form_multi_page_title_and_subtitle_as_template(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string', varname='foo'),
fields.PageField(id='2', label='2nd page'),
fields.TitleField(id='4', label='<i>title of second page {{ form_var_foo }}</i>'),
fields.SubtitleField(id='5', label='<i>subtitle of second page {{ form_var_foo }}</i>'),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp.form['f1'] = '35 < 42'
resp = resp.form.submit('submit')
resp.form['f3'] = 'bar'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.form.submit('submit').follow()
assert 'The form has been recorded' in resp.text
expected_label = '&lt;i&gt;title of second page 35 &lt; 42&lt;/i&gt;'
assert '<div class="title "><h3>%s</h3></div>' % expected_label in resp.text
expected_label = '&lt;i&gt;subtitle of second page 35 &lt; 42&lt;/i&gt;'
assert '<div class="subtitle "><h4>%s</h4></div>' % expected_label in resp.text
def test_form_multi_page_condition(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page', condition={'type': 'python', 'value': 'False'}),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp.forms[0]['f1'] = 'foo'
resp = resp.forms[0].submit('submit') # should go straight to validation
assert 'Check values then click submit.' in resp.text
assert resp.forms[0]['previous']
resp = resp.forms[0].submit('previous')
assert resp.forms[0]['f1']
def test_form_multi_page_condition_select(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.ItemField(id='1', label='select', required=True, varname='foo', items=['Foo', 'Bar']),
fields.PageField(id='2', label='2nd page', condition={'type': 'python', 'value': 'var_foo == "Foo"'}),
fields.PageField(id='3', label='3rd page', condition={'type': 'python', 'value': 'var_foo == "Bar"'}),
fields.StringField(id='4', label='string 2'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert '2nd page' not in resp.text
assert '3rd page' not in resp.text
assert resp.forms[0]['f1'].value == 'Foo' # preset
resp = resp.forms[0].submit('submit')
assert '2nd page' in resp.text
assert '3rd page' not in resp.text
assert_current_page(resp, '2nd page')
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'Bar'
resp = resp.forms[0].submit('submit')
assert '2nd page' not in resp.text
assert '3rd page' in resp.text
assert_current_page(resp, '3rd page')
def test_form_multi_page_condition_select_new_varname(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.ItemField(id='1', label='select', required=True, varname='foo', items=['Foo', 'Bar']),
fields.PageField(
id='2',
label='2nd page',
condition={'type': 'python', 'value': 'form_var_foo == "Foo"'},
),
fields.PageField(
id='3',
label='3rd page',
condition={'type': 'python', 'value': 'form_var_foo == "Bar"'},
),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert '2nd page' not in resp.text
assert '3rd page' not in resp.text
resp.forms[0]['f1'] = 'Foo'
resp = resp.forms[0].submit('submit')
assert '2nd page' in resp.text
assert '3rd page' not in resp.text
assert_current_page(resp, '2nd page')
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'Bar'
resp = resp.forms[0].submit('submit')
assert '2nd page' not in resp.text
assert '3rd page' in resp.text
assert_current_page(resp, '3rd page')
def test_form_multi_page_condition_checkbox(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.BoolField(id='1', label='checkbox', varname='checkbox'),
fields.PageField(
id='2',
condition={'type': 'python', 'value': 'var_checkbox == "False"'},
),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp.forms[0]['f1'].checked = True
resp = resp.forms[0].submit('submit') # should go straight to validation
assert 'Check values then click submit.' in resp.text
assert resp.forms[0]['previous']
resp = resp.forms[0].submit('previous')
assert resp.forms[0]['f1']
resp.forms[0]['f1'].checked = False
resp = resp.forms[0].submit('submit') # should go to second page
assert 'f3' in resp.forms[0].fields
def test_form_multi_page_condition_json_check(pub):
# make sure the json export has no value for fields from hidden pages
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.BoolField(id='1', label='checkbox', varname='checkbox'),
fields.PageField(
id='2',
label='2nd page',
condition={'type': 'python', 'value': 'var_checkbox == "False"'},
),
fields.StringField(id='3', label='string 2', varname='st2'),
fields.PageField(
id='4',
label='3rd page',
condition={'type': 'python', 'value': 'var_checkbox == "True"'},
),
fields.StringField(id='5', label='string 3', varname='st3'),
]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp.form['f1'].checked = True
resp = resp.form.submit('submit') # should go straight to 3rd page
assert 'f5' in resp.form.fields
resp.form['f5'] = 'VALUE F5'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert resp.form['previous']
resp = resp.form.submit('previous')
resp = resp.form.submit('previous')
# back to first page
assert 'f1' in resp.form.fields
resp.form['f1'].checked = False
resp = resp.form.submit('submit') # should go to second page
assert 'f3' in resp.form.fields
resp.form['f3'] = 'VALUE F3'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.form.submit()
assert len(formdef.data_class().select()) == 1
json_dict = formdef.data_class().select()[0].get_json_export_dict()
assert json_dict['fields']['st2'] == 'VALUE F3'
assert json_dict['fields']['st3'] is None
def test_form_multi_page_condition_no_confirmation_json_check(pub):
# same as above but without the confirmation page.
formdef = create_formdef()
formdef.confirmation = False
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.BoolField(id='1', label='checkbox', varname='checkbox'),
fields.PageField(
id='2',
label='2nd page',
condition={'type': 'python', 'value': 'var_checkbox == "False"'},
),
fields.StringField(id='3', label='string 2', varname='st2'),
fields.PageField(
id='4',
label='3rd page',
condition={'type': 'python', 'value': 'var_checkbox == "True"'},
),
fields.StringField(id='5', label='string 3', varname='st3'),
fields.PageField(id='6', label='4th page'),
fields.CommentField(id='7', label='Check values then click submit.'),
]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp.form['f1'].checked = True
resp = resp.form.submit('submit') # should go straight to 3rd page
assert 'f5' in resp.form.fields
resp.form['f5'] = 'VALUE F5'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert resp.form['previous']
resp = resp.form.submit('previous')
resp = resp.form.submit('previous')
# back to first page
assert 'f1' in resp.form.fields
resp.form['f1'].checked = False
resp = resp.form.submit('submit') # should go to second page
assert 'f3' in resp.form.fields
resp.form['f3'] = 'VALUE F3'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.form.submit('submit')
assert len(formdef.data_class().select()) == 1
json_dict = formdef.data_class().select()[0].get_json_export_dict()
assert json_dict['fields']['st2'] == 'VALUE F3'
assert json_dict['fields']['st3'] is None
def test_form_multi_page_condition_data_source(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.BoolField(id='1', label='checkbox', varname='checkbox'),
fields.PageField(
id='2',
label='2nd page',
condition={'type': 'python', 'value': 'len(data_source.foobar) > 0'},
),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
# add the named data source, empty
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {'type': 'jsonvalue', 'value': '[]'}
data_source.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp = resp.forms[0].submit('submit') # should go straight to validation
assert 'Check values then click submit.' in resp.text
assert resp.forms[0]['previous']
resp = resp.forms[0].submit('previous')
assert resp.forms[0]['f1']
# replace the named data source with one with items
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {
'type': 'jsonvalue',
'value': '[{"id": "un", "text": "un"}, {"id": "deux", "text": "deux"}]',
}
data_source.store()
resp = resp.forms[0].submit('submit') # should go to second page
assert 'f3' in resp.forms[0].fields
def test_form_multi_page_condition_data_source_with_form_variable(pub):
# this tries to recreate #8272 which is about a json datasource being
# used in a page condition and taking a value from the given page to
# filter its content. It is emulated here with a Python datasource
# being empty if a field was not set.
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string', varname='xxx', required=False),
fields.PageField(
id='2',
label='2nd page',
condition={'type': 'python', 'value': 'len(data_source.foobar) > 0'},
),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
# add the named data source, related to a field on the first page
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {'type': 'formula', 'value': 'form_var_xxx and [form_var_xxx] or []'}
data_source.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp = resp.forms[0].submit('submit') # should go straight to validation
assert 'Check values then click submit.' in resp.text
assert resp.forms[0]['previous']
resp = resp.forms[0].submit('previous')
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'HELLO'
resp = resp.forms[0].submit('submit') # should go to second page
assert 'f3' in resp.forms[0].fields
def test_form_multi_page_condition_on_first_page(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page', condition={'type': 'python', 'value': 'False'}),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page'),
fields.StringField(id='3', label='string 2'),
fields.PageField(id='4', label='3rd page'),
]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
# should be on second page already
assert resp.pyquery('.buttons button.form-previous[hidden][disabled]')
resp.form['f3'] = 'foo'
assert_current_page(resp, '2nd page')
resp = resp.form.submit('submit') # -> 3rd page
assert_current_page(resp, '3rd page')
resp = resp.form.submit('submit') # -> validation page
assert 'Check values then click submit.' in resp.text
assert resp.form['previous']
resp = resp.form.submit('previous') # -> 3rd page
assert_current_page(resp, '3rd page')
resp = resp.form.submit('previous') # -> 2nd page
assert_current_page(resp, '2nd page')
assert resp.form['f3']
assert resp.pyquery('.buttons button.form-previous[hidden][disabled]')
def test_form_multi_page_condition_on_first_and_next(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page', condition={'type': 'python', 'value': 'True'}),
fields.StringField(id='1', label='string', varname='val1'),
fields.PageField(
id='2',
label='2nd page',
condition={'type': 'python', 'value': 'vars().get("form_var_val1") == "foo"'},
),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'foo'
resp = resp.form.submit('submit')
assert resp.form['f3']
resp.form['f3'] = 'bar'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.form.submit('submit')
assert len(formdef.data_class().select()) == 1
data_id = formdef.data_class().select()[0].id
data = formdef.data_class().get(data_id)
assert data.data == {'1': 'foo', '3': 'bar'}
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'xxx'
resp = resp.form.submit('submit')
with pytest.raises(AssertionError):
assert resp.form['f3']
assert 'Check values then click submit.' in resp.text
resp = resp.form.submit('submit')
assert len(formdef.data_class().select()) == 1
data_id = formdef.data_class().select()[0].id
data = formdef.data_class().get(data_id)
assert data.data.get('1') == 'xxx'
assert data.data.get('3') is None
def test_form_multi_page_condition_no_visible_page(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page', condition={'type': 'python', 'value': 'False'}),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page', condition={'type': 'python', 'value': 'False'}),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
resp = get_app(pub).get('/test/')
assert 'error-page' in resp
assert 'This form has no visible page.' in resp
def test_form_multi_page_many_conditions(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.PageField(id='0', label='2nd page', condition={'type': 'python', 'value': 'True'}),
]
formdef.store()
formdef.data_class().wipe()
with mock.patch('wcs.qommon.publisher.Substitutions.invalidate_cache') as invalidate_cache:
get_app(pub).get('/test/')
call_count = invalidate_cache.call_count
for i in range(30):
formdef.fields.append(
fields.PageField(
id=str(i + 2),
label='page %s' % (i + 2),
condition={'type': 'python', 'value': 'True'},
)
)
formdef.store()
# check the cache doesn't get invalidated for every page
with mock.patch('wcs.qommon.publisher.Substitutions.invalidate_cache') as invalidate_cache:
get_app(pub).get('/test/')
assert invalidate_cache.call_count <= call_count
def test_form_multi_page_condition_stored_values(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string', varname='foo'),
fields.PageField(
id='2',
label='2nd page',
condition={'type': 'python', 'value': 'form_var_foo == "toto"'},
),
fields.StringField(id='3', label='string 2'),
fields.PageField(id='4', label='3rd page'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.form['f1'] = 'toto'
resp = resp.form.submit('submit') # -> page 2
resp.form['f3'] = 'bar'
resp = resp.form.submit('submit') # -> page 3
resp = resp.form.submit('submit') # -> validation page
assert 'Check values then click submit.' in resp.text
assert 'bar' in resp.text
resp = resp.form.submit('previous') # -> page 3
resp = resp.form.submit('previous') # -> page 2
resp = resp.form.submit('previous') # -> page 1
resp.form['f1'] = 'blah'
resp = resp.form.submit('submit') # -> page 3
resp = resp.form.submit('submit') # -> validation page
assert 'Check values then click submit.' in resp.text
assert 'bar' not in resp.text
resp = resp.form.submit('submit')
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data['1'] == 'blah'
assert formdata.data.get('3') is None
# same without validation page
formdef.confirmation = False
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.form['f1'] = 'toto'
resp = resp.form.submit('submit') # -> page 2
resp.form['f3'] = 'bar'
resp = resp.form.submit('submit') # -> page 3
resp = resp.form.submit('previous') # -> page 2
resp = resp.form.submit('previous') # -> page 1
resp.form['f1'] = 'blah'
resp = resp.form.submit('submit') # -> page 3
resp = resp.form.submit('submit')
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data['1'] == 'blah'
assert formdata.data.get('3') is None
def test_form_multi_page_post_conditions(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string', varname='foo'),
fields.PageField(id='2', label='2nd page', condition={'type': 'python', 'value': 'False'}),
fields.StringField(id='3', label='string 2'),
fields.PageField(id='4', label='3rd page'),
fields.StringField(id='5', label='string 3', varname='bar'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'foo'
resp = resp.forms[0].submit('submit')
resp.forms[0]['f5'] = 'bar'
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.forms[0].submit('submit')
assert formdef.data_class().count() == 1
assert formdef.data_class().select()[0].data['1'] == 'foo'
assert formdef.data_class().select()[0].data['5'] == 'bar'
formdef.fields[4].post_conditions = [
{'condition': {'type': 'python', 'value': 'False'}, 'error_message': 'You shall not pass.'},
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'foo'
resp = resp.forms[0].submit('submit')
resp.forms[0]['f5'] = 'bar'
resp = resp.forms[0].submit('submit')
assert 'errornotice' in resp.text
assert 'global-errors' in resp.text
assert 'You shall not pass.' in resp.text
formdef.fields[4].post_conditions = [
{
'condition': {'type': 'python', 'value': 'form_var_foo == "foo"'},
'error_message': 'You shall not pass.',
},
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'bar'
resp = resp.forms[0].submit('submit')
resp.forms[0]['f5'] = 'bar'
resp = resp.forms[0].submit('submit')
assert 'errornotice' in resp.text
assert 'You shall not pass.' in resp.text
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'foo'
resp = resp.forms[0].submit('submit')
resp.forms[0]['f5'] = 'bar'
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.text
# check a post-condition raising an exception, they should always fail.
formdef.fields[4].post_conditions = [
{'condition': {'type': 'python', 'value': '1/0'}, 'error_message': 'You shall not pass.'},
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'bar'
resp = resp.forms[0].submit('submit')
resp.forms[0]['f5'] = 'bar'
resp = resp.forms[0].submit('submit')
assert 'errornotice' in resp.text
assert 'You shall not pass.' in resp.text
# check a post-condition referring to a field on the same page
formdef.fields[4].post_conditions = [
{
'condition': {'type': 'python', 'value': 'form_var_bar == "bar"'},
'error_message': 'You shall not pass.',
},
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'bar'
resp = resp.forms[0].submit('submit')
resp.forms[0]['f5'] = 'foo'
resp = resp.forms[0].submit('submit')
assert 'errornotice' in resp.text
assert 'You shall not pass.' in resp.text
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'bar'
resp = resp.forms[0].submit('submit')
resp.forms[0]['f5'] = 'bar'
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.text
def test_form_multi_page_conditions_and_post_conditions(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{
'condition': {'type': 'python', 'value': 'form_var_bar == "bar"'},
'error_message': 'You shall not pass.',
}
],
),
fields.StringField(id='1', label='string', varname='bar'),
fields.PageField(id='3', label='2nd page'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.form['f1'] = 'bar'
resp = resp.form.submit('submit')
assert_current_page(resp, '2nd page')
resp = get_app(pub).get('/test/')
resp.form['f1'] = 'foo'
resp = resp.form.submit('submit')
assert 'You shall not pass.' in resp.text
# add a conditional page, this will cause pages to be evaluated first
# (and would trigger #25197)
formdef.fields.append(
fields.PageField(id='4', label='3rd page', condition={'type': 'python', 'value': 'True'})
)
formdef.store()
resp = get_app(pub).get('/test/')
resp.form['f1'] = 'bar'
resp = resp.form.submit('submit')
assert_current_page(resp, '2nd page')
resp = get_app(pub).get('/test/')
resp.form['f1'] = 'foo'
resp = resp.form.submit('submit')
assert 'You shall not pass.' in resp.text
def test_form_multi_page_page_name_as_title(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.TitleField(id='4', label='1st page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page'),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
page = get_app(pub).get('/test/')
formdef.data_class().wipe()
page.forms[0]['f1'] = 'foo'
next_page = page.forms[0].submit('submit')
assert_current_page(next_page, '2nd page')
assert next_page.forms[0]['previous']
next_page.forms[0]['f3'] = 'bar'
next_page = next_page.forms[0].submit('submit')
assert_current_page(next_page, 'Validating')
assert 'Check values then click submit.' in next_page.text
assert next_page.text.count('1st page') == 2 # in steps and in main body
# add a comment that will not be displayed and should therefore not be
# considered.
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.CommentField(id='5', label='bla bla bla'),
fields.TitleField(id='4', label='1st page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page'),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
page = get_app(pub).get('/test/')
formdef.data_class().wipe()
page.forms[0]['f1'] = 'foo'
next_page = page.forms[0].submit('submit')
assert_current_page(next_page, '2nd page')
assert next_page.forms[0]['previous']
next_page.forms[0]['f3'] = 'bar'
next_page = next_page.forms[0].submit('submit')
assert_current_page(next_page, 'Validating')
assert 'Check values then click submit.' in next_page.text
assert next_page.text.count('1st page') == 2 # in steps and in main body
def test_form_submit_with_user(pub, emails):
create_user(pub)
formdef = create_formdef()
page = login(get_app(pub), username='foo', password='foo').get('/test/')
formdef.data_class().wipe()
next_page = page.forms[0].submit('submit')
assert 'Check values then click submit.' in next_page.text
next_page = next_page.forms[0].submit('submit')
assert next_page.status_int == 302
next_page = next_page.follow()
assert 'The form has been recorded' in next_page.text
assert formdef.data_class().count() == 1
assert '<div class="section foldable folded" id="summary">' in next_page.text
# check the user received a copy by email
assert emails.get('New form (test)')
assert emails.get('New form (test)')['email_rcpt'] == ['foo@localhost']
def test_form_submit_with_just_disabled_user(pub, emails):
user = create_user(pub)
formdef = create_formdef()
app = login(get_app(pub), username='foo', password='foo')
formdef.data_class().wipe()
resp = app.get('/test/')
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp
user.is_active = False
user.store()
resp = resp.form.submit('submit')
resp = resp.follow()
assert 'Sorry, your session have been lost.' in resp
def test_form_titles(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.TitleField(id='4', label='1st page'),
fields.SubtitleField(id='5', label='subtitle of 1st page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page'),
fields.TitleField(id='6', label='title of second page'),
fields.StringField(id='3', label='string 2', required=False),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert '<h3 data-field-id="0">1st page/h3>' not in resp.text
assert '<h4 data-field-id="5">subtitle of 1st page</h4>' in resp.text
resp.form['f1'] = 'foo'
resp = resp.form.submit('submit')
assert '<h3 data-field-id="6">title of second page</h3>' in resp.text
resp.form['f3'] = 'foo'
resp = resp.form.submit('submit') # -> validation page
assert '<h3>1st page</h3>' in resp.text
assert '<h4 data-field-id="5">subtitle of 1st page</h4>' in resp.text
assert '<h3 data-field-id="6">title of second page</h3>' in resp.text
resp = resp.form.submit('submit').follow() # -> submit
assert '<h3>1st page</h3>' in resp.text
assert '<div class="title "><h3>1st page</h3></div>' not in resp.text
assert '<div class="subtitle "><h4>subtitle of 1st page</h4></div>' in resp.text
assert '<div class="title "><h3>title of second page</h3></div>' in resp.text
def test_form_summary_empty_pages(pub):
create_user(pub)
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string', varname='toto'),
fields.PageField(
id='2',
label='2nd page',
condition={'type': 'python', 'value': 'form_var_toto == "foo"'},
),
fields.TitleField(id='6', label='title in second page'),
fields.StringField(id='3', label='string'),
fields.PageField(id='4', label='3rd page'),
fields.StringField(id='5', label='string'),
fields.PageField(id='7', label='4th page'),
fields.CommentField(id='8', label='Bla bla bla'),
]
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/test/') # -> 1st page
resp.form['f1'] = 'foo'
resp = resp.form.submit('submit') # -> 2nd page
resp.form['f3'] = 'bar'
resp = resp.form.submit('submit') # -> 3rd page
resp.form['f5'] = 'baz'
resp = resp.form.submit('submit') # -> 4th page
resp = resp.form.submit('submit') # -> validation
resp = resp.form.submit('submit')
formdata_id = resp.location.split('/')[-2]
resp = resp.follow() # -> submit
assert '<h3>1st page</h3>' in resp.text
assert '<h3>2nd page</h3>' in resp.text
assert '<h3>3rd page</h3>' in resp.text
assert '<h3>4th page</h3>' not in resp.text
resp = app.get('/test/') # -> 1st page
resp.form['f1'] = 'foo'
resp = resp.form.submit('submit') # -> 2nd page
resp.form['f3'] = 'bar'
resp = resp.form.submit('previous') # -> 1st page
resp.form['f1'] = 'baz'
resp = resp.form.submit('submit') # -> 3rd page
resp.form['f5'] = 'baz'
resp = resp.form.submit('submit') # -> 4th page
resp = resp.form.submit('submit') # -> validation
resp = resp.form.submit('submit').follow() # -> submit
assert '<h3>1st page</h3>' in resp.text
assert '<h3>2nd page</h3>' not in resp.text
assert '<h3>3rd page</h3>' in resp.text
assert '<h3>4th page</h3>' not in resp.text
# change condition to have second page never displayed
formdef.fields[2].condition['value'] = False
formdef.store()
formdata = formdef.data_class().get(formdata_id)
resp = app.get(formdata.get_url())
# it was filled by user, it should still appear (conditions should not be
# replayed)
assert '<h3>1st page</h3>' in resp.text
assert '<h3>2nd page</h3>' in resp.text
assert '<h3>3rd page</h3>' in resp.text
assert '<h3>4th page</h3>' not in resp.text
def test_form_display_locations(pub):
formdef = create_formdef()
formdef.fields = [
fields.StringField(id='1', label='string1', display_locations=[]),
fields.StringField(id='2', label='string2', display_locations=['validation']),
fields.StringField(id='3', label='string3', display_locations=['summary']),
fields.CommentField(id='4', label='Bla bla bla', display_locations=['validation', 'summary']),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.form['f1'] = 'plop1'
resp.form['f2'] = 'plop2'
resp.form['f3'] = 'plop3'
resp = resp.form.submit('submit') # -> validation
pq = resp.pyquery.remove_namespaces()
assert pq('div[style="display: none;"] [name=f1]')
assert not pq('div[style="display: none;"] [name=f2]')
assert pq('div[style="display: none;"] [name=f3]')
assert 'Bla bla bla' in resp.text
resp = resp.form.submit('submit').follow() # -> submit
assert formdef.data_class().select()[0].data['1'] == 'plop1'
assert formdef.data_class().select()[0].data['2'] == 'plop2'
assert formdef.data_class().select()[0].data['3'] == 'plop3'
assert 'plop1' not in resp.text
assert 'plop2' not in resp.text
assert 'plop3' in resp.text
assert 'Bla bla bla' in resp.text
def test_multipage_form_display_locations(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string1', display_locations=[]),
fields.PageField(id='2', label='2nd page'),
fields.CommentField(id='3', label='Bla bla bla', display_locations=['validation']),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.form['f1'] = 'plop1'
resp = resp.form.submit('submit') # -> page 2
resp = resp.form.submit('submit') # -> validation
pq = resp.pyquery.remove_namespaces()
assert '<h3>1st page</h3>' not in resp.text # page 1 title not displayed
assert pq('div[style="display: none;"] [name=f1]') # but page 1 field included, hidden
assert '<h3>2nd page</h3>' in resp.text # page 2 title
assert 'Bla bla bla' in resp.text # and page 2 comment field
def test_form_visit_existing(pub):
user = create_user(pub)
formdef = create_formdef()
login(get_app(pub), username='foo', password='foo').get('/test/')
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.store()
formdata_user = formdef.data_class()()
formdata_user.user_id = user.id
formdata_user.store()
resp = get_app(pub).get('/test/%s/' % formdata.id)
assert resp.location.startswith('http://example.net/login/?next=')
resp = get_app(pub).get('/test/%s/' % formdata_user.id)
assert resp.location.startswith('http://example.net/login/?next=')
resp = login(get_app(pub), username='foo', password='foo').get('/test/%s/' % formdata_user.id)
assert 'The form has been recorded on' in resp
def test_form_discard_draft(pub, nocache):
create_user(pub)
formdef = create_formdef()
formdef.fields = [fields.StringField(id='0', label='string')]
formdef.enable_tracking_codes = False
formdef.store()
formdef.data_class().wipe()
# anonymous user, no tracking code (-> no draft)
app = get_app(pub)
resp = app.get('/test/')
resp.form['f0'] = 'foobar'
resp = resp.form.submit('submit')
resp = resp.form.submit('previous')
assert [x.status for x in formdef.data_class().select()] == []
assert 'Cancel' in resp.text
assert 'Discard' not in resp.text
resp = resp.form.submit('cancel')
# anonymous user, tracking code (-> draft)
formdef.enable_tracking_codes = True
formdef.store()
app = get_app(pub)
resp = app.get('/test/')
resp.form['f0'] = 'foobar'
resp = resp.form.submit('submit')
resp = resp.form.submit('previous')
assert [x.status for x in formdef.data_class().select()] == ['draft']
assert 'Cancel' not in resp.text
assert 'Discard' in resp.text
resp = resp.form.submit('cancel')
assert [x.status for x in formdef.data_class().select()] == [] # discarded
# logged-in user, no tracking code
formdef.enable_tracking_codes = False
formdef.store()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp.form['f0'] = 'foobar'
resp = resp.form.submit('submit')
resp = resp.form.submit('previous')
assert [x.status for x in formdef.data_class().select()] == ['draft']
assert 'Cancel' not in resp.text
assert 'Discard' in resp.text
resp = resp.form.submit('cancel')
assert [x.status for x in formdef.data_class().select()] == [] # discarded
# logged-in user, tracking code
formdef.enable_tracking_codes = True
formdef.store()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp.form['f0'] = 'foobar'
resp = resp.form.submit('submit')
resp = resp.form.submit('previous')
assert [x.status for x in formdef.data_class().select()] == ['draft']
assert 'Cancel' not in resp.text
assert 'Discard' in resp.text
resp = resp.form.submit('cancel')
assert [x.status for x in formdef.data_class().select()] == [] # discarded
# anonymous user, tracking code, recalled
formdef.enable_tracking_codes = True
formdef.store()
app = get_app(pub)
resp = app.get('/test/')
resp.form['f0'] = 'foobar'
resp = resp.form.submit('submit')
resp = resp.form.submit('previous')
assert [x.status for x in formdef.data_class().select()] == ['draft']
assert 'Cancel' not in resp.text
assert 'Discard' in resp.text
tracking_code = get_displayed_tracking_code(resp)
resp = get_app(pub).get('/')
resp.form['code'] = tracking_code
resp = resp.form.submit().follow().follow().follow()
assert resp.forms[1]['f0'].value == 'foobar'
assert 'Cancel' in resp.text
assert 'Discard Draft' in resp.text
resp = resp.forms[1].submit('cancel')
assert [x.status for x in formdef.data_class().select()] == ['draft']
# logged-in user, no tracking code, recalled
formdef.data_class().wipe()
formdef.enable_tracking_codes = False
formdef.store()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp.form['f0'] = 'foobar'
resp = resp.form.submit('submit')
resp = resp.form.submit('previous')
assert [x.status for x in formdef.data_class().select()] == ['draft']
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp = resp.click('Continue with draft').follow()
assert 'Cancel' in resp.text
assert 'Discard Draft' in resp.text
resp = resp.forms[1].submit('cancel')
assert [x.status for x in formdef.data_class().select()] == ['draft']
def test_form_invalid_previous_data(pub):
create_user(pub)
formdef = create_formdef()
formdef.fields = [fields.DateField(id='0', label='date')]
formdef.enable_tracking_codes = True
formdef.store()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
formdef.data_class().wipe()
tracking_code = get_displayed_tracking_code(resp)
assert tracking_code is not None
resp.forms[0]['f0'] = time.strftime('%Y-%m-%d', time.localtime())
resp = resp.forms[0].submit('submit') # -> validation page
formdef.fields[0].minimum_is_future = True
formdef.store()
# load the formdata as a draft
resp = login(get_app(pub), username='foo', password='foo').get('/')
resp.forms[0]['code'] = tracking_code
resp = resp.forms[0].submit().follow().follow().follow()
assert resp.forms[1]['f0'].value == time.strftime('%Y-%m-%d', time.localtime())
resp = resp.forms[1].submit('submit') # -> submit
assert 'This form has already been submitted.' not in resp.text
assert 'Unexpected field error' in resp.text
def test_form_draft_with_file(pub):
create_user(pub)
formdef = create_formdef()
formdef.fields = [fields.FileField(id='0', label='file')]
formdef.enable_tracking_codes = True
formdef.store()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
formdef.data_class().wipe()
assert '<h3>Tracking code</h3>' in resp.text
tracking_code = get_displayed_tracking_code(resp)
assert tracking_code is not None
resp.forms[0]['f0$file'] = Upload('test.txt', b'foobar', 'text/plain')
resp = resp.forms[0].submit('submit')
tracking_code_2 = get_displayed_tracking_code(resp)
assert tracking_code == tracking_code_2
# check we can load the formdata as a draft
resp = login(get_app(pub), username='foo', password='foo').get('/')
resp.forms[0]['code'] = tracking_code
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/code/%s/load' % tracking_code
resp = resp.follow()
resp = resp.follow()
assert resp.location.startswith('http://example.net/test/?mt=')
resp = resp.follow()
resp = resp.forms[1].submit('previous')
assert resp.pyquery('.filename').text() == 'test.txt'
# check file is downloadable
r2 = resp.click('test.txt')
assert r2.content_type == 'text/plain'
assert r2.text == 'foobar'
# check submitted form keeps the file
resp = resp.forms[1].submit('submit') # -> confirmation page
resp = resp.forms[1].submit('submit') # -> done
resp = resp.follow()
assert resp.click('test.txt').follow().text == 'foobar'
def test_form_draft_with_file_direct_validation(pub):
create_user(pub)
formdef = create_formdef()
formdef.fields = [fields.FileField(id='0', label='file')]
formdef.enable_tracking_codes = True
formdef.store()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
formdef.data_class().wipe()
tracking_code = get_displayed_tracking_code(resp)
resp.forms[0]['f0$file'] = Upload('test2.txt', b'foobar2', 'text/plain')
resp = resp.forms[0].submit('submit')
resp = login(get_app(pub), username='foo', password='foo').get('/')
resp.forms[0]['code'] = tracking_code
resp = resp.forms[0].submit().follow().follow().follow()
assert 'test2.txt' in resp.text
# check submitted form keeps the file
resp = resp.forms[1].submit('submit') # -> done
resp = resp.follow()
assert resp.click('test2.txt').follow().text == 'foobar2'
def test_form_draft_with_date(pub):
create_user(pub)
formdef = create_formdef()
formdef.fields = [fields.DateField(id='0', label='date')]
formdef.enable_tracking_codes = True
formdef.store()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
formdef.data_class().wipe()
tracking_code = get_displayed_tracking_code(resp)
resp.forms[0]['f0'] = '2012-02-12'
resp = resp.forms[0].submit('submit')
resp = login(get_app(pub), username='foo', password='foo').get('/')
resp.forms[0]['code'] = tracking_code
resp = resp.forms[0].submit().follow().follow().follow()
assert '2012-02-12' in resp.text
# check submitted form keeps the date
resp = resp.forms[1].submit('submit') # -> done
resp = resp.follow()
assert '2012-02-12' in resp.text
def test_form_draft_save_on_error_page(pub):
create_user(pub)
formdef = create_formdef()
formdef.fields = [
fields.StringField(id='1', label='string1', required=False),
fields.StringField(id='2', label='string2', required=True),
]
formdef.enable_tracking_codes = True
formdef.store()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
formdef.data_class().wipe()
tracking_code = get_displayed_tracking_code(resp)
resp.forms[0]['f1'] = 'plop'
resp = resp.forms[0].submit('submit')
assert resp.pyquery('#form_error_f2').text() == 'required field'
resp = login(get_app(pub), username='foo', password='foo').get('/')
resp.forms[0]['code'] = tracking_code
resp = resp.forms[0].submit().follow().follow().follow()
assert resp.forms[1]['f1'].value == 'plop'
@pytest.mark.parametrize('tracking_code', [True, False])
def test_form_direct_draft_access(pub, tracking_code):
user = create_user(pub)
formdef = create_formdef()
formdef.fields = [fields.StringField(id='0', label='string')]
formdef.enable_tracking_codes = tracking_code
formdef.store()
formdata = formdef.data_class()()
formdata.data = {'0': 'foobar'}
formdata.status = 'draft'
formdata.store()
resp = get_app(pub).get('/test/%s/' % formdata.id, status=302)
assert resp.location.startswith('http://example.net/login')
formdata.user_id = user.id
formdata.store()
resp = get_app(pub).get('/test/%s/' % formdata.id, status=302)
assert resp.location.startswith('http://example.net/login')
resp = login(get_app(pub), 'foo', 'foo').get('/test/%s/' % formdata.id, status=302)
assert resp.location.startswith('http://example.net/test/?mt=')
formdata.user_id = 1000
formdata.store()
resp = login(get_app(pub), 'foo', 'foo').get('/test/%s/' % formdata.id, status=403)
def form_password_field_submit(app, password):
formdef = create_formdef()
formdef.enable_tracking_codes = True
formdef.fields = [fields.PasswordField(id='0', label='password', formats=['sha1', 'md5', 'cleartext'])]
formdef.store()
page = app.get('/test/')
formdef.data_class().wipe()
next_page = page.forms[0].submit('submit') # but the field is required
assert [x.text for x in next_page.pyquery('div.error p')] == ['required field'] * 2
next_page.forms[0]['f0$pwd1'] = password
next_page.forms[0]['f0$pwd2'] = password
next_page = next_page.forms[0].submit('submit')
assert 'Check values then click submit.' in next_page.text
next_page = next_page.forms[0].submit('submit')
assert next_page.status_int == 302
next_page = next_page.follow()
assert 'The form has been recorded' in next_page.text
assert formdef.data_class().count() == 1
data_id = formdef.data_class().select()[0].id
data = formdef.data_class().get(data_id)
assert data.data == {
'0': {
'sha1': force_str(hashlib.sha1(force_bytes(password)).hexdigest()),
'md5': force_str(hashlib.md5(force_bytes(password)).hexdigest()),
'cleartext': force_str(password),
}
}
def test_form_password_field_submit(pub):
create_user(pub)
form_password_field_submit(get_app(pub), 'foobar')
form_password_field_submit(get_app(pub), force_str('• 83003706'))
form_password_field_submit(login(get_app(pub), username='foo', password='foo'), 'foobar\u00eb')
def test_form_multi_page_formdef_count_condition(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string'),
fields.PageField(
id='2',
label='2nd page',
condition={'type': 'python', 'value': 'form_objects.count > 0'},
),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp.forms[0]['f1'] = 'foo'
resp = resp.forms[0].submit('submit') # should go straight to validation
assert 'Check values then click submit.' in resp.text
# add a formdata this will make the second page appear.
formdata = formdef.data_class()()
formdata.just_created()
formdata.store()
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'foo'
resp = resp.forms[0].submit('submit') # should NOT go straight to validation
assert 'Check values then click submit.' not in resp.text
def test_form_multi_page_post_edit(pub):
create_user(pub)
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page'),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
editable = st1.add_action('editable', id='_editable')
editable.by = ['_submitter', '_receiver']
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
page = login(get_app(pub), username='foo', password='foo').get('/test/')
page.forms[0]['f1'] = 'foo'
next_page = page.forms[0].submit('submit')
next_page.forms[0]['f3'] = 'barXYZ'
next_page = next_page.forms[0].submit('submit')
next_page = next_page.forms[0].submit('submit')
next_page = next_page.follow()
assert 'The form has been recorded' in next_page.text
data_id = formdef.data_class().select()[0].id
page = login(get_app(pub), username='foo', password='foo').get('/test/%s/' % data_id)
assert 'button_editable-button' in page.text
assert 'barXYZ' in page.text
resp = page.forms[0].submit('button_editable')
assert resp.location.startswith('http://example.net/test/%s/wfedit-' % data_id)
resp = resp.follow()
# check there's no new "phantom" history entry
assert len(formdef.data_class().get(data_id).evolution) == 1
assert resp.forms[0]['f1'].value == 'foo'
resp.forms[0]['f1'] = 'foo2'
resp = resp.forms[0].submit('submit')
assert resp.forms[0]['f3'].value == 'barXYZ'
resp = resp.forms[0].submit('previous')
assert resp.forms[0]['f1'].value == 'foo2'
resp = resp.forms[0].submit('submit')
assert 'Save Changes' in resp.text
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/test/%s/' % data_id
resp = resp.follow()
assert 'foo2' in resp.text # modified value is there
assert 'barXYZ' in resp.text # unchanged value is still there
assert len(formdef.data_class().get(data_id).evolution) == 2 # new history entry
assert formdef.data_class().get(data_id).evolution[-1].who == '_submitter'
assert formdef.data_class().get(data_id).evolution[-1].status is None
# modify workflow to jump to another status after the edition
st2 = workflow.add_status('Status2', 'st2')
editable.status = st2.id
workflow.store()
assert formdef.data_class().get(data_id).status == 'wf-%s' % st1.id
page = login(get_app(pub), username='foo', password='foo').get('/test/%s/' % data_id)
assert 'button_editable-button' in page.text
assert 'barXYZ' in page.text
resp = page.forms[0].submit('button_editable')
assert resp.location.startswith('http://example.net/test/%s/wfedit-' % data_id)
resp = resp.follow()
assert resp.forms[0]['f1'].value == 'foo2'
resp.forms[0]['f1'] = 'foo3'
resp = resp.forms[0].submit('submit')
assert (
formdef.data_class().get(data_id).data['1'] == 'foo2'
) # check foo3 has not been overwritten in database
assert resp.forms[0]['f3'].value == 'barXYZ'
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/test/%s/' % data_id
resp = resp.follow()
assert 'foo3' in resp.text # modified value is there
assert 'barXYZ' in resp.text # unchanged value is still there
assert formdef.data_class().get(data_id).status == 'wf-%s' % st2.id
assert len(formdef.data_class().get(data_id).evolution) == 3 # single new history entry
assert formdef.data_class().get(data_id).evolution[-1].who == '_submitter'
assert formdef.data_class().get(data_id).evolution[-1].status == 'wf-%s' % st2.id
# jump to a nonexistent status == do not jump, but add a LoggedError
pub.loggederror_class.wipe()
assert pub.loggederror_class.count() == 0
editable.status = 'deleted_status_id'
workflow.store()
# go back to st1
formdata = formdef.data_class().get(data_id)
formdata.status = 'wf-%s' % st1.id
formdata.store()
assert formdef.data_class().get(data_id).status == 'wf-%s' % st1.id
page = login(get_app(pub), username='foo', password='foo').get('/test/%s/' % data_id)
resp = page.forms[0].submit('button_editable')
resp = resp.follow()
resp.forms[0]['f1'] = 'foo3'
resp = resp.forms[0].submit('submit')
resp = resp.forms[0].submit('submit')
resp = resp.follow()
assert formdef.data_class().get(data_id).status == 'wf-%s' % st1.id # stay on st1
assert pub.loggederror_class.count() == 1
logged_error = pub.loggederror_class.select()[0]
assert logged_error.formdata_id == str(formdata.id)
assert logged_error.formdef_id == formdef.id
assert logged_error.workflow_id == workflow.id
assert logged_error.status_id == st1.id
assert logged_error.status_item_id == editable.id
assert logged_error.occurences_count == 1
# do it again: increment logged_error.occurences_count
page = login(get_app(pub), username='foo', password='foo').get('/test/%s/' % data_id)
resp = page.forms[0].submit('button_editable')
resp = resp.follow()
resp.forms[0]['f1'] = 'foo3'
resp = resp.forms[0].submit('submit')
resp = resp.forms[0].submit('submit')
resp = resp.follow()
assert formdef.data_class().get(data_id).status == 'wf-%s' % st1.id # stay on st1
assert pub.loggederror_class.count() == 1
logged_error = pub.loggederror_class.select()[0]
assert logged_error.occurences_count == 2
def test_form_edit_autocomplete_list(pub):
create_user(pub)
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {'type': 'json', 'value': 'http://remote.example.net/json'}
data_source.query_parameter = 'q'
data_source.id_parameter = 'id'
data_source.store()
formdef = create_formdef()
formdef.data_class().wipe()
formdef.fields = [
fields.ItemField(
id='0',
label='string',
data_source={'type': 'foobar'},
display_mode='autocomplete',
),
]
formdef.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
editable = st1.add_action('editable', id='_editable')
editable.by = ['_submitter', '_receiver']
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
app = get_app(pub)
login(app, username='foo', password='foo')
with responses.RequestsMock() as rsps:
data = {
'data': [
{'id': '1', 'text': 'hello', 'extra': 'foo'},
{'id': '2', 'text': 'world', 'extra': 'bar'},
]
}
rsps.get('http://remote.example.net/json', json=data)
resp = app.get('/test/')
assert 'data-select2-url=' in resp.text
# simulate select2 mode, with qommon.forms.js adding an extra hidden widget
resp.form.fields['f0_display'] = Hidden(form=resp.form, tag='input', name='f0_display', pos=10)
resp.form['f0'].force_value('1')
resp.form.fields['f0_display'].force_value('hello')
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submit
assert formdef.data_class().select()[0].data['0'] == '1'
assert formdef.data_class().select()[0].data['0_display'] == 'hello'
assert formdef.data_class().select()[0].data['0_structured'] == data['data'][0]
resp = resp.follow()
url = resp.request.url
resp = resp.form.submit('button_editable')
assert 'wfedit' in resp.location
resp = resp.follow()
assert 'data-value="1"' in resp
assert 'data-initial-display-value="hello"' in resp
# relogin
app = get_app(pub)
login(app, username='foo', password='foo')
resp = app.get(url)
resp = resp.form.submit('button_editable')
resp = resp.follow()
assert 'data-value="1"' in resp
assert 'data-initial-display-value="hello"' in resp
def test_form_edit_with_internal_id_condition(pub):
create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
formdef.fields = [
fields.StringField(id='1', label='1st field'),
fields.StringField(
id='2',
label='2nd field',
condition={'type': 'django', 'value': 'form_internal_id'},
),
]
formdef.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
editable = st1.add_action('editable', id='_editable')
editable.by = ['_submitter', '_receiver']
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
app = get_app(pub)
login(app, username='foo', password='foo')
resp = app.get(formdef.get_url())
resp.form['f1'] = 'test'
assert 'f2' not in resp.form.fields
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submit
assert formdef.data_class().select()[0].data['1'] == 'test'
resp = resp.follow()
resp = resp.form.submit('button_editable')
assert 'wfedit' in resp.location
resp = resp.follow()
assert 'f2' in resp.form.fields
def test_form_edit_action_jump_to_previously_marked(pub):
create_user(pub)
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1')
st2 = workflow.add_status('Status2')
choice = st1.add_action('choice')
choice.label = 'go to status2'
choice.by = ['_submitter']
choice.status = st2.id
choice.set_marker_on_status = True
editable = st2.add_action('editable')
editable.by = ['_submitter']
editable.label = 'edit'
editable.status = '_previous'
workflow.store()
formdef = create_formdef()
formdef.fields = [
fields.StringField(id='1', label='string'),
]
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp.forms[0]['f1'] = 'foo'
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit') # -> submit
resp = resp.follow()
assert 'The form has been recorded' in resp.text
formdata = formdef.data_class().select()[0]
resp = login(get_app(pub), username='foo', password='foo').get(formdata.get_url())
resp = resp.forms[0].submit('button1').follow() # jump
formdata.refresh_from_storage()
assert formdata.status == f'wf-{st2.id}'
resp = resp.forms[0].submit('button1') # edit
resp = resp.follow()
resp.forms[0]['f1'] = 'foo2'
resp = resp.forms[0].submit('submit')
resp = resp.follow()
formdata.refresh_from_storage()
assert formdata.status == f'wf-{st1.id}'
def test_form_count_dispatching(pub):
create_user(pub)
formdef = create_formdef()
formdef.fields = []
formdef.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
jump = st1.add_action('jump')
jump.condition = {'type': 'python', 'value': 'form_objects.count_status_st2 < 1'}
jump.status = 'st2'
workflow.add_status('Status2', 'st2')
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
page = login(get_app(pub), username='foo', password='foo').get('/test/')
page = page.forms[0].submit('submit') # form page
page = page.forms[0].submit('submit') # confirmation page
page = page.follow()
assert 'The form has been recorded' in page.text # success
assert len(formdef.data_class().select(clause=lambda x: x.status == 'wf-st1')) == 0
assert len(formdef.data_class().select(clause=lambda x: x.status == 'wf-st2')) == 1
page = login(get_app(pub), username='foo', password='foo').get('/test/')
page = page.forms[0].submit('submit') # form page
page = page.forms[0].submit('submit') # confirmation page
page = page.follow()
assert 'The form has been recorded' in page.text # success
assert len(formdef.data_class().select(clause=lambda x: x.status == 'wf-st2')) == 1
assert len(formdef.data_class().select(clause=lambda x: x.status == 'wf-st1')) == 1
def test_preview_form(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
formdef.fields = []
formdef.disabled = True
formdef.store()
# check the preview page is not accessible to regular users
get_app(pub).get('/preview/test/', status=403)
# check it's accessible to admins
user.is_admin = True
user.store()
page = login(get_app(pub), username='foo', password='foo').get('/preview/test/')
# check the form is marked as a preview (this disables autosave calls)
assert page.pyquery('form[data-autosave=false]').length
# check no formdata gets stored
next_page = page.forms[0].submit('submit')
assert 'Check values then click submit.' in next_page.text
next_page = next_page.forms[0].submit('submit')
assert next_page.status_int == 200
assert 'This was only a preview: form was not actually submitted.' in next_page.text
assert len([x for x in formdef.data_class().select() if not x.is_draft()]) == 0
# check no drafts are proposed for recall
formdef.data_class().wipe()
draft = formdef.data_class()()
draft.user_id = user.id
draft.status = 'draft'
draft.data = {}
draft.store()
resp = login(get_app(pub), username='foo', password='foo').get('/preview/test/')
assert 'You already started to fill this form.' not in resp.text
# check the preview is ok when there is a category
Category.wipe()
cat = Category(name='foobar')
cat.store()
formdef.category_id = cat.id
formdef.store()
resp = login(get_app(pub), username='foo', password='foo').get('/preview/test/', status=200)
def test_form_captcha(pub):
create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
formdef.fields = [fields.StringField(id='0', label='Some field')]
formdef.has_captcha = True
formdef.enable_tracking_codes = True
formdef.store()
# test authenticated users are not presented with a captcha
resp = login(get_app(pub), username='foo', password='foo').get('/')
resp = resp.click('test')
resp.form['f0'] = 'test'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'form_captcha' not in resp.text
# check anonymous user gets the captcha
app = get_app(pub)
resp = app.get('/')
resp = resp.click('test')
resp.form['f0'] = 'test'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'form_captcha' in resp.text
session_id = list(app.cookies.values())[0].strip('"')
session = pub.session_class.get(session_id)
resp.form['captcha$q'] = session.get_captcha_token(resp.forms[0]['captcha$token'].value)['answer']
resp = resp.form.submit('submit')
assert resp.status_code == 302 # redirect when formdata is created
# and check it gets it only once
resp = app.get('/')
resp = resp.click('test')
resp.form['f0'] = 'test'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'form_captcha' not in resp.text
def test_form_captcha_and_no_validation_page(pub):
create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
formdef.fields = [fields.StringField(id='0', label='Some field')]
formdef.has_captcha = True
formdef.enable_tracking_codes = True
formdef.confirmation = False
formdef.store()
# test authenticated users are not stopped on a confirmation page
resp = login(get_app(pub), username='foo', password='foo').get('/')
resp = resp.click('test')
resp.form['f0'] = 'test'
resp = resp.form.submit('submit')
assert resp.status_code == 302 # redirect when formdata is created
# check anonymous user gets the captcha
app = get_app(pub)
resp = app.get('/')
resp = resp.click('test')
resp.form['f0'] = 'test'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'form_captcha' in resp.text
def test_form_table_field_submit(pub, emails):
formdef = create_formdef()
formdef.fields = [
fields.TableField(
id='0',
label='table',
rows=[force_str('à'), 'b'],
columns=['c', 'd', force_str('e')],
required=False,
)
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.forms[0].submit('submit')
assert formdef.data_class().select()[0].data == {'0': [['', '', ''], ['', '', '']]}
formdef.data_class().wipe()
formdef.fields = [
fields.TableField(id='0', label='table', rows=['a', 'b'], columns=['c', 'd', 'e'], required=True)
]
formdef.store()
resp = get_app(pub).get('/test/')
resp = resp.form.submit('submit')
assert 'Check values then click submit.' not in resp.text
resp = get_app(pub).get('/test/')
resp.form['f0$c-0-0'] = 'a'
resp.form['f0$c-1-0'] = 'b'
resp.form['f0$c-0-1'] = 'c'
resp.form['f0$c-1-1'] = 'd'
resp.form['f0$c-0-2'] = 'e'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.form.submit('submit')
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().select()[0].data == {'0': [['a', 'c', 'e'], ['b', 'd', '']]}
# check table is present in received email (via form_details).
create_user(pub)
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp.form['f0$c-0-0'] = 'àà' # would trigger column length bug (#23072)
resp.form['f0$c-1-0'] = 'bb'
resp.form['f0$c-0-1'] = 'cc'
resp.form['f0$c-1-1'] = 'dd'
resp.form['f0$c-0-2'] = 'ee'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.form.submit('submit')
resp = resp.follow()
assert 'The form has been recorded' in resp.text
# check rst2html didn't fail
assert b'ee' in emails.get('New form (test)')['msg'].get_payload()[1].get_payload(decode=True)
def test_form_table_rows_field_submit(pub, emails):
formdef = create_formdef()
formdef.fields = [fields.TableRowsField(id='0', label='table', columns=['a', 'b'], required=False)]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.forms[0].submit('submit')
assert formdef.data_class().select()[0].data == {'0': []}
formdef.data_class().wipe()
formdef.fields = [fields.TableRowsField(id='0', label='table', columns=['a', 'b'], required=True)]
formdef.store()
resp = get_app(pub).get('/test/')
resp = resp.form.submit('submit')
assert 'Check values then click submit.' not in resp.text
resp = get_app(pub).get('/test/')
resp.form['f0$element0$col0'] = 'a'
resp.form['f0$element0$col1'] = 'b'
resp.form['f0$element1$col0'] = 'c'
resp.form['f0$element1$col1'] = 'd'
resp.form['f0$element2$col0'] = 'e'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.form.submit('submit')
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().select()[0].data == {'0': [['a', 'b'], ['c', 'd'], ['e', '']]}
formdef.data_class().wipe()
formdef.fields = [
fields.TableRowsField(id='0', label='table', columns=['a', 'b'], required=True, total_row=True)
]
formdef.store()
resp = get_app(pub).get('/test/')
resp.form['f0$element0$col0'] = 'a'
resp.form['f0$element0$col1'] = '14'
resp.form['f0$element1$col0'] = 'c'
resp.form['f0$element1$col1'] = '23'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.form.submit('submit')
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert '37.00' in resp.text
# check table is present in received email (via form_details).
create_user(pub)
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp.form['f0$element0$col0'] = 'àà'
resp.form['f0$element0$col1'] = '14'
resp.form['f0$element1$col0'] = 'ee'
resp.form['f0$element1$col1'] = '23'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.form.submit('submit')
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert b'ee' in emails.get('New form (test)')['msg'].get_payload()[1].get_payload(decode=True)
def test_form_new_table_rows_field_draft_recall(pub):
formdef = create_formdef()
formdef.enable_tracking_codes = True
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page'),
]
formdef.store()
formdef.data_class().wipe()
formdef.store()
app = get_app(pub)
resp = app.get('/test/')
resp.form['f1'] = 'test'
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
tracking_code = get_displayed_tracking_code(resp)
assert tracking_code is not None
# add new table rows field to formdef
formdef.fields.append(fields.TableRowsField(id='3', label='table', columns=['a', 'b'], required=False))
formdef.store()
# restore form on validation page
resp = get_app(pub).get('/')
resp.form['code'] = tracking_code
resp = resp.form.submit().follow().follow().follow()
# validate form
resp = resp.forms[1].submit()
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
assert formdef.data_class().select()[0].data['1'] == 'test'
assert formdef.data_class().select()[0].data['3'] is None
def test_form_table_rows_add_row(pub):
formdef = create_formdef()
formdef.fields = [
fields.StringField(id='1', label='string', require=True),
fields.TableRowsField(id='0', label='table', columns=['a', 'b'], required=True),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert len(resp.pyquery.find('input[name^="f0$element"]')) == 10
resp = resp.form.submit('f0$add_element')
assert 'There were errors processing the form' not in resp
assert len(resp.pyquery.find('input[name^="f0$element"]')) == 12
resp = resp.form.submit('f0$add_element')
assert len(resp.pyquery.find('input[name^="f0$element"]')) == 14
resp = resp.form.submit('submit')
assert 'There were errors processing the form' in resp
def test_form_middle_session_change(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page'),
fields.StringField(id='3', label='string 2'),
]
formdef.store()
app = get_app(pub)
resp = app.get('/test/')
resp.forms[0]['f1'] = 'foo'
assert resp.forms[0].fields['submit'][0].value_if_submitted() == 'Next'
resp = resp.forms[0].submit('submit')
assert resp.forms[0]['previous']
app.cookiejar.clear()
resp.forms[0]['f3'] = 'bar'
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/test/'
resp = resp.follow()
assert 'Sorry, your session have been lost.' in resp.text
app = get_app(pub)
resp = app.get('/test/')
resp.forms[0]['f1'] = 'foo'
assert resp.forms[0].fields['submit'][0].value_if_submitted() == 'Next'
resp = resp.forms[0].submit('submit')
assert resp.forms[0]['previous']
resp.forms[0]['f3'] = 'bar'
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.text
app.cookiejar.clear()
resp = resp.forms[0].submit('submit')
resp = resp.follow()
assert 'Sorry, your session have been lost.' in resp.text
def test_form_autocomplete_variadic_url(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.PageField(id='3', label='2nd page', condition={'type': 'python', 'value': 'True'}),
fields.ItemField(id='1', label='string', varname='foo', items=['Foo', 'Bar']),
fields.StringField(
id='2', label='string2', required=True, data_source={'type': 'jsonp', 'value': '[var_foo]'}
),
fields.PageField(id='4', label='3rd page', condition={'type': 'python', 'value': 'True'}),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp = resp.form.submit('submit') # next
# test javascript will be used to compute the full URL
assert 'options.wcs_base_url' in resp.text
assert 'jquery-ui.min.js' in resp.text
# test going forward (will error out), check it's still a variadic URL (#9786)
resp.form['f1'] = 'Foo'
resp = resp.form.submit('submit')
assert 'options.wcs_base_url' in resp.text
def test_form_date_field_submit(pub):
formdef = create_formdef()
formdef.fields = [fields.DateField(id='0', label='string', required=False)]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp.forms[0]['f0'] = '2015-01-01'
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
data_id = formdef.data_class().select()[0].id
data = formdef.data_class().get(data_id)
assert time.strftime('%Y-%m-%d', data.data['0']) == '2015-01-01'
# without filling the field
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
data_id = formdef.data_class().select()[0].id
data = formdef.data_class().get(data_id)
assert data.data['0'] is None
def test_form_string_regex_field_submit(pub):
formdef = create_formdef()
formdef.fields = [
fields.StringField(
id='0',
label='string',
validation={'type': 'regex', 'value': r'\d{5}$'},
required=False,
)
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.forms[0]['f0'] = '12345'
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
data_id = formdef.data_class().select()[0].id
data = formdef.data_class().get(data_id)
assert data.data['0'] == '12345'
# without filling the field
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
data_id = formdef.data_class().select()[0].id
data = formdef.data_class().get(data_id)
assert data.data['0'] is None
# with an invalid input
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.forms[0]['f0'] = 'foobar'
resp = resp.forms[0].submit('submit')
assert 'invalid value' in resp.text
def test_form_text_field_submit(pub):
formdef = create_formdef()
formdef.fields = [fields.TextField(id='0', label='string', required=False)]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.forms[0]['f0'] = '12345'
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
data_id = formdef.data_class().select()[0].id
data = formdef.data_class().get(data_id)
assert data.data['0'] == '12345'
# without filling the field
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
data_id = formdef.data_class().select()[0].id
data = formdef.data_class().get(data_id)
assert data.data['0'] is None
# check max length
formdef.fields = [fields.TextField(id='0', label='string', maxlength=10)]
formdef.store()
resp = get_app(pub).get('/test/')
resp.forms[0]['f0'] = 'x' * 11
resp = resp.forms[0].submit('submit')
assert 'too many characters (limit is 10)' in resp.text
# check it counts characters, not bytes
resp.forms[0]['f0'] = '' * 10
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.text
def test_unknown_datasource(pub):
formdef = create_formdef()
formdef.fields = [
fields.StringField(
id='1', label='string', varname='string', required=False, data_source={'type': 'foobar'}
),
fields.ItemField(
id='2', label='item', varname='item', required=False, data_source={'type': 'foobar'}
),
fields.ItemsField(
id='3', label='items', varname='items', required=False, data_source={'type': 'foobar'}
),
]
formdef.store()
data_class = formdef.data_class()
data_class.wipe()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp = resp.forms[0].submit('submit') # should go straight to validation
assert 'Check values then click submit.' in resp.text
def test_form_ranked_items_field_submit(pub):
formdef = create_formdef()
formdef.fields = [
fields.RankedItemsField(id='0', label='ranked items', required=False, items=['foo', 'bar', 'baz'])
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.form['f0$element0'] = '1'
resp.form['f0$element1'] = '2'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.form.submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
data_id = formdef.data_class().select()[0].id
data = formdef.data_class().get(data_id)
assert data.data['0'] == {'bar': 2, 'foo': 1}
def test_form_ranked_items_randomize_order(pub):
formdef = create_formdef()
formdef.fields = [
fields.RankedItemsField(
id='0',
label='ranked items',
required=False,
randomize_items=True,
items=['foo', 'bar', 'baz'],
)
]
formdef.store()
orders = {}
for _ in range(10):
resp = get_app(pub).get('/test/')
orders['%s-%s-%s' % (resp.text.index('foo'), resp.text.index('bar'), resp.text.index('baz'))] = True
assert len(orders.keys()) > 1
def test_form_autosave(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page'),
fields.StringField(id='3', label='string 2'),
]
formdef.enable_tracking_codes = True
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/test/')
resp.form['f1'] = 'foobar'
assert app.post('/test/autosave', params=resp.form.submit_fields()).json == {'result': 'success'}
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.status == 'draft'
assert formdef.data_class().select()[0].data['1'] == 'foobar'
resp.form['f1'] = 'foobar2'
assert app.post('/test/autosave', params=resp.form.submit_fields()).json == {'result': 'success'}
assert formdef.data_class().select()[0].data['1'] == 'foobar2'
resp.form['f1'] = 'foobar3'
resp = resp.forms[0].submit('submit')
assert formdef.data_class().select()[0].data['1'] == 'foobar3'
resp.form['f3'] = 'xxx'
assert app.post('/test/autosave', params=resp.form.submit_fields()).json == {'result': 'success'}
assert formdef.data_class().select()[0].data['1'] == 'foobar3'
assert formdef.data_class().select()[0].data['3'] == 'xxx'
resp.form['f3'] = 'xxx2'
assert app.post('/test/autosave', params=resp.form.submit_fields()).json == {'result': 'success'}
assert formdef.data_class().select()[0].data['1'] == 'foobar3'
assert formdef.data_class().select()[0].data['3'] == 'xxx2'
resp.form['f3'] = 'xxx3'
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'foobar3' in resp.text
assert 'xxx3' in resp.text
resp = resp.forms[0].submit('submit')
assert formdef.data_class().count() == 1
assert formdef.data_class().select()[0].data['1'] == 'foobar3'
assert formdef.data_class().select()[0].data['3'] == 'xxx3'
# make sure autosave() doesn't destroy data that would have been submitted
# in the meantime
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/test/')
resp.form['f1'] = 'foobar'
autosave_fields = resp.form.submit_fields()
resp.form['f1'] = 'foobar3'
resp = resp.forms[0].submit('submit')
assert formdef.data_class().select()[0].data['1'] == 'foobar3'
# post content with 'foobar' as value, it should not be saved
ajax_resp = app.post('/test/autosave', params=autosave_fields)
assert json.loads(ajax_resp.text)['result'] == 'error'
assert formdef.data_class().select()[0].data['1'] == 'foobar3'
def test_form_autosave_timeout(pub, monkeypatch):
from wcs.forms.root import FormPage
monkeypatch.setattr(FormPage, 'AUTOSAVE_TIMEOUT', 0.0001)
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page'),
fields.StringField(id='3', label='string 2'),
]
formdef.enable_tracking_codes = True
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/test/')
resp.form['f1'] = 'foobar'
resp = app.post('/test/autosave', params=resp.form.submit_fields())
assert resp.json == {'reason': 'too long', 'result': 'error'}
def test_form_autosave_with_invalid_data(pub):
formdef = create_formdef()
formdef.fields = [
fields.EmailField(id='1', label='email'),
]
formdef.enable_tracking_codes = True
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/test/')
resp.form['f1'] = 'foobar' # not a valid email
assert app.post('/test/autosave', params=resp.form.submit_fields()).json == {'result': 'success'}
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.status == 'draft'
assert formdef.data_class().select()[0].data['1'] == 'foobar'
# restore draft
tracking_code = get_displayed_tracking_code(resp)
resp = get_app(pub).get('/')
resp.forms[0]['code'] = tracking_code
resp = resp.forms[0].submit().follow().follow().follow()
assert resp.forms[1]['f1'].value == 'foobar' # not a valid email
def test_form_autosave_with_parameterized_datasource(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string', varname='foo'),
fields.PageField(id='2', label='2nd page'),
fields.ItemField(
id='3',
label='item',
data_source={'type': 'formula', 'value': '''[('1', form_var_foo*2)]'''},
),
]
formdef.enable_tracking_codes = True
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/test/')
resp.form['f1'] = 'bar'
assert app.post('/test/autosave', params=resp.form.submit_fields()).json == {'result': 'success'}
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.status == 'draft'
assert formdef.data_class().select()[0].data['1'] == 'bar'
assert formdef.data_class().select()[0].data.get('3') is None
resp = resp.forms[0].submit('submit')
assert app.post('/test/autosave', params=resp.form.submit_fields()).json == {'result': 'success'}
assert formdef.data_class().count() == 1
assert formdef.data_class().select()[0].data['1'] == 'bar'
assert formdef.data_class().select()[0].data['3'] == '1'
assert formdef.data_class().select()[0].data['3_display'] == 'barbar'
def test_form_autosave_never_overwrite(mocker, pub, settings):
create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string1'),
fields.PageField(id='2', label='2nd page'),
fields.StringField(id='3', label='string2'),
]
formdef.store()
app = get_app(pub)
login(app, username='foo', password='foo')
resp = app.get('/test/')
resp.form['f1'] = '1'
# go to the second page
resp = resp.form.submit('submit')
resp.form['f3'] = 'tmp'
# autosave this temporary data
autosave_data = dict(resp.form.submit_fields())
resp_autosave = app.post('/test/autosave', params=autosave_data)
assert resp_autosave.json == {'result': 'success'}
# check the draft has been modified
formdata = formdef.data_class().select()[0]
formdata.refresh_from_storage()
assert formdata.data['3'] == 'tmp'
# now finish submitting with new value
resp.form['f3'] = '1'
resp = resp.form.submit('submit') # -> validation page
formdata.refresh_from_storage()
assert formdata.data['3'] == '1'
# autosave wrong data
# _ajax_form_token is just a form_token, so take the current one to
# simulate a rogue autosave from the previous page
autosave_data['_ajax_form_token'] = resp.form['_form_id'].value
resp_autosave = app.post('/test/autosave', params=autosave_data)
formdata.refresh_from_storage()
assert resp_autosave.json != {'result': 'success'}
assert formdata.data['3'] == '1'
# validate
resp = resp.form.submit('submit') # -> submit
# everything is still fine in the end, even for pickle storage
# (as the overwritten # data are recreated from validation page)
assert formdef.data_class().select()[0].data == {'1': '1', '3': '1'}
def test_form_string_field_autocomplete(pub):
formdef = create_formdef()
formdef.fields = [fields.StringField(id='0', label='string', required=False)]
formdef.fields[0].data_source = {'type': 'jsonp'}
formdef.store()
# not filled completed, no call to .autocomplete
resp = get_app(pub).get('/test/')
assert ').autocomplete({' not in resp.text
# straight URL
formdef.fields[0].data_source = {'type': 'jsonp', 'value': 'http://example.org'}
formdef.store()
resp = get_app(pub).get('/test/')
assert ').autocomplete({' in resp.text
assert 'http://example.org' in resp.text
# URL from variable
formdef.fields[0].data_source = {'type': 'jsonp', 'value': '[site_url]'}
formdef.store()
resp = get_app(pub).get('/test/')
assert ').autocomplete({' in resp.text
assert 'http://example.net' in resp.text
def test_form_string_field_autocomplete_named_datasource(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.StringField(id='0', label='string', required=False, data_source={'type': 'foobar'})
]
formdef.store()
# jsonp datasource
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {'type': 'jsonp', 'value': 'http://remote.example.net/json'}
data_source.store()
resp = get_app(pub).get('/test/')
assert ').autocomplete({' in resp.text
assert "options.url = 'http://remote.example.net/json'" in resp.text
assert "options.url = '/api/autocomplete/" not in resp.text
assert 'dataType: "jsonp",' in resp.text
# json datasource
data_source.data_source['type'] = 'json'
data_source.query_parameter = 'q'
data_source.store()
resp = get_app(pub).get('/test/')
assert ').autocomplete({' in resp.text
assert "options.url = 'http://remote.example.net/json'" not in resp.text
assert "options.url = '/api/autocomplete/" in resp.text
assert 'dataType: "json",' in resp.text
# card datasource
CardDef.wipe()
carddef = CardDef()
carddef.name = 'Foo'
carddef.fields = []
carddef.store()
data_source.data_source['type'] = 'carddef:foo'
data_source.store()
resp = get_app(pub).get('/test/')
assert ').autocomplete({' in resp.text
assert "options.url = 'http://remote.example.net/json'" not in resp.text
assert "options.url = '/api/autocomplete/" in resp.text
assert 'dataType: "json",' in resp.text
def test_form_autocomplete_named_datasource_expired_token(pub):
CardDef.wipe()
FormDef.wipe()
TransientData.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.StringField(id='0', label='string', required=False, data_source={'type': 'foobar'})
]
formdef.store()
carddef = CardDef()
carddef.name = 'Foo'
carddef.fields = []
carddef.store()
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {'type': 'carddef:foo'}
data_source.store()
resp = get_app(pub).get('/test/')
assert TransientData.count() == 1
token = TransientData.select()[0]
assert '/api/autocomplete/%s' % token.id in resp.text
# new session, check a new token is generated
resp = get_app(pub).get('/test/')
assert '/api/autocomplete/%s' % token.id not in resp.text
@pytest.mark.parametrize('sign', ['without-signature', 'with-signature'])
def test_form_autocomplete_named_datasource_cache_duration(pub, sign):
CardDef.wipe()
FormDef.wipe()
TransientData.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.StringField(id='0', label='string', required=False, data_source={'type': 'foobar'})
]
formdef.store()
url = 'http://remote.example.net/json_%s_%s' % (hashlib.sha1(pub.app_dir.encode()).hexdigest(), sign)
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {'type': 'json', 'value': url}
data_source.query_parameter = 'q'
data_source.id_parameter = 'id'
data_source.cache_duration = '1200'
data_source.store()
if sign == 'with-signature':
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
fd.write(
'''\
[wscall-secrets]
remote.example.net = 1234
'''
)
app = get_app(pub)
resp = app.get('/test/')
assert TransientData.count() == 1
token = TransientData.select()[0]
assert '/api/autocomplete/%s' % token.id in resp.text
with responses.RequestsMock() as rsps:
data = {
'data': [
{'id': '1', 'text': 'hello', 'extra': 'foo'},
{'id': '2', 'text': 'world', 'extra': 'bar'},
]
}
rsps.get(url, json=data)
resp = app.get('/api/autocomplete/%s?q=a' % token.id)
assert len(rsps.calls) == 1
assert len(resp.json['data']) == 2
resp = app.get('/api/autocomplete/%s?q=a' % token.id)
assert len(rsps.calls) == 1 # cached
assert len(resp.json['data']) == 2
resp = app.get('/api/autocomplete/%s?q=b' % token.id)
assert len(rsps.calls) == 2 # not cached
assert len(resp.json['data']) == 2
def test_form_workflow_trigger(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.fields = []
formdef.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
jump = st1.add_action('jump')
jump.trigger = 'XXX'
jump.status = 'st2'
jump2 = st1.add_action('jump')
jump2.trigger = 'YYY'
jump2.status = 'st3'
jump2.set_marker_on_status = True
workflow.add_status('Status2', 'st2')
workflow.add_status('Status3', 'st3')
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.just_created()
formdata.store()
assert formdef.data_class().get(formdata.id).status == 'wf-st1'
app = get_app(pub)
login(app, username='foo', password='foo').get('/')
app.post(formdata.get_url() + 'jump/trigger/XXX', status=403)
pub.role_class.wipe()
role = pub.role_class(name='xxx')
role.store()
jump.by = [role.id]
workflow.store()
app.post(formdata.get_url() + 'jump/trigger/XXX', status=403)
user.roles = [role.id]
user.store()
app.post(formdata.get_url() + 'jump/trigger/XXX', status=302)
formdata = formdef.data_class().get(formdata.id)
assert formdata.status == 'wf-st2'
formdata.status = 'wf-st1'
formdata.store()
app.post(formdata.get_url() + 'jump/trigger/YYY', status=403)
jump2.by = [role.id]
workflow.store()
app.post(formdata.get_url() + 'jump/trigger/YYY', status=302)
formdata = formdef.data_class().get(formdata.id)
assert formdata.status == 'wf-st3'
assert formdata.workflow_data.get('_markers_stack') == [{'status_id': 'st1'}]
formdata.status = 'wf-st1'
formdata.store()
app.post(
formdata.get_url() + 'jump/trigger/YYY',
params=json.dumps({'data': {'foo': 'bar'}}),
content_type='application/json',
)
formdata = formdef.data_class().get(formdata.id)
assert formdata.workflow_data.get('data') == {'foo': 'bar'}
def test_form_worklow_multiple_identical_status(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.fields = []
formdef.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
st1.extra_css_class = 'CSS-STATUS1'
jump = st1.add_action('jump')
jump.trigger = 'XXX'
jump.status = 'st1'
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.just_created()
formdata.store()
assert formdef.data_class().get(formdata.id).status == 'wf-st1'
app = get_app(pub)
pub.role_class.wipe()
role = pub.role_class(name='xxx')
role.allows_backoffice_access = False
role.store()
jump.by = [role.id]
workflow.store()
user.roles = [role.id]
user.store()
assert len(formdef.data_class().get(formdata.id).evolution) == 1
assert formdef.data_class().get(formdata.id).evolution[0].last_jump_datetime is None
login(app, username='foo', password='foo')
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302)
formdata = formdef.data_class().get(formdata.id)
# status is not changed: no new evolution, only a new last_jump_datetime
assert len(formdata.evolution) == 1
assert formdata.status == 'wf-st1'
assert formdata.evolution[0].last_jump_datetime is not None
assert (
formdef.data_class().get(formdata.id).get_static_substitution_variables()['form_status_changed']
is False
)
assert formdef.data_class().get(formdata.id).get_substitution_variables()['form_status_changed'] is False
# add a comment to last evolution, forcing create a new one
formdata.evolution[-1].comment = 'new-evolution-1'
formdata.store()
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302)
formdata = formdef.data_class().get(formdata.id)
assert len(formdata.evolution) == 2
assert formdata.status == 'wf-st1'
assert (
formdef.data_class().get(formdata.id).get_static_substitution_variables()['form_status_changed']
is False
)
assert formdef.data_class().get(formdata.id).get_substitution_variables()['form_status_changed'] is False
# again
formdata.evolution[-1].comment = 'new-evolution-2'
formdata.store()
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302)
# last evolution is empty, this last trigger does not create a new one
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302)
# finally, 3 evolutions: new-evolution-1, new-evolution-2, empty
formdata = formdef.data_class().get(formdata.id)
assert len(formdata.evolution) == 3
assert formdata.status == 'wf-st1'
assert formdata.evolution[0].comment == 'new-evolution-1'
assert formdata.evolution[1].comment == 'new-evolution-2'
assert formdata.evolution[2].comment is None
# mark user as owner so it can check the UI
formdata.user_id = user.id
formdata.store()
resp = app.get(formdata.get_url())
assert resp.text.count('Status1') == 2 # two in journal
assert resp.text.count('CSS-STATUS1') == 2
assert resp.text.count('new-evolution-1') == 1
assert resp.text.count('new-evolution-2') == 1
def test_form_worklow_comments_on_same_status(pub):
pub.session_manager.session_class.wipe()
user = create_user(pub)
role = pub.role_class(name='xxx')
role.allows_backoffice_access = True
role.store()
user.roles = [role.id]
user.store()
formdef = create_formdef()
formdef.fields = []
formdef.workflow_roles = {'_receiver': role.id}
formdef.store()
workflow = Workflow.get_default_workflow()
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.just_created()
formdata.store()
formdata.perform_workflow()
formdata.store()
assert formdef.data_class().get(formdata.id).status == 'wf-new'
app = get_app(pub)
assert (
formdef.data_class().get(formdata.id).get_static_substitution_variables()['form_status_changed']
is True
)
assert formdef.data_class().get(formdata.id).get_substitution_variables()['form_status_changed'] is True
login(app, username='foo', password='foo')
resp = app.get(formdata.get_url()).follow()
resp.form['comment'] = 'TEST COMMENT'
resp = resp.form.submit('button_commentable')
assert (
formdef.data_class().get(formdata.id).get_static_substitution_variables()['form_status_changed']
is False
)
assert formdef.data_class().get(formdata.id).get_substitution_variables()['form_status_changed'] is False
resp = app.get(formdata.get_url()).follow()
resp = resp.form.submit('button_accept')
assert (
formdef.data_class().get(formdata.id).get_static_substitution_variables()['form_status_changed']
is True
)
assert formdef.data_class().get(formdata.id).get_substitution_variables()['form_status_changed'] is True
def test_form_worklow_double_comments(pub):
Workflow.wipe()
create_user(pub)
wf = Workflow(name='status')
st1 = wf.add_status('Status1', 'st1')
commentable = st1.add_action('commentable', id='1')
commentable.by = [logged_users_role().id]
commentable = st1.add_action('commentable', id='2')
commentable.by = [logged_users_role().id]
wf.store()
formdef = create_formdef()
formdef.workflow = wf
formdef.store()
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.just_created()
formdata.perform_workflow()
formdata.store()
app = get_app(pub)
login(app, username='foo', password='foo')
resp = app.get(formdata.get_url())
resp.form['comment'] = 'TEST COMMENT'
resp = resp.form.submit('button_commentable').follow()
assert resp.text.count('TEST COMMENT') == 1
def test_display_message(pub):
create_user(pub)
formdef = create_formdef()
formdef.fields = []
formdef.store()
workflow = Workflow(name='test')
st0 = workflow.add_status('Status0', 'st0')
jump = st0.add_action('jump')
jump.status = 'st1'
st1 = workflow.add_status('Status1', 'st1')
display1 = st1.add_action('displaymsg')
display1.message = 'message-to-all'
display1.to = []
display2 = st1.add_action('displaymsg')
display2.message = 'message-to-submitter'
display2.to = ['_submitter']
display3 = st1.add_action('displaymsg')
display3.message = 'message-to-nobody'
display3.to = ['xxx']
display4 = st1.add_action('displaymsg')
display4.message = 'message-to-xxx-and-submitter'
display4.to = ['_submitter', 'xxx']
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
page = app.get('/test/')
page = page.forms[0].submit('submit') # form page
page = page.forms[0].submit('submit') # confirmation page
page = page.follow()
assert 'message-to-all' in page.text
assert 'message-to-submitter' in page.text
assert 'message-to-nobody' not in page.text
assert 'message-to-xxx-and-submitter' in page.text
assert page.text.index('message-to-submitter') < page.text.index('message-to-xxx-and-submitter')
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
# actions alert vs top alert
display2.position = 'actions'
workflow.store()
page = app.get(formdata.get_url())
assert 'message-to-all' in page.text
assert 'message-to-submitter' not in page.text
assert 'message-to-xxx-and-submitter' in page.text
# add an action, so display2 will appear again
jump1 = st1.add_action('choice', id='_jump1')
jump1.label = 'Jump 1'
jump1.by = ['_submitter']
jump1.status = st1.id
workflow.store()
page = app.get(formdata.get_url())
assert 'message-to-all' in page.text
assert 'message-to-submitter' in page.text
assert 'message-to-xxx-and-submitter' in page.text
assert page.text.index('message-to-submitter') > page.text.index('message-to-xxx-and-submitter')
jump1.by = ['xxx']
workflow.store()
page = app.get(formdata.get_url())
assert 'message-to-all' in page.text
assert 'message-to-submitter' not in page.text
assert 'message-to-xxx-and-submitter' in page.text
# change to always display at the bottom
display2.position = 'bottom'
workflow.store()
page = app.get(formdata.get_url())
assert 'message-to-all' in page.text
assert 'message-to-submitter' in page.text
assert 'message-to-xxx-and-submitter' in page.text
assert page.text.index('message-to-submitter') > page.text.index('message-to-xxx-and-submitter')
# set a level
display2.level = 'warning'
workflow.store()
page = app.get(formdata.get_url())
assert 'warningnotice' in page.text
# check message is not displayed if status is not visible to user
st1.visibility = ['_receiver']
workflow.store()
page = app.get(formdata.get_url())
assert 'warningnotice' not in page.text
def test_workflow_condition_on_message(pub):
create_user(pub)
formdef = create_formdef()
formdef.fields = []
formdef.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
display1 = st1.add_action('displaymsg')
display1.message = 'message-to-all'
display1.to = []
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
page = app.get('/test/')
page = page.forms[0].submit('submit') # form page
page = page.forms[0].submit('submit') # confirmation page
page = page.follow()
assert 'message-to-all' in page.text
formdata = formdef.data_class().select()[0]
page = app.get(formdata.get_url())
assert 'message-to-all' in page.text
display1.condition = {'type': 'django', 'value': 'xxx'}
workflow.store()
page = app.get(formdata.get_url())
assert 'message-to-all' not in page.text
def test_workflow_message_with_template_error(pub):
create_user(pub)
formdef = create_formdef()
formdef.fields = []
formdef.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
display1 = st1.add_action('displaymsg')
display1.message = '<p>{% for x in 0 %}crash{% endfor %}'
display1.to = []
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/test/')
resp = resp.forms[0].submit('submit') # form page
resp = resp.forms[0].submit('submit') # confirmation page
resp = resp.follow()
assert 'Error rendering message.' in resp.text
assert pub.loggederror_class.count() == 1
logged_error = pub.loggederror_class.select()[0]
assert logged_error.summary == "Error in template of workflow message ('int' object is not iterable)"
def test_session_cookie_flags(pub):
create_formdef()
app = get_app(pub)
resp = app.get('/test/', status=200)
assert resp.headers['Set-Cookie'].strip().startswith('sessionid-')
assert 'HttpOnly' in resp.headers['Set-Cookie']
assert 'Secure' not in resp.headers['Set-Cookie']
app = get_app(pub, https=True)
resp = app.get('/test/', status=200)
assert resp.headers['Set-Cookie'].strip().startswith('sessionid-')
assert 'HttpOnly' in resp.headers['Set-Cookie']
assert 'Secure' in resp.headers['Set-Cookie']
def test_form_worklow_multiple_identical_status_with_journal_error(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.fields = []
formdef.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
jump = st1.add_action('jump')
jump.trigger = 'XXX'
jump.status = 'st1'
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.just_created()
formdata.store()
assert formdef.data_class().get(formdata.id).status == 'wf-st1'
app = get_app(pub)
login(app, username='foo', password='foo')
pub.role_class.wipe()
role = pub.role_class(name='xxx')
role.allows_backoffice_access = False
role.store()
jump.by = [role.id]
workflow.store()
user.roles = [role.id]
user.store()
for _i in range(3):
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302)
formdata = formdef.data_class().get(formdata.id)
formdata.evolution[-1].add_part(JournalWsCallErrorPart('bla', 'bla', {}))
formdata.evolution[-1].add_part(JournalAssignationErrorPart('foo', 'foo'))
formdata.store()
# mark user as owner so it can check the UI
formdata.user_id = user.id
formdata.store()
resp = app.get(formdata.get_url())
assert resp.text.count('<li class="msg') == 1
role.allows_backoffice_access = True
role.store()
resp = app.get(formdata.get_url(backoffice=True))
assert len(resp.pyquery('div.msg')) == 3
assert len(resp.pyquery('div.msg div.ws-error')) == 3
assert len(resp.pyquery('div.msg div.assignation-error')) == 3
def test_form_data_keywords(pub):
formdef = create_formdef()
formdef.keywords = 'hello,world'
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert 'data-keywords="hello world"' in resp.text
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'data-keywords="hello world"' in resp.text
resp = resp.form.submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
def test_logged_errors(pub):
Workflow.wipe()
workflow = Workflow.get_default_workflow()
workflow.id = '12'
st1 = workflow.possible_status[0]
jump = st1.add_action('jump', id='_jump', prepend=True)
jump.id = '_jump'
jump.status = 'rejected'
jump.condition = {'type': 'python', 'value': '1//0'} # ZeroDivisionError
workflow.store()
FormDef.wipe()
formdef = FormDef()
formdef.id = '34'
formdef.workflow = workflow
formdef.name = 'test'
formdef.confirmation = False
formdef.fields = []
formdef.store()
app = get_app(pub)
resp = app.get('/test/')
resp = resp.form.submit('submit').follow()
resp = resp.form.submit('submit')
assert pub.loggederror_class.count() == 1
# new expression, but raise the same exception (ZeroDivisionError),
# just update the created logged error
jump.condition = {'type': 'python', 'value': '2//0'}
workflow.store()
resp = app.get('/test/')
resp = resp.form.submit('submit').follow()
resp = resp.form.submit('submit')
assert pub.loggederror_class.count() == 1
error = list(
pub.loggederror_class.select(
[
Equal(
'tech_id',
'34-12-just_submitted-_jump-failed-to-evaluate-condition-ZeroDivisionError-integer-division-or-modulo-by-zero',
)
]
)
)[0]
assert error.occurences_count == 2
assert error.expression == '2//0'
assert pub.loggederror_class.count([Equal('formdef_id', '34')]) == 1
assert pub.loggederror_class.count([Equal('formdef_id', 'X')]) == 0
assert pub.loggederror_class.count([Equal('workflow_id', '12')]) == 1
assert pub.loggederror_class.count([Equal('workflow_id', 'X')]) == 0
def test_resubmit(pub):
user = create_user(pub)
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [fields.StringField(id='1', label='string', varname='toto')]
formdef.store()
formdef2 = FormDef()
formdef2.name = 'form title bis'
formdef2.enable_tracking_codes = True
formdef2.fields = [
fields.StringField(id='1', label='string', varname='titi'),
fields.StringField(id='2', label='string', varname='toto'),
]
formdef2.store()
wf = Workflow(name='resubmit')
st1 = wf.add_status('Status1')
st2 = wf.add_status('Status2')
resubmit = st1.add_action('resubmit', id='_resubmit')
resubmit.by = ['_submitter']
resubmit.formdef_slug = formdef2.url_name
jump = st1.add_action('jumponsubmit', id='_jump')
jump.status = st2.id
register_comment = st2.add_action('register-comment', id='_register')
register_comment.comment = '<p><a href="[resubmit_formdata_draft_url]">new draft</a></p>'
wf.store()
formdef.workflow_id = wf.id
formdef.store()
formdef2.data_class().wipe()
formdata = formdef.data_class()()
formdata.user_id = user.id
formdata.just_created()
formdata.data = {'1': 'XXX'}
formdata.store()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdata.get_url())
resp = resp.form.submit('button_resubmit')
resp = resp.follow()
assert 'new draft' in resp.text
assert formdef2.data_class().select()[0].status == 'draft'
assert formdef2.data_class().select()[0].data.get('1') is None
assert formdef2.data_class().select()[0].data.get('2') == 'XXX'
resp = resp.click('new draft')
resp = resp.follow()
assert resp.forms[1]['f2'].value == 'XXX'
# anonymous
formdef2.data_class().wipe()
app = get_app(pub)
resp = app.get('/form-title/')
resp.form['f1'] = 'foo'
resp = resp.form.submit('submit') # -> validation
resp = resp.form.submit('submit') # -> submission
resp = resp.follow()
resp = resp.form.submit('button_resubmit')
resp = resp.follow()
assert 'new draft' in resp.text
assert formdef2.data_class().select()[0].status == 'draft'
assert formdef2.data_class().select()[0].data.get('1') is None
assert formdef2.data_class().select()[0].data.get('2') == 'foo'
resp = resp.click('new draft')
resp = resp.follow()
assert resp.forms[1]['f2'].value == 'foo'
def test_form_custom_select_template(pub):
formdef = create_formdef()
formdef.fields = [
fields.ItemField(id='1', label='select', required=True, varname='foo', items=['Foo', 'Bar', 'Baz'])
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert 'TEST TEMPLATE' not in resp.text
formdef.fields[0].extra_css_class = 'template-test'
formdef.store()
resp = get_app(pub).get('/test/')
assert 'TEST TEMPLATE' in resp.text
# make sure request is available in context
assert '<!-- backoffice: False -->' in resp.text
assert '<!-- backoffice compat: False -->' in resp.text
# test for substitution variables being available
if not pub.site_options.has_section('variables'):
pub.site_options.add_section('variables')
pub.site_options.set('variables', 'example_url', 'http://remote.example.net/')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = get_app(pub).get('/test/')
assert 'substitution variable: http://remote.example.net/' in resp.text
def test_form_status_appearance_keywords(pub):
create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
formdef = create_formdef()
formdef.fields = [
fields.ItemField(id='1', label='select', required=True, varname='foo', items=['Foo', 'Bar', 'Baz'])
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.store()
root = PublicFormStatusPage(formdef, formdata, register_workflow_subdirs=False)
template_names = root.get_formdef_template_variants(root.status_templates)
assert list(template_names) == root.status_templates
formdef.appearance_keywords = 'foobar plop'
formdef.store()
template_names = root.get_formdef_template_variants(root.status_templates)
assert list(template_names) == [
'wcs/front/appearance-foobar/formdata_status.html',
'wcs/front/appearance-plop/formdata_status.html',
'wcs/front/formdata_status.html',
'wcs/appearance-foobar/formdata_status.html',
'wcs/appearance-plop/formdata_status.html',
'wcs/formdata_status.html',
]
resp = get_app(pub).get('/test/')
assert 'class="quixote foobar plop"' in resp.text
def test_user_global_action(pub):
create_user(pub)
workflow = Workflow.get_default_workflow()
workflow.id = '2'
action = workflow.add_global_action('FOOBAR')
register_comment = action.add_action('register-comment')
register_comment.comment = 'HELLO WORLD GLOBAL ACTION'
jump = action.add_action('jump')
jump.status = 'finished'
trigger = action.triggers[0]
workflow.store()
formdef = FormDef()
formdef.name = 'test global action'
formdef.fields = []
formdef.workflow_id = workflow.id
formdef.workflow_roles = {}
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
resp = app.get(formdata.get_url())
assert 'button-action-1' not in resp.text
trigger.roles = ['_submitter']
workflow.store()
WorkflowTrace.wipe()
resp = app.get(formdata.get_url())
assert 'button-action-1' in resp.form.fields
resp = resp.form.submit('button-action-1')
resp = app.get(formdata.get_url())
assert 'HELLO WORLD GLOBAL ACTION' in resp.text
assert formdef.data_class().get(formdata.id).status == 'wf-finished'
trace_event, trace_action = WorkflowTrace.select_for_formdata(formdata)[:2]
assert (
trace_action.get_base_url(workflow, trace_action.status_id, trace_event)
== 'http://example.net/backoffice/workflows/2/global-actions/1/'
)
def test_user_global_action_same_status_store(pub):
create_user(pub)
workflow = Workflow.get_default_workflow()
workflow.id = '2'
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.StringField(id='bo1', label='bo field 1'),
]
action = workflow.add_global_action('FOOBAR')
jump = action.add_action('jump')
jump.status = 'new'
trigger = action.triggers[0]
trigger.roles = ['_submitter']
new_status = workflow.possible_status[1]
setbo = new_status.add_action('set-backoffice-fields', prepend=True)
setbo.fields = [{'field_id': 'bo1', 'value': '123'}]
workflow.store()
formdef = FormDef()
formdef.name = 'test global action'
formdef.fields = []
formdef.workflow_id = workflow.id
formdef.workflow_roles = {}
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data['bo1'] == '123'
# change global action
setbo.fields = [{'field_id': 'bo1', 'value': '321'}]
workflow.store()
resp = app.get(formdata.get_url())
assert 'button-action-1' in resp.form.fields
resp = resp.form.submit('button-action-1') # click global action
# check status actions are rerun
resp = app.get(formdata.get_url())
assert formdef.data_class().get(formdata.id).status == 'wf-new'
assert formdef.data_class().get(formdata.id).data['bo1'] == '321'
def test_anonymous_user_global_action(pub):
workflow = Workflow.get_default_workflow()
workflow.id = '2'
action = workflow.add_global_action('FOOBAR')
register_comment = action.add_action('register-comment')
register_comment.comment = 'HELLO WORLD GLOBAL ACTION'
jump = action.add_action('jump')
jump.status = 'finished'
trigger = action.triggers[0]
trigger.roles = ['_submitter']
workflow.store()
formdef = FormDef()
formdef.name = 'test global action'
formdef.fields = []
formdef.enable_tracking_codes = True
formdef.workflow_id = workflow.id
formdef.workflow_roles = {}
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get(formdef.get_url())
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
app.cookiejar.clear()
resp = app.get('/')
resp.forms[0]['code'] = formdata.tracking_code
resp = resp.forms[0].submit().follow().follow()
assert 'button-action-1' in resp.form.fields
resp = resp.form.submit('button-action-1')
resp = app.get(formdata.get_url())
assert 'HELLO WORLD GLOBAL ACTION' in resp.text
assert formdef.data_class().get(formdata.id).status == 'wf-finished'
def test_condition_on_action(pub, emails):
create_user(pub)
workflow = Workflow.get_default_workflow()
# change email subjects to differentiate them
workflow.possible_status[0].items[0].subject = 'New form ([name])'
workflow.possible_status[0].items[1].subject = 'New form2 ([name])'
workflow.id = '2'
workflow.store()
formdef = FormDef()
formdef.name = 'test condition on action'
formdef.fields = []
formdef.workflow_id = workflow.id
formdef.workflow_roles = {}
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
assert not emails.get('New form (test condition on action)') # no receiver
assert emails.get('New form2 (test condition on action)') # submitter
emails.empty()
workflow.possible_status[0].items[1].condition = {'type': 'python', 'value': 'False'}
workflow.store()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
assert not emails.get('New form2 (test condition on action)')
# check with a condition on field data
formdef.fields = [fields.StringField(id='0', label='string', varname='foobar')]
formdef.store()
workflow.possible_status[0].items[1].condition = {'type': 'django', 'value': 'form_var_foobar'}
workflow.store()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
resp.form['f0'] = ''
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
assert not emails.get('New form2 (test condition on action)')
# check with condition evaluating positively
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
resp.form['f0'] = 'toto'
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
assert emails.get('New form2 (test condition on action)')
def test_user_global_action_along_form(pub):
# check it's possible to click on a global action button even if there's a
# form with required fields.
create_user(pub)
workflow = Workflow.get_default_workflow()
workflow.id = '2'
action = workflow.add_global_action('FOOBAR')
register_comment = action.add_action('register-comment')
register_comment.comment = 'HELLO WORLD GLOBAL ACTION'
jump = action.add_action('jump')
jump.status = 'finished'
trigger = action.triggers[0]
trigger.roles = ['_submitter']
status = workflow.get_status('new')
display_form = status.add_action('form', id='_x')
display_form.id = '_x'
display_form.by = ['_submitter']
display_form.varname = 'xxx'
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
display_form.formdef.fields.append(fields.StringField(id='1', label='blah', required=True))
workflow.store()
formdef = FormDef()
formdef.name = 'test global action'
formdef.fields = []
formdef.workflow_id = workflow.id
formdef.workflow_roles = {}
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
resp = app.get(formdata.get_url())
assert resp.form['fxxx_1'].attrs['aria-required'] == 'true'
assert 'button-action-1' in resp.form.fields
resp = resp.form.submit('button-action-1')
resp = app.get(formdata.get_url())
assert 'HELLO WORLD GLOBAL ACTION' in resp.text
assert formdef.data_class().get(formdata.id).status == 'wf-finished'
def test_user_global_action_specific_statuses(pub):
user = create_user(pub)
workflow = Workflow.get_default_workflow()
workflow.id = '2'
action = workflow.add_global_action('FOOBAR')
register_comment = action.add_action('register-comment')
register_comment.comment = 'HELLO WORLD GLOBAL ACTION'
trigger = action.triggers[0]
trigger.roles = ['_submitter']
workflow.store()
formdef = FormDef()
formdef.name = 'test global action'
formdef.fields = []
formdef.workflow_id = workflow.id
formdef.workflow_roles = {}
formdef.store()
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.user_id = user.id
formdata.just_created()
formdata.store()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdata.get_url())
assert 'button-action-1' in resp.text
trigger.statuses = ['accepted']
workflow.store()
resp = app.get(formdata.get_url())
assert 'button-action-1' not in resp.text
formdata.jump_status('accepted')
formdata.store()
resp = app.get(formdata.get_url())
assert 'button-action-1' in resp.form.fields
resp = resp.form.submit('button-action-1')
resp = app.get(formdata.get_url())
assert 'HELLO WORLD GLOBAL ACTION' in resp.text
def test_email_actions(pub, emails):
create_user(pub)
workflow = Workflow.get_default_workflow()
workflow.id = '2'
# change email subjects to differentiate them
workflow.possible_status[0].items[0].subject = 'New form ([name])'
workflow.possible_status[0].items[1].subject = 'New form2 ([name])'
workflow.possible_status[0].items[
1
].body = 'Hello; {% action_button "do-accept" label="Accepté!" %} Adiós.'
workflow.possible_status[1].items[1].identifier = 'do-accept'
workflow.store()
formdef = FormDef()
formdef.name = 'test email action'
formdef.fields = []
formdef.workflow_id = workflow.id
formdef.workflow_roles = {'_receiver': 1}
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
email_data = emails.get('New form2 (test email action)')
action_url = re.findall(r'http.* ', email_data['payload'])[0].strip()
assert '/actions/' in action_url
if docutils:
assert len(email_data['payloads']) == 2
assert action_url in force_str(email_data['payloads'][1])
app = get_app(pub)
resp = app.get(action_url)
assert 'Accepté!' in resp.text
resp = resp.form.submit()
assert 'The action has been confirmed.' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.status == 'wf-accepted'
assert [x.event for x in formdata.get_workflow_traces() if x.event][-1] == 'email-button'
# action token has been used, it will now return a custom 404
resp = app.get(action_url, status=404)
assert 'This action link has already been used or has expired.' in resp.text
# check against independently changed status, it should also return a
# custom 404.
emails.empty()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
email_data = emails.get('New form2 (test email action)')
action_url = re.findall(r'http.* ', email_data['payload'])[0].strip()
formdata = formdef.data_class().select()[0]
formdata.jump_status('rejected')
app = get_app(pub)
resp = app.get(action_url, status=404)
assert 'This action link has already been used or has expired.' in resp.text
# check action link referencing a deleted formdata
emails.empty()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
email_data = emails.get('New form2 (test email action)')
action_url = re.findall(r'http.* ', email_data['payload'])[0].strip()
formdata = formdef.data_class().select()[0]
formdata.remove_self()
app = get_app(pub)
resp = app.get(action_url, status=404)
assert 'This action link is no longer valid' in resp.text
# two buttons on the same line, two urls
workflow.possible_status[0].items[
1
].body = '{% action_button "ok" label="OK" %}{% action_button "ko" label="KO" %} '
workflow.store()
emails.empty()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
email_data = emails.get('New form2 (test email action)')
assert len(re.findall(r'http.*?\s', email_data['payload'])) == 2
# custom messages
workflow.possible_status[0].items[
1
].body = 'Hello {% action_button "do-accept" label="ok" message="FOOmessageBAR" done_message="FOOdoneBAR" %} bye.'
workflow.store()
emails.empty()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
email_data = emails.get('New form2 (test email action)')
action_url = re.findall(r'http.* ', email_data['payload'])[0].strip()
app = get_app(pub)
resp = app.get(action_url)
assert 'FOOmessageBAR' in resp.text
resp = resp.form.submit()
assert 'FOOdoneBAR' in resp.text
def test_card_email_actions(pub, emails):
create_user(pub)
workflow = Workflow.get_default_workflow()
workflow.id = '2'
workflow.possible_status[0].items[0].subject = None # disable first mail
workflow.possible_status[0].items[1].subject = 'New card'
workflow.possible_status[0].items[1].body = 'XXX {% action_button "do-accept" label="Accepté!" %}'
workflow.possible_status[0].items[1].to = ['test@example.net'] # force recipient
workflow.possible_status[1].items[1].identifier = 'do-accept'
workflow.store()
carddef = CardDef()
carddef.name = 'test email action'
carddef.fields = []
carddef.workflow_id = workflow.id
carddef.workflow_roles = {'_receiver': 1}
carddef.store()
carddef.data_class().wipe()
carddata = carddef.data_class()()
carddata.just_created()
carddata.store()
carddata.perform_workflow()
carddata.store()
email_data = emails.get('New card')
action_url = re.findall(r'\shttp.*\s', email_data['payload'])[0].strip()
assert '/actions/' in action_url
if docutils:
assert len(email_data['payloads']) == 2
assert action_url in force_str(email_data['payloads'][1])
app = get_app(pub)
resp = app.get(action_url)
assert 'Accepté!' in resp.text
resp = resp.form.submit()
assert 'The action has been confirmed.' in resp.text
assert carddef.data_class().count() == 1
carddata = carddef.data_class().select()[0]
assert carddata.status == 'wf-accepted'
def test_manager_public_access(pub):
user, manager = create_user_and_admin(pub)
pub.role_class.wipe()
role = pub.role_class(name='xxx')
role.allows_backoffice_access = True
role.store()
manager.is_admin = False
manager.roles = [role.id]
manager.store()
assert manager.can_go_in_backoffice()
formdef = create_formdef()
formdef.workflow_roles = {'_receiver': role.id}
formdef.store()
formdata = formdef.data_class()()
formdata.user_id = user.id
formdata.data = {}
formdata.just_created()
formdata.store()
# user access to own formdata
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdata.get_url())
assert 'The form has been recorded' in resp.text
# agent access to formdata
app = login(get_app(pub), username='admin', password='admin')
resp = app.get(formdata.get_url())
assert resp.location == formdata.get_url(backoffice=True)
resp = resp.follow()
assert 'The form has been recorded' in resp.text
# agent access to an unauthorized formdata
formdef.workflow_roles = {'_receiver': None}
formdef.store()
resp = app.get(formdata.get_url(), status=403)
# agent access via a tracking code (stays in frontoffice)
formdef.workflow_roles = {'_receiver': role.id}
formdef.enable_tracking_codes = True
formdef.store()
code = pub.tracking_code_class()
code.formdata = formdata
code.store()
resp = app.get('/code/%s/load' % code.id)
resp = resp.follow() # -> /test/1/
assert 'backoffice' not in resp.request.path
assert 'The form has been recorded' in resp.text
# authorized access but not backoffice access
app = login(get_app(pub), username='admin', password='admin') # reset session
resp = app.get(formdata.get_url())
assert resp.location == formdata.get_url(backoffice=True) # check tracking code is no longer effective
role.allows_backoffice_access = False
role.store()
resp = app.get(formdata.get_url())
assert 'The form has been recorded' in resp.text
# agent access to own formdata (stays in frontoffice)
formdata = formdef.data_class()()
formdata.user_id = manager.id
formdata.data = {}
formdata.just_created()
formdata.store()
resp = app.get(formdata.get_url())
assert 'The form has been recorded' in resp.text
def test_form_and_category_same_slug(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'foobar'
formdef.fields = []
formdef.store()
# check we get to the form, not the category
resp = get_app(pub).get('/foobar/')
assert resp.form
def test_field_condition(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.StringField(
id='1',
label='Bar',
size='40',
required=True,
condition={'type': 'django', 'value': '1'},
),
fields.StringField(
id='2',
label='Foo',
size='40',
required=True,
condition={'type': 'django', 'value': '0'},
),
]
formdef.store()
resp = get_app(pub).get('/foo/')
assert 'f1' in resp.form.fields
assert 'f2' not in resp.form.fields
resp.form['f1'] = 'hello'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'name="f1"' in resp.text
assert 'name="f2"' not in resp.text
resp = resp.form.submit('submit')
resp = resp.follow()
assert 'Bar' in [x.text for x in resp.pyquery('p.label')]
assert 'Foo' not in [x.text for x in resp.pyquery('p.label')]
def test_field_unicode_condition(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.PageField(id='0', label='2nd page'),
fields.StringField(id='1', label='Bar', size='40', required=True, varname='bar'),
fields.PageField(id='3', label='1st page'),
fields.StringField(
id='4',
label='Baz',
size='40',
required=True,
varname='baz',
condition={'type': 'django', 'value': 'form_var_bar == "éléphant"'},
),
]
formdef.store()
resp = get_app(pub).get('/foo/')
resp.form['f1'] = 'hello'
resp = resp.form.submit('submit')
assert 'f4' not in resp.form.fields
resp = get_app(pub).get('/foo/')
resp.form['f1'] = 'éléphant'
resp = resp.form.submit('submit')
assert 'f4' in resp.form.fields
def test_field_unicode_condition_contains_in_list(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.PageField(id='0', label='2nd page'),
fields.StringField(id='1', label='Bar', size='40', required=True, varname='bar'),
fields.PageField(id='3', label='1st page'),
fields.StringField(
id='4',
label='Baz',
size='40',
required=True,
varname='baz',
condition={'type': 'django', 'value': 'form_var_bar in "éléphant"|split'},
),
]
formdef.store()
resp = get_app(pub).get('/foo/')
resp.form['f1'] = 'hello'
resp = resp.form.submit('submit')
assert 'f4' not in resp.form.fields
resp = get_app(pub).get('/foo/')
resp.form['f1'] = 'éléphant'
resp = resp.form.submit('submit')
assert 'f4' in resp.form.fields
def test_field_unicode_condition_contains_in_string(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.PageField(id='0', label='2nd page'),
fields.StringField(id='1', label='Bar', size='40', required=True, varname='bar'),
fields.PageField(id='3', label='1st page'),
fields.StringField(
id='4',
label='Baz',
size='40',
required=True,
varname='baz',
condition={'type': 'django', 'value': '"éléphant" in form_var_bar'},
),
]
formdef.store()
resp = get_app(pub).get('/foo/')
resp.form['f1'] = 'hello'
resp = resp.form.submit('submit')
assert 'f4' not in resp.form.fields
resp = get_app(pub).get('/foo/')
resp.form['f1'] = 'éléphant'
resp = resp.form.submit('submit')
assert 'f4' in resp.form.fields
def test_field_unicode_condition_in_array(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.ItemsField(
id='1',
label='items',
required=True,
varname='foo',
items=['Pomme', 'Poire', 'Pêche', 'Abricot'],
),
fields.PageField(id='2', label='2nd page'),
fields.StringField(
id='3',
label='Baz',
size='40',
required=True,
varname='baz',
condition={'type': 'django', 'value': '"Pêche" in form_var_foo'},
),
fields.CommentField(id='4', label='{{form_var_foo}}'),
fields.CommentField(id='5', label='{% if "Pêche" in form_var_foo %}CHECK OK{% endif %}'),
]
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
display1 = st1.add_action('displaymsg')
display1.message = 'Message {% if "Pêche" in form_var_foo %}CHECK OK{% endif %}'
display1.to = []
workflow.store()
formdef.workflow = workflow
formdef.store()
resp = get_app(pub).get('/foo/')
resp.form['f1$element1'].checked = True
resp = resp.form.submit('submit')
assert 'f3' not in resp.form.fields
resp = get_app(pub).get('/foo/')
resp.form['f1$element1'].checked = True
resp.form['f1$element2'].checked = True
resp = resp.form.submit('submit')
assert 'f3' in resp.form.fields # check it's ok in field condition
resp.form['f3'] = 'hop'
assert '>Poire, Pêche<' in resp.text # check it's displayed correctly
assert 'CHECK OK' in resp.text # check it's ok in template condition
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
resp = resp.form.submit('submit').follow()
assert '<p>Message CHECK OK</p>' in resp.text # check it's ok in workflow template
def test_form_edit_and_backoffice_field_change(pub):
create_user(pub)
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string', varname='foo'),
fields.PageField(id='2', label='2nd page'),
]
formdef.store()
formdef.data_class().wipe()
Workflow.wipe()
workflow = Workflow(name='test')
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.StringField(id='bo1', label='bo field 1', varname='plop'),
]
st1 = workflow.add_status('Status1', 'st1')
setbo = st1.add_action('set-backoffice-fields')
setbo.fields = [{'field_id': 'bo1', 'value': '=form_var_foo'}]
setbo2 = st1.add_action('set-backoffice-fields')
setbo2.fields = [{'field_id': 'bo1', 'value': '="foo" + form_var_plop'}]
jump = st1.add_action('jump')
jump.status = 'st2'
st2 = workflow.add_status('Status2', 'st2')
editable = st2.add_action('editable', id='_editable')
editable.by = ['_submitter']
editable.status = st1.id
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp.form['f1'] = 'bar'
resp = resp.form.submit('submit') # -> page 2
resp = resp.form.submit('submit') # -> validation
resp = resp.form.submit('submit').follow() # -> submitted
assert 'The form has been recorded' in resp.text
data_id = formdef.data_class().select()[0].id
assert formdef.data_class().get(data_id).data['bo1'] == 'foobar'
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/test/%s/' % data_id)
assert 'button_editable-button' in resp.text
resp = resp.form.submit('button_editable')
resp = resp.follow()
assert resp.form['f1'].value == 'bar'
resp.form['f1'].value = 'baz'
resp = resp.form.submit('submit') # -> page 2
resp = resp.form.submit('submit').follow() # -> saved
assert formdef.data_class().get(data_id).data['bo1'] == 'foobaz'
def test_backoffice_fields_just_after_conditional_form_submit(pub):
"""
simulate selection of a structured list via condition on form,
followed by an evaluation on workflow in order to get structured value
from the selected list.
ie: test unfeed on ConditionVars
"""
Workflow.wipe()
workflow = Workflow(name='test')
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.StringField(id='bo1', label='first text', varname='both_text'),
fields.StringField(id='bo2', label='first more', varname='both_more'),
]
st1 = workflow.add_status('Status1', 'st1')
setbo = st1.add_action('set-backoffice-fields')
setbo.fields = [
{'field_id': 'bo1', 'value': '{{ form_var_listA }} vs {{ form_var_listB }}'},
{'field_id': 'bo2', 'value': '{{ form_var_listA_more }} vs {{ form_var_listB_more }}'},
]
workflow.store()
items_A = [{'id': '1', 'text': 'A1', 'more': 'moreA1'}]
items_B = [{'id': '1', 'text': 'B1', 'more': 'moreB1'}, {'id': '2', 'text': 'B2', 'more': 'moreB2'}]
formdef = create_formdef()
formdef.fields = [
fields.ItemField(id='1', varname='choice', items=['A', 'B'], label='list to choice'),
fields.ItemField(
id='2',
varname='listA',
label='list A',
data_source={'type': 'jsonvalue', 'value': json.dumps(items_A)},
condition={'type': 'python', 'value': 'form_var_choice_raw == "A"'},
),
fields.ItemField(
id='3',
varname='listB',
label='list B',
data_source={'type': 'jsonvalue', 'value': json.dumps(items_B)},
condition={'type': 'python', 'value': 'form_var_choice_raw == "B"'},
),
]
formdef.confirmation = False
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
create_user_and_admin(pub)
resp = get_app(pub).get('/test/')
resp.form['f1'].value = 'B'
resp.form['f2'].value = '1'
resp.form['f3'].value = '2'
resp = resp.form.submit('submit').follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data['1'] == 'B'
assert formdata.data.get('2') is None
assert formdata.data['3'] == '2'
assert formdata.data['bo1'] == 'None vs B2'
assert formdata.data['bo2'] == 'vs moreB2'
def test_backoffice_fields_just_after_conditional_form_edit_action(pub):
"""
test unfeed on ConditionVars within edit context
"""
Workflow.wipe()
workflow = Workflow(name='test')
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.StringField(id='bo1', label='both text', varname='both_text'),
fields.StringField(id='bo2', label='both more', varname='both_more'),
]
st1 = workflow.add_status('Status1', 'st1')
setbo = st1.add_action('set-backoffice-fields')
setbo.fields = [
{'field_id': 'bo1', 'value': '{{ form_var_listA }} vs {{ form_var_listB }}'},
{'field_id': 'bo2', 'value': '{{ form_var_listA_more }} vs {{ form_var_listB_more }}'},
]
editable = st1.add_action('editable', id='_editable')
editable.by = ['_submitter']
editable.status = st1.id
workflow.store()
items_A = [{'id': '1', 'text': 'A1', 'more': 'moreA1'}]
items_B = [{'id': '1', 'text': 'B1', 'more': 'moreB1'}, {'id': '2', 'text': 'B2', 'more': 'moreB2'}]
formdef = create_formdef()
formdef.fields = [
fields.ItemField(id='1', varname='choice', items=['A', 'B'], label='list to choice'),
fields.ItemField(
id='2',
varname='listA',
label='list A',
data_source={'type': 'jsonvalue', 'value': json.dumps(items_A)},
condition={'type': 'python', 'value': 'form_var_choice_raw == "A"'},
),
fields.ItemField(
id='3',
varname='listB',
label='list B',
data_source={'type': 'jsonvalue', 'value': json.dumps(items_B)},
condition={'type': 'python', 'value': 'form_var_choice_raw == "B"'},
),
]
formdef.confirmation = False
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
create_user(pub)
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp.form['f1'].value = 'B'
resp.form['f2'].value = '1'
resp.form['f3'].value = '2'
resp = resp.form.submit('submit').follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data['1'] == 'B'
assert formdata.data.get('2') is None
assert formdata.data['3'] == '2'
# check unfeed on FormPage::submitted()
assert formdata.data['bo1'] == 'None vs B2'
assert formdata.data['bo2'] == 'vs moreB2'
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/test/%s/' % formdata.id)
assert 'button_editable-button' in resp.text
resp = resp.form.submit('button_editable').follow()
assert resp.form['f1'].value == 'B'
resp.form['f1'].value = 'A'
resp = resp.form.submit('submit').follow() # -> saved
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data['1'] == 'A'
assert formdata.data['2'] == '1'
assert formdata.data.get('3') is None
# check unfeed on FormPage::submitted_existing()
assert formdata.data['bo1'] == 'A1 vs None'
assert formdata.data['bo2'] == 'moreA1 vs'
def test_backoffice_fields_set_from_live(pub):
carddef = CardDef()
carddef.name = 'items'
carddef.digest_templates = {'default': '{{form_var_name}}'}
carddef.fields = [
fields.StringField(id='0', label='string', varname='name'),
fields.StringField(id='1', label='string', varname='attr'),
]
carddef.store()
for i, value in enumerate(['foo', 'bar', 'baz']):
carddata = carddef.data_class()()
carddata.data = {
'0': value,
'1': 'attr%s' % i,
}
carddata.just_created()
carddata.store()
Workflow.wipe()
workflow = Workflow(name='test')
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.StringField(id='bo1', label='first text', varname='both_text'),
fields.StringField(id='bo2', label='first more', varname='both_more'),
]
st1 = workflow.add_status('Status1', 'st1')
setbo = st1.add_action('set-backoffice-fields')
setbo.fields = [
{'field_id': 'bo1', 'value': '{{ form_var.foo.attr }}'},
{'field_id': 'bo2', 'value': '{{ form_var.foo.live.var.attr }}'},
]
workflow.store()
ds = {'type': 'carddef:%s' % carddef.url_name}
formdef = create_formdef()
formdef.fields = [
fields.ItemField(id='1', label='string', varname='foo', data_source=ds, display_disabled_items=True)
]
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
create_user_and_admin(pub)
resp = get_app(pub).get('/test/')
resp.form['f1'].value = '2'
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submit
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data['1'] == '2'
assert formdata.data['bo1'] == 'attr1'
assert formdata.data['bo2'] == 'attr1'
def test_form_recall_draft(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/test/')
assert 'You already started to fill this form.' not in resp.text
draft = formdef.data_class()()
draft.user_id = user.id
draft.status = 'draft'
draft.data = {}
draft.store()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/test/')
assert 'You already started to fill this form.' in resp.text
assert 'href="%s/"' % draft.id in resp.text
draft2 = formdef.data_class()()
draft2.user_id = user.id
draft2.status = 'draft'
draft2.data = {}
draft2.store()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/test/')
assert 'You already started to fill this form.' in resp.text
assert 'href="%s/"' % draft.id in resp.text
assert 'href="%s/"' % draft2.id in resp.text
def test_choice_button_ignore_form_errors(pub):
create_user(pub)
formdef = create_formdef()
formdef.roles = [logged_users_role().id]
formdef.store()
wf = Workflow(name='status')
st1 = wf.add_status('Status1', 'st1')
st2 = wf.add_status('Status2', 'st2')
commentable = st1.add_action('commentable', id='_commentable')
commentable.by = [logged_users_role().id]
commentable.required = True
choice = st1.add_action('choice', id='_x1')
choice.label = 'Submit'
choice.by = [logged_users_role().id]
choice.status = st2.id
choice2 = st1.add_action('choice', id='_x2')
choice2.label = 'Submit no check'
choice2.by = [logged_users_role().id]
choice2.status = st2.id
choice2.ignore_form_errors = True
wf.store()
formdef.workflow = wf
formdef.store()
# no comment
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submission
resp = resp.follow()
resp = resp.form.submit('button_x1')
assert 'There were errors processing your form.' in resp.text
# comment
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submission
resp = resp.follow()
resp.form['comment'] = 'plop'
resp = resp.form.submit('button_x1').follow()
assert resp.pyquery('.comment').text() == 'plop'
assert '<span class="status">Status2' in resp.text
# no comment but no check
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submission
resp = resp.follow()
resp = resp.form.submit('button_x2').follow()
assert '<span class="status">Status2' in resp.text
def test_form_comment_is_hidden_attribute(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string 1', varname='choice1'),
fields.PageField(id='2', label='2nd page'),
fields.StringField(
id='3',
label='string 2',
varname='choice2',
condition={'type': 'django', 'value': 'form_var_choice1 == "1"'},
),
fields.CommentField(
id='5',
label='this should not be displayed',
condition={'type': 'django', 'value': 'False and form_var_choice2 == "???"'},
),
]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp.forms[0]['f1'] = '1'
resp = resp.forms[0].submit('submit')
comment = re.compile('.*comment-field.*"')
assert resp.html.find('div', {'data-field-id': '5'})
assert 'style="display: none"' in comment.search(resp.forms[0].text).group(0)
resp = resp.forms[0].submit('previous')
resp.forms[0]['f1'] = '2'
resp = resp.forms[0].submit('submit')
assert not resp.html.find('div', {'data-field-id': '5'})
@pytest.fixture
def create_formdata(pub):
FormDef.wipe()
data = [
{'id': '1', 'text': 'un', 'more': 'foo'},
{'id': '2', 'text': 'deux', 'more': 'bar'},
]
ds = {
'type': 'jsonvalue',
'value': json.dumps(data),
}
source_formdef = FormDef()
source_formdef.name = 'source form'
source_formdef.fields = [
fields.StringField(id='0', label='string', varname='toto_string'),
fields.FileField(id='1', label='file', varname='toto_file'),
fields.ItemField(
id='2', label='item', required=False, data_source=ds, varname='toto_item', hint='hint'
),
]
source_formdef.store()
target_formdef = FormDef()
target_formdef.name = 'target form'
target_formdef.enable_tracking_codes = True
target_formdef.fields = [
fields.StringField(id='0', label='string', varname='foo_string'),
fields.FileField(id='1', label='file', varname='foo_file'),
fields.ItemField(id='2', label='item', data_source=ds, varname='foo_item'),
]
target_formdef.store()
wf = Workflow(name='create-formdata')
st1 = wf.add_status('New')
st2 = wf.add_status('Resubmit')
jump = st1.add_action('choice', id='_resubmit')
jump.label = 'Resubmit'
jump.by = ['_submitter']
jump.status = st2.id
create_formdata = st2.add_action('create_formdata', id='_create_formdata')
create_formdata.varname = 'resubmitted'
create_formdata.draft = True
create_formdata.formdef_slug = target_formdef.url_name
create_formdata.mappings = [
Mapping(field_id='0', expression='=form_var_toto_string'),
Mapping(field_id='1', expression='=form_var_toto_file_raw'),
Mapping(field_id='2', expression='=form_var_toto_item_raw'),
]
redirect = st2.add_action('redirect_to_url', id='_redirect')
redirect.url = '{{ form_links_resubmitted.form_url }}'
display = st2.add_action('displaymsg', id='_display')
display.message = '''<div class="linked">{% if form_links_resubmitted %}
<p>Linked status: <span class="status">{{ form_links_resubmitted.form_status }}</span></p>
<p>Target formdata field: <span class="foo_string">{{ form_links_resubmitted.form_var_foo_string }}</span></p>
{% endif %}</div>'''
display.to = []
wf.store()
source_formdef.workflow_id = wf.id
source_formdef.store()
return locals()
def test_create_formdata_anonymous_draft(create_formdata):
create_formdata['source_formdef'].data_class().wipe()
create_formdata['target_formdef'].data_class().wipe()
app = get_app(create_formdata['pub'])
resp = app.get('/source-form/')
resp.form['f0'] = 'zob'
resp.form['f1$file'] = Upload('test.txt', b'foobar', 'text/plain')
resp.form['f2'] = '2'
resp = resp.form.submit('submit') # -> validation
resp = resp.form.submit('submit') # -> submission
resp = resp.follow()
assert create_formdata['target_formdef'].data_class().count() == 0
resp = resp.form.submit('button_resubmit')
assert create_formdata['target_formdef'].data_class().count() == 1
target_formdata = create_formdata['target_formdef'].data_class().select()[0]
assert target_formdata.data.get('0') == 'zob'
assert target_formdata.data.get('1').get_content() == b'foobar'
assert target_formdata.status == 'draft'
assert target_formdata.submission_context == {
'orig_object_type': 'formdef',
'orig_formdata_id': str(create_formdata['source_formdef'].data_class().select()[0].id),
'orig_formdef_id': str(create_formdata['source_formdef'].id),
}
resp = resp.follow()
resp = resp.follow()
assert 'zob' in resp
assert resp.click('test.txt').text == 'foobar'
resp = resp.forms[1].submit('submit') # -> validation
resp = resp.forms[1].submit('submit') # -> submission
assert create_formdata['target_formdef'].data_class().count() == 1
target_formdata = create_formdata['target_formdef'].data_class().select()[0]
assert target_formdata.data.get('0') == 'zob'
assert target_formdata.data.get('1').get_content() == b'foobar'
assert target_formdata.data.get('1').get_content() == b'foobar'
assert target_formdata.data.get('2') == '2'
assert target_formdata.data.get('2_display') == 'deux'
assert target_formdata.data.get('2_structured') == {'text': 'deux', 'id': '2', 'more': 'bar'}
assert target_formdata.status == 'wf-new'
source_formdata = create_formdata['source_formdef'].data_class().select()[0]
resp = app.get(source_formdata.get_url())
pq = resp.pyquery.remove_namespaces()
assert pq('.linked .status').text() == 'New'
assert pq('.linked .foo_string').text() == 'zob'
def test_create_formdata_anonymous_submitted(create_formdata):
create_formdata['source_formdef'].data_class().wipe()
create_formdata['target_formdef'].data_class().wipe()
# submit directly
create_formdata['wf'].get_status('2').items[0].draft = False
create_formdata['wf'].store()
app = get_app(create_formdata['pub'])
resp = app.get('/source-form/')
resp.form['f0'] = 'zob'
resp.form['f1$file'] = Upload('test.txt', b'foobar', 'text/plain')
resp.form['f2'] = '2'
resp = resp.form.submit('submit') # -> validation
resp = resp.form.submit('submit') # -> submission
resp = resp.follow()
assert create_formdata['target_formdef'].data_class().count() == 0
resp = resp.form.submit('button_resubmit')
assert create_formdata['target_formdef'].data_class().count() == 1
target_formdata = create_formdata['target_formdef'].data_class().select()[0]
assert target_formdata.data.get('0') == 'zob'
assert target_formdata.data.get('1').get_content() == b'foobar'
assert target_formdata.status == 'wf-new'
assert target_formdata.submission_context == {
'orig_object_type': 'formdef',
'orig_formdata_id': str(create_formdata['source_formdef'].data_class().select()[0].id),
'orig_formdef_id': str(create_formdata['source_formdef'].id),
}
resp = resp.follow()
assert 'New' in resp
assert 'zob' in resp
target_formdata = create_formdata['target_formdef'].data_class().select()[0]
assert target_formdata.data.get('1').get_content() == b'foobar'
assert target_formdata.data.get('1').get_content() == b'foobar'
assert target_formdata.status == 'wf-new'
source_formdata = create_formdata['source_formdef'].data_class().select()[0]
resp = app.get(source_formdata.get_url())
pq = resp.pyquery.remove_namespaces()
assert pq('.linked .status').text() == 'New'
assert pq('.linked .foo_string').text() == 'zob'
def test_create_formdata_empty_item_ds_with_id_parameter(pub, create_formdata):
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {
'type': 'json',
'value': 'http://remote.example.net/json',
}
data_source.id_parameter = 'id'
data_source.store()
create_formdata['source_formdef'].data_class().wipe()
create_formdata['target_formdef'].data_class().wipe()
create_formdata['source_formdef'].fields[2].data_source = {'type': 'foobar'}
create_formdata['source_formdef'].store()
create_formdata['target_formdef'].fields[2].data_source = {'type': 'foobar'}
create_formdata['target_formdef'].store()
with responses.RequestsMock() as rsps:
data = {'data': create_formdata['data']}
rsps.get('http://remote.example.net/json', json=data)
app = get_app(create_formdata['pub'])
resp = app.get('/source-form/')
resp.form['f0'] = 'zob'
resp.form['f1$file'] = Upload('test.txt', b'foobar', 'text/plain')
resp = resp.form.submit('submit') # -> validation
resp = resp.form.submit('submit') # -> submission
resp = resp.follow()
assert create_formdata['target_formdef'].data_class().count() == 0
assert pub.loggederror_class.count() == 0
resp = resp.form.submit('button_resubmit')
assert pub.loggederror_class.count() == 0
def test_create_formdata_locked_prefill_parent(create_formdata):
create_formdata['source_formdef'].data_class().wipe()
create_formdata['target_formdef'].data_class().wipe()
target_formdef = create_formdata['target_formdef']
target_formdef.fields[0].prefill = {
'type': 'string',
'value': '{{form_parent_form_var_toto_string}}',
'locked': True,
}
target_formdef.store()
app = get_app(create_formdata['pub'])
resp = app.get('/source-form/')
resp.form['f0'] = 'zob'
resp.form['f1$file'] = Upload('test.txt', b'foobar', 'text/plain')
resp.form['f2'] = '2'
resp = resp.form.submit('submit') # -> validation
resp = resp.form.submit('submit') # -> submission
resp = resp.follow()
assert create_formdata['target_formdef'].data_class().count() == 0
resp = resp.form.submit('button_resubmit')
assert create_formdata['target_formdef'].data_class().count() == 1
target_formdata = create_formdata['target_formdef'].data_class().select()[0]
assert target_formdata.data.get('0') == 'zob'
assert target_formdata.status == 'draft'
resp = resp.follow()
resp = resp.follow()
assert resp.forms[1]['f0'].value == 'zob'
assert resp.forms[1]['f0'].attrs['readonly']
# try altering readonly field
resp.forms[1]['f0'].value = 'xxx'
resp = resp.forms[1].submit('submit')
resp = resp.forms[1].submit('previous')
assert resp.forms[1]['f0'].value == 'zob'
resp = resp.forms[1].submit('submit')
resp = resp.forms[1].submit('submit')
assert create_formdata['target_formdef'].data_class().count() == 1
target_formdata = create_formdata['target_formdef'].data_class().select()[0]
assert target_formdata.data['0'] == 'zob'
def test_js_libraries(pub):
create_formdef()
resp = get_app(pub).get('/test/', status=200)
assert 'jquery.js' not in resp.text
assert 'jquery.min.js' in resp.text
assert 'qommon.forms.js' in resp.text
pub.cfg['debug'] = {'debug_mode': True}
pub.write_cfg()
resp = get_app(pub).get('/test/', status=200)
assert 'jquery.js' in resp.text
assert 'jquery.min.js' not in resp.text
assert 'qommon.forms.js' in resp.text
pub.cfg['branding'] = {'included_js_libraries': ['jquery.js']}
pub.write_cfg()
resp = get_app(pub).get('/test/', status=200)
assert 'jquery.js' not in resp.text
assert 'jquery.min.js' not in resp.text
assert 'qommon.forms.js' in resp.text
def test_after_submit_location(pub):
create_user(pub)
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
commentable = st1.add_action('commentable', id='_commentable')
commentable.by = [logged_users_role().id]
commentable.required = True
workflow.store()
formdef = create_formdef()
formdef.fields = []
formdef.store()
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/test/')
resp = resp.forms[0].submit('submit') # form page
resp = resp.forms[0].submit('submit') # confirmation page
resp = resp.follow()
resp.form['comment'] = 'plop'
resp = resp.form.submit('submit')
assert resp.location == 'http://example.net/test/1/#action-zone'
resp = resp.follow()
display = st1.add_action('displaymsg')
display.message = 'message-to-all'
display.to = []
workflow.store()
resp.form['comment'] = 'plop'
resp = resp.form.submit('submit')
assert resp.location == 'http://example.net/test/1/#'
def test_form_honeypot(pub):
formdef = create_formdef()
formdef.fields = [fields.StringField(id='0', label='string', required=False)]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.forms[0]['f0'] = 'plop'
resp.forms[0]['f00'] = 'honey?'
resp = resp.forms[0].submit('submit')
assert 'Honey pot should be left untouched.' in resp
assert formdef.data_class().count() == 0 # check no drafts have been saved
def test_structured_workflow_options(pub):
create_user_and_admin(pub)
workflow = Workflow(name='test')
workflow.variables_formdef = WorkflowVariablesFieldsFormDef(workflow=workflow)
data_source = {
'type': 'jsonvalue',
'value': json.dumps(
[{'id': '1', 'text': 'un', 'more': 'foo'}, {'id': '2', 'text': 'deux', 'more': 'bar'}]
),
}
workflow.variables_formdef.fields = [
fields.StringField(id='1', label='Test', varname='foo'),
fields.ItemField(id='2', label='Test List', varname='bar', data_source=data_source),
fields.ItemsField(id='3', label='Test Multi', varname='baz', data_source=data_source),
fields.DateField(id='4', label='Date', varname='date'),
]
st1 = workflow.add_status('Status1', 'st1')
comment = st1.add_action('register-comment', id='_comment')
comment.comment = 'Date option: {{ form_option_date }}'
workflow.store()
formdef = create_formdef()
formdef.fields = [
fields.ItemField(
id='1',
label='Test List',
varname='bar',
data_source={'type': 'formula', 'value': 'form_option_baz_structured'},
),
]
formdef.store()
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
# configure workflow options
resp = login(get_app(pub), username='admin', password='admin').get('/backoffice/forms/%s/' % formdef.id)
resp = resp.click('Options')
resp.form['f1'].value = 'plop'
resp.form['f2'].value = '1'
resp.form['f3$element1'].checked = True
resp.form['f4'].value = '2020-04-18'
resp = resp.form.submit('submit')
formdef = FormDef.get(formdef.id)
assert formdef.workflow_options == {
'foo': 'plop',
'bar': '1',
'bar_display': 'un',
'bar_structured': {'id': '1', 'more': 'foo', 'text': 'un'},
'baz': ['1'],
'baz_display': 'un',
'baz_structured': [{'id': '1', 'more': 'foo', 'text': 'un'}],
'date': time.strptime('2020-04-18', '%Y-%m-%d'),
}
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/test/')
assert resp.form['f1'].options == [('1', False, 'un')]
resp = resp.form.submit('submit') # form page
resp = resp.form.submit('submit') # confirmation page
resp = resp.follow()
formdata = formdef.data_class().select()[0]
assert formdata.data == {
'1': '1',
'1_display': 'un',
'1_structured': {'id': '1', 'text': 'un', 'more': 'foo'},
}
assert '2020-04-18' in formdata.evolution[0].parts[1].content
def test_exclude_self_condition(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [
fields.PageField(
id='1',
label='1st page',
post_conditions=[
{
'condition': {
'type': 'django',
'value': 'form_objects|filter_by:"foo"|filter_value:form_var_foo|exclude_self|count == 0',
},
'error_message': 'You shall not pass.',
}
],
),
fields.StringField(id='1', label='string', varname='foo'),
]
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
editable = st1.add_action('editable', id='_editable')
editable.by = ['_submitter', '_receiver']
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
app = get_app(pub)
resp = app.get(formdef.get_url())
resp.form['f1'] = 'test'
resp = resp.form.submit('submit') # -> validation page
assert 'You shall not pass.' not in resp.text
resp = resp.form.submit('submit') # -> submit
resp = resp.follow()
# edit is ok
resp = resp.form.submit('button_editable').follow()
resp = resp.form.submit('submit') # -> validation page
assert 'You shall not pass.' not in resp
# 2nd submission
resp = app.get(formdef.get_url())
resp.form['f1'] = 'test'
resp = resp.form.submit('submit') # -> validation page
assert 'You shall not pass.' in resp.text
# submission with other value
resp = app.get(formdef.get_url())
resp.form['f1'] = 'other'
resp = resp.form.submit('submit') # -> validation page
assert 'You shall not pass.' not in resp.text
resp = resp.form.submit('submit') # -> submit
resp = resp.follow()
# edit is ok
resp = resp.form.submit('button_editable').follow()
resp.form['f1'] = 'test'
resp = resp.form.submit('submit') # -> validation page
assert 'You shall not pass.' in resp
def test_rich_commentable_action(pub):
create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
formdef.roles = [logged_users_role().id]
formdef.store()
wf = Workflow(name='status')
st1 = wf.add_status('Status1', 'st1')
commentable = st1.add_action('commentable', id='_commentable')
commentable.by = [logged_users_role().id]
commentable.required = True
choice = st1.add_action('choice', id='_x1')
choice.label = 'Submit'
choice.by = [logged_users_role().id]
choice.status = st1.id
wf.store()
formdef.workflow = wf
formdef.store()
# comment
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submission
resp = resp.follow()
resp.form['comment'] = '<p>hello <i>world</i></p>'
resp = resp.form.submit('button_x1').follow()
assert resp.pyquery('div.comment').text() == 'hello world'
assert '<p>hello <i>world</i></p>' in resp.text
formdata = formdef.data_class().select()[0]
assert formdata.evolution[-1].parts[-2].comment == '<p>hello <i>world</i></p>'
# check link
resp.form['comment'] = '<p>hello <a href="http://localhost/">link</a>.</p>'
resp = resp.form.submit('button_x1').follow()
assert '<p>hello <a href="http://localhost/" rel="nofollow">link</a>.</p>' in resp.text
formdata = formdef.data_class().select()[0]
assert (
formdata.evolution[-1].parts[-2].comment
== '<p>hello <a href="http://localhost/" rel="nofollow">link</a>.</p>'
)
# check unauthorized tags are removed
resp.form['comment'] = '<p>hello <script>evil</script></p>'
resp = resp.form.submit('button_x1').follow()
assert '<p>hello evil</p>' in resp.text
formdata = formdef.data_class().select()[0]
assert formdata.evolution[-1].parts[-2].comment == '<p>hello evil</p>'
resp.form['comment'] = '<p></p>' # left empty
resp = resp.form.submit('button_x1')
assert resp.pyquery('.error').text() == 'required field'
resp.form['comment'] = '<p> </p>' # left ~empty
resp = resp.form.submit('button_x1')
assert resp.pyquery('.error').text() == 'required field'
# url to links
resp.form['comment'] = '<p>Here is the address: https://example.net</p>'
resp = resp.form.submit('button_x1').follow()
assert (
'<p>Here is the address: <a href="https://example.net" rel="nofollow">https://example.net</a></p>'
in resp.text
)
# test paragraphs are converted to newlines in plain text view
resp.form['comment'] = '<p>hello</p><p>world</p>'
resp = resp.form.submit('button_x1').follow()
formdata = formdef.data_class().select()[0]
pub.substitutions.feed(formdata)
context = pub.substitutions.get_context_variables(mode='lazy')
tmpl = Template('{{form_comment}}')
assert tmpl.render(context) == 'hello\n\nworld'
# test <br> are accepted and converted to single-newline in plain text view
resp.form['comment'] = '<p>hello<br>world</p>'
resp = resp.form.submit('button_x1').follow()
formdata = formdef.data_class().select()[0]
assert formdata.evolution[-1].parts[-2].comment == '<p>hello<br>world</p>'
pub.substitutions.feed(formdata)
context = pub.substitutions.get_context_variables(mode='lazy')
tmpl = Template('{{form_comment}}')
assert tmpl.render(context) == 'hello\nworld'
def test_jumps_with_by_and_no_trigger(pub):
FormDef.wipe()
Workflow.wipe()
pub.role_class.wipe()
role = pub.role_class(name='xxx')
role.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
jump = st1.add_action('jump')
jump.status = 'st2'
jump.by = [role.id]
jump = st1.add_action('jump')
jump.status = 'st3'
jump.by = []
workflow.add_status('Status2', 'st2')
workflow.add_status('Status3', 'st3')
workflow.store()
formdef = create_formdef()
formdef.fields = []
formdef.workflow = workflow
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp = resp.form.submit('submit')
resp = resp.form.submit('submit')
# it jumps to st2, as jump.by is only related to triggers
assert formdef.data_class().count() == 1
assert formdef.data_class().select()[0].status == 'wf-st2'
def test_user_filter_auto_custom_view(pub):
user = create_user(pub)
CardDef.wipe()
carddef = CardDef()
carddef.name = 'items'
carddef.user_support = 'optional'
carddef.digest_templates = {'default': '{{form_var_name}}'}
carddef.fields = [
fields.StringField(id='0', label='string', varname='name'),
]
carddef.store()
for i, value in enumerate(['foo', 'bar', 'baz']):
carddata = carddef.data_class()()
carddata.data = {
'0': value,
'1': 'attr%s' % i,
}
carddata.user_id = user.id
carddata.just_created()
carddata.store()
carddata.user_id = None # don't associate latest (baz) with user
carddata.store()
ds = {'type': 'carddef:%s' % carddef.url_name}
formdef = FormDef()
formdef.name = 'foobar'
formdef.fields = [
fields.ItemField(id='0', label='item', varname='foo', data_source=ds),
]
formdef.store()
app = get_app(pub)
resp = app.get(formdef.get_url())
assert [x[2] for x in resp.form['f0'].options] == ['bar', 'baz', 'foo']
formdef.fields[0].data_source['type'] = 'carddef:%s:_with_user_filter' % carddef.url_name
formdef.store()
resp = app.get(formdef.get_url())
assert [x[2] for x in resp.form['f0'].options] == ['---']
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
assert [x[2] for x in resp.form['f0'].options] == ['bar', 'foo']
def test_go_to_backoffice(pub):
formdef = create_formdef()
app = get_app(pub)
resp = app.get('/test/go-to-backoffice')
assert resp.location.endswith('/backoffice/forms/%s/' % formdef.id)
def test_global_interactive_action(pub):
user = create_user(pub)
formdef = FormDef()
formdef.name = 'test global action'
formdef.fields = []
workflow = Workflow.get_default_workflow()
workflow.id = '2'
action = workflow.add_global_action('FOOBAR')
display = action.add_action('displaymsg')
display.message = 'This is a message'
display.to = []
form_action = action.add_action('form')
form_action.varname = 'blah'
form_action.hide_submit_button = False
form_action.formdef = WorkflowFormFieldsFormDef(item=form_action)
form_action.formdef.fields.append(fields.StringField(id='1', label='Test', varname='test', required=True))
register_comment = action.add_action('register-comment')
register_comment.comment = 'HELLO {{ form_workflow_form_blah_var_test }}'
trigger = action.triggers[0]
trigger.roles = ['_submitter']
workflow.store()
formdef.workflow_id = workflow.id
formdef.workflow_roles = {'_receiver': 1}
formdef.store()
formdata = formdef.data_class()()
formdata.user_id = user.id
formdata.just_created()
formdata.store()
formdata.perform_workflow()
formdata.store()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdata.get_url(backoffice=False))
assert 'button-action-1' in resp.form.fields
resp = resp.form.submit('button-action-1')
resp = resp.follow() # -> error, empty action
resp = resp.follow() # -> back to form
assert 'Configuration error: no available action.' in resp.text
form_action.by = trigger.roles
workflow.store()
resp = app.get(formdata.get_url(backoffice=False))
resp = resp.form.submit('button-action-1')
resp = resp.follow()
assert 'This is a message' in resp.text
resp = resp.form.submit('submit')
assert resp.pyquery('#form_error_fblah_1').text() == 'required field'
resp.form['fblah_1'] = 'GLOBAL INTERACTIVE ACTION'
resp = resp.form.submit('submit')
assert resp.location == formdata.get_url(backoffice=False)
resp = resp.follow()
assert 'HELLO GLOBAL INTERACTIVE ACTION' in resp.text
def test_global_interactive_action_form_prefill(pub):
user = create_user(pub)
formdef = FormDef()
formdef.name = 'test global action'
formdef.fields = []
workflow = Workflow.get_default_workflow()
workflow.id = '2'
action = workflow.add_global_action('FOOBAR')
form_action = action.add_action('form')
form_action.varname = 'blah'
form_action.hide_submit_button = False
form_action.formdef = WorkflowFormFieldsFormDef(item=form_action)
form_action.formdef.fields = [
fields.StringField(
id='1',
label='Test',
varname='test',
required=True,
prefill={'type': 'string', 'value': 'aaa'},
),
fields.StringField(
id='2',
label='Test2',
varname='test2',
required=True,
prefill={'type': 'string', 'value': 'bbb'},
condition={'type': 'django', 'value': 'False'},
),
]
form_action.by = ['_submitter']
register_comment = action.add_action('register-comment')
register_comment.comment = 'HELLO {{ form_workflow_form_blah_var_test }}'
trigger = action.triggers[0]
trigger.roles = ['_submitter']
workflow.store()
formdef.workflow_id = workflow.id
formdef.workflow_roles = {'_receiver': 1}
formdef.store()
formdata = formdef.data_class()()
formdata.user_id = user.id
formdata.just_created()
formdata.store()
formdata.perform_workflow()
formdata.store()
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdata.get_url(backoffice=False))
resp = resp.form.submit('button-action-1')
resp = resp.follow()
assert resp.form['fblah_1'].value == 'aaa' # prefill
assert 'fblah_2' not in resp.form.fields # conditioned out
resp.form['fblah_1'].value = 'HELLO GLOBAL INTERACTIVE ACTION'
resp = resp.form.submit('submit')
assert resp.location == formdata.get_url(backoffice=False)
resp = resp.follow()
assert 'HELLO GLOBAL INTERACTIVE ACTION' in resp.text
def test_category_redirection(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test category redirection'
formdef.fields = []
formdef.store()
get_app(pub).get(formdef.get_url())
Category.wipe()
cat = Category(name='foo')
cat.store()
cat2 = Category(name='bar')
cat2.store()
formdef.category_id = cat.id
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.store()
resp = get_app(pub).get(formdef.get_url(), status=302)
assert resp.location == 'http://example.net/foo/test-category-redirection/'
resp = resp.follow()
resp = get_app(pub).get(formdef.get_url() + '?test=toto', status=302)
assert resp.location == 'http://example.net/foo/test-category-redirection/?test=toto'
# missing trailing /
resp = get_app(pub).get(formdef.get_url().rstrip('/'), status=302)
assert resp.location == 'http://example.net/test-category-redirection/'
resp = resp.follow()
assert resp.location == 'http://example.net/foo/test-category-redirection/'
# missing trailing / + query string
resp = get_app(pub).get(formdef.get_url().rstrip('/') + '?test=toto', status=302)
assert resp.location == 'http://example.net/test-category-redirection/?test=toto'
resp = resp.follow()
assert resp.location == 'http://example.net/foo/test-category-redirection/?test=toto'
resp = get_app(pub).get('/bar/test-category-redirection/', status=302)
assert resp.location == 'http://example.net/foo/test-category-redirection/'
resp = get_app(pub).get('/bar/test-category-redirection/?x=y', status=302)
assert resp.location == 'http://example.net/foo/test-category-redirection/?x=y'
# check formdata is redirected to login
resp = get_app(pub).get('/foo/test-category-redirection/%s/' % formdata.id, status=302)
assert '/login/' in resp.location
# but there's no redirection if used with the wrong category
resp = get_app(pub).get('/bar/test-category-redirection/%s/' % formdata.id, status=404)
resp = get_app(pub).get(formdata.get_url(), status=302)
assert (
urllib.parse.urlparse(resp.location).path
== urllib.parse.urlparse(formdata.get_url(include_category=True)).path
)
resp = get_app(pub).get(formdata.get_url() + '?test=toto', status=302)
assert (
urllib.parse.urlparse(resp.location).path
== urllib.parse.urlparse(formdata.get_url(include_category=True)).path
)
assert urllib.parse.urlparse(resp.location).query == 'test=toto'
# check with formdef and category with same slug
formdef = FormDef()
formdef.name = 'bar'
formdef.fields = []
formdef.category_id = cat2.id
formdef.store()
formdata2 = formdef.data_class()()
formdata2.just_created()
formdata2.store()
resp = get_app(pub).get('/bar/', status=302) # redirect even if category with same slug
assert resp.location == 'http://example.net/bar/bar/'
resp = get_app(pub).get('/bar/bar/', status=200) # get formpage when full url is used
assert resp.pyquery('#steps') # make sure it's a form page
resp = get_app(pub).get('/bar/bar/bar/', status=404) # do not allow to go deeper
resp = get_app(pub).get(formdata2.get_url(), status=302)
assert (
urllib.parse.urlparse(resp.location).path
== urllib.parse.urlparse(formdata2.get_url(include_category=True)).path
)
# check other pages are ok with and without category slug
get_app(pub).get('/bar/qrcode', status=200)
get_app(pub).get('/bar/bar/qrcode', status=200)
# check POST are ok
resp = get_app(pub).get('/bar/bar/', status=200)
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
# check another formdef in same category is ok
formdef = FormDef()
formdef.name = 'bar2'
formdef.fields = []
formdef.category_id = cat2.id
formdef.store()
resp = get_app(pub).get('/bar2/', status=302)
assert resp.location == 'http://example.net/bar/bar2/'
resp = get_app(pub).get('/bar/bar2/', status=200)
assert resp.pyquery('#steps') # make sure it's a form page
def test_form_edit_with_category(pub):
create_user(pub)
Category.wipe()
cat = Category(name='foobar')
cat.store()
formdef = create_formdef()
formdef.category_id = cat.id
formdef.data_class().wipe()
formdef.fields = [fields.StringField(id='1', label='1st field')]
formdef.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
editable = st1.add_action('editable', id='_editable')
editable.by = ['_submitter', '_receiver']
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
app = get_app(pub)
login(app, username='foo', password='foo')
resp = app.get(formdef.get_url(include_category=True))
resp.form['f1'] = 'test'
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submit
resp = resp.follow().follow()
resp = resp.form.submit('button_editable')
assert 'wfedit' in resp.location
resp = resp.follow()
assert 'f1' in resp.form.fields
def test_form_edit_single_page(pub):
user = create_user(pub)
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
editable = st1.add_action('editable', id='_editable')
editable.by = ['_submitter', '_receiver']
workflow.store()
formdef = create_formdef()
formdef.data_class().wipe()
formdef.fields = [
fields.PageField(id='1', label='1st page'),
fields.StringField(id='2', label='field1'),
fields.PageField(id='3', label='2nd page'),
fields.StringField(id='4', label='field2'),
fields.PageField(id='5', label='3rd page'),
fields.StringField(id='6', label='field3'),
]
formdef.workflow_id = workflow.id
formdef.store()
formdata = formdef.data_class()()
formdata.user_id = user.id
formdata.data = {'2': 'a', '4': 'b', '6': 'c'}
formdata.just_created()
formdata.store()
app = get_app(pub)
login(app, username='foo', password='foo')
resp = app.get(formdata.get_url())
resp = resp.form.submit('button_editable').follow()
assert [x.text for x in resp.pyquery('#steps .label')] == ['1st page', '2nd page', '3rd page']
editable.operation_mode = 'single'
editable.page_identifier = 'plop'
workflow.store()
# unknown page identifier, a 404 is raised
resp = app.get(formdata.get_url())
resp = resp.form.submit('button_editable').follow(status=404)
# add identifier to second page, and edit it
formdef.fields[2].varname = 'plop'
formdef.store()
resp = app.get(formdata.get_url())
resp = resp.form.submit('button_editable').follow()
assert [x.text for x in resp.pyquery('#steps .label')] == ['2nd page']
resp.form['f4'] = 'changed'
assert [x.text for x in resp.pyquery('.buttons button')] == ['Save Changes', 'Previous', 'Cancel']
assert resp.pyquery('.buttons button.form-previous[hidden][disabled]')
resp = resp.form.submit('submit')
formdata.refresh_from_storage()
assert formdata.data == {'2': 'a', '4': 'changed', '6': 'c'}
# change action to edit all pages starting at page 2
editable.operation_mode = 'partial'
workflow.store()
resp = app.get(formdata.get_url())
resp = resp.form.submit('button_editable').follow()
assert [x.text for x in resp.pyquery('#steps .label')] == ['2nd page', '3rd page']
resp.form['f4'] = 'other change'
assert [x.text for x in resp.pyquery('.buttons button')] == ['Next', 'Previous', 'Cancel']
assert resp.pyquery('.buttons button.form-previous[hidden][disabled]')
resp = resp.form.submit('submit')
assert [x.text for x in resp.pyquery('.buttons button')] == ['Save Changes', 'Previous', 'Cancel']
assert resp.pyquery('.buttons button.form-previous:not([hidden])')
assert resp.pyquery('.buttons button.form-previous:not([disabled])')
resp = resp.form.submit('previous')
assert [x.text for x in resp.pyquery('.buttons button')] == ['Next', 'Previous', 'Cancel']
assert resp.pyquery('.buttons button.form-previous[hidden][disabled]')
resp = resp.form.submit('submit')
resp.form['f6'] = 'last change'
assert [x.text for x in resp.pyquery('.buttons button')] == ['Save Changes', 'Previous', 'Cancel']
assert resp.pyquery('.buttons button.form-previous:not([hidden])')
assert resp.pyquery('.buttons button.form-previous:not([disabled])')
resp = resp.form.submit('submit')
formdata.refresh_from_storage()
assert formdata.data == {'2': 'a', '4': 'other change', '6': 'last change'}
def test_form_edit_and_jump_on_submit(pub):
wf = Workflow(name='edit and jump on submit')
st0 = wf.add_status('Status0')
st1 = wf.add_status('Status1')
st2 = wf.add_status('Status2')
button = st0.add_action('choice')
button.by = ['_submitter', '_receiver']
button.label = 'jump'
button.status = st1.id
jump = st1.add_action('jumponsubmit', id='_jump')
jump.status = st2.id
editable = st1.add_action('editable', id='_editable')
editable.by = ['_submitter', '_receiver']
wf.store()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [fields.StringField(id='1', label='string', varname='toto')]
formdef.workflow_id = wf.id
formdef.store()
resp = get_app(pub).get(formdef.get_url())
resp.form['f1'] = 'test'
resp = resp.form.submit('submit')
resp = resp.form.submit('submit').follow()
resp = resp.form.submit(f'button{button.id}').follow()
assert formdef.data_class().select()[0].status == f'wf-{st1.id}'
resp = resp.form.submit(f'button{editable.id}').follow()
resp.form['f1'] = 'test2'
resp = resp.form.submit('submit')
assert formdef.data_class().select()[0].status == f'wf-{st1.id}'
assert formdef.data_class().select()[0].data['1'] == 'test2'
def test_form_html_titles(pub):
formdef = create_formdef()
formdef.fields = [
fields.StringField(id='1', label='string', required=False),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert resp.pyquery('title').text() == 'test - 1/2 - Filling'
resp = resp.forms[0].submit('submit') # -> validation
assert resp.pyquery('title').text() == 'test - 2/2 - Validating'
resp = resp.forms[0].submit('submit').follow() # -> submit
assert resp.pyquery('title').text() == 'test #1-1'
# without confirmation page, single page, no counter
formdef.confirmation = False
formdef.store()
resp = get_app(pub).get('/test/')
assert resp.pyquery('title').text() == 'test - Filling'
# naming first page
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string'),
]
formdef.store()
resp = get_app(pub).get('/test/')
assert resp.pyquery('title').text() == 'test - 1st page'
# multi pages
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='string', required=False),
fields.PageField(id='2', label='2nd page'),
]
formdef.store()
resp = get_app(pub).get('/test/')
assert resp.pyquery('title').text() == 'test - 1/2 - 1st page'
resp = resp.forms[0].submit('submit') # -> 2nd page
assert resp.pyquery('title').text() == 'test - 2/2 - 2nd page'
# multi pages and confirmation page
formdef.confirmation = True
formdef.store()
resp = get_app(pub).get('/test/')
assert resp.pyquery('title').text() == 'test - 1/3 - 1st page'
resp = resp.forms[0].submit('submit') # -> 2nd page
assert resp.pyquery('title').text() == 'test - 2/3 - 2nd page'
resp = resp.forms[0].submit('submit') # -> validation
assert resp.pyquery('title').text() == 'test - 3/3 - Validating'
def test_only_one_check(pub):
create_user(pub)
FormDef.wipe()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [fields.StringField(id='1', label='1st field')]
formdef.only_allow_one = True
formdef.store()
formdef.data_class().wipe()
for i in range(5):
resp = get_app(pub).get('/form-title/')
resp.form['f1'] = 'test'
resp = resp.form.submit('submit') # -> validation
resp = resp.form.submit('submit') # -> submit
assert formdef.data_class().count() == (i + 1)
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/form-title/')
resp.form['f1'] = 'test2'
resp = resp.form.submit('submit') # -> validation
# draft has been saved
assert len([x for x in formdef.data_class().select() if x.is_draft()]) == 1
# the draft doesn't prevent a new form being completed
resp = app.get('/form-title/')
resp.form['f1'] = 'test2'
resp = resp.form.submit('submit') # -> validation
resp = resp.form.submit('submit') # -> submit
# when there's a form, redirect to it
resp = app.get('/form-title/')
resp = resp.follow()
assert '>test2<' in resp.text
def test_form_errors_summary(pub):
formdef = create_formdef()
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{'condition': {'type': 'django', 'value': 'false'}, 'error_message': 'You shall not pass.'}
],
),
fields.StringField(id='1', label='string1', required=True),
fields.StringField(id='2', label='string2', required=False),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp = resp.forms[0].submit('submit')
assert 'You shall not pass.' in resp.pyquery('.errornotice').text()
assert 'The following field has an error: string1' in resp.pyquery('.errornotice').text()
resp.forms[0]['f1'] = 'foo'
resp = resp.forms[0].submit('submit')
assert 'You shall not pass.' in resp.pyquery('.errornotice').text()
assert 'The following field has an error:' not in resp.pyquery('.errornotice').text()
# remove post condition
formdef.fields[0].post_conditions = []
formdef.store()
resp = get_app(pub).get('/test/')
resp = resp.forms[0].submit('submit')
assert 'The following field has an error: string1' in resp.pyquery('.errornotice').text()
# check plurals
formdef.fields[2].required = True
formdef.store()
resp = get_app(pub).get('/test/')
resp = resp.forms[0].submit('submit')
assert 'The following fields have an error: string1, string2' in resp.pyquery('.errornotice').text()
for error_link in [x.attrib['href'] for x in resp.pyquery('.errornotice a')]:
assert resp.pyquery(error_link)
# check block
BlockDef.wipe()
block = BlockDef()
block.name = 'foobar'
block.fields = [
fields.StringField(id='123', required=True, label='Test1'),
fields.StringField(id='234', required=True, label='Test2'),
]
block.store()
formdef.fields.append(fields.BlockField(id='3', label='testblock', block_slug='foobar', max_items=3))
formdef.store()
for label_display in ('normal', 'subtitle', 'hidden'):
formdef.fields[-1].label_display = label_display
formdef.store()
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'foo'
resp.forms[0]['f2'] = 'foo'
resp = resp.forms[0].submit('submit')
assert 'The following field has an error: testblock' in resp.pyquery('.errornotice').text()
for error_link in [x.attrib['href'] for x in resp.pyquery('.errornotice a')]:
assert resp.pyquery(error_link)
# check there's a single link to block if there are errors in multiple rows
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'foo'
resp.forms[0]['f2'] = 'foo'
resp.forms[0]['f3$element0$f123'] = 'foo'
resp.forms[0]['f3$element0$f234'] = 'bar'
resp = resp.form.submit('f3$add_element')
assert not resp.pyquery('.errornotice')
resp.forms[0]['f3$element0$f234'] = ''
resp.forms[0]['f3$element1$f123'] = 'foo'
resp = resp.forms[0].submit('submit')
assert 'The following field has an error: testblock' in resp.pyquery('.errornotice').text()
assert resp.pyquery('.error').text() == 'required field required field '