Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 405 lines (339 sloc) 12.721 kB
9821eb6 @victorjonsson initial commit...
authored
1 var filesystem = require('fs'),
2 util = require('util'),
3 querystring = require('querystring');
4
7ae499d @victorjonsson wip...
authored
5
9821eb6 @victorjonsson initial commit...
authored
6 /**
7 * @param {Object} options
8 */
9 function mergeWithDefaultOptions(options) {
66f81a6 @victorjonsson closing in on a beta release
authored
10 if(options.url == undefined)
9821eb6 @victorjonsson initial commit...
authored
11 throw new Error('property url missing from options');
66f81a6 @victorjonsson closing in on a beta release
authored
12 if(options.port == undefined)
13 options.port = 80;
14 if(options.headers == undefined)
15 options.headers = {};
16 if(options.write == undefined)
17 options.write = '';
18 if(options.method == undefined)
19 options.method = 'GET';
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
20 if(options.encoding == undefined)
21 options.encoding = 'utf8';
22 if(options.keepCookies == undefined)
23 options.keepCookies = false;
7ae499d @victorjonsson wip...
authored
24
25 if(typeof options.write != 'string')
26 options.write = querystring.stringify(options.write);
27
66f81a6 @victorjonsson closing in on a beta release
authored
28 if(options.headers['Content-Length'] == undefined && typeof options.write == 'string')
29 options.headers['Content-Length'] = Buffer.byteLength(options.write, 'utf8');
7ae499d @victorjonsson wip...
authored
30
66f81a6 @victorjonsson closing in on a beta release
authored
31 if(typeof options.url == 'object')
32 options.path = querystring.stringify(options.url);
9821eb6 @victorjonsson initial commit...
authored
33 else
66f81a6 @victorjonsson closing in on a beta release
authored
34 options.path = options.url;
9821eb6 @victorjonsson initial commit...
authored
35
66f81a6 @victorjonsson closing in on a beta release
authored
36 if(options.headers['User-Agent'] == undefined)
37 options['User-Agent'] = 'node dokimon scraper';
7ae499d @victorjonsson wip...
authored
38
39 if(options.timeout == undefined)
40 options.timeout = 8000;
9821eb6 @victorjonsson initial commit...
authored
41 }
42
43
44 /** * * * * * * * * * * * * * * * * * * * * * * * *
45 * @class {TestManager}
46 * @param {Object} config
47 * @param {Function} testFinishCallback - Optional
48 * * * * * * * * * * * * * * * * * * * * * * * * */
49 var TestManager = function(config, testFinishCallback) {
50
51 this.index = 0;
52 this.successes = [];
53 this.fails = [];
54 this.tests = [];
55
56 /**
57 * Wrapper that makes it possible to handle the
58 * message with something other then console.log
59 *
60 * @param {String} message
61 */
62 this.log = function(message) {
63 if(config.log)
64 config.log(message);
65 else
66 console.log(message);
67 };
68
69 /**
7c6efcd @victorjonsson work in progress...
authored
70 * @param {Test[]} tests
9821eb6 @victorjonsson initial commit...
authored
71 */
72 this.setTests = function(tests) {
7c6efcd @victorjonsson work in progress...
authored
73 this.tests = tests;
9821eb6 @victorjonsson initial commit...
authored
74 };
75
76 /**
77 * Runs tests starting from current position of this.index
78 */
79 this.run = function() {
80 if(config.verbose)
81 this.log('Running scripts starting from '+this.index);
82
83 for(var i=this.index; i < this.tests.length; i++) {
84 // blocking request
85 if(this.tests[i].blocking) {
86 if(config.verbose)
7ae499d @victorjonsson wip...
authored
87 this.log('Blocking upcoming tests on index '+i+' for '+this.tests[i].name);
9821eb6 @victorjonsson initial commit...
authored
88
89 this.index = i+1;
90 var self = this;
91 this.tests[i].execute(config.host, this, config.verbose, function() {
92 self.run();
93 });
94 break;
95 }
96 else
97 this.tests[i].execute(config.host, this, config.verbose);
98 }
99 };
100
101 /**
102 * Run a specific test in the test list
103 *
104 * @param {String} testName
105 * @param {Boolean} verbose - Optional
106 */
107 this.runTest = function(testName, verbose) {
108 for(var i=0; i < this.tests.length; i++) {
109 if(this.tests[i].name == testName) {
7ae499d @victorjonsson wip...
authored
110 this.tests[i].execute(config.host, this, config.verbose);
9821eb6 @victorjonsson initial commit...
authored
111 return;
112 }
113 }
114
115 throw new Error('Test not found');
116 };
117
118 /**
119 * @param {Object} success
120 */
121 this.registerSuccessfullTest = function(success) {
122 this.successes.push(success);
123 this.log('* '+success.name + ' successfull');
124 _checkIfFinished(this);
125 };
126
127 /**
128 * @param {Object} fail
129 * @param {Error} err - Optional
130 */
131 this.registerFailedtest = function(fail, err) {
132 this.fails.push(fail);
133 this.log('- '+fail.name + " FAILED! \n"+fail.message);
134 if(err)
135 this.log(err);
7ae499d @victorjonsson wip...
authored
136 this.log('');
9821eb6 @victorjonsson initial commit...
authored
137 _checkIfFinished(this);
138 };
139
140 /**
141 * Get the correct path to script. Throws error if not exists
142 *
143 * @param {String} name
144 * @return {String}
145 */
146 this.scriptNameToPath = function(name) {
147 var testFileName = name.substring(0, config.testdir.length) == config.testdir ? name : config.testdir+'/'+name;
148 if(!_hasDokimonExtension(testFileName))
149 testFileName += TestManager.TEST_EXTENSION;
150
151 return testFileName;
152 };
153
154 /**
155 * Get a list of all available scripts in given directory
156 *
157 * @param {String} path
158 * @return {Array}
159 */
160 this.loadScriptsInDir = function(path) {
161 var scripts = [];
162 filesystem.readdirSync(path).forEach(function(f) {
163 if(_hasDokimonExtension(f))
164 scripts.push(f.substr(0, f.length - TestManager.TEST_EXTENSION.length));
165 });
166 return scripts;
167 };
168
169 /**
170 * Will output test result to client when test i finished
171 *
172 * @access private
173 * @param {TestManager} manager
174 */
175 var _checkIfFinished = function(manager) {
176 if(manager.tests.length == (manager.successes.length + manager.fails.length)) {
177 if(manager.fails.length == 0) {
178 manager.log("\n------------------------------\nEverything is fine :)");
179 manager.log('Executed a total of '+manager.successes.length+' successfull tests');
180 }
181 else {
182 manager.log("\n------------------------------\nAll is not well :(");
183 manager.log(manager.successes.length+' test'+(manager.successes.length > 1 ? 's':'')+' was successfull');
7ae499d @victorjonsson wip...
authored
184 manager.log(manager.fails.length+' test'+(manager.fails.length > 1 ? 's':'')+' failed');
185 for(var i=0; i < manager.fails.length; i++)
186 manager.log(" - "+manager.fails[i].name+" ("+manager.fails[i].message+")");
9821eb6 @victorjonsson initial commit...
authored
187 }
188
189 manager.log("");
190 if(typeof testFinishCallback == 'function')
191 testFinishCallback(manager);
192 }
193 };
194
195 /**
196 * @access private
197 * @param {String} path
198 * @return {Boolean}
199 */
200 var _hasDokimonExtension = function(path) {
201 return path.substr(-1 * TestManager.TEST_EXTENSION.length) == TestManager.TEST_EXTENSION;
202 };
203 };
204
205
206 /**
207 * Extension for scripts containing tests
208 */
209 TestManager.TEST_EXTENSION = '.dokimon';
210
211
212 /** * * * * * * * * * * * * * * * * * * * * * * * *
213 * @class {Test}
214 * @param {String} testName - Name of this test
215 * @param {Object} options - requires url, optional is port:Number, method:String, write:String and headers:Object
216 * @param {Function} callback - function(response, body)
217 * @param {Boolean} blocking - Optional, whether or not this test is supposed to finish before running any other tests
218 * * * * * * * * * * * * * * * * * * * * * * * * */
219 var Test = function(testName, options, callback, blocking) {
220
66f81a6 @victorjonsson closing in on a beta release
authored
221 mergeWithDefaultOptions(options);
9821eb6 @victorjonsson initial commit...
authored
222
223 this.blocking = blocking;
224 this.name = testName;
225
226 /**
7c6efcd @victorjonsson work in progress...
authored
227 * @param {TestManager} manager
9821eb6 @victorjonsson initial commit...
authored
228 * @param {Boolean} verbose - Optional
229 * @param {Function} exeFinishCallback - Optional, will be called when request is finished and test callback is called
230 */
7c6efcd @victorjonsson work in progress...
authored
231 this.execute = function(host, manager, verbose, exeFinishCallback) {
9821eb6 @victorjonsson initial commit...
authored
232
233 options.host = host;
234
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
235 if(options.keepCookies && Test.lastCookies)
236 options.headers['Cookie'] = Test.lastCookies;
237
9821eb6 @victorjonsson initial commit...
authored
238 if(verbose)
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
239 manager.log("About to request "+host+" for "+testName+" with \n"+_formatJSON(options)+"\n");
9821eb6 @victorjonsson initial commit...
authored
240
7ae499d @victorjonsson wip...
authored
241 var self = this;
242
9821eb6 @victorjonsson initial commit...
authored
243 var req = require('http').request(options, function(res) {
244 var collectedBody = '';
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
245 res.setEncoding(options.encoding);
9821eb6 @victorjonsson initial commit...
authored
246 res.on('data', function (body) {
247 collectedBody += body;
248 });
249 res.on('end', function() {
7ae499d @victorjonsson wip...
authored
250
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
251 if(verbose)
252 manager.log(testName+" recieved response from "+options.host+options.path+" "+_formatJSON(res.headers)+"\n");
253
254 if(res.headers['set-cookie'] != undefined)
255 Test.lastCookies = _setToGetCookies(res.headers['set-cookie']);
256
257 var loc = res.headers.location == undefined ? res.headers.Location : res.headers.location;
258 if(loc != undefined) {
259
260 if(verbose)
261 manager.log('Redirecting '+testName+' to '+loc);
7ae499d @victorjonsson wip...
authored
262
263 options.method = 'GET';
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
264 options.headers['Content-Length'] = 0;
7ae499d @victorjonsson wip...
authored
265 options.write = '';
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
266 options.path = loc;
267 if(options.path.indexOf(options.host) > -1) {
268 options.path = options.path.split(options.host)[1];
269 }
7ae499d @victorjonsson wip...
authored
270
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
271 self.execute(options.host, manager, verbose, exeFinishCallback);
7c6efcd @victorjonsson work in progress...
authored
272 }
7ae499d @victorjonsson wip...
authored
273 else {
274
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
275 res.url = options.path;
276
277 try {
278 self.runCallback(res, collectedBody, verbose);
7ae499d @victorjonsson wip...
authored
279 manager.registerSuccessfullTest({name : testName, message:''});
280 if(typeof exeFinishCallback == 'function')
281 exeFinishCallback();
282 }
283 catch(e) {
284 manager.registerFailedtest({name : testName, message:e.message}, e);
285 if(typeof exeFinishCallback == 'function')
286 exeFinishCallback();
287 }
7c6efcd @victorjonsson work in progress...
authored
288 }
9821eb6 @victorjonsson initial commit...
authored
289 });
290 });
291
292 req.on('error', function(e) {
7c6efcd @victorjonsson work in progress...
authored
293 manager.registerFailedtest({name : testName, message:''}, e);
9821eb6 @victorjonsson initial commit...
authored
294 });
295
7ae499d @victorjonsson wip...
authored
296 req.connection.setTimeout(options.timeout, function() {
297 manager.registerFailedtest({name: testName, message : 'Timeout after '+options.timeout+' ms'}, '');
298 req.abort();
299 if(typeof exeFinishCallback == 'function')
300 exeFinishCallback();
301 });
302
9821eb6 @victorjonsson initial commit...
authored
303 req.write(options.write);
304 req.end();
305 };
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
306
307 /**
308 * Runs the test callback when request is finished
309 * @param {Object} res
310 * @param {String} collectedBody
311 * @param {Boolean} verbose
312 */
313 this.runCallback = function(res, collectedBody, verbose) {
314 callback(res, collectedBody, verbose);
315 };
316
317 /**
318 * @access private
319 * @param {Array} responseCookies
320 * @return {String}
321 */
322 var _setToGetCookies = function(responseCookies) {
323 var cookies = '';
324 for(var i=0; i < responseCookies.length; i++) {
325 cookies += ', '+responseCookies[i].split(';')[0];
326 }
327
328 return cookies.length == 0 ? '': cookies.substr(2, cookies.length);
329 };
330
331 /**
332 * @access private
333 * @param {Object} json
334 * @return {String}
335 */
336 var _formatJSON = function(json) {
337 var str = JSON.stringify(json);
338 if(str == '[object Object]') {
339 str = '';
340 for(var x in json) {
341 var type = typeof(json[x]);
342 str += type +" : "+( type == 'string' || type == 'number' ? json[x]:json[x].toString())+"\n";
343 }
344 }
345
346 return str.replace(/\,\"/g, ",\n\"");
347 };
9821eb6 @victorjonsson initial commit...
authored
348 };
349
bec465f @victorjonsson TestPostForm bug fixes, now possible to maintain a server session
authored
350 /**
351 * @
352 */
353 Test.lastCookies = false;
9821eb6 @victorjonsson initial commit...
authored
354
355 /** * * * * * * * * * * * * * * * * * * * * * * * *
356 * @class {TestFormPost}
357 * @param {String} testName - Name of this test
358 * @param {Object} options - requires url, optional is port:Number, method:String, write:String and headers:Object
359 * @param {Function} callback - function(response, body)
360 * @param {Boolean} blocking - Optional, whether or not this test is supposed to finish before running any other tests
361 * * * * * * * * * * * * * * * * * * * * * * * * */
362 var TestFormPost = function(testName, options, callback, blocking) {
7ae499d @victorjonsson wip...
authored
363
364 TestFormPost.super_.call(this, testName, options, callback, blocking);
365
9821eb6 @victorjonsson initial commit...
authored
366 options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
367 if(typeof(options.write) == 'object')
368 options.write = querystring.stringify(options.write);
369
370 options.headers['Content-Length'] = Buffer.byteLength(options.write, 'utf8');
371
7ae499d @victorjonsson wip...
authored
372 options.method = 'POST';
9821eb6 @victorjonsson initial commit...
authored
373 };
374 util.inherits(TestFormPost, Test);
375
376
377 module.exports = {
378
379 /**
380 * @param {Object} config
381 * @return {TestManager}
382 */
383 TestManager : TestManager,
384
385 /**
386 * @return {Test}
387 */
388 Test : Test,
389
390 /**
66f81a6 @victorjonsson closing in on a beta release
authored
391 * @return {TestFormPost}
392 */
393 TestFormPost: TestFormPost,
394
395 /**
9821eb6 @victorjonsson initial commit...
authored
396 * @param {Test} t
397 */
398 runTest : function(t, config, testFinishCallback) {
399 var manager = new TestManager(config, testFinishCallback);
400 manager.setTests([t]);
401 manager.run();
402 },
403
404 TEST_EXTENSION : TestManager.TEST_EXTENSION
405 };
Something went wrong with that request. Please try again.