-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add typechecking to itemsExpression #3610
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
from openforms.utils.tests.cache import clear_caches | ||
from openforms.variables.constants import FormVariableDataTypes, FormVariableSources | ||
|
||
from ...models import Form | ||
from ..factories import ( | ||
FormDefinitionFactory, | ||
FormFactory, | ||
|
@@ -2076,3 +2077,71 @@ def setUpTestData(): | |
'duplicate-key: in "FORM DEFINITION #1" and "FORM DEFINITION #2"' | ||
'duplicate-key-2: in "FORM DEFINITION #1" and "FORM DEFINITION #3"' | ||
) | ||
|
||
|
||
class FormDesignerJsonLogicTests(E2ETestCase): | ||
async def test_variable_options(self) -> None: | ||
@sync_to_async | ||
def setUpTestData() -> Form: | ||
return FormStepFactory.create( | ||
form__name="Inferring type of variable options", | ||
form_definition__name="Pick your poison", | ||
form_definition__configuration={ | ||
"components": [ | ||
{"label": "Single choice", "type": "radio", "key": "single"}, | ||
{ | ||
"label": "Multiple choice", | ||
"type": "selectboxes", | ||
"key": "multi", | ||
}, | ||
{"label": "Dropdown", "type": "select", "key": "dropdown"}, | ||
], | ||
}, | ||
).form | ||
|
||
await create_superuser() | ||
form = await setUpTestData() | ||
|
||
edit_form_url = str( | ||
furl(self.live_server_url) | ||
/ reverse("admin:forms_form_change", args=(form.pk,)) | ||
) | ||
|
||
async def make_assertions(): | ||
await page.get_by_text("Keuzeopties", exact=True).click() | ||
await page.get_by_text("Variabele", exact=True).click() | ||
# clicking the "Items expression" label doesn't focus the editor :( | ||
component = page.locator( | ||
'css=[ref="component"]', has_text="Opties-expressie" | ||
) | ||
editor = component.locator("css=.ace_content") | ||
await editor.click() | ||
await editor.press("Control+a") | ||
await editor.press("Backspace") | ||
await editor.type('{"map": [{"var": "somevar"}]}') | ||
await page.get_by_role("button", name="Opslaan").click() | ||
await expect(page.locator("css=.formio-dialog-content")).to_be_visible() | ||
await expect( | ||
page.locator('css=[data-component-key="openForms.itemsExpression"]') | ||
).to_contain_text("Error") | ||
await editor.click() | ||
await editor.press("Control+a") | ||
await editor.press("Backspace") | ||
await editor.type( | ||
'{"map": [{"var": "somevar"}, [{"var": ""}, {"cat": ["Optie ", {"var": ""}]}]]}' | ||
) | ||
Comment on lines
+2130
to
+2132
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does it know that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know the exact order, but it follows from
It's why the const result = infer({'===': [logic, expected]}, {}); with It uses the fact that
|
||
await page.get_by_role("button", name="Opslaan").click() | ||
await expect(page.locator("css=.formio-dialog-content")).not_to_be_visible() | ||
|
||
page: Page | ||
async with browser_page() as page: | ||
await self._admin_login(page) | ||
await page.goto(edit_form_url) | ||
await page.get_by_role("tab", name="Steps and fields").click() | ||
await page.get_by_role("button", name="Pick your poison").click() | ||
await open_component_options_modal(page, "Single choice") | ||
await make_assertions() | ||
await open_component_options_modal(page, "Multiple choice") | ||
await make_assertions() | ||
await open_component_options_modal(page, "Dropdown") | ||
await make_assertions() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import {infer} from '@open-formulieren/infernologic'; | ||
import cloneDeep from 'lodash/cloneDeep'; | ||
import set from 'lodash/set'; | ||
import PropTypes from 'prop-types'; | ||
|
@@ -17,6 +18,21 @@ const getBuilderOptions = () => { | |
const maxFileUploadSize = jsonScriptToVar('setting-MAX_FILE_UPLOAD_SIZE'); | ||
const formFieldsRequiredDefault = jsonScriptToVar('config-REQUIRED_DEFAULT'); | ||
|
||
/** | ||
* For use in a FormIO custom validator. | ||
* | ||
* | ||
* @param {object | array | number | string} logic - JsonLogic expression | ||
* @param {object | array | number | string} expected - example expected result value [["label", "value"]] | ||
* @returns {boolean | string} - OK or an error message | ||
*/ | ||
const validateLogic = (logic, expected) => { | ||
// We don't evaluate logic, we just look at types. | ||
// "===" forces the logic expression align with the expectancy | ||
const result = infer({'===': [logic, expected]}, {}); | ||
return result.startsWith('result type:') || result; | ||
Comment on lines
+32
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
}; | ||
|
||
return { | ||
builder: { | ||
basic: false, | ||
|
@@ -251,6 +267,7 @@ const getBuilderOptions = () => { | |
evalContext: { | ||
serverUploadLimit: maxFileUploadSize, | ||
requiredDefault: formFieldsRequiredDefault, | ||
validateLogic, | ||
}, | ||
editors: { | ||
ckeditor: { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Contents of "Error" will change. These assertions just assert that type check has occurred and accepts correct expressions.
Testing for the better error messages belongs in a unittest, E2E is way to slow and expensive for that.