feat(engine): Add async option to actions

This commit is contained in:
JohannMoller 2021-08-13 15:22:26 +02:00
parent d5d19b1872
commit 81036db446
2 changed files with 218 additions and 12 deletions

View File

@ -27,18 +27,46 @@ class Actions {
this.actions = actions;
}
async callAsyncAction({ action, arrayIndices, block, event, index, responses }) {
try {
const response = await this.callAction({
action,
arrayIndices,
block,
event,
index,
responses,
});
responses[action.id] = response;
} catch (error) {
responses[action.id] = error;
console.error(error);
}
}
async callActionLoop({ actions, arrayIndices, block, event, responses }) {
for (const [index, action] of actions.entries()) {
try {
const response = await this.callAction({
action,
arrayIndices,
block,
event,
index,
responses,
});
responses[action.id] = response;
if (action.async === true) {
this.callAsyncAction({
action,
arrayIndices,
block,
event,
index,
responses,
});
} else {
const response = await this.callAction({
action,
arrayIndices,
block,
event,
index,
responses,
});
responses[action.id] = response;
}
} catch (error) {
responses[action.id] = error;
throw {

View File

@ -21,15 +21,26 @@ import actions from '../../src/actions/index.js';
jest.mock('../../src/actions/index.js', () => ({
ActionSync: jest.fn(({ params }) => params),
ActionAsync: jest.fn(({ params }) => Promise.resolve(params)),
ActionAsync: jest.fn(async ({ params }) => {
await timeout(params.ms || 1);
return params;
}),
ActionError: jest.fn(() => {
throw new Error('Test error');
}),
CatchActionError: jest.fn(() => {
throw new Error('Test catch error');
}),
ActionAsyncError: jest.fn(async ({ params }) => {
await timeout(params.ms || 1);
throw new Error('Test error');
}),
}));
const timeout = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
const pageId = 'one';
const RealDate = Date;
@ -46,8 +57,8 @@ const arrayIndices = [];
const eventName = 'eventName';
// Comment out to use console.log
console.log = () => {};
console.error = () => {};
// console.log = () => {};
// console.error = () => {};
beforeEach(() => {
global.Date = mockDate;
@ -181,6 +192,51 @@ test('call 2 actions', async () => {
});
});
test('call async then sync action', async () => {
const rootBlock = {
blockId: 'root',
meta: {
category: 'context',
},
};
const context = await testContext({
lowdefy,
rootBlock,
});
const Actions = context.Actions;
const res = await Actions.callActions({
actions: [
{ id: 'test1', type: 'ActionAsync', params: 'params1' },
{ id: 'test2', type: 'ActionSync', params: 'params2' },
],
arrayIndices,
block: { blockId: 'blockId' },
catchActions: [],
event: {},
eventName,
});
expect(res).toEqual({
blockId: 'blockId',
event: {},
eventName: 'eventName',
responses: {
test1: {
type: 'ActionSync',
index: 1,
response: 'params2',
},
test2: {
type: 'ActionAsync',
index: 0,
response: 'params1',
},
},
success: true,
startTimestamp: { date: 0 },
endTimestamp: { date: 0 },
});
});
test('operators are evaluated in params, skip and messages', async () => {
const rootBlock = {
blockId: 'root',
@ -1097,3 +1153,125 @@ test('Call catchActions when actions throws error and catchActions throws error'
});
expect(actions.ActionAsync.mock.calls.length).toBe(1);
});
test('call 2 actions, first with async: true', async () => {
const rootBlock = {
blockId: 'root',
meta: {
category: 'context',
},
};
const context = await testContext({
lowdefy,
rootBlock,
});
const Actions = context.Actions;
const res = await Actions.callActions({
actions: [
{ id: 'test1', type: 'ActionAsync', async: true, params: { ms: 100 } },
{ id: 'test2', type: 'ActionSync', params: 'params2' },
],
arrayIndices,
block: { blockId: 'blockId' },
catchActions: [],
event: {},
eventName,
});
expect(res).toEqual({
blockId: 'blockId',
event: {},
eventName: 'eventName',
responses: {
test2: {
type: 'ActionSync',
index: 1,
response: 'params2',
},
},
success: true,
startTimestamp: { date: 0 },
endTimestamp: { date: 0 },
});
await timeout(110);
expect(res).toEqual({
blockId: 'blockId',
event: {},
eventName: 'eventName',
responses: {
test1: {
type: 'ActionAsync',
index: 0,
response: { ms: 100 },
},
test2: {
type: 'ActionSync',
index: 1,
response: 'params2',
},
},
success: true,
startTimestamp: { date: 0 },
endTimestamp: { date: 0 },
});
});
test('call async: true with error', async () => {
const rootBlock = {
blockId: 'root',
meta: {
category: 'context',
},
};
const context = await testContext({
lowdefy,
rootBlock,
});
const Actions = context.Actions;
const res = await Actions.callActions({
actions: [
{ id: 'test1', type: 'ActionAsyncError', async: true, params: { ms: 100 } },
{ id: 'test2', type: 'ActionSync', params: 'params2' },
],
arrayIndices,
block: { blockId: 'blockId' },
catchActions: [],
event: {},
eventName,
});
expect(res).toEqual({
blockId: 'blockId',
event: {},
eventName: 'eventName',
responses: {
test2: {
type: 'ActionSync',
response: 'params2',
index: 1,
},
},
endTimestamp: { date: 0 },
startTimestamp: { date: 0 },
success: true,
});
await timeout(110);
expect(res).toEqual({
blockId: 'blockId',
event: {},
eventName: 'eventName',
responses: {
test2: {
type: 'ActionSync',
response: 'params2',
index: 1,
},
test1: {
type: 'ActionAsyncError',
error: new Error('Test error'),
index: 0,
},
},
success: true,
startTimestamp: { date: 0 },
endTimestamp: { date: 0 },
});
});