From 1d70f64983922070916537c0a81b4ed343810365 Mon Sep 17 00:00:00 2001
From: Sandile <sah.memela@gmail.com>
Date: Tue, 15 Feb 2022 14:29:33 +0200
Subject: [PATCH] feat(actions-core): Updated DisplayMessage action and tests
 to include edge cases.

---
 .pnp.cjs                                      |  41 ++-
 .../jest-npm-27.4.7-cdda9da561-28ce948b30.zip | Bin 0 -> 3590 bytes
 .../src/actions/DisplayMessage.js             |  19 +-
 .../src/actions/DisplayMessage.test.js        | 333 +++++++++++++++++-
 yarn.lock                                     |  24 +-
 5 files changed, 396 insertions(+), 21 deletions(-)
 create mode 100644 .yarn/cache/jest-npm-27.4.7-cdda9da561-28ce948b30.zip

diff --git a/.pnp.cjs b/.pnp.cjs
index ef9ea868d..ab3486ee8 100755
--- a/.pnp.cjs
+++ b/.pnp.cjs
@@ -2810,7 +2810,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
             ["@swc/cli", "virtual:babee6e81435a5d101529cd67f2c6b175f4db37a4ab0b58df15adf73dd11be8917ac14caf44ab4e6882a92c61661055072365b349016e85173e049f006fc2305#npm:0.1.55"],
             ["@swc/core", "npm:1.2.135"],
             ["@swc/jest", "virtual:babee6e81435a5d101529cd67f2c6b175f4db37a4ab0b58df15adf73dd11be8917ac14caf44ab4e6882a92c61661055072365b349016e85173e049f006fc2305#npm:0.2.17"],
-            ["jest", "virtual:babee6e81435a5d101529cd67f2c6b175f4db37a4ab0b58df15adf73dd11be8917ac14caf44ab4e6882a92c61661055072365b349016e85173e049f006fc2305#npm:27.4.7"]
+            ["jest", "virtual:42e288f3c714e1d86d12bf40fb17a72864641a713e0a167e85a3dab47534d11bcd2e71357e431b2f4737000bc9432f95c60dff0c9bfb212cd46e1f0f5bf0ad9f#npm:27.4.7"]
           ],
           "linkType": "SOFT",
         }]
