mirror of
https://github.com/lowdefy/lowdefy.git
synced 2025-02-17 14:30:34 +08:00
commit
4bde58f654
@ -45,11 +45,11 @@ const ConfirmModal = ({ blockId, events, content, methods, properties }) => {
|
||||
zIndex: properties.zIndex,
|
||||
onOk: async () => {
|
||||
const response = await methods.triggerEvent({ name: 'onOk' });
|
||||
if (response.success === false) throw response;
|
||||
if (response.success === false && response.bounced !== true) throw response;
|
||||
},
|
||||
onCancel: async () => {
|
||||
const response = await methods.triggerEvent({ name: 'onCancel' });
|
||||
if (response.success === false) throw response;
|
||||
if (response.success === false && response.bounced !== true) throw response;
|
||||
},
|
||||
...additionalProps,
|
||||
});
|
||||
|
@ -59,7 +59,9 @@ const DrawerBlock = ({ blockId, content, properties, methods, rename, onClose })
|
||||
(async () => {
|
||||
const response = await methods.triggerEvent({ name: 'onClose' });
|
||||
if (response.success === false) return;
|
||||
triggerSetOpen({ state: false, setOpen, methods, rename });
|
||||
if (response.bounced !== true) {
|
||||
triggerSetOpen({ state: false, setOpen, methods, rename });
|
||||
}
|
||||
})
|
||||
}
|
||||
drawerStyle={methods.makeCssClass(properties.drawerStyle, { styleObjectOnly: true })}
|
||||
|
@ -56,12 +56,16 @@ const ModalBlock = ({ blockId, content, properties, events, methods }) => {
|
||||
onOk={async () => {
|
||||
const response = await methods.triggerEvent({ name: 'onOk' });
|
||||
if (response.success === false) return;
|
||||
triggerSetOpen({ state: false, setOpen, methods });
|
||||
if (response.bounced !== true) {
|
||||
triggerSetOpen({ state: false, setOpen, methods });
|
||||
}
|
||||
}}
|
||||
onCancel={async () => {
|
||||
const response = await methods.triggerEvent({ name: 'onCancel' });
|
||||
if (response.success === false) return;
|
||||
triggerSetOpen({ state: false, setOpen, methods });
|
||||
if (response.bounced !== true) {
|
||||
triggerSetOpen({ state: false, setOpen, methods });
|
||||
}
|
||||
}}
|
||||
afterClose={() => methods.triggerEvent({ name: 'afterClose' })}
|
||||
confirmLoading={get(events, 'onOk.loading')}
|
||||
|
@ -97,19 +97,23 @@ _ref:
|
||||
|
||||
When events are triggered, the can provide a data object describing the event (e.g. a description of the clicked item or uploaded file). This data object can be accessed using the [`_event`](/_event) operator in an action definition.
|
||||
|
||||
# Catching action errors
|
||||
|
||||
If one action in the chain of event actions fails by throwing an error, the actions in the list following the failed action will not be executed. To handle any errors thrown by an action, Lowdefy event actions can be provided as lists of `try` and `catch` actions.
|
||||
|
||||
The schema for passing actions to Lowdefy events is:
|
||||
```
|
||||
(eventName: action[])
|
||||
(eventName: {
|
||||
debounce?: {
|
||||
ms?: number,
|
||||
immediate?: boolean,
|
||||
},
|
||||
try: action[],
|
||||
catch?: action[],
|
||||
})
|
||||
```
|
||||
|
||||
# Catching action errors
|
||||
|
||||
If one action in the chain of event actions fails by throwing an error, the actions in the list following the failed action will not be executed. To handle any errors thrown by an action, Lowdefy event actions can be provided as lists of `try` and `catch` actions.
|
||||
|
||||
###### Event try catch actions example for dealing with action errors:
|
||||
```yaml
|
||||
- id: block_with_actions
|
||||
@ -132,6 +136,51 @@ _ref:
|
||||
# ...
|
||||
```
|
||||
|
||||
# Debouncing events
|
||||
|
||||
Event debouncing can be turned on by setting the `debounce` field on event objects. If `debounce.immediate` is `true`, leading edge debouncing or throttling will apply, else it will be debounced as trailing edge.
|
||||
|
||||
To control the debounce delay, set `debounce.ms` to the number of milliseconds to delay. The default delay is 300 milliseconds. If an event is triggered within that time, the event will not be triggered again. See [debounce vs throttling](https://redd.one/blog/debounce-vs-throttle) for a more detailed explanation.
|
||||
|
||||
###### Event trailing edge debouncing example:
|
||||
```yaml
|
||||
- id: block_with_actions
|
||||
type: Block
|
||||
properties:
|
||||
# ...
|
||||
events:
|
||||
onEvent1:
|
||||
debounce:
|
||||
ms: 1000
|
||||
try:
|
||||
- id: action1
|
||||
type: ActionType1
|
||||
params:
|
||||
# ...
|
||||
- id: action2
|
||||
type: ActionType2
|
||||
```
|
||||
|
||||
###### Event throttling or leading edge debouncing example:
|
||||
```yaml
|
||||
- id: block_with_actions
|
||||
type: Block
|
||||
properties:
|
||||
# ...
|
||||
events:
|
||||
onEvent1:
|
||||
debounce:
|
||||
ms: 1000
|
||||
immediate: true
|
||||
try:
|
||||
- id: action1
|
||||
type: ActionType1
|
||||
params:
|
||||
# ...
|
||||
- id: action2
|
||||
type: ActionType2
|
||||
```
|
||||
|
||||
# Context initialisation events
|
||||
|
||||
Four events are always defined for [`context`](/context) type blocks, called in the following order:
|
||||
|
@ -90,33 +90,36 @@ class Actions {
|
||||
console.error(errorCatch);
|
||||
return {
|
||||
blockId: block.blockId,
|
||||
bounced: false,
|
||||
endTimestamp: new Date(),
|
||||
error,
|
||||
errorCatch,
|
||||
event,
|
||||
eventName,
|
||||
responses,
|
||||
endTimestamp: new Date(),
|
||||
startTimestamp,
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
return {
|
||||
blockId: block.blockId,
|
||||
bounced: false,
|
||||
endTimestamp: new Date(),
|
||||
error,
|
||||
event,
|
||||
eventName,
|
||||
responses,
|
||||
endTimestamp: new Date(),
|
||||
startTimestamp,
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
return {
|
||||
blockId: block.blockId,
|
||||
bounced: false,
|
||||
endTimestamp: new Date(),
|
||||
event,
|
||||
eventName,
|
||||
responses,
|
||||
endTimestamp: new Date(),
|
||||
startTimestamp,
|
||||
success: true,
|
||||
};
|
||||
|
@ -18,7 +18,9 @@ import { type } from '@lowdefy/helpers';
|
||||
|
||||
class Events {
|
||||
constructor({ arrayIndices, block, context }) {
|
||||
this.defaultDebounceMs = 300;
|
||||
this.events = {};
|
||||
this.timeouts = {};
|
||||
this.arrayIndices = arrayIndices;
|
||||
this.block = block;
|
||||
this.context = context;
|
||||
@ -35,6 +37,7 @@ class Events {
|
||||
return {
|
||||
actions: (type.isObject(actions) ? actions.try : actions) || [],
|
||||
catchActions: (type.isObject(actions) ? actions.catch : []) || [],
|
||||
debounce: type.isObject(actions) ? actions.debounce : null,
|
||||
history: [],
|
||||
loading: false,
|
||||
};
|
||||
@ -52,37 +55,82 @@ class Events {
|
||||
|
||||
async triggerEvent({ name, event }) {
|
||||
const eventDescription = this.events[name];
|
||||
let result = {
|
||||
blockId: this.block.blockId,
|
||||
event,
|
||||
eventName: name,
|
||||
responses: {},
|
||||
endTimestamp: new Date(),
|
||||
startTimestamp: new Date(),
|
||||
success: true,
|
||||
bounced: false,
|
||||
};
|
||||
// no event
|
||||
if (type.isUndefined(eventDescription)) {
|
||||
return {
|
||||
blockId: this.block.blockId,
|
||||
event,
|
||||
eventName: name,
|
||||
responses: {},
|
||||
endTimestamp: new Date(),
|
||||
startTimestamp: new Date(),
|
||||
success: true,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
eventDescription.loading = true;
|
||||
this.block.update = true;
|
||||
this.context.update();
|
||||
|
||||
const result = await this.context.Actions.callActions({
|
||||
actions: eventDescription.actions,
|
||||
arrayIndices: this.arrayIndices,
|
||||
block: this.block,
|
||||
catchActions: eventDescription.catchActions,
|
||||
event,
|
||||
eventName: name,
|
||||
const actionHandle = async () => {
|
||||
const res = await this.context.Actions.callActions({
|
||||
actions: eventDescription.actions,
|
||||
arrayIndices: this.arrayIndices,
|
||||
block: this.block,
|
||||
catchActions: eventDescription.catchActions,
|
||||
event,
|
||||
eventName: name,
|
||||
});
|
||||
eventDescription.history.unshift(res);
|
||||
this.context.eventLog.unshift(res);
|
||||
eventDescription.loading = false;
|
||||
this.block.update = true;
|
||||
this.context.update();
|
||||
return res;
|
||||
};
|
||||
|
||||
// no debounce
|
||||
if (type.isNone(eventDescription.debounce)) {
|
||||
return actionHandle();
|
||||
}
|
||||
const delay = !type.isNone(eventDescription.debounce.ms)
|
||||
? eventDescription.debounce.ms
|
||||
: this.defaultDebounceMs;
|
||||
// leading edge: bounce
|
||||
if (this.timeouts[name] && eventDescription.debounce.immediate === true) {
|
||||
result.bounced = true;
|
||||
eventDescription.history.unshift(result);
|
||||
this.context.eventLog.unshift(result);
|
||||
return result;
|
||||
}
|
||||
// leading edge: trigger
|
||||
if (eventDescription.debounce.immediate === true) {
|
||||
this.timeouts[name] = setTimeout(() => {
|
||||
this.timeouts[name] = null;
|
||||
}, delay);
|
||||
return actionHandle();
|
||||
}
|
||||
|
||||
// trailing edge
|
||||
if (eventDescription.bouncer) {
|
||||
eventDescription.bouncer();
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
const timeout = setTimeout(async () => {
|
||||
eventDescription.bouncer = null;
|
||||
const res = await actionHandle();
|
||||
resolve(res);
|
||||
}, delay);
|
||||
|
||||
eventDescription.bouncer = () => {
|
||||
clearTimeout(timeout);
|
||||
result.bounced = true;
|
||||
eventDescription.history.unshift(result);
|
||||
this.context.eventLog.unshift(result);
|
||||
resolve(result);
|
||||
};
|
||||
});
|
||||
|
||||
eventDescription.history.unshift(result);
|
||||
this.context.eventLog.unshift(result);
|
||||
eventDescription.loading = false;
|
||||
this.block.update = true;
|
||||
this.context.update();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,7 @@ test('call a synchronous action', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
responses: {
|
||||
@ -131,6 +132,7 @@ test('call a asynchronous action', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
responses: {
|
||||
@ -172,6 +174,7 @@ test('call 2 actions', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
responses: {
|
||||
@ -483,6 +486,7 @@ test('skip a action', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
responses: {
|
||||
@ -521,6 +525,7 @@ test('action throws a error', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
error: {
|
||||
@ -574,6 +579,7 @@ test('actions after a error are not called throws a error', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
error: {
|
||||
@ -625,6 +631,7 @@ test('Invalid action type', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
error: {
|
||||
@ -674,6 +681,7 @@ test('Parser error in action', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
error: {
|
||||
@ -968,6 +976,7 @@ test('Call catchActions when actions throws error', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
endTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
@ -1051,6 +1060,7 @@ test('Call catchActions when actions throws error and catchActions throws error'
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
endTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
@ -1134,6 +1144,7 @@ test('call 2 actions, first with async: true', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
responses: {
|
||||
@ -1150,6 +1161,7 @@ test('call 2 actions, first with async: true', async () => {
|
||||
await timeout(110);
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
responses: {
|
||||
@ -1195,6 +1207,7 @@ test('call async: true with error', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
responses: {
|
||||
@ -1211,6 +1224,7 @@ test('call async: true with error', async () => {
|
||||
await timeout(110);
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
responses: {
|
||||
@ -1256,6 +1270,7 @@ test('call 2 actions, first with async: false', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
responses: {
|
||||
@ -1301,6 +1316,7 @@ test('call 2 actions, first with async: null', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'blockId',
|
||||
bounced: false,
|
||||
event: {},
|
||||
eventName: 'eventName',
|
||||
responses: {
|
||||
|
@ -85,6 +85,7 @@ test('CallMethod with no args, synchronous method', async () => {
|
||||
const res = await button.triggerEvent({ name: 'onClick' });
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -161,6 +162,7 @@ test('CallMethod method return a promise', async () => {
|
||||
const res = await button.triggerEvent({ name: 'onClick' });
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -229,6 +231,7 @@ test('CallMethod with args not an array', async () => {
|
||||
const res = await button.triggerEvent({ name: 'onClick' });
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
@ -315,6 +318,7 @@ test('CallMethod with multiple positional args, synchronous method', async () =>
|
||||
const res = await button.triggerEvent({ name: 'onClick' });
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -535,6 +539,7 @@ test('CallMethod with method does not exist', async () => {
|
||||
const res = await button.triggerEvent({ name: 'onClick' });
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
|
@ -79,6 +79,7 @@ test('JsAction with no args, synchronous fn', async () => {
|
||||
const res = await button.triggerEvent({ name: 'onClick' });
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -145,6 +146,7 @@ test('JsAction with no args, async fn', async () => {
|
||||
const res = await button.triggerEvent({ name: 'onClick' });
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -206,6 +208,7 @@ test('JsAction with args, synchronous fn', async () => {
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"blockId": "button",
|
||||
"bounced": false,
|
||||
"endTimestamp": Object {
|
||||
"date": 0,
|
||||
},
|
||||
@ -298,6 +301,7 @@ test('JsAction name not a string', async () => {
|
||||
const res = await button.triggerEvent({ name: 'onClick' });
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
@ -364,11 +368,11 @@ test('JsAction args not an array', async () => {
|
||||
lowdefy,
|
||||
rootBlock,
|
||||
});
|
||||
const mockFn = jest.fn(() => 'js_fn');
|
||||
const { button } = context.RootBlocks.map;
|
||||
const res = await button.triggerEvent({ name: 'onClick' });
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
@ -437,11 +441,11 @@ test('JsAction args not a function', async () => {
|
||||
lowdefy,
|
||||
rootBlock,
|
||||
});
|
||||
const mockFn = jest.fn(() => 'js_fn');
|
||||
const { button } = context.RootBlocks.map;
|
||||
const res = await button.triggerEvent({ name: 'onClick' });
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
|
@ -163,6 +163,7 @@ test('Link error', async () => {
|
||||
]);
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
|
@ -141,6 +141,7 @@ test('Request call one request', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -221,6 +222,7 @@ test('Request call all requests', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -301,6 +303,7 @@ test('Request call array of requests', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -400,6 +403,7 @@ test('Request call request error', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
@ -469,6 +473,7 @@ test('Request call request graphql error', async () => {
|
||||
});
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
|
@ -112,6 +112,7 @@ test('RestValidation after required field', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
@ -155,6 +156,7 @@ test('RestValidation after required field', async () => {
|
||||
await reset.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
endTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
|
@ -92,6 +92,7 @@ test('Throw no params', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -153,6 +154,7 @@ test('Throw throw true no message or metaData', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
error: {
|
||||
error: {
|
||||
type: 'Throw',
|
||||
@ -236,6 +238,7 @@ test('Throw throw true message no metaData', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
error: {
|
||||
error: {
|
||||
type: 'Throw',
|
||||
@ -319,6 +322,7 @@ test('Throw throw true message metaData string', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
error: {
|
||||
error: {
|
||||
type: 'Throw',
|
||||
@ -410,6 +414,7 @@ test('Throw throw true message metaData object', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
error: {
|
||||
error: {
|
||||
type: 'Throw',
|
||||
@ -501,6 +506,7 @@ test('Throw throw false', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -562,6 +568,7 @@ test('Throw throw invalid', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
error: {
|
||||
error: {
|
||||
type: 'Throw',
|
||||
|
@ -97,6 +97,7 @@ test('Validate required field', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
@ -143,6 +144,7 @@ test('Validate required field', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -235,6 +237,7 @@ test('Validate all fields', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
@ -286,6 +289,7 @@ test('Validate all fields', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
@ -337,6 +341,7 @@ test('Validate all fields', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -433,6 +438,7 @@ test('Validate only one field', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
@ -485,6 +491,7 @@ test('Validate only one field', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -606,6 +613,7 @@ test('Validate list of fields', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
@ -663,6 +671,7 @@ test('Validate list of fields', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -721,6 +730,7 @@ test('Invalid Validate params', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
error: {
|
||||
@ -815,6 +825,7 @@ test('Validate does not fail on warnings', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
@ -906,6 +917,7 @@ test('Validate on nested objects using params.regex string', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
error: {
|
||||
error: { type: 'Validate', error: new Error('Your input has 1 validation error.'), index: 0 },
|
||||
action: { id: 'validate', type: 'Validate', params: { regex: '^obj.*1$' } },
|
||||
@ -1015,6 +1027,7 @@ test('Validate on nested objects using params.regex array', async () => {
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
error: {
|
||||
error: {
|
||||
type: 'Validate',
|
||||
@ -1139,6 +1152,7 @@ test('Validate on nested objects using params.regex array and blockIds', async (
|
||||
await button.triggerEvent({ name: 'onClick' });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
error: {
|
||||
error: {
|
||||
type: 'Validate',
|
||||
|
@ -132,6 +132,7 @@ test('Wait ms not a integer', async () => {
|
||||
const res = await button.triggerEvent({ name: 'onClick' });
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
endTimestamp: { date: 0 },
|
||||
error: {
|
||||
action: { id: 'a', params: { ms: 1.1 }, type: 'Wait' },
|
||||
|
@ -57,6 +57,10 @@ const lowdefy = {
|
||||
pageId,
|
||||
};
|
||||
|
||||
const timeout = (ms) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
|
||||
// Comment out to use console
|
||||
console.log = () => {};
|
||||
console.error = () => {};
|
||||
@ -105,10 +109,11 @@ test('init Events', async () => {
|
||||
const { button } = context.RootBlocks.map;
|
||||
expect(button.Events.events).toEqual({
|
||||
onClick: {
|
||||
actions: [{ id: 'a', type: 'SetState', params: { a: 'a' } }],
|
||||
catchActions: [],
|
||||
debounce: null,
|
||||
history: [],
|
||||
loading: false,
|
||||
catchActions: [],
|
||||
actions: [{ id: 'a', type: 'SetState', params: { a: 'a' } }],
|
||||
},
|
||||
});
|
||||
});
|
||||
@ -144,6 +149,7 @@ test('triggerEvent no event defined', async () => {
|
||||
const res = await promise;
|
||||
expect(res).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
endTimestamp: { date: 0 },
|
||||
event: undefined,
|
||||
eventName: 'onClick',
|
||||
@ -185,15 +191,17 @@ test('triggerEvent x1', async () => {
|
||||
const promise = button.triggerEvent({ name: 'onClick', event: { x: 1 } });
|
||||
expect(button.Events.events).toEqual({
|
||||
onClick: {
|
||||
actions: [{ id: 'a', type: 'SetState', params: { a: 'a' } }],
|
||||
catchActions: [],
|
||||
debounce: null,
|
||||
history: [],
|
||||
loading: true,
|
||||
catchActions: [],
|
||||
actions: [{ id: 'a', type: 'SetState', params: { a: 'a' } }],
|
||||
},
|
||||
});
|
||||
await promise;
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: {
|
||||
x: 1,
|
||||
},
|
||||
@ -290,6 +298,7 @@ test('triggerEvent error', async () => {
|
||||
await button.triggerEvent({ name: 'onClick', event: { x: 1 } });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: {
|
||||
x: 1,
|
||||
},
|
||||
@ -353,15 +362,17 @@ test('registerEvent then triggerEvent x1', async () => {
|
||||
});
|
||||
expect(button.Events.events).toEqual({
|
||||
onClick: {
|
||||
actions: [{ id: 'a', type: 'SetState', params: { a: 'a' } }],
|
||||
catchActions: [],
|
||||
debounce: null,
|
||||
history: [],
|
||||
loading: false,
|
||||
catchActions: [],
|
||||
actions: [{ id: 'a', type: 'SetState', params: { a: 'a' } }],
|
||||
},
|
||||
});
|
||||
await button.triggerEvent({ name: 'onClick', event: { x: 1 } });
|
||||
expect(button.Events.events.onClick.history[0]).toEqual({
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
event: {
|
||||
x: 1,
|
||||
},
|
||||
@ -424,9 +435,11 @@ test('triggerEvent skip', async () => {
|
||||
},
|
||||
],
|
||||
"catchActions": Array [],
|
||||
"debounce": null,
|
||||
"history": Array [
|
||||
Object {
|
||||
"blockId": "button",
|
||||
"bounced": false,
|
||||
"endTimestamp": Object {
|
||||
"date": 0,
|
||||
},
|
||||
@ -455,6 +468,7 @@ test('triggerEvent skip', async () => {
|
||||
Array [
|
||||
Object {
|
||||
"blockId": "button",
|
||||
"bounced": false,
|
||||
"endTimestamp": Object {
|
||||
"date": 0,
|
||||
},
|
||||
@ -523,9 +537,11 @@ test('triggerEvent skip tests === true', async () => {
|
||||
},
|
||||
],
|
||||
"catchActions": Array [],
|
||||
"debounce": null,
|
||||
"history": Array [
|
||||
Object {
|
||||
"blockId": "button",
|
||||
"bounced": false,
|
||||
"endTimestamp": Object {
|
||||
"date": 0,
|
||||
},
|
||||
@ -554,6 +570,7 @@ test('triggerEvent skip tests === true', async () => {
|
||||
Array [
|
||||
Object {
|
||||
"blockId": "button",
|
||||
"bounced": false,
|
||||
"endTimestamp": Object {
|
||||
"date": 0,
|
||||
},
|
||||
@ -611,8 +628,8 @@ test('Actions array defaults', async () => {
|
||||
actions: null,
|
||||
});
|
||||
expect(button.Events.events).toEqual({
|
||||
onClick: { actions: [], history: [], loading: false, catchActions: [] },
|
||||
registered: { actions: [], history: [], loading: false, catchActions: [] },
|
||||
onClick: { actions: [], history: [], loading: false, catchActions: [], debounce: null },
|
||||
registered: { actions: [], history: [], loading: false, catchActions: [], debounce: null },
|
||||
});
|
||||
});
|
||||
|
||||
@ -649,7 +666,7 @@ test('Actions try catch array defaults', async () => {
|
||||
});
|
||||
const { button } = context.RootBlocks.map;
|
||||
expect(button.Events.events).toEqual({
|
||||
onClick: { actions: [], history: [], loading: false, catchActions: [] },
|
||||
onClick: { actions: [], history: [], loading: false, catchActions: [], debounce: undefined },
|
||||
});
|
||||
});
|
||||
|
||||
@ -694,3 +711,344 @@ test('Actions try catch arrays', async () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('Actions try catch arrays and debounce.immediate == true (leading edge)', async () => {
|
||||
const rootBlock = {
|
||||
blockId: 'root',
|
||||
meta: {
|
||||
category: 'context',
|
||||
},
|
||||
areas: {
|
||||
content: {
|
||||
blocks: [
|
||||
{
|
||||
blockId: 'button',
|
||||
type: 'Button',
|
||||
meta: {
|
||||
category: 'display',
|
||||
valueType: 'string',
|
||||
},
|
||||
events: {
|
||||
onClick: {
|
||||
debounce: {
|
||||
ms: 100,
|
||||
immediate: true,
|
||||
},
|
||||
try: [{ id: 'a', type: 'SetState', params: { a: 'a' } }],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const context = await testContext({
|
||||
lowdefy,
|
||||
rootBlock,
|
||||
});
|
||||
const { button } = context.RootBlocks.map;
|
||||
expect(button.Events.events).toEqual({
|
||||
onClick: {
|
||||
actions: [{ id: 'a', type: 'SetState', params: { a: 'a' } }],
|
||||
history: [],
|
||||
loading: false,
|
||||
catchActions: [],
|
||||
debounce: {
|
||||
immediate: true,
|
||||
ms: 100,
|
||||
},
|
||||
},
|
||||
});
|
||||
const firstClick = button.triggerEvent({ name: 'onClick', event: { x: 1 } });
|
||||
await timeout(10);
|
||||
expect(context.eventLog.length).toEqual(1);
|
||||
const secondClick = button.triggerEvent({ name: 'onClick', event: { x: 1 } });
|
||||
expect(context.eventLog.length).toEqual(2);
|
||||
const secondClickResponse = await secondClick;
|
||||
expect(secondClickResponse.bounced).toEqual(true);
|
||||
const firstClickResponse = await firstClick;
|
||||
expect(firstClickResponse.bounced).toEqual(false);
|
||||
await timeout(100);
|
||||
const thirdClick = await button.triggerEvent({ name: 'onClick', event: { x: 1 } });
|
||||
expect(thirdClick.bounced).toEqual(false);
|
||||
expect(context.eventLog).toEqual([
|
||||
{
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
endTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
event: {
|
||||
x: 1,
|
||||
},
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
a: {
|
||||
index: 0,
|
||||
response: undefined,
|
||||
type: 'SetState',
|
||||
},
|
||||
},
|
||||
startTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
blockId: 'button',
|
||||
bounced: true,
|
||||
endTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
event: {
|
||||
x: 1,
|
||||
},
|
||||
eventName: 'onClick',
|
||||
responses: {},
|
||||
startTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
endTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
event: {
|
||||
x: 1,
|
||||
},
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
a: {
|
||||
index: 0,
|
||||
response: undefined,
|
||||
type: 'SetState',
|
||||
},
|
||||
},
|
||||
startTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Actions try catch arrays and debounce.immediate == undefined (trailing edge)', async () => {
|
||||
const rootBlock = {
|
||||
blockId: 'root',
|
||||
meta: {
|
||||
category: 'context',
|
||||
},
|
||||
areas: {
|
||||
content: {
|
||||
blocks: [
|
||||
{
|
||||
blockId: 'button',
|
||||
type: 'Button',
|
||||
meta: {
|
||||
category: 'display',
|
||||
valueType: 'string',
|
||||
},
|
||||
events: {
|
||||
onClick: {
|
||||
debounce: {
|
||||
ms: 100,
|
||||
},
|
||||
try: [{ id: 'a', type: 'SetState', params: { a: 'a' } }],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const context = await testContext({
|
||||
lowdefy,
|
||||
rootBlock,
|
||||
});
|
||||
const { button } = context.RootBlocks.map;
|
||||
expect(button.Events.events).toEqual({
|
||||
onClick: {
|
||||
actions: [{ id: 'a', type: 'SetState', params: { a: 'a' } }],
|
||||
history: [],
|
||||
loading: false,
|
||||
catchActions: [],
|
||||
debounce: {
|
||||
ms: 100,
|
||||
},
|
||||
},
|
||||
});
|
||||
const firstClick = button.triggerEvent({ name: 'onClick', event: { x: 1 } });
|
||||
await timeout(10);
|
||||
const secondClick = button.triggerEvent({ name: 'onClick', event: { x: 1 } });
|
||||
const firstClickResponse = await firstClick;
|
||||
expect(firstClickResponse.bounced).toEqual(true);
|
||||
const secondClickResponse = await secondClick;
|
||||
expect(secondClickResponse.bounced).toEqual(false);
|
||||
const thirdClick = await button.triggerEvent({ name: 'onClick', event: { x: 1 } });
|
||||
expect(thirdClick.bounced).toEqual(false);
|
||||
expect(context.eventLog).toEqual([
|
||||
{
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
endTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
event: {
|
||||
x: 1,
|
||||
},
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
a: {
|
||||
index: 0,
|
||||
response: undefined,
|
||||
type: 'SetState',
|
||||
},
|
||||
},
|
||||
startTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
endTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
event: {
|
||||
x: 1,
|
||||
},
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
a: {
|
||||
index: 0,
|
||||
response: undefined,
|
||||
type: 'SetState',
|
||||
},
|
||||
},
|
||||
startTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
blockId: 'button',
|
||||
bounced: true,
|
||||
endTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
event: {
|
||||
x: 1,
|
||||
},
|
||||
eventName: 'onClick',
|
||||
responses: {},
|
||||
startTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Actions try catch arrays and debounce.immediate == false default ms (trailing edge)', async () => {
|
||||
const rootBlock = {
|
||||
blockId: 'root',
|
||||
meta: {
|
||||
category: 'context',
|
||||
},
|
||||
areas: {
|
||||
content: {
|
||||
blocks: [
|
||||
{
|
||||
blockId: 'button',
|
||||
type: 'Button',
|
||||
meta: {
|
||||
category: 'display',
|
||||
valueType: 'string',
|
||||
},
|
||||
events: {
|
||||
onClick: {
|
||||
debounce: {
|
||||
immediate: false,
|
||||
},
|
||||
try: [{ id: 'a', type: 'SetState', params: { a: 'a' } }],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const context = await testContext({
|
||||
lowdefy,
|
||||
rootBlock,
|
||||
});
|
||||
const { button } = context.RootBlocks.map;
|
||||
expect(button.Events.events).toEqual({
|
||||
onClick: {
|
||||
actions: [{ id: 'a', type: 'SetState', params: { a: 'a' } }],
|
||||
history: [],
|
||||
loading: false,
|
||||
catchActions: [],
|
||||
debounce: {
|
||||
immediate: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
const firstClick = button.triggerEvent({ name: 'onClick', event: { x: 1 } });
|
||||
await timeout(10);
|
||||
expect(context.eventLog.length).toEqual(0);
|
||||
const secondClick = button.triggerEvent({ name: 'onClick', event: { x: 1 } });
|
||||
expect(context.eventLog.length).toEqual(1);
|
||||
await timeout(250);
|
||||
expect(context.eventLog.length).toEqual(1);
|
||||
await timeout(60);
|
||||
expect(context.eventLog.length).toEqual(2);
|
||||
const firstClickResponse = await firstClick;
|
||||
expect(firstClickResponse.bounced).toEqual(true);
|
||||
const secondClickResponse = await secondClick;
|
||||
expect(secondClickResponse.bounced).toEqual(false);
|
||||
expect(context.eventLog).toEqual([
|
||||
{
|
||||
blockId: 'button',
|
||||
bounced: false,
|
||||
endTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
event: {
|
||||
x: 1,
|
||||
},
|
||||
eventName: 'onClick',
|
||||
responses: {
|
||||
a: {
|
||||
index: 0,
|
||||
response: undefined,
|
||||
type: 'SetState',
|
||||
},
|
||||
},
|
||||
startTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
blockId: 'button',
|
||||
bounced: true,
|
||||
endTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
event: {
|
||||
x: 1,
|
||||
},
|
||||
eventName: 'onClick',
|
||||
responses: {},
|
||||
startTimestamp: {
|
||||
date: 0,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user