visionmedia-jspec 2.8.4 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/jspec.js CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  JSpec = {
7
7
 
8
- version : '2.8.4',
8
+ version : '2.9.0',
9
9
  cache : {},
10
10
  suites : [],
11
11
  modules : [],
@@ -13,8 +13,8 @@
13
13
  matchers : {},
14
14
  stubbed : [],
15
15
  request : 'XMLHttpRequest' in this ? XMLHttpRequest : null,
16
- stats : { specs : 0, assertions : 0, failures : 0, passes : 0, specsFinished : 0, suitesFinished : 0 },
17
- options : { profile : false },
16
+ stats : { specs: 0, assertions: 0, failures: 0, passes: 0, specsFinished: 0, suitesFinished: 0 },
17
+ options : { profile: false },
18
18
 
19
19
  /**
20
20
  * Default context in which bodies are evaluated.
@@ -81,6 +81,46 @@
81
81
  // --- Objects
82
82
 
83
83
  formatters : {
84
+
85
+ /**
86
+ * Report to server.
87
+ *
88
+ * Options:
89
+ * - uri specific uri to report to.
90
+ * - verbose weither or not to output messages
91
+ * - failuresOnly output failure messages only
92
+ *
93
+ * @api public
94
+ */
95
+
96
+ Server : function(results, options) {
97
+ var uri = options.uri || 'http://' + window.location.host + '/results'
98
+ JSpec.post(uri, {
99
+ stats: JSpec.stats,
100
+ options: options,
101
+ results: map(results.allSuites, function(suite) {
102
+ if (suite.hasSpecs())
103
+ return {
104
+ description: suite.description,
105
+ specs: map(suite.specs, function(spec) {
106
+ return {
107
+ description: spec.description,
108
+ message: !spec.passed() ? spec.failure().message : null,
109
+ status: spec.requiresImplementation() ? 'pending' :
110
+ spec.passed() ? 'pass' :
111
+ 'fail',
112
+ assertions: map(spec.assertions, function(assertion){
113
+ return {
114
+ passed: assertion.passed
115
+ }
116
+ })
117
+ }
118
+ })
119
+ }
120
+ })
121
+ })
122
+ if ('close' in main) main.close()
123
+ },
84
124
 
85
125
  /**
86
126
  * Default formatter, outputting to the DOM.
@@ -98,48 +138,33 @@
98
138
  var failuresOnly = option('failuresOnly')
99
139
  var classes = results.stats.failures ? 'has-failures' : ''
100
140
  if (!report) throw 'JSpec requires the element #' + id + ' to output its reports'
101
-
102
- var markup =
103
- '<div id="jspec-report" class="' + classes + '"><div class="heading"> \
104
- <span class="passes">Passes: <em>' + results.stats.passes + '</em></span> \
105
- <span class="failures">Failures: <em>' + results.stats.failures + '</em></span> \
106
- </div><table class="suites">'
107
141
 
108
- bodyContents = function(body) {
142
+ function bodyContents(body) {
109
143
  return JSpec.
110
144
  escape(JSpec.contentsOf(body)).
111
145
  replace(/^ */gm, function(a){ return (new Array(Math.round(a.length / 3))).join(' ') }).
112
146
  replace("\n", '<br/>')
113
147
  }
114
148
 
