wcs/tests/form_pages/test_live.py

1267 lines
47 KiB
Python

import datetime
import io
import json
from unittest import mock
import pytest
from webtest import Hidden
from wcs import fields
from wcs.blocks import BlockDef
from wcs.carddef import CardDef
from wcs.data_sources import NamedDataSource
from wcs.formdef import FormDef
from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef
from wcs.workflows import Workflow
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app, login
from .test_all import create_user
def pytest_generate_tests(metafunc):
if 'pub' in metafunc.fixturenames:
metafunc.parametrize('pub', ['pickle', 'sql', 'pickle-templates', 'pickle-lazy'], indirect=True)
@pytest.fixture
def pub(request):
pub = create_temporary_pub(
sql_mode=bool('sql' in request.param),
templates_mode=bool('templates' in request.param),
lazy_mode=bool('lazy' in request.param),
)
pub.cfg['identification'] = {'methods': ['password']}
pub.cfg['language'] = {'language': 'en'}
pub.write_cfg()
return pub
def teardown_module(module):
clean_temporary_pub()
def test_field_live_condition(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.StringField(type='string', id='1', label='Bar', size='40', required=True, varname='bar'),
fields.StringField(
type='string',
id='2',
label='Foo',
size='40',
required=True,
varname='foo',
condition={'type': 'django', 'value': 'form_var_bar == "bye"'},
),
]
formdef.store()
app = get_app(pub)
resp = app.get('/foo/')
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
resp.form['f1'] = 'hello'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert not live_resp.json['result']['2']['visible']
resp.form['f1'] = 'bye'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
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 '<span class="label">Bar</span>' in resp.text
assert '<span class="label">Foo</span>' not in resp.text
resp = get_app(pub).get('/foo/')
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
resp.form['f1'] = 'bye'
resp = resp.form.submit('submit')
assert 'There were errors' in resp.text
assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') is None
resp.form['f2'] = 'bye'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'name="f1"' in resp.text
assert 'name="f2"' in resp.text
resp = resp.form.submit('submit')
resp = resp.follow()
assert '<span class="label">Bar</span>' in resp.text
assert '<span class="label">Foo</span>' in resp.text
def test_field_live_items_condition(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.ItemsField(type='items', id='1', label='Bar', items=['a', 'b'], varname='bar'),
fields.StringField(
type='string',
id='2',
label='Foo',
size='40',
required=True,
varname='foo',
condition={'type': 'django', 'value': '"b" in form_var_bar'},
),
]
formdef.store()
create_user(pub)
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/foo/')
assert 'f1$element0' in resp.form.fields
assert 'f1$element1' in resp.form.fields
assert 'f2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
resp.form['f1$element0'].checked = True
app.post('/foo/autosave', params=resp.form.submit_fields())
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert not live_resp.json['result']['2']['visible']
resp.form['f1$element1'].checked = True
app.post('/foo/autosave', params=resp.form.submit_fields())
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
resp.form['f1$element0'].checked = False
resp.form['f1$element1'].checked = False
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert not live_resp.json['result']['2']['visible']
def test_live_field_condition_on_required_field(pub):
# from https://dev.entrouvert.org/issues/27247
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.PageField(id='0', label='1st page', type='page'),
fields.ItemField(
type='item',
id='1',
label='Bar',
items=['oui', 'non'],
display_mode='radio',
required=True,
varname='bar',
),
fields.ItemField(
type='item',
id='2',
label='Foo',
size='40',
required=True,
hint='---',
items=['plop'],
condition={'type': 'django', 'value': 'form_var_bar == "oui"'},
),
fields.PageField(
id='3', label='1st page', type='page', condition={'type': 'python', 'value': 'True'}
),
fields.CommentField(type='comment', id='4', label='HELLO!'),
]
formdef.store()
app = get_app(pub)
resp = app.get('/foo/')
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
resp.form['f1'] = 'non'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert not live_resp.json['result']['2']['visible']
resp = resp.form.submit('submit')
assert 'HELLO' in resp.text
resp = resp.form.submit('previous')
resp.form['f1'] = 'oui'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
resp = resp.form.submit('submit')
assert resp.pyquery('div.error').text() == 'required field'
assert 'HELLO' not in resp.text
def test_field_live_condition_multipages(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.PageField(id='0', label='2nd page', type='page'),
fields.StringField(type='string', id='1', label='Bar', size='40', required=True, varname='bar'),
fields.StringField(
type='string',
id='2',
label='Foo',
size='40',
required=True,
varname='foo',
condition={'type': 'django', 'value': 'form_var_bar == "bye"'},
),
fields.PageField(id='3', label='1st page', type='page'),
fields.StringField(type='string', id='4', label='Baz', size='40', required=True, varname='baz'),
]
formdef.store()
app = get_app(pub)
resp = app.get('/foo/')
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
resp.form['f1'] = 'hello'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert not live_resp.json['result']['2']['visible']
resp.form['f1'] = 'bye'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
resp.form['f1'] = 'bye'
resp.form['f2'] = 'bye'
resp = resp.form.submit('submit')
resp = resp.form.submit('previous')
assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') is None
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
resp = resp.form.submit('submit')
resp.form['f4'] = 'plop'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'name="f1"' in resp.text
assert 'name="f2"' in resp.text
assert 'name="f4"' in resp.text
resp = resp.form.submit('submit')
def test_field_live_select_content(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.StringField(type='string', id='1', label='Bar', size='40', required=True, varname='bar'),
fields.StringField(type='string', id='2', label='Bar2', size='40', required=True, varname='bar2'),
fields.ItemField(
type='item',
id='3',
label='Foo',
data_source={
'type': 'json',
'value': '{% if form_var_bar2 %}http://remote.example.net/json-list?plop={{form_var_bar2}}{% endif %}',
},
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '2'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '3'}).find('select')
resp.form['f1'] = 'hello'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
assert live_resp.json['result']['3']['visible']
assert 'items' not in live_resp.json['result']['3']
resp.form['f2'] = 'plop'
live_resp = app.post('/foo/live?modified_field_id=2', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
assert live_resp.json['result']['3']['visible']
assert 'items' in live_resp.json['result']['3']
resp.form['f3'].options = []
for item in live_resp.json['result']['3']['items']:
# simulate javascript filling the <select>
resp.form['f3'].options.append((item['id'], False, item['text']))
resp.form['f3'] = 'a'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'name="f1"' in resp.text
assert 'name="f2"' in resp.text
assert 'name="f3"' in resp.text
resp = resp.form.submit('submit')
resp = resp.follow()
formdata = formdef.data_class().select()[0]
assert formdata.data['1'] == 'hello'
assert formdata.data['2'] == 'plop'
assert formdata.data['3'] == 'a'
assert formdata.data['3_display'] == 'b'
# create and use geojson datasource
NamedDataSource.wipe()
data_source = NamedDataSource(name='geofoobar')
data_source.data_source = {
'type': 'geojson',
'value': 'http://remote.example.net/geojson?plop={{form_var_bar2}}',
}
data_source.id_property = 'id'
data_source.label_template_property = '{{ text }}'
data_source.cache_duration = '5'
data_source.store()
formdef.fields[2].data_source = {'type': 'geofoobar'}
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '2'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '3'}).find('select')
resp.form['f1'] = 'hello'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
assert live_resp.json['result']['3']['visible']
assert 'items' not in live_resp.json['result']['3']
resp.form['f2'] = 'plop'
live_resp = app.post('/foo/live?modified_field_id=2', params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
assert live_resp.json['result']['3']['visible']
assert 'items' in live_resp.json['result']['3']
resp.form['f3'].options = []
for item in live_resp.json['result']['3']['items']:
# simulate javascript filling the <select>
resp.form['f3'].options.append((item['id'], False, item['text']))
resp.form['f3'] = '1'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'name="f1"' in resp.text
assert 'name="f2"' in resp.text
assert 'name="f3"' in resp.text
resp = resp.form.submit('submit')
resp = resp.follow()
formdata = formdef.data_class().select()[0]
assert formdata.data['1'] == 'hello'
assert formdata.data['2'] == 'plop'
assert formdata.data['3'] == '1'
assert formdata.data['3_display'] == 'foo'
def test_field_live_select_content_on_workflow_form(pub, http_requests):
create_user(pub)
wf = Workflow(name='wf-title')
st1 = wf.add_status('Status1', 'st1')
# form displayed into the workflow
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 = [
fields.StringField(type='string', id='1', label='Bar', size='40', required=True, varname='bar'),
fields.StringField(type='string', id='2', label='Bar2', size='40', required=True, varname='bar2'),
fields.ItemField(
type='item',
id='3',
label='Foo',
required=False,
varname='foo',
data_source={
'type': 'json',
'value': '{% if xxx_var_bar2 %}http://remote.example.net/json-list?plop={{xxx_var_bar2}}{% endif %}',
},
),
]
st1.items.append(display_form)
display_form.parent = st1
wf.store()
# initial empty form
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = []
formdef.confirmation = False
formdef.workflow_id = wf.id
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = login(app, username='foo', password='foo').get('/test/')
assert 'Forms - test' in resp.text
resp = resp.form.submit('submit').follow()
assert 'The form has been recorded' in resp.text
assert 'data-live-url' in resp.html.find('form').attrs
assert 'fxxx_1' in resp.form.fields
assert 'fxxx_2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': 'xxx_2'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': 'xxx_3'}).find('select')
resp = resp.form.submit('submit') # submit with error, to check <form> attributes
assert 'data-live-url' in resp.html.find('form').attrs
assert 'fxxx_1' in resp.form.fields
assert 'fxxx_2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': 'xxx_2'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': 'xxx_3'}).find('select')
resp.form['fxxx_1'] = 'hello'
live_resp = app.post('/test/1/live', params=resp.form.submit_fields())
assert live_resp.json['result']['xxx_1']['visible']
assert live_resp.json['result']['xxx_2']['visible']
assert live_resp.json['result']['xxx_3']['visible']
assert 'items' not in live_resp.json['result']['xxx_3']
resp.form['fxxx_2'] = 'plop'
live_resp = app.post('/test/1/live?modified_field_id=xxx_2', params=resp.form.submit_fields())
assert live_resp.json['result']['xxx_1']['visible']
assert live_resp.json['result']['xxx_2']['visible']
assert live_resp.json['result']['xxx_3']['visible']
assert 'items' in live_resp.json['result']['xxx_3']
assert len(live_resp.json['result']['xxx_3']['items']) > 0
resp.form['fxxx_3'].options = []
for item in live_resp.json['result']['xxx_3']['items']:
# simulate javascript filling the <select>
resp.form['fxxx_3'].options.append((item['id'], False, item['text']))
resp.form['fxxx_3'] = 'a'
resp = resp.form.submit('submit')
assert 'invalid value selected' not in resp
resp = resp.follow()
formdata = formdef.data_class().select()[0]
assert formdata.workflow_data['xxx_var_bar'] == 'hello'
assert formdata.workflow_data['xxx_var_bar2'] == 'plop'
assert formdata.workflow_data['xxx_var_foo_raw'] == 'a'
assert formdata.workflow_data['xxx_var_foo'] == 'b'
def test_field_live_select_content_based_on_prefill(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.StringField(
type='string',
id='1',
label='Bar',
size='40',
required=True,
varname='bar',
prefill={'type': 'string', 'value': 'HELLO WORLD'},
),
fields.ItemField(
type='item',
id='2',
label='Foo',
data_source={
'type': 'json',
'value': '{% if form_var_bar %}http://remote.example.net/json-list?plop={{form_var_bar}}{% endif %}',
},
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '2'}).find('select')
assert resp.html.find('option', {'value': 'a'})
assert http_requests.get_last('url') == 'http://remote.example.net/json-list?plop=HELLO WORLD'
# check with autocomplete and a remote source with id
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {
'type': 'json',
'value': 'http://remote.example.net/json?plop={{form_var_bar}}',
}
data_source.query_parameter = 'q'
data_source.id_parameter = 'id'
data_source.store()
formdef.fields[1].display_mode = 'autocomplete'
formdef.fields[1].data_source['type'] = 'foobar'
formdef.store()
app = get_app(pub)
resp = app.get('/foo/')
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
def test_field_live_select_content_on_other_default_select_option(pub, http_requests):
create_user(pub)
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.ItemField(
type='item',
id='2',
label='Foo',
varname='bar2',
data_source={'type': 'json', 'value': 'http://remote.example.net/json-list'},
),
fields.ItemField(
type='item',
id='3',
label='Foo',
data_source={
'type': 'json',
'value': '{% if form_var_bar2 %}http://remote.example.net/json-list?plop={{form_var_bar2}}{% endif %}',
},
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert 'f2' in resp.form.fields
assert 'f3' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '2'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '3'}).find('select')
# javascript will make an initial call with ?modified_dield_id=init,
# simulate.
live_resp = app.post('/foo/live?modified_field_id=init', params=resp.form.submit_fields())
assert 'items' in live_resp.json['result']['3']
resp.form['f3'].options = []
for item in live_resp.json['result']['3']['items']:
# simulate javascript filling the <select>
resp.form['f3'].options.append((item['id'], False, item['text']))
resp.form['f3'] = 'a'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'name="f2"' in resp.text
assert 'name="f3"' in resp.text
resp = resp.form.submit('submit')
resp = resp.follow()
formdata = formdef.data_class().select()[0]
assert formdata.data['2'] == 'a'
assert formdata.data['3'] == 'a'
def test_field_live_select(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.StringField(type='string', id='2', label='Bar2', size='40', required=True, varname='bar2'),
fields.ItemField(
type='item',
id='3',
label='Foo',
data_source={
'type': 'json',
'value': '{% if form_var_bar2 %}http://remote.example.net/json-list-extra-with-disabled?plop={{form_var_bar2}}{% endif %}',
},
display_disabled_items=True,
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert resp.html.find('div', {'data-field-id': '2'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '3'}).find('select')
resp.form['f2'] = 'plop'
live_resp = app.post('/foo/live?modified_field_id=2', params=resp.form.submit_fields())
assert len(live_resp.json['result']['3']['items']) == 2
assert live_resp.json['result']['3']['items'][1]['disabled'] is True
formdef.fields[1].display_disabled_items = False
formdef.store()
resp = app.get('/foo/')
resp.form['f2'] = 'plop'
live_resp = app.post('/foo/live?modified_field_id=2', params=resp.form.submit_fields())
assert len(live_resp.json['result']['3']['items']) == 1
def test_field_live_template_content(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.StringField(type='string', id='2', label='Bar2', size='40', required=True, varname='bar2'),
fields.ItemField(
type='item',
id='3',
label='Foo',
extra_css_class='template-whatever',
data_source={
'type': 'json',
'value': '{% if form_var_bar2 %}http://remote.example.net/json-list-extra-with-disabled?plop={{form_var_bar2}}{% endif %}',
},
display_disabled_items=True,
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert resp.html.find('div', {'data-field-id': '2'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '3'}).find('select')
resp.form['f2'] = 'plop'
live_resp = app.post('/foo/live?modified_field_id=2', params=resp.form.submit_fields())
assert live_resp.json['result']['3']['items'][0]['foo'] == 'bar'
assert len(live_resp.json['result']['3']['items']) == 2
assert live_resp.json['result']['3']['items'][1]['disabled'] is True
formdef.fields[1].display_disabled_items = False
formdef.store()
resp = app.get('/foo/')
resp.form['f2'] = 'plop'
live_resp = app.post('/foo/live?modified_field_id=2', params=resp.form.submit_fields())
assert len(live_resp.json['result']['3']['items']) == 1
def test_field_live_timetable_select(pub, http_requests):
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {
'type': 'json',
'value': 'http://remote.example.net/api/datetimes{% if form_var_bar2 %}?{% endif %}',
}
data_source.store()
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.StringField(type='string', id='2', label='Bar2', size='40', required=True, varname='bar2'),
fields.ItemField(
id='3',
type='item',
label='datetime',
display_mode='timetable',
data_source={'type': 'foobar'},
display_disabled_items=True,
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
with mock.patch('wcs.qommon.misc.urlopen') as urlopen:
data = {
"data": [
{"id": "1", "datetime": "2021-01-12 10:00:00", "text": "event 1", "api": {}},
{"id": "2", "datetime": "2021-01-13 10:20:00", "text": "event 2", "api": {}},
{
"id": "3",
"datetime": "2021-01-14 10:40:00",
"text": "event 3",
"api": {},
"disabled": True,
},
]
}
urlopen.side_effect = lambda *args: io.StringIO(json.dumps(data))
resp = app.get('/foo/')
assert resp.html.find('div', {'data-field-id': '2'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '3'}).find('select')
resp.form['f2'] = 'plop'
live_resp = app.post('/foo/live?modified_field_id=2', params=resp.form.submit_fields())
assert 'datetime' in live_resp.json['result']['3']['items'][0]
assert 'api' not in live_resp.json['result']['3']['items'][0]
assert len(live_resp.json['result']['3']['items']) == 3
assert live_resp.json['result']['3']['items'][2]['disabled'] is True
formdef.fields[1].display_disabled_items = False
formdef.store()
resp = app.get('/foo/')
assert resp.html.find('div', {'data-field-id': '2'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '3'}).find('select')
resp.form['f2'] = 'plop'
live_resp = app.post('/foo/live?modified_field_id=2', params=resp.form.submit_fields())
assert 'datetime' in live_resp.json['result']['3']['items'][0]
assert 'api' not in live_resp.json['result']['3']['items'][0]
assert len(live_resp.json['result']['3']['items']) == 2
def test_field_live_comment_content(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.StringField(type='string', id='1', label='Bar', size='40', required=True, varname='bar'),
fields.StringField(type='string', id='2', label='Baz', size='40'),
fields.CommentField(id='5', label='bla {{form_var_bar}} bla', type='comment'),
fields.StringField(type='string', id='6', label='Bar2', size='40', required=True, varname='bar2'),
fields.CommentField(id='7', label='bla {{form_var_bar2}} bla', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert 'f1' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
resp.form['f1'] = 'hello'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['5']['content'] == '<p>bla hello bla</p>'
resp.form['f1'] = 'toto'
live_resp = app.post('/foo/live?modified_field_id=1', params=resp.form.submit_fields())
assert live_resp.json['result']['5']['content'] == '<p>bla toto bla</p>'
live_resp = app.post('/foo/live?modified_field_id=2', params=resp.form.submit_fields())
assert live_resp.json['result']['5']['content'] == '<p>bla toto bla</p>'
# check evaluation of later fields
# <https://dev.entrouvert.org/issues/31922>
resp = app.get('/foo/')
resp.form['f1'] = 'hello'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['5']['content'] == '<p>bla hello bla</p>'
resp.form['f6'] = 'hello'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['7']['content'] == '<p>bla hello bla</p>'
def test_field_live_comment_content_from_structured_item_data(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.ItemField(
type='item',
id='1',
label='Foo',
varname='bar',
data_source={'type': 'json', 'value': 'http://remote.example.net/json-list-extra'},
),
fields.CommentField(id='7', label='bla {{form_var_bar_structured_foo}} bla', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert 'f1' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
resp.form['f1'] = 'a'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
assert live_resp.json['result']['7']['content'] == '<p>bla bar bla</p>'
def test_field_live_string_prefill(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.StringField(type='string', id='1', label='Bar', size='40', required=True, varname='bar'),
fields.StringField(
type='string',
id='2',
label='Bar2',
size='40',
required=True,
varname='bar2',
prefill={'type': 'string', 'value': '{{form_var_bar|default:""}}'},
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.pyquery('#var_bar2.widget-prefilled') # second field is marked as prefilled
assert resp.form['f2'].value == ''
resp.form['f1'] = 'hello'
live_resp = app.post('/foo/live?modified_field_id=1&prefilled_2=on', params=resp.form.submit_fields())
assert live_resp.json['result']['2'] == {'visible': True, 'content': 'hello'}
resp.form['f2'] = 'other' # manually changed -> widget-prefilled class will be removed
resp.form['f1'] = 'xxx'
live_resp = app.post('/foo/live?modified_field_id=1', params=resp.form.submit_fields())
assert live_resp.json['result']['2'] == {'visible': True}
# check it's not possible to declare user change from frontoffice
live_resp = app.post('/foo/live?modified_field_id=user', params=resp.form.submit_fields(), status=403)
def test_field_live_bool_prefill(pub, http_requests):
CardDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.digest_templates = {'default': '{{ form_var_foo }}'}
carddef.fields = [
fields.StringField(id='1', type='string', label='string', varname='foo'),
fields.BoolField(
type='bool',
id='2',
varname='bool',
),
]
carddef.store()
carddef.data_class().wipe()
carddata1 = carddef.data_class()()
carddata1.data = {
'1': 'bar',
'2': True,
}
carddata1.just_created()
carddata1.store()
carddata2 = carddef.data_class()()
carddata2.data = {
'1': 'baz',
'2': False,
}
carddata2.just_created()
carddata2.store()
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.ItemField(
type='item', id='1', label='foo', varname='foo', data_source={'type': 'carddef:foo'}
),
fields.BoolField(
type='bool',
id='2',
label='bool',
varname='bool',
prefill={'type': 'string', 'value': '{{ form_var_foo_live_var_bool }}'},
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.pyquery('#var_bool.widget-prefilled') # second field is marked as prefilled
assert resp.form['f2'].value is None
resp.form['f1'] = str(carddata1.id)
live_resp = app.post('/foo/live?modified_field_id=1&prefilled_2=on', params=resp.form.submit_fields())
assert live_resp.json['result']['2'] == {'visible': True, 'content': True}
resp.form['f2'] = False # manually changed -> widget-prefilled class will be removed
resp.form['f1'] = str(carddata2.id)
live_resp = app.post('/foo/live?modified_field_id=1', params=resp.form.submit_fields())
assert live_resp.json['result']['2'] == {'visible': True}
def test_field_live_date_prefill(pub, http_requests):
pub.cfg['language'] = {'language': 'fr'}
pub.write_cfg()
CardDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.digest_templates = {'default': '{{ form_var_foo }}'}
carddef.fields = [
fields.StringField(id='1', type='string', label='string', varname='foo'),
fields.DateField(
type='date',
id='2',
varname='date',
),
]
carddef.store()
carddef.data_class().wipe()
carddata1 = carddef.data_class()()
carddata1.data = {
'1': 'bar',
'2': datetime.date(2021, 10, 1).timetuple(),
}
carddata1.just_created()
carddata1.store()
carddata2 = carddef.data_class()()
carddata2.data = {
'1': 'baz',
'2': datetime.date(2021, 10, 30).timetuple(),
}
carddata2.just_created()
carddata2.store()
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.ItemField(
type='item', id='1', label='foo', varname='foo', data_source={'type': 'carddef:foo'}
),
fields.DateField(
type='date',
id='2',
label='date',
varname='date',
prefill={'type': 'string', 'value': '{{ form_var_foo_live_var_date }}'},
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.pyquery('#var_date.widget-prefilled') # second field is marked as prefilled
assert resp.form['f2'].value == ''
resp.form['f1'] = str(carddata1.id)
live_resp = app.post('/foo/live?modified_field_id=1&prefilled_2=on', params=resp.form.submit_fields())
assert live_resp.json['result']['2'] == {'visible': True, 'content': '2021-10-01'}
resp.form['f2'] = '2021-10-30' # manually changed -> widget-prefilled class will be removed
resp.form['f1'] = str(carddata2.id)
live_resp = app.post('/foo/live?modified_field_id=1', params=resp.form.submit_fields())
assert live_resp.json['result']['2'] == {'visible': True}
def test_field_live_block_string_prefill(pub, http_requests):
FormDef.wipe()
BlockDef.wipe()
block = BlockDef()
block.name = 'foobar'
block.fields = [
fields.StringField(
id='123',
required=True,
label='Test',
type='string',
prefill={'type': 'string', 'value': '{{form_var_foo|default:""}}'},
),
]
block.store()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [
fields.StringField(id='1', label='test', type='string', varname='foo'),
fields.BlockField(id='2', label='test', type='block:foobar'),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get(formdef.get_url())
live_url = resp.html.find('form').attrs['data-live-url']
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.pyquery('[data-field-id="123"].widget-prefilled') # block/string
assert resp.form['f2$element0$f123'].value == ''
resp.form['f1'] = 'hello'
live_resp = app.post(live_url + '?modified_field_id=1&prefilled_123=on', params=resp.form.submit_fields())
assert live_resp.json['result'] == {
'1': {'visible': True},
'2': {'visible': True},
'2-123': {'block_id': '2', 'content': 'hello', 'field_id': '123', 'visible': True},
}
def test_field_live_condition_unknown_page_id(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.PageField(id='0', label='2nd page', type='page'),
fields.StringField(type='string', id='1', label='Bar', size='40', required=True, varname='bar'),
fields.StringField(
type='string',
id='2',
label='Foo',
size='40',
required=True,
varname='foo',
condition={'type': 'django', 'value': 'form_var_bar == "bye"'},
),
fields.PageField(id='3', label='1st page', type='page'),
fields.StringField(type='string', id='4', label='Baz', size='40', required=True, varname='baz'),
]
formdef.store()
app = get_app(pub)
resp = app.get('/foo/')
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
resp.form['f1'] = 'hello'
params = resp.form.submit_fields()
params = [(key, value if key != 'page_id' else 'eiuiu') for key, value in params]
app.post('/foo/live', params=params)
def test_field_live_locked_prefilled_field(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.StringField(type='string', id='1', label='Bar', size='40', required=True, varname='bar'),
fields.StringField(
type='string',
id='2',
label='locked',
size='40',
required=True,
prefill={'type': 'string', 'value': 'bla {{form_var_bar}} bla', 'locked': True},
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert 'f1' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
resp.form['f1'] = 'hello'
live_resp = app.post('/foo/live?prefilled_2=on', params=resp.form.submit_fields())
assert live_resp.json['result']['2']['content'] == 'bla hello bla'
resp.form['f1'] = 'toto'
live_resp = app.post('/foo/live?modified_field_id=1&prefilled_2=on', params=resp.form.submit_fields())
assert live_resp.json['result']['2']['content'] == 'bla toto bla'
def test_field_live_locked_error_prefilled_field(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.StringField(
type='string',
id='2',
label='locked',
size='40',
required=True,
prefill={'type': 'string', 'value': 'bla {% if foo %}{{ foo }}{% end %}', 'locked': True},
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert 'readonly' in resp.form['f2'].attrs
assert not resp.form['f2'].attrs.get('value')
def test_dynamic_item_field_from_custom_view_on_cards(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
pub.role_class.wipe()
pub.custom_view_class.wipe()
user = create_user(pub)
role = pub.role_class(name='xxx')
role.store()
user.roles = [role.id]
user.is_admin = True
user.store()
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = []
formdef.store()
formdef.data_class().wipe()
CardDef.wipe()
carddef = CardDef()
carddef.name = 'items'
carddef.digest_templates = {'default': '{{form_var_attr}}'}
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.fields = [
fields.ItemField(id='0', type='item', label='item', varname='item', items=['foo', 'bar', 'baz']),
fields.StringField(id='1', type='string', label='string', varname='attr'),
]
carddef.store()
carddef.data_class().wipe()
baz_ids = set()
for i, value in enumerate(['foo', 'bar', 'baz'] * 10):
carddata = carddef.data_class()()
carddata.data = {
'0': value,
'0_display': value,
'1': 'attr%s' % i,
}
carddata.just_created()
carddata.store()
if value == 'baz':
baz_ids.add(str(carddata.id))
# create custom view
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/backoffice/data/items/')
if pub.is_using_postgresql():
assert resp.text.count('<tr') == 21 # thead + 20 items (max per page)
else:
assert resp.text.count('<tr') == 31 # thead + all items
resp.forms['listing-settings']['filter-0'].checked = True
resp.forms['listing-settings']['filter-status'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['listing-settings']['filter'].value = 'recorded'
resp = resp.forms['listing-settings'].submit()
resp.forms['save-custom-view']['title'] = 'as data source'
resp.forms['save-custom-view']['visibility'] = 'datasource'
resp = resp.forms['save-custom-view'].submit().follow()
assert resp.forms['listing-settings']['filter-0-value'].attrs['data-allow-template']
assert 'custom value' in [x[2] for x in resp.forms['listing-settings']['filter-0-value'].options]
resp.forms['listing-settings']['filter-0-value'].force_value('{{ form_var_blah }}')
resp = resp.forms['listing-settings'].submit()
assert resp.forms['listing-settings']['filter-0-value'].value == '{{ form_var_blah }}'
assert resp.text.count('<tr') == 1 # thead only
# save custom view with filter
resp = resp.forms['save-custom-view'].submit().follow()
custom_view = pub.custom_view_class.select()[0]
# use custom view as source
ds = {'type': 'carddef:%s:%s' % (carddef.url_name, custom_view.slug)}
formdef.fields = [
fields.ItemField(id='0', type='item', label='item', varname='blah', items=['foo', 'bar', 'baz']),
fields.ItemField(id='1', label='string', type='item', data_source=ds, display_disabled_items=True),
]
formdef.store()
resp = get_app(pub).get('/test/')
assert resp.form['f1'].options == [('', False, '---')]
resp.form['f0'] = 'baz'
live_resp = app.post('/test/live?modified_field_id=0', params=resp.form.submit_fields())
assert len(live_resp.json['result']['1']['items']) == 10
assert {str(x['id']) for x in live_resp.json['result']['1']['items']} == baz_ids
resp.form['f1'].options = []
for item in live_resp.json['result']['1']['items']:
# simulate javascript filling the <select>
resp.form['f1'].options.append((str(item['id']), False, item['text']))
resp.form['f1'] = resp.form['f1'].options[0][0]
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submit
assert formdef.data_class().select()[0].data['1'] in baz_ids
assert formdef.data_class().select()[0].data['1_structured']['item'] == 'baz'
# same in autocomplete mode
formdef.fields[1].display_mode = 'autocomplete'
formdef.store()
app = get_app(pub)
resp = app.get('/test/')
# simulate select2 mode, with qommon.forms.js adding an extra hidden widget
resp.form.fields['f1_display'] = Hidden(form=resp.form, tag='input', name='f1_display', pos=10)
select2_url = resp.pyquery('select:last').attr['data-select2-url']
resp_json = app.get(select2_url + '?q=')
assert len(resp_json.json['data']) == 0
resp.form['f0'] = 'baz'
live_resp = app.post('/test/live?modified_field_id=0', params=resp.form.submit_fields())
new_select2_url = live_resp.json['result']['1']['source_url']
resp_json = app.get(new_select2_url + '?q=')
assert len(resp_json.json['data']) == 10
assert {str(x['id']) for x in resp_json.json['data']} == baz_ids
resp.form['f1'].force_value(str(resp_json.json['data'][0]['id']))
resp.form.fields['f1_display'].force_value(resp_json.json['data'][0]['text'])
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submit
assert formdef.data_class().select()[0].data['1'] in baz_ids
assert formdef.data_class().select()[0].data['1_structured']['item'] == 'baz'
# delete custom view
if pub.is_using_postgresql():
pub.loggederror_class.wipe()
custom_view.remove_self()
resp = get_app(pub).get('/test/')
assert resp.form['f1'].options == []
if pub.is_using_postgresql():
assert pub.loggederror_class.count() == 1
logged_error = pub.loggederror_class.select()[0]
assert logged_error.formdef_id == formdef.id
assert logged_error.summary == '[DATASOURCE] Unknown custom view "as-data-source" for CardDef "items"'
def test_item_field_from_cards_check_lazy_live(pub):
create_user(pub)
CardDef.wipe()
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()
carddef.data_class().wipe()
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()
ds = {'type': 'carddef:%s' % carddef.url_name}
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.PageField(id='0', label='1st page', type='page'),
fields.ItemField(id='1', label='string', type='item', varname='item', data_source=ds),
fields.PageField(id='2', label='2nd page', type='page'),
fields.CommentField(id='3', label='live value: {{ form_var_item_live_var_attr }}', type='comment'),
fields.PageField(id='4', label='3rd page', type='page'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.form['f1'] = '2'
resp = resp.form.submit('submit') # -> 2nd page
assert 'live value: attr1' in resp
# add a field with a condition on first page and third page
formdef.fields[1:1] = [
fields.StringField(
id='5',
label='field with condition',
type='string',
required=False,
condition={'type': 'django', 'value': '1'},
),
]
formdef.fields.append(
fields.StringField(
id='6',
label='second field with condition',
type='string',
required=False,
condition={'type': 'django', 'value': '1'},
)
)
formdef.store()
resp = get_app(pub).get('/test/')
resp.form['f1'] = '2'
resp = resp.form.submit('submit') # -> 2nd page
assert 'live value: attr1' in resp
resp = resp.form.submit('submit') # -> 3rd page
resp = resp.form.submit('previous') # -> 2nd page
assert 'live value: attr1' in resp