Coverage

10%
79
8
71

/home/ubuntu/src/github.com/dalekjs/dalek-internal-testsuite/index.js

10%
79
8
71
LineHitsSource
1/*!
2 *
3 * Copyright (c) 2013 Sebastian Golasch
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 */
24
251'use strict';
26
27// ext. libs
281var _ = require('lodash');
291var fs = require('fs');
301var EventEmitter2 = require('eventemitter2').EventEmitter2;
31
32// int. libs
331var test = require('dalek-internal-test');
34
35/**
36 * @constructor
37 * @param {object} options
38 */
39
401var Testsuite = function (options) {
410 this.emitter = new EventEmitter2();
420 this.initialize(options);
430 this.suite = this.loadTestsuite(options.file);
44};
45
46/**
47 * Testsuite
48 *
49 * @module DalekJS
50 * @class Testsuite
51 * @namespace Dalek
52 * @part Testsuite
53 * @api
54 */
55
561Testsuite.prototype = {
57
58 /**
59 * Assigns the initial options
60 * driverEmitter -> the drivers event dispatcher
61 * reporterEmitter -> the reporters event dispatcher
62 * driver -> the driver instance (e.g. native webdriver, selenium, etc.)
63 * name -> the suites filename (default suite name)
64 *
65 * @method initialize
66 * @param {object} options
67 * @chainable
68 */
69
70 initialize: function (options) {
710 this.driverEmitter = options.driverEmitter;
720 this.reporterEmitter = options.reporterEmitter;
730 this.driver = options.driver;
740 this.name = options.file;
750 this.numberOfSuites = options.numberOfSuites;
760 this.error = null;
770 return this;
78 },
79
80 /**
81 * Loads the testsuite that should be executed
82 *
83 * @method loadTestsuite
84 * @param {string} testfile
85 * @return {object} testsuite
86 */
87
88 loadTestsuite: function (testfile) {
890 var suite = {};
90 // catch any errors, like falsy requires & stuff
910 try {
92
930 if (fs.existsSync(process.cwd() + '/' + testfile)) {
940 suite = require(process.cwd() + '/' + testfile.replace('.js', ''));
95 } else {
960 this.error = 'Suite "' + testfile + '" does not exist. Skipping!';
970 return suite;
98 }
99 } catch (e) {
1000 this.error = 'Failure loading suite "' + testfile + '". Skipping!';
1010 return suite;
102 }
103
1040 suite._uid = _.uniqueId('Suite');
1050 return suite;
106 },
107
108 /**
109 * Checks if all tests from the testsuite are executed.
110 * Runs the next test if not.
111 * Triggers `asyncs` callback if the suite is finished.
112 * Decrements the `testsToBeExecuted` counter
113 *
114 * @method testFinished
115 * @param {function} callback
116 * @param {array} tests
117 * @param {object} test
118 * @param {string} event
119 * @chainable
120 */
121
122 testFinished: function (callback, tests) {
123 // run a function after the test, if given
1240 if (this.options.afterEach) {
1250 this.options.afterEach();
126 }
127
128 // check if there are still tests that should be executed in this suite,
129 // if so, run them
1300 if (this.decrementTestsToBeExecuted() > 1) {
1310 this.executeNextTest(tests);
1320 return this;
133 }
134
135 // run a function after the testsuite, if given
1360 if (this.options.teardown) {
1370 this.options.teardown();
138 }
139
140 // emit the suite started event
1410 this.reporterEmitter.emit('report:testsuite:finished', this.name);
142
143 // move on to the next suite
1440 callback();
1450 return this;
146 },
147
148 /**
149 * Decrements number of tests that should be executed in this suite
150 *
151 * @method decrementTestsToBeExecuted
152 * @return {integer} numberOfTestsToBeExecuted
153 */
154
155 decrementTestsToBeExecuted: function () {
1560 return (this.testsToBeExecuted--) -1;
157 },
158
159 /**
160 * Returns the name of the testsuite
161 * If the suite has no name, it will return the testsuites filename
162 *
163 * @method getName
164 * @return {string} name
165 */
166
167 getName: function () {
1680 if (this.suite.name && _.isString(this.suite.name)) {
1690 var name = this.suite.name;
1700 delete this.suite.name;
1710 return name;
172 }
173
1740 return this.name;
175 },
176
177 /**
178 * Returns the options of the testsuite
179 * If the suite has no options, it will return an empty object
180 *
181 * @method getOptions
182 * @return {object} options Suite options
183 */
184
185 getOptions: function () {
1860 if (this.suite.options && _.isObject(this.suite.options)) {
1870 var options = this.suite.options;
1880 delete this.suite.options;
1890 return options;
190 }
191
1920 return {};
193 },
194
195 /**
196 * Returns all names (aka. object keys) the tests that should be executed
197 *
198 * @method getTests
199 * @return {array} test
200 */
201
202 getTests: function () {
2030 return Object.keys(this.suite);
204 },
205
206 /**
207 * Returns the number of tests to be executed
208 *
209 * @method getNumberOfTests
210 * @param {array} tests
211 * @return {integer} numberOfTests
212 */
213
214 getNumberOfTests: function (tests) {
2150 return tests.length;
216 },
217
218 /**
219 * Returns the next test, that should be executed
220 *
221 * @method getNextTest
222 * @return {string} testName
223 */
224
225 getNextTest: function (tests) {
2260 return tests.shift();
227 },
228
229 /**
230 * Executes the next test in the sequence
231 *
232 * @method executeNextTest
233 * @param {array} tests
234 * @return {mixed} testValue
235 */
236
237 executeNextTest: function (tests) {
238 // grab the next test in the queue
2390 var testName = this.getNextTest(tests);
240 // get the next test function
2410 var testFunction = this.getTest(testName);
242 // run a setup function before the test, if given
2430 if (this.options.beforeEach) {
2440 this.options.beforeEach();
245 }
246 // generate an instance of the test & start it
2470 return testFunction(this.getTestInstance(testName));
248 },
249
250 /**
251 * Generates a new test instance
252 *
253 * @method getTestInstance
254 * @param {string} name
255 * @return {Dalek.Test} test
256 */
257
258 getTestInstance: function (name) {
2590 return test({events: this.emitter, driver: this.driver, reporter: this.reporterEmitter, name: name});
260 },
261
262 /**
263 * Returns a test function by its name
264 *
265 * @method getTest
266 * @param {string} name
267 * @return {function} test
268 */
269
270 getTest: function (name) {
2710 return this.suite[name] && _.isFunction(this.suite[name]) ? this.suite[name] : this.testDoesNotExist;
272 },
273
274 /**
275 * Will be executed if a test is started, that does not exist
276 *
277 * @method testDoesNotExist
278 * @param {object} options
279 */
280
281 testDoesNotExist: function (options) {
2820 if (options.name) {
2830 this.reporterEmitter.emit('warning', 'Test "' + options.name + '" does not exist! Skipping.');
284 }
2850 return this;
286 },
287
288 /**
289 * Runs any tests from this testsuite in sequence
290 *
291 * @method run
292 * @param {function} callback
293 * @chainable
294 */
295
296 run: function (callback) {
2970 var tests = [];
298
299 // check if the suite is
3000 if (this.error) {
3010 this.reporterEmitter.emit('report:testsuite:started', null);
302 // emit a warning notice
3030 this.reporterEmitter.emit('warning', this.error);
304 // emit the suite finished event
3050 this.reporterEmitter.emit('report:testsuite:finished', null);
306 // move on to the next suite
3070 callback();
308 }
309
310 // extract suite name
3110 this.name = this.getName();
312 // extract suite options
3130 this.options = this.getOptions();
314
315 // extract tests
3160 tests = this.getTests();
3170 this.testsToBeExecuted = this.numberOfTests = this.getNumberOfTests(tests);
318
319 // run a function before the testsuite has been launched, if given
3200 if (this.options.setup) {
3210 this.options.setup();
3220 console.dir(test);
323 }
324
325 // kickstart the test execution
3260 this.executeNextTest(tests);
327
328 // emit the suite started event
3290 this.reporterEmitter.emit('report:testsuite:started', this.name);
330
331 // listen to the test:finished event & then start the next test
332 // if there are no tests in this suite left,
333 // run the async callback & mark this suite as finished
3340 this.emitter.onAny(this.testFinished.bind(this, callback, tests));
3350 return this;
336 }
337};
338
339// export the testuite instance
3401module.exports = Testsuite;
341