UNPKG

21.5 kBJavaScriptView Raw
1(function (factory) {
2 if (typeof module === "object" && typeof module.exports === "object") {
3 var v = factory(require, exports);
4 if (v !== undefined) module.exports = v;
5 }
6 else if (typeof define === "function" && define.amd) {
7 define(["require", "exports", "@theintern/common", "./Deferred", "./Test"], factory);
8 }
9})(function (require, exports) {
10 "use strict";
11 Object.defineProperty(exports, "__esModule", { value: true });
12 var common_1 = require("@theintern/common");
13 var Deferred_1 = require("./Deferred");
14 var Test_1 = require("./Test");
15 var Suite = (function () {
16 function Suite(options) {
17 var _this = this;
18 this.publishAfterSetup = false;
19 this.tests = [];
20 Object.keys(options)
21 .filter(function (key) {
22 return key !== 'tests';
23 })
24 .forEach(function (option) {
25 var key = option;
26 _this[key] = options[key];
27 });
28 if (options.tests) {
29 options.tests.forEach(function (suiteOrTest) { return _this.add(suiteOrTest); });
30 }
31 if (!this.name && this.parent) {
32 throw new Error('A non-root Suite must have a name');
33 }
34 }
35 Object.defineProperty(Suite.prototype, "bail", {
36 get: function () {
37 return this._bail || (this.parent && this.parent.bail);
38 },
39 set: function (value) {
40 this._bail = value;
41 },
42 enumerable: true,
43 configurable: true
44 });
45 Object.defineProperty(Suite.prototype, "executor", {
46 get: function () {
47 return (this.parent && this.parent.executor) || this._executor;
48 },
49 set: function (value) {
50 if (this._executor) {
51 var error = new Error('An executor may only be set once per suite');
52 error.name = 'AlreadyAssigned';
53 throw error;
54 }
55 this._executor = value;
56 },
57 enumerable: true,
58 configurable: true
59 });
60 Object.defineProperty(Suite.prototype, "grep", {
61 get: function () {
62 return this._grep || (this.parent && this.parent.grep) || /.*/;
63 },
64 set: function (value) {
65 this._grep = value;
66 this._applyGrepToChildren();
67 },
68 enumerable: true,
69 configurable: true
70 });
71 Object.defineProperty(Suite.prototype, "name", {
72 get: function () {
73 return this._name;
74 },
75 set: function (value) {
76 this._name = value;
77 this._applyGrepToChildren();
78 },
79 enumerable: true,
80 configurable: true
81 });
82 Object.defineProperty(Suite.prototype, "id", {
83 get: function () {
84 var name = [];
85 var suite = this;
86 do {
87 suite.name != null && name.unshift(suite.name);
88 } while ((suite = suite.parent));
89 return name.join(' - ');
90 },
91 enumerable: true,
92 configurable: true
93 });
94 Object.defineProperty(Suite.prototype, "parentId", {
95 get: function () {
96 var parent = this.parent;
97 if (parent) {
98 return parent.id;
99 }
100 },
101 enumerable: true,
102 configurable: true
103 });
104 Object.defineProperty(Suite.prototype, "remote", {
105 get: function () {
106 return this.parent && this.parent.remote
107 ? this.parent.remote
108 : this._remote;
109 },
110 set: function (value) {
111 if (this._remote) {
112 throw new Error('AlreadyAssigned: remote may only be set once per suite');
113 }
114 this._remote = value;
115 },
116 enumerable: true,
117 configurable: true
118 });
119 Object.defineProperty(Suite.prototype, "sessionId", {
120 get: function () {
121 if (this.parent) {
122 return this.parent.sessionId;
123 }
124 if (this._sessionId) {
125 return this._sessionId;
126 }
127 if (this.remote) {
128 return this.remote.session.sessionId;
129 }
130 return '';
131 },
132 set: function (value) {
133 this._sessionId = value;
134 },
135 enumerable: true,
136 configurable: true
137 });
138 Object.defineProperty(Suite.prototype, "numTests", {
139 get: function () {
140 return this.tests.reduce(function (numTests, suiteOrTest) {
141 if (isSuite(suiteOrTest)) {
142 return numTests + suiteOrTest.numTests;
143 }
144 return numTests + 1;
145 }, 0);
146 },
147 enumerable: true,
148 configurable: true
149 });
150 Object.defineProperty(Suite.prototype, "numPassedTests", {
151 get: function () {
152 return this.tests.reduce(function (numPassedTests, suiteOrTest) {
153 if (isSuite(suiteOrTest)) {
154 return numPassedTests + suiteOrTest.numPassedTests;
155 }
156 else if (suiteOrTest.hasPassed) {
157 return numPassedTests + 1;
158 }
159 return numPassedTests;
160 }, 0);
161 },
162 enumerable: true,
163 configurable: true
164 });
165 Object.defineProperty(Suite.prototype, "numFailedTests", {
166 get: function () {
167 return this.tests.reduce(function (numFailedTests, suiteOrTest) {
168 if (isSuite(suiteOrTest)) {
169 return numFailedTests + suiteOrTest.numFailedTests;
170 }
171 else if (suiteOrTest.error) {
172 return numFailedTests + 1;
173 }
174 return numFailedTests;
175 }, 0);
176 },
177 enumerable: true,
178 configurable: true
179 });
180 Object.defineProperty(Suite.prototype, "numSkippedTests", {
181 get: function () {
182 return this.tests.reduce(function (numSkippedTests, suiteOrTest) {
183 if (isSuite(suiteOrTest)) {
184 return numSkippedTests + suiteOrTest.numSkippedTests;
185 }
186 else if (suiteOrTest.skipped) {
187 return numSkippedTests + 1;
188 }
189 return numSkippedTests;
190 }, 0);
191 },
192 enumerable: true,
193 configurable: true
194 });
195 Object.defineProperty(Suite.prototype, "hasParent", {
196 get: function () {
197 return Boolean(this.parent);
198 },
199 enumerable: true,
200 configurable: true
201 });
202 Object.defineProperty(Suite.prototype, "timeout", {
203 get: function () {
204 if (this._timeout != null) {
205 return this._timeout;
206 }
207 if (this.parent) {
208 return this.parent.timeout;
209 }
210 return 30000;
211 },
212 set: function (value) {
213 this._timeout = value;
214 },
215 enumerable: true,
216 configurable: true
217 });
218 Suite.prototype.add = function (suiteOrTest) {
219 if (!Test_1.isTest(suiteOrTest) && !isSuite(suiteOrTest)) {
220 throw new Error('Tried to add invalid suite or test');
221 }
222 if (suiteOrTest.parent != null && suiteOrTest.parent !== this) {
223 throw new Error('This Suite or Test already belongs to another parent');
224 }
225 this.tests.forEach(function (existingSuiteOrTest) {
226 if (existingSuiteOrTest.name === suiteOrTest.name) {
227 throw new Error("A suite or test named \"" + suiteOrTest.name + "\" has already been added");
228 }
229 });
230 suiteOrTest.parent = this;
231 this.tests.push(suiteOrTest);
232 this._applyGrepToSuiteOrTest(suiteOrTest);
233 if (Test_1.isTest(suiteOrTest)) {
234 this.executor.emit('testAdd', suiteOrTest);
235 }
236 else {
237 this.executor.emit('suiteAdd', suiteOrTest);
238 }
239 };
240 Suite.prototype._applyGrepToSuiteOrTest = function (suiteOrTest) {
241 if (suiteOrTest instanceof Suite) {
242 suiteOrTest._applyGrepToChildren();
243 }
244 else {
245 var grepSkipReason = 'grep';
246 if (suiteOrTest.skipped === grepSkipReason) {
247 suiteOrTest.skipped = undefined;
248 }
249 if (!this.grep.test(suiteOrTest.id)) {
250 suiteOrTest.skipped = grepSkipReason;
251 }
252 }
253 };
254 Suite.prototype._applyGrepToChildren = function () {
255 var _this = this;
256 this.tests.forEach(function (suiteOrTest) {
257 return _this._applyGrepToSuiteOrTest(suiteOrTest);
258 });
259 };
260 Suite.prototype.run = function () {
261 var _this = this;
262 var startTime;
263 var start = function () {
264 return _this.executor.emit('suiteStart', _this).then(function () {
265 startTime = Date.now();
266 });
267 };
268 var end = function () {
269 _this.timeElapsed = Date.now() - startTime;
270 return _this.executor.emit('suiteEnd', _this);
271 };
272 var allTestsSkipped = this.numTests === this.numSkippedTests;
273 var runLifecycleMethod = function (suite, name, test) {
274 var result;
275 if (!_this._executor && allTestsSkipped) {
276 return common_1.Task.resolve();
277 }
278 return new common_1.Task(function (resolve, reject) {
279 var dfd;
280 var timeout;
281 suite.async = function (_timeout) {
282 timeout = _timeout;
283 var _dfd = new Deferred_1.default();
284 dfd = _dfd;
285 suite.async = function () {
286 return _dfd;
287 };
288 return _dfd;
289 };
290 var suiteFunc = suite[name];
291 result =
292 suiteFunc &&
293 (test
294 ? suiteFunc.call(suite, test, suite)
295 : suiteFunc.call(suite, suite));
296 if (dfd) {
297 var _dfd_1 = dfd;
298 if (timeout) {
299 var timer_1 = setTimeout(function () {
300 var error = new Error("Timeout reached on " + suite.id + "#" + name);
301 error.name = 'TimeoutError';
302 _dfd_1.reject(error);
303 }, timeout);
304 _dfd_1.promise
305 .catch(function (_error) { })
306 .then(function () { return timer_1 && clearTimeout(timer_1); });
307 }
308 if (common_1.isPromiseLike(result)) {
309 result.then(function () { return _dfd_1.resolve(); }, function (error) { return _dfd_1.reject(error); });
310 }
311 result = dfd.promise;
312 }
313 if (common_1.isPromiseLike(result)) {
314 result.then(function () { return resolve(); }, reject);
315 }
316 else {
317 resolve();
318 }
319 }, function () {
320 if (common_1.isTask(result)) {
321 result.cancel();
322 }
323 })
324 .finally(function () {
325 suite.async = undefined;
326 })
327 .catch(function (error) {
328 if (error !== Test_1.SKIP) {
329 if (!_this.error) {
330 _this.executor.log('Suite errored with non-skip error', error);
331 _this.error = error;
332 }
333 throw error;
334 }
335 });
336 };
337 var before = function () {
338 return runLifecycleMethod(_this, 'before');
339 };
340 var after = function () {
341 return runLifecycleMethod(_this, 'after');
342 };
343 this.error = undefined;
344 this.timeElapsed = 0;
345 var task;
346 var runTask;
347 try {
348 task = this.publishAfterSetup
349 ? before().then(start)
350 : start().then(before);
351 }
352 catch (error) {
353 return common_1.Task.reject(error);
354 }
355 return task
356 .then(function () {
357 var runTestLifecycle = function (name, test) {
358 var methodQueue = [];
359 var suite = _this;
360 do {
361 if (name === 'beforeEach') {
362 methodQueue.push(suite);
363 }
364 else {
365 methodQueue.unshift(suite);
366 }
367 } while ((suite = suite.parent));
368 var currentMethod;
369 return new common_1.Task(function (resolve, reject) {
370 var firstError;
371 var handleError = function (error) {
372 if (name === 'afterEach') {
373 firstError = firstError || error;
374 next();
375 }
376 else {
377 reject(error);
378 }
379 };
380 var next = function () {
381 var suite = methodQueue.pop();
382 if (!suite) {
383 firstError ? reject(firstError) : resolve();
384 return;
385 }
386 currentMethod = runLifecycleMethod(suite, name, test).then(next, handleError);
387 };
388 next();
389 }, function () {
390 methodQueue.splice(0, methodQueue.length);
391 if (currentMethod) {
392 currentMethod.cancel();
393 }
394 });
395 };
396 var i = 0;
397 var tests = _this.tests;
398 var current;
399 runTask = new common_1.Task(function (resolve, reject) {
400 var firstError;
401 var testTask;
402 var next = function () {
403 var test = tests[i++];
404 if (!test) {
405 firstError ? reject(firstError) : resolve();
406 return;
407 }
408 var handleError = function (error) {
409 if (error && error.relatedTest == null) {
410 error.relatedTest = test;
411 }
412 };
413 var runTest = function () {
414 var result = test.run().catch(function (error) {
415 handleError(error);
416 });
417 testTask = new common_1.Task(function (resolve) {
418 result.then(resolve);
419 }, function () {
420 result.cancel();
421 });
422 return testTask;
423 };
424 if (_this.skipped != null) {
425 test.skipped = _this.skipped;
426 }
427 if (isSuite(test)) {
428 current = runTest();
429 }
430 else {
431 if (test.skipped != null) {
432 current = _this.executor.emit('testEnd', test);
433 }
434 else {
435 current = runTestLifecycle('beforeEach', test)
436 .then(function () {
437 if (test.skipped != null) {
438 return _this.executor.emit('testEnd', test);
439 }
440 else {
441 return runTest();
442 }
443 })
444 .finally(function () {
445 if (testTask) {
446 testTask.cancel();
447 }
448 testTask = undefined;
449 return runTestLifecycle('afterEach', test);
450 })
451 .catch(function (error) {
452 firstError = firstError || error;
453 return handleError(error);
454 });
455 }
456 }
457 current.then(function () {
458 var skipRestOfSuite = function () {
459 _this.skipped =
460 _this.skipped != null ? _this.skipped : BAIL_REASON;
461 };
462 if (isSuite(test) && test.skipped === BAIL_REASON) {
463 skipRestOfSuite();
464 }
465 else if (test.error && _this.bail) {
466 skipRestOfSuite();
467 }
468 next();
469 });
470 };
471 next();
472 }, function () {
473 i = Infinity;
474 if (current) {
475 current.cancel();
476 }
477 });
478 return runTask;
479 })
480 .finally(function () {
481 if (runTask) {
482 runTask.cancel();
483 }
484 })
485 .finally(function () { return (_this.publishAfterSetup ? end() : after()); })
486 .finally(function () { return (_this.publishAfterSetup ? after() : end()); });
487 };
488 Suite.prototype.skip = function (message) {
489 if (message === void 0) { message = 'suite skipped'; }
490 this.skipped = message;
491 throw Test_1.SKIP;
492 };
493 Suite.prototype.toJSON = function () {
494 var _this = this;
495 var json = {
496 hasParent: Boolean(this.parent),
497 tests: this.tests.map(function (test) { return test.toJSON(); })
498 };
499 var properties = [
500 'name',
501 'id',
502 'parentId',
503 'sessionId',
504 'timeElapsed',
505 'numTests',
506 'numPassedTests',
507 'numFailedTests',
508 'numSkippedTests',
509 'skipped'
510 ];
511 properties.forEach(function (key) {
512 var value = _this[key];
513 if (typeof value !== 'undefined') {
514 json[key] = value;
515 }
516 });
517 if (this.error) {
518 json.error = {
519 name: this.error.name,
520 message: this.error.message,
521 stack: this.error.stack
522 };
523 if (this.error.relatedTest && this.error.relatedTest !== this) {
524 json.error.relatedTest = this.error.relatedTest.toJSON();
525 }
526 }
527 return json;
528 };
529 return Suite;
530 }());
531 exports.default = Suite;
532 function isSuite(value) {
533 return Array.isArray(value.tests) && typeof value.hasParent === 'boolean';
534 }
535 exports.isSuite = isSuite;
536 var BAIL_REASON = 'bailed';
537});
538//# sourceMappingURL=Suite.js.map
\No newline at end of file