@5minds/node-red-contrib-processcube-tools 1.0.1-feature-4ad612-mfdvfdql → 1.0.1-feature-df88cf-mfdx610k

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.
@@ -0,0 +1,332 @@
1
+ const should = require('should');
2
+
3
+ describe('Email Receiver Node - Unit Tests', function() {
4
+ // Set a reasonable timeout
5
+ this.timeout(10000);
6
+
7
+ // Module and mocking setup
8
+ let emailReceiverNode;
9
+ let originalLoad;
10
+ let mockImap;
11
+ let mockMailparser;
12
+
13
+ before(function() {
14
+ // Create mock modules with correct behavior
15
+ mockImap = function(config) {
16
+ this.config = config;
17
+ this.connect = () => {
18
+ // Simulate a successful connection by immediately emitting 'ready'
19
+ if (this.events && this.events.ready) {
20
+ this.events.ready();
21
+ }
22
+ };
23
+ this.openBox = (folder, readOnly, callback) => { callback(null, { messages: { total: 1 } }); };
24
+ this.search = (criteria, callback) => { callback(null, [123]); };
25
+ this.fetch = (results, options) => {
26
+ return {
27
+ on: (event, cb) => {
28
+ if (event === 'message') {
29
+ cb({ on: (e, bodyCb) => { if (e === 'body') bodyCb({}); } });
30
+ }
31
+ },
32
+ once: (event, cb) => {
33
+ if (event === 'end') { cb(); }
34
+ }
35
+ };
36
+ };
37
+ this.end = () => {};
38
+ this.once = (event, callback) => {
39
+ if (!this.events) this.events = {};
40
+ this.events[event] = callback;
41
+ };
42
+ return this;
43
+ };
44
+
45
+ mockMailparser = {
46
+ simpleParser: function() {
47
+ return Promise.resolve({
48
+ subject: 'test',
49
+ text: 'test body',
50
+ html: '<p>test</p>',
51
+ from: { text: 'test@test.com' },
52
+ date: new Date(),
53
+ headers: new Map(),
54
+ attachments: []
55
+ });
56
+ }
57
+ };
58
+
59
+ const mockModules = {
60
+ 'node-imap': mockImap,
61
+ 'mailparser': mockMailparser
62
+ };
63
+
64
+ // Override require
65
+ const Module = require('module');
66
+ originalLoad = Module._load;
67
+ Module._load = function(request, parent) {
68
+ if (mockModules[request]) {
69
+ return mockModules[request];
70
+ }
71
+ return originalLoad.apply(this, arguments);
72
+ };
73
+
74
+ // Load the node with mocked dependencies
75
+ emailReceiverNode = require('../../email-receiver/email-receiver.js');
76
+ });
77
+
78
+ after(function() {
79
+ // Restore original module loading
80
+ if (originalLoad) {
81
+ const Module = require('module');
82
+ Module._load = originalLoad;
83
+ }
84
+ });
85
+
86
+ describe('Module Export', function() {
87
+ it('should export a function', function() {
88
+ // ARRANGE: Node module is already loaded
89
+
90
+ // ACT: Check the type of the exported module
91
+
92
+ // ASSERT: Should be a function
93
+ emailReceiverNode.should.be.type('function');
94
+ });
95
+ });
96
+
97
+ describe('Node Registration', function() {
98
+ it('should register node type without errors', function() {
99
+ // ARRANGE: Set up mock RED object and capture registration calls
100
+ let registeredType;
101
+ let registeredConstructor;
102
+
103
+ const mockRED = {
104
+ nodes: {
105
+ createNode: function(node, config) {
106
+ node.id = config.id;
107
+ node.type = config.type;
108
+ node.name = config.name;
109
+ node.on = function() {};
110
+ node.status = function() {};
111
+ node.error = function() {};
112
+ node.send = function() {};
113
+ return node;
114
+ },
115
+ registerType: function(type, constructor) {
116
+ registeredType = type;
117
+ registeredConstructor = constructor;
118
+ }
119
+ },
120
+ util: {
121
+ evaluateNodeProperty: function(value, type) {
122
+ return value;
123
+ },
124
+ encrypt: function(value) {
125
+ return 'encrypted:' + value;
126
+ }
127
+ }
128
+ };
129
+
130
+ // ACT: Call the node registration function
131
+ emailReceiverNode(mockRED);
132
+
133
+ // ASSERT: Verify registration was called correctly
134
+ registeredType.should.equal('email-receiver');
135
+ registeredConstructor.should.be.type('function');
136
+ });
137
+ });
138
+
139
+ describe('Node Instantiation', function() {
140
+ it('should handle node instantiation with valid config', function() {
141
+ // ARRANGE: Set up mock RED object and node instance tracking
142
+ let nodeInstance;
143
+
144
+ const mockRED = {
145
+ nodes: {
146
+ createNode: function(node, config) {
147
+ nodeInstance = node;
148
+ node.id = config.id;
149
+ node.type = config.type;
150
+ node.name = config.name;
151
+ node.on = function() {};
152
+ node.status = function() {};
153
+ node.error = function() {};
154
+ node.send = function() {};
155
+ return node;
156
+ },
157
+ registerType: function(type, NodeConstructor) {
158
+ // Simulate creating a node instance with valid config
159
+ const config = {
160
+ id: 'test-node',
161
+ type: 'email-receiver',
162
+ name: 'Test Email Receiver',
163
+ host: 'imap.test.com',
164
+ hostType: 'str',
165
+ port: 993,
166
+ portType: 'num',
167
+ user: 'test@test.com',
168
+ userType: 'str',
169
+ password: 'testpass',
170
+ passwordType: 'str',
171
+ folder: ["INBOX"],
172
+ folderType: 'json',
173
+ markseen: true,
174
+ markseenType: 'bool'
175
+ };
176
+
177
+ new NodeConstructor(config);
178
+ }
179
+ },
180
+ util: {
181
+ evaluateNodeProperty: function(value, type) {
182
+ return value;
183
+ },
184
+ encrypt: function(value) {
185
+ return 'encrypted:' + value;
186
+ }
187
+ }
188
+ };
189
+
190
+ // ACT: Register the node and create an instance
191
+ emailReceiverNode(mockRED);
192
+
193
+ // ASSERT: Verify the node instance was created with correct properties
194
+ should.exist(nodeInstance);
195
+ nodeInstance.should.have.property('name', 'Test Email Receiver');
196
+ });
197
+ });
198
+
199
+ describe('Folder Configuration', function() {
200
+ it('should handle an array of folders', function(done) {
201
+ // ARRANGE: Mock the Node-RED environment
202
+ let nodeInstance;
203
+ let inputCallback;
204
+ let messagesSent = 0;
205
+ const expectedMessages = 2;
206
+
207
+ const mockRED = {
208
+ nodes: {
209
+ createNode: function(node, config) {
210
+ nodeInstance = node;
211
+ node.on = (event, callback) => { if (event === 'input') inputCallback = callback; };
212
+ node.status = () => {};
213
+ node.error = () => {};
214
+ node.send = (msg) => {
215
+ should.exist(msg);
216
+ msg.payload.should.equal('test body');
217
+ messagesSent++;
218
+ if (messagesSent === expectedMessages) {
219
+ done();
220
+ }
221
+ };
222
+ return node;
223
+ },
224
+ registerType: (type, constructor) => {
225
+ const mockNode = {
226
+ id: 'mock-node-id',
227
+ on: (event, callback) => {
228
+ if (event === 'input') {
229
+ inputCallback = callback;
230
+ }
231
+ },
232
+ status: () => {},
233
+ error: () => {},
234
+ send: (msg) => {
235
+ should.exist(msg);
236
+ msg.payload.should.equal('test body');
237
+ messagesSent++;
238
+ if (messagesSent === expectedMessages) {
239
+ done();
240
+ }
241
+ },
242
+ };
243
+ // Create an instance of the node with the test configuration
244
+ const node = new constructor(mockNode, {
245
+ host: "imap.test.com", hostType: "str",
246
+ port: 993, portType: "num",
247
+ user: "test@test.com", userType: "str",
248
+ password: "testpass", passwordType: "str",
249
+ folder: ["INBOX", "Junk"], folderType: 'json',
250
+ markseen: true, markseenType: 'bool'
251
+ });
252
+ }
253
+ },
254
+ util: { evaluateNodeProperty: (value) => value },
255
+ };
256
+
257
+ // ACT: Register the node, then simulate input
258
+ emailReceiverNode(mockRED);
259
+ // After the node is registered, simulate an input to trigger the flow
260
+ setTimeout(() => {
261
+ if (inputCallback) {
262
+ inputCallback({});
263
+ } else {
264
+ done(new Error("Input callback not set up."));
265
+ }
266
+ }, 50); // Use a small timeout to ensure the mocks are fully set up
267
+ });
268
+ });
269
+
270
+ describe('Error Handling', function() {
271
+ it('should call node.error for invalid folder type', function(done) {
272
+ // ARRANGE: Mock the node instance to capture errors
273
+ let errorCalled = false;
274
+ const nodeInstance = {
275
+ config: { folder: 123, folderType: 'num' },
276
+ on: (event, callback) => { if (event === 'input') nodeInstance.inputCallback = callback; },
277
+ status: () => {},
278
+ error: (err) => {
279
+ errorCalled = true;
280
+ err.should.containEql('The \'folders\' property must be an array of strings');
281
+ done();
282
+ },
283
+ send: () => {},
284
+ };
285
+ const mockRED = {
286
+ nodes: {
287
+ createNode: (node, config) => Object.assign(node, { on: nodeInstance.on, status: nodeInstance.status, error: nodeInstance.error, send: nodeInstance.send }),
288
+ registerType: (type, constructor) => new constructor(nodeInstance.config),
289
+ },
290
+ util: { evaluateNodeProperty: (value, type) => value },
291
+ };
292
+
293
+ // ACT: Register and instantiate the node, then simulate an input message
294
+ emailReceiverNode(mockRED);
295
+ nodeInstance.inputCallback({});
296
+ });
297
+
298
+ it('should call node.error for missing config', function(done) {
299
+ // ARRANGE: Mock the node instance to capture errors
300
+ let errorCalled = false;
301
+ let statusCalled = false;
302
+ const nodeInstance = {
303
+ config: {
304
+ host: "imap.test.com", hostType: "str",
305
+ port: 993, portType: "num",
306
+ user: "test@test.com", userType: "str",
307
+ password: "", passwordType: "str", // Empty password should trigger error
308
+ folder: ["INBOX"], folderType: "json"
309
+ },
310
+ on: (event, callback) => { if (event === 'input') nodeInstance.inputCallback = callback; },
311
+ status: (s) => { statusCalled = true; s.fill.should.equal('red'); },
312
+ error: (err) => {
313
+ errorCalled = true;
314
+ err.should.containEql('Missing required IMAP config');
315
+ done();
316
+ },
317
+ send: () => {},
318
+ };
319
+ const mockRED = {
320
+ nodes: {
321
+ createNode: (node, config) => Object.assign(node, { on: nodeInstance.on, status: nodeInstance.status, error: nodeInstance.error, send: nodeInstance.send }),
322
+ registerType: (type, constructor) => new constructor(nodeInstance.config),
323
+ },
324
+ util: { evaluateNodeProperty: (value, type) => value },
325
+ };
326
+
327
+ // ACT: Register and instantiate the node, then simulate an input message
328
+ emailReceiverNode(mockRED);
329
+ nodeInstance.inputCallback({});
330
+ });
331
+ });
332
+ });
@@ -1,50 +0,0 @@
1
- <script type="text/javascript">
2
- RED.nodes.registerType('email-sender',{
3
- category: 'network',
4
- color: '#a6bbcf',
5
- defaults: {
6
- name: {value: ""},
7
- email: {value: "", type: "json"},
8
- smtp: {value: "", type: "json"}
9
- },
10
- inputs:1,
11
- outputs:1,
12
- icon: "font-awesome/fa-envelope",
13
- label: function() {
14
- return this.name || "Custom Email Sender";
15
- },
16
- labelStyle: function() {
17
- return this.name?"node_label_italic":"";
18
- }
19
- });
20
- </script>
21
-
22
- <script type="text/html" data-template-name="custom-email-sender">
23
- <div class="form-row">
24
- <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
25
- <input type="text" id="node-input-name" placeholder="Name">
26
- </div>
27
- <div class="form-row">
28
- <label for="node-input-email"><i class="fa fa-envelope"></i> E-Mail Konfiguration</label>
29
- <input type="text" id="node-input-email" placeholder="JSON-Objekt oder J:...">
30
- <input type="hidden" id="node-input-emailType">
31
- </div>
32
- <div class="form-row">
33
- <label for="node-input-smtp"><i class="fa fa-cogs"></i> SMTP Konfiguration</label>
34
- <input type="text" id="node-input-smtp" placeholder="JSON-Objekt oder J:...">
35
- <input type="hidden" id="node-input-smtpType">
36
- </div>
37
- </script>
38
-
39
- <script type="text/html" data-help-name="custom-email-sender">
40
- <p>Eine benutzerdefinierte Node, um E-Mails über einen SMTP-Server zu versenden.</p>
41
- <h3>Inputs</h3>
42
- <dl class="message-properties">
43
- <dt>payload
44
- <span class="property-type">object</span>
45
- </dt>
46
- <dd>Beinhaltet die Daten zum E-Mail-Versand.</dd>
47
- </dl>
48
- <h3>Details</h3>
49
- <p>Die Node verwendet Nodemailer, um eine E-Mail zu senden. Die Konfiguration kann über die Node-Eigenschaften oder die eingehende Nachricht erfolgen.</p>
50
- </script>
@@ -1,84 +0,0 @@
1
- module.exports = function(RED) {
2
- "use strict";
3
- const nodemailer = require("nodemailer");
4
-
5
- function EmailSenderNode(config) {
6
- RED.nodes.createNode(this, config);
7
- var node = this;
8
-
9
- node.on('input', function(msg, send, done) {
10
-
11
- // Konfiguration und Daten aus der Nachricht extrahieren
12
- const emailConfig = RED.util.evaluateNodeProperty(config.email, config.emailType, node, msg) || {};
13
- const smtpConfig = RED.util.evaluateNodeProperty(config.smtp, config.smtpType, node, msg) || {};
14
-
15
- // Fallback für send und done, falls ältere Node-RED Version
16
- send = send || function() { node.send.apply(node, arguments); }
17
- done = done || function(err) { if (err) node.error(err, msg); }
18
-
19
- // Logik vom Code-Snippet
20
- const transporter = nodemailer.createTransport({
21
- host: smtpConfig.host,
22
- port: smtpConfig.port,
23
- auth: {
24
- user: smtpConfig.auth?.user,
25
- pass: smtpConfig.auth?.pass
26
- },
27
- secure: smtpConfig.secure !== undefined ? smtpConfig.secure : true,
28
- proxy: smtpConfig.proxy || undefined,
29
- tls: {
30
- rejectUnauthorized: smtpConfig.tls?.rejectUnauthorized !== undefined
31
- ? smtpConfig.tls.rejectUnauthorized
32
- : true
33
- }
34
- });
35
-
36
- const mail = {
37
- from: emailConfig.from,
38
- to: emailConfig.to,
39
- cc: emailConfig.cc || "",
40
- bcc: emailConfig.bcc || "",
41
- subject: emailConfig.subject || msg.topic || "Message from Node-RED",
42
- attachments: emailConfig.attachments,
43
- text: emailConfig.text,
44
- html: emailConfig.html,
45
- amp: emailConfig.amp,
46
- priority: emailConfig.priority || "normal"
47
- };
48
-
49
- // E-Mail senden und den Status überprüfen
50
- transporter.sendMail(mail, (error, info) => {
51
- if (error) {
52
- node.error(error, msg);
53
- done(error); // Fehler an Node-RED weiterleiten
54
- return;
55
- }
56
-
57
- node.log('E-Mail gesendet: ' + info.response);
58
- msg.payload = info;
59
-
60
- // Statusprüfung basierend auf dem Code-Auszug
61
- if (msg.payload.accepted && msg.payload.accepted.length > 0) {
62
- msg.payload = { result: msg.input };
63
- send(msg); // Nachricht an den nächsten Knoten senden
64
- } else if (msg.payload.rejected && msg.payload.rejected.length > 0) {
65
- msg.error = { result: msg.payload.rejected };
66
- done('E-Mail abgelehnt: ' + msg.payload.rejected.join(', '));
67
- node.status({fill:"red", shape:"dot", text:"rejected"});
68
- return;
69
- } else if (msg.payload.pending && msg.payload.pending.length > 0) {
70
- msg.error = { result: msg.payload.pending };
71
- done('E-Mail ausstehend: ' + msg.payload.pending.join(', '));
72
- node.status({fill:"yellow", shape:"dot", text:"pending"});
73
- return;
74
- } else {
75
- done('Unbekannter Fehler beim Senden der E-Mail.');
76
- node.status({fill:"red", shape:"dot", text:"error"});
77
- return;
78
- }
79
- });
80
- });
81
- }
82
-
83
- RED.nodes.registerType("email-sender", EmailSenderNode);
84
- };