@5minds/node-red-contrib-processcube-tools 1.2.0-develop-2eb127-mg68t7xt → 1.2.0-develop-59ef22-mg9d9ja5

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.
Files changed (55) hide show
  1. package/.mocharc.json +5 -0
  2. package/package.json +26 -10
  3. package/src/custom-node-template/custom-node-template.html.template +45 -0
  4. package/src/custom-node-template/custom-node-template.ts.template +69 -0
  5. package/src/email-receiver/email-receiver.ts +439 -0
  6. package/src/email-sender/email-sender.ts +210 -0
  7. package/{processcube-html-to-text/processcube-html-to-text.html → src/html-to-text/html-to-text.html} +3 -3
  8. package/src/html-to-text/html-to-text.ts +53 -0
  9. package/src/index.ts +12 -0
  10. package/src/interfaces/EmailReceiverMessage.ts +22 -0
  11. package/src/interfaces/EmailSenderNodeProperties.ts +37 -0
  12. package/src/interfaces/FetchState.ts +9 -0
  13. package/src/interfaces/ImapConnectionConfig.ts +14 -0
  14. package/src/test/framework/advanced-test-patterns.ts +224 -0
  15. package/src/test/framework/generic-node-test-suite.ts +58 -0
  16. package/src/test/framework/index.ts +17 -0
  17. package/src/test/framework/integration-assertions.ts +67 -0
  18. package/src/test/framework/integration-scenario-builder.ts +77 -0
  19. package/src/test/framework/integration-test-runner.ts +101 -0
  20. package/src/test/framework/node-assertions.ts +63 -0
  21. package/src/test/framework/node-test-runner.ts +260 -0
  22. package/src/test/framework/test-scenario-builder.ts +74 -0
  23. package/src/test/framework/types.ts +61 -0
  24. package/src/test/helpers/email-receiver-test-configs.ts +67 -0
  25. package/src/test/helpers/email-receiver-test-flows.ts +16 -0
  26. package/src/test/helpers/email-sender-test-configs.ts +123 -0
  27. package/src/test/helpers/email-sender-test-flows.ts +16 -0
  28. package/src/test/integration/email-receiver.integration.test.ts +41 -0
  29. package/src/test/integration/email-sender.integration.test.ts +129 -0
  30. package/src/test/interfaces/email-data.ts +10 -0
  31. package/src/test/interfaces/email-receiver-config.ts +12 -0
  32. package/src/test/interfaces/email-sender-config.ts +26 -0
  33. package/src/test/interfaces/imap-config.ts +9 -0
  34. package/src/test/interfaces/imap-mailbox.ts +5 -0
  35. package/src/test/interfaces/mail-options.ts +20 -0
  36. package/src/test/interfaces/parsed-email.ts +11 -0
  37. package/src/test/interfaces/send-mail-result.ts +7 -0
  38. package/src/test/mocks/imap-mock.ts +147 -0
  39. package/src/test/mocks/mailparser-mock.ts +82 -0
  40. package/src/test/mocks/nodemailer-mock.ts +118 -0
  41. package/src/test/unit/email-receiver.unit.test.ts +471 -0
  42. package/src/test/unit/email-sender.unit.test.ts +550 -0
  43. package/tsconfig.json +23 -0
  44. package/email-receiver/email-receiver.js +0 -304
  45. package/email-sender/email-sender.js +0 -178
  46. package/examples/.gitkeep +0 -0
  47. package/processcube-html-to-text/processcube-html-to-text.js +0 -22
  48. package/test/helpers/email-receiver.mocks.js +0 -447
  49. package/test/helpers/email-sender.mocks.js +0 -368
  50. package/test/integration/email-receiver.integration.test.js +0 -515
  51. package/test/integration/email-sender.integration.test.js +0 -239
  52. package/test/unit/email-receiver.unit.test.js +0 -304
  53. package/test/unit/email-sender.unit.test.js +0 -570
  54. /package/{email-receiver → src/email-receiver}/email-receiver.html +0 -0
  55. /package/{email-sender → src/email-sender}/email-sender.html +0 -0
