Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 525 lines (444 sloc) 16.936 kb
9821eb6 @victorjonsson initial commit...
authored
1 var filesystem = require('fs'),
2 util = require('util'),
50622d5 @victorjonsson new version, messages about failing tests now sent to console.error
authored
3 querystring = require('querystring'),
4 colors = {
5 red : '\033[31m',
6 green : '\033[32m',
7 reset : '\033[0m'
8 };
9821eb6 @victorjonsson initial commit...
authored
9
7ae499d @victorjonsson wip...
authored
10
9821eb6 @victorjonsson initial commit...
authored
11 /**
12 * @param {Object} options
13 */
14 function mergeWithDefaultOptions(options) {
66f81a6 @victorjonsson closing in on a beta release
authored
15 if(options.url == undefined)
9821eb6 @victorjonsson initial commit...
authored
16 throw new Error('property url missing from options');
66f81a6 @victorjonsson closing in on a beta release
authored
17 if(options.port == undefined)
18 options.port = 80;
19 if(options.headers == undefined)
20 options.headers = {};
21 if(options.write == undefined)
22 options.write = '';
23 if(options.method == undefined)
24 options.method = 'GET';
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
25 if(options.encoding == undefined)
26 options.encoding = 'utf8';
27 if(options.keepCookies == undefined)
28 options.keepCookies = false;
7ae499d @victorjonsson wip...
authored
29
30 if(typeof options.write != 'string')
31 options.write = querystring.stringify(options.write);
32
66f81a6 @victorjonsson closing in on a beta release
authored
33 if(options.headers['Content-Length'] == undefined && typeof options.write == 'string')
34 options.headers['Content-Length'] = Buffer.byteLength(options.write, 'utf8');
7ae499d @victorjonsson wip...
authored
35
66f81a6 @victorjonsson closing in on a beta release
authored
36 if(typeof options.url == 'object')
37 options.path = querystring.stringify(options.url);
9821eb6 @victorjonsson initial commit...
authored
38 else
66f81a6 @victorjonsson closing in on a beta release
authored
39 options.path = options.url;
9821eb6 @victorjonsson initial commit...
authored
40
66f81a6 @victorjonsson closing in on a beta release
authored
41 if(options.headers['User-Agent'] == undefined)
42 options['User-Agent'] = 'node dokimon scraper';
7ae499d @victorjonsson wip...
authored
43
44 if(options.timeout == undefined)
45 options.timeout = 8000;
9821eb6 @victorjonsson initial commit...
authored
46 }
47
48
49 /** * * * * * * * * * * * * * * * * * * * * * * * *
50 * @class {TestManager}
51 * @param {Object} config
52 * @param {Function} testFinishCallback - Optional
53 * * * * * * * * * * * * * * * * * * * * * * * * */
54 var TestManager = function(config, testFinishCallback) {
55
56 this.index = 0;
57 this.successes = [];
58 this.fails = [];
59 this.tests = [];
c844427 @victorjonsson New feature, issue #1
authored
60 this.executed = [];
9821eb6 @victorjonsson initial commit...
authored
61
62 /**
63 * Wrapper that makes it possible to handle the
64 * message with something other then console.log
65 *
66 * @param {String} message
50622d5 @victorjonsson new version, messages about failing tests now sent to console.error
authored
67 * @param {Boolean} [error]
9821eb6 @victorjonsson initial commit...
authored
68 */
50622d5 @victorjonsson new version, messages about failing tests now sent to console.error
authored
69 this.log = function(message, error) {
9821eb6 @victorjonsson initial commit...
authored
70 if(config.log)
50622d5 @victorjonsson new version, messages about failing tests now sent to console.error
authored
71 config.log(message, error);
72 else {
73 if(error)
74 console.error(message);
75 else
76 console.log(message)
77 }
9821eb6 @victorjonsson initial commit...
authored
78 };
79
80 /**
7c6efcd @victorjonsson work in progress...
authored
81 * @param {Test[]} tests
9821eb6 @victorjonsson initial commit...
authored
82 */
83 this.setTests = function(tests) {
7c6efcd @victorjonsson work in progress...
authored
84 this.tests = tests;
9821eb6 @victorjonsson initial commit...
authored
85 };
86
87 /**
88 * Runs tests starting from current position of this.index
89 */
90 this.run = function() {
c844427 @victorjonsson New feature, issue #1
authored
91 if(this.index == 0) {
92 this.executed = [];
93 }
94
95 var _self = this;
9821eb6 @victorjonsson initial commit...
authored
96 if(config.verbose)
97 this.log('Running scripts starting from '+this.index);
98
c844427 @victorjonsson New feature, issue #1
authored
99 var callBlockingTest = function(currentIndex) {
100 if(config.verbose)
101 _self.log('Blocking upcoming tests on index '+currentIndex+' for '+_self.tests[currentIndex].name);
102
103 _self.index = currentIndex+1;
104 _self.tests[currentIndex].execute(config.host, _self, config.verbose, function() {
105 _self.run();
106 });
107 };
108
9821eb6 @victorjonsson initial commit...
authored
109 for(var i=this.index; i < this.tests.length; i++) {
c844427 @victorjonsson New feature, issue #1
authored
110 var dependingTest = this.tests[i].options.dependsOn;
111 if( dependingTest && this.executed.indexOf(dependingTest) == -1) {
112
113 this.log('# Running depending test "'+dependingTest+'"');
114
115 this._doRunSingleTest(dependingTest, config.verbose, function() {
116 if(_self.tests[i].blocking) {
117 callBlockingTest(i);
118 }
119 else {
120 this.tests[i].execute(config.host, _self, config.verbose);
121 _self.run();
122 }
9821eb6 @victorjonsson initial commit...
authored
123 });
c844427 @victorjonsson New feature, issue #1
authored
124
9821eb6 @victorjonsson initial commit...
authored
125 break;
c844427 @victorjonsson New feature, issue #1
authored
126
127 }
128 else {
129 // blocking request
130 if(this.tests[i].blocking) {
131 callBlockingTest(i);
132 break;
133 }
134 else
135 this.tests[i].execute(config.host, this, config.verbose);
9821eb6 @victorjonsson initial commit...
authored
136 }
137 }
138 };
139
140 /**
141 * Run a specific test in the test list
142 *
143 * @param {String} testName
144 * @param {Boolean} verbose - Optional
145 */
146 this.runTest = function(testName, verbose) {
c844427 @victorjonsson New feature, issue #1
authored
147 this.executed = [];
148 this._doRunSingleTest(testName, verbose);
149 };
150
151 /**
152 * @param name
153 * @param verbose
154 * @param callback
155 * @private
156 */
157 this._doRunSingleTest = function(name, verbose, callback) {
9821eb6 @victorjonsson initial commit...
authored
158 for(var i=0; i < this.tests.length; i++) {
c844427 @victorjonsson New feature, issue #1
authored
159 if(this.tests[i].name == name) {
160 var dependingTest = this.tests[i].options.dependsOn;
161 if( dependingTest && this.executed.indexOf(dependingTest) == -1 ) {
162 var _self = this;
163 this.log('# Running depending test "'+dependingTest+'"');
164 this._doRunSingleTest(dependingTest, verbose, function() {
165 _self.tests[i].execute(config.host, _self, verbose, function() {
166 if( typeof callback == 'function' ) {
167 callback();
168 }
169 });
170 });
171 }
172 else {
173 this.tests[i].execute(config.host, this, verbose, function() {
174 if( typeof callback == 'function' ) {
175 callback();
176 }
177 });
178 }
9821eb6 @victorjonsson initial commit...
authored
179 return;
180 }
181 }
182
c844427 @victorjonsson New feature, issue #1
authored
183 throw new Error('Test "'+name+'" not found');
9821eb6 @victorjonsson initial commit...
authored
184 };
185
186 /**
187 * @param {Object} success
188 */
c844427 @victorjonsson New feature, issue #1
authored
189 this.registerSuccessfulTest = function(success) {
9821eb6 @victorjonsson initial commit...
authored
190 this.successes.push(success);
191 this.log('* '+success.name + ' successfull');
192 _checkIfFinished(this);
193 };
194
195 /**
196 * @param {Object} fail
197 * @param {Error} err - Optional
198 */
c844427 @victorjonsson New feature, issue #1
authored
199 this.registerFailedTest = function(fail, err) {
9821eb6 @victorjonsson initial commit...
authored
200 this.fails.push(fail);
50622d5 @victorjonsson new version, messages about failing tests now sent to console.error
authored
201 this.log('- '+fail.name + " FAILED! \n"+fail.message, true);
9821eb6 @victorjonsson initial commit...
authored
202 if(err)
203 this.log(err);
7ae499d @victorjonsson wip...
authored
204 this.log('');
9821eb6 @victorjonsson initial commit...
authored
205 _checkIfFinished(this);
206 };
207
208 /**
209 * Get the correct path to script. Throws error if not exists
210 *
211 * @param {String} name
212 * @return {String}
213 */
214 this.scriptNameToPath = function(name) {
215 var testFileName = name.substring(0, config.testdir.length) == config.testdir ? name : config.testdir+'/'+name;
216 if(!_hasDokimonExtension(testFileName))
217 testFileName += TestManager.TEST_EXTENSION;
218
219 return testFileName;
220 };
221
222 /**
223 * Get a list of all available scripts in given directory
224 *
225 * @param {String} path
226 * @return {Array}
227 */
228 this.loadScriptsInDir = function(path) {
229 var scripts = [];
230 filesystem.readdirSync(path).forEach(function(f) {
231 if(_hasDokimonExtension(f))
232 scripts.push(f.substr(0, f.length - TestManager.TEST_EXTENSION.length));
233 });
234 return scripts;
235 };
236
237 /**
238 * Will output test result to client when test i finished
239 *
240 * @access private
241 * @param {TestManager} manager
242 */
243 var _checkIfFinished = function(manager) {
244 if(manager.tests.length == (manager.successes.length + manager.fails.length)) {
245 if(manager.fails.length == 0) {
50622d5 @victorjonsson new version, messages about failing tests now sent to console.error
authored
246 manager.log("\n------------------------------\n"+colors.green+"Everything is fine :)"+colors.reset);
9821eb6 @victorjonsson initial commit...
authored
247 manager.log('Executed a total of '+manager.successes.length+' successfull tests');
248 }
249 else {
50622d5 @victorjonsson new version, messages about failing tests now sent to console.error
authored
250 manager.log("\n------------------------------\n"+colors.red+"All is not well :("+colors.reset, true);
251 manager.log(manager.successes.length+' test'+(manager.successes.length > 1 ? 's':'')+' was successfull', true);
252 manager.log(manager.fails.length+' test'+(manager.fails.length > 1 ? 's':'')+' failed', true);
7ae499d @victorjonsson wip...
authored
253 for(var i=0; i < manager.fails.length; i++)
50622d5 @victorjonsson new version, messages about failing tests now sent to console.error
authored
254 manager.log(" - "+manager.fails[i].name+" ("+manager.fails[i].message+")", true);
9821eb6 @victorjonsson initial commit...
authored
255 }
256
257 manager.log("");
258 if(typeof testFinishCallback == 'function')
259 testFinishCallback(manager);
260 }
261 };
262
263 /**
264 * @access private
265 * @param {String} path
266 * @return {Boolean}
267 */
268 var _hasDokimonExtension = function(path) {
269 return path.substr(-1 * TestManager.TEST_EXTENSION.length) == TestManager.TEST_EXTENSION;
270 };
271 };
272
273
274 /**
275 * Extension for scripts containing tests
276 */
e706f86 @victorjonsson changed test files extensions to .djs
authored
277 TestManager.TEST_EXTENSION = '.djs';
9821eb6 @victorjonsson initial commit...
authored
278
279
280 /** * * * * * * * * * * * * * * * * * * * * * * * *
281 * @class {Test}
282 * @param {String} testName - Name of this test
283 * @param {Object} options - requires url, optional is port:Number, method:String, write:String and headers:Object
284 * @param {Function} callback - function(response, body)
285 * @param {Boolean} blocking - Optional, whether or not this test is supposed to finish before running any other tests
286 * * * * * * * * * * * * * * * * * * * * * * * * */
287 var Test = function(testName, options, callback, blocking) {
288
66f81a6 @victorjonsson closing in on a beta release
authored
289 mergeWithDefaultOptions(options);
9821eb6 @victorjonsson initial commit...
authored
290
c844427 @victorjonsson New feature, issue #1
authored
291 this.options = options;
9821eb6 @victorjonsson initial commit...
authored
292 this.blocking = blocking;
293 this.name = testName;
294
295 /**
7c6efcd @victorjonsson work in progress...
authored
296 * @param {TestManager} manager
c844427 @victorjonsson New feature, issue #1
authored
297 * @param {String} host
298 * @param {Boolean} [verbose] - Optional
299 * @param {Function} [callback] - Optional, will be called when request is finished and test callback is called
9821eb6 @victorjonsson initial commit...
authored
300 */
c844427 @victorjonsson New feature, issue #1
authored
301 this.execute = function(host, manager, verbose, callback) {
302
303 var exeFinishCallback = function() {
304 manager.executed.push(testName);
305 if( typeof callback == 'function' )
306 callback();
307 };
9821eb6 @victorjonsson initial commit...
authored
308
309 options.host = host;
310
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
311 if(options.keepCookies && Test.lastCookies)
312 options.headers['Cookie'] = Test.lastCookies;
313
9821eb6 @victorjonsson initial commit...
authored
314 if(verbose)
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
315 manager.log("About to request "+host+" for "+testName+" with \n"+_formatJSON(options)+"\n");
9821eb6 @victorjonsson initial commit...
authored
316
c844427 @victorjonsson New feature, issue #1
authored
317 var _self = this;
7ae499d @victorjonsson wip...
authored
318
9821eb6 @victorjonsson initial commit...
authored
319 var req = require('http').request(options, function(res) {
320 var collectedBody = '';
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
321 res.setEncoding(options.encoding);
9821eb6 @victorjonsson initial commit...
authored
322 res.on('data', function (body) {
323 collectedBody += body;
324 });
325 res.on('end', function() {
7ae499d @victorjonsson wip...
authored
326
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
327 if(verbose)
328 manager.log(testName+" recieved response from "+options.host+options.path+" "+_formatJSON(res.headers)+"\n");
329
330 if(res.headers['set-cookie'] != undefined)
9b45b3e @victorjonsson new release, v 0.0.17
authored
331 Test.lastCookies = _setToGetCookies(res.headers['set-cookie'], Test.lastCookies ? Test.lastCookies:'');
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
332
333 var loc = res.headers.location == undefined ? res.headers.Location : res.headers.location;
334 if(loc != undefined) {
335
c844427 @victorjonsson New feature, issue #1
authored
336 if( loc.indexOf('/') !== 0 )
337 loc = '/'+loc;
338
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
339 if(verbose)
340 manager.log('Redirecting '+testName+' to '+loc);
7ae499d @victorjonsson wip...
authored
341
342 options.method = 'GET';
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
343 options.headers['Content-Length'] = 0;
7ae499d @victorjonsson wip...
authored
344 options.write = '';
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
345 options.path = loc;
346 if(options.path.indexOf(options.host) > -1) {
347 options.path = options.path.split(options.host)[1];
9b45b3e @victorjonsson new release, v 0.0.17
authored
348 options.url = options.path;
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
349 }
7ae499d @victorjonsson wip...
authored
350
c844427 @victorjonsson New feature, issue #1
authored
351 _self.execute(options.host, manager, verbose, exeFinishCallback);
7c6efcd @victorjonsson work in progress...
authored
352 }
7ae499d @victorjonsson wip...
authored
353 else {
354
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
355 res.url = options.path;
356
357 try {
c844427 @victorjonsson New feature, issue #1
authored
358 _self.runCallback(res, collectedBody, verbose);
359 manager.registerSuccessfulTest({name : testName, message:''});
83bb576 @victorjonsson Fixed bug that callback function wasnt always called when test failed…
authored
360 exeFinishCallback();
7ae499d @victorjonsson wip...
authored
361 }
362 catch(e) {
c844427 @victorjonsson New feature, issue #1
authored
363 manager.registerFailedTest({name : testName, message:e.message}, e);
83bb576 @victorjonsson Fixed bug that callback function wasnt always called when test failed…
authored
364 exeFinishCallback();
7ae499d @victorjonsson wip...
authored
365 }
7c6efcd @victorjonsson work in progress...
authored
366 }
9821eb6 @victorjonsson initial commit...
authored
367 });
368 });
369
370 req.on('error', function(e) {
c844427 @victorjonsson New feature, issue #1
authored
371 manager.registerFailedTest({name : testName, message:''}, e);
83bb576 @victorjonsson Fixed bug that callback function wasnt always called when test failed…
authored
372 exeFinishCallback();
9821eb6 @victorjonsson initial commit...
authored
373 });
374
83bb576 @victorjonsson Fixed bug that callback function wasnt always called when test failed…
authored
375 if(req.connection != undefined) {
376 req.connection.setTimeout(options.timeout, function() {
c844427 @victorjonsson New feature, issue #1
authored
377 manager.registerFailedTest({name: testName, message : 'Timeout after '+options.timeout+' ms'}, '');
83bb576 @victorjonsson Fixed bug that callback function wasnt always called when test failed…
authored
378 req.abort();
7ae499d @victorjonsson wip...
authored
379 exeFinishCallback();
83bb576 @victorjonsson Fixed bug that callback function wasnt always called when test failed…
authored
380 });
381 }
7ae499d @victorjonsson wip...
authored
382
9821eb6 @victorjonsson initial commit...
authored
383 req.write(options.write);
384 req.end();
385 };
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
386
387 /**
388 * Runs the test callback when request is finished
389 * @param {Object} res
390 * @param {String} collectedBody
391 * @param {Boolean} verbose
392 */
393 this.runCallback = function(res, collectedBody, verbose) {
394 callback(res, collectedBody, verbose);
395 };
396
397 /**
398 * @access private
399 * @param {Array} responseCookies
9b45b3e @victorjonsson new release, v 0.0.17
authored
400 * @param {String} currentCookies
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
401 * @return {String}
402 */
9b45b3e @victorjonsson new release, v 0.0.17
authored
403 var _setToGetCookies = function(responseCookies, currentCookies) {
404 var currentCookiesObj = {};
405 var currentCookieParts = currentCookies.split(';');
406 var cookies = '', i;
407
408 // Build up an object with all current cookies
409 for(i=0; i < currentCookieParts.length; i++) {
410 var p = currentCookieParts[i].split('=');
411 if(p.length > 1)
412 currentCookiesObj[p[0].trim()] = p[1].trim();
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
413 }
414
9b45b3e @victorjonsson new release, v 0.0.17
authored
415
416 // Go through repsonse cookies and determine which
417 // has expired or new cookies
418 var now = new Date().getTime();
419 for(i=0; i < responseCookies.length; i++) {
420 var cookieParts = responseCookies[i].split(';');
421 var cookieName = cookieParts[0].split('=')[0];
422 if(typeof currentCookiesObj[cookieName] != 'undefined') {
423 // maybe expired
424 for(var j=0; j < cookieParts.length; j++) {
425 var cookieArgs = cookieParts[j].split('=');
426 if(cookieArgs[0] == 'expires') {
427 var d = new Date(cookieArgs[0]);
428 if(d.getTime() < now) {
429 currentCookiesObj[cookieName] = false;
430 }
431 }
432 }
433 }
434
435 cookies += '; '+cookieParts[0];
436 }
437
438 // Append previous cookies (which is not deleted by expire argument)
439 Object.keys(currentCookiesObj).forEach(function(k) {
440 var val = currentCookiesObj[k];
441 if(val)
442 cookies += '; '+k+'='+val;
443 });
444
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
445 return cookies.length == 0 ? '': cookies.substr(2, cookies.length);
446 };
447
448 /**
449 * @access private
450 * @param {Object} json
451 * @return {String}
452 */
453 var _formatJSON = function(json) {
454 var str = JSON.stringify(json);
455 if(str == '[object Object]') {
456 str = '';
457 for(var x in json) {
458 var type = typeof(json[x]);
459 str += type +" : "+( type == 'string' || type == 'number' ? json[x]:json[x].toString())+"\n";
460 }
461 }
462
463 return str.replace(/\,\"/g, ",\n\"");
464 };
9821eb6 @victorjonsson initial commit...
authored
465 };
466
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
467 /**
83bb576 @victorjonsson Fixed bug that callback function wasnt always called when test failed…
authored
468 * @var {Boolean|String}
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
469 */
470 Test.lastCookies = false;
9821eb6 @victorjonsson initial commit...
authored
471
83bb576 @victorjonsson Fixed bug that callback function wasnt always called when test failed…
authored
472
9821eb6 @victorjonsson initial commit...
authored
473 /** * * * * * * * * * * * * * * * * * * * * * * * *
474 * @class {TestFormPost}
475 * @param {String} testName - Name of this test
476 * @param {Object} options - requires url, optional is port:Number, method:String, write:String and headers:Object
477 * @param {Function} callback - function(response, body)
478 * @param {Boolean} blocking - Optional, whether or not this test is supposed to finish before running any other tests
479 * * * * * * * * * * * * * * * * * * * * * * * * */
480 var TestFormPost = function(testName, options, callback, blocking) {
7ae499d @victorjonsson wip...
authored
481
482 TestFormPost.super_.call(this, testName, options, callback, blocking);
483
9821eb6 @victorjonsson initial commit...
authored
484 options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
485 if(typeof(options.write) == 'object')
486 options.write = querystring.stringify(options.write);
487
488 options.headers['Content-Length'] = Buffer.byteLength(options.write, 'utf8');
489
7ae499d @victorjonsson wip...
authored
490 options.method = 'POST';
9821eb6 @victorjonsson initial commit...
authored
491 };
492 util.inherits(TestFormPost, Test);
493
494
495 module.exports = {
496
497 /**
498 * @param {Object} config
499 * @return {TestManager}
500 */
501 TestManager : TestManager,
502
503 /**
504 * @return {Test}
505 */
506 Test : Test,
507
508 /**
66f81a6 @victorjonsson closing in on a beta release
authored
509 * @return {TestFormPost}
510 */
511 TestFormPost: TestFormPost,
512
513 /**
9821eb6 @victorjonsson initial commit...
authored
514 * @param {Test} t
83bb576 @victorjonsson Fixed bug that callback function wasnt always called when test failed…
authored
515 * @param {Object} config
516 * @param {Function} testFinishCallback - Optional
9821eb6 @victorjonsson initial commit...
authored
517 */
518 runTest : function(t, config, testFinishCallback) {
519 var manager = new TestManager(config, testFinishCallback);
520 manager.setTests([t]);
521 manager.run();
522 },
523
524 TEST_EXTENSION : TestManager.TEST_EXTENSION
525 };
Something went wrong with that request. Please try again.