wcs/tests/test_form_pages.py

1363 lines
50 KiB
Python

import pytest
import hashlib
import os
import re
import StringIO
from webtest import Upload
from quixote.http_request import Upload as QuixoteUpload
from wcs.qommon.form import UploadedFile
from wcs.qommon.ident.password_accounts import PasswordAccount
from wcs.formdef import FormDef
from wcs.workflows import Workflow, EditableWorkflowStatusItem, ExportToModel
from wcs.wf.jump import JumpWorkflowStatusItem
from wcs.wf.attachment import AddAttachmentWorkflowStatusItem
from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef
from wcs.categories import Category
from wcs.roles import Role, logged_users_role
from wcs.tracking_code import TrackingCode
from wcs.data_sources import NamedDataSource
from wcs import fields
from wcs.sessions import BasicSession
from utilities import get_app, login, create_temporary_pub, clean_temporary_pub, emails
def pytest_generate_tests(metafunc):
if 'pub' in metafunc.fixturenames:
metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True)
@pytest.fixture
def pub(request):
pub = create_temporary_pub(sql_mode=(request.param == 'sql'))
pub.cfg['identification'] = {'methods': ['password']}
pub.cfg['misc'] = {'charset': 'utf-8'}
pub.cfg['language'] = {'language': 'en'}
pub.write_cfg()
if Category.count() == 0:
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.email = 'foo@localhost'
user.store()
account = PasswordAccount(id='foo')
account.set_password('foo')
account.user_id = user.id
account.store()
return user
def test_home(pub):
create_formdef()
home = get_app(pub).get('/')
assert 'category-misc' in home.body
assert '<a class="" href="test/">test</a>' in home.body
def test_home_category(pub):
formdef = create_formdef()
formdef.category_id = '1'
formdef.store()
home = get_app(pub).get('/')
assert 'category-foobar' in home.body
assert not 'category-misc' in home.body
assert '<a class="" href="foobar/test/">test</a>' in home.body
def test_home_disabled(pub):
formdef = create_formdef()
formdef.disabled = True
formdef.store()
home = get_app(pub).get('/')
assert not '<a href="test/">test</a>' in home.body
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'
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.body
assert '<a href="test/">test</a><span> (authentication required)</span>' in home.body
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/')
assert '<h2>foobar</h2>' in resp.body
assert '<a class="" href="test/">test</a>' in resp.body
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)
Role.wipe()
role = Role(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()]
formdef.store()
user = create_user(pub)
login(get_app(pub), username='foo', password='foo').get('/test/', status=403)
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_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.body
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.body
assert formdef.data_class().count() == 1
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.body
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 '<div class="error">required field</div>' in next_page.body
next_page.forms[0]['f0'] = 'foobar'
next_page = next_page.forms[0].submit('submit')
assert 'Check values then click submit.' in next_page.body
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.body
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 assert_current_page(resp, page_label):
assert re.findall('<li class=".*?current.*?">.*?<span class="label">(.*?)</span></li>',
resp.body)[0] == page_label
def test_form_multi_page(pub):
formdef = create_formdef()
formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page', type='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'
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.body
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.body
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_condition(pub):
formdef = create_formdef()
formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page', type='page', condition='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.body
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', type='page'),
fields.ItemField(id='1', label='select', type='item',
required=True,
varname='foo', items=['Foo', 'Bar']),
fields.PageField(id='2', label='2nd page', type='page',
condition='var_foo == "Foo"'),
fields.PageField(id='3', label='3rd page', type='page',
condition='var_foo == "Bar"'),
fields.StringField(id='3', label='string 2')]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert not '2nd page' in resp.body
assert not '3rd page' in resp.body
assert resp.forms[0]['f1'].value == 'Foo' # preset
resp = resp.forms[0].submit('submit')
assert '2nd page' in resp.body
assert not '3rd page' in resp.body
assert_current_page(resp, '2nd page')
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'Bar'
resp = resp.forms[0].submit('submit')
assert not '2nd page' in resp.body
assert '3rd page' in resp.body
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', type='page'),
fields.ItemField(id='1', label='select', type='item',
required=True,
varname='foo', items=['Foo', 'Bar']),
fields.PageField(id='2', label='2nd page', type='page',
condition='form_var_foo == "Foo"'),
fields.PageField(id='3', label='3rd page', type='page',
condition='form_var_foo == "Bar"'),
fields.StringField(id='3', label='string 2')]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert not '2nd page' in resp.body
assert not '3rd page' in resp.body
resp.forms[0]['f1'] = 'Foo'
resp = resp.forms[0].submit('submit')
assert '2nd page' in resp.body
assert not '3rd page' in resp.body
assert_current_page(resp, '2nd page')
resp = get_app(pub).get('/test/')
resp.forms[0]['f1'] = 'Bar'
resp = resp.forms[0].submit('submit')
assert not '2nd page' in resp.body
assert '3rd page' in resp.body
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', type='page'),
fields.BoolField(id='1', label='checkbox', varname='checkbox'),
fields.PageField(id='2', label='2nd page', type='page',
condition='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.body
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_data_source(pub):
formdef = create_formdef()
formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
fields.BoolField(id='1', label='checkbox', varname='checkbox'),
fields.PageField(id='2', label='2nd page', type='page',
condition='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': 'formula', 'value': repr([])}
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.body
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': 'formula', 'value': repr(['un', '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', type='page'),
fields.StringField(id='1', label='string', varname='xxx',
required=False),
fields.PageField(id='2', label='2nd page', type='page',
condition='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.body
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_submit_with_user(pub):
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.body
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.body
assert formdef.data_class().count() == 1
# check the user received a copy by email
assert emails.emails.get('New form (test)')
assert emails.emails.get('New form (test)')['email_rcpt'] == ['foo@localhost']
def test_form_visit_existing(pub):
user = create_user(pub)
formdef = create_formdef()
page = 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 == 'http://example.net/login'
resp = get_app(pub).get('/test/%s/' % formdata_user.id)
assert resp.location == 'http://example.net/login'
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_auth(pub):
create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/auth')
assert resp.location == 'http://example.net/login/?ReturnUrl=http%3A//example.net/test/'
resp = login(get_app(pub), username='foo', password='foo').get('/test/auth')
assert resp.location == 'http://example.net/test/'
def test_form_tryauth(pub):
create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/tryauth')
assert resp.location == 'http://example.net/test/'
app = login(get_app(pub), username='foo', password='foo')
pub.cfg['identification'] = {'methods': ['idp']}
pub.write_cfg()
# if the user is logged in, the form should be presented
resp = app.get('/test/tryauth')
assert resp.location == 'http://example.net/test/'
# if the user is unlogged, there should be a passive redirection to SSO
resp = get_app(pub).get('/test/tryauth')
assert 'IsPassive=true' in resp.location
pub.cfg['identification'] = {'methods': ['password']}
pub.write_cfg()
def test_form_no_tracking_code(pub):
formdef = create_formdef()
formdef.data_class().wipe()
formdef.fields = [fields.StringField(id='0', label='string')]
formdef.enable_tracking_codes = False
formdef.store()
resp = get_app(pub).get('/test/')
assert not '<h3>Tracking code</h3>' in resp.body
def test_form_tracking_code(pub):
formdef = create_formdef()
formdef.fields = [fields.StringField(id='0', label='string')]
formdef.enable_tracking_codes = True
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
assert '<h3>Tracking code</h3>' in resp.body
resp.forms[0]['f0'] = 'foobar'
resp = resp.forms[0].submit('submit')
tracking_code = None
for a_tag in resp.html.findAll('a'):
if 'code/' in a_tag['href']:
tracking_code = a_tag.text
break
assert tracking_code is not None
assert formdef.data_class().count() == 1
assert formdef.data_class().select()[0].is_draft()
assert formdef.data_class().select()[0].tracking_code == tracking_code
assert formdef.data_class().select()[0].data['0'] == 'foobar'
formdata_id = formdef.data_class().select()[0].id
# check we can load the formdata as a draft
resp = get_app(pub).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()
assert resp.location == 'http://example.net/test/%s' % formdata_id
resp = resp.follow()
assert resp.location.startswith('http://example.net/test/?mt=')
resp = resp.follow()
resp = resp.forms[0].submit('previous')
assert resp.forms[0]['f0'].value == 'foobar'
# check submitted form keeps the tracking code
resp.forms[0]['f0'] = 'barfoo'
resp = resp.forms[0].submit('submit') # -> confirmation page
resp = resp.forms[0].submit('submit') # -> done
resp = resp.follow()
assert 'barfoo' in resp.body
assert formdef.data_class().count() == 1 # check the draft one has been removed
assert formdef.data_class().select()[0].tracking_code == tracking_code
assert formdef.data_class().select()[0].status == 'wf-new'
assert formdef.data_class().select()[0].data['0'] == 'barfoo'
formdata_id = formdef.data_class().select()[0].id
# check we can still go back to it
app = get_app(pub)
resp = app.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()
assert resp.location == 'http://example.net/test/%s' % formdata_id
resp = resp.follow()
resp = resp.follow()
assert 'form_comment' in resp.body # makes sure user is treated as submitter
resp.forms[0]['comment'] = 'hello world'
session_id = app.cookies.values()[0].strip('"')
session = BasicSession.get(session_id)
resp.forms[0]['captcha$q'] = session.get_captcha_token(resp.forms[0]['captcha$token'].value)['answer']
resp = resp.forms[0].submit()
assert formdef.data_class().get(formdata_id).evolution[-1].comment == 'hello world'
def test_form_tracking_code_as_user(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.fields = [fields.StringField(id='0', label='string')]
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.body
resp.forms[0]['f0'] = 'foobar'
resp = resp.forms[0].submit('submit')
tracking_code = None
for a_tag in resp.html.findAll('a'):
if 'code/' in a_tag['href']:
tracking_code = a_tag.text
break
assert tracking_code is not None
assert formdef.data_class().count() == 1
assert formdef.data_class().select()[0].is_draft()
assert formdef.data_class().select()[0].tracking_code == tracking_code
assert formdef.data_class().select()[0].data['0'] == 'foobar'
formdata_id = formdef.data_class().select()[0].id
# 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()
assert resp.location == 'http://example.net/test/%s' % formdata_id
resp = resp.follow()
assert resp.location.startswith('http://example.net/test/?mt=')
resp = resp.follow()
resp = resp.forms[0].submit('previous')
assert resp.forms[0]['f0'].value == 'foobar'
# check submitted form keeps the tracking code
resp.forms[0]['f0'] = 'barfoo'
resp = resp.forms[0].submit('submit') # -> confirmation page
resp = resp.forms[0].submit('submit') # -> done
resp = resp.follow()
assert 'barfoo' in resp.body
assert formdef.data_class().count() == 1 # check the draft one has been removed
assert formdef.data_class().select()[0].tracking_code == tracking_code
assert str(formdef.data_class().select()[0].user_id) == str(user.id)
assert formdef.data_class().select()[0].status == 'wf-new'
assert formdef.data_class().select()[0].data['0'] == 'barfoo'
formdata_id = formdef.data_class().select()[0].id
# check we can still go back to it
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()
assert resp.location == 'http://example.net/test/%s' % formdata_id
resp = resp.follow()
resp = resp.follow()
assert 'form_comment' in resp.body # makes sure user is treated as submitter
resp.forms[0]['comment'] = 'hello world'
resp = resp.forms[0].submit()
assert formdef.data_class().get(formdata_id).evolution[-1].comment == 'hello world'
# and check we can also get back to it as anonymous
app = get_app(pub)
resp = app.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()
assert resp.location == 'http://example.net/test/%s' % formdata_id
resp = resp.follow()
resp = resp.follow()
assert 'form_comment' in resp.body # makes sure user is treated as submitter
def test_form_tracking_code_email(pub):
formdef = create_formdef()
formdef.data_class().wipe()
formdef.fields = [fields.StringField(id='0', label='string')]
formdef.enable_tracking_codes = True
formdef.store()
formdata = formdef.data_class()()
formdata.data = {'0': 'foobar'}
formdata.tracking_code = 'ABCDEF'
formdata.store()
resp = get_app(pub).get('/test/code/ABCDEF/')
assert '<h2>Keep your tracking code</h2>' in resp.body
resp.forms[0]['email'] = 'foo@localhost'
resp = resp.forms[0].submit()
assert emails.emails.get('Tracking Code reminder')
assert 'ABCDEF' in emails.emails.values()[0]['payload']
assert resp.location == 'http://example.net/test/code/ABCDEF/load'
def test_form_invalid_tracking_code(pub):
formdef = create_formdef()
formdef.fields = [fields.StringField(id='0', label='string')]
formdef.enable_tracking_codes = True
formdef.store()
# create a secondary formdef, to always have the tracking code form
# displayed on homepage
formdef2 = FormDef()
formdef2.name = 'test2'
formdef2.fields = []
formdef2.enable_tracking_codes = True
formdef2.store()
resp = get_app(pub).get('/')
formdata = formdef.data_class()()
formdata.data = {'0': 'foobar'}
formdata.store()
# check we can go back to it
formdef.data_class().wipe()
code = pub.tracking_code_class()
code.formdata = formdata
code.store()
resp.forms[0]['code'] = code.id
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/code/%s/load' % code.id
resp = resp.follow()
assert resp.location == 'http://example.net/test/%s' % formdata.id
resp = resp.follow()
# check we get a not found error message on non-existent code
fake_code = TrackingCode().get_new_id()
resp = get_app(pub).get('/')
resp.forms[0]['code'] = fake_code
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/code/%s/load' % fake_code
resp = resp.follow(status=404)
# check we also get an error if tracking code access is disabled after the
# fact
formdef.enable_tracking_codes = False
formdef.store()
resp = get_app(pub).get('/')
resp.forms[0]['code'] = code.id
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/code/%s/load' % code.id
resp = resp.follow(status=404)
def form_password_field_submit(pub, password):
password = unicode(password).encode(pub.site_charset)
formdef = create_formdef()
formdef.fields = [fields.PasswordField(id='0', label='password',
formats=['sha1', 'md5', 'cleartext'])]
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 '<div class="error">required field</div>' in next_page.body
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.body
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.body
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': hashlib.sha1(password).hexdigest(),
'md5': hashlib.md5(password).hexdigest(),
'cleartext': unicode(password, 'utf-8'),
}}
def test_form_password_field_submit(pub):
form_password_field_submit(pub, 'foobar')
form_password_field_submit(pub, u'foobar\u00eb')
def test_form_multi_page_formdef_count_condition(pub):
formdef = create_formdef()
formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page', type='page',
condition='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.body
# add a formdata this will make the second page appear.
formdef.data_class()().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.body
def test_form_multi_page_post_edit(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page', type='page'),
fields.StringField(id='3', label='string 2')]
formdef.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
editable = EditableWorkflowStatusItem()
editable.id = '_editable'
editable.by = ['_submitter', '_receiver']
st1.items.append(editable)
editable.parent = st1
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.body
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.body
assert 'barXYZ' in page.body
resp = page.forms[0].submit('button_editable')
assert resp.location == 'http://example.net/test/%s/wfedit' % data_id
resp = resp.follow()
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.body
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/test/%s/' % data_id
resp = resp.follow()
assert 'foo2' in resp.body # modified value is there
assert 'barXYZ' in resp.body # unchanged value is still there
def test_form_count_dispatching(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.fields = []
formdef.store()
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
jump = JumpWorkflowStatusItem()
jump.condition = 'form_objects.count_status_st2 < 1'
jump.status = 'st2'
st1.items.append(jump)
jump.parent = st1
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.body # 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.body # 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 no formdata gets stored
next_page = page.forms[0].submit('submit')
assert 'Check values then click submit.' in next_page.body
next_page = next_page.forms[0].submit('submit')
assert next_page.status_int == 302
assert next_page.location == 'http://example.net/preview/test/'
assert formdef.data_class().count() == 0
def test_form_item_data_source_field_submit(pub):
def submit_item_data_source_field(ds):
formdef = create_formdef()
formdef.fields = [fields.ItemField(id='0', label='string', data_source=ds)]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp.forms[0]['f0'] = '1'
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.body
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.body
assert formdef.data_class().count() == 1
data_id = formdef.data_class().select()[0].id
return formdef.data_class().get(data_id).data
ds = {
'type': 'formula',
'value': repr([('1', 'un'), ('2', 'deux')]),
}
assert submit_item_data_source_field(ds) == {'0': '1', '0_display': 'un'}
ds['value'] = repr([{'id': '1', 'text': 'un'}, {'id': '2', 'text': 'deux'}])
assert submit_item_data_source_field(ds) == {'0': '1', '0_display': 'un'}
ds['value'] = repr([
{'id': '1', 'text': 'un', 'more': 'foo'},
{'id': '2', 'text': 'deux', 'more': 'bar'}])
assert submit_item_data_source_field(ds) == {
'0': '1', '0_display': 'un', '0_structured': {'id': '1', 'text': 'un', 'more': 'foo'}}
def test_form_items_data_source_field_submit(pub):
def submit_items_data_source_field(ds):
formdef = create_formdef()
formdef.fields = [fields.ItemsField(id='0', label='string', data_source=ds)]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp.forms[0]['f0$element1'].checked = True
resp.forms[0]['f0$element3'].checked = True
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.body
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.body
assert formdef.data_class().count() == 1
data_id = formdef.data_class().select()[0].id
return formdef.data_class().get(data_id).data
ds = {
'type': 'formula',
'value': repr([('1', 'un'), ('2', 'deux'), ('3', 'trois')]),
}
assert submit_items_data_source_field(ds) == {'0': ['1', '3'], '0_display': 'un, trois'}
ds['value'] = repr([{'id': '1', 'text': 'un'}, {'id': '2', 'text': 'deux'},
{'id': '3', 'text': 'trois'}])
assert submit_items_data_source_field(ds) == {'0': ['1', '3'], '0_display': 'un, trois'}
ds['value'] = repr([
{'id': '1', 'text': 'un', 'more': 'foo'},
{'id': '2', 'text': 'deux', 'more': 'bar'},
{'id': '3', 'text': 'trois', 'more': 'baz'}])
assert submit_items_data_source_field(ds) == {
'0': ['1', '3'],
'0_display': 'un, trois',
'0_structured': [
{'id': '1', 'more': 'foo', 'text': 'un'},
{'id': '3', 'more': 'baz', 'text': 'trois'}]}
def test_form_page_query_string_prefill(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
formdef.fields = [fields.StringField(id='0', label='string',
prefill={'type': 'formula', 'value': 'session_var_foo'})]
formdef.store()
# check it's empty if it doesn't exist
resp = get_app(pub).get('/test/')
assert resp.forms[0]['f0'].value == ''
# check it's not set if it's not whitelisted
resp = get_app(pub).get('/?session_var_foo=hello')
assert resp.location == 'http://example.net/'
resp = resp.follow()
resp = resp.click('test')
assert resp.forms[0]['f0'].value == ''
# check it works
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write('''[options]
query_string_allowed_vars = foo,bar
''')
resp = get_app(pub).get('/?session_var_foo=hello')
assert resp.location == 'http://example.net/'
resp = resp.follow()
resp = resp.click('test')
assert resp.forms[0]['f0'].value == 'hello'
# check it survives a login
resp = get_app(pub).get('/?session_var_foo=hello2')
assert resp.location == 'http://example.net/'
resp = resp.follow()
resp = resp.click('Login')
resp = resp.follow()
resp.forms[0]['username'] = 'foo'
resp.forms[0]['password'] = 'foo'
resp = resp.forms[0].submit()
resp = resp.follow()
resp = resp.click('test')
assert resp.forms[0]['f0'].value == 'hello2'
# check repeated options are ignored
resp = get_app(pub).get('/?session_var_foo=hello&session_var_foo=hello2')
assert resp.location == 'http://example.net/'
resp = resp.follow()
resp = resp.click('test')
assert resp.forms[0]['f0'].value == ''
# check extra query string parameters are not lost
resp = get_app(pub).get('/?session_var_foo=hello&foo=bar')
assert resp.location == 'http://example.net/?foo=bar'
os.unlink(os.path.join(pub.app_dir, 'site-options.cfg'))
def test_form_captcha(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
formdef.fields = [fields.StringField(id='0', label='Some field')]
formdef.has_captcha = True
formdef.store()
# test authentic users are not presented with a captcha
resp = login(get_app(pub), username='foo', password='foo').get('/')
resp = resp.click('test')
assert 'Some field' in resp.body
# check anonymous user gets the captcha
app = get_app(pub)
resp = app.get('/')
resp = resp.click('test')
assert 'form_captcha' in resp.body
session_id = app.cookies.values()[0].strip('"')
session = BasicSession.get(session_id)
resp.forms[0]['captcha$q'] = session.get_captcha_token(resp.forms[0]['captcha$token'].value)['answer']
resp = resp.forms[0].submit()
assert 'Some field' in resp.body
# and check it gets it only once
resp = app.get('/')
resp = resp.click('test')
assert 'Some field' in resp.body
def test_form_file_field_submit(pub):
formdef = create_formdef()
formdef.fields = [fields.FileField(id='0', label='file')]
formdef.store()
formdef.data_class().wipe()
upload = Upload('test.txt', 'foobar', 'text/plain')
resp = get_app(pub).get('/test/')
resp.forms[0]['f0$file'] = upload
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.body
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.body
resp = resp.click('test.txt')
assert resp.location.endswith('/test.txt')
resp = resp.follow()
assert resp.content_type == 'text/plain'
assert resp.body == 'foobar'
def test_formdata_attachment_download(pub):
create_user(pub)
wf = Workflow(name='status')
st1 = wf.add_status('Status1', 'st1')
attach = AddAttachmentWorkflowStatusItem()
attach.id = '_attach'
attach.by = ['_submitter']
st1.items.append(attach)
attach.parent = st1
wf.store()
formdef = create_formdef()
formdef.workflow_id = wf.id
formdef.fields = []
formdef.store()
formdef.data_class().wipe()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.body
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.body
resp.forms[0]['attachment_attach'] = Upload('test.txt', 'foobar', 'text/plain')
resp = resp.forms[0].submit('button_attach')
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.evolution[-1].parts[0].__class__.__name__ == 'AttachmentEvolutionPart'
attachment = formdata.evolution[-1].parts[0]
assert attachment.content_type == 'text/plain'
assert attachment.orig_filename == 'test.txt'
resp = resp.follow() # back to form page
resp = resp.click('test.txt')
assert resp.location.endswith('/test.txt')
resp = resp.follow()
assert resp.content_type == 'text/plain'
assert resp.body == 'foobar'
def test_formdata_generated_document_download(pub):
create_user(pub)
wf = Workflow(name='status')
st1 = wf.add_status('Status1', 'st1')
export_to = ExportToModel()
export_to.label = 'create doc'
upload = QuixoteUpload('/foo/test.rtf', content_type='text/rtf')
upload.fp = StringIO.StringIO()
upload.fp.write('HELLO WORLD')
upload.fp.seek(0)
export_to.model_file = UploadedFile(pub.app_dir, None, upload)
export_to.id = '_export_to'
export_to.by = ['_submitter']
st1.items.append(export_to)
export_to.parent = st1
wf.store()
formdef = create_formdef()
formdef.workflow_id = wf.id
formdef.fields = []
formdef.store()
formdef.data_class().wipe()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.body
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
form_location = resp.location
resp = resp.follow()
assert 'The form has been recorded' in resp.body
resp = resp.form.submit('button_export_to')
resp = resp.follow() # $form/$id/create_doc
resp = resp.follow() # $form/$id/create_doc/
assert resp.body == 'HELLO WORLD'
export_to.attach_to_history = True
wf.store()
resp = login(get_app(pub), username='foo', password='foo').get(form_location)
resp = resp.form.submit('button_export_to')
assert resp.location == form_location
resp = resp.follow() # back to form page
resp = resp.click('test.rtf')
assert resp.location.endswith('/test.rtf')
resp = resp.follow()
assert resp.content_type == 'text/rtf'
assert resp.body == 'HELLO WORLD'
# change file content, same name
upload = QuixoteUpload('/foo/test.rtf', content_type='text/rtf')
upload.fp = StringIO.StringIO()
upload.fp.write('HELLO NEW WORLD')
upload.fp.seek(0)
export_to.model_file = UploadedFile(pub.app_dir, None, upload)
wf.store()
resp = login(get_app(pub), username='foo', password='foo').get(form_location)
resp = resp.form.submit('button_export_to')
assert resp.location == form_location
resp = resp.follow() # back to form page
assert resp.click('test.rtf', index=0).follow().body == 'HELLO WORLD'
assert resp.click('test.rtf', index=1).follow().body == 'HELLO NEW WORLD'
def test_formdata_form_file_download(pub):
create_user(pub)
wf = Workflow(name='status')
st1 = wf.add_status('Status1', 'st1')
display_form = FormWorkflowStatusItem()
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.FileField(id='1', label='File',
type='file', varname='yyy'))
st1.items.append(display_form)
display_form.parent = st1
wf.store()
formdef = create_formdef()
formdef.workflow_id = wf.id
formdef.fields = []
formdef.store()
formdef.data_class().wipe()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.body
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.body
resp.forms[0]['f1$file'] = Upload('test.txt', 'foobar', 'text/plain')
resp = resp.forms[0].submit('submit')
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert 'xxx_var_yyy_raw' in formdata.workflow_data
download = resp.test_app.get(resp.location + 'files/form-xxx-yyy/test.txt')
assert download.content_type == 'text/plain'
assert download.body == 'foobar'
# go back to the status page, this will exercise the substitution variables
# codepath.
resp = resp.follow()
def test_form_map_field_back_and_submit(pub):
formdef = create_formdef()
formdef.fields = [
fields.MapField(id='0', label='map'),
fields.StringField(id='1', label='street', required=False,
prefill={'type': 'geolocation', 'value': 'road'}),
]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
assert 'qommon.map.js' in resp.body
assert 'qommon.geolocation.js' in resp.body
# with a real user interaction this would get set by javascript
resp.forms[0]['f0$latlng'] = '1.234;-1.234'
assert 'data-geolocation="road"' in resp.body
# check summary page
resp = resp.forms[0].submit('submit')
assert 'Check values then click submit.' in resp.body
assert 'data-init-lng="-1.234"' in resp.body
assert 'data-init-lat="1.234"' in resp.body
# get back to the map field
resp = resp.forms[0].submit('previous')
# check the field is still marked as holding the road
assert 'data-geolocation="road"' in resp.body
assert resp.forms[0]['f0$latlng'].value == '1.234;-1.234'
# back to summary page
resp = resp.forms[0].submit('submit')
# and submitting the form
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.body
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': None, '0': '1.234;-1.234'}
def test_form_map_multi_page(pub):
formdef = create_formdef()
formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
fields.MapField(id='1', label='map'),
fields.PageField(id='2', label='2nd page', type='page'),
fields.StringField(id='3', label='string 2')]
formdef.store()
resp = get_app(pub).get('/test/')
formdef.data_class().wipe()
resp.forms[0]['f1$latlng'] = '1.234;-1.234'
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 resp.forms[0]['f1$latlng'].value == '1.234;-1.234'
assert 'Check values then click submit.' in resp.body
resp = resp.forms[0].submit('submit')
assert resp.status_int == 302
resp = resp.follow()
assert 'The form has been recorded' in resp.body
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': '1.234;-1.234', '3': 'bar'}
def test_form_middle_session_change(pub):
formdef = create_formdef()
formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
fields.StringField(id='1', label='string'),
fields.PageField(id='2', label='2nd page', type='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.body
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.body
app.cookiejar.clear()
resp = resp.forms[0].submit('submit')
resp = resp.follow()
assert 'Sorry, your session have been lost.' in resp.body
def test_form_autocomplete_variadic_url(pub):
formdef = create_formdef()
formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
fields.ItemField(id='1', label='string', type='item',
varname='foo', items=['Foo', 'Bar']),
fields.StringField(id='2', label='string2',
data_source={'type': 'jsonp', 'value': '[var_foo]'}),
fields.PageField(id='3', label='2nd page', type='page',
condition='foo == "Foo"'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
# test javascript will be used to compute the full URL
assert 'options.wcs_base_url' in resp.body