115
- renderSuite = function(suite) {
149
+ report.innerHTML = '<div id="jspec-report" class="' + classes + '"><div class="heading"> \
150
+ <span class="passes">Passes: <em>' + results.stats.passes + '</em></span> \
151
+ <span class="failures">Failures: <em>' + results.stats.failures + '</em></span> \
152
+ </div><table class="suites">' + map(results.allSuites, function(suite) {
116
153
  var displaySuite = failuresOnly ? suite.ran && !suite.passed() : suite.ran
117
- if (displaySuite && suite.hasSpecs()) {
118
- markup += '<tr class="description"><td colspan="2">' + escape(suite.description) + '</td></tr>'
119
- each(suite.specs, function(i, spec){
120
- markup += '<tr class="' + (i % 2 ? 'odd' : 'even') + '">'
121
- if (spec.requiresImplementation())
122
- markup += '<td class="requires-implementation" colspan="2">' + escape(spec.description) + '</td>'
123
- else if (spec.passed() && !failuresOnly)
124
- markup += '<td class="pass">' + escape(spec.description)+ '</td><td>' + spec.assertionsGraph() + '</td>'
125
- else if(!spec.passed())
126
- markup += '<td class="fail">' + escape(spec.description) + ' <em>' + spec.failure().message + '</em>' + '</td><td>' + spec.assertionsGraph() + '</td>'
127
- markup += '<tr class="body"><td colspan="2"><pre>' + bodyContents(spec.body) + '</pre></td></tr>'
128
- })
129
- markup += '</tr>'
130
- }
131
- }
132
-
133
- renderSuites = function(suites) {
134
- each(suites, function(suite){
135
- renderSuite(suite)
136
- if (suite.hasSuites()) renderSuites(suite.suites)
137
- })
138
- }
139
-
140
- renderSuites(results.suites)
141
- markup += '</table></div>'
142
- report.innerHTML = markup
154
+ if (displaySuite && suite.hasSpecs())
155
+ return '<tr class="description"><td colspan="2">' + escape(suite.description) + '</td></tr>' +
156
+ map(suite.specs, function(i, spec) {
157
+ return '<tr class="' + (i % 2 ? 'odd' : 'even') + '">' +
158
+ (spec.requiresImplementation() ?
159
+ '<td class="requires-implementation" colspan="2">' + escape(spec.description) + '</td>' :
160
+ (spec.passed() && !failuresOnly) ?
161
+ '<td class="pass">' + escape(spec.description)+ '</td><td>' + spec.assertionsGraph() + '</td>' :
162
+ !spec.passed() ?
163
+ '<td class="fail">' + escape(spec.description) + ' <em>' + escape(spec.failure().message) + '</em>' + '</td><td>' + spec.assertionsGraph() + '</td>' :
164
+ '') +
165
+ '<tr class="body"><td colspan="2"><pre>' + bodyContents(spec.body) + '</pre></td></tr>'
166
+ }).join('') + '</tr>'
167
+ }).join('') + '</table></div>'
143
168
  },
144
169
 
145
170
  /**
@@ -153,42 +178,33 @@
153
178
  print(color("\n Passes: ", 'bold') + color(results.stats.passes, 'green') +
154
179
  color(" Failures: ", 'bold') + color(results.stats.failures, 'red') + "\n")
155
180
 
156
- indent = function(string) {
181
+ function indent(string) {
157
182
  return string.replace(/^(.)/gm, ' $1')
158
183
  }
159
-
160
- renderSuite = function(suite) {
161
- displaySuite = failuresOnly ? suite.ran && !suite.passed() : suite.ran
162
- if (displaySuite && suite.hasSpecs()) {
163
- print(color(' ' + suite.description, 'bold'))
164
- each(suite.specs, function(spec){
165
- var assertionsGraph = inject(spec.assertions, '', function(graph, assertion){
166
- return graph + color('.', assertion.passed ? 'green' : 'red')
167
- })
168
- if (spec.requiresImplementation())
169
- print(color(' ' + spec.description, 'blue') + assertionsGraph)
170
- else if (spec.passed() && !failuresOnly)
171
- print(color(' ' + spec.description, 'green') + assertionsGraph)
172
- else if (!spec.passed())
173
- print(color(' ' + spec.description, 'red') + assertionsGraph +
174
- "\n" + indent(spec.failure().message) + "\n")
175
- })
176
- print("")
177
- }
178
- }
179
-
180
- renderSuites = function(suites) {
181
- each(suites, function(suite){
182
- renderSuite(suite)
183
- if (suite.hasSuites()) renderSuites(suite.suites)
184
- })
185
- }
186
-
187
- renderSuites(results.suites)
184
+
185
+ each(results.allSuites, function(suite) {
186
+ var displaySuite = failuresOnly ? suite.ran && !suite.passed() : suite.ran
187
+ if (displaySuite && suite.hasSpecs()) {
188
+ print(color(' ' + suite.description, 'bold'))
189
+ each(suite.specs, function(spec){
190
+ var assertionsGraph = inject(spec.assertions, '', function(graph, assertion){
191
+ return graph + color('.', assertion.passed ? 'green' : 'red')
192
+ })
193
+ if (spec.requiresImplementation())
194
+ print(color(' ' + spec.description, 'blue') + assertionsGraph)
195
+ else if (spec.passed() && !failuresOnly)
196
+ print(color(' ' + spec.description, 'green') + assertionsGraph)
197
+ else if (!spec.passed())
198
+ print(color(' ' + spec.description, 'red') + assertionsGraph +
199
+ "\n" + indent(spec.failure().message) + "\n")
200
+ })
201
+ print("")
202
+ }
203
+ })
188
204
  },
189
205
 
190
206
  /**
191
- * Console formatter, tested with Firebug and Safari 4.
207
+ * Console formatter.
192
208
  *
193
209
  * @api public
194
210
  */