@@ -3070,8 +3070,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
           "packageLocation": "./packages/build/",
           "packageDependencies": [
             ["@lowdefy/build", "workspace:packages/build"],
-            ["@lowdefy/actions-core", "workspace:packages/plugins/actions/actions-core"],
             ["@jest/globals", "npm:27.5.1"],
+            ["@lowdefy/actions-core", "workspace:packages/plugins/actions/actions-core"],
             ["@lowdefy/ajv", "workspace:packages/utils/ajv"],
             ["@lowdefy/blocks-antd", "workspace:packages/plugins/blocks/blocks-antd"],
             ["@lowdefy/blocks-basic", "workspace:packages/plugins/blocks/blocks-basic"],
@@ -10506,6 +10506,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
         }]
       ]],
       ["jest", [
+        ["npm:27.4.7", {
+          "packageLocation": "./.yarn/cache/jest-npm-27.4.7-cdda9da561-28ce948b30.zip/node_modules/jest/",
+          "packageDependencies": [
+            ["jest", "npm:27.4.7"]
+          ],
+          "linkType": "SOFT",
+        }],
         ["npm:27.5.1", {
           "packageLocation": "./.yarn/cache/jest-npm-27.5.1-bacad4fe2a-96f1d69042.zip/node_modules/jest/",
           "packageDependencies": [
@@ -10513,6 +10520,22 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
           ],
           "linkType": "SOFT",
         }],
+        ["virtual:42e288f3c714e1d86d12bf40fb17a72864641a713e0a167e85a3dab47534d11bcd2e71357e431b2f4737000bc9432f95c60dff0c9bfb212cd46e1f0f5bf0ad9f#npm:27.4.7", {
+          "packageLocation": "./.yarn/__virtual__/jest-virtual-4325f46d83/0/cache/jest-npm-27.4.7-cdda9da561-28ce948b30.zip/node_modules/jest/",
+          "packageDependencies": [
+            ["jest", "virtual:42e288f3c714e1d86d12bf40fb17a72864641a713e0a167e85a3dab47534d11bcd2e71357e431b2f4737000bc9432f95c60dff0c9bfb212cd46e1f0f5bf0ad9f#npm:27.4.7"],
+            ["@jest/core", "virtual:af105d7a2bc799821f67e8114057f53830abc6c7c9faa96846247605351f361b4158d347970d00ebc3c04dbab6e46e1c1e24df193c1caf1f7d80a00c65068a2a#npm:27.5.1"],
+            ["@types/node-notifier", null],
+            ["import-local", "npm:3.1.0"],
+            ["jest-cli", "virtual:af105d7a2bc799821f67e8114057f53830abc6c7c9faa96846247605351f361b4158d347970d00ebc3c04dbab6e46e1c1e24df193c1caf1f7d80a00c65068a2a#npm:27.5.1"],
+            ["node-notifier", null]
+          ],
+          "packagePeers": [
+            "@types/node-notifier",
+            "node-notifier"
+          ],
+          "linkType": "HARD",
+        }],
         ["virtual:babee6e81435a5d101529cd67f2c6b175f4db37a4ab0b58df15adf73dd11be8917ac14caf44ab4e6882a92c61661055072365b349016e85173e049f006fc2305#npm:27.5.1", {
           "packageLocation": "./.yarn/__virtual__/jest-virtual-af105d7a2b/0/cache/jest-npm-27.5.1-bacad4fe2a-96f1d69042.zip/node_modules/jest/",
           "packageDependencies": [
@@ -23634,7 +23657,7 @@ module.exports = require("path");;
 /************************************************************************/
 /******/ 	// The module cache
 /******/ 	var __webpack_module_cache__ = {};
-/******/
+/******/ 	
 /******/ 	// The require function
 /******/ 	function __webpack_require__(moduleId) {
 /******/ 		// Check if module is in cache
@@ -23648,14 +23671,14 @@ module.exports = require("path");;
 /******/ 			// no module.loaded needed
 /******/ 			exports: {}
 /******/ 		};
-/******/
+/******/ 	
 /******/ 		// Execute the module function
 /******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
-/******/
+/******/ 	
 /******/ 		// Return the exports of the module
 /******/ 		return module.exports;
 /******/ 	}
-/******/
+/******/ 	
 /************************************************************************/
 /******/ 	/* webpack/runtime/compat get default export */
 /******/ 	(() => {
@@ -23668,7 +23691,7 @@ module.exports = require("path");;
 /******/ 			return getter;
 /******/ 		};
 /******/ 	})();
-/******/
+/******/ 	
 /******/ 	/* webpack/runtime/define property getters */
 /******/ 	(() => {
 /******/ 		// define getter functions for harmony exports
@@ -23680,12 +23703,12 @@ module.exports = require("path");;
 /******/ 			}
 /******/ 		};
 /******/ 	})();
-/******/
+/******/ 	
 /******/ 	/* webpack/runtime/hasOwnProperty shorthand */
 /******/ 	(() => {
 /******/ 		__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
 /******/ 	})();
-/******/
+/******/ 	
 /************************************************************************/
 var __webpack_exports__ = {};
 // This entry need to be wrapped in an IIFE because it need to be in strict mode.
diff --git a/.yarn/cache/jest-npm-27.4.7-cdda9da561-28ce948b30.zip b/.yarn/cache/jest-npm-27.4.7-cdda9da561-28ce948b30.zip
new file mode 100644
index 0000000000000000000000000000000000000000..feec693d575f9fc5fb0fc56a4cf82699783260bc
GIT binary patch
literal 3590
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

literal 0
HcmV?d00001

diff --git a/packages/plugins/actions/actions-core/src/actions/DisplayMessage.js b/packages/plugins/actions/actions-core/src/actions/DisplayMessage.js
index fa5c22271..9e7379a51 100644
--- a/packages/plugins/actions/actions-core/src/actions/DisplayMessage.js
+++ b/packages/plugins/actions/actions-core/src/actions/DisplayMessage.js
@@ -17,11 +17,22 @@
 import { type } from '@lowdefy/helpers';
 
 function DisplayMessage({ methods: { displayMessage }, params }) {
-  let defaultParams = params;
-  if (type.isObject(params)) {
-    defaultParams = { ...defaultParams, content: params.content || 'Success' };
+  if (!type.isObject(params) && !type.isNone(params)) {
+    throw new Error(
+      `Invalid DisplayMessage, check action params. Params must be an object, received "${JSON.stringify(
+        params
+      )}".`
+    );
+  }
+  if (type.isNull(params) || type.isUndefined(params)) {
+    displayMessage({ content: 'Success' });
+  }
+  if (type.isObject(params)) {
+    displayMessage({
+      ...params,
+      content: type.isNone(params.content) ? 'Success' : params.content,
+    });
   }
-  displayMessage(defaultParams);
 }
 
 export default DisplayMessage;
diff --git a/packages/plugins/actions/actions-core/src/actions/DisplayMessage.test.js b/packages/plugins/actions/actions-core/src/actions/DisplayMessage.test.js
index 8db50cfd6..52d2d2e03 100644
--- a/packages/plugins/actions/actions-core/src/actions/DisplayMessage.test.js
+++ b/packages/plugins/actions/actions-core/src/actions/DisplayMessage.test.js
@@ -14,15 +14,338 @@
   limitations under the License.
 */
 
+import testContext from './testContext.js';
 import DisplayMessage from './DisplayMessage.js';
 
-const mockActionMethod = jest.fn();
+const displayMessage = jest.fn();
+const lowdefy = {
+  _internal: {
+    actions: {
+      DisplayMessage,
+    },
+    blockComponents: {
+      Button: { meta: { category: 'display' } },
+    },
+    displayMessage,
+  },
+};
+
+const RealDate = Date;
+const mockDate = jest.fn(() => ({ date: 0 }));
+mockDate.now = jest.fn(() => 0);
+
+// Comment out to use console
+console.log = () => {};
+console.error = () => {};
 
 beforeEach(() => {
-  mockActionMethod.mockReset();
+  displayMessage.mockReset();
 });
 
-test('Message action invocation', async () => {
-  DisplayMessage({ methods: { displayMessage: mockActionMethod }, params: 'call' });
-  expect(mockActionMethod.mock.calls).toEqual([['call']]);
+beforeAll(() => {
+  global.Date = mockDate;
+});
+
+afterAll(() => {
+  global.Date = RealDate;
+});
+
+test('DisplayMessage params is not object', async () => {
+  const rootBlock = {
+    id: 'block:root:root:0',
+    blockId: 'root',
+    meta: {
+      category: 'container',
+    },
+    areas: {
+      content: {
+        blocks: [
+          {
+            id: 'block:root:button:0',
+            blockId: 'button',
+            type: 'Button',
+            meta: {
+              category: 'display',
+            },
+            events: {
+              onClick: [
+                {
+                  id: 'a',
+                  type: 'DisplayMessage',
+                  params: 1,
+                },
+              ],
+            },
+          },
+        ],
+      },
+    },
+  };
+  const context = await testContext({
+    lowdefy,
+    rootBlock,
+  });
+  const button = context._internal.RootBlocks.map['block:root:button:0'];
+  await button.triggerEvent({ name: 'onClick' });
+  expect(button.Events.events.onClick.history[0]).toEqual({
+    blockId: 'button',
+    bounced: false,
+    event: undefined,
+    eventName: 'onClick',
+    error: {
+      action: {
+        id: 'a',
+        type: 'DisplayMessage',
+        params: 1,
+      },
+      error: {
+        error: new Error(
+          'Invalid DisplayMessage, check action params. Params must be an object, received "1".'
+        ),
+        index: 0,
+        type: 'DisplayMessage',
+      },
+    },
+    responses: {
+      a: {
+        error: new Error(
+          'Invalid DisplayMessage, check action params. Params must be an object, received "1".'
+        ),
+        type: 'DisplayMessage',
+        index: 0,
+      },
+    },
+    endTimestamp: { date: 0 },
+    startTimestamp: { date: 0 },
+    success: false,
+  });
+});
+
+test('DisplayMessage params is null or undefined', async () => {
+  const rootBlock = {
+    id: 'block:root:root:0',
+    blockId: 'root',
+    meta: {
+      category: 'container',
+    },
+    areas: {
+      content: {
+        blocks: [
+          {
+            id: 'block:root:button:0',
+            blockId: 'button',
+            type: 'Button',
+            meta: {
+              category: 'display',
+            },
+            events: {
+              onClick: [
+                {
+                  id: 'a',
+                  type: 'DisplayMessage',
+                },
+              ],
+            },
+          },
+        ],
+      },
+    },
+  };
+  const context = await testContext({
+    lowdefy,
+    rootBlock,
+  });
+  const button = context._internal.RootBlocks.map['block:root:button:0'];
+  await button.triggerEvent({ name: 'onClick' });
+  expect(button.Events.events.onClick.history[0]).toEqual({
+    blockId: 'button',
+    bounced: false,
+    event: undefined,
+    eventName: 'onClick',
+    responses: {
+      a: {
+        response: undefined,
+        type: 'DisplayMessage',
+        index: 0,
+      },
+    },
+    endTimestamp: { date: 0 },
+    startTimestamp: { date: 0 },
+    success: true,
+  });
+  expect(displayMessage.mock.calls).toEqual([[{ content: 'Success' }]]);
+});
+
+test('DisplayMessage params.content is none', async () => {
+  const rootBlock = {
+    id: 'block:root:root:0',
+    blockId: 'root',
+    meta: {
+      category: 'container',
+    },
+    areas: {
+      content: {
+        blocks: [
+          {
+            id: 'block:root:button:0',
+            blockId: 'button',
+            type: 'Button',
+            meta: {
+              category: 'display',
+            },
+            events: {
+              onClick: [
+                {
+                  id: 'a',
+                  type: 'DisplayMessage',
+                  params: {
+                    status: 'info',
+                  },
+                },
+              ],
+            },
+          },
+        ],
+      },
+    },
+  };
+  const context = await testContext({
+    lowdefy,
+    rootBlock,
+  });
+  const button = context._internal.RootBlocks.map['block:root:button:0'];
+  await button.triggerEvent({ name: 'onClick' });
+  expect(button.Events.events.onClick.history[0]).toEqual({
+    blockId: 'button',
+    bounced: false,
+    event: undefined,
+    eventName: 'onClick',
+    responses: {
+      a: {
+        response: undefined,
+        type: 'DisplayMessage',
+        index: 0,
+      },
+    },
+    endTimestamp: { date: 0 },
+    startTimestamp: { date: 0 },
+    success: true,
+  });
+  expect(displayMessage.mock.calls).toEqual([[{ content: 'Success', status: 'info' }]]);
+});
+
+test('DisplayMessage params.content is ""', async () => {
+  const rootBlock = {
+    id: 'block:root:root:0',
+    blockId: 'root',
+    meta: {
+      category: 'container',
+    },
+    areas: {
+      content: {
+        blocks: [
+          {
+            id: 'block:root:button:0',
+            blockId: 'button',
+            type: 'Button',
+            meta: {
+              category: 'display',
+            },
+            events: {
+              onClick: [
+                {
+                  id: 'a',
+                  type: 'DisplayMessage',
+                  params: {
+                    content: '',
+                  },
+                },
+              ],
+            },
+          },
+        ],
+      },
+    },
+  };
+  const context = await testContext({
+    lowdefy,
+    rootBlock,
+  });
+  const button = context._internal.RootBlocks.map['block:root:button:0'];
+  await button.triggerEvent({ name: 'onClick' });
+  expect(button.Events.events.onClick.history[0]).toEqual({
+    blockId: 'button',
+    bounced: false,
+    event: undefined,
+    eventName: 'onClick',
+    responses: {
+      a: {
+        response: undefined,
+        type: 'DisplayMessage',
+        index: 0,
+      },
+    },
+    endTimestamp: { date: 0 },
+    startTimestamp: { date: 0 },
+    success: true,
+  });
+  expect(displayMessage.mock.calls).toEqual([[{ content: '' }]]);
+});
+
+test('DisplayMessage params.content is falsy', async () => {
+  const rootBlock = {
+    id: 'block:root:root:0',
+    blockId: 'root',
+    meta: {
+      category: 'container',
+    },
+    areas: {
+      content: {
+        blocks: [
+          {
+            id: 'block:root:button:0',
+            blockId: 'button',
+            type: 'Button',
+            meta: {
+              category: 'display',
+            },
+            events: {
+              onClick: [
+                {
+                  id: 'a',
+                  type: 'DisplayMessage',
+                  params: {
+                    content: 0,
+                  },
+                },
+              ],
+            },
+          },
+        ],
+      },
+    },
+  };
+  const context = await testContext({
+    lowdefy,
+    rootBlock,
+  });
+  const button = context._internal.RootBlocks.map['block:root:button:0'];
+  await button.triggerEvent({ name: 'onClick' });
+  expect(button.Events.events.onClick.history[0]).toEqual({
+    blockId: 'button',
+    bounced: false,
+    event: undefined,
+    eventName: 'onClick',
+    responses: {
+      a: {
+        response: undefined,
+        type: 'DisplayMessage',
+        index: 0,
+      },
+    },
+    endTimestamp: { date: 0 },
+    startTimestamp: { date: 0 },
+    success: true,
+  });
+  expect(displayMessage.mock.calls).toEqual([[{ content: 0 }]]);
 });
diff --git a/yarn.lock b/yarn.lock
index f83565e5c..39e68f408 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -960,7 +960,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@jest/core@npm:^27.5.1":
+"@jest/core@npm:^27.4.7, @jest/core@npm:^27.5.1":
   version: 27.5.1
   resolution: "@jest/core@npm:27.5.1"
   dependencies:
@@ -2213,8 +2213,8 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@lowdefy/build@workspace:packages/build"
   dependencies:
-    "@lowdefy/actions-core": 4.0.0-alpha.6
     "@jest/globals": 27.5.1
+    "@lowdefy/actions-core": 4.0.0-alpha.6
     "@lowdefy/ajv": 4.0.0-alpha.6
     "@lowdefy/blocks-antd": 4.0.0-alpha.6
     "@lowdefy/blocks-basic": 4.0.0-alpha.6
@@ -8560,7 +8560,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"jest-cli@npm:^27.5.1":
+"jest-cli@npm:^27.4.7, jest-cli@npm:^27.5.1":
   version: 27.5.1
   resolution: "jest-cli@npm:27.5.1"
   dependencies:
@@ -9011,6 +9011,24 @@ __metadata:
   languageName: node
   linkType: hard
 
+"jest@npm:27.4.7":
+  version: 27.4.7
+  resolution: "jest@npm:27.4.7"
+  dependencies:
+    "@jest/core": ^27.4.7
+    import-local: ^3.0.2
+    jest-cli: ^27.4.7
+  peerDependencies:
+    node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+  peerDependenciesMeta:
+    node-notifier:
+      optional: true
+  bin:
+    jest: bin/jest.js
+  checksum: 28ce948b30c074907393f37553acac4422d0f60190776e62b3403e4c742d33dd6012e3a20748254a43e38b5b4ce52d813b13a3a5be1d43d6d12429bd08ce1a23
+  languageName: node
+  linkType: hard
+
 "jest@npm:27.5.1":
   version: 27.5.1
   resolution: "jest@npm:27.5.1"