@5minds/node-red-contrib-processcube-tools 1.2.0-feature-37541f-mg92jkdw → 1.2.0-feature-8f3d72-mg9cplxi
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/.env.template +7 -1
- package/email-receiver/email-receiver.js +304 -0
- package/email-sender/email-sender.js +178 -0
- package/examples/.gitkeep +0 -0
- package/file-storage/file-storage.html +203 -0
- package/file-storage/file-storage.js +148 -0
- package/package.json +17 -26
- package/{src/html-to-text/html-to-text.html → processcube-html-to-text/processcube-html-to-text.html} +3 -3
- package/processcube-html-to-text/processcube-html-to-text.js +22 -0
- package/storage/providers/fs.js +117 -0
- package/storage/providers/postgres.js +160 -0
- package/storage/storage-core.js +77 -0
- package/test/helpers/email-receiver.mocks.js +447 -0
- package/test/helpers/email-sender.mocks.js +368 -0
- package/test/integration/email-receiver.integration.test.js +515 -0
- package/test/integration/email-sender.integration.test.js +239 -0
- package/test/unit/email-receiver.unit.test.js +304 -0
- package/test/unit/email-sender.unit.test.js +570 -0
- package/.mocharc.json +0 -5
- package/src/custom-node-template/custom-node-template.html.template +0 -45
- package/src/custom-node-template/custom-node-template.ts.template +0 -69
- package/src/email-receiver/email-receiver.ts +0 -439
- package/src/email-sender/email-sender.ts +0 -210
- package/src/html-to-text/html-to-text.ts +0 -53
- package/src/index.ts +0 -12
- package/src/interfaces/EmailReceiverMessage.ts +0 -22
- package/src/interfaces/EmailSenderNodeProperties.ts +0 -37
- package/src/interfaces/FetchState.ts +0 -9
- package/src/interfaces/ImapConnectionConfig.ts +0 -14
- package/src/test/framework/advanced-test-patterns.ts +0 -224
- package/src/test/framework/generic-node-test-suite.ts +0 -58
- package/src/test/framework/index.ts +0 -17
- package/src/test/framework/integration-assertions.ts +0 -67
- package/src/test/framework/integration-scenario-builder.ts +0 -77
- package/src/test/framework/integration-test-runner.ts +0 -101
- package/src/test/framework/node-assertions.ts +0 -63
- package/src/test/framework/node-test-runner.ts +0 -260
- package/src/test/framework/test-scenario-builder.ts +0 -74
- package/src/test/framework/types.ts +0 -61
- package/src/test/helpers/email-receiver-test-configs.ts +0 -67
- package/src/test/helpers/email-receiver-test-flows.ts +0 -16
- package/src/test/helpers/email-sender-test-configs.ts +0 -123
- package/src/test/helpers/email-sender-test-flows.ts +0 -16
- package/src/test/integration/email-receiver.integration.test.ts +0 -41
- package/src/test/integration/email-sender.integration.test.ts +0 -129
- package/src/test/interfaces/email-data.ts +0 -10
- package/src/test/interfaces/email-receiver-config.ts +0 -12
- package/src/test/interfaces/email-sender-config.ts +0 -26
- package/src/test/interfaces/imap-config.ts +0 -9
- package/src/test/interfaces/imap-mailbox.ts +0 -5
- package/src/test/interfaces/mail-options.ts +0 -20
- package/src/test/interfaces/parsed-email.ts +0 -11
- package/src/test/interfaces/send-mail-result.ts +0 -7
- package/src/test/mocks/imap-mock.ts +0 -147
- package/src/test/mocks/mailparser-mock.ts +0 -82
- package/src/test/mocks/nodemailer-mock.ts +0 -118
- package/src/test/unit/email-receiver.unit.test.ts +0 -471
- package/src/test/unit/email-sender.unit.test.ts +0 -550
- package/tsconfig.json +0 -23
- /package/{src/email-receiver → email-receiver}/email-receiver.html +0 -0
- /package/{src/email-sender → email-sender}/email-sender.html +0 -0
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
const { expect } = require('chai');
|
|
2
|
+
const {
|
|
3
|
+
createMockNodeRED,
|
|
4
|
+
setupModuleMocks,
|
|
5
|
+
emailSenderConfigs,
|
|
6
|
+
createMockNodemailer,
|
|
7
|
+
} = require('../helpers/email-sender.mocks.js');
|
|
8
|
+
|
|
9
|
+
describe('E-Mail Sender Node - Unit Tests', function () {
|
|
10
|
+
this.timeout(10000);
|
|
11
|
+
|
|
12
|
+
let emailSenderNode;
|
|
13
|
+
let cleanupMocks;
|
|
14
|
+
|
|
15
|
+
before(function () {
|
|
16
|
+
// Set up module mocks using helper
|
|
17
|
+
cleanupMocks = setupModuleMocks();
|
|
18
|
+
|
|
19
|
+
// Load the node with mocked dependencies
|
|
20
|
+
emailSenderNode = require('../../email-sender/email-sender.js');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
after(function () {
|
|
24
|
+
// Clean up mocks
|
|
25
|
+
if (cleanupMocks) {
|
|
26
|
+
cleanupMocks();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// A separate describe block for module export
|
|
31
|
+
describe('Module Export', function () {
|
|
32
|
+
it('should export a function', function () {
|
|
33
|
+
expect(emailSenderNode).to.be.a('function');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('Node Registration', function () {
|
|
38
|
+
it('should register node type without errors', function () {
|
|
39
|
+
// ARRANGE: Create mock Node-RED with tracking
|
|
40
|
+
const mockRED = createMockNodeRED();
|
|
41
|
+
|
|
42
|
+
// ACT: Register the node
|
|
43
|
+
emailSenderNode(mockRED);
|
|
44
|
+
|
|
45
|
+
// ASSERT: Verify registration
|
|
46
|
+
expect(mockRED.nodes.lastRegisteredType).to.equal('email-sender');
|
|
47
|
+
expect(mockRED.nodes.lastRegisteredConstructor).to.be.a('function');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('Node Instantiation', function () {
|
|
52
|
+
it('should handle node instantiation with valid config', function () {
|
|
53
|
+
// ARRANGE: Track node creation
|
|
54
|
+
let createdNode = null;
|
|
55
|
+
const mockRED = createMockNodeRED({
|
|
56
|
+
onHandler: function (event, callback) {
|
|
57
|
+
createdNode = this;
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// ACT: Register and create node instance
|
|
62
|
+
emailSenderNode(mockRED);
|
|
63
|
+
new mockRED.nodes.lastRegisteredConstructor(emailSenderConfigs.valid);
|
|
64
|
+
|
|
65
|
+
// ASSERT: Verify node was created with correct properties
|
|
66
|
+
expect(createdNode).to.exist;
|
|
67
|
+
expect(createdNode).to.have.property('name', emailSenderConfigs.valid.name);
|
|
68
|
+
expect(createdNode).to.have.property('id', emailSenderConfigs.valid.id);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should handle minimal config', function () {
|
|
72
|
+
// ARRANGE: Use minimal test config
|
|
73
|
+
let createdNode = null;
|
|
74
|
+
const mockRED = createMockNodeRED({
|
|
75
|
+
onHandler: function (event, callback) {
|
|
76
|
+
createdNode = this;
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// ACT: Register and create node with minimal config
|
|
81
|
+
emailSenderNode(mockRED);
|
|
82
|
+
new mockRED.nodes.lastRegisteredConstructor(emailSenderConfigs.minimal);
|
|
83
|
+
|
|
84
|
+
// ASSERT: Verify node creation
|
|
85
|
+
expect(createdNode).to.exist;
|
|
86
|
+
expect(createdNode).to.have.property('id', emailSenderConfigs.minimal.id);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('Node Functionality', function () {
|
|
91
|
+
beforeEach(function () {
|
|
92
|
+
// Clear the module cache BEFORE requiring anything
|
|
93
|
+
delete require.cache[require.resolve('nodemailer')];
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
afterEach(function () {
|
|
97
|
+
delete require.cache[require.resolve('nodemailer')];
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should send email successfully and set status to "sent"', function (done) {
|
|
101
|
+
let statusSet = false;
|
|
102
|
+
|
|
103
|
+
// ARRANGE: Initialize mockNodemailer
|
|
104
|
+
const mockNodemailer = createMockNodemailer();
|
|
105
|
+
|
|
106
|
+
// Mock the nodemailer module
|
|
107
|
+
delete require.cache[require.resolve('nodemailer')];
|
|
108
|
+
require.cache[require.resolve('nodemailer')] = {
|
|
109
|
+
exports: mockNodemailer,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// ARRANGE: Create mock Node-RED environment
|
|
113
|
+
const mockRED = createMockNodeRED({
|
|
114
|
+
onHandler: function (event, callback) {
|
|
115
|
+
if (event === 'input') {
|
|
116
|
+
this.inputCallback = callback;
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
statusHandler: function (status) {
|
|
120
|
+
if (status.fill === 'green') {
|
|
121
|
+
expect(status.text).to.include('sent');
|
|
122
|
+
expect(status.shape).to.equal('dot');
|
|
123
|
+
statusSet = true;
|
|
124
|
+
done();
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
errorHandler: function (err) {
|
|
128
|
+
done(err || new Error('Unexpected error handler called'));
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// ACT: Initialize the email sender node
|
|
133
|
+
const emailSenderNode = require('../../email-sender/email-sender.js');
|
|
134
|
+
emailSenderNode(mockRED);
|
|
135
|
+
|
|
136
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
137
|
+
const nodeInstance = new nodeConstructor(emailSenderConfigs.valid);
|
|
138
|
+
|
|
139
|
+
nodeInstance.inputCallback({
|
|
140
|
+
payload: 'test',
|
|
141
|
+
topic: 'test message',
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should handle sendMail error and set status to "error sending"', function (done) {
|
|
146
|
+
let errorHandlerCalled = false;
|
|
147
|
+
let redStatusSet = false;
|
|
148
|
+
|
|
149
|
+
function checkDone() {
|
|
150
|
+
if (errorHandlerCalled && redStatusSet) {
|
|
151
|
+
done();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Explicitly set shouldFail to true
|
|
156
|
+
const mockOptions = { shouldFail: true };
|
|
157
|
+
const mockNodemailer = createMockNodemailer(mockOptions);
|
|
158
|
+
|
|
159
|
+
// Mock the nodemailer module
|
|
160
|
+
delete require.cache[require.resolve('nodemailer')];
|
|
161
|
+
require.cache[require.resolve('nodemailer')] = {
|
|
162
|
+
exports: mockNodemailer,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const mockRED = createMockNodeRED({
|
|
166
|
+
onHandler: function (event, callback) {
|
|
167
|
+
if (event === 'input') {
|
|
168
|
+
this.inputCallback = callback;
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
statusHandler: function (status) {
|
|
172
|
+
if (status.fill === 'red' && status.text === 'error sending') {
|
|
173
|
+
redStatusSet = true;
|
|
174
|
+
checkDone();
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
errorHandler: function (err) {
|
|
178
|
+
expect(err.message).to.equal('Mock sendMail error');
|
|
179
|
+
errorHandlerCalled = true;
|
|
180
|
+
checkDone();
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const emailSenderNode = require('../../email-sender/email-sender.js');
|
|
185
|
+
emailSenderNode(mockRED);
|
|
186
|
+
|
|
187
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
188
|
+
const nodeInstance = new nodeConstructor(emailSenderConfigs.valid);
|
|
189
|
+
|
|
190
|
+
nodeInstance.inputCallback({
|
|
191
|
+
payload: 'test',
|
|
192
|
+
topic: 'test message',
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should handle an array of attachments correctly', function (done) {
|
|
197
|
+
let attachmentsChecked = false;
|
|
198
|
+
let statusSet = false;
|
|
199
|
+
|
|
200
|
+
function checkDone() {
|
|
201
|
+
if (attachmentsChecked && statusSet) {
|
|
202
|
+
done();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ARRANGE: Configure test attachments
|
|
207
|
+
const attachments = [
|
|
208
|
+
{
|
|
209
|
+
filename: 'test1.txt',
|
|
210
|
+
content: 'This is the first test file.',
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
filename: 'test2.txt',
|
|
214
|
+
content: 'This is the second test file.',
|
|
215
|
+
},
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
const mockNodemailer = createMockNodemailer({
|
|
219
|
+
onSendMail: (mailOptions) => {
|
|
220
|
+
expect(mailOptions.attachments).to.be.an('array').with.lengthOf(2);
|
|
221
|
+
expect(mailOptions.attachments[0].filename).to.equal('test1.txt');
|
|
222
|
+
expect(mailOptions.attachments[1].content).to.equal('This is the second test file.');
|
|
223
|
+
attachmentsChecked = true;
|
|
224
|
+
checkDone();
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Mock the nodemailer module
|
|
229
|
+
delete require.cache[require.resolve('nodemailer')];
|
|
230
|
+
require.cache[require.resolve('nodemailer')] = {
|
|
231
|
+
exports: mockNodemailer,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const mockRED = createMockNodeRED({
|
|
235
|
+
onHandler: function (event, callback) {
|
|
236
|
+
if (event === 'input') {
|
|
237
|
+
this.inputCallback = callback;
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
statusHandler: function (status) {
|
|
241
|
+
if (status.fill === 'green') {
|
|
242
|
+
expect(status.text).to.include('sent');
|
|
243
|
+
expect(status.shape).to.equal('dot');
|
|
244
|
+
statusSet = true;
|
|
245
|
+
checkDone();
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
errorHandler: function (err) {
|
|
249
|
+
done(err || new Error('Unexpected error handler called'));
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const emailSenderNode = require('../../email-sender/email-sender.js');
|
|
254
|
+
emailSenderNode(mockRED);
|
|
255
|
+
|
|
256
|
+
const config = { ...emailSenderConfigs.valid };
|
|
257
|
+
config.attachments = JSON.stringify(attachments);
|
|
258
|
+
config.attachmentsType = 'json';
|
|
259
|
+
|
|
260
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
261
|
+
const nodeInstance = new nodeConstructor(config);
|
|
262
|
+
|
|
263
|
+
setTimeout(() => {
|
|
264
|
+
nodeInstance.inputCallback({
|
|
265
|
+
payload: 'test',
|
|
266
|
+
topic: 'test message',
|
|
267
|
+
});
|
|
268
|
+
}, 100);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should throw error for malformed attachments', function (done) {
|
|
272
|
+
let errorHandlerCalled = false;
|
|
273
|
+
let redStatusSet = false;
|
|
274
|
+
|
|
275
|
+
function checkDone() {
|
|
276
|
+
if (errorHandlerCalled && redStatusSet) {
|
|
277
|
+
done();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ARRANGE: Configure the node with a JSON string containing a malformed attachment
|
|
282
|
+
const malformedAttachments = [
|
|
283
|
+
{
|
|
284
|
+
filename: 'test.txt',
|
|
285
|
+
content: 'This is a test file.',
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
// Malformed attachment with missing content
|
|
289
|
+
filename: 'invalid.txt',
|
|
290
|
+
},
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
const mockNodemailer = createMockNodemailer();
|
|
294
|
+
|
|
295
|
+
// Mock the nodemailer module
|
|
296
|
+
delete require.cache[require.resolve('nodemailer')];
|
|
297
|
+
require.cache[require.resolve('nodemailer')] = {
|
|
298
|
+
exports: mockNodemailer,
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const mockRED = createMockNodeRED({
|
|
302
|
+
onHandler: function (event, callback) {
|
|
303
|
+
if (event === 'input') {
|
|
304
|
+
this.inputCallback = callback;
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
statusHandler: function (status) {
|
|
308
|
+
if (status.fill === 'red') {
|
|
309
|
+
redStatusSet = true;
|
|
310
|
+
checkDone();
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
errorHandler: function (err) {
|
|
314
|
+
expect(err).to.equal("Attachment object is missing 'filename' or 'content' property.");
|
|
315
|
+
errorHandlerCalled = true;
|
|
316
|
+
checkDone();
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const emailSenderNode = require('../../email-sender/email-sender.js');
|
|
321
|
+
emailSenderNode(mockRED);
|
|
322
|
+
|
|
323
|
+
const config = { ...emailSenderConfigs.valid };
|
|
324
|
+
config.attachments = JSON.stringify(malformedAttachments);
|
|
325
|
+
config.attachmentsType = 'json';
|
|
326
|
+
|
|
327
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
328
|
+
const nodeInstance = new nodeConstructor(config);
|
|
329
|
+
|
|
330
|
+
nodeInstance.inputCallback({
|
|
331
|
+
payload: 'test',
|
|
332
|
+
topic: 'test message',
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('should handle rejected emails and set status to rejected', function (done) {
|
|
337
|
+
let errorHandlerCalled = false;
|
|
338
|
+
let redStatusSet = false;
|
|
339
|
+
|
|
340
|
+
function checkDone() {
|
|
341
|
+
if (errorHandlerCalled && redStatusSet) {
|
|
342
|
+
done();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ARRANGE: Configure mock to simulate rejected emails
|
|
347
|
+
const mockOptions = {
|
|
348
|
+
rejectedEmails: ['recipient@example.com'],
|
|
349
|
+
acceptedEmails: [], // Ensure no emails are accepted
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const mockNodemailer = createMockNodemailer(mockOptions);
|
|
353
|
+
|
|
354
|
+
// Mock the nodemailer module
|
|
355
|
+
delete require.cache[require.resolve('nodemailer')];
|
|
356
|
+
require.cache[require.resolve('nodemailer')] = {
|
|
357
|
+
exports: mockNodemailer,
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const mockRED = createMockNodeRED({
|
|
361
|
+
onHandler: function (event, callback) {
|
|
362
|
+
if (event === 'input') {
|
|
363
|
+
this.inputCallback = callback;
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
statusHandler: function (status) {
|
|
367
|
+
if (status.fill === 'red' && status.text === 'rejected') {
|
|
368
|
+
redStatusSet = true;
|
|
369
|
+
checkDone();
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
errorHandler: function (err) {
|
|
373
|
+
expect(err.message).to.include('Email rejected: recipient@example.com');
|
|
374
|
+
errorHandlerCalled = true;
|
|
375
|
+
checkDone();
|
|
376
|
+
},
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
const emailSenderNode = require('../../email-sender/email-sender.js');
|
|
380
|
+
emailSenderNode(mockRED);
|
|
381
|
+
|
|
382
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
383
|
+
const nodeInstance = new nodeConstructor(emailSenderConfigs.valid);
|
|
384
|
+
|
|
385
|
+
nodeInstance.inputCallback({
|
|
386
|
+
payload: 'test',
|
|
387
|
+
topic: 'test message',
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('should handle pending emails and set status to pending', function (done) {
|
|
392
|
+
let errorHandlerCalled = false;
|
|
393
|
+
let yellowStatusSet = false;
|
|
394
|
+
|
|
395
|
+
function checkDone() {
|
|
396
|
+
if (errorHandlerCalled && yellowStatusSet) {
|
|
397
|
+
done();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// ARRANGE: Configure mock to simulate pending emails
|
|
402
|
+
const mockOptions = {
|
|
403
|
+
pendingEmails: ['recipient@example.com'],
|
|
404
|
+
acceptedEmails: [], // Ensure no emails are accepted
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const mockNodemailer = createMockNodemailer(mockOptions);
|
|
408
|
+
|
|
409
|
+
// Mock the nodemailer module
|
|
410
|
+
delete require.cache[require.resolve('nodemailer')];
|
|
411
|
+
require.cache[require.resolve('nodemailer')] = {
|
|
412
|
+
exports: mockNodemailer,
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
const mockRED = createMockNodeRED({
|
|
416
|
+
onHandler: function (event, callback) {
|
|
417
|
+
if (event === 'input') {
|
|
418
|
+
this.inputCallback = callback;
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
statusHandler: function (status) {
|
|
422
|
+
if (status.fill === 'yellow' && status.text === 'pending') {
|
|
423
|
+
yellowStatusSet = true;
|
|
424
|
+
checkDone();
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
errorHandler: function (err) {
|
|
428
|
+
expect(err.message).to.include('Email pending: recipient@example.com');
|
|
429
|
+
errorHandlerCalled = true;
|
|
430
|
+
checkDone();
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
const emailSenderNode = require('../../email-sender/email-sender.js');
|
|
435
|
+
emailSenderNode(mockRED);
|
|
436
|
+
|
|
437
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
438
|
+
const nodeInstance = new nodeConstructor(emailSenderConfigs.valid);
|
|
439
|
+
|
|
440
|
+
nodeInstance.inputCallback({
|
|
441
|
+
payload: 'test',
|
|
442
|
+
topic: 'test message',
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it('should handle a single attachment object correctly', function (done) {
|
|
447
|
+
let attachmentChecked = false;
|
|
448
|
+
let statusSet = false;
|
|
449
|
+
|
|
450
|
+
function checkDone() {
|
|
451
|
+
if (attachmentChecked && statusSet) {
|
|
452
|
+
done();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// ARRANGE: Configure test with single attachment object
|
|
457
|
+
const singleAttachment = {
|
|
458
|
+
filename: 'single-test.txt',
|
|
459
|
+
content: 'This is a single test file.',
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
const mockNodemailer = createMockNodemailer({
|
|
463
|
+
onSendMail: (mailOptions) => {
|
|
464
|
+
expect(mailOptions.attachments).to.be.an('array').with.lengthOf(1);
|
|
465
|
+
expect(mailOptions.attachments[0].filename).to.equal('single-test.txt');
|
|
466
|
+
expect(mailOptions.attachments[0].content).to.equal('This is a single test file.');
|
|
467
|
+
attachmentChecked = true;
|
|
468
|
+
checkDone();
|
|
469
|
+
},
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// Mock the nodemailer module
|
|
473
|
+
delete require.cache[require.resolve('nodemailer')];
|
|
474
|
+
require.cache[require.resolve('nodemailer')] = {
|
|
475
|
+
exports: mockNodemailer,
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const mockRED = createMockNodeRED({
|
|
479
|
+
onHandler: function (event, callback) {
|
|
480
|
+
if (event === 'input') {
|
|
481
|
+
this.inputCallback = callback;
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
statusHandler: function (status) {
|
|
485
|
+
if (status.fill === 'green') {
|
|
486
|
+
expect(status.text).to.include('sent');
|
|
487
|
+
expect(status.shape).to.equal('dot');
|
|
488
|
+
statusSet = true;
|
|
489
|
+
checkDone();
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
errorHandler: function (err) {
|
|
493
|
+
done(err || new Error('Unexpected error handler called'));
|
|
494
|
+
},
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const emailSenderNode = require('../../email-sender/email-sender.js');
|
|
498
|
+
emailSenderNode(mockRED);
|
|
499
|
+
|
|
500
|
+
const config = { ...emailSenderConfigs.valid };
|
|
501
|
+
config.attachments = JSON.stringify(singleAttachment);
|
|
502
|
+
config.attachmentsType = 'json';
|
|
503
|
+
|
|
504
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
505
|
+
const nodeInstance = new nodeConstructor(config);
|
|
506
|
+
|
|
507
|
+
setTimeout(() => {
|
|
508
|
+
nodeInstance.inputCallback({
|
|
509
|
+
payload: 'test',
|
|
510
|
+
topic: 'test message',
|
|
511
|
+
});
|
|
512
|
+
}, 100);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
it('should handle an empty attachments string without error', function (done) {
|
|
516
|
+
let statusSet = false;
|
|
517
|
+
|
|
518
|
+
// ARRANGE: Create mock nodemailer to verify no attachments are processed
|
|
519
|
+
const mockNodemailer = createMockNodemailer({
|
|
520
|
+
onSendMail: (mailOptions) => {
|
|
521
|
+
// Should be an empty array when no attachments are provided
|
|
522
|
+
expect(mailOptions.attachments).to.be.an('array').with.lengthOf(0);
|
|
523
|
+
},
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
// Mock the nodemailer module
|
|
527
|
+
delete require.cache[require.resolve('nodemailer')];
|
|
528
|
+
require.cache[require.resolve('nodemailer')] = {
|
|
529
|
+
exports: mockNodemailer,
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
const mockRED = createMockNodeRED({
|
|
533
|
+
onHandler: function (event, callback) {
|
|
534
|
+
if (event === 'input') {
|
|
535
|
+
this.inputCallback = callback;
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
statusHandler: function (status) {
|
|
539
|
+
if (status.fill === 'green') {
|
|
540
|
+
expect(status.text).to.include('sent');
|
|
541
|
+
expect(status.shape).to.equal('dot');
|
|
542
|
+
statusSet = true;
|
|
543
|
+
done();
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
errorHandler: function (err) {
|
|
547
|
+
done(err || new Error('Unexpected error handler called'));
|
|
548
|
+
},
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
const emailSenderNode = require('../../email-sender/email-sender.js');
|
|
552
|
+
emailSenderNode(mockRED);
|
|
553
|
+
|
|
554
|
+
const config = { ...emailSenderConfigs.valid };
|
|
555
|
+
// Set attachments to empty string to test this scenario
|
|
556
|
+
config.attachments = '';
|
|
557
|
+
config.attachmentsType = 'str';
|
|
558
|
+
|
|
559
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
560
|
+
const nodeInstance = new nodeConstructor(config);
|
|
561
|
+
|
|
562
|
+
setTimeout(() => {
|
|
563
|
+
nodeInstance.inputCallback({
|
|
564
|
+
payload: 'test',
|
|
565
|
+
topic: 'test message',
|
|
566
|
+
});
|
|
567
|
+
}, 100);
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
});
|
package/.mocharc.json
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
<script type="text/javascript">
|
|
2
|
-
RED.nodes.registerType('my-new-node-name', {
|
|
3
|
-
category: 'ProcessCube Tools',
|
|
4
|
-
color: '#02AFD6',
|
|
5
|
-
defaults: {
|
|
6
|
-
name: { value: '' },
|
|
7
|
-
// Füge hier deine Konfigurations-Eigenschaften hinzu
|
|
8
|
-
// Beispiel:
|
|
9
|
-
// myProperty: { value: 'standardwert', required: true, validate: RED.validators.typedInput('myPropertyType') },
|
|
10
|
-
// myPropertyType: { value: 'str' }
|
|
11
|
-
},
|
|
12
|
-
inputs: 1, // Anzahl der Eingangsports
|
|
13
|
-
outputs: 1, // Anzahl der Ausgangsports
|
|
14
|
-
icon: 'font-awesome/fa-tag', // Symbol für die Node
|
|
15
|
-
label: function () {
|
|
16
|
-
return this.name || 'Meine Neue Node';
|
|
17
|
-
},
|
|
18
|
-
oneditprepare: function () {
|
|
19
|
-
// Hier konfigurierst du die TypedInput-Felder, die du im defaults-Block definiert hast.
|
|
20
|
-
// $('#node-input-myProperty').typedInput({
|
|
21
|
-
// default: 'str',
|
|
22
|
-
// types: ['str', 'msg', 'flow', 'global', 'env'],
|
|
23
|
-
// typeField: '#node-input-myPropertyType',
|
|
24
|
-
// });
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
</script>
|
|
28
|
-
|
|
29
|
-
<script type="text/html" data-template-name="my-new-node-name">
|
|
30
|
-
<div class="form-row">
|
|
31
|
-
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
32
|
-
<input type="text" id="node-input-name" placeholder="Name" />
|
|
33
|
-
</div>
|
|
34
|
-
|
|
35
|
-
</script>
|
|
36
|
-
|
|
37
|
-
<script type="text/html" data-help-name="my-new-node-name">
|
|
38
|
-
<p>Eine einfache Node-RED-Node-Vorlage, die eine grundlegende Struktur für neue Nodes bietet.</p>
|
|
39
|
-
|
|
40
|
-
<h3>Konfiguration</h3>
|
|
41
|
-
<dl class="message-properties">
|
|
42
|
-
<dt>Name</dt>
|
|
43
|
-
<dd>Der Name deiner Node, wie er im Flow angezeigt wird.</dd>
|
|
44
|
-
</dl>
|
|
45
|
-
</script>
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { NodeInitializer, Node, NodeDef, NodeMessage } from 'node-red';
|
|
2
|
-
// Wenn du eine externe Bibliothek benötigst, importiere sie hier.
|
|
3
|
-
// const myExternalLibrary = require('my-external-library');
|
|
4
|
-
|
|
5
|
-
// Definiere ein Interface für die Konfigurations-Eigenschaften deines Nodes.
|
|
6
|
-
// Ersetze 'MyNewNode' durch den Namen deiner Node.
|
|
7
|
-
interface MyNewNodeProperties extends NodeDef {
|
|
8
|
-
// Füge hier deine benutzerdefinierten Konfigurationseigenschaften hinzu.
|
|
9
|
-
// Zum Beispiel:
|
|
10
|
-
// myConfigProperty: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Definiere ein Interface für die Nachrichten-Payload, falls sie einen bestimmten Typ haben muss.
|
|
14
|
-
// Ersetze 'MyNewNode' durch den Namen deiner Node.
|
|
15
|
-
interface MyNewNodeMessage extends NodeMessage {
|
|
16
|
-
payload: any; // Ändere 'any' in den erwarteten Typ (z.B. 'string', 'number').
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Die Initialisierungsfunktion, die von Node-RED geladen wird.
|
|
20
|
-
// Ersetze 'MyNewNode' durch den Namen deiner Node.
|
|
21
|
-
const MyNewNode: NodeInitializer = function(RED) {
|
|
22
|
-
// Die Hauptfunktion für deine Node.
|
|
23
|
-
// Ersetze 'MyNewNode' durch den Namen deiner Node.
|
|
24
|
-
function MyNewNode(this: Node, config: MyNewNodeProperties) {
|
|
25
|
-
RED.nodes.createNode(this, config);
|
|
26
|
-
const node = this;
|
|
27
|
-
|
|
28
|
-
// Speichere die Konfiguration in der Node-Instanz.
|
|
29
|
-
// node.myConfigProperty = config.myConfigProperty;
|
|
30
|
-
|
|
31
|
-
// Registriere einen Listener für eingehende Nachrichten.
|
|
32
|
-
(node as any).on("input", (msg: MyNewNodeMessage, send?: Function, done?: Function) => {
|
|
33
|
-
// Stelle sicher, dass 'send' und 'done' verfügbar sind.
|
|
34
|
-
send = send || function(m: NodeMessage | NodeMessage[]) { node.send(m); };
|
|
35
|
-
done = done || function(err?: Error) { if (err) node.error(err, msg); };
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
// --- Hier kommt deine eigentliche Node-Logik rein ---
|
|
39
|
-
// Beispiel:
|
|
40
|
-
// if (typeof msg.payload === 'string') {
|
|
41
|
-
// msg.payload = "Hallo, das ist eine neue Node!";
|
|
42
|
-
// }
|
|
43
|
-
// --------------------------------------------------
|
|
44
|
-
|
|
45
|
-
// Sende die Nachricht weiter an die nächste Node.
|
|
46
|
-
send(msg);
|
|
47
|
-
|
|
48
|
-
// Signalisiere, dass die Verarbeitung abgeschlossen ist.
|
|
49
|
-
done();
|
|
50
|
-
|
|
51
|
-
} catch (error) {
|
|
52
|
-
// Bei einem Fehler, rufe `done` mit dem Fehlerobjekt auf.
|
|
53
|
-
done(error instanceof Error ? error : new Error(String(error)));
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// Optional: Füge hier einen 'close'-Handler hinzu, um Ressourcen freizugeben.
|
|
58
|
-
// (node as any).on("close", (done) => {
|
|
59
|
-
// // Aufräumarbeiten (z.B. Verbindung schließen)
|
|
60
|
-
// done();
|
|
61
|
-
// });
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Registriere den Node-Typ bei Node-RED.
|
|
65
|
-
// Der String hier muss dem Namen in deiner .html-Datei entsprechen.
|
|
66
|
-
RED.nodes.registerType('my-new-node-name', MyNewNode);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export = MyNewNode;
|