@@ -1,515 +0,0 @@
1
- const { expect } = require('chai');
2
- const helper = require('node-red-node-test-helper');
3
- const {
4
- createMockImap,
5
- createMockMailparser,
6
- setupModuleMocks,
7
- testConfigs,
8
- testFlows,
9
- testUtils,
10
- } = require('../helpers/email-receiver.mocks.js');
11
-
12
- describe('E-Mail Receiver Node - Integration Tests', function () {
13
- // Set a reasonable timeout for integration tests
14
- this.timeout(10000);
15
-
16
- let emailReceiverNode;
17
- let cleanupMocks;
18
-
19
- before(function (done) {
20
- // Set up mocks using helper
21
- cleanupMocks = setupModuleMocks();
22
-
23
- // Load the node with mocked dependencies
24
- emailReceiverNode = require('../../email-receiver/email-receiver.js');
25
-
26
- // CRITICAL: Initialize the helper with Node-RED
27
- helper.init(require.resolve('node-red'));
28
- done();
29
- });
30
-
31
- after(function () {
32
- // Clean up mocks using helper cleanup function
33
- if (cleanupMocks) {
34
- cleanupMocks();
35
- }
36
- });
37
-
38
- beforeEach(function (done) {
39
- helper.startServer(done);
40
- });
41
-
42
- afterEach(function (done) {
43
- helper.unload();
44
- helper.stopServer(done);
45
- });
46
-
47
- describe('Node Loading', function () {
48
- it('should load in Node-RED test environment', function (done) {
49
- // ARRANGE: Use test flow from helpers
50
- const flow = [testConfigs.valid];
51
-
52
- // ACT: Load the node in the test helper environment
53
- helper.load(emailReceiverNode, flow, function () {
54
- try {
55
- // ASSERT: Verify the node loaded correctly
56
- const n1 = helper.getNode(testConfigs.valid.id);
57
- expect(n1).to.exist;
58
- expect(n1).to.have.property('name', testConfigs.valid.name);
59
- expect(n1).to.have.property('type', 'email-receiver');
60
- done();
61
- } catch (err) {
62
- done(err);
63
- }
64
- });
65
- });
66
-
67
- it('should load with minimal configuration', function (done) {
68
- // ARRANGE: Use minimal test config from helpers
69
- const flow = [testConfigs.minimal];
70
-
71
- // ACT: Load the node
72
- helper.load(emailReceiverNode, flow, function () {
73
- try {
74
- // ASSERT: Verify the node loaded with minimal config
75
- const n1 = helper.getNode(testConfigs.minimal.id);
76
- expect(n1).to.exist;
77
- expect(n1).to.have.property('type', 'email-receiver');
78
- done();
79
- } catch (err) {
80
- done(err);
81
- }
82
- });
83
- });
84
-
85
- it('should load with array folders configuration', function (done) {
86
- // ARRANGE: Use array folders config from helpers
87
- const flow = [testConfigs.arrayFolders];
88
-
89
- // ACT: Load the node
90
- helper.load(emailReceiverNode, flow, function () {
91
- try {
92
- // ASSERT: Verify the node loaded with array folders
93
- const n1 = helper.getNode(testConfigs.arrayFolders.id);
94
- expect(n1).to.exist;
95
- expect(n1).to.have.property('name', testConfigs.arrayFolders.name);
96
- done();
97
- } catch (err) {
98
- done(err);
99
- }
100
- });
101
- });
102
- });
103
-
104
- describe('Node Connections', function () {
105
- it('should create wired connections correctly', function (done) {
106
- // ARRANGE: Use connected test flow from helpers
107
- const flow = testFlows.connected;
108
-
109
- // ACT: Load nodes and verify connections
110
- helper.load(emailReceiverNode, flow, function () {
111
- try {
112
- const n1 = helper.getNode(testConfigs.valid.id);
113
- const h1 = helper.getNode('h1');
114
-
115
- // ASSERT: Both nodes should exist and be connected
116
- expect(n1).to.exist;
117
- expect(h1).to.exist;
118
- expect(n1).to.have.property('name', testConfigs.valid.name);
119
- expect(h1).to.have.property('type', 'helper');
120
-
121
- done();
122
- } catch (err) {
123
- done(err);
124
- }
125
- });
126
- });
127
-
128
- it('should handle multiple output connections', function (done) {
129
- // ARRANGE: Use multi-output test flow from helpers
130
- const flow = testFlows.multiOutput;
131
-
132
- // ACT: Load nodes
133
- helper.load(emailReceiverNode, flow, function () {
134
- try {
135
- const n1 = helper.getNode(testConfigs.valid.id);
136
- const h1 = helper.getNode('h1');
137
- const h2 = helper.getNode('h2');
138
-
139
- // ASSERT: All nodes should exist
140
- expect(n1).to.exist;
141
- expect(h1).to.exist;
142
- expect(h2).to.exist;
143
- expect(n1).to.have.property('name', testConfigs.valid.name);
144
-
145
- done();
146
- } catch (err) {
147
- done(err);
148
- }
149
- });
150
- });
151
- });
152
-
153
- describe('Message Flow', function () {
154
- it('should handle input without crashing', async function () {
155
- // ARRANGE: Use test flow from helpers
156
- const flow = testFlows.single;
157
-
158
- return new Promise((resolve, reject) => {
159
- helper.load(emailReceiverNode, flow, function () {
160
- try {
161
- const n1 = helper.getNode(testConfigs.valid.id);
162
- expect(n1).to.exist;
163
-
164
- // Send input - this should not crash due to mocked IMAP
165
- n1.receive({ payload: 'test input' });
166
-
167
- // ASSERT: If we reach here, the node handled input gracefully
168
- testUtils.wait(500).then(() => {
169
- resolve(); // Success if no errors thrown
170
- });
171
- } catch (err) {
172
- reject(err);
173
- }
174
- });
175
- });
176
- });
177
-
178
- it('should process messages through connected nodes', function (done) {
179
- // ARRANGE: Use connected test flow from helpers
180
- const flow = testFlows.connected;
181
-
182
- // ACT: Load nodes and set up message listener
183
- helper.load(emailReceiverNode, flow, function () {
184
- try {
185
- const n1 = helper.getNode(testConfigs.valid.id);
186
- const h1 = helper.getNode('h1');
187
-
188
- // Set up listener for messages from email receiver
189
- h1.on('input', function (msg) {
190
- try {
191
- // ASSERT: Should receive a message with expected properties
192
- expect(msg).to.exist;
193
- expect(msg.payload).to.exist;
194
- done();
195
- } catch (err) {
196
- done(err);
197
- }
198
- });
199
-
200
- // Simulate the email processing
201
- // The email-receiver node likely starts processing emails automatically
202
- // Let's trigger the mock IMAP flow by simulating what happens when emails are found
203
- setTimeout(() => {
204
- // Simulate the email receiver processing emails and sending a message
205
- // This is what your email-receiver node should do internally
206
- try {
207
- const mockEmailMessage = {
208
- payload: 'This is a mock email body for testing purposes.',
209
- topic: 'email',
210
- from: 'sender@test.com',
211
- subject: 'Mock Email Subject',
212
- };
213
-
214
- // Directly send a message through the node (simulating internal processing)
215
- n1.send(mockEmailMessage);
216
- } catch (err) {
217
- done(err);
218
- }
219
- }, 100);
220
- } catch (err) {
221
- done(err);
222
- }
223
- });
224
- });
225
-
226
- it('should handle message timeout gracefully', async function () {
227
- // ARRANGE: Use connected test flow
228
- const flow = testFlows.connected;
229
-
230
- return new Promise((resolve, reject) => {
231
- helper.load(emailReceiverNode, flow, function () {
232
- try {
233
- const n1 = helper.getNode(testConfigs.valid.id);
234
- const h1 = helper.getNode('h1');
235
-
236
- // Use testUtils.waitForMessage with timeout
237
- testUtils
238
- .waitForMessage(h1, 1000)
239
- .then((msg) => {
240
- // ASSERT: Should receive message within timeout
241
- expect(msg).to.exist;
242
- resolve();
243
- })
244
- .catch((err) => {
245
- // ASSERT: Should handle timeout appropriately
246
- expect(err.message).to.include('Timeout waiting for message');
247
- resolve(); // This is expected behavior for this test
248
- });
249
-
250
- // Don't trigger anything to test timeout behavior
251
- // The timeout should occur as expected
252
- } catch (err) {
253
- reject(err);
254
- }
255
- });
256
- });
257
- });
258
-
259
- it('should process emails and send messages when emails are received', function (done) {
260
- const flow = testFlows.connected;
261
-
262
- helper.load(emailReceiverNode, flow, function () {
263
- try {
264
- const n1 = helper.getNode(testConfigs.valid.id);
265
- const h1 = helper.getNode('h1');
266
-
267
- h1.on('input', function (msg) {
268
- try {
269
- expect(msg).to.exist;
270
- expect(msg.payload).to.exist;
271
- expect(msg).to.have.property('subject');
272
- expect(msg).to.have.property('from');
273
- done();
274
- } catch (err) {
275
- done(err);
276
- }
277
- });
278
-
279
- // Simulate the email processing that would normally happen
280
- // when the IMAP connection finds new emails
281
- setTimeout(() => {
282
- // This simulates what your email-receiver node does internally
283
- // when it processes an email from IMAP
284
- const processedEmail = {
285
- payload: 'This is a mock email body for testing purposes.',
286
- subject: 'Mock Email Subject',
287
- from: { text: 'sender@test.com' },
288
- to: { text: 'recipient@test.com' },
289
- date: new Date(),
290
- messageId: '<mock-message-id@test.com>',
291
- };
292
-
293
- n1.send(processedEmail);
294
- }, 50);
295
- } catch (err) {
296
- done(err);
297
- }
298
- });
299
- });
300
- });
301
-
302
- describe('Configuration Validation', function () {
303
- it('should handle invalid configuration gracefully', function (done) {
304
- // ARRANGE: Use invalid config from helpers
305
- const flow = [testConfigs.invalidConfig];
306
-
307
- // ACT: Load node with invalid config
308
- helper.load(emailReceiverNode, flow, function () {
309
- try {
310
- const n1 = helper.getNode(testConfigs.invalidConfig.id);
311
- expect(n1).to.exist;
312
-
313
- // ASSERT: Node should exist but handle invalid config appropriately
314
- // Send input to trigger validation
315
- n1.receive({ payload: 'test' });
316
-
317
- // If we get here without crashing, the validation worked
318
- testUtils.wait(300).then(() => {
319
- done();
320
- });
321
- } catch (err) {
322
- done(err);
323
- }
324
- });
325
- });
326
-
327
- it('should validate folder configurations properly', async function () {
328
- // ARRANGE: Test different folder configurations
329
- const folderConfigs = [testConfigs.valid, testConfigs.arrayFolders];
330
-
331
- for (const config of folderConfigs) {
332
- await new Promise((resolve, reject) => {
333
- const flow = [config];
334
-
335
- helper.load(emailReceiverNode, flow, function () {
336
- try {
337
- const n1 = helper.getNode(config.id);
338
-
339
- // ASSERT: Node should load successfully with different folder configs
340
- expect(n1).to.exist;
341
- expect(n1).to.have.property('name', config.name);
342
-
343
- helper.unload();
344
- resolve();
345
- } catch (err) {
346
- reject(err);
347
- }
348
- });
349
- });
350
- }
351
- });
352
- });
353
-
354
- describe('Mock Integration Verification', function () {
355
- it('should work with createMockImap from helpers', function (done) {
356
- // ARRANGE: Create IMAP mock instance directly
357
- const mockImap = createMockImap();
358
- const imapInstance = new mockImap({
359
- host: testConfigs.valid.host,
360
- port: testConfigs.valid.port,
361
- secure: true,
362
- });
363
-
364
- // ACT: Test IMAP mock behavior
365
- let readyFired = false;
366
- imapInstance.once('ready', () => {
367
- readyFired = true;
368
-
369
- // Test openBox functionality
370
- imapInstance.openBox('INBOX', false, (err, box) => {
371
- expect(err).to.not.exist;
372
- expect(box).to.exist;
373
- expect(box).to.have.property('messages');
374
-
375
- // ASSERT: Mock IMAP should work as expected
376
- expect(readyFired).to.be.true;
377
- done();
378
- });
379
- });
380
-
381
- imapInstance.connect();
382
- });
383
-
384
- it('should work with createMockMailparser from helpers', async function () {
385
- // ARRANGE: Create mailparser mock instance directly
386
- const mockMailparser = createMockMailparser();
387
-
388
- // ACT: Test mailparser mock behavior
389
- const result = await mockMailparser.simpleParser('test content', {
390
- subject: 'Integration Test Email',
391
- from: 'integration@test.com',
392
- });
393
-
394
- // ASSERT: Mock mailparser should return expected structure
395
- expect(result).to.have.property('subject', 'Integration Test Email');
396
- expect(result).to.have.property('from');
397
- expect(result.from).to.have.property('text', 'integration@test.com');
398
- expect(result).to.have.property('headers');
399
- expect(result.headers).to.be.an.instanceOf(Map);
400
- });
401
- });
402
-
403
- describe('Node Lifecycle', function () {
404
- it('should clean up properly on unload', async function () {
405
- // ARRANGE: Use test flow from helpers
406
- const flow = testFlows.single;
407
-
408
- return new Promise((resolve, reject) => {
409
- helper.load(emailReceiverNode, flow, function () {
410
- try {
411
- const n1 = helper.getNode(testConfigs.valid.id);
412
- expect(n1).to.exist;
413
-
414
- // Simulate some activity
415
- n1.receive({ payload: 'test' });
416
-
417
- // Wait a bit for any async operations
418
- testUtils.wait(100).then(() => {
419
- // ASSERT: Unloading should not throw errors
420
- helper.unload();
421
- resolve();
422
- });
423
- } catch (err) {
424
- reject(err);
425
- }
426
- });
427
- });
428
- });
429
-
430
- it('should handle multiple load/unload cycles', async function () {
431
- // ARRANGE: Test multiple cycles
432
- const flow = testFlows.single;
433
- const cycles = 3;
434
-
435
- for (let i = 0; i < cycles; i++) {
436
- await new Promise((resolve, reject) => {
437
- helper.load(emailReceiverNode, flow, function () {
438
- try {
439
- const n1 = helper.getNode(testConfigs.valid.id);
440
- expect(n1).to.exist;
441
-
442
- // Quick activity simulation
443
- n1.receive({ payload: `test cycle ${i}` });
444
-
445
- testUtils.wait(50).then(() => {
446
- helper.unload();
447
- resolve();
448
- });
449
- } catch (err) {
450
- reject(err);
451
- }
452
- });
453
- });
454
- }
455
-
456
- // ASSERT: If we complete all cycles without error, lifecycle handling works
457
- // This assertion is implicit in the successful completion of the loop
458
- });
459
- });
460
-
461
- describe('Advanced Flow Testing', function () {
462
- it('should handle complex message flows with multiple helpers', function (done) {
463
- // ARRANGE: Use multi-output flow from helpers
464
- const flow = testFlows.multiOutput;
465
- let receivedMessages = [];
466
-
467
- helper.load(emailReceiverNode, flow, function () {
468
- try {
469
- const n1 = helper.getNode(testConfigs.valid.id);
470
- const h1 = helper.getNode('h1');
471
- const h2 = helper.getNode('h2');
472
-
473
- // Set up listeners for both helper nodes
474
- h1.on('input', function (msg) {
475
- receivedMessages.push({ node: 'h1', msg: msg });
476
- checkCompletion();
477
- });
478
-
479
- h2.on('input', function (msg) {
480
- receivedMessages.push({ node: 'h2', msg: msg });
481
- checkCompletion();
482
- });
483
-
484
- function checkCompletion() {
485
- if (receivedMessages.length >= 2) {
486
- // ASSERT: Both helpers should receive messages
487
- expect(receivedMessages.length).to.equal(2);
488
-
489
- receivedMessages.forEach((item) => {
490
- expect(item.msg).to.exist;
491
- expect(item.msg.payload).to.exist;
492
- });
493
-
494
- done();
495
- }
496
- }
497
-
498
- // Simulate email processing that sends to multiple outputs
499
- setTimeout(() => {
500
- const mockEmail = {
501
- payload: 'This is a mock email body for testing purposes.',
502
- subject: 'Multi-output Test',
503
- from: { text: 'multi@test.com' },
504
- };
505
-
506
- // Send the same message to both outputs (simulating multi-output behavior)
507
- n1.send([mockEmail, mockEmail]);
508
- }, 50);
509
- } catch (err) {
510
- done(err);
511
- }
512
- });
513
- });
514
- });
515
- });