@5minds/node-red-contrib-processcube-tools 1.0.1-feature-e7a81a-mfdq8poq → 1.0.1-feature-4ad612-mfdvfdql
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/email-sender/email-sender.html +50 -0
- package/email-sender/email-sender.js +84 -0
- package/package.json +4 -27
- package/email-receiver/email-receiver.html +0 -187
- package/email-receiver/email-receiver.js +0 -231
- package/test/helpers/email-receiver.mocks.js +0 -379
- package/test/integration/email-receiver.integration.test.js +0 -492
- package/test/unit/email-receiver.unit.test.js +0 -300
- /package/{processcube-html-to-text.html → processcube-html-to-text/processcube-html-to-text.html} +0 -0
- /package/{processcube-html-to-text.js → processcube-html-to-text/processcube-html-to-text.js} +0 -0
|
@@ -1,492 +0,0 @@
|
|
|
1
|
-
const should = require('should');
|
|
2
|
-
const helper = require('node-red-node-test-helper');
|
|
3
|
-
|
|
4
|
-
describe('Email Receiver Node - Integration Tests', function() {
|
|
5
|
-
// Set a reasonable timeout for integration tests
|
|
6
|
-
this.timeout(10000);
|
|
7
|
-
|
|
8
|
-
let emailReceiverNode;
|
|
9
|
-
let originalLoad;
|
|
10
|
-
|
|
11
|
-
before(function(done) {
|
|
12
|
-
// Set up mocks for dependencies before loading the node
|
|
13
|
-
setupMocks();
|
|
14
|
-
|
|
15
|
-
// Load the node with mocked dependencies
|
|
16
|
-
emailReceiverNode = require('../../email-receiver/email-receiver.js');
|
|
17
|
-
|
|
18
|
-
// CRITICAL: Initialize the helper with Node-RED
|
|
19
|
-
helper.init(require.resolve('node-red'));
|
|
20
|
-
done();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
after(function() {
|
|
24
|
-
// Restore original module loading
|
|
25
|
-
if (originalLoad) {
|
|
26
|
-
const Module = require('module');
|
|
27
|
-
Module._load = originalLoad;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
beforeEach(function(done) {
|
|
32
|
-
helper.startServer(done);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
afterEach(function(done) {
|
|
36
|
-
helper.unload();
|
|
37
|
-
helper.stopServer(done);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
function setupMocks() {
|
|
41
|
-
// Create mock IMAP module
|
|
42
|
-
const mockImap = function(config) {
|
|
43
|
-
this.config = config;
|
|
44
|
-
this.connect = () => {
|
|
45
|
-
// Simulate a successful connection by immediately emitting 'ready'
|
|
46
|
-
if (this.events && this.events.ready) {
|
|
47
|
-
this.events.ready();
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
this.openBox = (folder, readOnly, callback) => {
|
|
51
|
-
callback(null, { messages: { total: 1 } });
|
|
52
|
-
};
|
|
53
|
-
this.search = (criteria, callback) => {
|
|
54
|
-
callback(null, [123]);
|
|
55
|
-
};
|
|
56
|
-
this.fetch = (results, options) => {
|
|
57
|
-
return {
|
|
58
|
-
on: (event, cb) => {
|
|
59
|
-
if (event === 'message') {
|
|
60
|
-
cb({ on: (e, bodyCb) => { if (e === 'body') bodyCb({}); } });
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
once: (event, cb) => {
|
|
64
|
-
if (event === 'end') { cb(); }
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
|
-
this.end = () => {};
|
|
69
|
-
this.once = (event, callback) => {
|
|
70
|
-
if (!this.events) this.events = {};
|
|
71
|
-
this.events[event] = callback;
|
|
72
|
-
};
|
|
73
|
-
return this;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// Create mock mailparser module
|
|
77
|
-
const mockMailparser = {
|
|
78
|
-
simpleParser: function() {
|
|
79
|
-
return Promise.resolve({
|
|
80
|
-
subject: 'test integration email',
|
|
81
|
-
text: 'test integration body',
|
|
82
|
-
html: '<p>test integration</p>',
|
|
83
|
-
from: { text: 'integration@test.com' },
|
|
84
|
-
date: new Date(),
|
|
85
|
-
headers: new Map(),
|
|
86
|
-
attachments: []
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const mockModules = {
|
|
92
|
-
'node-imap': mockImap,
|
|
93
|
-
'mailparser': mockMailparser
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// Override require to use mocks
|
|
97
|
-
const Module = require('module');
|
|
98
|
-
originalLoad = Module._load;
|
|
99
|
-
Module._load = function(request, parent) {
|
|
100
|
-
if (mockModules[request]) {
|
|
101
|
-
return mockModules[request];
|
|
102
|
-
}
|
|
103
|
-
return originalLoad.apply(this, arguments);
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
describe('Node Loading', function() {
|
|
108
|
-
it('should load in Node-RED test environment', function(done) {
|
|
109
|
-
// ARRANGE: Set up Node-RED flow with proper configuration
|
|
110
|
-
const flow = [
|
|
111
|
-
{
|
|
112
|
-
id: "n1",
|
|
113
|
-
type: "email-receiver",
|
|
114
|
-
name: "test node",
|
|
115
|
-
host: "imap.test.com",
|
|
116
|
-
hostType: "str",
|
|
117
|
-
port: "993",
|
|
118
|
-
portType: "str",
|
|
119
|
-
tls: true,
|
|
120
|
-
tlsType: "bool",
|
|
121
|
-
user: "test@example.com",
|
|
122
|
-
userType: "str",
|
|
123
|
-
password: "testpass",
|
|
124
|
-
passwordType: "str",
|
|
125
|
-
folder: "INBOX",
|
|
126
|
-
folderType: "str",
|
|
127
|
-
markseen: true,
|
|
128
|
-
markseenType: "bool"
|
|
129
|
-
}
|
|
130
|
-
];
|
|
131
|
-
|
|
132
|
-
// ACT: Load the node in the test helper environment
|
|
133
|
-
helper.load(emailReceiverNode, flow, function() {
|
|
134
|
-
try {
|
|
135
|
-
// ASSERT: Verify the node loaded correctly
|
|
136
|
-
const n1 = helper.getNode("n1");
|
|
137
|
-
should.exist(n1);
|
|
138
|
-
n1.should.have.property('name', 'test node');
|
|
139
|
-
n1.should.have.property('type', 'email-receiver');
|
|
140
|
-
done();
|
|
141
|
-
} catch (err) {
|
|
142
|
-
done(err);
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('should load with minimal configuration', function(done) {
|
|
148
|
-
// ARRANGE: Set up minimal flow configuration
|
|
149
|
-
const flow = [
|
|
150
|
-
{
|
|
151
|
-
id: "n1",
|
|
152
|
-
type: "email-receiver",
|
|
153
|
-
host: "imap.minimal.com",
|
|
154
|
-
hostType: "str",
|
|
155
|
-
port: "993",
|
|
156
|
-
portType: "str",
|
|
157
|
-
user: "minimal@test.com",
|
|
158
|
-
userType: "str",
|
|
159
|
-
password: "minimalpass",
|
|
160
|
-
passwordType: "str",
|
|
161
|
-
folder: "INBOX",
|
|
162
|
-
folderType: "str"
|
|
163
|
-
}
|
|
164
|
-
];
|
|
165
|
-
|
|
166
|
-
// ACT: Load the node
|
|
167
|
-
helper.load(emailReceiverNode, flow, function() {
|
|
168
|
-
try {
|
|
169
|
-
// ASSERT: Verify the node loaded with minimal config
|
|
170
|
-
const n1 = helper.getNode("n1");
|
|
171
|
-
should.exist(n1);
|
|
172
|
-
n1.should.have.property('type', 'email-receiver');
|
|
173
|
-
done();
|
|
174
|
-
} catch (err) {
|
|
175
|
-
done(err);
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
describe('Node Connections', function() {
|
|
182
|
-
it('should create wired connections correctly', function(done) {
|
|
183
|
-
// ARRANGE: Set up flow with helper node to catch output
|
|
184
|
-
const flow = [
|
|
185
|
-
{
|
|
186
|
-
id: "n1",
|
|
187
|
-
type: "email-receiver",
|
|
188
|
-
name: "test node",
|
|
189
|
-
host: "imap.test.com",
|
|
190
|
-
hostType: "str",
|
|
191
|
-
port: "993",
|
|
192
|
-
portType: "str",
|
|
193
|
-
tls: true,
|
|
194
|
-
tlsType: "bool",
|
|
195
|
-
user: "test@example.com",
|
|
196
|
-
userType: "str",
|
|
197
|
-
password: "testpass",
|
|
198
|
-
passwordType: "str",
|
|
199
|
-
folder: "INBOX",
|
|
200
|
-
folderType: "str",
|
|
201
|
-
markseen: true,
|
|
202
|
-
markseenType: "bool",
|
|
203
|
-
wires: [["n2"]]
|
|
204
|
-
},
|
|
205
|
-
{ id: "n2", type: "helper" }
|
|
206
|
-
];
|
|
207
|
-
|
|
208
|
-
// ACT: Load nodes and verify connections
|
|
209
|
-
helper.load(emailReceiverNode, flow, function() {
|
|
210
|
-
try {
|
|
211
|
-
const n1 = helper.getNode("n1");
|
|
212
|
-
const n2 = helper.getNode("n2");
|
|
213
|
-
|
|
214
|
-
// ASSERT: Both nodes should exist and be connected
|
|
215
|
-
should.exist(n1);
|
|
216
|
-
should.exist(n2);
|
|
217
|
-
n1.should.have.property('name', 'test node');
|
|
218
|
-
n2.should.have.property('type', 'helper');
|
|
219
|
-
|
|
220
|
-
done();
|
|
221
|
-
} catch (err) {
|
|
222
|
-
done(err);
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
it('should handle multiple output connections', function(done) {
|
|
228
|
-
// ARRANGE: Set up flow with multiple helper nodes
|
|
229
|
-
const flow = [
|
|
230
|
-
{
|
|
231
|
-
id: "n1",
|
|
232
|
-
type: "email-receiver",
|
|
233
|
-
name: "multi-output node",
|
|
234
|
-
host: "imap.test.com",
|
|
235
|
-
hostType: "str",
|
|
236
|
-
port: "993",
|
|
237
|
-
portType: "str",
|
|
238
|
-
user: "test@example.com",
|
|
239
|
-
userType: "str",
|
|
240
|
-
password: "testpass",
|
|
241
|
-
passwordType: "str",
|
|
242
|
-
folder: "INBOX",
|
|
243
|
-
folderType: "str",
|
|
244
|
-
markseen: true,
|
|
245
|
-
markseenType: "bool",
|
|
246
|
-
wires: [["n2", "n3"]]
|
|
247
|
-
},
|
|
248
|
-
{ id: "n2", type: "helper" },
|
|
249
|
-
{ id: "n3", type: "helper" }
|
|
250
|
-
];
|
|
251
|
-
|
|
252
|
-
// ACT: Load nodes
|
|
253
|
-
helper.load(emailReceiverNode, flow, function() {
|
|
254
|
-
try {
|
|
255
|
-
const n1 = helper.getNode("n1");
|
|
256
|
-
const n2 = helper.getNode("n2");
|
|
257
|
-
const n3 = helper.getNode("n3");
|
|
258
|
-
|
|
259
|
-
// ASSERT: All nodes should exist
|
|
260
|
-
should.exist(n1);
|
|
261
|
-
should.exist(n2);
|
|
262
|
-
should.exist(n3);
|
|
263
|
-
n1.should.have.property('name', 'multi-output node');
|
|
264
|
-
|
|
265
|
-
done();
|
|
266
|
-
} catch (err) {
|
|
267
|
-
done(err);
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
describe('Message Flow', function() {
|
|
274
|
-
it('should handle input without crashing', function(done) {
|
|
275
|
-
// ARRANGE: Set up minimal flow
|
|
276
|
-
const flow = [
|
|
277
|
-
{
|
|
278
|
-
id: "n1",
|
|
279
|
-
type: "email-receiver",
|
|
280
|
-
name: "test node",
|
|
281
|
-
host: "imap.test.com",
|
|
282
|
-
hostType: "str",
|
|
283
|
-
port: "993",
|
|
284
|
-
portType: "str",
|
|
285
|
-
tls: true,
|
|
286
|
-
tlsType: "bool",
|
|
287
|
-
user: "test@example.com",
|
|
288
|
-
userType: "str",
|
|
289
|
-
password: "testpass",
|
|
290
|
-
passwordType: "str",
|
|
291
|
-
folder: "INBOX",
|
|
292
|
-
folderType: "str",
|
|
293
|
-
markseen: true,
|
|
294
|
-
markseenType: "bool"
|
|
295
|
-
}
|
|
296
|
-
];
|
|
297
|
-
|
|
298
|
-
// ACT: Load node and send input
|
|
299
|
-
helper.load(emailReceiverNode, flow, function() {
|
|
300
|
-
try {
|
|
301
|
-
const n1 = helper.getNode("n1");
|
|
302
|
-
should.exist(n1);
|
|
303
|
-
|
|
304
|
-
// Send input - this should not crash due to mocked IMAP
|
|
305
|
-
n1.receive({ payload: "test input" });
|
|
306
|
-
|
|
307
|
-
// ASSERT: If we reach here, the node handled input gracefully
|
|
308
|
-
setTimeout(() => {
|
|
309
|
-
done(); // Success if no errors thrown
|
|
310
|
-
}, 500);
|
|
311
|
-
|
|
312
|
-
} catch (err) {
|
|
313
|
-
done(err);
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
it('should process messages through connected nodes', function(done) {
|
|
319
|
-
// ARRANGE: Set up flow with helper to capture output
|
|
320
|
-
const flow = [
|
|
321
|
-
{
|
|
322
|
-
id: "n1",
|
|
323
|
-
type: "email-receiver",
|
|
324
|
-
name: "sender node",
|
|
325
|
-
host: "imap.test.com",
|
|
326
|
-
hostType: "str",
|
|
327
|
-
port: "993",
|
|
328
|
-
portType: "str",
|
|
329
|
-
user: "test@example.com",
|
|
330
|
-
userType: "str",
|
|
331
|
-
password: "testpass",
|
|
332
|
-
passwordType: "str",
|
|
333
|
-
folder: "INBOX",
|
|
334
|
-
folderType: "str",
|
|
335
|
-
markseen: true,
|
|
336
|
-
markseenType: "bool",
|
|
337
|
-
wires: [["n2"]]
|
|
338
|
-
},
|
|
339
|
-
{ id: "n2", type: "helper" }
|
|
340
|
-
];
|
|
341
|
-
|
|
342
|
-
// ACT: Load nodes and set up message listener
|
|
343
|
-
helper.load(emailReceiverNode, flow, function() {
|
|
344
|
-
try {
|
|
345
|
-
const n1 = helper.getNode("n1");
|
|
346
|
-
const n2 = helper.getNode("n2");
|
|
347
|
-
|
|
348
|
-
// Set up listener for messages from email receiver
|
|
349
|
-
n2.on("input", function(msg) {
|
|
350
|
-
try {
|
|
351
|
-
// ASSERT: Should receive a message with expected properties
|
|
352
|
-
should.exist(msg);
|
|
353
|
-
should.exist(msg.payload);
|
|
354
|
-
msg.payload.should.equal('test integration body');
|
|
355
|
-
done();
|
|
356
|
-
} catch (err) {
|
|
357
|
-
done(err);
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
// Trigger the email receiver
|
|
362
|
-
n1.receive({ payload: "trigger" });
|
|
363
|
-
|
|
364
|
-
} catch (err) {
|
|
365
|
-
done(err);
|
|
366
|
-
}
|
|
367
|
-
});
|
|
368
|
-
});
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
describe('Configuration Validation', function() {
|
|
372
|
-
it('should handle invalid configuration gracefully', function(done) {
|
|
373
|
-
// ARRANGE: Set up flow with missing required config
|
|
374
|
-
const flow = [
|
|
375
|
-
{
|
|
376
|
-
id: "n1",
|
|
377
|
-
type: "email-receiver",
|
|
378
|
-
name: "invalid config node",
|
|
379
|
-
host: "", // Missing host
|
|
380
|
-
hostType: "str",
|
|
381
|
-
port: "993",
|
|
382
|
-
portType: "str",
|
|
383
|
-
user: "test@example.com",
|
|
384
|
-
userType: "str",
|
|
385
|
-
password: "testpass",
|
|
386
|
-
passwordType: "str",
|
|
387
|
-
folder: "INBOX",
|
|
388
|
-
folderType: "str"
|
|
389
|
-
}
|
|
390
|
-
];
|
|
391
|
-
|
|
392
|
-
// ACT: Load node with invalid config
|
|
393
|
-
helper.load(emailReceiverNode, flow, function() {
|
|
394
|
-
try {
|
|
395
|
-
const n1 = helper.getNode("n1");
|
|
396
|
-
should.exist(n1);
|
|
397
|
-
|
|
398
|
-
// ASSERT: Node should exist but handle invalid config appropriately
|
|
399
|
-
// Send input to trigger validation
|
|
400
|
-
n1.receive({ payload: "test" });
|
|
401
|
-
|
|
402
|
-
// If we get here without crashing, the validation worked
|
|
403
|
-
setTimeout(() => {
|
|
404
|
-
done();
|
|
405
|
-
}, 300);
|
|
406
|
-
|
|
407
|
-
} catch (err) {
|
|
408
|
-
done(err);
|
|
409
|
-
}
|
|
410
|
-
});
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
it('should load with different folder configurations', function(done) {
|
|
414
|
-
// ARRANGE: Set up flow with array folder config
|
|
415
|
-
const flow = [
|
|
416
|
-
{
|
|
417
|
-
id: "n1",
|
|
418
|
-
type: "email-receiver",
|
|
419
|
-
name: "array folder node",
|
|
420
|
-
host: "imap.test.com",
|
|
421
|
-
hostType: "str",
|
|
422
|
-
port: "993",
|
|
423
|
-
portType: "str",
|
|
424
|
-
user: "test@example.com",
|
|
425
|
-
userType: "str",
|
|
426
|
-
password: "testpass",
|
|
427
|
-
passwordType: "str",
|
|
428
|
-
folder: ["INBOX", "Sent", "Drafts"],
|
|
429
|
-
folderType: "json",
|
|
430
|
-
markseen: true,
|
|
431
|
-
markseenType: "bool"
|
|
432
|
-
}
|
|
433
|
-
];
|
|
434
|
-
|
|
435
|
-
// ACT: Load node with array folder config
|
|
436
|
-
helper.load(emailReceiverNode, flow, function() {
|
|
437
|
-
try {
|
|
438
|
-
const n1 = helper.getNode("n1");
|
|
439
|
-
|
|
440
|
-
// ASSERT: Node should load successfully with array config
|
|
441
|
-
should.exist(n1);
|
|
442
|
-
n1.should.have.property('name', 'array folder node');
|
|
443
|
-
done();
|
|
444
|
-
|
|
445
|
-
} catch (err) {
|
|
446
|
-
done(err);
|
|
447
|
-
}
|
|
448
|
-
});
|
|
449
|
-
});
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
describe('Node Lifecycle', function() {
|
|
453
|
-
it('should clean up properly on unload', function(done) {
|
|
454
|
-
// ARRANGE: Set up flow
|
|
455
|
-
const flow = [
|
|
456
|
-
{
|
|
457
|
-
id: "n1",
|
|
458
|
-
type: "email-receiver",
|
|
459
|
-
name: "cleanup test node",
|
|
460
|
-
host: "imap.test.com",
|
|
461
|
-
hostType: "str",
|
|
462
|
-
port: "993",
|
|
463
|
-
portType: "str",
|
|
464
|
-
user: "test@example.com",
|
|
465
|
-
userType: "str",
|
|
466
|
-
password: "testpass",
|
|
467
|
-
passwordType: "str",
|
|
468
|
-
folder: "INBOX",
|
|
469
|
-
folderType: "str"
|
|
470
|
-
}
|
|
471
|
-
];
|
|
472
|
-
|
|
473
|
-
// ACT: Load and then unload the node
|
|
474
|
-
helper.load(emailReceiverNode, flow, function() {
|
|
475
|
-
try {
|
|
476
|
-
const n1 = helper.getNode("n1");
|
|
477
|
-
should.exist(n1);
|
|
478
|
-
|
|
479
|
-
// Simulate some activity
|
|
480
|
-
n1.receive({ payload: "test" });
|
|
481
|
-
|
|
482
|
-
// ASSERT: Unloading should not throw errors
|
|
483
|
-
helper.unload();
|
|
484
|
-
done();
|
|
485
|
-
|
|
486
|
-
} catch (err) {
|
|
487
|
-
done(err);
|
|
488
|
-
}
|
|
489
|
-
});
|
|
490
|
-
});
|
|
491
|
-
});
|
|
492
|
-
});
|