newjs 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
- == 1.2.1 2008-02-18
1
+ == 1.3.0 2008-02-18
2
2
 
3
+ * rails: javascript_test generator - creates HTML test, plus installs assets + rake tasks
3
4
  * install_website: txt2html uses RUBYFORGE_PROJECT for downloads url
4
5
 
5
6
  == 1.2.0 2008-02-18
@@ -54,6 +54,14 @@ newjs_theme_generators/plain_theme/plain_theme_generator.rb
54
54
  newjs_theme_generators/plain_theme/templates/website/javascripts/rounded_corners_lite.inc.js
55
55
  newjs_theme_generators/plain_theme/templates/website/stylesheets/screen.css
56
56
  newjs_theme_generators/plain_theme/templates/website/template.html.erb
57
+ rails_generators/javascript_test/USAGE
58
+ rails_generators/javascript_test/javascript_test_generator.rb
59
+ rails_generators/javascript_test/templates/assets/jsunittest.js
60
+ rails_generators/javascript_test/templates/assets/unittest.css
61
+ rails_generators/javascript_test/templates/plugins/javascript_unittest/README
62
+ rails_generators/javascript_test/templates/plugins/javascript_unittest/lib/jstest.rb
63
+ rails_generators/javascript_test/templates/plugins/javascript_unittest/tasks/runner.rake
64
+ rails_generators/javascript_test/templates/test.html.erb
57
65
  script/destroy
58
66
  script/generate
59
67
  script/txt2html
@@ -65,6 +73,7 @@ test/test_functional_test_generator.rb
65
73
  test/test_generator_helper.rb
66
74
  test/test_helper.rb
67
75
  test/test_install_website_generator.rb
76
+ test/test_javascript_test_generator.rb
68
77
  test/test_newjs_generator.rb
69
78
  test/test_plain_theme_generator.rb
70
79
  test/test_unit_test_generator.rb
@@ -5,7 +5,6 @@ class FunctionalTestGenerator < RubiGen::Base
5
5
  def initialize(runtime_args, runtime_options = {})
6
6
  super
7
7
  usage if args.empty?
8
- @destination_root = File.expand_path(".")
9
8
  @name = args.shift
10
9
  @dist_name = args.shift || base_name
11
10
  extract_options
@@ -26,7 +25,7 @@ class FunctionalTestGenerator < RubiGen::Base
26
25
  <<-EOS
27
26
  Creates a functional test file for the final distribution JavaScript file(s).
28
27
 
29
- USAGE: #{$0} #{spec.name} name [dist_name]"
28
+ USAGE: #{$0} #{spec.name} name [dist_name]
30
29
 
31
30
  NOTES:
32
31
  * name - creates a file test/functional/name_test.html
@@ -25,7 +25,7 @@ class UnitTestGenerator < RubiGen::Base
25
25
  <<-EOS
26
26
  Creates an HTML unit test file for a JavaScript library.
27
27
 
28
- USAGE: #{$0} #{spec.name} name [library_name]"
28
+ USAGE: #{$0} #{spec.name} name [library_name]
29
29
 
30
30
  NOTES:
31
31
  * name - creates a file test/unit/name_test.html
@@ -1,8 +1,8 @@
1
1
  module Newjs #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
