testdef: add snapshots (#88755) #1379

Merged
vdeniaud merged 6 commits from wip/88755-testdef-avoir-un-historique-des into main 2024-04-15 11:31:29 +02:00
14 changed files with 272 additions and 64 deletions

View File

@ -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')

View File

@ -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)

View File

@ -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)

View File

@ -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(
{

View File

@ -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

View File

@ -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 = ''

View File

@ -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]

View File

@ -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

View File

@ -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:

View File

@ -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()

View File

@ -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>

View File

@ -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>

View File

@ -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 %}

View File

@ -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