fix(engine): Fix required validation broken by dynamic operators.

This commit is contained in:
SamTolmay 2021-04-28 12:08:40 +02:00
parent 10a017fd08
commit 6d38dbbe57
3 changed files with 144 additions and 1 deletions

View File

@ -30,6 +30,7 @@ const blockData = ({
id,
layout,
meta,
operators,
pageId,
properties,
requests,
@ -47,6 +48,7 @@ const blockData = ({
id,
layout,
meta,
operators,
pageId,
properties,
requests,
@ -69,12 +71,13 @@ const getContext = async ({ block, contextId, lowdefy }) => {
if (!lowdefy.inputs[contextId]) {
lowdefy.inputs[contextId] = {};
}
const operatorsSet = new Set([...block.operators, '_not', '_type']);
lowdefy.contexts[contextId] = {
id: contextId,
blockId: block.blockId,
eventLog: [],
requests: {},
operators: block.operators || [],
operators: [...operatorsSet],
lowdefy,
pageId: lowdefy.pageId,
rootBlock: blockData(block), // filter block to prevent circular structure

View File

@ -46,6 +46,116 @@ afterAll(() => {
global.Date = RealDate;
});
test('Validate required field', async () => {
const rootBlock = {
blockId: 'root',
meta: {
category: 'context',
},
areas: {
content: {
blocks: [
{
blockId: 'text1',
type: 'TextInput',
meta: {
category: 'input',
valueType: 'string',
},
required: true,
},
{
blockId: 'button',
type: 'Button',
meta: {
category: 'display',
},
events: {
onClick: [
{
id: 'validate',
type: 'Validate',
},
],
},
},
],
},
},
};
const context = await testContext({
lowdefy,
rootBlock,
});
const { button, text1 } = context.RootBlocks.map;
expect(text1.validationEval.output).toEqual({
errors: ['This field is required'],
status: null,
warnings: [],
});
await button.triggerEvent({ name: 'onClick' });
expect(button.Events.events.onClick.history[0]).toEqual({
blockId: 'button',
event: undefined,
eventName: 'onClick',
responses: [
{
actionId: 'validate',
actionType: 'Validate',
error: new Error('Your input has 1 validation error.'),
},
],
success: false,
timestamp: {
date: 0,
},
});
expect(text1.validationEval.output).toEqual({
errors: ['This field is required'],
status: 'error',
warnings: [],
});
expect(displayMessage.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
Object {
"content": "Your input has 1 validation error.",
"duration": 6,
"status": "error",
},
],
]
`);
displayMessage.mockReset();
displayMessage.mockImplementation(() => closeLoader);
text1.setValue('text1');
await button.triggerEvent({ name: 'onClick' });
expect(button.Events.events.onClick.history[0]).toEqual({
blockId: 'button',
event: undefined,
eventName: 'onClick',
responses: [
{
actionId: 'validate',
actionType: 'Validate',
response: undefined,
},
],
success: true,
timestamp: {
date: 0,
},
});
expect(text1.validationEval.output).toEqual({
errors: [],
status: 'success',
warnings: [],
});
expect(displayMessage.mock.calls).toEqual([]);
displayMessage.mockReset();
displayMessage.mockImplementation(() => closeLoader);
});
test('Validate all fields', async () => {
const rootBlock = {
blockId: 'root',

View File

@ -46,6 +46,7 @@ test('memoize context', async () => {
meta: {
type: 'context',
},
operators: [],
};
const c1 = await getContext({ block, contextId: 'c1', lowdefy });
const c2 = await getContext({ block, contextId: 'c1', lowdefy });
@ -71,6 +72,7 @@ test('create context', async () => {
meta: {
type: 'context',
},
operators: [],
};
const context = await getContext({ block, contextId: 'contextId', lowdefy });
expect(context.Actions).toBeDefined();
@ -81,6 +83,7 @@ test('create context', async () => {
expect(context.lowdefy).toEqual(lowdefy);
expect(context.eventLog).toEqual([]);
expect(context.id).toEqual('contextId');
expect(context.operators).toBeInstanceOf(Array);
expect(context.lowdefy.pageId).toEqual('pageId');
expect(context.parser).toBeDefined();
expect(context.requests).toEqual({});
@ -111,6 +114,7 @@ test('create context, initialize input', async () => {
meta: {
type: 'context',
},
operators: [],
};
const context = await getContext({ block, contextId: 'contextId', lowdefy });
expect(context.lowdefy.inputs.contextId).toEqual({});
@ -129,12 +133,14 @@ test('call update for listening contexts', async () => {
meta: {
type: 'context',
},
operators: [],
};
const block2 = {
blockId: 'block2',
meta: {
type: 'context',
},
operators: [],
};
const mockUpdate = jest.fn();
const c1 = await getContext({ block: block1, contextId: 'c1', lowdefy });
@ -158,6 +164,7 @@ test('remove contextId from updateListeners if not found', async () => {
meta: {
type: 'context',
},
operators: [],
};
const c1 = await getContext({ block, contextId: 'c1', lowdefy });
@ -180,6 +187,7 @@ test('remove contextId from updateListeners if equal to own contextId', async ()
meta: {
type: 'context',
},
operators: [],
};
const c1 = await getContext({ block, contextId: 'c1', lowdefy });
@ -202,6 +210,7 @@ test('update memoized context', async () => {
meta: {
type: 'context',
},
operators: [],
};
const mockUpdate = jest.fn();
const c1 = await getContext({ block, contextId: 'c1', lowdefy });
@ -223,6 +232,7 @@ test('call update for nested contexts and prevent circular loop structure', asyn
meta: {
type: 'context',
},
operators: [],
};
const block1 = {
blockId: 'block1',
@ -234,6 +244,7 @@ test('call update for nested contexts and prevent circular loop structure', asyn
blocks: block2,
},
},
operators: [],
};
const c1 = await getContext({ block: block1, contextId: 'c1', lowdefy });
const getC2 = () =>
@ -244,3 +255,22 @@ test('call update for nested contexts and prevent circular loop structure', asyn
});
await expect(getC2()).resolves.not.toThrow();
});
test('Add operators for required validation', async () => {
const lowdefy = {
client,
contexts: {},
inputs: {},
pageId,
updateBlock,
};
const block = {
blockId: 'blockId',
meta: {
type: 'context',
},
operators: [],
};
const context = await getContext({ block, contextId: 'contextId', lowdefy });
expect(context.operators).toEqual(expect.arrayContaining(['_not', '_type']));
});