1 | const { OperationQueue } = require('../src/OperationQueue');
|
2 | const { QueueEvent } = require('../src/QueueEvent');
|
3 | const { TestOperation, TimeOutOperation } = require('./TestOperation');
|
4 | const { QueuePriority } = require('../src/QueuePriority');
|
5 | const { OperationEvent } = require('../src/OperationEvent');
|
6 | const { delay, nextTick } = require('./utils');
|
7 |
|
8 | describe('OperationQueue', () => {
|
9 |
|
10 | describe('function OperationQueue', function () {
|
11 | test('should be defined', () => {
|
12 | expect(OperationQueue).toBeDefined();
|
13 | });
|
14 |
|
15 | test('should be instantiated', () => {
|
16 | const operationQueue = new OperationQueue(1);
|
17 |
|
18 | expect(operationQueue).toBeDefined();
|
19 | });
|
20 |
|
21 | test('inherits from Operation', () => {
|
22 | const operationQueue = new OperationQueue(1);
|
23 |
|
24 | expect(operationQueue.__proto__.__proto__.constructor.name).toBe('Object');
|
25 | });
|
26 |
|
27 | test('should be valid type', () => {
|
28 | const operationQueue = new OperationQueue(1);
|
29 |
|
30 | expect(operationQueue.constructor.name).toBe('OperationQueue');
|
31 | });
|
32 | });
|
33 |
|
34 | describe('function addOperation', () => {
|
35 | test('should add a single operation to its operations prop', () => {
|
36 | const operationQueue = new OperationQueue(1);
|
37 | const operation1 = new TestOperation();
|
38 |
|
39 | operationQueue.addOperation(operation1);
|
40 |
|
41 | expect(operationQueue.operations).toEqual([operation1]);
|
42 | });
|
43 | });
|
44 |
|
45 | describe('function addOperations', () => {
|
46 | test('should add multiple operations to its operations prop', () => {
|
47 | const operationQueue = new OperationQueue(1);
|
48 | const operation1 = new TestOperation();
|
49 | const operation2 = new TestOperation();
|
50 |
|
51 | operationQueue.addOperations([operation1, operation2]);
|
52 |
|
53 | expect(operationQueue.operations).toEqual([operation1, operation2]);
|
54 | })
|
55 | });
|
56 |
|
57 | describe('function pause', function () {
|
58 | test('should set/get property isPaused', () => {
|
59 | const operationQueue = new OperationQueue();
|
60 |
|
61 | operationQueue.pause();
|
62 |
|
63 | expect(operationQueue.isPaused).toEqual(true);
|
64 | });
|
65 |
|
66 | test('should not start any newly added opeations when queue is paused', () => {
|
67 | const operationQueue = new OperationQueue();
|
68 | const operation1 = new TestOperation();
|
69 |
|
70 | operationQueue.pause();
|
71 | operationQueue.addOperation(operation1);
|
72 |
|
73 | expect(operationQueue.runningQueue.length).toBe(0);
|
74 | });
|
75 |
|
76 | test('should emit paused event', () => {
|
77 | const pausedFunction = jest.fn(() => {
|
78 | });
|
79 | const operationQueue = new OperationQueue();
|
80 | operationQueue.on(QueueEvent.PAUSED, pausedFunction)
|
81 |
|
82 | operationQueue.pause();
|
83 |
|
84 | expect(pausedFunction).toHaveBeenCalled();
|
85 | });
|
86 |
|
87 | test('should emit paused event only once', () => {
|
88 | const pausedFunction = jest.fn(() => {
|
89 | });
|
90 | const operationQueue = new OperationQueue();
|
91 | operationQueue.on(QueueEvent.PAUSED, pausedFunction)
|
92 |
|
93 | operationQueue.pause();
|
94 | operationQueue.pause();
|
95 | operationQueue.pause();
|
96 | operationQueue.pause();
|
97 |
|
98 | expect(pausedFunction).toHaveBeenCalledTimes(1);
|
99 | });
|
100 | });
|
101 |
|
102 | describe('function off', () => {
|
103 | test('should remove callback from subscriber', async (done) => {
|
104 | const mockFunction = jest.fn(() => {
|
105 | });
|
106 | const operationQueue = new OperationQueue();
|
107 |
|
108 | operationQueue.on(OperationEvent.START, mockFunction);
|
109 | operationQueue.off(OperationEvent.START, mockFunction);
|
110 | operationQueue.pause();
|
111 | await nextTick();
|
112 |
|
113 | expect(mockFunction).not.toHaveBeenCalled();
|
114 | done();
|
115 | });
|
116 | });
|
117 |
|
118 | describe('function resume', function () {
|
119 | test('should un-set/get property isPaused', () => {
|
120 | const operationQueue = new OperationQueue();
|
121 |
|
122 | operationQueue.pause();
|
123 | operationQueue.resume();
|
124 |
|
125 | expect(operationQueue.isPaused).toEqual(false);
|
126 | });
|
127 |
|
128 | test('should start opeations when queue is un-paused', () => {
|
129 | const operationQueue = new OperationQueue();
|
130 | const operation1 = new TestOperation();
|
131 | const operation2 = new TestOperation();
|
132 |
|
133 | operationQueue.addOperation(operation1);
|
134 | operationQueue.pause();
|
135 |
|
136 | expect(operationQueue.runningQueue.length).toBe(1);
|
137 |
|
138 | operationQueue.addOperation(operation2);
|
139 |
|
140 | expect(operationQueue.runningQueue.length).toBe(1);
|
141 |
|
142 | operationQueue.resume();
|
143 |
|
144 | expect(operationQueue.runningQueue.length).toBe(2);
|
145 | });
|
146 |
|
147 | test('should emit resumed event', () => {
|
148 | const resumedFunction = jest.fn(() => {
|
149 | });
|
150 | const operationQueue = new OperationQueue();
|
151 | operationQueue.on(QueueEvent.RESUMED, resumedFunction)
|
152 | operationQueue.pause();
|
153 |
|
154 | operationQueue.resume();
|
155 |
|
156 | expect(resumedFunction).toHaveBeenCalled();
|
157 | });
|
158 |
|
159 | test('should emit resumed event only once', () => {
|
160 | const resumedFunction = jest.fn(() => {
|
161 | });
|
162 | const operationQueue = new OperationQueue();
|
163 | operationQueue.on(QueueEvent.PAUSED, resumedFunction)
|
164 | operationQueue.pause();
|
165 |
|
166 | operationQueue.resume();
|
167 | operationQueue.resume();
|
168 |
|
169 | expect(resumedFunction).toHaveBeenCalledTimes(1);
|
170 | });
|
171 | });
|
172 |
|
173 | describe('adding operations while queue is still executing', () => {
|
174 | test('should add operations seperatly, at any given time', () => {
|
175 | const operationQueue = new OperationQueue();
|
176 | const operation1 = new TestOperation();
|
177 | const operation2 = new TestOperation();
|
178 |
|
179 | operationQueue.addOperation(operation1);
|
180 | expect(operationQueue.runningQueue.length).toBe(1);
|
181 |
|
182 | operationQueue.addOperations([operation2]);
|
183 | expect(operationQueue.runningQueue.length).toBe(2);
|
184 | });
|
185 | });
|
186 |
|
187 | describe('function addOperation/addOperations', () => {
|
188 | test('should throw error when operation are dependent form each other', async (done) => {
|
189 | const operationQueue = new OperationQueue();
|
190 | const operation1 = new TestOperation();
|
191 | const operation2 = new TestOperation();
|
192 |
|
193 | operation1.dependencies = [operation2];
|
194 | operation2.dependencies = [operation1];
|
195 |
|
196 | try {
|
197 | operationQueue.addOperation(operation1);
|
198 | await nextTick();
|
199 | fail('should have failed');
|
200 | } catch (e) {
|
201 | console.log(e);
|
202 | expect(e.constructor.name === 'CircularOperationValidatorError').toBe(true);
|
203 | done();
|
204 | }
|
205 | });
|
206 |
|
207 | test('properties before calling start', () => {
|
208 | const operationQueue = new OperationQueue();
|
209 |
|
210 | expect(operationQueue.isExecuting).toBe(false);
|
211 | });
|
212 |
|
213 | test('properties while running operations', () => {
|
214 | const operationQueue = new OperationQueue();
|
215 | const operation1 = new TimeOutOperation(500);
|
216 |
|
217 | operationQueue.addOperation(operation1)
|
218 |
|
219 | expect(operationQueue.isExecuting).toBe(true);
|
220 | });
|
221 |
|
222 | test('properties after running operations', async () => {
|
223 | const operationQueue = new OperationQueue();
|
224 | const operation1 = new TestOperation();
|
225 |
|
226 | operationQueue.addOperation(operation1)
|
227 | await delay(500);
|
228 |
|
229 | expect(operationQueue.isExecuting).toBe(false);
|
230 | });
|
231 |
|
232 | test('properties after running multiple operations', async (done) => {
|
233 | const operationQueue = new OperationQueue();
|
234 | const operation1 = new TestOperation();
|
235 | const operation2 = new TimeOutOperation(500);
|
236 |
|
237 | operationQueue.addOperation(operation1)
|
238 | await nextTick();
|
239 | operationQueue.addOperation(operation2)
|
240 |
|
241 | operationQueue.on(QueueEvent.DONE, () => {
|
242 | expect(operationQueue.isExecuting).toBe(false);
|
243 | done();
|
244 | })
|
245 |
|
246 | });
|
247 |
|
248 | test('should call done event when queue becomes empty', async (done) => {
|
249 | const operationQueue = new OperationQueue();
|
250 | const operation1 = new TestOperation();
|
251 | const doneFn = jest.fn(() => {
|
252 | done();
|
253 | });
|
254 | operationQueue.on(QueueEvent.DONE, doneFn);
|
255 |
|
256 | operationQueue.addOperation(operation1);
|
257 |
|
258 | await nextTick();
|
259 |
|
260 | expect(doneFn).toHaveBeenCalledTimes(1);
|
261 | });
|
262 |
|
263 | test('should call done once event when queue becomes empty', async () => {
|
264 | const operationQueue = new OperationQueue();
|
265 | const operation1 = new TestOperation();
|
266 | const doneFn = jest.fn(() => {
|
267 | });
|
268 | operationQueue.on(QueueEvent.DONE, doneFn);
|
269 |
|
270 | operationQueue.addOperation(operation1);
|
271 | });
|
272 |
|
273 | test('should call promise resolve when operations are re-added', async () => {
|
274 | const operationQueue = new OperationQueue();
|
275 | const operation1 = new TestOperation();
|
276 | const operation2 = new TestOperation();
|
277 | const doneFn = jest.fn(() => {
|
278 | });
|
279 |
|
280 | operationQueue.on(QueueEvent.DONE, doneFn);
|
281 |
|
282 | operationQueue.addOperation(operation1);
|
283 | operationQueue.addOperation(operation2)
|
284 |
|
285 | await nextTick();
|
286 |
|
287 | expect(doneFn).toHaveBeenCalledTimes(1);
|
288 | });
|
289 |
|
290 | test('should set order queue priority of operations', (done) => {
|
291 | const operationQueue = new OperationQueue();
|
292 | operationQueue.maximumConcurentOperations = 1;
|
293 |
|
294 | const operation1 = new TestOperation();
|
295 | operation1.queuePriority = QueuePriority.veryHigh;
|
296 | const operation2 = new TestOperation();
|
297 | operation2.queuePriority = QueuePriority.high;
|
298 | const operation3 = new TestOperation();
|
299 | operation3.queuePriority = QueuePriority.normal;
|
300 | const operation4 = new TestOperation();
|
301 | operation4.queuePriority = QueuePriority.low;
|
302 | const operation5 = new TestOperation();
|
303 | operation5.queuePriority = QueuePriority.veryLow;
|
304 |
|
305 | const doneOperations = [];
|
306 | operation1.on(OperationEvent.DONE, () => {
|
307 | doneOperations.push(operation1);
|
308 | });
|
309 | operation2.on(OperationEvent.DONE, () => {
|
310 | doneOperations.push(operation2);
|
311 | });
|
312 | operation3.on(OperationEvent.DONE, () => {
|
313 | doneOperations.push(operation3);
|
314 | });
|
315 | operation4.on(OperationEvent.DONE, () => {
|
316 | doneOperations.push(operation4);
|
317 | });
|
318 | operation5.on(OperationEvent.DONE, () => {
|
319 | doneOperations.push(operation5);
|
320 | });
|
321 |
|
322 | operationQueue.addOperations([
|
323 | operation2,
|
324 | operation1,
|
325 | operation4,
|
326 | operation5,
|
327 | operation3
|
328 | ]);
|
329 |
|
330 | operationQueue.on(QueueEvent.DONE, () => {
|
331 | expect(doneOperations[0] === operation1);
|
332 | expect(doneOperations[1] === operation2);
|
333 | expect(doneOperations[2] === operation3);
|
334 | expect(doneOperations[3] === operation4);
|
335 | expect(doneOperations[4] === operation5);
|
336 | done();
|
337 | });
|
338 | });
|
339 | });
|
340 |
|
341 | describe('function cancel', () => {
|
342 | test('can cancel an operation waiting to be executed in queue', async () => {
|
343 | const operationQueue = new OperationQueue();
|
344 | operationQueue.maximumConcurentOperations = 1;
|
345 |
|
346 | const operation1 = new TimeOutOperation(1000);
|
347 | const operation2 = new TestOperation();
|
348 |
|
349 | operationQueue.addOperation(operation1);
|
350 | operationQueue.addOperation(operation2);
|
351 | await nextTick();
|
352 | operation2.cancel();
|
353 | await nextTick();
|
354 |
|
355 | expect(operationQueue.operations.length).toBe(1);
|
356 | });
|
357 |
|
358 | test('cancel last operation to end queue', async (done) => {
|
359 | const operationQueue = new OperationQueue();
|
360 | operationQueue.maximumConcurentOperations = 1;
|
361 | const doneFunction = jest.fn(() => {
|
362 | done();
|
363 | });
|
364 | operationQueue.on(QueueEvent.DONE, doneFunction);
|
365 | const operation = new TestOperation();
|
366 |
|
367 | operationQueue.addOperation(operation);
|
368 | operation.cancel();
|
369 | });
|
370 | })
|
371 | }) |
\ | No newline at end of file |