Completed
Push — master ( e70c86...318007 )
by Esaú
01:50
created

hierarchy-helper.js ➔ testThird   B

Complexity

Conditions 1
Paths 16384

Size

Total Lines 131

Duplication

Lines 43
Ratio 32.82 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 16384
dl 43
loc 131
rs 8.2857
nop 1

1 Function

Rating   Name   Duplication   Size   Complexity  
B hierarchy-helper.js ➔ ... ➔ ??? 0 17 5

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
// spec/helpers/hierarchy-helper.js
2
"use strict";
3
4
// :: DEPENDENCIES
5
6
const path = require("path");
7
const root = path.dirname(path.dirname(__dirname));
8
9
module.exports = (hierarchy) => {
10
    // check parameters
11
    if (!Array.isArray(hierarchy)) {
12
        throw new Error("hierarchy must be an array");
13
    }
14
15
    // load dependencies
16
    let deps      = hierarchy.map(value => require(path.join(root, value + ".js")));
17
    let klassName = hierarchy.pop();
18
    let Klass     = deps.pop();
19
    const first   = (hierarchy.length === 0);
20
    const third   = (hierarchy.length >= 3);
21
22
    // :: TESTING
23
24
    // test the last class in the hierarchy tree
25
    describe(klassName, () => {
26
27
        // :: INHERITED PROTOTYPE
28
29
        // all inherit from Object
30
        it("should inherit from 'Object'", () => {
31
            expect(new Klass()).toEqual(jasmine.any(Object));
32
        });
33
34
        // check the hierarchy tree
35
        for (let i = 0; i < hierarchy.length; i += 1) {
36
            it("should inherit from '" + hierarchy[i] + "'", () => {
37
                expect(new Klass()).toEqual(jasmine.any(deps[i]));
38
            });
39
        }
40
41
        // check inherited properties
42
        if (!first) {
43
            it("should have a prototype property named 'name'", () => {
44
                expect(Klass.prototype).toHaveString("name");
45
            });
46
47
            it("should have a prototype property named 'message'", () => {
48
                expect(Klass.prototype).toHaveString("message");
49
            });
50
51
            it("should have a prototype property named 'code'", () => {
52
                expect(Klass.prototype).toHaveMember("code");
53
            });
54
        }
55
56
        // check Object methods
57
        it("should have a prototype method named 'toString()'", () => {
58
            expect(Klass.prototype).toHaveMethod("toString");
59
        });
60
61
        // check inherited methods
62
        if (!first) {
63
            it("should have a prototype method named 'native()'", () => {
64
                expect(Klass.prototype).toHaveMethod("native");
65
            });
66
        }
67
68
        // :: EXTENDED PROTOTYPE
69
70
        // check extended properties
71
        if (first) {
72
            it("should have a prototype property named 'name'", () => {
73
                expect(Klass.prototype).toHaveString("name");
74
            });
75
76
            it("should have a prototype property named 'message'", () => {
77
                expect(Klass.prototype).toHaveString("message");
78
            });
79
80
            it("should have a prototype property named 'code'", () => {
81
                expect(Klass.prototype).toHaveMember("code");
82
            });
83
84
            it("should have a prototype method named 'native()'", () => {
85
                expect(Klass.prototype).toHaveMethod("native");
86
            });
87
        }
88
89
        // :: PROTOTYPE VALUES
90
91
        it("should have the 'class' name in the prototype property named 'name'", () => {
92
            expect(Klass.prototype.name).toEqual(klassName);
93
        });
94
95
        it("should have a dummy default value as message", () => {
96
            expect(Klass.prototype.message).toEqual("thrown");
97
        });
98
99
        it("should have a null default value as code", () => {
100
            expect(Klass.prototype.code).toBeNull();
101
        });
102
103
        // use the tests according to the hierarchy level
104
        if (third) {
105
            testThird(Klass);
106
        } else {
107
            test(Klass);
108
        }
109
110
    });
111
112
};
113
114
// Does a loop for each argument of the Klass constructor.
115
// If the iteration is even, the parameter is defined.
116
// If the iteration is odd, the parameter is null.
117
function instanceDefinedOrNull(Klass, name, message, code, fn1, fn2, fn3, third) {
118
    for (let i = 0; i < 2; i += 1) {
119
        const even1   = (i % 2 === 0);
120
        const arg1    = (even1 ? (third ? message : name) : null);
121
        const source1 = new Klass(arg1);
122
        fn1(source1, even1);
123
        for (let j = 0; j < 2; j += 1) {
124
            const even2   = (j % 2 === 0);
125
            const arg2    = (even2 ? (third ? code : message) : null);
126
            const source2 = new Klass(arg1, arg2);
127
            fn2(source2, even1, even2);
128
            if (!third) {
129
                for (let e = 0; e < 2; e += 1) {
130
                    const even3   = (e % 2 === 0);
131
                    const arg3    = (even3 ? code : null);
132
                    const source3 = new Klass(arg1, arg2, arg3);
133
                    fn3(source3, even1, even2, even3);
134
                }
135
            }
136
        }
137
    }
138
}
139
140
// Tests classes that are a third level subclass, meaning that they require 2 arguments (message and code).
141
function testThird(Klass) {
142
143
    // :: CONSTRUCTOR
144
145
    it("should instantiate without parameters", () => {
146
        let arg1, arg2, test;
147
        test = (() => new Klass(arg1, arg2));
148
        for (let i = 0; i < 2; i += 1) {
149
            arg1 = (i % 2 === 0 ? undefined : null);
150
            for (let j = 0; j < 2; j += 1) {
151
                arg2 = (j % 2 === 0 ? undefined : null);
152
                expect(test).not.toThrowError("parameter 'name' must be a 'string'");
153
                expect(test).not.toThrowError("parameter 'message' must be a 'string'");
154
                expect(test).not.toThrowError("parameter 'code' must be a 'number'");
155
            }
156
        }
157
        test = (() => new Klass());
158
        expect(test).not.toThrowError("parameter 'name' must be a 'string'");
159
        expect(test).not.toThrowError("parameter 'message' must be a 'string'");
160
        expect(test).not.toThrowError("parameter 'code' must be a 'number'");
161
    });
162
163
    it("should instantiate with parameters", () => {
164
        let arg1, arg2;
165
        const test1 = (() => new Klass(arg1));
166
        const test2 = (() => new Klass(arg1, arg2));
167
        const args1 = [undefined, null, Klass.prototype.message];
168
        const args2 = [undefined, null, Math.round(Math.random() * 0xFFFFFFFF)];
169
        for (let i = 0; i < args1.length; i += 1) {
170
            arg1 = args1[i];
171
            expect(test1).not.toThrowError("parameter 'name' must be a 'string'");
172
            expect(test1).not.toThrowError("parameter 'message' must be a 'string'");
173
            expect(test1).not.toThrowError("parameter 'code' must be a 'number'");
174
            for (let j = 0; j < args2.length; j += 1) {
175
                arg2 = args2[j];
176
                expect(test2).not.toThrowError("parameter 'name' must be a 'string'");
177
                expect(test2).not.toThrowError("parameter 'message' must be a 'string'");
178
                expect(test2).not.toThrowError("parameter 'code' must be a 'number'");
179
            }
180
        }
181
    });
182
183 View Code Duplication
    it("should throw an Error if 'message' or 'code' are invalid parameters", () => {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
184
        let arg1, arg2, arg3, test21, test22, test11;
0 ignored issues
show
Unused Code introduced by
The variable arg3 seems to be never used. Consider removing it.
Loading history...
185
        const noStr   = [{}, true, false, 42, 3.1416, -42, -3.1416, () => null];
186
        const noNmb   = [{}, true, false, '', "qwerty", () => null];
187
        test22 = (() => new Klass(arg1, arg2));
188
        test21 = (() => new Klass(null, arg2));
189
        test11 = (() => new Klass(arg1));
190
        if (typeof Symbol === "function") {
191
            noStr.push(Symbol("symbol"));
192
            noNmb.push(Symbol("symbol"));
193
        }
194
        for (let i = 0; i < noStr.length; i += 1) {
195
            arg1 = noStr[i];
196
            expect(test11).toThrowError("parameter 'message' must be a 'string'");
197
            for (let j = 0; j < noNmb.length; j += 1) {
198
                arg2 = noNmb[j];
199
                expect(test22).toThrowError("parameter 'message' must be a 'string'");
200
                expect(test21).toThrowError("parameter 'code' must be a 'number'");
201
            }
202
        }
203
    });
204
205
    // :: MEMBER PROPERTIES
206
207
    const message = "asdf";
208
    const code    = Math.round(Math.random() * 0xFFFFFFFF);
209
210
    it("should have all correct properties once instantiated", () => {
211
        instanceDefinedOrNull(Klass, null, message, code, (instance, even) => {
212
            if (even) {
213
                expect(instance.name).toEqual(Klass.prototype.name);
214
                expect(instance.message).toEqual(message);
215
            } else {
216
                expect(instance.name).toEqual(Klass.prototype.name);
217
                expect(instance.message).toEqual(Klass.prototype.message);
218
            }
219
            expect(instance.code).toBeNull();
220
        }, (instance, even1, even2) => {
221
            expect(instance.name).toEqual(Klass.prototype.name);
222
            expect(instance.message).toEqual(even1 ? message : Klass.prototype.message);
223
            expect(instance.code).toEqual(even2 ? code : null);
224
        }, (instance, even1, even2, even3) => {
225
            expect(instance.name).toEqual(even1 ? name : Klass.prototype.name);
0 ignored issues
show
Bug introduced by
The variable name seems to be never declared. If this is a global, consider adding a /** global: name */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
226
            expect(instance.message).toEqual(even2 ? message : Klass.prototype.message);
227
            expect(instance.code).toEqual(even3 ? code : null);
228
        }, true);
229
    });
230
231
    // :: MEMBER METHODS
232
233
    it("#toString()", () => {
234 View Code Duplication
        for (let i = 0; i < 2; i += 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
235
            let exp1;
236
            const even1   = (i % 2 === 0);
237
            const arg1    = (even1 ? message : null);
238
            const source1 = new Klass(arg1);
239
            if (even1) {
240
                exp1 = Klass.prototype.name + ": " + message + '.';
241
            } else {
242
                exp1 = Klass.prototype.name + ": " + Klass.prototype.message + '.';
243
            }
244
            expect(source1.toString()).toEqual(exp1);
245
            for (let j = 0; j < 2; j += 1) {
246
                let exp2;
247
                const even2   = (j % 2 === 0);
248
                const arg2    = (even2 ? code : null);
249
                const source2 = new Klass(arg1, arg2);
250
                exp2 = Klass.prototype.name;
251
                exp2 += (even2 ? " (0x" + code.toString(16) + "):" : ':' ) + ' ';
252
                exp2 += (even1 ? message : Klass.prototype.message) + '.';
253
                expect(source2.toString()).toEqual(exp2);
254
            }
255
        }
256
    });
257
258
    it("#native()", () => {
259
        instanceDefinedOrNull(Klass, null, message, code, (instance, even) => {
260
            const exp = (even ? message : Klass.prototype.message);
261
            expect(instance.native()).toEqual(new Error(exp));
262
        }, (instance, even1) => {
263
            const exp = (even1 ? message : Klass.prototype.message);
264
            expect(instance.native()).toEqual(new Error(exp));
265
        }, (instance, even1, even2) => {
266
            const exp = (even2 ? message : Klass.prototype.message);
267
            expect(instance.native()).toEqual(new Error(exp));
268
        }, true);
269
    });
270
271
}
272
273
// Tests classes that require 3 arguments (name, message and code).
274
function test(Klass) {
275
276
    // :: CONSTRUCTOR
277
278
    it("should instantiate without parameters", () => {
279
        let arg1, arg2, arg3, test;
280
        test = (() => new Klass(arg1, arg2, arg3));
281
        for (let i = 0; i < 2; i += 1) {
282
            arg1 = (i % 2 === 0 ? undefined : null);
283
            for (let j = 0; j < 2; j += 1) {
284
                arg2 = (j % 2 === 0 ? undefined : null);
285
                for (let e = 0; e < 2; e += 1) {
286
                    arg3 = (e % 2 === 0 ? undefined : null);
287
                    expect(test).not.toThrowError("parameter 'name' must be a 'string'");
288
                    expect(test).not.toThrowError("parameter 'message' must be a 'string'");
289
                    expect(test).not.toThrowError("parameter 'code' must be a 'number'");
290
                }
291
            }
292
        }
293
        test = (() => new Klass());
0 ignored issues
show
Comprehensibility introduced by
It seems like you are trying to overwrite a function name here. test is already defined in line 274 as a function. While this will work, it can be very confusing.
Loading history...
294
        expect(test).not.toThrowError("parameter 'name' must be a 'string'");
295
        expect(test).not.toThrowError("parameter 'message' must be a 'string'");
296
        expect(test).not.toThrowError("parameter 'code' must be a 'number'");
297
    });
298
299
    it("should instantiate with parameters", () => {
300
        let arg1, arg2, arg3, test1, test2, test3, args1, args2, args3;
301
        test1 = (() => new Klass(arg1));
302
        test2 = (() => new Klass(arg1, arg2));
303
        test3 = (() => null);
0 ignored issues
show
Unused Code introduced by
The assignment to variable test3 seems to be never used. Consider removing it.
Loading history...
304
        test3 = (() => new Klass(arg1, arg2, arg3));
305
        args1 = [undefined, null, Klass.prototype.name];
306
        args2 = [undefined, null, Klass.prototype.message];
307
        args3 = [undefined, null, Math.round(Math.random() * 0xFFFFFFFF)];
308
        for (let i = 0; i < args1.length; i += 1) {
309
            arg1 = args1[i];
310
            expect(test1).not.toThrowError("parameter 'name' must be a 'string'");
311
            expect(test1).not.toThrowError("parameter 'message' must be a 'string'");
312
            expect(test1).not.toThrowError("parameter 'code' must be a 'number'");
313
            for (let j = 0; j < args2.length; j += 1) {
314
                arg2 = args2[j];
315
                expect(test2).not.toThrowError("parameter 'name' must be a 'string'");
316
                expect(test2).not.toThrowError("parameter 'message' must be a 'string'");
317
                expect(test2).not.toThrowError("parameter 'code' must be a 'number'");
318
                for (let e = 0; e < args3.length; e += 1) {
319
                    arg3 = args3[e];
320
                    expect(test3).not.toThrowError("parameter 'name' must be a 'string'");
321
                    expect(test3).not.toThrowError("parameter 'message' must be a 'string'");
322
                    expect(test3).not.toThrowError("parameter 'code' must be a 'number'");
323
                }
324
            }
325
        }
326
    });
327
328 View Code Duplication
    it("should throw an Error if 'message' or 'code' are invalid parameters", () => {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
329
        let arg1, arg2, arg3, test31, test32, test33, test21, test22, test11;
330
        const noStr   = [{}, true, false, 42, 3.1416, -42, -3.1416, () => null];
331
        const noNmb   = [{}, true, false, '', "qwerty", () => null];
332
        test33 = (() => new Klass(arg1, arg2, arg3));
333
        test32 = (() => new Klass(null, arg2, arg3));
334
        test31 = (() => new Klass(null, null, arg3));
335
        test22 = (() => new Klass(arg1, arg2));
336
        test21 = (() => new Klass(null, arg2));
337
        test11 = (() => new Klass(arg1));
338
        if (typeof Symbol === "function") {
339
            noStr.push(Symbol("symbol"));
340
            noNmb.push(Symbol("symbol"));
341
        }
342
        for (let i = 0; i < noStr.length; i += 1) {
343
            arg1 = noStr[i];
344
            expect(test11).toThrowError("parameter 'name' must be a 'string'");
345
            for (let j = 0; j < noStr.length; j += 1) {
346
                arg2 = noStr[j];
347
                expect(test22).toThrowError("parameter 'name' must be a 'string'");
348
                expect(test21).toThrowError("parameter 'message' must be a 'string'");
349
                for (let e = 0; e < noNmb.length; e += 1) {
350
                    arg3 = noNmb[e];
351
                    expect(test33).toThrowError("parameter 'name' must be a 'string'");
352
                    expect(test32).toThrowError("parameter 'message' must be a 'string'");
353
                    expect(test31).toThrowError("parameter 'code' must be a 'number'");
354
                }
355
            }
356
        }
357
    });
358
359
    // :: MEMBER PROPERTIES
360
361
    const name    = "qwerty";
362
    const message = "asdf";
363
    const code    = Math.round(Math.random() * 0xFFFFFFFF);
364
365
    it("should have all correct properties once instantiated", () => {
366
        instanceDefinedOrNull(Klass, name, message, code, (instance, even) => {
367
            if (even) {
368
                expect(instance.name).toEqual(name);
369
                expect(instance.message).toEqual(Klass.prototype.message);
370
            } else {
371
                expect(instance.name).toEqual(Klass.prototype.name);
372
                expect(instance.message).toEqual(Klass.prototype.message);
373
            }
374
            expect(instance.code).toBeNull();
375
        }, (instance, even1, even2) => {
376
            expect(instance.name).toEqual(even1 ? name : Klass.prototype.name);
377
            expect(instance.message).toEqual(even2 ? message : Klass.prototype.message);
378
            expect(instance.code).toBeNull();
379
        }, (instance, even1, even2, even3) => {
380
            expect(instance.name).toEqual(even1 ? name : Klass.prototype.name);
381
            expect(instance.message).toEqual(even2 ? message : Klass.prototype.message);
382
            expect(instance.code).toEqual(even3 ? code : null);
383
        }, false);
384
    });
385
386
    // :: MEMBER METHODS
387
388
    it("#toString()", () => {
389
        for (let i = 0; i < 2; i += 1) {
390
            let exp1;
391
            const even1   = (i % 2 === 0);
392
            const arg1    = (even1 ? name : null);
393
            const source1 = new Klass(arg1);
394
            if (even1) {
395
                exp1 = name + ": " + Klass.prototype.message + '.';
396
            } else {
397
                exp1 = Klass.prototype.name + ": " + Klass.prototype.message + '.';
398
            }
399
            expect(source1.toString()).toEqual(exp1);
400 View Code Duplication
            for (let j = 0; j < 2; j += 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
401
                let exp2;
402
                const even2   = (j % 2 === 0);
403
                const arg2    = (even2 ? message : null);
404
                const source2 = new Klass(arg1, arg2);
405
                exp2 = (even1 ? name : Klass.prototype.name) + ':';
406
                exp2 += ' ' + (even2 ? message : Klass.prototype.message) + '.';
407
                expect(source2.toString()).toEqual(exp2);
408
                for (let e = 0; e < 2; e += 1) {
409
                    let exp3;
410
                    const even3   = (e % 2 === 0);
411
                    const arg3    = (even3 ? code : null);
412
                    const source3 = new Klass(arg1, arg2, arg3);
413
                    exp3  = (even1 ? name : Klass.prototype.name);
414
                    exp3 += (even3 ? " (0x" + code.toString(16) + "):" : ':') + ' ';
415
                    exp3 += (even2 ? message : Klass.prototype.message) + '.';
416
                    expect(source3.toString()).toEqual(exp3);
417
                }
418
            }
419
        }
420
    });
421
422
    it("#native()", () => {
423
        instanceDefinedOrNull(Klass, name, message, code, (instance) => {
424
            const exp = Klass.prototype.message;
425
            expect(instance.native()).toEqual(new Error(exp));
426
        }, (instance, even1, even2) => {
427
            const exp  = (even2 ? message : Klass.prototype.message);
428
            expect(instance.native()).toEqual(new Error(exp));
429
        }, (instance, even1, even2) => {
430
            const exp = (even2 ? message : Klass.prototype.message);
431
            expect(instance.native()).toEqual(new Error(exp));
432
        }, false);
433
    });
434
435
}