UNPKG

4.02 kBJavaScriptView Raw
1'use strict';
2var EventEmitter = require('events').EventEmitter;
3var util = require('util');
4var fnName = require('fn-name');
5var Concurrent = require('./concurrent');
6var Sequence = require('./sequence');
7var Test = require('./test');
8
9module.exports = TestCollection;
10
11function TestCollection() {
12 if (!(this instanceof TestCollection)) {
13 throw new TypeError('Class constructor TestCollection cannot be invoked without \'new\'');
14 }
15
16 EventEmitter.call(this);
17
18 this.hasExclusive = false;
19 this.tests = {
20 concurrent: [],
21 serial: []
22 };
23
24 this.hooks = {
25 before: [],
26 beforeEach: [],
27 after: [],
28 afterEach: []
29 };
30
31 this._emitTestResult = this._emitTestResult.bind(this);
32}
33
34util.inherits(TestCollection, EventEmitter);
35
36TestCollection.prototype.add = function (test) {
37 var metadata = test.metadata;
38 var type = metadata.type;
39
40 if (!type) {
41 throw new Error('Test type must be specified');
42 }
43
44 if (!test.title && test.fn) {
45 test.title = fnName(test.fn);
46 }
47
48 // workaround for Babel giving anonymous functions a name
49 if (test.title === 'callee$0$0') {
50 test.title = null;
51 }
52
53 if (!test.title) {
54 if (type === 'test') {
55 test.title = '[anonymous]';
56 } else {
57 test.title = type;
58 }
59 }
60
61 // add a hook
62 if (type !== 'test') {
63 if (metadata.exclusive) {
64 throw new Error('"only" cannot be used with a ' + type + ' test');
65 }
66
67 this.hooks[type].push(test);
68 return;
69 }
70
71 // add .only() tests if .only() was used previously
72 if (this.hasExclusive && !metadata.exclusive) {
73 return;
74 }
75
76 if (metadata.exclusive && !this.hasExclusive) {
77 this.tests.concurrent = [];
78 this.tests.serial = [];
79 this.hasExclusive = true;
80 }
81
82 if (metadata.serial) {
83 this.tests.serial.push(test);
84 } else {
85 this.tests.concurrent.push(test);
86 }
87};
88
89TestCollection.prototype._skippedTest = function (test) {
90 var self = this;
91
92 return {
93 run: function () {
94 var result = {
95 passed: true,
96 result: test
97 };
98
99 self._emitTestResult(result);
100
101 return result;
102 }
103 };
104};
105
106TestCollection.prototype._emitTestResult = function (test) {
107 this.emit('test', test);
108};
109
110TestCollection.prototype._buildHooks = function (hooks, testTitle, context) {
111 return hooks.map(function (hook) {
112 var test = this._buildHook(hook, testTitle, context);
113
114 if (hook.metadata.skipped || hook.metadata.todo) {
115 return this._skippedTest(test);
116 }
117
118 return test;
119 }, this);
120};
121
122TestCollection.prototype._buildHook = function (hook, testTitle, context) {
123 var title = hook.title;
124
125 if (testTitle) {
126 title += ' for ' + testTitle;
127 }
128
129 if (!context) {
130 context = null;
131 }
132
133 var test = new Test(title, hook.fn, context, this._emitTestResult);
134 test.metadata = hook.metadata;
135
136 return test;
137};
138
139TestCollection.prototype._buildTest = function (test, context) {
140 if (!context) {
141 context = null;
142 }
143
144 var metadata = test.metadata;
145
146 test = new Test(test.title, test.fn, context, this._emitTestResult);
147 test.metadata = metadata;
148
149 return test;
150};
151
152TestCollection.prototype._buildTestWithHooks = function (test) {
153 if (test.metadata.skipped) {
154 return [this._skippedTest(this._buildTest(test))];
155 }
156
157 var context = {context: {}};
158
159 var beforeHooks = this._buildHooks(this.hooks.beforeEach, test.title, context);
160 var afterHooks = this._buildHooks(this.hooks.afterEach, test.title, context);
161
162 return [].concat(beforeHooks, this._buildTest(test, context), afterHooks);
163};
164
165TestCollection.prototype._buildTests = function (tests) {
166 return tests.map(function (test) {
167 return new Sequence(this._buildTestWithHooks(test), true);
168 }, this);
169};
170
171TestCollection.prototype.build = function (bail) {
172 var beforeHooks = new Sequence(this._buildHooks(this.hooks.before));
173 var afterHooks = new Sequence(this._buildHooks(this.hooks.after));
174
175 var serialTests = new Sequence(this._buildTests(this.tests.serial), bail);
176 var concurrentTests = new Concurrent(this._buildTests(this.tests.concurrent), bail);
177 var allTests = new Sequence([serialTests, concurrentTests]);
178
179 return new Sequence([beforeHooks, allTests, afterHooks], true);
180};