From fc4bc8d4284164dfe82d67d19732a3b3abd09e98 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Wed, 14 Dec 2022 16:38:10 +0100 Subject: [PATCH 2/3] testdef: add views to run tests (#71296) --- tests/admin_pages/test_tests.py | 217 ++++++++++++++++++ tests/test_testdef.py | 117 ++++++++++ wcs/admin/forms.py | 6 + wcs/admin/tests.py | 173 ++++++++++++++ wcs/fields.py | 3 +- .../wcs/backoffice/test_results.html | 14 ++ wcs/templates/wcs/backoffice/tests.html | 38 +++ wcs/testdef.py | 110 +++++++++ 8 files changed, 677 insertions(+), 1 deletion(-) create mode 100644 tests/admin_pages/test_tests.py create mode 100644 tests/test_testdef.py create mode 100644 wcs/admin/tests.py create mode 100644 wcs/templates/wcs/backoffice/test_results.html create mode 100644 wcs/templates/wcs/backoffice/tests.html diff --git a/tests/admin_pages/test_tests.py b/tests/admin_pages/test_tests.py new file mode 100644 index 000000000..e272e21fb --- /dev/null +++ b/tests/admin_pages/test_tests.py @@ -0,0 +1,217 @@ +import datetime +import json +import os + +import pytest +from django.utils.html import escape +from webtest import Upload + +from wcs import fields +from wcs.formdef import FormDef +from wcs.qommon.http_request import HTTPRequest +from wcs.testdef import TestDef + +from ..utilities import clean_temporary_pub, create_temporary_pub, get_app, login +from .test_all import create_superuser + + +@pytest.fixture +def pub(): + pub = create_temporary_pub() + + req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) + pub.set_app_dir(req) + pub.cfg['identification'] = {'methods': ['password']} + pub.write_cfg() + + if not pub.site_options.has_section('options'): + pub.site_options.add_section('options') + pub.site_options.set('options', 'enable-tests', 'true') + with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: + pub.site_options.write(fd) + + FormDef.wipe() + TestDef.do_table() + TestDef.wipe() + return pub + + +def teardown_module(module): + clean_temporary_pub() + + +def test_tests_link_on_formdef_page(pub): + formdef = FormDef() + formdef.name = 'test title' + formdef.store() + + create_superuser(pub) + app = login(get_app(pub)) + + resp = app.get(formdef.get_admin_url()) + assert 'Tests' in resp.text + + pub.site_options.set('options', 'enable-tests', 'false') + with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: + pub.site_options.write(fd) + + resp = app.get(formdef.get_admin_url()) + assert 'Tests' not in resp.text + + +def test_tests_page(pub): + user = create_superuser(pub) + + formdef = FormDef() + formdef.name = 'test title' + formdef.fields = [ + fields.PageField( + id='0', + label='1st page', + type='page', + post_conditions=[ + {'condition': {'type': 'django', 'value': 'form_var_text == "a"'}, 'error_message': 'Error'} + ], + ), + fields.StringField(id='1', label='Text', varname='text'), + ] + formdef.store() + + app = login(get_app(pub)) + + resp = app.get(formdef.get_admin_url()) + resp = resp.click('Tests') + assert 'There are no tests yet.' in resp.text + assert 'Run' not in resp.text + assert 'Tests cannot be created because there are no completed forms.' in resp.text + assert 'New' not in resp.text + + formdata = formdef.data_class()() + formdata.just_created() + formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple() + formdata.data['1'] = 'a' + formdata.user_id = user.id + formdata.store() + + resp = app.get(formdef.get_admin_url()) + resp = resp.click('Tests') + assert 'There are no tests yet.' in resp.text + assert 'Run' not in resp.text + assert 'New tests cannot be created' not in resp.text + + resp = resp.click('New') + resp.form['name'] = 'First test' + resp.form['formdata_id'].select(text='1-1 - admin') + assert len(resp.form['formdata_id'].options) == 1 + + resp = resp.form.submit().follow() + assert 'First test' in resp.text + assert 'no tests yet' not in resp.text + + resp = resp.click('Run') + assert 'First test: Success!' in resp.text + + formdata = formdef.data_class()() + formdata.just_created() + formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple() + formdata.data['1'] = 'b' + formdata.store() + + resp = app.get('/backoffice/forms/1/tests/new') + resp.form['name'] = 'Second test' + resp.form['formdata_id'].select(text='1-2 - Unknown User') + assert len(resp.form['formdata_id'].options) == 2 + + resp = resp.form.submit().follow() + assert 'First test' in resp.text + assert 'Second test' in resp.text + + resp = resp.click('Run') + assert 'First test: Success!' in resp.text + assert 'Second test: Page 1 post condition was not met (form_var_text == "a").' in resp.text + + +def test_tests_page_formdefs_isolation(pub): + formdef = FormDef() + formdef.name = 'dummy' + formdef.store() + + formdata = formdef.data_class()() + formdata.just_created() + formdata.store() + + other_formdef = FormDef() + other_formdef.name = 'dummy2' + other_formdef.store() + + formdata2 = other_formdef.data_class()() + formdata2.just_created() + formdata2.store() + + create_superuser(pub) + app = login(get_app(pub)) + + resp = app.get('/backoffice/forms/1/tests/new') + assert len(resp.form['formdata_id'].options) == 1 + assert resp.form['formdata_id'].options[0][2] == '1-1 - Unknown User' + + resp = app.get('/backoffice/forms/2/tests/new') + assert len(resp.form['formdata_id'].options) == 1 + assert resp.form['formdata_id'].options[0][2] == '2-1 - Unknown User' + + +def test_tests_import_export(pub): + user = create_superuser(pub) + + formdef = FormDef() + formdef.name = 'test title' + formdef.fields = [fields.StringField(id='1', varname='test field', label='Test', type='string')] + formdef.store() + + formdata = formdef.data_class()() + formdata.just_created() + formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple() + formdata.data['1'] = 'a' + formdata.user_id = user.id + formdata.store() + + testdef = TestDef.create_from_formdata(formdef, formdata) + testdef.name = 'First test' + testdef.store() + + app = login(get_app(pub)) + + resp = app.get('/backoffice/forms/1/tests/') + export_resp = resp.click('First test') + + resp = resp.click('remove') + resp = resp.form.submit().follow() + assert 'First test' not in resp.text + + resp = resp.click('Import') + resp.form['file'] = Upload('export.json', export_resp.body, 'application/json') + resp = resp.form.submit().follow() + assert TestDef.count() == 1 + assert 'First test' in resp.text + assert escape('Test "First test" has been successfully imported.') in resp.text + + imported_testdef = TestDef.select()[0] + assert imported_testdef.export_to_json() == testdef.export_to_json() + + export_json = json.loads(export_resp.body) + export_json['data']['fields']['1'] = 'b' + + resp = resp.click('Import') + resp.form['file'] = Upload('export.json', json.dumps(export_json).encode(), 'application/json') + resp = resp.form.submit().follow() + assert TestDef.count() == 1 + assert 'First test' in resp.text + assert escape('Test "First test" has been successfully imported.') in resp.text + + imported_testdef = TestDef.get(imported_testdef.id) + assert imported_testdef.data['fields']['1'] == 'b' + + resp = resp.click('Import') + resp.form['file'] = Upload('export.json', b'invalid', 'application/json') + resp = resp.form.submit() + assert 'Expecting value: line 1 column 1' in resp.text diff --git a/tests/test_testdef.py b/tests/test_testdef.py new file mode 100644 index 000000000..7148e0407 --- /dev/null +++ b/tests/test_testdef.py @@ -0,0 +1,117 @@ +import datetime + +import pytest + +from wcs import fields +from wcs.formdef import FormDef +from wcs.qommon.http_request import HTTPRequest +from wcs.testdef import TestDef, TestError + +from .utilities import clean_temporary_pub, create_temporary_pub + + +@pytest.fixture +def pub(): + pub = create_temporary_pub() + + req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) + pub.set_app_dir(req) + pub.write_cfg() + + FormDef.wipe() + return pub + + +def teardown_module(module): + clean_temporary_pub() + + +def test_testdef_slug_generation(pub): + TestDef.do_table() + + testdef = TestDef() + testdef.name = 'test' + testdef.object_type = 'formdef' + testdef.object_id = '1' + testdef.store() + assert testdef.slug == 'test' + + testdef.slug = testdef.id = None + testdef.store() + assert testdef.slug == 'test-2' + + testdef.slug = testdef.id = None + testdef.object_id = '2' + testdef.store() + assert testdef.slug == 'test' + + +def test_page_post_conditions(pub): + formdef = FormDef() + formdef.name = 'test title' + formdef.fields = [ + fields.TitleField(id='0', label='Title', type='title'), + fields.PageField( + id='1', + label='1st page', + type='page', + post_conditions=[ + {'condition': {'type': 'django', 'value': 'form_var_text == "a"'}, 'error_message': 'Error'} + ], + ), + fields.StringField(id='2', label='Text', varname='text'), + fields.PageField( + id='3', + label='2nd page', + type='page', + post_conditions=[ + {'condition': {'type': 'django', 'value': 'form_var_text2 == "a"'}, 'error_message': 'Error'} + ], + ), + fields.StringField(id='4', label='Text', varname='text2'), + ] + formdef.store() + + formdata = formdef.data_class()() + formdata.just_created() + formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple() + formdata.data['2'] = 'a' + formdata.data['4'] = 'a' + + testdef = TestDef.create_from_formdata(formdef, formdata) + testdef.run(formdef) + + formdata.data['4'] = 'b' + testdef = TestDef.create_from_formdata(formdef, formdata) + with pytest.raises(TestError) as excinfo: + testdef.run(formdef) + assert str(excinfo.value) == 'Page 2 post condition was not met (form_var_text2 == "a").' + + formdata.data['2'] = 'b' + testdef = TestDef.create_from_formdata(formdef, formdata) + with pytest.raises(TestError) as excinfo: + testdef.run(formdef) + assert str(excinfo.value) == 'Page 1 post condition was not met (form_var_text == "a").' + + +def test_page_post_condition_invalid(pub): + formdef = FormDef() + formdef.name = 'test title' + formdef.fields = [ + fields.PageField( + id='0', + label='1st page', + type='page', + post_conditions=[{'condition': {'type': 'django', 'value': '{{}'}, 'error_message': 'Error'}], + ), + ] + formdef.store() + + formdata = formdef.data_class()() + formdata.just_created() + formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple() + + testdef = TestDef.create_from_formdata(formdef, formdata) + with pytest.raises(TestError) as excinfo: + testdef.run(formdef) + assert str(excinfo.value) == 'Failed to evaluate page 1 post condition.' diff --git a/wcs/admin/forms.py b/wcs/admin/forms.py index d61d0cc1a..3ddd267a3 100644 --- a/wcs/admin/forms.py +++ b/wcs/admin/forms.py @@ -614,6 +614,7 @@ class FormDefPage(Directory): 'qrcode', 'information', 'inspect', + 'tests', ('public-url', 'public_url'), ('backoffice-submission-roles', 'backoffice_submission_roles'), ('logged-errors', 'logged_errors_dir'), @@ -641,6 +642,8 @@ class FormDefPage(Directory): inspect_template_name = 'wcs/backoffice/formdef-inspect.html' def __init__(self, component, instance=None): + from .tests import TestsDirectory + try: self.formdef = instance or self.formdef_class.get(component) except KeyError: @@ -653,6 +656,7 @@ class FormDefPage(Directory): self.role = WorkflowRoleDirectory(self.formdef) self.role.html_top = self.html_top self.options = self.options_directory_class(self.formdef, self.formdefui) + self.tests = TestsDirectory(self.formdef) self.logged_errors_dir = LoggedErrorsDirectory( parent_dir=self, formdef_class=self.formdef_class, formdef_id=self.formdef.id ) @@ -938,6 +942,8 @@ class FormDefPage(Directory): r += htmltext('
  • %s
  • ') % _('Save snapshot') r += htmltext('
  • %s
  • ') % _('History') r += htmltext('
  • %s
  • ') % _('Inspector') + if get_publisher().has_site_option('enable-tests'): + r += htmltext('
  • %s
  • ') % _('Tests') r += htmltext('') r += htmltext('