mirror of
https://github.com/lowdefy/lowdefy.git
synced 2025-02-23 14:39:32 +08:00
feat(engine): showValidation on block level and params.regex for Validate.
This commit is contained in:
parent
df6a5904a8
commit
6824b07127
@ -217,6 +217,7 @@ class Blocks {
|
||||
const initState = serializer.copy(initWithState || this.context.state);
|
||||
this.loopBlocks((block) => {
|
||||
block.update = true;
|
||||
block.showValidation = false;
|
||||
if (get(block, 'meta.category') === 'input' || get(block, 'meta.category') === 'list') {
|
||||
let blockValue = get(initState, block.field);
|
||||
if (type.isUndefined(blockValue)) {
|
||||
@ -368,13 +369,13 @@ class Blocks {
|
||||
}
|
||||
}
|
||||
});
|
||||
if (this.context.showValidationErrors && validation.length > 0) {
|
||||
if (validation.length > 0) {
|
||||
block.validationEval.output.status = 'success';
|
||||
}
|
||||
if (validationWarning) {
|
||||
block.validationEval.output.status = 'warning';
|
||||
}
|
||||
if (this.context.showValidationErrors && validationError) {
|
||||
if (validationError) {
|
||||
block.validationEval.output.status = 'error';
|
||||
}
|
||||
|
||||
@ -497,18 +498,26 @@ class Blocks {
|
||||
});
|
||||
}
|
||||
|
||||
getValidateRec(result) {
|
||||
getValidateRec(params, match, result) {
|
||||
this.loopBlocks((block) => {
|
||||
if (block.visibleEval.output && block.validationEval.output.status === 'error') {
|
||||
result.push({
|
||||
blockId: block.blockId,
|
||||
validation: block.validationEval.output,
|
||||
});
|
||||
if (match(block.blockId, params)) {
|
||||
block.showValidation = true;
|
||||
block.update = true;
|
||||
if (
|
||||
block.visibleEval.output !== false &&
|
||||
block.validationEval.output &&
|
||||
block.validationEval.output.status === 'error'
|
||||
) {
|
||||
result.push({
|
||||
blockId: block.blockId,
|
||||
validation: block.validationEval.output,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.keys(this.subBlocks).forEach((subKey) => {
|
||||
this.subBlocks[subKey].forEach((subBlock) => {
|
||||
subBlock.getValidateRec(result);
|
||||
subBlock.getValidateRec(params, match, result);
|
||||
});
|
||||
});
|
||||
return result;
|
||||
@ -536,9 +545,11 @@ class Blocks {
|
||||
});
|
||||
}
|
||||
|
||||
validate() {
|
||||
this.update(); // update to recalculate validationEval with showValidationErrors set to raise block errors
|
||||
return this.getValidateRec([]);
|
||||
validate(params, match) {
|
||||
this.updateStateFromRoot(); // update to recalculate validationEval to raise block errors
|
||||
const validationErrors = this.getValidateRec(params, match, []); // get all relevant raised block errors and set showValidation
|
||||
this.setBlocksCache(); // update cache to render
|
||||
return validationErrors;
|
||||
}
|
||||
|
||||
update() {
|
||||
@ -562,7 +573,10 @@ class Blocks {
|
||||
required: block.requiredEval.output,
|
||||
layout: block.layoutEval.output,
|
||||
style: block.styleEval.output,
|
||||
validation: block.validationEval.output,
|
||||
validation: {
|
||||
...(block.validationEval.output || {}),
|
||||
status: block.showValidation ? (block.validationEval.output || {}).status : null,
|
||||
},
|
||||
value: type.isNone(block.value) ? null : block.value,
|
||||
visible: block.visibleEval.output,
|
||||
};
|
||||
|
@ -16,23 +16,47 @@
|
||||
|
||||
import { type } from '@lowdefy/helpers';
|
||||
|
||||
const getMatch = (params) => (id) => {
|
||||
if (params.blockIds === true || (type.isArray(params.blockIds) && params.blockIds.includes(id))) {
|
||||
return true;
|
||||
}
|
||||
if (type.isArray(params.regex)) {
|
||||
for (const regex of params.regex) {
|
||||
if (regex.test(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
async function Validate({ context, params }) {
|
||||
if (!type.isNone(params) && !type.isString(params) && !type.isArray(params)) {
|
||||
let testParams = params;
|
||||
if (type.isNone(testParams)) {
|
||||
testParams = { blockIds: true };
|
||||
}
|
||||
if (type.isString(testParams)) {
|
||||
testParams = { blockIds: [testParams] };
|
||||
}
|
||||
if (type.isArray(testParams)) {
|
||||
testParams = { blockIds: testParams };
|
||||
}
|
||||
if (!type.isObject(testParams)) {
|
||||
throw new Error('Invalid validate params.');
|
||||
}
|
||||
context.showValidationErrors = true;
|
||||
let validationErrors = context.RootBlocks.validate();
|
||||
if (params) {
|
||||
const blockIds = type.isString(params) ? [params] : params;
|
||||
validationErrors = validationErrors.filter((block) => {
|
||||
return blockIds.includes(block.blockId);
|
||||
});
|
||||
if (type.isString(testParams.regex)) {
|
||||
testParams.regex = [testParams.regex];
|
||||
}
|
||||
if (type.isArray(testParams.regex)) {
|
||||
testParams.regex = testParams.regex.map((regex) => new RegExp(regex));
|
||||
}
|
||||
const validationErrors = context.RootBlocks.validate(testParams, getMatch(testParams));
|
||||
if (validationErrors.length > 0) {
|
||||
const message = `Your input has ${validationErrors.length} validation error${
|
||||
validationErrors.length !== 1 ? 's' : ''
|
||||
}.`;
|
||||
const error = new Error(message);
|
||||
const error = new Error(
|
||||
`Your input has ${validationErrors.length} validation error${
|
||||
validationErrors.length !== 1 ? 's' : ''
|
||||
}.`
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
@ -29,8 +29,9 @@ const RealDate = Date;
|
||||
const mockDate = jest.fn(() => ({ date: 0 }));
|
||||
mockDate.now = jest.fn(() => 0);
|
||||
|
||||
// Comment out to use console.log
|
||||
// Comment out to use console
|
||||
console.log = () => {};
|
||||
console.error = () => {};
|
||||
|
||||
beforeEach(() => {
|
||||
displayMessage.mockReset();
|
||||
@ -88,7 +89,7 @@ test('Validate required field', async () => {
|
||||
rootBlock,
|
||||
});
|
||||
const { button, text1 } = context.RootBlocks.map;
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: ['This field is required'],
|
||||
status: null,
|
||||
warnings: [],
|
||||
@ -120,7 +121,7 @@ test('Validate required field', async () => {
|
||||
startTimestamp: { date: 0 },
|
||||
endTimestamp: { date: 0 },
|
||||
});
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: ['This field is required'],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
@ -155,7 +156,7 @@ test('Validate required field', async () => {
|
||||
startTimestamp: { date: 0 },
|
||||
endTimestamp: { date: 0 },
|
||||
});
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'success',
|
||||
warnings: [],
|
||||
@ -226,7 +227,7 @@ test('Validate all fields', async () => {
|
||||
rootBlock,
|
||||
});
|
||||
const { button, text1, text2 } = context.RootBlocks.map;
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: ['text1 does not match pattern "text1"'],
|
||||
status: null,
|
||||
warnings: [],
|
||||
@ -258,12 +259,12 @@ test('Validate all fields', async () => {
|
||||
startTimestamp: { date: 0 },
|
||||
endTimestamp: { date: 0 },
|
||||
});
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: ['text1 does not match pattern "text1"'],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
});
|
||||
expect(text2.validationEval.output).toEqual({
|
||||
expect(text2.eval.validation).toEqual({
|
||||
errors: ['text2 does not match pattern "text2"'],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
@ -309,12 +310,12 @@ test('Validate all fields', async () => {
|
||||
startTimestamp: { date: 0 },
|
||||
endTimestamp: { date: 0 },
|
||||
});
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'success',
|
||||
warnings: [],
|
||||
});
|
||||
expect(text2.validationEval.output).toEqual({
|
||||
expect(text2.eval.validation).toEqual({
|
||||
errors: ['text2 does not match pattern "text2"'],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
@ -349,12 +350,12 @@ test('Validate all fields', async () => {
|
||||
startTimestamp: { date: 0 },
|
||||
endTimestamp: { date: 0 },
|
||||
});
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'success',
|
||||
warnings: [],
|
||||
});
|
||||
expect(text2.validationEval.output).toEqual({
|
||||
expect(text2.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'success',
|
||||
warnings: [],
|
||||
@ -424,7 +425,7 @@ test('Validate only one field', async () => {
|
||||
rootBlock,
|
||||
});
|
||||
const { button, text1, text2 } = context.RootBlocks.map;
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: ['text1 does not match pattern "text1"'],
|
||||
status: null,
|
||||
warnings: [],
|
||||
@ -457,14 +458,14 @@ test('Validate only one field', async () => {
|
||||
startTimestamp: { date: 0 },
|
||||
endTimestamp: { date: 0 },
|
||||
});
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: ['text1 does not match pattern "text1"'],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
});
|
||||
expect(text2.validationEval.output).toEqual({
|
||||
expect(text2.eval.validation).toEqual({
|
||||
errors: ['text2 does not match pattern "text2"'],
|
||||
status: 'error',
|
||||
status: null,
|
||||
warnings: [],
|
||||
});
|
||||
expect(displayMessage.mock.calls).toMatchInlineSnapshot(`
|
||||
@ -497,14 +498,14 @@ test('Validate only one field', async () => {
|
||||
startTimestamp: { date: 0 },
|
||||
endTimestamp: { date: 0 },
|
||||
});
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'success',
|
||||
warnings: [],
|
||||
});
|
||||
expect(text2.validationEval.output).toEqual({
|
||||
expect(text2.eval.validation).toEqual({
|
||||
errors: ['text2 does not match pattern "text2"'],
|
||||
status: 'error',
|
||||
status: null,
|
||||
warnings: [],
|
||||
});
|
||||
expect(displayMessage.mock.calls).toMatchInlineSnapshot(`Array []`);
|
||||
@ -587,17 +588,17 @@ test('Validate list of fields', async () => {
|
||||
});
|
||||
const { button, text1, text2, text3 } = context.RootBlocks.map;
|
||||
text1.setValue('text1');
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: null,
|
||||
warnings: [],
|
||||
});
|
||||
expect(text2.validationEval.output).toEqual({
|
||||
expect(text2.eval.validation).toEqual({
|
||||
errors: ['text2 does not match pattern "text2"'],
|
||||
status: null,
|
||||
warnings: [],
|
||||
});
|
||||
expect(text3.validationEval.output).toEqual({
|
||||
expect(text3.eval.validation).toEqual({
|
||||
errors: ['text3 does not match pattern "text3"'],
|
||||
status: null,
|
||||
warnings: [],
|
||||
@ -644,19 +645,19 @@ test('Validate list of fields', async () => {
|
||||
displayMessage.mockReset();
|
||||
displayMessage.mockImplementation(() => closeLoader);
|
||||
text2.setValue('text2');
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'success',
|
||||
warnings: [],
|
||||
});
|
||||
expect(text2.validationEval.output).toEqual({
|
||||
expect(text2.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'success',
|
||||
warnings: [],
|
||||
});
|
||||
expect(text3.validationEval.output).toEqual({
|
||||
expect(text3.eval.validation).toEqual({
|
||||
errors: ['text3 does not match pattern "text3"'],
|
||||
status: 'error',
|
||||
status: null,
|
||||
warnings: [],
|
||||
});
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
@ -675,9 +676,9 @@ test('Validate list of fields', async () => {
|
||||
startTimestamp: { date: 0 },
|
||||
endTimestamp: { date: 0 },
|
||||
});
|
||||
expect(text3.validationEval.output).toEqual({
|
||||
expect(text3.eval.validation).toEqual({
|
||||
errors: ['text3 does not match pattern "text3"'],
|
||||
status: 'error',
|
||||
status: null,
|
||||
warnings: [],
|
||||
});
|
||||
expect(displayMessage.mock.calls).toMatchInlineSnapshot(`Array []`);
|
||||
@ -703,7 +704,7 @@ test('Invalid Validate params', async () => {
|
||||
{
|
||||
id: 'validate',
|
||||
type: 'Validate',
|
||||
params: { invalid: true },
|
||||
params: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -725,9 +726,7 @@ test('Invalid Validate params', async () => {
|
||||
error: {
|
||||
action: {
|
||||
id: 'validate',
|
||||
params: {
|
||||
invalid: true,
|
||||
},
|
||||
params: 1,
|
||||
type: 'Validate',
|
||||
},
|
||||
error: {
|
||||
@ -808,9 +807,9 @@ test('Validate does not fail on warnings', async () => {
|
||||
rootBlock,
|
||||
});
|
||||
const { button, text1 } = context.RootBlocks.map;
|
||||
expect(text1.validationEval.output).toEqual({
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'warning',
|
||||
status: null,
|
||||
warnings: ['text1 does not match pattern "text1"'],
|
||||
});
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
@ -828,4 +827,356 @@ test('Validate does not fail on warnings', async () => {
|
||||
startTimestamp: { date: 0 },
|
||||
endTimestamp: { date: 0 },
|
||||
});
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'warning',
|
||||
warnings: ['text1 does not match pattern "text1"'],
|
||||
});
|
||||
});
|
||||
|
||||
test('Validate on nested objects using params.regex string', async () => {
|
||||
const rootBlock = {
|
||||
blockId: 'root',
|
||||
meta: {
|
||||
category: 'context',
|
||||
},
|
||||
areas: {
|
||||
content: {
|
||||
blocks: [
|
||||
{
|
||||
blockId: 'obj.text1',
|
||||
type: 'TextInput',
|
||||
meta: {
|
||||
category: 'input',
|
||||
valueType: 'string',
|
||||
},
|
||||
validate: [
|
||||
{
|
||||
pass: { _regex: { pattern: 'text1', key: 'text1' } },
|
||||
message: 'text1 does not match pattern "text1"',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blockId: 'text2',
|
||||
type: 'TextInput',
|
||||
meta: {
|
||||
category: 'input',
|
||||
valueType: 'string',
|
||||
},
|
||||
validate: [
|
||||
{
|
||||
pass: { _regex: { pattern: 'text2', key: 'text2' } },
|
||||
message: 'text2 does not match pattern "text2"',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blockId: 'button',
|
||||
type: 'Button',
|
||||
meta: {
|
||||
category: 'display',
|
||||
},
|
||||
events: {
|
||||
onClick: [
|
||||
{
|
||||
id: 'validate',
|
||||
type: 'Validate',
|
||||
params: {
|
||||
regex: '^obj.*1$',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const context = await testContext({
|
||||
lowdefy,
|
||||
rootBlock,
|
||||
});
|
||||
const { button, 'obj.text1': text1 } = context.RootBlocks.map;
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: ['text1 does not match pattern "text1"'],
|
||||
status: null,
|
||||
warnings: [],
|
||||
});
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
error: {
|
||||
error: { type: 'Validate', error: new Error('Your input has 1 validation error.'), index: 0 },
|
||||
action: { id: 'validate', type: 'Validate', params: { regex: '^obj.*1$' } },
|
||||
},
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
validate: {
|
||||
type: 'Validate',
|
||||
error: new Error('Your input has 1 validation error.'),
|
||||
index: 0,
|
||||
},
|
||||
},
|
||||
endTimestamp: { date: 0 },
|
||||
startTimestamp: { date: 0 },
|
||||
success: false,
|
||||
});
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: ['text1 does not match pattern "text1"'],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
});
|
||||
});
|
||||
|
||||
test('Validate on nested objects using params.regex array', async () => {
|
||||
const rootBlock = {
|
||||
blockId: 'root',
|
||||
meta: {
|
||||
category: 'context',
|
||||
},
|
||||
areas: {
|
||||
content: {
|
||||
blocks: [
|
||||
{
|
||||
blockId: 'obj.text1',
|
||||
type: 'TextInput',
|
||||
meta: {
|
||||
category: 'input',
|
||||
valueType: 'string',
|
||||
},
|
||||
validate: [
|
||||
{
|
||||
pass: { _regex: { pattern: 'text1', key: 'text1' } },
|
||||
message: 'text1 does not match pattern "text1"',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blockId: 'obj.abc1',
|
||||
type: 'TextInput',
|
||||
meta: {
|
||||
category: 'input',
|
||||
valueType: 'string',
|
||||
},
|
||||
validate: [
|
||||
{
|
||||
pass: { _regex: { pattern: 'abc1', key: 'abc1' } },
|
||||
message: 'abc1 does not match pattern "abc1"',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blockId: 'text2',
|
||||
type: 'TextInput',
|
||||
meta: {
|
||||
category: 'input',
|
||||
valueType: 'string',
|
||||
},
|
||||
validate: [
|
||||
{
|
||||
pass: { _regex: { pattern: 'text2', key: 'text2' } },
|
||||
message: 'text2 does not match pattern "text2"',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blockId: 'button',
|
||||
type: 'Button',
|
||||
meta: {
|
||||
category: 'display',
|
||||
},
|
||||
events: {
|
||||
onClick: [
|
||||
{
|
||||
id: 'validate',
|
||||
type: 'Validate',
|
||||
params: {
|
||||
regex: ['^obj.*1$'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const context = await testContext({
|
||||
lowdefy,
|
||||
rootBlock,
|
||||
});
|
||||
const { button, text2, 'obj.text1': text1, 'obj.abc1': abc1 } = context.RootBlocks.map;
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: ['text1 does not match pattern "text1"'],
|
||||
status: null,
|
||||
warnings: [],
|
||||
});
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
error: {
|
||||
error: {
|
||||
type: 'Validate',
|
||||
error: new Error('Your input has 2 validation errors.'),
|
||||
index: 0,
|
||||
},
|
||||
action: { id: 'validate', type: 'Validate', params: { regex: ['^obj.*1$'] } },
|
||||
},
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
validate: {
|
||||
type: 'Validate',
|
||||
error: new Error('Your input has 2 validation errors.'),
|
||||
index: 0,
|
||||
},
|
||||
},
|
||||
endTimestamp: { date: 0 },
|
||||
startTimestamp: { date: 0 },
|
||||
success: false,
|
||||
});
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: ['text1 does not match pattern "text1"'],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
});
|
||||
expect(abc1.eval.validation).toEqual({
|
||||
errors: ['abc1 does not match pattern "abc1"'],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
});
|
||||
expect(text2.eval.validation).toEqual({
|
||||
errors: ['text2 does not match pattern "text2"'],
|
||||
status: null,
|
||||
warnings: [],
|
||||
});
|
||||
});
|
||||
|
||||
test('Validate on nested objects using params.regex array and blockIds', async () => {
|
||||
const rootBlock = {
|
||||
blockId: 'root',
|
||||
meta: {
|
||||
category: 'context',
|
||||
},
|
||||
areas: {
|
||||
content: {
|
||||
blocks: [
|
||||
{
|
||||
blockId: 'obj.text1',
|
||||
type: 'TextInput',
|
||||
meta: {
|
||||
category: 'input',
|
||||
valueType: 'string',
|
||||
},
|
||||
validate: [
|
||||
{
|
||||
pass: { _regex: { pattern: 'text1', key: 'text1' } },
|
||||
message: 'text1 does not match pattern "text1"',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blockId: 'obj.abc1',
|
||||
type: 'TextInput',
|
||||
meta: {
|
||||
category: 'input',
|
||||
valueType: 'string',
|
||||
},
|
||||
validate: [
|
||||
{
|
||||
pass: { _regex: { pattern: 'abc1', key: 'abc1' } },
|
||||
message: 'abc1 does not match pattern "abc1"',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blockId: 'text2',
|
||||
type: 'TextInput',
|
||||
meta: {
|
||||
category: 'input',
|
||||
valueType: 'string',
|
||||
},
|
||||
validate: [
|
||||
{
|
||||
pass: { _regex: { pattern: 'text2', key: 'text2' } },
|
||||
message: 'text2 does not match pattern "text2"',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blockId: 'button',
|
||||
type: 'Button',
|
||||
meta: {
|
||||
category: 'display',
|
||||
},
|
||||
events: {
|
||||
onClick: [
|
||||
{
|
||||
id: 'validate',
|
||||
type: 'Validate',
|
||||
params: {
|
||||
regex: ['^obj.*t1$'],
|
||||
blockIds: ['text2'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const context = await testContext({
|
||||
lowdefy,
|
||||
rootBlock,
|
||||
});
|
||||
const { button, text2, 'obj.text1': text1, 'obj.abc1': abc1 } = context.RootBlocks.map;
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: ['text1 does not match pattern "text1"'],
|
||||
status: null,
|
||||
warnings: [],
|
||||
});
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
error: {
|
||||
error: {
|
||||
type: 'Validate',
|
||||
error: new Error('Your input has 2 validation errors.'),
|
||||
index: 0,
|
||||
},
|
||||
action: {
|
||||
id: 'validate',
|
||||
type: 'Validate',
|
||||
params: { regex: ['^obj.*t1$'], blockIds: ['text2'] },
|
||||
},
|
||||
},
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
validate: {
|
||||
type: 'Validate',
|
||||
error: new Error('Your input has 2 validation errors.'),
|
||||
index: 0,
|
||||
},
|
||||
},
|
||||
endTimestamp: { date: 0 },
|
||||
startTimestamp: { date: 0 },
|
||||
success: false,
|
||||
});
|
||||
expect(text1.eval.validation).toEqual({
|
||||
errors: ['text1 does not match pattern "text1"'],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
});
|
||||
expect(text2.eval.validation).toEqual({
|
||||
errors: ['text2 does not match pattern "text2"'],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
});
|
||||
expect(abc1.eval.validation).toEqual({
|
||||
errors: ['abc1 does not match pattern "abc1"'],
|
||||
status: null,
|
||||
warnings: [],
|
||||
});
|
||||
});
|
||||
|
@ -1,27 +1,27 @@
|
||||
/*
|
||||
Copyright 2020-2021 Lowdefy, Inc
|
||||
Copyright 2020-2021 Lowdefy, Inc
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import testContext from '../testContext';
|
||||
|
||||
const pageId = 'one';
|
||||
const match = () => true;
|
||||
const lowdefy = { pageId };
|
||||
|
||||
// Comment out to use console.log
|
||||
// Comment out to use console
|
||||
console.log = () => {};
|
||||
// Comment out to use console.log
|
||||
console.error = () => {};
|
||||
|
||||
test('parse validate on fields', async () => {
|
||||
@ -63,25 +63,24 @@ test('parse validate on fields', async () => {
|
||||
const { text } = context.RootBlocks.map;
|
||||
|
||||
expect(context.state).toEqual({ text: 'a' });
|
||||
expect(text.validationEval.output).toEqual({ errors: ["Not 'c'"], status: null, warnings: [] });
|
||||
expect(text.eval.validation).toEqual({ errors: ["Not 'c'"], status: null, warnings: [] });
|
||||
|
||||
context.showValidationErrors = true;
|
||||
context.update();
|
||||
expect(text.validationEval.output).toEqual({
|
||||
context.RootBlocks.validate(true, match);
|
||||
expect(text.eval.validation).toEqual({
|
||||
errors: ["Not 'c'"],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
});
|
||||
|
||||
text.setValue('c');
|
||||
expect(text.validationEval.output).toEqual({
|
||||
expect(text.eval.validation).toEqual({
|
||||
errors: ["Not 'a'"],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
});
|
||||
|
||||
text.setValue('b');
|
||||
expect(text.validationEval.output).toEqual({
|
||||
expect(text.eval.validation).toEqual({
|
||||
errors: ["Not 'a'", "Not 'c'"],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
@ -121,9 +120,8 @@ test('validate should fail if parser has errors', async () => {
|
||||
});
|
||||
const { text } = context.RootBlocks.map;
|
||||
|
||||
context.showValidationErrors = true;
|
||||
context.update();
|
||||
expect(text.validationEval.output).toEqual({
|
||||
context.RootBlocks.validate(true, match);
|
||||
expect(text.eval.validation).toEqual({
|
||||
errors: ['Parser failed'],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
@ -168,9 +166,8 @@ test('validate, only test where parser failed should fail', async () => {
|
||||
});
|
||||
const { text } = context.RootBlocks.map;
|
||||
|
||||
context.showValidationErrors = true;
|
||||
context.update();
|
||||
expect(text.validationEval.output).toEqual({
|
||||
context.RootBlocks.validate(true, match);
|
||||
expect(text.eval.validation).toEqual({
|
||||
errors: ['Parser failed'],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
@ -211,20 +208,19 @@ test('parse validate, validate an object not an array', async () => {
|
||||
});
|
||||
const { text } = context.RootBlocks.map;
|
||||
expect(context.state).toEqual({ text: 'a' });
|
||||
expect(text.validationEval.output).toEqual({ errors: ["Not 'c'"], status: null, warnings: [] });
|
||||
expect(text.eval.validation).toEqual({ errors: ["Not 'c'"], status: null, warnings: [] });
|
||||
|
||||
context.showValidationErrors = true;
|
||||
context.update();
|
||||
expect(text.validationEval.output).toEqual({
|
||||
context.RootBlocks.validate(true, match);
|
||||
expect(text.eval.validation).toEqual({
|
||||
errors: ["Not 'c'"],
|
||||
status: 'error',
|
||||
warnings: [],
|
||||
});
|
||||
text.setValue('c');
|
||||
expect(text.validationEval.output).toEqual({ errors: [], status: 'success', warnings: [] });
|
||||
expect(text.eval.validation).toEqual({ errors: [], status: 'success', warnings: [] });
|
||||
});
|
||||
|
||||
test('RootBlock.validate() to ignore errors where field not visible', async () => {
|
||||
test('RootBlock.validate(true, match) to ignore errors where field not visible', async () => {
|
||||
const rootBlock = {
|
||||
blockId: 'root',
|
||||
meta: {
|
||||
@ -288,14 +284,10 @@ test('RootBlock.validate() to ignore errors where field not visible', async () =
|
||||
rootBlock,
|
||||
});
|
||||
const { text, list } = context.RootBlocks.map;
|
||||
expect(context.RootBlocks.validate()).toEqual([]);
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([]);
|
||||
|
||||
text.setValue('1');
|
||||
expect(context.RootBlocks.validate()).toEqual([]);
|
||||
|
||||
context.showValidationErrors = true;
|
||||
context.RootBlocks.update();
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{
|
||||
blockId: 'list',
|
||||
validation: { errors: ['Error 123'], status: 'error', warnings: [] },
|
||||
@ -303,7 +295,7 @@ test('RootBlock.validate() to ignore errors where field not visible', async () =
|
||||
]);
|
||||
|
||||
text.setValue('12');
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{
|
||||
blockId: 'list',
|
||||
validation: { errors: ['Error 123'], status: 'error', warnings: [] },
|
||||
@ -311,11 +303,11 @@ test('RootBlock.validate() to ignore errors where field not visible', async () =
|
||||
]);
|
||||
|
||||
text.setValue('123');
|
||||
expect(context.RootBlocks.validate()).toEqual([]);
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([]);
|
||||
|
||||
text.setValue('12');
|
||||
list.pushItem();
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{ blockId: 'list', validation: { errors: ['Error 123'], status: 'error', warnings: [] } },
|
||||
{
|
||||
blockId: 'list.0.innerText',
|
||||
@ -325,7 +317,7 @@ test('RootBlock.validate() to ignore errors where field not visible', async () =
|
||||
|
||||
text.setValue('123');
|
||||
list.pushItem();
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{
|
||||
blockId: 'list.0.innerText',
|
||||
validation: { errors: ['Error 1234'], status: 'error', warnings: [] },
|
||||
@ -337,13 +329,13 @@ test('RootBlock.validate() to ignore errors where field not visible', async () =
|
||||
]);
|
||||
|
||||
text.setValue('1234');
|
||||
expect(context.RootBlocks.validate()).toEqual([]);
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([]);
|
||||
|
||||
text.setValue('0');
|
||||
expect(context.RootBlocks.validate()).toEqual([]);
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([]);
|
||||
|
||||
text.setValue('12');
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{ blockId: 'list', validation: { errors: ['Error 123'], status: 'error', warnings: [] } },
|
||||
{
|
||||
blockId: 'list.0.innerText',
|
||||
@ -356,7 +348,7 @@ test('RootBlock.validate() to ignore errors where field not visible', async () =
|
||||
]);
|
||||
});
|
||||
|
||||
test('required on input to return validation error on RootBlock.validate()', async () => {
|
||||
test('required on input to return validation error on RootBlock.validate(true, match)', async () => {
|
||||
const rootBlock = {
|
||||
blockId: 'root',
|
||||
meta: {
|
||||
@ -386,18 +378,16 @@ test('required on input to return validation error on RootBlock.validate()', asy
|
||||
expect(context.state).toEqual({
|
||||
text: null,
|
||||
});
|
||||
expect(context.RootBlocks.validate()).toEqual([]);
|
||||
context.showValidationErrors = true;
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{
|
||||
blockId: 'text',
|
||||
validation: { errors: ['This field is required'], status: 'error', warnings: [] },
|
||||
},
|
||||
]);
|
||||
text.setValue('a');
|
||||
expect(context.RootBlocks.validate()).toEqual([]);
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([]);
|
||||
text.setValue('');
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{
|
||||
blockId: 'text',
|
||||
validation: { errors: ['This field is required'], status: 'error', warnings: [] },
|
||||
@ -405,7 +395,7 @@ test('required on input to return validation error on RootBlock.validate()', asy
|
||||
]);
|
||||
});
|
||||
|
||||
test('required on input to return validation error with priority over validation errors on RootBlock.validate()', async () => {
|
||||
test('required on input to return validation error with priority over validation errors on RootBlock.validate(true, match)', async () => {
|
||||
const rootBlock = {
|
||||
blockId: 'root',
|
||||
meta: {
|
||||
@ -442,9 +432,7 @@ test('required on input to return validation error with priority over validation
|
||||
expect(context.state).toEqual({
|
||||
text: null,
|
||||
});
|
||||
expect(context.RootBlocks.validate()).toEqual([]);
|
||||
context.showValidationErrors = true;
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{
|
||||
blockId: 'text',
|
||||
validation: {
|
||||
@ -455,7 +443,7 @@ test('required on input to return validation error with priority over validation
|
||||
},
|
||||
]);
|
||||
text.setValue('a');
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{
|
||||
blockId: 'text',
|
||||
validation: {
|
||||
@ -466,9 +454,9 @@ test('required on input to return validation error with priority over validation
|
||||
},
|
||||
]);
|
||||
text.setValue('1234');
|
||||
expect(context.RootBlocks.validate()).toEqual([]);
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([]);
|
||||
text.setValue('');
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{
|
||||
blockId: 'text',
|
||||
validation: {
|
||||
@ -480,7 +468,7 @@ test('required on input to return validation error with priority over validation
|
||||
]);
|
||||
});
|
||||
|
||||
test('nested arrays with validate, and RootBlock.validate() returns all validation errors', async () => {
|
||||
test('nested arrays with validate, and RootBlock.validate(true, match) returns all validation errors', async () => {
|
||||
const rootBlock = {
|
||||
blockId: 'root',
|
||||
meta: {
|
||||
@ -609,11 +597,7 @@ test('nested arrays with validate, and RootBlock.validate() returns all validati
|
||||
{ innerList: [], swtch: true },
|
||||
],
|
||||
});
|
||||
expect(context.RootBlocks.validate()).toEqual([]);
|
||||
|
||||
context.showValidationErrors = true;
|
||||
context.RootBlocks.update();
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{
|
||||
blockId: 'list.0.swtch',
|
||||
validation: {
|
||||
@ -672,7 +656,7 @@ test('nested arrays with validate, and RootBlock.validate() returns all validati
|
||||
},
|
||||
]);
|
||||
text.setValue('1');
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{
|
||||
blockId: 'list.0.swtch',
|
||||
validation: {
|
||||
@ -715,9 +699,9 @@ test('nested arrays with validate, and RootBlock.validate() returns all validati
|
||||
},
|
||||
]);
|
||||
text.setValue('12');
|
||||
expect(context.RootBlocks.validate()).toEqual([]);
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([]);
|
||||
text.setValue('0');
|
||||
expect(context.RootBlocks.validate()).toEqual([
|
||||
expect(context.RootBlocks.validate(true, match)).toEqual([
|
||||
{
|
||||
blockId: 'list.0.swtch',
|
||||
validation: {
|
||||
@ -818,29 +802,28 @@ test('validation warnings', async () => {
|
||||
const { text } = context.RootBlocks.map;
|
||||
|
||||
expect(context.state).toEqual({ text: 'a' });
|
||||
expect(text.validationEval.output).toEqual({
|
||||
expect(text.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'warning',
|
||||
status: null,
|
||||
warnings: ["Not 'c'"],
|
||||
});
|
||||
|
||||
context.showValidationErrors = true;
|
||||
context.update();
|
||||
expect(text.validationEval.output).toEqual({
|
||||
context.RootBlocks.validate(true, match);
|
||||
expect(text.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'warning',
|
||||
warnings: ["Not 'c'"],
|
||||
});
|
||||
|
||||
text.setValue('c');
|
||||
expect(text.validationEval.output).toEqual({
|
||||
expect(text.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'warning',
|
||||
warnings: ["Not 'a'"],
|
||||
});
|
||||
|
||||
text.setValue('b');
|
||||
expect(text.validationEval.output).toEqual({
|
||||
expect(text.eval.validation).toEqual({
|
||||
errors: [],
|
||||
status: 'warning',
|
||||
warnings: ["Not 'a'", "Not 'c'"],
|
||||
|
Loading…
Reference in New Issue
Block a user