UNPKG

15.6 kBJavaScriptView Raw
1const { OperationQueue } = require('../src/OperationQueue');
2const { Operation } = require('../src/Operation');
3const { OperationEvent } = require('../src/OperationEvent');
4const { QueuePriority } = require('../src/QueuePriority');
5const { TestOperation } = require('./TestOperation');
6
7describe('Operation', () => {
8
9 describe('function Operation', () => {
10
11 test('should be defined', () => {
12 expect(Operation).toBeDefined();
13 });
14
15 test('should be instantiated', () => {
16 const operation = new Operation();
17
18 expect(operation).toBeDefined();
19 });
20
21 test('inherits from Object', () => {
22 const operation = new Operation();
23
24 expect(operation.__proto__.__proto__.constructor.name).toBe('Object');
25 });
26
27 test('should be valid type', () => {
28 const operation = new Operation();
29
30 expect(operation.constructor.name).toBe('Operation');
31 });
32 });
33
34 describe('function constructor', () => {
35 test('it should accept an ID if only a number if passed as first argument', () => {
36 const operation = new Operation(1);
37
38 expect(operation.id).toBe(1);
39 });
40 });
41
42 describe('function start', function () {
43
44 test('must throw error when internal run function was never implemented', async (done) => {
45 const operation = new Operation();
46
47 try {
48 const result = await operation.start()
49 } catch (e) {
50 done();
51 }
52 });
53
54 test('start function must return a promise', () => {
55 const operation = new TestOperation();
56
57 const promise = operation.start()
58
59 expect(promise.constructor).toBe(Promise);
60 });
61
62 test('start function must return a promise', () => {
63 const operation = new TestOperation();
64
65 const promise = operation.start()
66
67 expect(promise.constructor).toBe(Promise);
68 });
69
70 test('start function success returns data set to result property', async (done) => {
71 const operation = new TestOperation();
72
73 try {
74 const result = await operation.start()
75
76 expect(operation.result).toBe(`my result - ${operation.id}`);
77 done();
78 } catch (e) {
79 // failled
80 }
81 });
82
83 test('start function returns data from promise', async (done) => {
84 const operation = new Operation();
85 operation.run = async () => { return 'my result' };
86
87 try {
88 const result = await operation.start()
89 expect(result).toBe('my result');
90 done();
91 } catch (e) {
92 // failled
93 }
94 });
95
96 test('when operation is executing, must set required properties', () => {
97 const operation = new TestOperation();
98
99 operation.start();
100
101 expect(operation.isExecuting).toBeTruthy();
102 });
103
104 test('should try to start when ready', () => {
105
106 });
107
108 test('should not try to start when already executing', () => {
109
110 });
111
112 test('should not try to start when already cancelled', () => {
113
114 });
115
116 test('should not try to start when already finished', () => {
117
118 });
119 });
120
121 describe('function cancel', () => {
122
123 test('when operation is cancelled, must set required properties', () => {
124 const operation = new TestOperation();
125 operation.start();
126
127 operation.cancel();
128
129 expect(operation.isCancelled).toBeTruthy();
130 });
131
132 test('when operation is cancelled, must set required private properties', () => {
133 const operation = new TestOperation();
134 operation.start();
135
136 operation.cancel();
137
138 expect(operation._cancelled).toBeTruthy();
139 });
140
141 test('when operation is cancelled, must resolve promise', async (done) => {
142 const operation = new TestOperation();
143
144 operation.start();
145 operation.cancel();
146 operation.start()
147 .then((result) => {
148 done();
149 });
150 });
151
152 test('event OperationEvent.CANCEL must be emitted when operation is cancelled', () => {
153 const emittedFunction = jest.fn(() => {});
154 const operation = new TestOperation();
155 operation.on(OperationEvent.CANCEL, emittedFunction);
156
157 operation.cancel();
158
159 expect(emittedFunction).toHaveBeenCalledWith(operation);
160 });
161 });
162
163 describe('when an operation is done', () => {
164
165 test('it must set required properties', async () => {
166 const operation = new TestOperation();
167
168 try {
169 await operation.start();
170 } catch (e) {
171 // fail
172 }
173
174 expect(operation.isDone()).toBeTruthy();
175 expect(operation.isFinished).toBeTruthy();
176 });
177
178 test('it must set private properties', async () => {
179 const operation = new TestOperation();
180
181 try {
182 await operation.start();
183 } catch (e) {
184 // fail
185 }
186
187 expect(operation._done).toBeTruthy();
188 });
189
190 test('completionCallback is called', async () => {
191 const completionCallback = jest.fn(() => {});
192 const operation = new TestOperation();
193 operation.completionCallback = completionCallback;
194 try {
195 await operation.start();
196 } catch (e) {
197 // fail
198 }
199
200 expect(completionCallback).toHaveBeenCalledWith(operation);
201 });
202 });
203
204 describe('event: OperationEvent.START', () => {
205 test('event must be emitted when operation has started', () => {
206 const emittedFunction = jest.fn(() => {});
207 const operation = new TestOperation();
208 operation.on(OperationEvent.START, emittedFunction);
209
210 operation.start();
211
212 expect(emittedFunction).toHaveBeenCalledWith(operation);
213 });
214 });
215
216 describe('event: OperationEvent.CANCEL', () => {
217 test('event must be emitted when operation has cancelled', () => {
218 const emittedFunction = jest.fn(() => {});
219 const operation = new TestOperation();
220 operation.on(OperationEvent.CANCEL, emittedFunction);
221
222 operation.cancel();
223
224 expect(emittedFunction).toHaveBeenCalledWith(operation);
225 });
226 });
227
228 describe('event: OperationEvent.READY', () => {
229 test('when in an OperationQueue, event must be emitted when operation is ready to be executed', () => {
230 const queue = new OperationQueue();
231
232 const emittedFunction = jest.fn(() => {});
233 const operation = new TestOperation();
234 operation.on(OperationEvent.READY, emittedFunction);
235
236 queue.addOperation(operation);
237
238 expect(emittedFunction).toHaveBeenCalledWith(operation);
239 });
240
241 test('when calling directly the start function outside an OperationQueue, event must NOT be emitted', () => {
242 const emittedFunction = jest.fn(() => {});
243 const operation = new TestOperation();
244 operation.on(OperationEvent.READY, emittedFunction);
245
246 operation.start();
247
248 expect(emittedFunction).not.toHaveBeenCalledWith(operation);
249 });
250 });
251
252 describe('event: OperationEvent.DONE', () => {
253 test('event must be emitted when operation is finished executing', async () => {
254 const emittedFunction = jest.fn(() => {});
255 const operation = new TestOperation();
256 operation.on(OperationEvent.DONE, emittedFunction);
257
258 try {
259 await operation.start();
260 } catch (e) {
261 //
262 }
263
264 expect(emittedFunction).toHaveBeenCalledWith(operation);
265 });
266 });
267
268 describe('event: OperationEvent.ERROR', () => {
269 test('event must be emitted when something went wrong during the opreations task', async () => {
270 const emittedFunction = jest.fn(() => {});
271 const operation = new Operation();
272 const error = new Error('test error');
273 operation.run = async () => { throw error };
274 operation.on(OperationEvent.ERROR, emittedFunction);
275
276 try {
277 await operation.start();
278 } catch (e) {
279 // fail
280 }
281
282 expect(emittedFunction).toHaveBeenCalled();
283 const { err, operation: returnedOperation } = emittedFunction.mock.calls[0][0];
284 expect(err).toEqual(error);
285 expect(returnedOperation).toEqual(operation);
286 });
287 });
288
289 describe('operation with dependencies', () => {
290 it('operation must start after all dependencies are resolved', async () => {
291 const operation1 = new TestOperation(1);
292 const operation2 = new TestOperation(2);
293 operation1.dependencies = [operation2];
294
295 let copyOperationState1 = null;
296 operation2.completionCallback = op => {
297 const { isCancelled, isExecuting, isFinished } = operation1;
298 copyOperationState1 = { isCancelled, isExecuting, isFinished };
299 }
300
301 const promise = operation1.start();
302
303 await promise;
304
305 expect(copyOperationState1.isExecuting).toBe(false);
306 expect(copyOperationState1.isFinished).toBe(false);
307 expect(copyOperationState1.isCancelled).toBe(false);
308
309 expect(operation1.isExecuting).toBe(true);
310 expect(operation2.isExecuting).toBe(true);
311
312 expect(operation1.isFinished).toBe(true);
313 expect(operation2.isFinished).toBe(true);
314 });
315 });
316
317 describe('function addDependency', () => {
318 it('should add operation dependency', async () => {
319 const operation1 = new TestOperation();
320 const operation2 = new TestOperation();
321
322 operation1.addDependency(operation2);
323
324 expect(operation1.dependencies.length).toBe(1);
325 expect(operation1.dependencies[0].id).toBe(operation2.id);
326 });
327 });
328
329 describe('function removeDependency', () => {
330 it('should remove operation dependency', async () => {
331 const operation1 = new TestOperation();
332 const operation2 = new TestOperation();
333 const operation3 = new TestOperation();
334 operation1.addDependency(operation2);
335 operation1.addDependency(operation3);
336
337 operation1.removeDependency(operation2);
338
339 expect(operation1.dependencies.length).toBe(1);
340 expect(operation1.dependencies[0].id).toBe(operation3.id);
341
342 operation1.removeDependency(operation3);
343
344 expect(operation1.dependencies.length).toBe(0);
345 expect(operation1.dependencies).toEqual([]);
346 });
347 });
348
349 describe('property dependencies', () => {
350 test('should return a clone when getting dependencies when executing', () => {
351 const operation1 = new TestOperation();
352 const operation2 = new TestOperation();
353 operation1.addDependency(operation2);
354
355 operation1.main();
356
357 let dependencies = operation1.dependencies;
358
359 expect(dependencies).not.toBe(operation1.dependencies);
360 });
361
362 test('should set dependencies', () => {
363 const operation1 = new TestOperation();
364 const operation2 = new TestOperation();
365
366 operation1.dependencies = [operation2];
367
368 expect(operation1.dependencies).toEqual([operation2]);
369 });
370
371 test('should not set dependencies when operation is executing', () => {
372 const operation1 = new TestOperation();
373 const operation2 = new TestOperation();
374 operation1.isExecuting = true;
375
376 operation1.dependencies = [operation2];
377
378 expect(operation1.dependencies).toEqual([]);
379 });
380
381 test('should not set dependencies when operation is cancelled', () => {
382 const operation1 = new TestOperation();
383 const operation2 = new TestOperation();
384
385 operation1.cancel();
386 operation1.dependencies = [operation2];
387
388 expect(operation1.dependencies).toEqual([]);
389 });
390
391 test('should not set dependencies when operation is finished', () => {
392 const operation1 = new TestOperation();
393 const operation2 = new TestOperation();
394
395 operation1.done()
396 operation1.dependencies = [operation2];
397
398 expect(operation1.dependencies).toEqual([]);
399 });
400 })
401
402 describe('function on', () => {
403 test('should add callback as a subscriber', async (done) => {
404 const operation1 = new TestOperation();
405
406 operation1.on(OperationEvent.START, () => {
407 done();
408 });
409 operation1.start();
410 });
411 });
412
413 describe('function off', () => {
414 test('should remove callback from subscriber', async (done) => {
415 const mockFunction = jest.fn(() => {
416 });
417 const operation1 = new TestOperation();
418 operation1.on(OperationEvent.START, mockFunction);
419
420 operation1.off(OperationEvent.START, mockFunction);
421 await operation1.start();
422
423 expect(mockFunction).not.toHaveBeenCalled();
424 done();
425 });
426 });
427
428 describe('property queuePriority', function () {
429 test('should set valid queue priority', () => {
430 const operation = new Operation();
431
432 operation.queuePriority = QueuePriority.high;
433
434 expect(operation.queuePriority).toBe(QueuePriority.high);
435 });
436
437 test('should not set queue priority while executing', () => {
438 const operation = new Operation();
439 operation.queuePriority = QueuePriority.high;
440
441 operation.isExecuting = true;
442 operation.queuePriority = QueuePriority.normal;
443
444 expect(operation.queuePriority).toBe(QueuePriority.high);
445 expect(operation.queuePriority).not.toBe(QueuePriority.normal);
446 });
447
448 test('should not set queue priority when cancelled', () => {
449 const operation = new Operation();
450 operation.queuePriority = QueuePriority.high;
451
452 operation.cancel();
453 operation.queuePriority = QueuePriority.normal;
454
455 expect(operation.queuePriority).toBe(QueuePriority.high);
456 expect(operation.queuePriority).not.toBe(QueuePriority.normal);
457 });
458
459 test('should not set queue priority when finished', () => {
460 const operation = new Operation();
461 operation.queuePriority = QueuePriority.high;
462
463 operation.done();
464 operation.queuePriority = QueuePriority.normal;
465
466 expect(operation.queuePriority).toBe(QueuePriority.high);
467 expect(operation.queuePriority).not.toBe(QueuePriority.normal);
468 });
469 });
470});
\No newline at end of file