- MINOR = 2
5
- TINY = 1
4
+ MINOR = 3
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -0,0 +1,5 @@
1
+ Description:
2
+
3
+
4
+ Usage:
5
+
@@ -0,0 +1,65 @@
1
+ class JavascriptTestGenerator < Rails::Generator::Base
2
+
3
+ default_options :author => nil
4
+
5
+ attr_reader :name, :library_name
6
+
7
+ def initialize(runtime_args, runtime_options = {})
8
+ super
9
+ usage if args.empty?
10
+ @name = args.shift
11
+ @library_name = args.shift || name
12
+ extract_options
13
+ end
14
+
15
+ def manifest
16
+ record do |m|
17
+ # Ensure appropriate folder(s) exists
18
+ m.directory 'test/javascript/assets'
19
+ m.directory 'vendor/plugins/javascript_unittest/lib'
20
+ m.directory 'vendor/plugins/javascript_unittest/tasks'
21
+
22
+ # Create stubs
23
+ m.file "assets/jsunittest.js", "test/javascript/assets/jsunittest.js"
24
+ m.file "assets/unittest.css", "test/javascript/assets/unittest.css"
25
+ m.file "plugins/javascript_unittest/lib/jstest.rb",
26
+ "vendor/plugins/javascript_unittest/lib/jstest.rb"
27
+ m.file "plugins/javascript_unittest/tasks/runner.rake",
28
+ "vendor/plugins/javascript_unittest/tasks/runner.rake"
29
+ m.file "plugins/javascript_unittest/README",
30
+ "vendor/plugins/javascript_unittest/README"
31
+ m.template "test.html.erb", "test/javascript/#{name}_test.html"
32
+ end
33
+ end
34
+
35
+ protected
36
+ def banner
37
+ <<-EOS
38
+ Creates an HTML unit test file for a JavaScript library.
39
+
40
+ USAGE: #{$0} #{spec.name} name [library_name]
41
+
42
+ NOTES:
43
+ * name - creates a file test/javascript/name_test.html
44
+ * library_name - is for a file public/javascripts/library_name.js
45
+ EOS
46
+ end
47
+
48
+ def add_options!(opts)
49
+ # opts.separator ''
50
+ # opts.separator 'Options:'
51
+ # For each option below, place the default
52
+ # at the top of the file next to "default_options"
53
+ # opts.on("-a", "--author=\"Your Name\"", String,
54
+ # "Some comment about this option",
55
+ # "Default: none") { |options[:author]| }
56
+ # opts.on("-v", "--version", "Show the #{File.basename($0)} version number and quit.")
57
+ end
58
+
59
+ def extract_options
60
+ # for each option, extract it into a local variable (and create an "attr_reader :author" at the top)
61
+ # Templates can access these value via the attr_reader-generated methods, but not the
62
+ # raw instance variable value.
63
+ # @author = options[:author]
64
+ end
65
+ end
@@ -0,0 +1,964 @@
1
+ /* Jsunittest, version 0.6.0
2
+ * (c) 2008 Dr Nic Williams
3
+ *
4
+ * Jsunittest is freely distributable under
5
+ * the terms of an MIT-style license.
6
+ * For details, see the web site: http://jsunittest.rubyforge.org
7
+ *
8
+ *--------------------------------------------------------------------------*/
9
+
10
+ var JsUnitTest = {
11
+ Version: '0.6.0',
12
+ };
13
+
14
+ var DrNicTest = {
15
+ Unit: {},
16
+ inspect: function(object) {
17
+ try {
18
+ if (typeof object == "undefined") return 'undefined';
19
+ if (object === null) return 'null';
20
+ if (typeof object == "string") {
21
+ var useDoubleQuotes = arguments[1];
22
+ var escapedString = this.gsub(object, /[\x00-\x1f\\]/, function(match) {
23
+ var character = String.specialChar[match[0]];
24
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
25
+ });
26
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
27
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
28
+ };
29
+ return String(object);
30
+ } catch (e) {
31
+ if (e instanceof RangeError) return '...';
32
+ throw e;
33
+ }
34
+ },
35
+ $: function(element) {
36
+ if (arguments.length > 1) {
37
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
38
+ elements.push(this.$(arguments[i]));
39
+ return elements;
40
+ }
41
+ if (typeof element == "string")
42
+ element = document.getElementById(element);
43
+ return element;
44
+ },
45
+ gsub: function(source, pattern, replacement) {
46
+ var result = '', match;
47
+ replacement = arguments.callee.prepareReplacement(replacement);
48
+
49
+ while (source.length > 0) {
50
+ if (match = source.match(pattern)) {
51
+ result += source.slice(0, match.index);
52
+ result += DrNicTest.String.interpret(replacement(match));
53
+ source = source.slice(match.index + match[0].length);
54
+ } else {
55
+ result += source, source = '';
56
+ }
57
+ }
58
+ return result;
59
+ },
60
+ scan: function(source, pattern, iterator) {
61
+ this.gsub(source, pattern, iterator);
62
+ return String(source);
63
+ },
64
+ escapeHTML: function(data) {
65
+ return data.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
66
+ },
67
+ arrayfromargs: function(args) {
68
+ var myarray = new Array();
69
+ var i;
70
+
71
+ for (i=0;i<args.length;i++)
72
+ myarray[i] = args[i];
73
+
74
+ return myarray;
75
+ },
76
+ hashToSortedArray: function(hash) {
77
+ var results = [];
78
+ for (key in hash) {
79
+ results.push([key, hash[key]]);
80
+ }
81
+ return results.sort();
82
+ },
83
+ flattenArray: function(array) {
84
+ var results = arguments[1] || [];
85
+ for (var i=0; i < array.length; i++) {
86
+ var object = array[i];
87
+ if (object != null && typeof object == "object" &&
88
+ 'splice' in object && 'join' in object) {
89
+ this.flattenArray(object, results);
90
+ } else {
91
+ results.push(object);
92
+ }
93
+ };
94
+ return results;
95
+ },
96
+ selectorMatch: function(expression, element) {
97
+ var tokens = [];
98
+ var patterns = {
99
+ // combinators must be listed first
100
+ // (and descendant needs to be last combinator)
101
+ laterSibling: /^\s*~\s*/,
102
+ child: /^\s*>\s*/,
103
+ adjacent: /^\s*\+\s*/,
104
+ descendant: /^\s/,
105
+
106
+ // selectors follow
107
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
108
+ id: /^#([\w\-\*]+)(\b|$)/,
109
+ className: /^\.([\w\-\*]+)(\b|$)/,
110
+ pseudo:
111
+ /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
112
+ attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
113
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
114
+ };
115
+
116
+ var assertions = {
117
+ tagName: function(element, matches) {
118
+ return matches[1].toUpperCase() == element.tagName.toUpperCase();
119
+ },
120
+
121
+ className: function(element, matches) {
122
+ return Element.hasClassName(element, matches[1]);
123
+ },
124
+
125
+ id: function(element, matches) {
126
+ return element.id === matches[1];
127
+ },
128
+
129
+ attrPresence: function(element, matches) {
130
+ return Element.hasAttribute(element, matches[1]);
131
+ },
132
+
133
+ attr: function(element, matches) {
134
+ var nodeValue = Element.readAttribute(element, matches[1]);
135
+ return nodeValue && operators[matches[2]](nodeValue, matches[5] || matches[6]);
136
+ }
137
+ };
138
+ var e = this.expression, ps = patterns, as = assertions;
139
+ var le, p, m;
140
+
141
+ while (e && le !== e && (/\S/).test(e)) {
142
+ le = e;
143
+ for (var i in ps) {
144
+ p = ps[i];
145
+ if (m = e.match(p)) {
146
+ // use the Selector.assertions methods unless the selector
147
+ // is too complex.
148
+ if (as[i]) {
149
+ tokens.push([i, Object.clone(m)]);
150
+ e = e.replace(m[0], '');
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ var match = true, name, matches;
157
+ for (var i = 0, token; token = tokens[i]; i++) {
158
+ name = token[0], matches = token[1];
159
+ if (!assertions[name](element, matches)) {
160
+ match = false; break;
161
+ }
162
+ }
163
+
164
+ return match;
165
+ },
166
+ toQueryParams: function(query, separator) {
167
+ var query = query || window.location.search;
168
+ var match = query.replace(/^\s+/, '').replace(/\s+$/, '').match(/([^?#]*)(#.*)?$/);
169
+ if (!match) return { };
170
+
171
+ var hash = {};
172
+ var parts = match[1].split(separator || '&');
173
+ for (var i=0; i < parts.length; i++) {
174
+ var pair = parts[i].split('=');
175
+ if (pair[0]) {
176
+ var key = decodeURIComponent(pair.shift());
177
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
178
+ if (value != undefined) value = decodeURIComponent(value);
179
+
180
+ if (key in hash) {
181
+ var object = hash[key];
182
+ var isArray = object != null && typeof object == "object" &&
183
+ 'splice' in object && 'join' in object
184
+ if (!isArray) hash[key] = [hash[key]];
185
+ hash[key].push(value);
186
+ }
187
+ else hash[key] = value;
188
+ }
189
+ };
190
+ return hash;
191
+ },
192
+
193
+ String: {
194
+ interpret: function(value) {
195
+ return value == null ? '' : String(value);
196
+ }
197
+ }
198
+ };
199
+
200
+ DrNicTest.gsub.prepareReplacement = function(replacement) {
201
+ if (typeof replacement == "function") return replacement;
202
+ var template = new Template(replacement);
203
+ return function(match) { return template.evaluate(match) };
204
+ };
205
+
206
+ DrNicTest.Template = function(template, pattern) {
207
+ this.template = template; //template.toString();
208
+ this.pattern = pattern || DrNicTest.Template.Pattern;
209
+ };
210
+
211
+ DrNicTest.Template.prototype.evaluate = function(object) {
212
+ if (typeof object.toTemplateReplacements == "function")
213
+ object = object.toTemplateReplacements();
214
+
215
+ return DrNicTest.gsub(this.template, this.pattern, function(match) {
216
+ if (object == null) return '';
217
+
218
+ var before = match[1] || '';
219
+ if (before == '\\') return match[2];
220
+
221
+ var ctx = object, expr = match[3];
222
+ var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
223
+ match = pattern.exec(expr);
224
+ if (match == null) return before;
225
+
226
+ while (match != null) {
227
+ var comp = (match[1].indexOf('[]') === 0) ? match[2].gsub('\\\\]', ']') : match[1];
228
+ ctx = ctx[comp];
229
+ if (null == ctx || '' == match[3]) break;
230
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
231
+ match = pattern.exec(expr);
232
+ }
233
+
234
+ return before + DrNicTest.String.interpret(ctx);
235
+ });
236
+ }
237
+
238
+ DrNicTest.Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
239
+ DrNicTest.Event = {};
240
+ // written by Dean Edwards, 2005
241
+ // with input from Tino Zijdel, Matthias Miller, Diego Perini
242
+ // namespaced by Dr Nic Williams 2008
243
+
244
+ // http://dean.edwards.name/weblog/2005/10/add-event/
245
+ // http://dean.edwards.name/weblog/2005/10/add-event2/
246
+ DrNicTest.Event.addEvent = function(element, type, handler) {
247
+ if (element.addEventListener) {
248
+ element.addEventListener(type, handler, false);
249
+ } else {
250
+ // assign each event handler a unique ID
251
+ if (!handler.$$guid) handler.$$guid = addEvent.guid++;
252
+ // create a hash table of event types for the element
253
+ if (!element.events) element.events = {};
254
+ // create a hash table of event handlers for each element/event pair
255
+ var handlers = element.events[type];
256
+ if (!handlers) {
257
+ handlers = element.events[type] = {};
258
+ // store the existing event handler (if there is one)
259
+ if (element["on" + type]) {
260
+ handlers[0] = element["on" + type];
261
+ }
262
+ }
263
+ // store the event handler in the hash table
264
+ handlers[handler.$$guid] = handler;
265
+ // assign a global event handler to do all the work
266
+ element["on" + type] = handleEvent;
267
+ }
268
+ };
269
+ // a counter used to create unique IDs
270
+ DrNicTest.Event.addEvent.guid = 1;
271
+
272
+ DrNicTest.Event.removeEvent = function(element, type, handler) {
273
+ if (element.removeEventListener) {
274
+ element.removeEventListener(type, handler, false);
275
+ } else {
276
+ // delete the event handler from the hash table
277
+ if (element.events && element.events[type]) {
278
+ delete element.events[type][handler.$$guid];
279
+ }
280
+ }
281
+ };
282
+
283
+ DrNicTest.Event.handleEvent = function(event) {
284
+ var returnValue = true;
285
+ // grab the event object (IE uses a global event object)
286
+ event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
287
+ // get a reference to the hash table of event handlers
288
+ var handlers = this.events[event.type];
289
+ // execute each event handler
290
+ for (var i in handlers) {
291
+ this.$$handleEvent = handlers[i];
292
+ if (this.$$handleEvent(event) === false) {
293
+ returnValue = false;
294
+ }
295
+ }
296
+ return returnValue;
297
+ };
298
+
299
+ DrNicTest.Event.fixEvent = function(event) {
300
+ // add W3C standard event methods
301
+ event.preventDefault = fixEvent.preventDefault;
302
+ event.stopPropagation = fixEvent.stopPropagation;
303
+ return event;
304
+ };
305
+ DrNicTest.Event.fixEvent.preventDefault = function() {
306
+ this.returnValue = false;
307
+ };
308
+ DrNicTest.Event.fixEvent.stopPropagation = function() {
309
+ this.cancelBubble = true;
310
+ };
311
+
312
+ DrNicTest.Unit.Logger = function(element) {
313
+ this.element = DrNicTest.$(element);
314
+ if (this.element) this._createLogTable();
315
+ };
316
+
317
+ DrNicTest.Unit.Logger.prototype.start = function(testName) {
318
+ if (!this.element) return;
319
+ var tbody = this.element.getElementsByTagName('tbody')[0];
320
+ tbody.innerHTML = tbody.innerHTML + '<tr><td>' + testName + '</td><td></td><td></td></tr>';
321
+ };
322
+
323
+ DrNicTest.Unit.Logger.prototype.setStatus = function(status) {
324
+ var logline = this.getLastLogLine();
325
+ logline.className = status;
326
+ var statusCell = logline.getElementsByTagName('td')[1];
327
+ statusCell.innerHTML = status;
328
+ };
329
+
330
+ DrNicTest.Unit.Logger.prototype.finish = function(status, summary) {
331
+ if (!this.element) return;
332
+ this.setStatus(status);
333
+ this.message(summary);
334
+ };
335
+
336
+ DrNicTest.Unit.Logger.prototype.message = function(message) {
337
+ if (!this.element) return;
338
+ var cell = this.getMessageCell();
339
+ cell.innerHTML = this._toHTML(message);
340
+ };
341
+
342
+ DrNicTest.Unit.Logger.prototype.summary = function(summary) {
343
+ if (!this.element) return;
344
+ var div = this.element.getElementsByTagName('div')[0];
345
+ div.innerHTML = this._toHTML(summary);
346
+ };
347
+
348
+ DrNicTest.Unit.Logger.prototype.getLastLogLine = function() {
349
+ var tbody = this.element.getElementsByTagName('tbody')[0];
350
+ var loglines = tbody.getElementsByTagName('tr');
351
+ return loglines[loglines.length - 1];
352
+ };
353
+
354
+ DrNicTest.Unit.Logger.prototype.getMessageCell = function() {
355
+ var logline = this.getLastLogLine();
356
+ return logline.getElementsByTagName('td')[2];
357
+ };
358
+
359
+ DrNicTest.Unit.Logger.prototype._createLogTable = function() {
360
+ var html = '<div class="logsummary">running...</div>' +
361
+ '<table class="logtable">' +
362
+ '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
363
+ '<tbody class="loglines"></tbody>' +
364
+ '</table>';
365
+ this.element.innerHTML = html;
366
+ };
367
+
368
+ DrNicTest.Unit.Logger.prototype.appendActionButtons = function(actions) {
369
+ // actions = $H(actions);
370
+ // if (!actions.any()) return;
371
+ // var div = new Element("div", {className: 'action_buttons'});
372
+ // actions.inject(div, function(container, action) {
373
+ // var button = new Element("input").setValue(action.key).observe("click", action.value);
374
+ // button.type = "button";
375
+ // return container.insert(button);
376
+ // });
377
+ // this.getMessageCell().insert(div);
378
+ };
379
+
380
+ DrNicTest.Unit.Logger.prototype._toHTML = function(txt) {
381
+ return DrNicTest.escapeHTML(txt).replace(/\n/g,"<br/>");
382
+ };
383
+ DrNicTest.Unit.MessageTemplate = function(string) {
384
+ var parts = [];
385
+ var str = DrNicTest.scan((string || ''), /(?=[^\\])\?|(?:\\\?|[^\?])+/, function(part) {
386
+ parts.push(part[0]);
387
+ });
388
+ this.parts = parts;
389
+ };
390
+
391
+ DrNicTest.Unit.MessageTemplate.prototype.evaluate = function(params) {
392
+ var results = [];
393
+ for (var i=0; i < this.parts.length; i++) {
394
+ var part = this.parts[i];
395
+ var result = (part == '?') ? DrNicTest.inspect(params.shift()) : part.replace(/\\\?/, '?');
396
+ results.push(result);
397
+ };
398
+ return results.join('');
399
+ };
400
+ // A generic function for performming AJAX requests
401
+ // It takes one argument, which is an object that contains a set of options
402
+ // All of which are outline in the comments, below
403
+ // From John Resig's book Pro JavaScript Techniques
404
+ // published by Apress, 2006-8
405
+ DrNicTest.ajax = function( options ) {
406
+
407
+ // Load the options object with defaults, if no
408
+ // values were provided by the user
409
+ options = {
410
+ // The type of HTTP Request
411
+ type: options.type || "POST",
412
+
413
+ // The URL the request will be made to
414
+ url: options.url || "",
415
+
416
+ // How long to wait before considering the request to be a timeout
417
+ timeout: options.timeout || 5000,
418
+
419
+ // Functions to call when the request fails, succeeds,
420
+ // or completes (either fail or succeed)
421
+ onComplete: options.onComplete || function(){},
422
+ onError: options.onError || function(){},
423
+ onSuccess: options.onSuccess || function(){},
424
+
425
+ // The data type that'll be returned from the server
426
+ // the default is simply to determine what data was returned from the
427
+ // and act accordingly.
428
+ data: options.data || ""
429
+ };
430
+
431
+ // Create the request object
432
+ var xml = new XMLHttpRequest();
433
+
434
+ // Open the asynchronous POST request
435
+ xml.open(options.type, options.url, true);
436
+
437
+ // We're going to wait for a request for 5 seconds, before giving up
438
+ var timeoutLength = 5000;
439
+
440
+ // Keep track of when the request has been succesfully completed
441
+ var requestDone = false;
442
+
443
+ // Initalize a callback which will fire 5 seconds from now, cancelling
444
+ // the request (if it has not already occurred).
445
+ setTimeout(function(){
446
+ requestDone = true;
447
+ }, timeoutLength);
448
+
449
+ // Watch for when the state of the document gets updated
450
+ xml.onreadystatechange = function(){
451
+ // Wait until the data is fully loaded,
452
+ // and make sure that the request hasn't already timed out
453
+ if ( xml.readyState == 4 && !requestDone ) {
454
+
455
+ // Check to see if the request was successful
456
+ if ( httpSuccess( xml ) ) {
457
+
458
+ // Execute the success callback with the data returned from the server
459
+ options.onSuccess( httpData( xml, options.type ) );
460
+
461
+ // Otherwise, an error occurred, so execute the error callback
462
+ } else {
463
+ options.onError();
464
+ }
465
+
466
+ // Call the completion callback
467
+ options.onComplete();
468
+
469
+ // Clean up after ourselves, to avoid memory leaks
470
+ xml = null;
471
+ }
472
+ };
473
+
474
+ // Establish the connection to the server
475
+ xml.send();
476
+
477
+ // Determine the success of the HTTP response
478
+ function httpSuccess(r) {
479
+ try {
480
+ // If no server status is provided, and we're actually
481
+ // requesting a local file, then it was successful
482
+ return !r.status && location.protocol == "file:" ||
483
+
484
+ // Any status in the 200 range is good
485
+ ( r.status >= 200 && r.status < 300 ) ||
486
+
487
+ // Successful if the document has not been modified
488
+ r.status == 304 ||
489
+
490
+ // Safari returns an empty status if the file has not been modified
491
+ navigator.userAgent.indexOf("Safari") >= 0 && typeof r.status == "undefined";
492
+ } catch(e){}
493
+
494
+ // If checking the status failed, then assume that the request failed too
495
+ return false;
496
+ }
497
+
498
+ // Extract the correct data from the HTTP response
499
+ function httpData(r,type) {
500
+ // Get the content-type header
501
+ var ct = r.getResponseHeader("content-type");
502
+
503
+ // If no default type was provided, determine if some
504
+ // form of XML was returned from the server
505
+ var data = !type && ct && ct.indexOf("xml") >= 0;
506
+
507
+ // Get the XML Document object if XML was returned from
508
+ // the server, otherwise return the text contents returned by the server
509
+ data = type == "xml" || data ? r.responseXML : r.responseText;
510
+
511
+ // If the specified type is "script", execute the returned text
512
+ // response as if it was JavaScript
513
+ if ( type == "script" )
514
+ eval.call( window, data );
515
+
516
+ // Return the response data (either an XML Document or a text string)
517
+ return data;
518
+ }
519
+
520
+ }
521
+ DrNicTest.Unit.Assertions = {
522
+ buildMessage: function(message, template) {
523
+ var args = DrNicTest.arrayfromargs(arguments).slice(2);
524
+ return (message ? message + '\n' : '') +
525
+ new DrNicTest.Unit.MessageTemplate(template).evaluate(args);
526
+ },
527
+
528
+ flunk: function(message) {
529
+ this.assertBlock(message || 'Flunked', function() { return false });
530
+ },
531
+
532
+ assertBlock: function(message, block) {
533
+ try {
534
+ block.call(this) ? this.pass() : this.fail(message);
535
+ } catch(e) { this.error(e) }
536
+ },
537
+
538
+ assert: function(expression, message) {
539
+ message = this.buildMessage(message || 'assert', 'got <?>', expression);
540
+ this.assertBlock(message, function() { return expression });
541
+ },
542
+
543
+ assertEqual: function(expected, actual, message) {
544
+ message = this.buildMessage(message || 'assertEqual', 'expected <?>, actual: <?>', expected, actual);
545
+ this.assertBlock(message, function() { return expected == actual });
546
+ },
547
+
548
+ assertNotEqual: function(expected, actual, message) {
549
+ message = this.buildMessage(message || 'assertNotEqual', 'expected <?>, actual: <?>', expected, actual);
550
+ this.assertBlock(message, function() { return expected != actual });
551
+ },
552
+
553
+ assertEnumEqual: function(expected, actual, message) {
554
+ message = this.buildMessage(message || 'assertEnumEqual', 'expected <?>, actual: <?>', expected, actual);
555
+ var expected_array = DrNicTest.flattenArray(expected);
556
+ var actual_array = DrNicTest.flattenArray(actual);
557
+ this.assertBlock(message, function() {
558
+ if (expected_array.length == actual_array.length) {
559
+ for (var i=0; i < expected_array.length; i++) {
560
+ if (expected_array[i] != actual_array[i]) return false;
561
+ };
562
+ return true;
563
+ }
564
+ return false;
565
+ });
566
+ },
567
+
568
+ assertEnumNotEqual: function(expected, actual, message) {
569
+ message = this.buildMessage(message || 'assertEnumNotEqual', '<?> was the same as <?>', expected, actual);
570
+ var expected_array = DrNicTest.flattenArray(expected);
571
+ var actual_array = DrNicTest.flattenArray(actual);
572
+ this.assertBlock(message, function() {
573
+ if (expected_array.length == actual_array.length) {
574
+ for (var i=0; i < expected_array.length; i++) {
575
+ if (expected_array[i] != actual_array[i]) return true;
576
+ };
577
+ return false;
578
+ }
579
+ return true;
580
+ });
581
+ },
582
+
583
+ assertHashEqual: function(expected, actual, message) {
584
+ message = this.buildMessage(message || 'assertHashEqual', 'expected <?>, actual: <?>', expected, actual);
585
+ var expected_array = DrNicTest.flattenArray(DrNicTest.hashToSortedArray(expected));
586
+ var actual_array = DrNicTest.flattenArray(DrNicTest.hashToSortedArray(actual));
587
+ var block = function() {
588
+ if (expected_array.length == actual_array.length) {
589
+ for (var i=0; i < expected_array.length; i++) {
590
+ if (expected_array[i] != actual_array[i]) return false;
591
+ };
592
+ return true;
593
+ }
594
+ return false;
595
+ };
596
+ this.assertBlock(message, block);
597
+ },
598
+
599
+ assertHashNotEqual: function(expected, actual, message) {
600
+ message = this.buildMessage(message || 'assertHashNotEqual', '<?> was the same as <?>', expected, actual);
601
+ var expected_array = DrNicTest.flattenArray(DrNicTest.hashToSortedArray(expected));
602
+ var actual_array = DrNicTest.flattenArray(DrNicTest.hashToSortedArray(actual));
603
+ // from now we recursively zip & compare nested arrays
604
+ var block = function() {
605
+ if (expected_array.length == actual_array.length) {
606
+ for (var i=0; i < expected_array.length; i++) {
607
+ if (expected_array[i] != actual_array[i]) return true;
608
+ };
609
+ return false;
610
+ }
611
+ return true;
612
+ };
613
+ this.assertBlock(message, block);
614
+ },
615
+
616
+ assertIdentical: function(expected, actual, message) {
617
+ message = this.buildMessage(message || 'assertIdentical', 'expected <?>, actual: <?>', expected, actual);
618
+ this.assertBlock(message, function() { return expected === actual });
619
+ },
620
+
621
+ assertNotIdentical: function(expected, actual, message) {
622
+ message = this.buildMessage(message || 'assertNotIdentical', 'expected <?>, actual: <?>', expected, actual);
623
+ this.assertBlock(message, function() { return expected !== actual });
624
+ },
625
+
626
+ assertNull: function(obj, message) {
627
+ message = this.buildMessage(message || 'assertNull', 'got <?>', obj);
628
+ this.assertBlock(message, function() { return obj === null });
629
+ },
630
+
631
+ assertNotNull: function(obj, message) {
632
+ message = this.buildMessage(message || 'assertNotNull', 'got <?>', obj);
633
+ this.assertBlock(message, function() { return obj !== null });
634
+ },
635
+
636
+ assertUndefined: function(obj, message) {
637
+ message = this.buildMessage(message || 'assertUndefined', 'got <?>', obj);
638
+ this.assertBlock(message, function() { return typeof obj == "undefined" });
639
+ },
640
+
641
+ assertNotUndefined: function(obj, message) {
642
+ message = this.buildMessage(message || 'assertNotUndefined', 'got <?>', obj);
643
+ this.assertBlock(message, function() { return typeof obj != "undefined" });
644
+ },
645
+
646
+ assertNullOrUndefined: function(obj, message) {
647
+ message = this.buildMessage(message || 'assertNullOrUndefined', 'got <?>', obj);
648
+ this.assertBlock(message, function() { return obj == null });
649
+ },
650
+
651
+ assertNotNullOrUndefined: function(obj, message) {
652
+ message = this.buildMessage(message || 'assertNotNullOrUndefined', 'got <?>', obj);
653
+ this.assertBlock(message, function() { return obj != null });
654
+ },
655
+
656
+ assertMatch: function(expected, actual, message) {
657
+ message = this.buildMessage(message || 'assertMatch', 'regex <?> did not match <?>', expected, actual);
658
+ this.assertBlock(message, function() { return new RegExp(expected).exec(actual) });
659
+ },
660
+
661
+ assertNoMatch: function(expected, actual, message) {
662
+ message = this.buildMessage(message || 'assertNoMatch', 'regex <?> matched <?>', expected, actual);
663
+ this.assertBlock(message, function() { return !(new RegExp(expected).exec(actual)) });
664
+ },
665
+
666
+ assertHidden: function(element, message) {
667
+ message = this.buildMessage(message || 'assertHidden', '? isn\'t hidden.', element);
668
+ this.assertBlock(message, function() { return element.style.display == 'none' });
669
+ },
670
+
671
+ assertInstanceOf: function(expected, actual, message) {
672
+ message = this.buildMessage(message || 'assertInstanceOf', '<?> was not an instance of the expected type', actual);
673
+ this.assertBlock(message, function() { return actual instanceof expected });
674
+ },
675
+
676
+ assertNotInstanceOf: function(expected, actual, message) {
677
+ message = this.buildMessage(message || 'assertNotInstanceOf', '<?> was an instance of the expected type', actual);
678
+ this.assertBlock(message, function() { return !(actual instanceof expected) });
679
+ },
680
+
681
+ assertRespondsTo: function(method, obj, message) {
682
+ message = this.buildMessage(message || 'assertRespondsTo', 'object doesn\'t respond to <?>', method);
683
+ this.assertBlock(message, function() { return (method in obj && typeof obj[method] == 'function') });
684
+ },
685
+
686
+ assertRaise: function(exceptionName, method, message) {
687
+ message = this.buildMessage(message || 'assertRaise', '<?> exception expected but none was raised', exceptionName);
688
+ var block = function() {
689
+ try {
690
+ method();
691
+ return false;
692
+ } catch(e) {
693
+ if (e.name == exceptionName) return true;
694
+ else throw e;
695
+ }
696
+ };
697
+ this.assertBlock(message, block);
698
+ },
699
+
700
+ assertNothingRaised: function(method, message) {
701
+ try {
702
+ method();
703
+ this.assert(true, "Expected nothing to be thrown");
704
+ } catch(e) {
705
+ message = this.buildMessage(message || 'assertNothingRaised', '<?> was thrown when nothing was expected.', e);
706
+ this.flunk(message);
707
+ }
708
+ },
709
+
710
+ _isVisible: function(element) {
711
+ element = DrNicTest.$(element);
712
+ if(!element.parentNode) return true;
713
+ this.assertNotNull(element);
714
+ if(element.style && element.style.display == 'none')
715
+ return false;
716
+
717
+ return arguments.callee.call(this, element.parentNode);
718
+ },
719
+
720
+ assertVisible: function(element, message) {
721
+ message = this.buildMessage(message, '? was not visible.', element);
722
+ this.assertBlock(message, function() { return this._isVisible(element) });
723
+ },
724
+
725
+ assertNotVisible: function(element, message) {
726
+ message = this.buildMessage(message, '? was not hidden and didn\'t have a hidden parent either.', element);
727
+ this.assertBlock(message, function() { return !this._isVisible(element) });
728
+ },
729
+
730
+ assertElementsMatch: function() {
731
+ var pass = true, expressions = DrNicTest.arrayfromargs(arguments);
732
+ var elements = expressions.shift();
733
+ if (elements.length != expressions.length) {
734
+ message = this.buildMessage('assertElementsMatch', 'size mismatch: ? elements, ? expressions (?).', elements.length, expressions.length, expressions);
735
+ this.flunk(message);
736
+ pass = false;
737
+ }
738
+ for (var i=0; i < expressions.length; i++) {
739
+ var expression = expressions[i];
740
+ var element = DrNicTest.$(elements[i]);
741
+ if (DrNicTest.selectorMatch(expression, element)) {
742
+ pass = true;
743
+ break;
744
+ }
745
+ message = this.buildMessage('assertElementsMatch', 'In index <?>: expected <?> but got ?', index, expression, element);
746
+ this.flunk(message);
747
+ pass = false;
748
+ };
749
+ this.assert(pass, "Expected all elements to match.");
750
+ },
751
+
752
+ assertElementMatches: function(element, expression, message) {
753
+ this.assertElementsMatch([element], expression);
754
+ }
755
+ };
756
+ DrNicTest.Unit.Runner = function(testcases) {
757
+ var argumentOptions = arguments[1] || {};
758
+ var options = this.options = {};
759
+ options.testLog = ('testLog' in argumentOptions) ? argumentOptions.testLog : 'testlog';
760
+ options.resultsURL = this.queryParams.resultsURL;
761
+ options.testLog = DrNicTest.$(options.testLog);
762
+
763
+ this.tests = this.getTests(testcases);
764
+ this.currentTest = 0;
765
+ this.logger = new DrNicTest.Unit.Logger(options.testLog);
766
+
767
+ var self = this;
768
+ DrNicTest.Event.addEvent(window, "load", function() {
769
+ setTimeout(function() {
770
+ self.runTests();
771
+ }, 0.1);
772
+ });
773
+ };
774
+
775
+ DrNicTest.Unit.Runner.prototype.queryParams = DrNicTest.toQueryParams();
776
+
777
+ DrNicTest.Unit.Runner.prototype.portNumber = function() {
778
+ if (window.location.search.length > 0) {
779
+ var matches = window.location.search.match(/\:(\d{3,5})\//);
780
+ if (matches) {
781
+ return parseInt(matches[1]);
782
+ }
783
+ }
784
+ return null;
785
+ };
786
+
787
+ DrNicTest.Unit.Runner.prototype.getTests = function(testcases) {
788
+ var tests = [], options = this.options;
789
+ if (this.queryParams.tests) tests = this.queryParams.tests.split(',');
790
+ else if (options.tests) tests = options.tests;
791
+ else if (options.test) tests = [option.test];
792
+ else {
793
+ for (testname in testcases) {
794
+ if (testname.match(/^test/)) tests.push(testname);
795
+ }
796
+ }
797
+ var results = [];
798
+ for (var i=0; i < tests.length; i++) {
799
+ var test = tests[i];
800
+ if (testcases[test])
801
+ results.push(
802
+ new DrNicTest.Unit.Testcase(test, testcases[test], testcases.setup, testcases.teardown)
803
+ );
804
+ };
805
+ return results;
806
+ };
807
+
808
+ DrNicTest.Unit.Runner.prototype.getResult = function() {
809
+ var results = {
810
+ tests: this.tests.length,
811
+ assertions: 0,
812
+ failures: 0,
813
+ errors: 0
814
+ };
815
+
816
+ for (var i=0; i < this.tests.length; i++) {
817
+ var test = this.tests[i];
818
+ results.assertions += test.assertions;
819
+ results.failures += test.failures;
820
+ results.errors += test.errors;
821
+ };
822
+ return results;
823
+ };
824
+
825
+ DrNicTest.Unit.Runner.prototype.postResults = function() {
826
+ if (this.options.resultsURL) {
827
+ // new Ajax.Request(this.options.resultsURL,
828
+ // { method: 'get', parameters: this.getResult(), asynchronous: false });
829
+ var results = this.getResult();
830
+ var url = this.options.resultsURL + "?";
831
+ url += "assertions="+ results.assertions + "&";
832
+ url += "failures=" + results.failures + "&";
833
+ url += "errors=" + results.errors;
834
+ DrNicTest.ajax({
835
+ url: url,
836
+ type: 'GET'
837
+ })
838
+ }
839
+ };
840
+
841
+ DrNicTest.Unit.Runner.prototype.runTests = function() {
842
+ var test = this.tests[this.currentTest], actions;
843
+
844
+ if (!test) return this.finish();
845
+ if (!test.isWaiting) this.logger.start(test.name);
846
+ test.run();
847
+ var self = this;
848
+ if(test.isWaiting) {
849
+ this.logger.message("Waiting for " + test.timeToWait + "ms");
850
+ // setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
851
+ setTimeout(function() {
852
+ self.runTests();
853
+ }, test.timeToWait || 1000);
854
+ return;
855
+ }
856
+
857
+ this.logger.finish(test.status(), test.summary());
858
+ if (actions = test.actions) this.logger.appendActionButtons(actions);
859
+ this.currentTest++;
860
+ // tail recursive, hopefully the browser will skip the stackframe
861
+ this.runTests();
862
+ };
863
+
864
+ DrNicTest.Unit.Runner.prototype.finish = function() {
865
+ this.postResults();
866
+ this.logger.summary(this.summary());
867
+ };
868
+
869
+ DrNicTest.Unit.Runner.prototype.summary = function() {
870
+ return new DrNicTest.Template('#{tests} tests, #{assertions} assertions, #{failures} failures, #{errors} errors').evaluate(this.getResult());
871
+ };
872
+ DrNicTest.Unit.Testcase = function(name, test, setup, teardown) {
873
+ this.name = name;
874
+ this.test = test || function() {};
875
+ this.setup = setup || function() {};
876
+ this.teardown = teardown || function() {};
877
+ this.messages = [];
878
+ this.actions = {};
879
+ };
880
+ // import DrNicTest.Unit.Assertions
881
+
882
+ for (method in DrNicTest.Unit.Assertions) {
883
+ DrNicTest.Unit.Testcase.prototype[method] = DrNicTest.Unit.Assertions[method];
884
+ }
885
+
886
+ DrNicTest.Unit.Testcase.prototype.isWaiting = false;
887
+ DrNicTest.Unit.Testcase.prototype.timeToWait = 1000;
888
+ DrNicTest.Unit.Testcase.prototype.assertions = 0;
889
+ DrNicTest.Unit.Testcase.prototype.failures = 0;
890
+ DrNicTest.Unit.Testcase.prototype.errors = 0;
891
+ // DrNicTest.Unit.Testcase.prototype.isRunningFromRake = window.location.port == 4711;
892
+ DrNicTest.Unit.Testcase.prototype.isRunningFromRake = window.location.port;
893
+
894
+ DrNicTest.Unit.Testcase.prototype.wait = function(time, nextPart) {
895
+ this.isWaiting = true;
896
+ this.test = nextPart;
897
+ this.timeToWait = time;
898
+ };
899
+
900
+ DrNicTest.Unit.Testcase.prototype.run = function(rethrow) {
901
+ try {
902
+ try {
903
+ if (!this.isWaiting) this.setup();
904
+ this.isWaiting = false;
905
+ this.test();
906
+ } finally {
907
+ if(!this.isWaiting) {
908
+ this.teardown();
909
+ }
910
+ }
911
+ }
912
+ catch(e) {
913
+ if (rethrow) throw e;
914
+ this.error(e, this);
915
+ }
916
+ };
917
+
918
+ DrNicTest.Unit.Testcase.prototype.summary = function() {
919
+ var msg = '#{assertions} assertions, #{failures} failures, #{errors} errors\n';
920
+ return new DrNicTest.Template(msg).evaluate(this) +
921
+ this.messages.join("\n");
922
+ };
923
+
924
+ DrNicTest.Unit.Testcase.prototype.pass = function() {
925
+ this.assertions++;
926
+ };
927
+
928
+ DrNicTest.Unit.Testcase.prototype.fail = function(message) {
929
+ this.failures++;
930
+ var line = "";
931
+ try {
932
+ throw new Error("stack");
933
+ } catch(e){
934
+ line = (/\.html:(\d+)/.exec(e.stack || '') || ['',''])[1];
935
+ }
936
+ this.messages.push("Failure: " + message + (line ? " Line #" + line : ""));
937
+ };
938
+
939
+ DrNicTest.Unit.Testcase.prototype.info = function(message) {
940
+ this.messages.push("Info: " + message);
941
+ };
942
+
943
+ DrNicTest.Unit.Testcase.prototype.error = function(error, test) {
944
+ this.errors++;
945
+ this.actions['retry with throw'] = function() { test.run(true) };
946
+ this.messages.push(error.name + ": "+ error.message + "(" + DrNicTest.inspect(error) + ")");
947
+ };
948
+
949
+ DrNicTest.Unit.Testcase.prototype.status = function() {
950
+ if (this.failures > 0) return 'failed';
951
+ if (this.errors > 0) return 'error';
952
+ return 'passed';
953
+ };
954
+
955
+ DrNicTest.Unit.Testcase.prototype.benchmark = function(operation, iterations) {
956
+ var startAt = new Date();
957
+ (iterations || 1).times(operation);
958
+ var timeTaken = ((new Date())-startAt);
959
+ this.info((arguments[2] || 'Operation') + ' finished ' +
960
+ iterations + ' iterations in ' + (timeTaken/1000)+'s' );
961
+ return timeTaken;
962
+ };
963
+
964
+ Test = DrNicTest