|
| 1 | +const assert = require("node:assert/strict"); |
| 2 | +const {test, describe} = require("node:test"); |
| 3 | +const group = typeof describe === "function" ? describe : (_n, fn) => fn(); |
| 4 | + |
| 5 | +// Add tests/shims to module resolution so 'logger' and 'node_helper' resolve to our shims |
| 6 | +const path = require("node:path"); |
| 7 | +const ModuleLib = require("module"); |
| 8 | +const shimDir = path.resolve(__dirname, "../shims"); |
| 9 | +process.env.NODE_PATH = shimDir + (process.env.NODE_PATH ? path.delimiter + process.env.NODE_PATH : ""); |
| 10 | +if (typeof ModuleLib._initPaths === "function") ModuleLib._initPaths(); |
| 11 | + |
| 12 | +// Import node_helper.js (returns the helper instance from our shimmed NodeHelper.create) |
| 13 | +const helperFactory = require("../../node_helper.js"); |
| 14 | + |
| 15 | +function freshHelper () { |
| 16 | + const h = Object.assign({}, helperFactory); |
| 17 | + h.__sent = []; |
| 18 | + h.__responses = []; |
| 19 | + h.sendSocketNotification = (what, payload) => { h.__sent.push({what, payload}); }; |
| 20 | + h.sendResponse = (_res, err, data) => { h.__responses.push({err, data}); }; |
| 21 | + return h; |
| 22 | +} |
| 23 | + |
| 24 | +group("executeQuery core actions", () => { |
| 25 | + test("HIDE forwards query unchanged to socket", () => { |
| 26 | + const h = freshHelper(); |
| 27 | + const q = {action: "HIDE", module: "clock"}; |
| 28 | + const res = {}; |
| 29 | + const result = h.executeQuery(q, res); |
| 30 | + assert.equal(result, true); |
| 31 | + assert.deepEqual(h.__sent[0], {what: "HIDE", payload: q}); |
| 32 | + assert.equal(h.__responses.length, 1); |
| 33 | + }); |
| 34 | + |
| 35 | + test("SHOW and TOGGLE also forward query", () => { |
| 36 | + const h = freshHelper(); |
| 37 | + const res = {}; |
| 38 | + h.executeQuery({action: "SHOW", module: "all"}, res); |
| 39 | + h.executeQuery({action: "TOGGLE", module: "module_1_clock"}, res); |
| 40 | + assert.deepEqual(h.__sent[0], {what: "SHOW", payload: {action: "SHOW", module: "all"}}); |
| 41 | + assert.deepEqual(h.__sent[1], {what: "TOGGLE", payload: {action: "TOGGLE", module: "module_1_clock"}}); |
| 42 | + assert.equal(h.__responses.length, 2); |
| 43 | + }); |
| 44 | + |
| 45 | + test("BRIGHTNESS forwards numeric value", () => { |
| 46 | + const h = freshHelper(); |
| 47 | + const res = {}; |
| 48 | + const q = {action: "BRIGHTNESS", value: 90}; |
| 49 | + const ok = h.executeQuery(q, res); |
| 50 | + assert.equal(ok, true); |
| 51 | + assert.deepEqual(h.__sent[0], {what: "BRIGHTNESS", payload: 90}); |
| 52 | + assert.equal(h.__responses.length, 1); |
| 53 | + }); |
| 54 | + |
| 55 | + test("TEMP forwards value and responds", () => { |
| 56 | + const h = freshHelper(); |
| 57 | + const res = {}; |
| 58 | + const q = {action: "TEMP", value: 22}; |
| 59 | + const ok = h.executeQuery(q, res); |
| 60 | + assert.equal(ok, true); |
| 61 | + assert.deepEqual(h.__sent[0], {what: "TEMP", payload: 22}); |
| 62 | + assert.equal(h.__responses.length, 1); |
| 63 | + }); |
| 64 | + |
| 65 | + test("SHOW_ALERT builds default payload when fields missing", () => { |
| 66 | + const h = freshHelper(); |
| 67 | + const res = {}; |
| 68 | + const ok = h.executeQuery({action: "SHOW_ALERT"}, res); |
| 69 | + assert.equal(ok, true); |
| 70 | + assert.equal(h.__sent[0].what, "SHOW_ALERT"); |
| 71 | + assert.deepEqual(h.__sent[0].payload, {type: "alert", title: "Note", message: "Attention!", timer: 4000}); |
| 72 | + assert.equal(h.__responses.length, 1); |
| 73 | + }); |
| 74 | + |
| 75 | + test("SHOW_ALERT respects provided fields and multiplies timer by 1000", () => { |
| 76 | + const h = freshHelper(); |
| 77 | + const res = {}; |
| 78 | + h.executeQuery({action: "SHOW_ALERT", type: "warning", title: "T", message: "M", timer: 2}, res); |
| 79 | + assert.deepEqual(h.__sent[0], {what: "SHOW_ALERT", payload: {type: "warning", title: "T", message: "M", timer: 2000}}); |
| 80 | + }); |
| 81 | + |
| 82 | + test("HIDE_ALERT and REFRESH emit simple notifications", () => { |
| 83 | + const h = freshHelper(); |
| 84 | + const res = {}; |
| 85 | + h.executeQuery({action: "HIDE_ALERT"}, res); |
| 86 | + h.executeQuery({action: "REFRESH"}, res); |
| 87 | + assert.deepEqual(h.__sent[0], {what: "HIDE_ALERT", payload: undefined}); |
| 88 | + assert.deepEqual(h.__sent[1], {what: "REFRESH", payload: undefined}); |
| 89 | + assert.equal(h.__responses.length, 2); |
| 90 | + }); |
| 91 | + |
| 92 | + test("NOTIFICATION without payload sends undefined payload", () => { |
| 93 | + const h = freshHelper(); |
| 94 | + const res = {}; |
| 95 | + const ok = h.executeQuery({action: "NOTIFICATION", notification: "HELLO"}, res); |
| 96 | + assert.equal(ok, true); |
| 97 | + assert.deepEqual(h.__sent[0], {what: "NOTIFICATION", payload: {notification: "HELLO", payload: undefined}}); |
| 98 | + assert.equal(h.__responses.length, 1); |
| 99 | + }); |
| 100 | + |
| 101 | + test("NOTIFICATION parses JSON string payload", () => { |
| 102 | + const h = freshHelper(); |
| 103 | + const res = {}; |
| 104 | + const ok = h.executeQuery({action: "NOTIFICATION", notification: "HELLO", payload: "{\"a\":1}"}, res); |
| 105 | + assert.equal(ok, true); |
| 106 | + assert.deepEqual(h.__sent[0], {what: "NOTIFICATION", payload: {notification: "HELLO", payload: {a: 1}}}); |
| 107 | + }); |
| 108 | + |
| 109 | + test("NOTIFICATION passes raw string when payload is not JSON", () => { |
| 110 | + const h = freshHelper(); |
| 111 | + const res = {}; |
| 112 | + h.executeQuery({action: "NOTIFICATION", notification: "HELLO", payload: "plain"}, res); |
| 113 | + assert.deepEqual(h.__sent[0], {what: "NOTIFICATION", payload: {notification: "HELLO", payload: "plain"}}); |
| 114 | + }); |
| 115 | + |
| 116 | + test("NOTIFICATION invalid JSON returns error via sendResponse", () => { |
| 117 | + const h = freshHelper(); |
| 118 | + const res = {}; |
| 119 | + const ok = h.executeQuery({action: "NOTIFICATION", notification: "HELLO", payload: "{"}, res); |
| 120 | + assert.equal(ok, true); |
| 121 | + assert.equal(h.__sent.length, 0, "should not send socket notification on parse error"); |
| 122 | + assert.equal(h.__responses.length, 1); |
| 123 | + assert.ok(h.__responses[0].err instanceof Error); |
| 124 | + }); |
| 125 | + |
| 126 | + test("USER_PRESENCE forwards boolean and updates state", () => { |
| 127 | + const h = freshHelper(); |
| 128 | + const res = {}; |
| 129 | + const ok = h.executeQuery({action: "USER_PRESENCE", value: true}, res); |
| 130 | + assert.equal(ok, true); |
| 131 | + assert.deepEqual(h.__sent[0], {what: "USER_PRESENCE", payload: true}); |
| 132 | + assert.equal(h.userPresence, true); |
| 133 | + assert.equal(h.__responses.length, 1); |
| 134 | + }); |
| 135 | + |
| 136 | + test("MANAGE_CLASSES: string key maps to lower-case actions and sends modules", () => { |
| 137 | + const h = freshHelper(); |
| 138 | + h.thisConfig = {classes: {Group1: {hide: "calendar", show: ["newsfeed"], toggle: ["clock", "weather"]}}}; |
| 139 | + const res = {}; |
| 140 | + h.executeQuery({action: "MANAGE_CLASSES", payload: {classes: "Group1"}}, res); |
| 141 | + // Order depends on object key iteration; check membership instead of order. |
| 142 | + const sent = h.__sent.reduce((acc, s) => acc.add(`${s.what}:${JSON.stringify(s.payload)}`), new Set()); |
| 143 | + assert.ok(sent.has("HIDE:{\"module\":\"calendar\"}")); |
| 144 | + assert.ok(sent.has("SHOW:{\"module\":\"newsfeed\"}")); |
| 145 | + assert.ok(sent.has("TOGGLE:{\"module\":\"clock\"}")); |
| 146 | + assert.ok(sent.has("TOGGLE:{\"module\":\"weather\"}")); |
| 147 | + assert.equal(h.__responses.length, 1); |
| 148 | + }); |
| 149 | + |
| 150 | + test("MANAGE_CLASSES: array of class names processes each", () => { |
| 151 | + const h = freshHelper(); |
| 152 | + h.thisConfig = {classes: { |
| 153 | + A: {show: ["one"]}, |
| 154 | + B: {hide: ["two"], toggle: "three"} |
| 155 | + }}; |
| 156 | + const res = {}; |
| 157 | + h.executeQuery({action: "MANAGE_CLASSES", payload: {classes: ["A", "B"]}}, res); |
| 158 | + const sent = h.__sent.reduce((acc, s) => acc.add(`${s.what}:${JSON.stringify(s.payload)}`), new Set()); |
| 159 | + assert.ok(sent.has("SHOW:{\"module\":\"one\"}")); |
| 160 | + assert.ok(sent.has("HIDE:{\"module\":\"two\"}")); |
| 161 | + assert.ok(sent.has("TOGGLE:{\"module\":\"three\"}")); |
| 162 | + assert.equal(h.__responses.length, 1); |
| 163 | + }); |
| 164 | +}); |
0 commit comments