testdef: add snapshots (#88755) #1379
|
@ -99,6 +99,9 @@ def test_tests_page(pub):
|
|||
resp = resp.click('Second test')
|
||||
assert 'This test is empty' in resp.text
|
||||
|
||||
resp = resp.click('History')
|
||||
assert 'Creation (empty)' in resp.text
|
||||
|
||||
# test run with empty test is allowed
|
||||
app.get('/backoffice/forms/1/tests/results/run').follow()
|
||||
|
||||
|
@ -374,6 +377,129 @@ def test_tests_status_page_image_field(pub):
|
|||
resp.follow(status=404)
|
||||
|
||||
|
||||
def test_tests_history_page(pub):
|
||||
user = create_superuser(pub)
|
||||
|
||||
formdef = FormDef()
|
||||
formdef.name = 'test title'
|
||||
formdef.fields = [fields.StringField(id='1', varname='test_field', label='Test Field')]
|
||||
formdef.store()
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.just_created()
|
||||
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
|
||||
formdata.data['1'] = 'This is a test'
|
||||
formdata.user_id = user.id
|
||||
formdata.store()
|
||||
|
||||
testdef = TestDef.create_from_formdata(formdef, formdata)
|
||||
testdef.name = 'Test 1'
|
||||
testdef.workflow_tests.actions = [
|
||||
workflow_tests.ButtonClick(id='1', button_name='xxx'),
|
||||
]
|
||||
# create one snapshot
|
||||
testdef.store()
|
||||
|
||||
response = WebserviceResponse()
|
||||
response.testdef_id = testdef.id
|
||||
response.name = 'Fake response'
|
||||
response.url = 'http://example.com/json'
|
||||
response.payload = '{"foo": "bar"}'
|
||||
response.store()
|
||||
|
||||
# create second snapshot
|
||||
testdef.name = 'Test 2'
|
||||
testdef.store()
|
||||
|
||||
# create third snapshot
|
||||
testdef.name = 'Test 3'
|
||||
testdef.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
|
||||
resp = app.get('/backoffice/forms/1/tests/%s/' % testdef.id)
|
||||
resp = resp.click('History')
|
||||
assert [x.attrib['class'] for x in resp.pyquery.find('.snapshots-list tr')] == [
|
||||
'new-day',
|
||||
'collapsed',
|
||||
'collapsed',
|
||||
]
|
||||
|
||||
# export snapshot
|
||||
resp_export = resp.click('Export', index=1)
|
||||
assert resp_export.content_type == 'application/x-wcs-snapshot'
|
||||
assert '>Test 2<' in resp_export.text
|
||||
|
||||
# view snapshot
|
||||
view_resp = resp.click('View', index=1)
|
||||
assert '<h2>Test 2</h2>' in view_resp.text
|
||||
assert 'Options' not in resp.text
|
||||
assert 'Delete' not in resp.text
|
||||
assert 'Edit' not in resp.text
|
||||
|
||||
resp = view_resp.click('Workflow tests')
|
||||
assert 'Simulate click on action button' in resp.text
|
||||
assert 'Add' not in resp.text
|
||||
assert 'Delete' not in resp.text
|
||||
assert 'Duplicate' not in resp.text
|
||||
|
||||
resp = resp.click('Edit')
|
||||
assert '>Submit<' not in resp.text
|
||||
|
||||
resp = view_resp.click('Webservice responses')
|
||||
assert 'New' not in resp.text
|
||||
assert 'Remove' not in resp.text
|
||||
assert 'Duplicate' not in resp.text
|
||||
|
||||
resp = resp.click('Fake response')
|
||||
assert 'Edit webservice response' in resp.text
|
||||
assert '>Submit<' not in resp.text
|
||||
|
||||
resp = view_resp.click('Inspect')
|
||||
|
||||
assert 'form_var_test_field' in resp.text
|
||||
|
||||
resp.form['django-condition'] = 'form_var_test_field == "This is a test"'
|
||||
resp = resp.form.submit()
|
||||
assert 'Condition result' in resp.text
|
||||
assert 'result-true' in resp.text
|
||||
|
||||
# restore as new
|
||||
assert TestDef.count() == 1
|
||||
assert WorkflowTests.count() == 1
|
||||
assert WebserviceResponse.count() == 1
|
||||
|
||||
resp = view_resp.click('Restore version')
|
||||
resp.form['action'] = 'as-new'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
|
||||
assert TestDef.count() == 2
|
||||
assert WorkflowTests.count() == 2
|
||||
assert WebserviceResponse.count() == 2
|
||||
assert '<h2>Test 2</h2>' in resp.text
|
||||
|
||||
# restore as current
|
||||
resp = view_resp.click('Restore version')
|
||||
resp.form['action'] = 'overwrite'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
|
||||
assert TestDef.count() == 2
|
||||
assert WorkflowTests.count() == 2
|
||||
assert WebserviceResponse.count() == 2
|
||||
assert '<h2>Test 2</h2>' in resp.text
|
||||
|
||||
# restore first version as current, making sure webservice response is deleted
|
||||
resp = resp.click('History')
|
||||
resp = resp.click('Restore', index=2)
|
||||
resp.form['action'] = 'overwrite'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
|
||||
assert TestDef.count() == 2
|
||||
assert WorkflowTests.count() == 2
|
||||
assert WebserviceResponse.count() == 1
|
||||
assert '<h2>Test 1</h2>' in resp.text
|
||||
|
||||
|
||||
def test_tests_edit(pub):
|
||||
create_superuser(pub)
|
||||
user = pub.user_class(name='test user')
|
||||
|
|
|
@ -78,6 +78,7 @@ def test_testdef_export_to_xml(pub):
|
|||
WebserviceResponse.wipe()
|
||||
|
||||
testdef2 = TestDef.import_from_xml(io.BytesIO(testdef_xml), formdef)
|
||||
testdef2.store()
|
||||
assert testdef2.name == 'test'
|
||||
assert testdef2.object_type == 'formdefs'
|
||||
assert testdef2.object_id == str(formdef.id)
|
||||
|
|
|
@ -25,10 +25,14 @@ from quixote import get_publisher, get_request, get_response, get_session, redir
|
|||
from quixote.directory import Directory
|
||||
from quixote.html import TemplateIO, htmltext
|
||||
|
||||
from wcs.admin import utils
|
||||
from wcs.admin.workflow_tests import WorkflowTestsDirectory
|
||||
from wcs.api import posted_json_data_to_formdata_data
|
||||
from wcs.backoffice.management import FormBackofficeEditPage, FormBackOfficeStatusPage
|
||||
from wcs.backoffice.pagination import pagination_links
|
||||
from wcs.backoffice.snapshots import SnapshotsDirectory
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.forms.common import FormStatusPage
|
||||
from wcs.qommon import _, misc, template
|
||||
from wcs.qommon.afterjobs import AfterJob
|
||||
|
@ -130,12 +134,12 @@ class TestEditPage(FormBackofficeEditPage):
|
|||
self.testdef.data = testdef.data
|
||||
|
||||
self.testdef.expected_error = get_request().form.get('error')
|
||||
self.testdef.store()
|
||||
self.testdef.store(comment=_('Mark test as failing'))
|
||||
return redirect('..')
|
||||
|
||||
def change_submission_mode(self):
|
||||
self.testdef.is_in_backoffice = not self.testdef.is_in_backoffice
|
||||
self.testdef.store()
|
||||
self.testdef.store(comment=_('Change submission mode'))
|
||||
return redirect('.')
|
||||
|
||||
|
||||
|
@ -148,13 +152,19 @@ class TestPage(FormBackOfficeStatusPage):
|
|||
'duplicate',
|
||||
('workflow', 'workflow_tests'),
|
||||
('webservice-responses', 'webservice_responses'),
|
||||
('history', 'snapshots_dir'),
|
||||
]
|
||||
|
||||
def __init__(self, component, objectdef):
|
||||
def __init__(self, component, objectdef=None, instance=None):
|
||||
try:
|
||||
self.testdef = TestDef.get(component)
|
||||
self.testdef = instance or TestDef.get(component)
|
||||
except KeyError:
|
||||
raise TraversalError()
|
||||
|
||||
if not objectdef:
|
||||
klass = FormDef if self.testdef.object_type == 'formdefs' else CardDef
|
||||
objectdef = klass.get(self.testdef.object_id)
|
||||
|
||||
self.testdef.formdef = objectdef
|
||||
|
||||
filled = self.testdef.build_formdata(objectdef, include_fields=True)
|
||||
|
@ -162,6 +172,7 @@ class TestPage(FormBackOfficeStatusPage):
|
|||
|
||||
self.workflow_tests = WorkflowTestsDirectory(self.testdef, self.formdef)
|
||||
self.webservice_responses = WebserviceResponseDirectory(self.testdef)
|
||||
self.snapshots_dir = SnapshotsDirectory(self.testdef)
|
||||
|
||||
@property
|
||||
def edit_data(self):
|
||||
|
@ -179,14 +190,27 @@ class TestPage(FormBackOfficeStatusPage):
|
|||
return False
|
||||
|
||||
def get_extra_context_bar(self, parent=None):
|
||||
return render_to_string('wcs/backoffice/test_sidebar.html', context={})
|
||||
if self.testdef.is_readonly():
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<div class="infonotice"><p>%s</p></div>') % _('This test is readonly.')
|
||||
r += utils.snapshot_info_block(self.testdef.snapshot_object)
|
||||
r += htmltext('<h3>%s</h3>') % _('Navigation')
|
||||
r += htmltext(
|
||||
'<li><a class="button button-paragraph" href="webservice-responses/">%s</a></li>'
|
||||
) % _('Webservice responses')
|
||||
r += htmltext('<li><a class="button button-paragraph" href="inspect">%s</a></li>') % _('Inspect')
|
||||
r += htmltext('</h3>')
|
||||
return r.getvalue()
|
||||
else:
|
||||
return render_to_string('wcs/backoffice/test_sidebar.html', context={})
|
||||
|
||||
def status(self):
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<div id="appbar">')
|
||||
r += htmltext('<h2>%s</h2>') % self.testdef
|
||||
r += htmltext('<span class="actions">')
|
||||
r += htmltext('<a href="edit-data/">%s</a>') % _('Edit data')
|
||||
if not self.testdef.is_readonly():
|
||||
r += htmltext('<a href="edit-data/">%s</a>') % _('Edit data')
|
||||
r += htmltext('<a href="workflow/">%s</a>') % _('Workflow tests')
|
||||
r += htmltext('</span>')
|
||||
r += htmltext('</div>')
|
||||
|
@ -254,7 +278,7 @@ class TestPage(FormBackOfficeStatusPage):
|
|||
self.testdef.name = form.get_widget('name').parse()
|
||||
self.testdef.user_uuid = form.get_widget('user').parse()
|
||||
|
||||
self.testdef.store()
|
||||
self.testdef.store(comment=_('Change in options'))
|
||||
return redirect('.')
|
||||
|
||||
def duplicate(self):
|
||||
|
@ -285,7 +309,7 @@ class TestPage(FormBackOfficeStatusPage):
|
|||
|
||||
self.testdef.name = form.get_widget('name').parse()
|
||||
self.testdef = TestDef.import_from_xml_tree(self.testdef.export_to_xml(), self.formdef)
|
||||
self.testdef.store()
|
||||
self.testdef.store(comment=_('Creation (from duplication)'))
|
||||
|
||||
return redirect(self.testdef.get_admin_url())
|
||||
|
||||
|
@ -386,7 +410,7 @@ class TestsDirectory(Directory):
|
|||
testdef = TestDef.create_from_formdata(self.objectdef, self.objectdef.data_class()())
|
||||
testdef.name = form.get_widget('name').parse()
|
||||
testdef.agent_id = test_agent_user.test_uuid
|
||||
testdef.store()
|
||||
testdef.store(comment=_('Creation (empty)'))
|
||||
return redirect(testdef.get_admin_url() + 'edit-data/')
|
||||
else:
|
||||
formdata_id = form.get_widget('formdata').parse()
|
||||
|
@ -399,7 +423,7 @@ class TestsDirectory(Directory):
|
|||
)
|
||||
testdef.name = form.get_widget('name').parse()
|
||||
testdef.agent_id = test_agent_user.test_uuid
|
||||
testdef.store()
|
||||
testdef.store(comment=_('Creation (from formdata)'))
|
||||
return redirect(testdef.get_admin_url())
|
||||
|
||||
def p_import(self):
|
||||
|
@ -434,6 +458,7 @@ class TestsDirectory(Directory):
|
|||
form.set_error('file', _('Invalid File'))
|
||||
raise e
|
||||
|
||||
testdef.store(comment=_('Creation (from import)'))
|
||||
get_session().message = ('info', _('Test "%s" has been successfully imported.') % testdef.name)
|
||||
return redirect('.')
|
||||
|
||||
|
@ -818,7 +843,8 @@ class WebserviceResponsePage(Directory):
|
|||
},
|
||||
)
|
||||
|
||||
form.add_submit('submit', _('Submit'))
|
||||
if not self.testdef.is_readonly():
|
||||
form.add_submit('submit', _('Submit'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
form.add_media()
|
||||
|
||||
|
@ -840,6 +866,7 @@ class WebserviceResponsePage(Directory):
|
|||
self.webservice_response.method = form.get_widget('method').parse()
|
||||
self.webservice_response.post_data = form.get_widget('post_data').parse()
|
||||
self.webservice_response.store()
|
||||
self.testdef.store(comment=_('Change webservice response "%s"') % self.webservice_response.name)
|
||||
|
||||
return redirect('..')
|
||||
|
||||
|
@ -865,6 +892,9 @@ class WebserviceResponsePage(Directory):
|
|||
new_webservice_response.id = None
|
||||
new_webservice_response.name = '%s %s' % (new_webservice_response.name, _('(copy)'))
|
||||
new_webservice_response.store()
|
||||
self.testdef.store(
|
||||
comment=_('Duplication of webservice response "%s"') % self.webservice_response.name
|
||||
)
|
||||
return redirect('..')
|
||||
|
||||
|
||||
|
@ -884,7 +914,8 @@ class WebserviceResponseDirectory(Directory):
|
|||
def _q_index(self):
|
||||
context = {
|
||||
'webservice_responses': self.testdef.get_webservice_responses(),
|
||||
'has_sidebar': True,
|
||||
'has_sidebar': bool(not self.testdef.is_readonly()),
|
||||
'testdef': self.testdef,
|
||||
}
|
||||
get_response().add_javascript(['popup.js'])
|
||||
get_response().set_title(_('Webservice responses'))
|
||||
|
@ -916,6 +947,7 @@ class WebserviceResponseDirectory(Directory):
|
|||
webservice_response.testdef_id = self.testdef.id
|
||||
webservice_response.name = form.get_widget('name').parse()
|
||||
webservice_response.store()
|
||||
self.testdef.store(comment=_('New webservice response "%s"') % webservice_response.name)
|
||||
|
||||
return redirect(self.testdef.get_admin_url() + 'webservice-responses/%s/' % webservice_response.id)
|
||||
|
||||
|
|
|
@ -47,7 +47,8 @@ class WorkflowTestActionPage(Directory):
|
|||
if not form.widgets:
|
||||
form.add_global_errors([htmltext(self.action.empty_form_error)])
|
||||
else:
|
||||
form.add_submit('submit', _('Submit'))
|
||||
if not self.testdef.is_readonly():
|
||||
form.add_submit('submit', _('Submit'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
|
||||
if form.get_widget('cancel').parse():
|
||||
|
@ -69,7 +70,7 @@ class WorkflowTestActionPage(Directory):
|
|||
|
||||
setattr(self.action, widget.name, value)
|
||||
|
||||
self.testdef.store()
|
||||
self.testdef.store(comment=_('Change in workflow test action "%s"') % self.action.label)
|
||||
return redirect('..')
|
||||
|
||||
def delete(self):
|
||||
|
@ -90,7 +91,7 @@ class WorkflowTestActionPage(Directory):
|
|||
self.testdef.workflow_tests.actions = [
|
||||
x for x in self.testdef.workflow_tests.actions if x.id != self.action.id
|
||||
]
|
||||
self.testdef.store()
|
||||
self.testdef.store(comment=_('Deletion of workflow test action "%s"') % self.action.label)
|
||||
return redirect('..')
|
||||
|
||||
def duplicate(self):
|
||||
|
@ -98,7 +99,7 @@ class WorkflowTestActionPage(Directory):
|
|||
new_action.id = self.testdef.workflow_tests.get_new_action_id()
|
||||
action_position = self.testdef.workflow_tests.actions.index(self.action)
|
||||
self.testdef.workflow_tests.actions.insert(action_position + 1, new_action)
|
||||
self.testdef.store()
|
||||
self.testdef.store(comment=_('Duplication of workflow test action "%s"') % self.action.label)
|
||||
return redirect('..')
|
||||
|
||||
|
||||
|
@ -120,7 +121,7 @@ class WorkflowTestsDirectory(Directory):
|
|||
def _q_index(self):
|
||||
context = {
|
||||
'testdef': self.testdef,
|
||||
'has_sidebar': True,
|
||||
'has_sidebar': bool(not self.testdef.is_readonly()),
|
||||
'sidebar_form': self.get_sidebar_form(),
|
||||
}
|
||||
|
||||
|
@ -161,7 +162,8 @@ class WorkflowTestsDirectory(Directory):
|
|||
**{'data-autocomplete': 'true'},
|
||||
)
|
||||
|
||||
form.add_submit('submit', _('Submit'))
|
||||
if not self.testdef.is_readonly():
|
||||
form.add_submit('submit', _('Submit'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
|
||||
if form.get_widget('cancel').parse():
|
||||
|
@ -176,7 +178,7 @@ class WorkflowTestsDirectory(Directory):
|
|||
return r.getvalue()
|
||||
|
||||
self.testdef.agent_id = form.get_widget('agent').parse()
|
||||
self.testdef.store()
|
||||
self.testdef.store(comment=_('Change in workflow test options'))
|
||||
return redirect('.')
|
||||
|
||||
def new(self):
|
||||
|
@ -190,7 +192,7 @@ class WorkflowTestsDirectory(Directory):
|
|||
action_type = form.get_widget('type').parse()
|
||||
action_class = get_test_action_class_by_type(action_type)
|
||||
self.testdef.workflow_tests.add_action(action_class)
|
||||
self.testdef.store()
|
||||
self.testdef.store(comment=_('New test action "%s"') % action_class.label)
|
||||
|
||||
return redirect('.')
|
||||
|
||||
|
@ -219,7 +221,7 @@ class WorkflowTestsDirectory(Directory):
|
|||
return json.dumps({'success': 'ko'})
|
||||
|
||||
self.testdef.workflow_tests.actions = new_actions
|
||||
self.testdef.store()
|
||||
self.testdef.store(comment=_('Change in workflow test actions order'))
|
||||
|
||||
return json.dumps(
|
||||
{
|
||||
|
|
|
@ -52,7 +52,8 @@ class SnapshotsDirectory(Directory):
|
|||
templates=['wcs/backoffice/snapshots.html'],
|
||||
context={
|
||||
'view': self,
|
||||
'form_has_tests': bool(TestDef.select_for_objectdef(self.obj)),
|
||||
'form_has_tests': self.object_type in ('formdef', 'carddef')
|
||||
and bool(TestDef.select_for_objectdef(self.obj)),
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -261,7 +262,7 @@ class SnapshotsDirectory(Directory):
|
|||
current_date = None
|
||||
snapshots = get_publisher().snapshot_class.select_object_history(self.obj)
|
||||
test_results = TestResult.select(
|
||||
[Equal('object_type', self.obj.get_table_name()), Equal('object_id', self.obj.id)]
|
||||
[Equal('object_type', self.obj.get_table_name()), Equal('object_id', str(self.obj.id))]
|
||||
)
|
||||
test_results_by_id = {x.id: x for x in test_results}
|
||||
day_snapshot = None
|
||||
|
|
|
@ -1768,7 +1768,8 @@ class FormDef(StorableObject):
|
|||
from .testdef import TestDef
|
||||
|
||||
for testdef in self.xml_testdefs:
|
||||
TestDef.import_from_xml_tree(testdef, self)
|
||||
obj = TestDef.import_from_xml_tree(testdef, self)
|
||||
obj.store()
|
||||
|
||||
def get_detailed_email_form(self, formdata, url):
|
||||
r = ''
|
||||
|
|
|
@ -1992,7 +1992,7 @@ class FormPage(Directory, TempfileDirectoryMixin, FormTemplateMixin):
|
|||
testdef = TestDef.create_from_formdata(self.formdef, self.edited_data)
|
||||
self.testdef.data = testdef.data
|
||||
self.testdef.expected_error = None
|
||||
self.testdef.store()
|
||||
self.testdef.store(comment=_('Change in test data'))
|
||||
return redirect(self.formdef.get_admin_url() + 'tests/%s/' % self.testdef.id)
|
||||
|
||||
evo = self.edited_data.evolution[-1]
|
||||
|
|
|
@ -590,6 +590,7 @@ class WcsPublisher(QommonPublisher):
|
|||
from wcs.data_sources import NamedDataSource
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.mail_templates import MailTemplate
|
||||
from wcs.testdef import TestDef
|
||||
from wcs.workflows import Workflow
|
||||
from wcs.wscalls import NamedWsCall
|
||||
|
||||
|
@ -609,6 +610,7 @@ class WcsPublisher(QommonPublisher):
|
|||
MailTemplateCategory,
|
||||
CommentTemplateCategory,
|
||||
DataSourceCategory,
|
||||
TestDef,
|
||||
):
|
||||
if klass.xml_root_node == object_type:
|
||||
return klass
|
||||
|
|
|
@ -354,6 +354,10 @@ class Snapshot:
|
|||
if self.object_type in self._category_types:
|
||||
# set position
|
||||
instance.position = max(i.position or 0 for i in self.get_object_class().select()) + 1
|
||||
elif self.object_type == 'testdef':
|
||||
instance.workflow_tests.id = None
|
||||
for response in instance.get_webservice_responses():
|
||||
response.id = None
|
||||
if hasattr(instance, 'disabled'):
|
||||
instance.disabled = True
|
||||
else:
|
||||
|
|
|
@ -3689,12 +3689,12 @@ class Snapshot(SqlMixin, wcs.snapshots.Snapshot):
|
|||
@classmethod
|
||||
def select_object_history(cls, obj, clause=None):
|
||||
return cls.select(
|
||||
[Equal('object_type', obj.xml_root_node), Equal('object_id', obj.id)] + (clause or []),
|
||||
[Equal('object_type', obj.xml_root_node), Equal('object_id', str(obj.id))] + (clause or []),
|
||||
order_by='-timestamp',
|
||||
)
|
||||
|
||||
def is_from_object(self, obj):
|
||||
return self.object_type == obj.xml_root_node and self.object_id == obj.id
|
||||
return self.object_type == obj.xml_root_node and self.object_id == str(obj.id)
|
||||
|
||||
@classmethod
|
||||
def _row2ob(cls, row, **kwargs):
|
||||
|
@ -3725,7 +3725,7 @@ class Snapshot(SqlMixin, wcs.snapshots.Snapshot):
|
|||
)
|
||||
cur.execute(
|
||||
sql_statement,
|
||||
{'object_type': object_type, 'object_id': object_id, 'max_timestamp': max_timestamp},
|
||||
{'object_type': object_type, 'object_id': str(object_id), 'max_timestamp': max_timestamp},
|
||||
)
|
||||
row = cur.fetchone()
|
||||
cur.close()
|
||||
|
|
|
@ -21,8 +21,10 @@
|
|||
<i>({% trans "not configured" %})</i>
|
||||
{% endif %}
|
||||
</a>
|
||||
<a rel="popup" class="delete" href="{{ response.id }}/delete">{% trans "Remove" %}</a>
|
||||
<a class="link-action-icon duplicate" href="{{ response.id }}/duplicate">{% trans "Duplicate" %}</a>
|
||||
{% if not testdef.is_readonly %}
|
||||
<a rel="popup" class="delete" href="{{ response.id }}/delete">{% trans "Remove" %}</a>
|
||||
<a class="link-action-icon duplicate" href="{{ response.id }}/duplicate">{% trans "Duplicate" %}</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<h3>{% trans "Navigation" %}</h3>
|
||||
<ul class="sidebar--buttons">
|
||||
<li><a class="button button-paragraph" rel="popup" href="edit">{% trans "Options" %}</a></li>
|
||||
<li><a class="button button-paragraph" href="history/">{% trans "History" %}</a></li>
|
||||
<li><a class="button button-paragraph" href="webservice-responses/">{% trans "Webservice responses" %}</a></li>
|
||||
<li><a class="button button-paragraph" href="inspect">{% trans "Inspect" %}</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -29,12 +29,14 @@
|
|||
<a href="{{ action.id }}/" rel="popup" title="{% trans "Edit" %}">{% trans "Edit" %}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="duplicate">
|
||||
<a href="{{ action.id }}/duplicate" title="{% trans "Duplicate" %}">{% trans "Duplicate" %}</a>
|
||||
</span>
|
||||
<span class="remove">
|
||||
<a href="{{ action.id }}/delete" rel="popup" title="{% trans "Delete" %}">{% trans "Delete" %}</a>
|
||||
</span>
|
||||
{% if not testdef.is_readonly %}
|
||||
<span class="duplicate">
|
||||
<a href="{{ action.id }}/duplicate" title="{% trans "Duplicate" %}">{% trans "Duplicate" %}</a>
|
||||
</span>
|
||||
<span class="remove">
|
||||
<a href="{{ action.id }}/delete" rel="popup" title="{% trans "Delete" %}">{% trans "Delete" %}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
|
@ -33,8 +33,10 @@ from quixote import get_publisher, get_session_manager
|
|||
from urllib3 import HTTPResponse
|
||||
|
||||
from wcs import sql
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.compat import CompatHTTPRequest
|
||||
from wcs.fields import Field, PageField
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.qommon.form import FileWithPreviewWidget, Form, get_selection_error_text
|
||||
from wcs.qommon.storage import Equal
|
||||
from wcs.qommon.template import TemplateError
|
||||
|
@ -76,7 +78,7 @@ class TestDefXmlProxy(XmlStorableObject):
|
|||
'boolean': 'bool',
|
||||
'jsonb': 'jsonb',
|
||||
}
|
||||
excluded_fields = ['id', 'object_type', 'object_id']
|
||||
excluded_fields = ['id']
|
||||
extra_fields = [
|
||||
('_webservice_responses', 'webservice_responses'),
|
||||
('workflow_tests', 'workflow_tests'),
|
||||
|
@ -89,26 +91,32 @@ class TestDefXmlProxy(XmlStorableObject):
|
|||
] + extra_fields
|
||||
|
||||
def export_jsonb_to_xml(self, element, attribute_name, **kwargs):
|
||||
element.text = json.dumps(getattr(self, attribute_name))
|
||||
element.text = json.dumps(getattr(self, attribute_name), indent=2)
|
||||
|
||||
def import_jsonb_from_xml(self, element, **kwargs):
|
||||
return json.loads(element.text)
|
||||
|
||||
def export_workflow_tests_to_xml(self, element, attribute_name, **kwargs):
|
||||
for subelement in self.workflow_tests.export_to_xml():
|
||||
def export_workflow_tests_to_xml(self, element, attribute_name, include_id=False):
|
||||
workflow_tests = self.workflow_tests.export_to_xml(include_id=include_id)
|
||||
if include_id:
|
||||
element.set('id', workflow_tests.get('id'))
|
||||
|
||||
for subelement in workflow_tests:
|
||||
element.append(subelement)
|
||||
|
||||
def import_workflow_tests_from_xml(self, element, **kwargs):
|
||||
def import_workflow_tests_from_xml(self, element, include_id=False):
|
||||
from wcs.workflow_tests import WorkflowTests
|
||||
|
||||
return WorkflowTests.import_from_xml_tree(element)
|
||||
return WorkflowTests.import_from_xml_tree(element, include_id=include_id)
|
||||
|
||||
def export_webservice_responses_to_xml(self, element, attribute_name, **kwargs):
|
||||
def export_webservice_responses_to_xml(self, element, attribute_name, include_id=False):
|
||||
for response in self._webservice_responses:
|
||||
element.append(response.export_to_xml())
|
||||
element.append(response.export_to_xml(include_id=include_id))
|
||||
|
||||
def import_webservice_responses_from_xml(self, element, **kwargs):
|
||||
return [WebserviceResponse.import_from_xml_tree(response) for response in element]
|
||||
def import_webservice_responses_from_xml(self, element, include_id=False):
|
||||
return [
|
||||
WebserviceResponse.import_from_xml_tree(response, include_id=include_id) for response in element
|
||||
]
|
||||
|
||||
|
||||
class TestDef(sql.TestDef):
|
||||
|
@ -134,6 +142,11 @@ class TestDef(sql.TestDef):
|
|||
'tablerows',
|
||||
'ranked-items',
|
||||
)
|
||||
backoffice_class = 'wcs.admin.tests.TestPage'
|
||||
|
||||
xml_root_node = TestDefXmlProxy.xml_root_node
|
||||
get_table_name = TestDefXmlProxy.get_table_name
|
||||
is_readonly = TestDefXmlProxy.is_readonly
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -146,15 +159,18 @@ class TestDef(sql.TestDef):
|
|||
return self._workflow_tests
|
||||
|
||||
workflow_tests_list = WorkflowTests.select([Equal('testdef_id', self.id)])
|
||||
self._workflow_tests = workflow_tests_list[0] if workflow_tests_list else WorkflowTests()
|
||||
self._workflow_tests.testdef = self
|
||||
self.workflow_tests = workflow_tests_list[0] if workflow_tests_list else WorkflowTests()
|
||||
return self._workflow_tests
|
||||
|
||||
@workflow_tests.setter
|
||||
def workflow_tests(self, value):
|
||||
self._workflow_tests = value
|
||||
self._workflow_tests.testdef = self
|
||||
|
||||
def get_webservice_responses(self):
|
||||
if hasattr(self, '_webservice_responses'):
|
||||
# this attribute is set by import/export, and should be used in snapshot context
|
||||
return self._webservice_responses
|
||||
return WebserviceResponse.select([Equal('testdef_id', self.id)], order_by='name')
|
||||
|
||||
def get_admin_url(self):
|
||||
|
@ -162,13 +178,27 @@ class TestDef(sql.TestDef):
|
|||
objects_dir = 'forms' if self.object_type == 'formdefs' else 'cards'
|
||||
return '%s/%s/%s/tests/%s/' % (base_url, objects_dir, self.object_id, self.id)
|
||||
|
||||
def store(self, *args, **kwargs):
|
||||
super().store(*args, **kwargs)
|
||||
def store(self, comment=None):
|
||||
super().store()
|
||||
|
||||
self.workflow_tests.testdef_id = self.id
|
||||
self.workflow_tests.testdef = self
|
||||
self.workflow_tests.store()
|
||||
|
||||
if hasattr(self, '_webservice_responses'):
|
||||
# first store after import, attach webservice responses and delete old ones on snapshot restore
|
||||
response_ids = {x.id for x in self._webservice_responses}
|
||||
for response in WebserviceResponse.select([Equal('testdef_id', self.id)]):
|
||||
if response.id not in response_ids:
|
||||
response.remove_self()
|
||||
|
||||
for response in self._webservice_responses:
|
||||
response.testdef_id = self.id
|
||||
response.store()
|
||||
del self._webservice_responses
|
||||
|
||||
if get_publisher().snapshot_class:
|
||||
get_publisher().snapshot_class.snap(instance=self, comment=comment)
|
||||
|
||||
@classmethod
|
||||
def remove_object(cls, id):
|
||||
super().remove_object(id)
|
||||
|
@ -538,11 +568,12 @@ class TestDef(sql.TestDef):
|
|||
return widget
|
||||
|
||||
def export_to_xml(self, include_id=False):
|
||||
self._webservice_responses = self.get_webservice_responses()
|
||||
|
||||
testdef_xml = TestDefXmlProxy()
|
||||
testdef_xml = TestDefXmlProxy(id=str(self.id))
|
||||
for field, dummy in TestDefXmlProxy.XML_NODES: # pylint: disable=not-an-iterable
|
||||
setattr(testdef_xml, field, getattr(self, field))
|
||||
if field == '_webservice_responses':
|
||||
testdef_xml._webservice_responses = self.get_webservice_responses()
|
||||
else:
|
||||
setattr(testdef_xml, field, getattr(self, field))
|
||||
|
||||
return testdef_xml.export_to_xml(include_id=include_id)
|
||||
|
||||
|
@ -555,20 +586,23 @@ class TestDef(sql.TestDef):
|
|||
return cls.import_from_xml_tree(tree, formdef, include_id=include_id)
|
||||
|
||||
@classmethod
|
||||
def import_from_xml_tree(cls, tree, formdef, include_id=False):
|
||||
testdef = TestDef.create_from_formdata(formdef, formdef.data_class()())
|
||||
|
||||
def import_from_xml_tree(cls, tree, formdef=None, include_id=False, **kwargs):
|
||||
testdef_xml = TestDefXmlProxy.import_from_xml_tree(tree, include_id)
|
||||
|
||||
if not formdef:
|
||||
klass = FormDef if testdef_xml.object_type == 'formdefs' else CardDef
|
||||
formdef = klass.get(testdef_xml.object_id)
|
||||
|
||||
testdef = TestDef.create_from_formdata(formdef, formdef.data_class()())
|
||||
testdef.id = int(testdef_xml.id) if testdef_xml.id else None
|
||||
|
||||
for field, dummy in TestDefXmlProxy.XML_NODES: # pylint: disable=not-an-iterable
|
||||
if field in ('object_type', 'object_id'):
|
||||
continue
|
||||
|
||||
if hasattr(testdef_xml, field):
|
||||
setattr(testdef, field, getattr(testdef_xml, field))
|
||||
|
||||
testdef.store()
|
||||
|
||||
for response in testdef._webservice_responses:
|
||||
response.testdef_id = testdef.id
|
||||
response.store()
|
||||
|
||||
return testdef
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue