@5minds/node-red-contrib-processcube-tools 1.0.1-develop-b4cfca-mfdomgtt → 1.0.1-feature-b241c2-mfdq69jq
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-receiver/email-receiver.html +187 -0
- package/email-receiver/email-receiver.js +231 -0
- package/package.json +26 -3
- package/test/helpers/email-receiver.mocks.js +379 -0
- package/test/integration/email-receiver.integration.test.js +492 -0
- package/test/unit/email-receiver.unit.test.js +300 -0
|
@@ -0,0 +1,300 @@
|
|
|
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: 'str',
|
|
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
|
+
const mockRED = {
|
|
205
|
+
nodes: {
|
|
206
|
+
createNode: function(node, config) {
|
|
207
|
+
nodeInstance = node;
|
|
208
|
+
node.on = (event, callback) => { if (event === 'input') inputCallback = callback; };
|
|
209
|
+
node.status = () => {};
|
|
210
|
+
node.error = () => {};
|
|
211
|
+
node.send = (msg) => {
|
|
212
|
+
should.exist(msg);
|
|
213
|
+
msg.payload.should.equal('test body');
|
|
214
|
+
done();
|
|
215
|
+
};
|
|
216
|
+
return node;
|
|
217
|
+
},
|
|
218
|
+
registerType: (type, constructor) => {
|
|
219
|
+
new constructor({
|
|
220
|
+
host: "imap.test.com", hostType: "str",
|
|
221
|
+
port: 993, portType: "num",
|
|
222
|
+
user: "test@test.com", userType: "str",
|
|
223
|
+
password: "testpass", passwordType: "str",
|
|
224
|
+
folder: ["INBOX", "Junk"], folderType: 'json',
|
|
225
|
+
markseen: true, markseenType: 'bool'
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
util: { evaluateNodeProperty: (value) => value },
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// ACT: Register the node, then simulate input
|
|
233
|
+
emailReceiverNode(mockRED);
|
|
234
|
+
inputCallback({});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe('Error Handling', function() {
|
|
239
|
+
it('should call node.error for invalid folder type', function(done) {
|
|
240
|
+
// ARRANGE: Mock the node instance to capture errors
|
|
241
|
+
let errorCalled = false;
|
|
242
|
+
const nodeInstance = {
|
|
243
|
+
config: { folder: 123, folderType: 'num' },
|
|
244
|
+
on: (event, callback) => { if (event === 'input') nodeInstance.inputCallback = callback; },
|
|
245
|
+
status: () => {},
|
|
246
|
+
error: (err) => {
|
|
247
|
+
errorCalled = true;
|
|
248
|
+
err.should.containEql('The \'folders\' property must be an array of strings');
|
|
249
|
+
done();
|
|
250
|
+
},
|
|
251
|
+
send: () => {},
|
|
252
|
+
};
|
|
253
|
+
const mockRED = {
|
|
254
|
+
nodes: {
|
|
255
|
+
createNode: (node, config) => Object.assign(node, { on: nodeInstance.on, status: nodeInstance.status, error: nodeInstance.error, send: nodeInstance.send }),
|
|
256
|
+
registerType: (type, constructor) => new constructor(nodeInstance.config),
|
|
257
|
+
},
|
|
258
|
+
util: { evaluateNodeProperty: (value, type) => value },
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// ACT: Register and instantiate the node, then simulate an input message
|
|
262
|
+
emailReceiverNode(mockRED);
|
|
263
|
+
nodeInstance.inputCallback({});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should call node.error for missing config', function(done) {
|
|
267
|
+
// ARRANGE: Mock the node instance to capture errors
|
|
268
|
+
let errorCalled = false;
|
|
269
|
+
let statusCalled = false;
|
|
270
|
+
const nodeInstance = {
|
|
271
|
+
config: {
|
|
272
|
+
host: "imap.test.com", hostType: "str",
|
|
273
|
+
port: 993, portType: "num",
|
|
274
|
+
user: "test@test.com", userType: "str",
|
|
275
|
+
password: "", passwordType: "str", // Empty password should trigger error
|
|
276
|
+
folder: "INBOX", folderType: "str"
|
|
277
|
+
},
|
|
278
|
+
on: (event, callback) => { if (event === 'input') nodeInstance.inputCallback = callback; },
|
|
279
|
+
status: (s) => { statusCalled = true; s.fill.should.equal('red'); },
|
|
280
|
+
error: (err) => {
|
|
281
|
+
errorCalled = true;
|
|
282
|
+
err.should.containEql('Missing required IMAP config');
|
|
283
|
+
done();
|
|
284
|
+
},
|
|
285
|
+
send: () => {},
|
|
286
|
+
};
|
|
287
|
+
const mockRED = {
|
|
288
|
+
nodes: {
|
|
289
|
+
createNode: (node, config) => Object.assign(node, { on: nodeInstance.on, status: nodeInstance.status, error: nodeInstance.error, send: nodeInstance.send }),
|
|
290
|
+
registerType: (type, constructor) => new constructor(nodeInstance.config),
|
|
291
|
+
},
|
|
292
|
+
util: { evaluateNodeProperty: (value, type) => value },
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
// ACT: Register and instantiate the node, then simulate an input message
|
|
296
|
+
emailReceiverNode(mockRED);
|
|
297
|
+
nodeInstance.inputCallback({});
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
});
|