@@ -196,8 +212,7 @@
196
212
  Console : function(results, options) {
197
213
  console.log('')
198
214
  console.log('Passes: ' + results.stats.passes + ' Failures: ' + results.stats.failures)
199
-
200
- renderSuite = function(suite) {
215
+ each(results.allSuites, function(suite) {
201
216
  if (suite.ran) {
202
217
  console.group(suite.description)
203
218
  each(suite.specs, function(spec){
@@ -210,28 +225,19 @@
210
225
  console.error(assertionCount + ' ' + spec.description + ', ' + spec.failure().message)
211
226
  })
212
227
  console.groupEnd()
213
- }
214
- }
215
-
216
- renderSuites = function(suites) {
217
- each(suites, function(suite){
218
- renderSuite(suite)
219
- if (suite.hasSuites()) renderSuites(suite.suites)
220
- })
221
- }
222
-
223
- renderSuites(results.suites)
228
+ }
229
+ })
224
230
  }
225
231
  },
226
232
 
227
233
  Assertion : function(matcher, actual, expected, negate) {
228
234
  extend(this, {
229
- message : '',
230
- passed : false,
231
- actual : actual,
232
- negate : negate,
233
- matcher : matcher,
234
- expected : expected,
235
+ message: '',
236
+ passed: false,
237
+ actual: actual,
238
+ negate: negate,
239
+ matcher: matcher,
240
+ expected: expected,
235
241
 
236
242
  // Report assertion results
237
243
 
@@ -269,18 +275,18 @@
269
275
  // Times
270
276
 
271
277
  this.times = {
272
- 'once' : 1,
273
- 'twice' : 2
278
+ once : 1,
279
+ twice : 2
274
280
  }[times] || times || 1
275
281
 
276
282
  extend(this, {
277
- calls : [],
278
- message : '',
279
- defer : true,
280
- passed : false,
281
- negate : negate,
282
- object : object,
283
- method : method,
283
+ calls: [],
284
+ message: '',
285
+ defer: true,
286
+ passed: false,
287
+ negate: negate,
288
+ object: object,
289
+ method: method,
284
290
 
285
291
  // Proxy return value
286
292
 
@@ -370,7 +376,7 @@
370
376
  // Report assertion results
371
377
 
372
378
  report : function() {
373
- this.passed ? JSpec.stats.passes++ : JSpec.stats.failures++
379
+ this.passed ? ++JSpec.stats.passes : ++JSpec.stats.failures
374
380
  return this
375
381
  },
376
382
 
@@ -380,7 +386,7 @@
380
386
  var methodString = 'expected ' + object.toString() + '.' + method + '()' + (negate ? ' not' : '' )
381
387
 
382
388
  function times(n) {
383
- return n > 2 ? n + ' times' : { 1 : 'once', 2 : 'twice' }[n]
389
+ return n > 2 ? n + ' times' : { 1: 'once', 2: 'twice' }[n]
384
390
  }
385
391
 
386
392
  if (this.expectedResult != null && (negate ? this.anyResultsPass() : this.anyResultsFail()))
@@ -488,9 +494,9 @@
488
494
 
489
495
  Spec : function(description, body) {
490
496
  extend(this, {
491
- body : body,
492
- description : description,
493
- assertions : [],
497
+ body: body,
498
+ description: description,
499
+ assertions: [],
494
500
 
495
501
  // Add passing assertion
496
502
 
@@ -556,6 +562,63 @@
556
562
  extend(this, methods)
557
563
  },
558
564
 
565
+ JSON : {
566
+
567
+ /**
568
+ * Generic sequences.
569
+ */
570
+
571
+ meta : {
572
+ '\b' : '\\b',
573
+ '\t' : '\\t',
574
+ '\n' : '\\n',
575
+ '\f' : '\\f',
576
+ '\r' : '\\r',
577
+ '"' : '\\"',
578
+ '\\' : '\\\\'
579
+ },
580
+
581
+ /**
582
+ * Escapable sequences.
583
+ */
584
+
585
+ escapable : /[\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
586
+
587
+ /**
588
+ * JSON encode _object_.
589
+ *
590
+ * @param {mixed} object
591
+ * @return {string}
592
+ * @api private
593
+ */
594
+
595
+ encode : function(object) {
596
+ var self = this
597
+ if (object == undefined || object == null) return 'null'
598
+ if (object === true) return 'true'
599
+ if (object === false) return 'false'
600
+ switch (typeof object) {
601
+ case 'number': return object
602
+ case 'string': return this.escapable.test(object) ?
603
+ '"' + object.replace(this.escapable, function (a) {
604
+ return typeof self.meta[a] === 'string' ? self.meta[a] :
605
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4)
606
+ }) + '"' :
607
+ '"' + object + '"'
608
+ case 'object':
609
+ if (object.constructor == Array)
610
+ return '[' + map(object, function(val){
611
+ return self.encode(val)
612
+ }).join(', ') + ']'
613
+ else if (object)
614
+ return '{' + map(object, function(key, val){
615
+ return self.encode(key) + ':' + self.encode(val)
616
+ }).join(', ') + '}'
617
+ }
618
+ return 'null'
619
+ }
620
+ },
621
+
559
622
  // --- DSLs
560
623
 
561
624
  DSLs : {
@@ -820,10 +883,10 @@
820
883
  case String:
821
884
  if (captures = body.match(/^alias (\w+)/)) return JSpec.matchers[last(captures)]
822
885
  if (body.length < 4) body = 'actual ' + body + ' expected'
823
- return { match : function(actual, expected) { return eval(body) }}
886
+ return { match: function(actual, expected) { return eval(body) }}
824
887
 
825
888
  case Function:
826
- return { match : body }
889
+ return { match: body }
827
890
 
828
891
  default:
829
892
  return body
@@ -856,7 +919,7 @@
856
919
  hash : function(object) {
857
920
  if (object == null) return 'null'
858
921
  if (object == undefined) return 'undefined'
859
- serialize = function(prefix) {
922
+ function serialize(prefix) {
860
923
  return inject(object, prefix + ':', function(buffer, key, value){
861
924
  return buffer += hash(value)
862
925
  })
@@ -905,13 +968,13 @@
905
968
  if (object === false) return 'false'
906
969
  if (object.an_instance_of) return 'an instance of ' + object.an_instance_of.name
907
970
  if (object.jquery && object.selector.length > 0) return 'selector ' + puts(object.selector) + ''
908
- if (object.jquery) return escape(object.html())
909
- if (object.nodeName) return escape(object.outerHTML)
971
+ if (object.jquery) return object.html()
972
+ if (object.nodeName) return object.outerHTML
910
973
  switch (object.constructor) {
911
- case String: return "'" + escape(object) + "'"
974
+ case String: return "'" + object + "'"
912
975
  case Number: return object
913
976
  case Function: return object.name || object
914
- case Array :
977
+ case Array:
915
978
  return inject(object, '[', function(b, v){
916
979
  return b + ', ' + puts(v)
917
980
  }).replace('[,', '[') + ' ]'
@@ -920,7 +983,7 @@
920
983
  return b + ', ' + puts(k) + ' : ' + puts(v)
921
984
  }).replace('{,', '{') + ' }'
922
985
  default:
923
- return escape(object.toString())
986
+ return object.toString()
924
987
  }
925
988
  },
926
989
 
@@ -933,11 +996,11 @@
933
996
  */
934
997
 
935
998
  escape : function(html) {
936
- return html.toString().
937
- replace(/&/gmi, '&amp;').
938
- replace(/"/gmi, '&quot;').
939
- replace(/>/gmi, '&gt;').
940
- replace(/</gmi, '&lt;')
999
+ return html.toString()
1000
+ .replace(/&/gmi, '&amp;')
1001
+ .replace(/"/gmi, '&quot;')
1002
+ .replace(/>/gmi, '&gt;')
1003
+ .replace(/</gmi, '&lt;')
941
1004
  },
942
1005
 
943
1006
  /**
@@ -1288,6 +1351,7 @@
1288
1351
  */
1289
1352
 
1290
1353
  preprocess : function(input) {
1354
+ if (typeof input != 'string') return
1291
1355
  input = hookImmutable('preprocessing', input)
1292
1356
  return input.
1293
1357
  replace(/([\w\.]+)\.(stub|destub)\((.*?)\)$/gm, '$2($1, $3)').
@@ -1467,32 +1531,19 @@
1467
1531
  /**
1468
1532
  * Ad-hoc POST request for JSpec server usage.
1469
1533
  *
1470
- * @param {string} url
1534
+ * @param {string} uri
1471
1535
  * @param {string} data
1472
1536
  * @api private
1473
1537
  */
1474
1538
 
1475
- post : function(url, data) {
1476
- if (any(hook('posting', url, data), haveStopped)) return
1539
+ post : function(uri, data) {
1540
+ if (any(hook('posting', uri, data), haveStopped)) return
1477
1541
  var request = this.xhr()
1478
- request.open('POST', url, false)
1479
- request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
1480
- request.send(data)
1542
+ request.open('POST', uri, false)
1543
+ request.setRequestHeader('Content-Type', 'application/json')
1544
+ request.send(JSpec.JSON.encode(data))
1481
1545
  },
1482
1546
 
1483
- /**
1484
- * Report to server with statistics.
1485
- *
1486
- * @param {string} url
1487
- * @api private
1488
- */
1489
-
1490
- reportToServer : function(url) {
1491
- if (any(hook('reportingToServer', url), haveStopped)) return
1492
- JSpec.post(url || 'http://localhost:4444', 'passes=' + JSpec.stats.passes + '&failures=' + JSpec.stats.failures)
1493
- if ('close' in main) main.close()
1494
- },
1495
-
1496
1547
  /**
1497
1548
  * Instantiate an XMLHttpRequest.
1498
1549
  *
@@ -1546,7 +1597,10 @@
1546
1597
  var request = this.xhr()
1547
1598
  request.open('GET', file, false)
1548
1599
  request.send(null)
1549
- if (request.readyState == 4) return request.responseText
1600
+ if (request.readyState == 4 &&
1601
+ (request.status == 0 ||
1602
+ parseInt(request.status.toString()[0]) == 2))
1603
+ return request.responseText
1550
1604
  }
1551
1605
  else
1552
1606
  error("failed to load `" + file + "'")
data/lib/jspec.xhr.js CHANGED
@@ -16,12 +16,12 @@
16
16
  }
17
17
 
18
18
  MockXMLHttpRequest.prototype = {
19
- status : 0,
20
- async : true,
21
- readyState : 0,
22
- responseText : '',
23
- abort : function(){},
24
- onreadystatechange : function(){},
19
+ status: 0,
20
+ async: true,
21
+ readyState: 0,
22
+ responseText: '',
23
+ abort: function(){},
24
+ onreadystatechange: function(){},
25
25
 
26
26
  /**
27
27
  * Return response headers hash.
@@ -77,47 +77,47 @@
77
77
  // --- Response status codes
78
78
 
79
79
  JSpec.statusCodes = {
80
- 100 : 'Continue',
81
- 101 : 'Switching Protocols',
82
- 200 : 'OK',
83
- 201 : 'Created',
84
- 202 : 'Accepted',
85
- 203 : 'Non-Authoritative Information',
86
- 204 : 'No Content',
87
- 205 : 'Reset Content',
88
- 206 : 'Partial Content',
89
- 300 : 'Multiple Choice',
90
- 301 : 'Moved Permanently',
91
- 302 : 'Found',
92
- 303 : 'See Other',
93
- 304 : 'Not Modified',
94
- 305 : 'Use Proxy',
95
- 307 : 'Temporary Redirect',
96
- 400 : 'Bad Request',
97
- 401 : 'Unauthorized',
98
- 402 : 'Payment Required',
99
- 403 : 'Forbidden',
100
- 404 : 'Not Found',
101
- 405 : 'Method Not Allowed',
102
- 406 : 'Not Acceptable',
103
- 407 : 'Proxy Authentication Required',
104
- 408 : 'Request Timeout',
105
- 409 : 'Conflict',
106
- 410 : 'Gone',
107
- 411 : 'Length Required',
108
- 412 : 'Precondition Failed',
109
- 413 : 'Request Entity Too Large',
110
- 414 : 'Request-URI Too Long',
111
- 415 : 'Unsupported Media Type',
112
- 416 : 'Requested Range Not Satisfiable',
113
- 417 : 'Expectation Failed',
114
- 422 : 'Unprocessable Entity',
115
- 500 : 'Internal Server Error',
116
- 501 : 'Not Implemented',
117
- 502 : 'Bad Gateway',
118
- 503 : 'Service Unavailable',
119
- 504 : 'Gateway Timeout',
120
- 505 : 'HTTP Version Not Supported'
80
+ 100: 'Continue',
81
+ 101: 'Switching Protocols',
82
+ 200: 'OK',
83
+ 201: 'Created',
84
+ 202: 'Accepted',
85
+ 203: 'Non-Authoritative Information',
86
+ 204: 'No Content',
87
+ 205: 'Reset Content',
88
+ 206: 'Partial Content',
89
+ 300: 'Multiple Choice',
90
+ 301: 'Moved Permanently',
91
+ 302: 'Found',
92
+ 303: 'See Other',
93
+ 304: 'Not Modified',
94
+ 305: 'Use Proxy',
95
+ 307: 'Temporary Redirect',
96
+ 400: 'Bad Request',
97
+ 401: 'Unauthorized',
98
+ 402: 'Payment Required',
99
+ 403: 'Forbidden',
100
+ 404: 'Not Found',
101
+ 405: 'Method Not Allowed',
102
+ 406: 'Not Acceptable',
103
+ 407: 'Proxy Authentication Required',
104
+ 408: 'Request Timeout',
105
+ 409: 'Conflict',
106
+ 410: 'Gone',
107
+ 411: 'Length Required',
108
+ 412: 'Precondition Failed',
109
+ 413: 'Request Entity Too Large',
110
+ 414: 'Request-URI Too Long',
111
+ 415: 'Unsupported Media Type',
112
+ 416: 'Requested Range Not Satisfiable',
113
+ 417: 'Expectation Failed',
114
+ 422: 'Unprocessable Entity',
115
+ 500: 'Internal Server Error',
116
+ 501: 'Not Implemented',
117
+ 502: 'Bad Gateway',
118
+ 503: 'Service Unavailable',
119
+ 504: 'Gateway Timeout',
120
+ 505: 'HTTP Version Not Supported'
121
121
  }
122
122
 
123
123
  /**
@@ -136,10 +136,10 @@
136
136
  headers = headers || {}
137
137
  headers['content-type'] = type
138
138
  JSpec.extend(XMLHttpRequest.prototype, {
139
- responseText : body,
140
- responseHeaders : headers,
141
- status : status,
142
- statusText : JSpec.statusCodes[status]
139
+ responseText: body,
140
+ responseHeaders: headers,
141
+ status: status,
142
+ statusText: JSpec.statusCodes[status]
143
143
  })
144
144
  }}
145
145
  }
@@ -159,14 +159,23 @@
159
159
  // --- Utilities
160
160
 
161
161
  utilities : {
162
- mockRequest : mockRequest,
163
- unmockRequest : unmockRequest
162
+ mockRequest: mockRequest,
163
+ unmockRequest: unmockRequest
164
164
  },
165
165
 
166
166
  // --- Hooks
167
167
 
168
168
  afterSpec : function() {
169
169
  this.utilities.unmockRequest()
170
+ },
171
+
172
+ // --- DSLs
173
+
174
+ DSLs : {
175
+ snake : {
176
+ mock_request: mockRequest,
177
+ unmock_request: unmockRequest
178
+ }
170
179
  }
171
180
 
172
181
  })