@bonsae/nrg 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -19
- package/package.json +38 -6
- package/server/index.cjs +38 -24
- package/server/resources/nrg-client.js +3667 -3521
- package/test/client/component/index.js +244 -0
- package/test/client/component/setup.js +201 -0
- package/test/client/e2e/index.js +2698 -0
- package/test/client/unit/index.js +200 -0
- package/test/client/unit/setup.js +199 -0
- package/test/{index.js → server/unit/index.js} +23 -8
- package/tsconfig/core/client.json +11 -0
- package/tsconfig/{server.json → core/server.json} +1 -1
- package/tsconfig/test/client/component.json +11 -0
- package/tsconfig/test/client/e2e.json +6 -0
- package/tsconfig/test/client/unit.json +6 -0
- package/tsconfig/test/server/unit.json +6 -0
- package/types/client.d.ts +66 -0
- package/types/server.d.ts +74 -58
- package/types/shims/form/components/node-red-config-input.vue.d.ts +15 -2
- package/types/shims/form/components/node-red-editor-input.vue.d.ts +18 -3
- package/types/shims/form/components/node-red-input.vue.d.ts +17 -5
- package/types/shims/form/components/node-red-json-schema-form.vue.d.ts +117 -19
- package/types/shims/form/components/node-red-select-input.vue.d.ts +15 -1
- package/types/shims/form/components/node-red-typed-input.vue.d.ts +52 -8
- package/types/test-client-component.d.ts +70 -0
- package/types/test-client-e2e.d.ts +152 -0
- package/types/test-client-unit.d.ts +52 -0
- package/types/{test.d.ts → test-server-unit.d.ts} +12 -2
- package/vite/index.js +15 -7
- package/tsconfig/client.json +0 -11
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// src/test/client/component/index.ts
|
|
2
|
+
import { vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
// src/test/client/mocks.ts
|
|
5
|
+
function createRED() {
|
|
6
|
+
return {
|
|
7
|
+
_: (key) => key,
|
|
8
|
+
editor: {
|
|
9
|
+
createEditor(options) {
|
|
10
|
+
let currentValue = options.value || "";
|
|
11
|
+
const session = {
|
|
12
|
+
on(_event, _cb) {
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
getValue: () => currentValue,
|
|
17
|
+
setValue: (val) => {
|
|
18
|
+
currentValue = val;
|
|
19
|
+
},
|
|
20
|
+
getSession: () => session,
|
|
21
|
+
focus: () => {
|
|
22
|
+
},
|
|
23
|
+
destroy: () => {
|
|
24
|
+
},
|
|
25
|
+
saveView: () => {
|
|
26
|
+
},
|
|
27
|
+
restoreView: () => {
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
prepareConfigNodeSelect: () => {
|
|
32
|
+
},
|
|
33
|
+
validateNode: () => true
|
|
34
|
+
},
|
|
35
|
+
tray: {
|
|
36
|
+
show: () => {
|
|
37
|
+
},
|
|
38
|
+
close: () => {
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
popover: {
|
|
42
|
+
tooltip: () => ({ delete: () => {
|
|
43
|
+
}, setAction: () => {
|
|
44
|
+
} })
|
|
45
|
+
},
|
|
46
|
+
nodes: {
|
|
47
|
+
registerType: () => {
|
|
48
|
+
},
|
|
49
|
+
node: () => null,
|
|
50
|
+
dirty: () => false
|
|
51
|
+
},
|
|
52
|
+
events: {
|
|
53
|
+
on: () => {
|
|
54
|
+
},
|
|
55
|
+
off: () => {
|
|
56
|
+
},
|
|
57
|
+
emit: () => {
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
settings: {},
|
|
61
|
+
notify: () => {
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function ensureState(el) {
|
|
66
|
+
if (el && !el.__jqState) {
|
|
67
|
+
el.__jqState = {
|
|
68
|
+
typedInput: { value: "", type: "" },
|
|
69
|
+
listeners: {}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return el ? el.__jqState : { typedInput: { value: "", type: "" }, listeners: {} };
|
|
73
|
+
}
|
|
74
|
+
function createJQ(el) {
|
|
75
|
+
const state = ensureState(el);
|
|
76
|
+
const jq = {
|
|
77
|
+
0: el,
|
|
78
|
+
length: el ? 1 : 0,
|
|
79
|
+
typedInput(action, value) {
|
|
80
|
+
if (typeof action === "object") {
|
|
81
|
+
state.typedInput = { value: "", type: action.default || "" };
|
|
82
|
+
return jq;
|
|
83
|
+
}
|
|
84
|
+
if (action === "value") {
|
|
85
|
+
if (value !== void 0) {
|
|
86
|
+
state.typedInput.value = String(value);
|
|
87
|
+
if (el) el.setAttribute("value", String(value));
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
return state.typedInput.value;
|
|
91
|
+
}
|
|
92
|
+
if (action === "type") {
|
|
93
|
+
if (value !== void 0) {
|
|
94
|
+
state.typedInput.type = String(value);
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
return state.typedInput.type;
|
|
98
|
+
}
|
|
99
|
+
return jq;
|
|
100
|
+
},
|
|
101
|
+
on(event, cb) {
|
|
102
|
+
if (!state.listeners[event]) state.listeners[event] = [];
|
|
103
|
+
state.listeners[event].push(cb);
|
|
104
|
+
return jq;
|
|
105
|
+
},
|
|
106
|
+
off(event) {
|
|
107
|
+
if (event) {
|
|
108
|
+
delete state.listeners[event];
|
|
109
|
+
} else {
|
|
110
|
+
for (const key of Object.keys(state.listeners)) {
|
|
111
|
+
delete state.listeners[key];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return jq;
|
|
115
|
+
},
|
|
116
|
+
val(value) {
|
|
117
|
+
if (value !== void 0) {
|
|
118
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) {
|
|
119
|
+
el.value = String(value);
|
|
120
|
+
}
|
|
121
|
+
return jq;
|
|
122
|
+
}
|
|
123
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) {
|
|
124
|
+
return el.value;
|
|
125
|
+
}
|
|
126
|
+
return "";
|
|
127
|
+
},
|
|
128
|
+
find(selector) {
|
|
129
|
+
return createJQ(el?.querySelector(selector) ?? null);
|
|
130
|
+
},
|
|
131
|
+
append(child) {
|
|
132
|
+
const childEl = child?.[0] || child;
|
|
133
|
+
if (el && childEl instanceof Element) el.appendChild(childEl);
|
|
134
|
+
return jq;
|
|
135
|
+
},
|
|
136
|
+
appendTo(target) {
|
|
137
|
+
const t = target?.[0] || target;
|
|
138
|
+
if (t instanceof Element && el) t.appendChild(el);
|
|
139
|
+
return jq;
|
|
140
|
+
},
|
|
141
|
+
html(content) {
|
|
142
|
+
if (el) el.innerHTML = content;
|
|
143
|
+
return jq;
|
|
144
|
+
},
|
|
145
|
+
empty() {
|
|
146
|
+
if (el) el.innerHTML = "";
|
|
147
|
+
return jq;
|
|
148
|
+
},
|
|
149
|
+
i18n() {
|
|
150
|
+
return jq;
|
|
151
|
+
},
|
|
152
|
+
addClass(cls) {
|
|
153
|
+
el?.classList.add(cls);
|
|
154
|
+
return jq;
|
|
155
|
+
},
|
|
156
|
+
removeClass(cls) {
|
|
157
|
+
el?.classList.remove(cls);
|
|
158
|
+
return jq;
|
|
159
|
+
},
|
|
160
|
+
__trigger(event) {
|
|
161
|
+
(state.listeners[event] || []).forEach((cb) => cb());
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
return jq;
|
|
165
|
+
}
|
|
166
|
+
function createJQuery() {
|
|
167
|
+
return function $(selector, attrs) {
|
|
168
|
+
if (typeof selector === "string") {
|
|
169
|
+
if (selector.trim().startsWith("<")) {
|
|
170
|
+
const tpl = document.createElement("template");
|
|
171
|
+
tpl.innerHTML = selector.trim();
|
|
172
|
+
const el = tpl.content.firstElementChild;
|
|
173
|
+
if (el && attrs) {
|
|
174
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
175
|
+
if (key === "html") {
|
|
176
|
+
el.innerHTML = String(value);
|
|
177
|
+
} else {
|
|
178
|
+
el.setAttribute(key, String(value));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return createJQ(el);
|
|
183
|
+
}
|
|
184
|
+
return createJQ(document.querySelector(selector));
|
|
185
|
+
}
|
|
186
|
+
if (selector instanceof Element) return createJQ(selector);
|
|
187
|
+
if (selector && typeof selector === "object" && selector.nodeType)
|
|
188
|
+
return createJQ(selector);
|
|
189
|
+
return createJQ(null);
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/test/client/component/index.ts
|
|
194
|
+
var defaultConfig = {
|
|
195
|
+
testTimeout: 3e4,
|
|
196
|
+
setupFiles: ["@bonsae/nrg/test/client/component/setup"],
|
|
197
|
+
browser: {
|
|
198
|
+
enabled: true,
|
|
199
|
+
instances: [{ browser: "chromium" }]
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
function createNode(overrides = {}) {
|
|
203
|
+
const node = {
|
|
204
|
+
id: `test-${Math.random().toString(36).slice(2, 10)}`,
|
|
205
|
+
type: "test-node",
|
|
206
|
+
changed: false,
|
|
207
|
+
_def: { outputs: 1 },
|
|
208
|
+
_: (key) => key,
|
|
209
|
+
...overrides
|
|
210
|
+
};
|
|
211
|
+
const RED = getMockRED();
|
|
212
|
+
spyOnRED(RED);
|
|
213
|
+
return { node, RED };
|
|
214
|
+
}
|
|
215
|
+
function spyIfNeeded(obj, method) {
|
|
216
|
+
if (!vi.isMockFunction(obj[method])) {
|
|
217
|
+
vi.spyOn(obj, method);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function spyOnRED(RED) {
|
|
221
|
+
spyIfNeeded(RED, "_");
|
|
222
|
+
spyIfNeeded(RED, "notify");
|
|
223
|
+
spyIfNeeded(RED.editor, "createEditor");
|
|
224
|
+
spyIfNeeded(RED.editor, "prepareConfigNodeSelect");
|
|
225
|
+
spyIfNeeded(RED.editor, "validateNode");
|
|
226
|
+
spyIfNeeded(RED.tray, "show");
|
|
227
|
+
spyIfNeeded(RED.tray, "close");
|
|
228
|
+
spyIfNeeded(RED.popover, "tooltip");
|
|
229
|
+
spyIfNeeded(RED.nodes, "registerType");
|
|
230
|
+
spyIfNeeded(RED.nodes, "node");
|
|
231
|
+
spyIfNeeded(RED.nodes, "dirty");
|
|
232
|
+
spyIfNeeded(RED.events, "on");
|
|
233
|
+
spyIfNeeded(RED.events, "off");
|
|
234
|
+
spyIfNeeded(RED.events, "emit");
|
|
235
|
+
}
|
|
236
|
+
function getMockRED() {
|
|
237
|
+
return window.RED;
|
|
238
|
+
}
|
|
239
|
+
export {
|
|
240
|
+
createJQuery,
|
|
241
|
+
createNode,
|
|
242
|
+
createRED,
|
|
243
|
+
defaultConfig
|
|
244
|
+
};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// src/test/client/component/setup.ts
|
|
2
|
+
import { beforeEach } from "vitest";
|
|
3
|
+
import { config } from "vitest-browser-vue";
|
|
4
|
+
|
|
5
|
+
// src/test/client/mocks.ts
|
|
6
|
+
function createRED() {
|
|
7
|
+
return {
|
|
8
|
+
_: (key) => key,
|
|
9
|
+
editor: {
|
|
10
|
+
createEditor(options) {
|
|
11
|
+
let currentValue = options.value || "";
|
|
12
|
+
const session = {
|
|
13
|
+
on(_event, _cb) {
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
getValue: () => currentValue,
|
|
18
|
+
setValue: (val) => {
|
|
19
|
+
currentValue = val;
|
|
20
|
+
},
|
|
21
|
+
getSession: () => session,
|
|
22
|
+
focus: () => {
|
|
23
|
+
},
|
|
24
|
+
destroy: () => {
|
|
25
|
+
},
|
|
26
|
+
saveView: () => {
|
|
27
|
+
},
|
|
28
|
+
restoreView: () => {
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
prepareConfigNodeSelect: () => {
|
|
33
|
+
},
|
|
34
|
+
validateNode: () => true
|
|
35
|
+
},
|
|
36
|
+
tray: {
|
|
37
|
+
show: () => {
|
|
38
|
+
},
|
|
39
|
+
close: () => {
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
popover: {
|
|
43
|
+
tooltip: () => ({ delete: () => {
|
|
44
|
+
}, setAction: () => {
|
|
45
|
+
} })
|
|
46
|
+
},
|
|
47
|
+
nodes: {
|
|
48
|
+
registerType: () => {
|
|
49
|
+
},
|
|
50
|
+
node: () => null,
|
|
51
|
+
dirty: () => false
|
|
52
|
+
},
|
|
53
|
+
events: {
|
|
54
|
+
on: () => {
|
|
55
|
+
},
|
|
56
|
+
off: () => {
|
|
57
|
+
},
|
|
58
|
+
emit: () => {
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
settings: {},
|
|
62
|
+
notify: () => {
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function ensureState(el) {
|
|
67
|
+
if (el && !el.__jqState) {
|
|
68
|
+
el.__jqState = {
|
|
69
|
+
typedInput: { value: "", type: "" },
|
|
70
|
+
listeners: {}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return el ? el.__jqState : { typedInput: { value: "", type: "" }, listeners: {} };
|
|
74
|
+
}
|
|
75
|
+
function createJQ(el) {
|
|
76
|
+
const state = ensureState(el);
|
|
77
|
+
const jq = {
|
|
78
|
+
0: el,
|
|
79
|
+
length: el ? 1 : 0,
|
|
80
|
+
typedInput(action, value) {
|
|
81
|
+
if (typeof action === "object") {
|
|
82
|
+
state.typedInput = { value: "", type: action.default || "" };
|
|
83
|
+
return jq;
|
|
84
|
+
}
|
|
85
|
+
if (action === "value") {
|
|
86
|
+
if (value !== void 0) {
|
|
87
|
+
state.typedInput.value = String(value);
|
|
88
|
+
if (el) el.setAttribute("value", String(value));
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
return state.typedInput.value;
|
|
92
|
+
}
|
|
93
|
+
if (action === "type") {
|
|
94
|
+
if (value !== void 0) {
|
|
95
|
+
state.typedInput.type = String(value);
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
return state.typedInput.type;
|
|
99
|
+
}
|
|
100
|
+
return jq;
|
|
101
|
+
},
|
|
102
|
+
on(event, cb) {
|
|
103
|
+
if (!state.listeners[event]) state.listeners[event] = [];
|
|
104
|
+
state.listeners[event].push(cb);
|
|
105
|
+
return jq;
|
|
106
|
+
},
|
|
107
|
+
off(event) {
|
|
108
|
+
if (event) {
|
|
109
|
+
delete state.listeners[event];
|
|
110
|
+
} else {
|
|
111
|
+
for (const key of Object.keys(state.listeners)) {
|
|
112
|
+
delete state.listeners[key];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return jq;
|
|
116
|
+
},
|
|
117
|
+
val(value) {
|
|
118
|
+
if (value !== void 0) {
|
|
119
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) {
|
|
120
|
+
el.value = String(value);
|
|
121
|
+
}
|
|
122
|
+
return jq;
|
|
123
|
+
}
|
|
124
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) {
|
|
125
|
+
return el.value;
|
|
126
|
+
}
|
|
127
|
+
return "";
|
|
128
|
+
},
|
|
129
|
+
find(selector) {
|
|
130
|
+
return createJQ(el?.querySelector(selector) ?? null);
|
|
131
|
+
},
|
|
132
|
+
append(child) {
|
|
133
|
+
const childEl = child?.[0] || child;
|
|
134
|
+
if (el && childEl instanceof Element) el.appendChild(childEl);
|
|
135
|
+
return jq;
|
|
136
|
+
},
|
|
137
|
+
appendTo(target) {
|
|
138
|
+
const t = target?.[0] || target;
|
|
139
|
+
if (t instanceof Element && el) t.appendChild(el);
|
|
140
|
+
return jq;
|
|
141
|
+
},
|
|
142
|
+
html(content) {
|
|
143
|
+
if (el) el.innerHTML = content;
|
|
144
|
+
return jq;
|
|
145
|
+
},
|
|
146
|
+
empty() {
|
|
147
|
+
if (el) el.innerHTML = "";
|
|
148
|
+
return jq;
|
|
149
|
+
},
|
|
150
|
+
i18n() {
|
|
151
|
+
return jq;
|
|
152
|
+
},
|
|
153
|
+
addClass(cls) {
|
|
154
|
+
el?.classList.add(cls);
|
|
155
|
+
return jq;
|
|
156
|
+
},
|
|
157
|
+
removeClass(cls) {
|
|
158
|
+
el?.classList.remove(cls);
|
|
159
|
+
return jq;
|
|
160
|
+
},
|
|
161
|
+
__trigger(event) {
|
|
162
|
+
(state.listeners[event] || []).forEach((cb) => cb());
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
return jq;
|
|
166
|
+
}
|
|
167
|
+
function createJQuery() {
|
|
168
|
+
return function $(selector, attrs) {
|
|
169
|
+
if (typeof selector === "string") {
|
|
170
|
+
if (selector.trim().startsWith("<")) {
|
|
171
|
+
const tpl = document.createElement("template");
|
|
172
|
+
tpl.innerHTML = selector.trim();
|
|
173
|
+
const el = tpl.content.firstElementChild;
|
|
174
|
+
if (el && attrs) {
|
|
175
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
176
|
+
if (key === "html") {
|
|
177
|
+
el.innerHTML = String(value);
|
|
178
|
+
} else {
|
|
179
|
+
el.setAttribute(key, String(value));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return createJQ(el);
|
|
184
|
+
}
|
|
185
|
+
return createJQ(document.querySelector(selector));
|
|
186
|
+
}
|
|
187
|
+
if (selector instanceof Element) return createJQ(selector);
|
|
188
|
+
if (selector && typeof selector === "object" && selector.nodeType)
|
|
189
|
+
return createJQ(selector);
|
|
190
|
+
return createJQ(null);
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/test/client/component/setup.ts
|
|
195
|
+
config.global.mocks.$i18n = (key) => key;
|
|
196
|
+
var RED = createRED();
|
|
197
|
+
window.$ = createJQuery();
|
|
198
|
+
window.RED = RED;
|
|
199
|
+
beforeEach(() => {
|
|
200
|
+
RED.settings = {};
|
|
201
|
+
});
|