Compare commits

..

9 Commits

Author SHA1 Message Date
Valentin Deniaud 6fb6771b4b workflow_tests: apply global action timeout trigger on skip time (#88404)
gitea/wcs/pipeline/head There was a failure building this commit Details
2024-03-27 09:56:05 +01:00
Valentin Deniaud 2d71bbf3e5 workflow_tests: fix crash on skip time with no jumps (#88404) 2024-03-27 09:56:05 +01:00
Valentin Deniaud 75bc2011e4 workflow_test: mock date globally for skip time action (#88412)
gitea/wcs/pipeline/head This commit looks good Details
2024-03-26 18:18:44 +01:00
Frédéric Péters d0358afa40 tests: check update of items relations (#88687)
gitea/wcs/pipeline/head This commit looks good Details
2024-03-26 14:53:21 +01:00
Frédéric Péters 4d5b309986 misc: use custom id in breadcrumb (#88557)
gitea/wcs/pipeline/head This commit looks good Details
2024-03-23 12:15:31 +01:00
Lauréline Guérin e0857ce653
admin: rename overwrite button for blockdef (#88502)
gitea/wcs/pipeline/head This commit looks good Details
2024-03-22 10:51:45 +01:00
Frédéric Péters 8c3374e790 translation update
gitea/wcs/pipeline/head This commit looks good Details
2024-03-21 19:00:39 +01:00
Frédéric Péters 03435d40a6 backoffice: warn about data loss when removing a page fields (#87505)
gitea/wcs/pipeline/head This commit looks good Details
2024-03-21 18:59:35 +01:00
Frédéric Péters 70b7087ad9 backoffice: add support for default value for date workflow options (#88346) 2024-03-21 18:59:28 +01:00
13 changed files with 197 additions and 25 deletions

1
debian/control vendored
View File

@ -23,6 +23,7 @@ Depends: graphviz,
python3-django-ratelimit,
python3-dnspython,
python3-emoji,
python3-freezegun,
python3-hobo,
python3-lasso,
python3-lxml,

View File

@ -204,6 +204,7 @@ setup(
'setproctitle',
'phonenumbers',
'emoji',
'pytest-freezegun',
],
package_dir={'wcs': 'wcs'},
packages=find_packages(),

View File

@ -398,6 +398,7 @@ def test_backoffice_card_item_link_id_template(pub):
resp = resp.form.submit('submit')
assert resp.location.endswith('/backoffice/data/foo/blah/')
resp = resp.follow()
assert resp.pyquery('.breadcrumbs a')[-1].attrib['href'] == '/backoffice/data/foo/blah/'
resp = app.get('/backoffice/data/foo/')
assert [x.attrib['href'] for x in resp.pyquery('table a')] == ['blah/', 'test/']

View File

@ -22,6 +22,7 @@ from wcs.workflows import (
Workflow,
WorkflowBackofficeFieldsFormDef,
WorkflowCriticalityLevel,
WorkflowVariablesFieldsFormDef,
)
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app, login
@ -1205,3 +1206,72 @@ def test_inspect_page_idp_role(pub):
resp.pyquery('[data-function-key="_receiver"] a').attr.href
== 'https://idp.example.net/manage/roles/uuid:d4b59e1ffb204dfd99fd3760f4952999/'
)
def test_inspect_page_form_option(pub):
create_user(pub, is_admin=True)
FormDef.wipe()
wf = Workflow(name='variables')
wf.variables_formdef = WorkflowVariablesFieldsFormDef(workflow=wf)
wf.add_status('st1')
wf.store()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = []
formdef.workflow = wf
formdef.store()
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.just_created()
formdata.store()
app = login(get_app(pub))
resp = app.get('%sinspect' % formdata.get_url(backoffice=True), status=200)
assert 'form_option' not in resp.text
wf.variables_formdef.fields = [
fields.StringField(label='String test', varname='string_test'),
fields.DateField(label='Date test', varname='date_test'),
]
wf.store()
resp = app.get('%sinspect' % formdata.get_url(backoffice=True), status=200)
assert (
resp.pyquery('[title="form_option_string_test"]').parents('li').children('div.value span').text()
== 'None (no value)'
)
wf.variables_formdef.fields[0].default_value = 'xxx'
wf.variables_formdef.fields[1].default_value = '2024-03-20'
wf.store()
resp = app.get('%sinspect' % formdata.get_url(backoffice=True), status=200)
assert (
resp.pyquery('[title="form_option_string_test"]').parents('li').children('div.value span').text()
== 'xxx'
)
assert (
resp.pyquery('[title="form_option_date_test"]').parents('li').children('div.value span').text()
== '2024-03-20'
)
assert (
resp.pyquery('[title="form_option_date_test_year"]').parents('li').children('div.value span').text()
== '2024 (integer number)'
)
formdef.workflow_options = {'string_test': 'yyy', 'date_test': datetime.date(2024, 3, 21).timetuple()}
formdef.store()
resp = app.get('%sinspect' % formdata.get_url(backoffice=True), status=200)
assert (
resp.pyquery('[title="form_option_string_test"]').parents('li').children('div.value span').text()
== 'yyy'
)
assert (
resp.pyquery('[title="form_option_date_test"]').parents('li').children('div.value span').text()
== '2024-03-21'
)
assert (
resp.pyquery('[title="form_option_date_test_year"]').parents('li').children('div.value span').text()
== '2024 (integer number)'
)

View File

@ -1626,6 +1626,57 @@ def test_card_update_related_cascading_loop(pub):
assert carddata2.data['2_display'] == 'card1 card2 card1 None'
def test_card_update_related_items_relation(pub):
CardDef.wipe()
FormDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = [
StringField(id='1', label='Test', varname='foo'),
]
carddef.digest_templates = {'default': '{{ form_var_foo }}'}
carddef.store()
carddef.data_class().wipe()
carddata1 = carddef.data_class()()
carddata1.data = {'1': 'card1'}
carddata1.just_created()
carddata1.store()
carddata2 = carddef.data_class()()
carddata2.data = {'1': 'card2'}
carddata2.just_created()
carddata2.store()
formdef = FormDef()
formdef.name = 'foo'
formdef.fields = [
ItemField(id='1', label='Test', data_source={'type': 'carddef:foo'}),
ItemsField(id='2', label='Test2', data_source={'type': 'carddef:foo'}),
]
formdef.store()
formdata = formdef.data_class()()
formdata.data = {'1': '1', '2': ['1', '2']}
formdata.data['1_display'] = formdef.fields[0].store_display_value(formdata.data, formdef.fields[0].id)
formdata.data['2_display'] = formdef.fields[1].store_display_value(formdata.data, formdef.fields[1].id)
assert formdata.data['1_display'] == 'card1'
assert formdata.data['2_display'] == 'card1, card2'
formdata.just_created()
formdata.store()
pub.cleanup()
carddef = carddef.get(carddef.id)
carddata1 = carddef.data_class().get(carddata1.id)
carddata1.data = {'1': 'card1-change1'}
carddata1.store()
formdata.refresh_from_storage()
assert formdata.data['1_display'] == 'card1-change1'
assert formdata.data['2_display'] == 'card1-change1, card2'
def test_card_update_related_deleted(pub):
BlockDef.wipe()
CardDef.wipe()

View File

@ -458,7 +458,6 @@ def test_workflow_tests_automatic_jump_condition(pub):
assert str(excinfo.value) == 'Form should be in status "Frog status" but is in status "Bear status".'
@pytest.mark.freeze_time('2024-02-19 12:00')
def test_workflow_tests_automatic_jump_timeout(pub):
user = pub.user_class(name='test user')
user.store()
@ -547,9 +546,9 @@ def test_workflow_tests_global_action_timeout(pub):
jump.status = end_status.id
# add choice so that new_status is not flagged as endpoint
jump = new_status.add_action('choice')
jump.label = 'Go to end status'
jump.status = end_status.id
choice = new_status.add_action('choice')
choice.label = 'Go to end status'
choice.status = end_status.id
workflow.store()
@ -585,6 +584,13 @@ def test_workflow_tests_global_action_timeout(pub):
testdef.run(formdef)
trigger.anchor = 'template'
trigger.anchor_template = '{{ form_receipt_date|date:"Y-m-d" }}'
workflow.store()
formdef.refresh_from_storage()
testdef.run(formdef)
trigger.anchor = 'finalized'
workflow.store()
formdef.refresh_from_storage()
@ -593,6 +599,27 @@ def test_workflow_tests_global_action_timeout(pub):
testdef.run(formdef)
assert str(excinfo.value) == 'Form should be in status "End status" but is in status "New status".'
# remove choice so new status becomes endpoint
new_status.items = [x for x in new_status.items if x.id != choice.id]
workflow.store()
formdef.refresh_from_storage()
testdef.run(formdef)
trigger.anchor = 'anonymisation'
workflow.store()
formdef.refresh_from_storage()
with pytest.raises(WorkflowTestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Form should be in status "End status" but is in status "New status".'
anonymise_action = new_status.add_action('anonymise')
workflow.store()
formdef.refresh_from_storage()
testdef.run(formdef)
@mock.patch('wcs.qommon.emails.send_email')
def test_workflow_tests_sendmail(mocked_send_email, pub):

View File

@ -23,7 +23,6 @@ deps =
pytest-cov
pytest-django
pytest-xdist
pytest-freezegun
WebTest
mechanize
pyquery
@ -53,7 +52,6 @@ deps =
pytest-mock
pytest-cov
pytest-django
pytest-freezegun
WebTest
mechanize
pyquery

View File

@ -152,7 +152,7 @@ class BlockDirectory(FieldsDirectory):
'Save snapshot'
)
r += htmltext('<li><a class="button button-paragraph" rel="popup" href="overwrite">%s</a>') % _(
'Overwrite'
'Overwrite with new import'
)
r += htmltext('</ul>')
r += htmltext('<h3>%s</h3>') % _('Navigation')

View File

@ -196,7 +196,20 @@ class FieldDefPage(Directory):
to_be_deleted.reverse()
# add delete_fields checkbox only if the page has fields
if to_be_deleted:
form.add(CheckboxWidget, 'delete_fields', title=_('Also remove all fields from the page'))
form.add(
CheckboxWidget,
'delete_fields',
title=_('Also remove all fields from the page'),
attrs={'data-dynamic-display-parent': 'true'},
)
form.widgets.append(
HtmlWidget(
'<div class="warningnotice" '
'data-dynamic-display-child-of="delete_fields" '
'data-dynamic-display-checked="true">%s</div>'
% _('Warning: the page fields data will be permanently deleted.')
)
)
form.add_submit('delete', _('Delete'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():

View File

@ -1030,7 +1030,7 @@ class FormStatusPage(Directory, FormTemplateMixin):
return Directory._q_lookup(self, component)
def _q_traverse(self, path):
get_response().breadcrumb.append((str(self.filled.id) + '/', self.filled.get_display_id()))
get_response().breadcrumb.append((self.filled.identifier + '/', self.filled.get_display_id()))
return super()._q_traverse(path)
def wfedit(self, action_id):

View File

@ -4,8 +4,8 @@ msgid ""
msgstr ""
"Project-Id-Version: wcs 0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-21 12:07+0100\n"
"PO-Revision-Date: 2024-03-21 12:04+0100\n"
"POT-Creation-Date: 2024-03-21 19:00+0100\n"
"PO-Revision-Date: 2024-03-21 19:00+0100\n"
"Last-Translator: Thomas Noël <tnoel@entrouvert.com>\n"
"Language-Team: french\n"
"Language: fr\n"
@ -931,6 +931,12 @@ msgstr "Vous allez supprimer le champ « %s »."
msgid "Also remove all fields from the page"
msgstr "Supprimer tous les champs de la page"
#: admin/fields.py
msgid "Warning: the page fields data will be permanently deleted."
msgstr ""
"Attention : les informations contenues dans les champs de la page seront "
"perdues de façon irréversible."
#: admin/fields.py
#, python-format
msgid "Deletion of field \"%s\""

View File

@ -757,8 +757,6 @@ class LazyFormDef:
@property
def option(self):
if not self._formdef.workflow.variables_formdef:
return {}
return LazyFormDefOptions(self._formdef)
@property
@ -1989,14 +1987,20 @@ class LazyRequest:
class LazyFormDefOptions(LazyFormDataVar):
def __init__(self, formdef):
self._formdef = formdef
fields = self._formdef.workflow.variables_formdef.fields
try:
fields = self._formdef.workflow.variables_formdef.fields
except AttributeError:
fields = []
data = self._formdef.workflow_options or {}
for field in fields:
# change field IDs as options are stored in data with their
# varnames, not id.
field.id = field.varname or field.id
if hasattr(field, 'default_value') and data.get(field.varname) is None:
data[field.varname] = field.default_value
if isinstance(field.default_value, str):
data[field.varname] = field.convert_value_from_str(field.default_value)
else:
data[field.varname] = field.default_value
super().__init__(fields, data)
def inspect_keys(self):

View File

@ -17,6 +17,8 @@
import datetime
import uuid
import freezegun
from django.utils.timezone import localtime
from quixote import get_publisher, get_session
from wcs import wf
@ -91,9 +93,13 @@ class WorkflowTests(XmlStorableObject):
# mark formdata as running workflow tests
formdata.workflow_test = True
formdata.frozen_receipt_time = formdata.receipt_time
self.reset_formdata_test_attributes(formdata)
with freezegun.freeze_time(formdata.receipt_time) as frozen_datetime:
formdata.frozen_datetime = frozen_datetime
self._run(formdata)
def _run(self, formdata):
formdata.perform_workflow()
for action in self.actions:
status = formdata.get_status()
@ -538,15 +544,9 @@ class SkipTime(WorkflowTestAction):
if previous_trace:
self.seconds = (trace.timestamp - previous_trace.timestamp).total_seconds()
def rewind(self, formdata):
def rewind_time(timestamp):
return timestamp - datetime.timedelta(seconds=self.seconds)
formdata.receipt_time = rewind_time(formdata.receipt_time)
formdata.evolution[-1].time = rewind_time(formdata.evolution[-1].time)
def perform(self, formdata):
self.rewind(formdata)
formdata.frozen_datetime.tick(self.seconds)
self.apply_jumps(formdata)
self.apply_global_actions_timeout(formdata)
@ -562,7 +562,7 @@ class SkipTime(WorkflowTestAction):
delay = wf.jump.get_min_jumps_delay(jump_actions)
if formdata.last_update_time > formdata.frozen_receipt_time - datetime.timedelta(seconds=delay):
if formdata.last_update_time > localtime() - datetime.timedelta(seconds=delay):
return
for jump_action in jump_actions: