client_side_validations 0.8.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +109 -0
- data/TODO +0 -1
- data/javascript/lib/client_side_validations.js +38 -28
- data/lib/client_side_validations.rb +37 -40
- data/lib/client_side_validations/adapters/action_view.rb +57 -0
- data/lib/{adapters → client_side_validations/adapters}/active_model.rb +15 -3
- data/lib/{adapters → client_side_validations/adapters}/active_record_2.rb +8 -0
- data/lib/client_side_validations/orm.rb +45 -0
- data/lib/client_side_validations/template.rb +3 -0
- data/lib/generators/client_side_validations_generator.rb +13 -0
- metadata +26 -47
- data/.document +0 -5
- data/.gitignore +0 -22
- data/README.rdoc +0 -7
- data/Rakefile +0 -46
- data/VERSION +0 -1
- data/client_side_validations.gemspec +0 -80
- data/javascript/jspec/commands/example_command.rb +0 -19
- data/javascript/jspec/rhino.js +0 -10
- data/javascript/jspec/unit/jquery.validate.spec.js +0 -146
- data/javascript/jspec/unit/spec.helper.js +0 -0
- data/javascript/vendor/jspec.js +0 -1889
- data/javascript/vendor/jspec.xhr.js +0 -210
- data/lib/adapters/action_view.rb +0 -39
- data/spec/action_view_2_spec.rb +0 -55
- data/spec/action_view_3_spec.rb +0 -55
- data/spec/active_model_3_spec.rb +0 -271
- data/spec/active_record_2_spec.rb +0 -286
- data/spec/spec.opts +0 -1
- data/spec/spec_helper.rb +0 -9
- data/spec/support/required_gems.rb +0 -2
- data/tasks/spec.rake +0 -31
File without changes
|
data/javascript/vendor/jspec.js
DELETED
@@ -1,1889 +0,0 @@
|
|
1
|
-
|
2
|
-
// JSpec - Core - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
3
|
-
|
4
|
-
;(function(){
|
5
|
-
|
6
|
-
JSpec = {
|
7
|
-
version : '4.3.1',
|
8
|
-
assert : true,
|
9
|
-
cache : {},
|
10
|
-
suites : [],
|
11
|
-
modules : [],
|
12
|
-
allSuites : [],
|
13
|
-
sharedBehaviors: [],
|
14
|
-
matchers : {},
|
15
|
-
stubbed : [],
|
16
|
-
options : {},
|
17
|
-
request : 'XMLHttpRequest' in this ? XMLHttpRequest : null,
|
18
|
-
stats : { specs: 0, assertions: 0, failures: 0, passes: 0, specsFinished: 0, suitesFinished: 0 },
|
19
|
-
|
20
|
-
/**
|
21
|
-
* Default context in which bodies are evaluated.
|
22
|
-
*
|
23
|
-
* Replace context simply by setting JSpec.context
|
24
|
-
* to your own like below:
|
25
|
-
*
|
26
|
-
* JSpec.context = { foo : 'bar' }
|
27
|
-
*
|
28
|
-
* Contexts can be changed within any body, this can be useful
|
29
|
-
* in order to provide specific helper methods to specific suites.
|
30
|
-
*
|
31
|
-
* To reset (usually in after hook) simply set to null like below:
|
32
|
-
*
|
33
|
-
* JSpec.context = null
|
34
|
-
*
|
35
|
-
*/
|
36
|
-
|
37
|
-
defaultContext : {
|
38
|
-
|
39
|
-
/**
|
40
|
-
* Return an object used for proxy assertions.
|
41
|
-
* This object is used to indicate that an object
|
42
|
-
* should be an instance of _object_, not the constructor
|
43
|
-
* itself.
|
44
|
-
*
|
45
|
-
* @param {function} constructor
|
46
|
-
* @return {hash}
|
47
|
-
* @api public
|
48
|
-
*/
|
49
|
-
|
50
|
-
an_instance_of : function(constructor) {
|
51
|
-
return { an_instance_of : constructor }
|
52
|
-
},
|
53
|
-
|
54
|
-
/**
|
55
|
-
* Load fixture at _path_.
|
56
|
-
*
|
57
|
-
* Fixtures are resolved as:
|
58
|
-
*
|
59
|
-
* - <path>
|
60
|
-
* - <path>.html
|
61
|
-
*
|
62
|
-
* @param {string} path
|
63
|
-
* @return {string}
|
64
|
-
* @api public
|
65
|
-
*/
|
66
|
-
|
67
|
-
fixture : function(path) {
|
68
|
-
if (JSpec.cache[path]) return JSpec.cache[path]
|
69
|
-
return JSpec.cache[path] =
|
70
|
-
JSpec.tryLoading(JSpec.options.fixturePath + '/' + path) ||
|
71
|
-
JSpec.tryLoading(JSpec.options.fixturePath + '/' + path + '.html')
|
72
|
-
},
|
73
|
-
|
74
|
-
/**
|
75
|
-
* Load json fixture at _path_.
|
76
|
-
*
|
77
|
-
* JSON fixtures are resolved as:
|
78
|
-
*
|
79
|
-
* - <path>
|
80
|
-
* - <path>.json
|
81
|
-
*
|
82
|
-
* @param {string} path
|
83
|
-
* @return {object}
|
84
|
-
* @api public
|
85
|
-
*/
|
86
|
-
|
87
|
-
json_fixture: function(path) {
|
88
|
-
if (!JSpec.cache['json:' + path])
|
89
|
-
JSpec.cache['json:' + path] =
|
90
|
-
JSpec.tryLoading(JSpec.options.fixturePath + '/' + path) ||
|
91
|
-
JSpec.tryLoading(JSpec.options.fixturePath + '/' + path + '.json')
|
92
|
-
try {
|
93
|
-
return eval('(' + JSpec.cache['json:' + path] + ')')
|
94
|
-
} catch (e) {
|
95
|
-
throw 'json_fixture("' + path + '"): ' + e
|
96
|
-
}
|
97
|
-
}
|
98
|
-
},
|
99
|
-
|
100
|
-
// --- Objects
|
101
|
-
|
102
|
-
reporters : {
|
103
|
-
|
104
|
-
/**
|
105
|
-
* Report to server.
|
106
|
-
*
|
107
|
-
* Options:
|
108
|
-
* - uri specific uri to report to.
|
109
|
-
* - verbose weither or not to output messages
|
110
|
-
* - failuresOnly output failure messages only
|
111
|
-
*
|
112
|
-
* @api public
|
113
|
-
*/
|
114
|
-
|
115
|
-
Server : function(results, options) {
|
116
|
-
var uri = options.uri || 'http://' + window.location.host + '/results'
|
117
|
-
JSpec.post(uri, {
|
118
|
-
stats: JSpec.stats,
|
119
|
-
options: options,
|
120
|
-
results: map(results.allSuites, function(suite) {
|
121
|
-
if (suite.isExecutable())
|
122
|
-
return {
|
123
|
-
description: suite.description,
|
124
|
-
specs: map(suite.specs, function(spec) {
|
125
|
-
return {
|
126
|
-
description: spec.description,
|
127
|
-
message: !spec.passed() ? spec.failure().message : null,
|
128
|
-
status: spec.requiresImplementation() ? 'pending' :
|
129
|
-
spec.passed() ? 'pass' :
|
130
|
-
'fail',
|
131
|
-
assertions: map(spec.assertions, function(assertion){
|
132
|
-
return {
|
133
|
-
passed: assertion.passed
|
134
|
-
}
|
135
|
-
})
|
136
|
-
}
|
137
|
-
})
|
138
|
-
}
|
139
|
-
})
|
140
|
-
})
|
141
|
-
if ('close' in main) main.close()
|
142
|
-
},
|
143
|
-
|
144
|
-
/**
|
145
|
-
* Default reporter, outputting to the DOM.
|
146
|
-
*
|
147
|
-
* Options:
|
148
|
-
* - reportToId id of element to output reports to, defaults to 'jspec'
|
149
|
-
* - failuresOnly displays only suites with failing specs
|
150
|
-
*
|
151
|
-
* @api public
|
152
|
-
*/
|
153
|
-
|
154
|
-
DOM : function(results, options) {
|
155
|
-
var id = option('reportToId') || 'jspec',
|
156
|
-
report = document.getElementById(id),
|
157
|
-
failuresOnly = option('failuresOnly'),
|
158
|
-
classes = results.stats.failures ? 'has-failures' : ''
|
159
|
-
if (!report) throw 'JSpec requires the element #' + id + ' to output its reports'
|
160
|
-
|
161
|
-
function bodyContents(body) {
|
162
|
-
return JSpec.
|
163
|
-
escape(JSpec.contentsOf(body)).
|
164
|
-
replace(/^ */gm, function(a){ return (new Array(Math.round(a.length / 3))).join(' ') }).
|
165
|
-
replace(/\r\n|\r|\n/gm, '<br/>')
|
166
|
-
}
|
167
|
-
|
168
|
-
report.innerHTML = '<div id="jspec-report" class="' + classes + '"><div class="heading"> \
|
169
|
-
<span class="passes">Passes: <em>' + results.stats.passes + '</em></span> \
|
170
|
-
<span class="failures">Failures: <em>' + results.stats.failures + '</em></span> \
|
171
|
-
<span class="passes">Duration: <em>' + results.duration + '</em> ms</span> \
|
172
|
-
</div><table class="suites">' + map(results.allSuites, function(suite) {
|
173
|
-
var displaySuite = failuresOnly ? suite.ran && !suite.passed() : suite.ran
|
174
|
-
if (displaySuite && suite.isExecutable())
|
175
|
-
return '<tr class="description"><td colspan="2">' + escape(suite.description) + '</td></tr>' +
|
176
|
-
map(suite.specs, function(i, spec) {
|
177
|
-
return '<tr class="' + (i % 2 ? 'odd' : 'even') + '">' +
|
178
|
-
(spec.requiresImplementation() ?
|
179
|
-
'<td class="requires-implementation" colspan="2">' + escape(spec.description) + '</td>' :
|
180
|
-
(spec.passed() && !failuresOnly) ?
|
181
|
-
'<td class="pass">' + escape(spec.description)+ '</td><td>' + spec.assertionsGraph() + '</td>' :
|
182
|
-
!spec.passed() ?
|
183
|
-
'<td class="fail">' + escape(spec.description) +
|
184
|
-
map(spec.failures(), function(a){ return '<em>' + escape(a.message) + '</em>' }).join('') +
|
185
|
-
'</td><td>' + spec.assertionsGraph() + '</td>' :
|
186
|
-
'') +
|
187
|
-
'<tr class="body"><td colspan="2"><pre>' + bodyContents(spec.body) + '</pre></td></tr>'
|
188
|
-
}).join('') + '</tr>'
|
189
|
-
}).join('') + '</table></div>'
|
190
|
-
},
|
191
|
-
|
192
|
-
/**
|
193
|
-
* Terminal reporter.
|
194
|
-
*
|
195
|
-
* @api public
|
196
|
-
*/
|
197
|
-
|
198
|
-
Terminal : function(results, options) {
|
199
|
-
var failuresOnly = option('failuresOnly')
|
200
|
-
print(color("\n Passes: ", 'bold') + color(results.stats.passes, 'green') +
|
201
|
-
color(" Failures: ", 'bold') + color(results.stats.failures, 'red') +
|
202
|
-
color(" Duration: ", 'bold') + color(results.duration, 'green') + " ms \n")
|
203
|
-
|
204
|
-
function indent(string) {
|
205
|
-
return string.replace(/^(.)/gm, ' $1')
|
206
|
-
}
|
207
|
-
|
208
|
-
each(results.allSuites, function(suite) {
|
209
|
-
var displaySuite = failuresOnly ? suite.ran && !suite.passed() : suite.ran
|
210
|
-
if (displaySuite && suite.isExecutable()) {
|
211
|
-
print(color(' ' + suite.description, 'bold'))
|
212
|
-
each(suite.specs, function(spec){
|
213
|
-
var assertionsGraph = inject(spec.assertions, '', function(graph, assertion){
|
214
|
-
return graph + color('.', assertion.passed ? 'green' : 'red')
|
215
|
-
})
|
216
|
-
if (spec.requiresImplementation())
|
217
|
-
print(color(' ' + spec.description, 'blue') + assertionsGraph)
|
218
|
-
else if (spec.passed() && !failuresOnly)
|
219
|
-
print(color(' ' + spec.description, 'green') + assertionsGraph)
|
220
|
-
else if (!spec.passed())
|
221
|
-
print(color(' ' + spec.description, 'red') + assertionsGraph +
|
222
|
-
"\n" + indent(map(spec.failures(), function(a){ return a.message }).join("\n")) + "\n")
|
223
|
-
})
|
224
|
-
print("")
|
225
|
-
}
|
226
|
-
})
|
227
|
-
|
228
|
-
quit(results.stats.failures)
|
229
|
-
}
|
230
|
-
},
|
231
|
-
|
232
|
-
Assertion : function(matcher, actual, expected, negate) {
|
233
|
-
extend(this, {
|
234
|
-
message: '',
|
235
|
-
passed: false,
|
236
|
-
actual: actual,
|
237
|
-
negate: negate,
|
238
|
-
matcher: matcher,
|
239
|
-
expected: expected,
|
240
|
-
|
241
|
-
// Report assertion results
|
242
|
-
|
243
|
-
report : function() {
|
244
|
-
if (JSpec.assert)
|
245
|
-
this.passed ? JSpec.stats.passes++ : JSpec.stats.failures++
|
246
|
-
return this
|
247
|
-
},
|
248
|
-
|
249
|
-
// Run the assertion
|
250
|
-
|
251
|
-
run : function() {
|
252
|
-
// TODO: remove unshifting
|
253
|
-
expected.unshift(actual)
|
254
|
-
this.result = matcher.match.apply(this, expected)
|
255
|
-
this.passed = negate ? !this.result : this.result
|
256
|
-
if (!this.passed) this.message = matcher.message.call(this, actual, expected, negate, matcher.name)
|
257
|
-
return this
|
258
|
-
}
|
259
|
-
})
|
260
|
-
},
|
261
|
-
|
262
|
-
ProxyAssertion : function(object, method, times, negate) {
|
263
|
-
var self = this,
|
264
|
-
old = object[method]
|
265
|
-
|
266
|
-
// Proxy
|
267
|
-
|
268
|
-
object[method] = function(){
|
269
|
-
var args = toArray(arguments),
|
270
|
-
result = old.apply(object, args)
|
271
|
-
self.calls.push({ args : args, result : result })
|
272
|
-
return result
|
273
|
-
}
|
274
|
-
|
275
|
-
// Times
|
276
|
-
|
277
|
-
this.times = {
|
278
|
-
once : 1,
|
279
|
-
twice : 2
|
280
|
-
}[times] || times || 1
|
281
|
-
|
282
|
-
extend(this, {
|
283
|
-
calls: [],
|
284
|
-
message: '',
|
285
|
-
defer: true,
|
286
|
-
passed: false,
|
287
|
-
negate: negate,
|
288
|
-
object: object,
|
289
|
-
method: method,
|
290
|
-
|
291
|
-
// Proxy return value
|
292
|
-
|
293
|
-
and_return : function(result) {
|
294
|
-
this.expectedResult = result
|
295
|
-
return this
|
296
|
-
},
|
297
|
-
|
298
|
-
// Proxy arguments passed
|
299
|
-
|
300
|
-
with_args : function() {
|
301
|
-
this.expectedArgs = toArray(arguments)
|
302
|
-
return this
|
303
|
-
},
|
304
|
-
|
305
|
-
// Check if any calls have failing results
|
306
|
-
|
307
|
-
anyResultsFail : function() {
|
308
|
-
return any(this.calls, function(call){
|
309
|
-
return self.expectedResult.an_instance_of ?
|
310
|
-
call.result.constructor != self.expectedResult.an_instance_of:
|
311
|
-
!equal(self.expectedResult, call.result)
|
312
|
-
})
|
313
|
-
},
|
314
|
-
|
315
|
-
// Check if any calls have passing results
|
316
|
-
|
317
|
-
anyResultsPass : function() {
|
318
|
-
return any(this.calls, function(call){
|
319
|
-
return self.expectedResult.an_instance_of ?
|
320
|
-
call.result.constructor == self.expectedResult.an_instance_of:
|
321
|
-
equal(self.expectedResult, call.result)
|
322
|
-
})
|
323
|
-
},
|
324
|
-
|
325
|
-
// Return the passing result
|
326
|
-
|
327
|
-
passingResult : function() {
|
328
|
-
return this.anyResultsPass().result
|
329
|
-
},
|
330
|
-
|
331
|
-
// Return the failing result
|
332
|
-
|
333
|
-
failingResult : function() {
|
334
|
-
return this.anyResultsFail().result
|
335
|
-
},
|
336
|
-
|
337
|
-
// Check if any arguments fail
|
338
|
-
|
339
|
-
anyArgsFail : function() {
|
340
|
-
return any(this.calls, function(call){
|
341
|
-
return any(self.expectedArgs, function(i, arg){
|
342
|
-
if (arg == null) return call.args[i] == null
|
343
|
-
return arg.an_instance_of ?
|
344
|
-
call.args[i].constructor != arg.an_instance_of:
|
345
|
-
!equal(arg, call.args[i])
|
346
|
-
|
347
|
-
})
|
348
|
-
})
|
349
|
-
},
|
350
|
-
|
351
|
-
// Check if any arguments pass
|
352
|
-
|
353
|
-
anyArgsPass : function() {
|
354
|
-
return any(this.calls, function(call){
|
355
|
-
return any(self.expectedArgs, function(i, arg){
|
356
|
-
return arg.an_instance_of ?
|
357
|
-
call.args[i].constructor == arg.an_instance_of:
|
358
|
-
equal(arg, call.args[i])
|
359
|
-
|
360
|
-
})
|
361
|
-
})
|
362
|
-
},
|
363
|
-
|
364
|
-
// Return the passing args
|
365
|
-
|
366
|
-
passingArgs : function() {
|
367
|
-
return this.anyArgsPass().args
|
368
|
-
},
|
369
|
-
|
370
|
-
// Return the failing args
|
371
|
-
|
372
|
-
failingArgs : function() {
|
373
|
-
return this.anyArgsFail().args
|
374
|
-
},
|
375
|
-
|
376
|
-
// Report assertion results
|
377
|
-
|
378
|
-
report : function() {
|
379
|
-
if (JSpec.assert)
|
380
|
-
this.passed ? ++JSpec.stats.passes : ++JSpec.stats.failures
|
381
|
-
return this
|
382
|
-
},
|
383
|
-
|
384
|
-
// Run the assertion
|
385
|
-
|
386
|
-
run : function() {
|
387
|
-
var methodString = 'expected ' + object.toString() + '.' + method + '()' + (negate ? ' not' : '' )
|
388
|
-
|
389
|
-
function times(n) {
|
390
|
-
return n > 2 ? n + ' times' : { 1: 'once', 2: 'twice' }[n]
|
391
|
-
}
|
392
|
-
|
393
|
-
if (this.expectedResult != null && (negate ? this.anyResultsPass() : this.anyResultsFail()))
|
394
|
-
this.message = methodString + ' to return ' + puts(this.expectedResult) +
|
395
|
-
' but ' + (negate ? 'it did' : 'got ' + puts(this.failingResult()))
|
396
|
-
|
397
|
-
if (this.expectedArgs && (negate ? !this.expectedResult && this.anyArgsPass() : this.anyArgsFail()))
|
398
|
-
this.message = methodString + ' to be called with ' + puts.apply(this, this.expectedArgs) +
|
399
|
-
' but was' + (negate ? '' : ' called with ' + puts.apply(this, this.failingArgs()))
|
400
|
-
|
401
|
-
if (negate ? !this.expectedResult && !this.expectedArgs && this.calls.length >= this.times : this.calls.length != this.times)
|
402
|
-
this.message = methodString + ' to be called ' + times(this.times) +
|
403
|
-
', but ' + (this.calls.length == 0 ? ' was not called' : ' was called ' + times(this.calls.length))
|
404
|
-
|
405
|
-
if (!this.message.length)
|
406
|
-
this.passed = true
|
407
|
-
|
408
|
-
return this
|
409
|
-
}
|
410
|
-
})
|
411
|
-
},
|
412
|
-
|
413
|
-
/**
|
414
|
-
* Specification Suite block object.
|
415
|
-
*
|
416
|
-
* @param {string} description
|
417
|
-
* @param {function} body
|
418
|
-
* @api private
|
419
|
-
*/
|
420
|
-
|
421
|
-
Suite : function(description, body, isShared) {
|
422
|
-
var self = this
|
423
|
-
extend(this, {
|
424
|
-
body: body,
|
425
|
-
description: description,
|
426
|
-
suites: [],
|
427
|
-
sharedBehaviors: [],
|
428
|
-
specs: [],
|
429
|
-
ran: false,
|
430
|
-
shared: isShared,
|
431
|
-
hooks: { 'before' : [], 'after' : [],
|
432
|
-
'before_each' : [], 'after_each' : [],
|
433
|
-
'before_nested' : [], 'after_nested' : []},
|
434
|
-
|
435
|
-
// Add a spec to the suite
|
436
|
-
|
437
|
-
addSpec : function(description, body) {
|
438
|
-
var spec = new JSpec.Spec(description, body)
|
439
|
-
this.specs.push(spec)
|
440
|
-
JSpec.stats.specs++ // TODO: abstract
|
441
|
-
spec.suite = this
|
442
|
-
},
|
443
|
-
|
444
|
-
// Add a before hook to the suite
|
445
|
-
|
446
|
-
addBefore : function(options, body) {
|
447
|
-
body.options = options || {}
|
448
|
-
this.befores.push(body)
|
449
|
-
},
|
450
|
-
|
451
|
-
// Add an after hook to the suite
|
452
|
-
|
453
|
-
addAfter : function(options, body) {
|
454
|
-
body.options = options || {}
|
455
|
-
this.afters.unshift(body)
|
456
|
-
},
|
457
|
-
|
458
|
-
// Add a hook to the suite
|
459
|
-
|
460
|
-
addHook : function(hook, body) {
|
461
|
-
this.hooks[hook].push(body)
|
462
|
-
},
|
463
|
-
|
464
|
-
// Add a nested suite
|
465
|
-
|
466
|
-
addSuite : function(description, body, isShared) {
|
467
|
-
var suite = new JSpec.Suite(description, body, isShared)
|
468
|
-
JSpec.allSuites.push(suite)
|
469
|
-
suite.name = suite.description
|
470
|
-
suite.description = this.description + ' ' + suite.description
|
471
|
-
this.suites.push(suite)
|
472
|
-
suite.suite = this
|
473
|
-
},
|
474
|
-
|
475
|
-
// Invoke a hook in context to this suite
|
476
|
-
|
477
|
-
hook : function(hook) {
|
478
|
-
if (hook != 'before' && hook != 'after')
|
479
|
-
if (this.suite) this.suite.hook(hook)
|
480
|
-
|
481
|
-
each(this.hooks[hook], function(body) {
|
482
|
-
JSpec.evalBody(body, "Error in hook '" + hook + "', suite '" + self.description + "': ")
|
483
|
-
})
|
484
|
-
},
|
485
|
-
|
486
|
-
// Check if nested suites are present
|
487
|
-
|
488
|
-
hasSuites : function() {
|
489
|
-
return this.suites.length
|
490
|
-
},
|
491
|
-
|
492
|
-
// Check if this suite has specs
|
493
|
-
|
494
|
-
hasSpecs : function() {
|
495
|
-
return this.specs.length
|
496
|
-
},
|
497
|
-
|
498
|
-
// Check if the entire suite passed
|
499
|
-
|
500
|
-
passed : function() {
|
501
|
-
return !any(this.specs, function(spec){
|
502
|
-
return !spec.passed()
|
503
|
-
})
|
504
|
-
},
|
505
|
-
|
506
|
-
isShared : function(){
|
507
|
-
return this.shared
|
508
|
-
},
|
509
|
-
|
510
|
-
isExecutable : function() {
|
511
|
-
return !this.isShared() && this.hasSpecs()
|
512
|
-
}
|
513
|
-
})
|
514
|
-
},
|
515
|
-
|
516
|
-
/**
|
517
|
-
* Specification block object.
|
518
|
-
*
|
519
|
-
* @param {string} description
|
520
|
-
* @param {function} body
|
521
|
-
* @api private
|
522
|
-
*/
|
523
|
-
|
524
|
-
Spec : function(description, body) {
|
525
|
-
extend(this, {
|
526
|
-
body: body,
|
527
|
-
description: description,
|
528
|
-
assertions: [],
|
529
|
-
|
530
|
-
// Add passing assertion
|
531
|
-
|
532
|
-
pass : function(message) {
|
533
|
-
this.assertions.push({ passed: true, message: message })
|
534
|
-
if (JSpec.assert) ++JSpec.stats.passes
|
535
|
-
},
|
536
|
-
|
537
|
-
// Add failing assertion
|
538
|
-
|
539
|
-
fail : function(message) {
|
540
|
-
this.assertions.push({ passed: false, message: message })
|
541
|
-
if (JSpec.assert) ++JSpec.stats.failures
|
542
|
-
},
|
543
|
-
|
544
|
-
// Run deferred assertions
|
545
|
-
|
546
|
-
runDeferredAssertions : function() {
|
547
|
-
each(this.assertions, function(assertion){
|
548
|
-
if (assertion.defer) assertion.run().report(), hook('afterAssertion', assertion)
|
549
|
-
})
|
550
|
-
},
|
551
|
-
|
552
|
-
// Find first failing assertion
|
553
|
-
|
554
|
-
failure : function() {
|
555
|
-
return find(this.assertions, function(assertion){
|
556
|
-
return !assertion.passed
|
557
|
-
})
|
558
|
-
},
|
559
|
-
|
560
|
-
// Find all failing assertions
|
561
|
-
|
562
|
-
failures : function() {
|
563
|
-
return select(this.assertions, function(assertion){
|
564
|
-
return !assertion.passed
|
565
|
-
})
|
566
|
-
},
|
567
|
-
|
568
|
-
// Weither or not the spec passed
|
569
|
-
|
570
|
-
passed : function() {
|
571
|
-
return !this.failure()
|
572
|
-
},
|
573
|
-
|
574
|
-
// Weither or not the spec requires implementation (no assertions)
|
575
|
-
|
576
|
-
requiresImplementation : function() {
|
577
|
-
return this.assertions.length == 0
|
578
|
-
},
|
579
|
-
|
580
|
-
// Sprite based assertions graph
|
581
|
-
|
582
|
-
assertionsGraph : function() {
|
583
|
-
return map(this.assertions, function(assertion){
|
584
|
-
return '<span class="assertion ' + (assertion.passed ? 'passed' : 'failed') + '"></span>'
|
585
|
-
}).join('')
|
586
|
-
}
|
587
|
-
})
|
588
|
-
},
|
589
|
-
|
590
|
-
Module : function(methods) {
|
591
|
-
extend(this, methods)
|
592
|
-
},
|
593
|
-
|
594
|
-
JSON : {
|
595
|
-
|
596
|
-
/**
|
597
|
-
* Generic sequences.
|
598
|
-
*/
|
599
|
-
|
600
|
-
meta : {
|
601
|
-
'\b' : '\\b',
|
602
|
-
'\t' : '\\t',
|
603
|
-
'\n' : '\\n',
|
604
|
-
'\f' : '\\f',
|
605
|
-
'\r' : '\\r',
|
606
|
-
'"' : '\\"',
|
607
|
-
'\\' : '\\\\'
|
608
|
-
},
|
609
|
-
|
610
|
-
/**
|
611
|
-
* Escapable sequences.
|
612
|
-
*/
|
613
|
-
|
614
|
-
escapable : /[\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
615
|
-
|
616
|
-
/**
|
617
|
-
* JSON encode _object_.
|
618
|
-
*
|
619
|
-
* @param {mixed} object
|
620
|
-
* @return {string}
|
621
|
-
* @api private
|
622
|
-
*/
|
623
|
-
|
624
|
-
encode : function(object) {
|
625
|
-
var self = this
|
626
|
-
if (object == undefined || object == null) return 'null'
|
627
|
-
if (object === true) return 'true'
|
628
|
-
if (object === false) return 'false'
|
629
|
-
switch (typeof object) {
|
630
|
-
case 'number': return object
|
631
|
-
case 'string': return this.escapable.test(object) ?
|
632
|
-
'"' + object.replace(this.escapable, function (a) {
|
633
|
-
return typeof self.meta[a] === 'string' ? self.meta[a] :
|
634
|
-
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4)
|
635
|
-
}) + '"' :
|
636
|
-
'"' + object + '"'
|
637
|
-
case 'object':
|
638
|
-
if (object.constructor == Array)
|
639
|
-
return '[' + map(object, function(val){
|
640
|
-
return self.encode(val)
|
641
|
-
}).join(', ') + ']'
|
642
|
-
else if (object)
|
643
|
-
return '{' + map(object, function(key, val){
|
644
|
-
return self.encode(key) + ':' + self.encode(val)
|
645
|
-
}).join(', ') + '}'
|
646
|
-
}
|
647
|
-
return 'null'
|
648
|
-
}
|
649
|
-
},
|
650
|
-
|
651
|
-
// --- DSLs
|
652
|
-
|
653
|
-
DSLs : {
|
654
|
-
snake : {
|
655
|
-
expect : function(actual){
|
656
|
-
return JSpec.expect(actual)
|
657
|
-
},
|
658
|
-
|
659
|
-
describe : function(description, body) {
|
660
|
-
return JSpec.currentSuite.addSuite(description, body, false)
|
661
|
-
},
|
662
|
-
|
663
|
-
it : function(description, body) {
|
664
|
-
return JSpec.currentSuite.addSpec(description, body)
|
665
|
-
},
|
666
|
-
|
667
|
-
before : function(body) {
|
668
|
-
return JSpec.currentSuite.addHook('before', body)
|
669
|
-
},
|
670
|
-
|
671
|
-
after : function(body) {
|
672
|
-
return JSpec.currentSuite.addHook('after', body)
|
673
|
-
},
|
674
|
-
|
675
|
-
before_each : function(body) {
|
676
|
-
return JSpec.currentSuite.addHook('before_each', body)
|
677
|
-
},
|
678
|
-
|
679
|
-
after_each : function(body) {
|
680
|
-
return JSpec.currentSuite.addHook('after_each', body)
|
681
|
-
},
|
682
|
-
|
683
|
-
before_nested : function(body) {
|
684
|
-
return JSpec.currentSuite.addHook('before_nested', body)
|
685
|
-
},
|
686
|
-
|
687
|
-
after_nested : function(body){
|
688
|
-
return JSpec.currentSuite.addhook('after_nested', body)
|
689
|
-
},
|
690
|
-
|
691
|
-
shared_behaviors_for : function(description, body){
|
692
|
-
return JSpec.currentSuite.addSuite(description, body, true)
|
693
|
-
},
|
694
|
-
|
695
|
-
should_behave_like : function(description) {
|
696
|
-
return JSpec.shareBehaviorsOf(description)
|
697
|
-
}
|
698
|
-
}
|
699
|
-
},
|
700
|
-
|
701
|
-
// --- Methods
|
702
|
-
|
703
|
-
/**
|
704
|
-
* Check if _value_ is 'stop'. For use as a
|
705
|
-
* utility callback function.
|
706
|
-
*
|
707
|
-
* @param {mixed} value
|
708
|
-
* @return {bool}
|
709
|
-
* @api public
|
710
|
-
*/
|
711
|
-
|
712
|
-
haveStopped : function(value) {
|
713
|
-
return value === 'stop'
|
714
|
-
},
|
715
|
-
|
716
|
-
/**
|
717
|
-
* Include _object_ which may be a hash or Module instance.
|
718
|
-
*
|
719
|
-
* @param {hash, Module} object
|
720
|
-
* @return {JSpec}
|
721
|
-
* @api public
|
722
|
-
*/
|
723
|
-
|
724
|
-
include : function(object) {
|
725
|
-
var module = object.constructor == JSpec.Module ? object : new JSpec.Module(object)
|
726
|
-
this.modules.push(module)
|
727
|
-
if ('init' in module) module.init()
|
728
|
-
if ('utilities' in module) extend(this.defaultContext, module.utilities)
|
729
|
-
if ('matchers' in module) this.addMatchers(module.matchers)
|
730
|
-
if ('reporters' in module) extend(this.reporters, module.reporters)
|
731
|
-
if ('DSLs' in module)
|
732
|
-
each(module.DSLs, function(name, methods){
|
733
|
-
JSpec.DSLs[name] = JSpec.DSLs[name] || {}
|
734
|
-
extend(JSpec.DSLs[name], methods)
|
735
|
-
})
|
736
|
-
return this
|
737
|
-
},
|
738
|
-
|
739
|
-
/**
|
740
|
-
* Add a module hook _name_, which is immediately
|
741
|
-
* called per module with the _args_ given. An array of
|
742
|
-
* hook return values is returned.
|
743
|
-
*
|
744
|
-
* @param {name} string
|
745
|
-
* @param {...} args
|
746
|
-
* @return {array}
|
747
|
-
* @api private
|
748
|
-
*/
|
749
|
-
|
750
|
-
hook : function(name, args) {
|
751
|
-
args = toArray(arguments, 1)
|
752
|
-
return inject(JSpec.modules, [], function(results, module){
|
753
|
-
if (typeof module[name] == 'function')
|
754
|
-
results.push(JSpec.evalHook(module, name, args))
|
755
|
-
})
|
756
|
-
},
|
757
|
-
|
758
|
-
/**
|
759
|
-
* Eval _module_ hook _name_ with _args_. Evaluates in context
|
760
|
-
* to the module itself, JSpec, and JSpec.context.
|
761
|
-
*
|
762
|
-
* @param {Module} module
|
763
|
-
* @param {string} name
|
764
|
-
* @param {array} args
|
765
|
-
* @return {mixed}
|
766
|
-
* @api private
|
767
|
-
*/
|
768
|
-
|
769
|
-
evalHook : function(module, name, args) {
|
770
|
-
hook('evaluatingHookBody', module, name)
|
771
|
-
return module[name].apply(module, args)
|
772
|
-
},
|
773
|
-
|
774
|
-
/**
|
775
|
-
* Same as hook() however accepts only one _arg_ which is
|
776
|
-
* considered immutable. This function passes the arg
|
777
|
-
* to the first module, then passes the return value of the last
|
778
|
-
* module called, to the following module.
|
779
|
-
*
|
780
|
-
* @param {string} name
|
781
|
-
* @param {mixed} arg
|
782
|
-
* @return {mixed}
|
783
|
-
* @api private
|
784
|
-
*/
|
785
|
-
|
786
|
-
hookImmutable : function(name, arg) {
|
787
|
-
return inject(JSpec.modules, arg, function(result, module){
|
788
|
-
if (typeof module[name] == 'function')
|
789
|
-
return JSpec.evalHook(module, name, [result])
|
790
|
-
})
|
791
|
-
},
|
792
|
-
|
793
|
-
/**
|
794
|
-
* Find a shared example suite by its description or name.
|
795
|
-
* First searches parent tree of suites for shared behavior
|
796
|
-
* before falling back to global scoped nested behaviors.
|
797
|
-
*
|
798
|
-
* @param {string} description
|
799
|
-
* @return {Suite}
|
800
|
-
* @api private
|
801
|
-
*/
|
802
|
-
|
803
|
-
findSharedBehavior : function(description) {
|
804
|
-
var behavior
|
805
|
-
return (behavior = JSpec.findLocalSharedBehavior(description))
|
806
|
-
? behavior
|
807
|
-
: JSpec.findGlobalSharedBehavior(description)
|
808
|
-
},
|
809
|
-
|
810
|
-
/**
|
811
|
-
* Find a shared example suite within the current suite's
|
812
|
-
* parent tree by its description or name.
|
813
|
-
*
|
814
|
-
* @param {string} description
|
815
|
-
* @return {Suite}
|
816
|
-
* @api private
|
817
|
-
*/
|
818
|
-
|
819
|
-
findLocalSharedBehavior : function(description) {
|
820
|
-
var behavior,
|
821
|
-
currentSuite = JSpec.currentSuite.suite
|
822
|
-
while (currentSuite)
|
823
|
-
if (behavior = find(currentSuite.suites, JSpec.suiteDescriptionPredicate(description)))
|
824
|
-
return behavior
|
825
|
-
else
|
826
|
-
currentSuite = currentSuite.suite
|
827
|
-
},
|
828
|
-
|
829
|
-
/**
|
830
|
-
* Find a shared example suite within the global
|
831
|
-
* scope by its description or name.
|
832
|
-
*
|
833
|
-
* @param {string} description
|
834
|
-
* @return {Suite}
|
835
|
-
* @api private
|
836
|
-
*/
|
837
|
-
|
838
|
-
findGlobalSharedBehavior : function(description) {
|
839
|
-
return find(JSpec.suites, JSpec.suiteDescriptionPredicate(description))
|
840
|
-
},
|
841
|
-
|
842
|
-
/**
|
843
|
-
* Build a predicate that will match a suite based on name or description
|
844
|
-
*
|
845
|
-
* @param {string} description
|
846
|
-
* @return {function}
|
847
|
-
* @api private
|
848
|
-
*/
|
849
|
-
|
850
|
-
suiteDescriptionPredicate : function(description) {
|
851
|
-
return function(suite){
|
852
|
-
return suite.name === description ||
|
853
|
-
suite.description === description
|
854
|
-
}
|
855
|
-
},
|
856
|
-
|
857
|
-
/**
|
858
|
-
* Share behaviors (specs) of the given suite with
|
859
|
-
* the current suite.
|
860
|
-
*
|
861
|
-
* @param {string} description
|
862
|
-
* @api public
|
863
|
-
*/
|
864
|
-
|
865
|
-
shareBehaviorsOf : function(description) {
|
866
|
-
var suite = JSpec.findSharedBehavior(description)
|
867
|
-
if (suite)
|
868
|
-
JSpec.evalBody(suite.body)
|
869
|
-
else
|
870
|
-
throw new Error("failed to find shared behaviors named `" + description + "'")
|
871
|
-
},
|
872
|
-
|
873
|
-
|
874
|
-
/**
|
875
|
-
* Convert arguments to an array.
|
876
|
-
*
|
877
|
-
* @param {object} arguments
|
878
|
-
* @param {int} offset
|
879
|
-
* @return {array}
|
880
|
-
* @api public
|
881
|
-
*/
|
882
|
-
|
883
|
-
toArray : function(arguments, offset) {
|
884
|
-
return Array.prototype.slice.call(arguments, offset || 0)
|
885
|
-
},
|
886
|
-
|
887
|
-
/**
|
888
|
-
* Return ANSI-escaped colored string.
|
889
|
-
*
|
890
|
-
* @param {string} string
|
891
|
-
* @param {string} color
|
892
|
-
* @return {string}
|
893
|
-
* @api public
|
894
|
-
*/
|
895
|
-
|
896
|
-
color : function(string, color) {
|
897
|
-
if (option('disableColors')) {
|
898
|
-
return string
|
899
|
-
} else {
|
900
|
-
return "\u001B[" + {
|
901
|
-
bold : 1,
|
902
|
-
black : 30,
|
903
|
-
red : 31,
|
904
|
-
green : 32,
|
905
|
-
yellow : 33,
|
906
|
-
blue : 34,
|
907
|
-
magenta : 35,
|
908
|
-
cyan : 36,
|
909
|
-
white : 37
|
910
|
-
}[color] + 'm' + string + "\u001B[0m"
|
911
|
-
}
|
912
|
-
},
|
913
|
-
|
914
|
-
/**
|
915
|
-
* Default matcher message callback.
|
916
|
-
*
|
917
|
-
* @api private
|
918
|
-
*/
|
919
|
-
|
920
|
-
defaultMatcherMessage : function(actual, expected, negate, name) {
|
921
|
-
return 'expected ' + puts(actual) + ' to ' +
|
922
|
-
(negate ? 'not ' : '') +
|
923
|
-
name.replace(/_/g, ' ') +
|
924
|
-
' ' + (expected.length > 1 ?
|
925
|
-
puts.apply(this, expected.slice(1)) :
|
926
|
-
'')
|
927
|
-
},
|
928
|
-
|
929
|
-
/**
|
930
|
-
* Normalize a matcher message.
|
931
|
-
*
|
932
|
-
* When no messge callback is present the defaultMatcherMessage
|
933
|
-
* will be assigned, will suffice for most matchers.
|
934
|
-
*
|
935
|
-
* @param {hash} matcher
|
936
|
-
* @return {hash}
|
937
|
-
* @api public
|
938
|
-
*/
|
939
|
-
|
940
|
-
normalizeMatcherMessage : function(matcher) {
|
941
|
-
if (typeof matcher.message != 'function')
|
942
|
-
matcher.message = this.defaultMatcherMessage
|
943
|
-
return matcher
|
944
|
-
},
|
945
|
-
|
946
|
-
/**
|
947
|
-
* Normalize a matcher body
|
948
|
-
*
|
949
|
-
* This process allows the following conversions until
|
950
|
-
* the matcher is in its final normalized hash state.
|
951
|
-
*
|
952
|
-
* - '==' becomes 'actual == expected'
|
953
|
-
* - 'actual == expected' becomes 'return actual == expected'
|
954
|
-
* - function(actual, expected) { return actual == expected } becomes
|
955
|
-
* { match : function(actual, expected) { return actual == expected }}
|
956
|
-
*
|
957
|
-
* @param {mixed} body
|
958
|
-
* @return {hash}
|
959
|
-
* @api public
|
960
|
-
*/
|
961
|
-
|
962
|
-
normalizeMatcherBody : function(body) {
|
963
|
-
var captures
|
964
|
-
switch (body.constructor) {
|
965
|
-
case String:
|
966
|
-
if (captures = body.match(/^alias (\w+)/)) return JSpec.matchers[last(captures)]
|
967
|
-
if (body.length < 4) body = 'actual ' + body + ' expected'
|
968
|
-
return { match: function(actual, expected) { return eval(body) }}
|
969
|
-
|
970
|
-
case Function:
|
971
|
-
return { match: body }
|
972
|
-
|
973
|
-
default:
|
974
|
-
return body
|
975
|
-
}
|
976
|
-
},
|
977
|
-
|
978
|
-
/**
|
979
|
-
* Get option value. This method first checks if
|
980
|
-
* the option key has been set via the query string,
|
981
|
-
* otherwise returning the options hash value.
|
982
|
-
*
|
983
|
-
* @param {string} key
|
984
|
-
* @return {mixed}
|
985
|
-
* @api public
|
986
|
-
*/
|
987
|
-
|
988
|
-
option : function(key) {
|
989
|
-
return (value = query(key)) !== null ? value :
|
990
|
-
JSpec.options[key] || null
|
991
|
-
},
|
992
|
-
|
993
|
-
/**
|
994
|
-
* Check if object _a_, is equal to object _b_.
|
995
|
-
*
|
996
|
-
* @param {object} a
|
997
|
-
* @param {object} b
|
998
|
-
* @return {bool}
|
999
|
-
* @api private
|
1000
|
-
*/
|
1001
|
-
|
1002
|
-
equal: function(a, b) {
|
1003
|
-
if (typeof a != typeof b) return
|
1004
|
-
if (a === b) return true
|
1005
|
-
if (a instanceof RegExp)
|
1006
|
-
return a.toString() === b.toString()
|
1007
|
-
if (a instanceof Date)
|
1008
|
-
return Number(a) === Number(b)
|
1009
|
-
if (typeof a != 'object') return
|
1010
|
-
if (a.length !== undefined)
|
1011
|
-
if (a.length !== b.length) return
|
1012
|
-
else
|
1013
|
-
for (var i = 0, len = a.length; i < len; ++i)
|
1014
|
-
if (!equal(a[i], b[i]))
|
1015
|
-
return
|
1016
|
-
for (var key in a)
|
1017
|
-
if (!equal(a[key], b[key]))
|
1018
|
-
return
|
1019
|
-
return true
|
1020
|
-
},
|
1021
|
-
|
1022
|
-
/**
|
1023
|
-
* Return last element of an array.
|
1024
|
-
*
|
1025
|
-
* @param {array} array
|
1026
|
-
* @return {object}
|
1027
|
-
* @api public
|
1028
|
-
*/
|
1029
|
-
|
1030
|
-
last : function(array) {
|
1031
|
-
return array[array.length - 1]
|
1032
|
-
},
|
1033
|
-
|
1034
|
-
/**
|
1035
|
-
* Convert object(s) to a print-friend string.
|
1036
|
-
*
|
1037
|
-
* @param {...} object
|
1038
|
-
* @return {string}
|
1039
|
-
* @api public
|
1040
|
-
*/
|
1041
|
-
|
1042
|
-
puts : function(object) {
|
1043
|
-
if (arguments.length > 1)
|
1044
|
-
return map(toArray(arguments), function(arg){
|
1045
|
-
return puts(arg)
|
1046
|
-
}).join(', ')
|
1047
|
-
if (object === undefined) return 'undefined'
|
1048
|
-
if (object === null) return 'null'
|
1049
|
-
if (object === true) return 'true'
|
1050
|
-
if (object === false) return 'false'
|
1051
|
-
if (object.an_instance_of) return 'an instance of ' + object.an_instance_of.name
|
1052
|
-
if (object.jquery && object.selector.length > 0) return 'selector ' + puts(object.selector)
|
1053
|
-
if (object.jquery) return object.get(0).outerHTML
|
1054
|
-
if (object.nodeName) return object.outerHTML
|
1055
|
-
switch (object.constructor) {
|
1056
|
-
case Function: return object.name || object
|
1057
|
-
case String:
|
1058
|
-
return '"' + object
|
1059
|
-
.replace(/"/g, '\\"')
|
1060
|
-
.replace(/\n/g, '\\n')
|
1061
|
-
.replace(/\t/g, '\\t')
|
1062
|
-
+ '"'
|
1063
|
-
case Array:
|
1064
|
-
return inject(object, '[', function(b, v){
|
1065
|
-
return b + ', ' + puts(v)
|
1066
|
-
}).replace('[,', '[') + ' ]'
|
1067
|
-
case Object:
|
1068
|
-
object.__hit__ = true
|
1069
|
-
return inject(object, '{', function(b, k, v) {
|
1070
|
-
if (k == '__hit__') return b
|
1071
|
-
return b + ', ' + k + ': ' + (v && v.__hit__ ? '<circular reference>' : puts(v))
|
1072
|
-
}).replace('{,', '{') + ' }'
|
1073
|
-
default:
|
1074
|
-
return object.toString()
|
1075
|
-
}
|
1076
|
-
},
|
1077
|
-
|
1078
|
-
/**
|
1079
|
-
* Parse an XML String and return a 'document'.
|
1080
|
-
*
|
1081
|
-
* @param {string} text
|
1082
|
-
* @return {document}
|
1083
|
-
* @api public
|
1084
|
-
*/
|
1085
|
-
|
1086
|
-
parseXML : function(text) {
|
1087
|
-
var xmlDoc
|
1088
|
-
if (window.DOMParser)
|
1089
|
-
xmlDoc = (new DOMParser()).parseFromString(text, "text/xml")
|
1090
|
-
else {
|
1091
|
-
xmlDoc = new ActiveXObject("Microsoft.XMLDOM")
|
1092
|
-
xmlDoc.async = "false"
|
1093
|
-
xmlDoc.loadXML(text)
|
1094
|
-
}
|
1095
|
-
return xmlDoc
|
1096
|
-
},
|
1097
|
-
|
1098
|
-
/**
|
1099
|
-
* Escape HTML.
|
1100
|
-
*
|
1101
|
-
* @param {string} html
|
1102
|
-
* @return {string}
|
1103
|
-
* @api public
|
1104
|
-
*/
|
1105
|
-
|
1106
|
-
escape : function(html) {
|
1107
|
-
return html.toString()
|
1108
|
-
.replace(/&/gmi, '&')
|
1109
|
-
.replace(/"/gmi, '"')
|
1110
|
-
.replace(/>/gmi, '>')
|
1111
|
-
.replace(/</gmi, '<')
|
1112
|
-
},
|
1113
|
-
|
1114
|
-
/**
|
1115
|
-
* Perform an assertion without reporting.
|
1116
|
-
*
|
1117
|
-
* This method is primarily used for internal
|
1118
|
-
* matchers in order retain DRYness. May be invoked
|
1119
|
-
* like below:
|
1120
|
-
*
|
1121
|
-
* does('foo', 'eql', 'foo')
|
1122
|
-
* does([1,2], 'include', 1, 2)
|
1123
|
-
*
|
1124
|
-
* External hooks are not run for internal assertions
|
1125
|
-
* performed by does().
|
1126
|
-
*
|
1127
|
-
* @param {mixed} actual
|
1128
|
-
* @param {string} matcher
|
1129
|
-
* @param {...} expected
|
1130
|
-
* @return {mixed}
|
1131
|
-
* @api private
|
1132
|
-
*/
|
1133
|
-
|
1134
|
-
does : function(actual, matcher, expected) {
|
1135
|
-
var assertion = new JSpec.Assertion(JSpec.matchers[matcher], actual, toArray(arguments, 2))
|
1136
|
-
return assertion.run().result
|
1137
|
-
},
|
1138
|
-
|
1139
|
-
/**
|
1140
|
-
* Perform an assertion.
|
1141
|
-
*
|
1142
|
-
* expect(true).to('be', true)
|
1143
|
-
* expect('foo').not_to('include', 'bar')
|
1144
|
-
* expect([1, [2]]).to('include', 1, [2])
|
1145
|
-
*
|
1146
|
-
* @param {mixed} actual
|
1147
|
-
* @return {hash}
|
1148
|
-
* @api public
|
1149
|
-
*/
|
1150
|
-
|
1151
|
-
expect : function(actual) {
|
1152
|
-
function assert(matcher, args, negate) {
|
1153
|
-
var expected = toArray(args, 1)
|
1154
|
-
matcher.negate = negate
|
1155
|
-
var assertion = new JSpec.Assertion(matcher, actual, expected, negate)
|
1156
|
-
hook('beforeAssertion', assertion)
|
1157
|
-
if (matcher.defer) assertion.run()
|
1158
|
-
else JSpec.currentSpec.assertions.push(assertion.run().report()), hook('afterAssertion', assertion)
|
1159
|
-
return assertion.result
|
1160
|
-
}
|
1161
|
-
|
1162
|
-
function to(matcher) {
|
1163
|
-
return assert(matcher, arguments, false)
|
1164
|
-
}
|
1165
|
-
|
1166
|
-
function not_to(matcher) {
|
1167
|
-
return assert(matcher, arguments, true)
|
1168
|
-
}
|
1169
|
-
|
1170
|
-
return {
|
1171
|
-
to : to,
|
1172
|
-
should : to,
|
1173
|
-
not_to: not_to,
|
1174
|
-
should_not : not_to
|
1175
|
-
}
|
1176
|
-
},
|
1177
|
-
|
1178
|
-
/**
|
1179
|
-
* Strim whitespace or chars.
|
1180
|
-
*
|
1181
|
-
* @param {string} string
|
1182
|
-
* @param {string} chars
|
1183
|
-
* @return {string}
|
1184
|
-
* @api public
|
1185
|
-
*/
|
1186
|
-
|
1187
|
-
strip : function(string, chars) {
|
1188
|
-
return string.
|
1189
|
-
replace(new RegExp('[' + (chars || '\\s') + ']*$'), '').
|
1190
|
-
replace(new RegExp('^[' + (chars || '\\s') + ']*'), '')
|
1191
|
-
},
|
1192
|
-
|
1193
|
-
/**
|
1194
|
-
* Call an iterator callback with arguments a, or b
|
1195
|
-
* depending on the arity of the callback.
|
1196
|
-
*
|
1197
|
-
* @param {function} callback
|
1198
|
-
* @param {mixed} a
|
1199
|
-
* @param {mixed} b
|
1200
|
-
* @return {mixed}
|
1201
|
-
* @api private
|
1202
|
-
*/
|
1203
|
-
|
1204
|
-
callIterator : function(callback, a, b) {
|
1205
|
-
return callback.length == 1 ? callback(b) : callback(a, b)
|
1206
|
-
},
|
1207
|
-
|
1208
|
-
/**
|
1209
|
-
* Extend an object with another.
|
1210
|
-
*
|
1211
|
-
* @param {object} object
|
1212
|
-
* @param {object} other
|
1213
|
-
* @api public
|
1214
|
-
*/
|
1215
|
-
|
1216
|
-
extend : function(object, other) {
|
1217
|
-
each(other, function(property, value){
|
1218
|
-
object[property] = value
|
1219
|
-
})
|
1220
|
-
},
|
1221
|
-
|
1222
|
-
/**
|
1223
|
-
* Iterate an object, invoking the given callback.
|
1224
|
-
*
|
1225
|
-
* @param {hash, array} object
|
1226
|
-
* @param {function} callback
|
1227
|
-
* @return {JSpec}
|
1228
|
-
* @api public
|
1229
|
-
*/
|
1230
|
-
|
1231
|
-
each : function(object, callback) {
|
1232
|
-
if (object.constructor == Array)
|
1233
|
-
for (var i = 0, len = object.length; i < len; ++i)
|
1234
|
-
callIterator(callback, i, object[i])
|
1235
|
-
else
|
1236
|
-
for (var key in object)
|
1237
|
-
if (object.hasOwnProperty(key))
|
1238
|
-
callIterator(callback, key, object[key])
|
1239
|
-
},
|
1240
|
-
|
1241
|
-
/**
|
1242
|
-
* Iterate with memo.
|
1243
|
-
*
|
1244
|
-
* @param {hash, array} object
|
1245
|
-
* @param {object} memo
|
1246
|
-
* @param {function} callback
|
1247
|
-
* @return {object}
|
1248
|
-
* @api public
|
1249
|
-
*/
|
1250
|
-
|
1251
|
-
inject : function(object, memo, callback) {
|
1252
|
-
each(object, function(key, value){
|
1253
|
-
memo = (callback.length == 2 ?
|
1254
|
-
callback(memo, value):
|
1255
|
-
callback(memo, key, value)) ||
|
1256
|
-
memo
|
1257
|
-
})
|
1258
|
-
return memo
|
1259
|
-
},
|
1260
|
-
|
1261
|
-
/**
|
1262
|
-
* Destub _object_'s _method_. When no _method_ is passed
|
1263
|
-
* all stubbed methods are destubbed. When no arguments
|
1264
|
-
* are passed every object found in JSpec.stubbed will be
|
1265
|
-
* destubbed.
|
1266
|
-
*
|
1267
|
-
* @param {mixed} object
|
1268
|
-
* @param {string} method
|
1269
|
-
* @api public
|
1270
|
-
*/
|
1271
|
-
|
1272
|
-
destub : function(object, method) {
|
1273
|
-
var captures
|
1274
|
-
if (method) {
|
1275
|
-
if (object['__prototype__' + method])
|
1276
|
-
delete object[method]
|
1277
|
-
else
|
1278
|
-
object[method] = object['__original__' + method]
|
1279
|
-
delete object['__prototype__' + method]
|
1280
|
-
delete object['__original____' + method]
|
1281
|
-
}
|
1282
|
-
else if (object) {
|
1283
|
-
for (var key in object)
|
1284
|
-
if (captures = key.match(/^(?:__prototype__|__original__)(.*)/))
|
1285
|
-
destub(object, captures[1])
|
1286
|
-
}
|
1287
|
-
else
|
1288
|
-
while (JSpec.stubbed.length)
|
1289
|
-
destub(JSpec.stubbed.shift())
|
1290
|
-
},
|
1291
|
-
|
1292
|
-
/**
|
1293
|
-
* Stub _object_'s _method_.
|
1294
|
-
*
|
1295
|
-
* stub(foo, 'toString').and_return('bar')
|
1296
|
-
*
|
1297
|
-
* @param {mixed} object
|
1298
|
-
* @param {string} method
|
1299
|
-
* @return {hash}
|
1300
|
-
* @api public
|
1301
|
-
*/
|
1302
|
-
|
1303
|
-
stub : function(object, method) {
|
1304
|
-
hook('stubbing', object, method)
|
1305
|
-
JSpec.stubbed.push(object)
|
1306
|
-
var type = object.hasOwnProperty(method) ? '__original__' : '__prototype__'
|
1307
|
-
object[type + method] = object[method]
|
1308
|
-
object[method] = function(){}
|
1309
|
-
return {
|
1310
|
-
and_return : function(value) {
|
1311
|
-
if (typeof value == 'function') object[method] = value
|
1312
|
-
else object[method] = function(){ return value }
|
1313
|
-
}
|
1314
|
-
}
|
1315
|
-
},
|
1316
|
-
|
1317
|
-
/**
|
1318
|
-
* Map callback return values.
|
1319
|
-
*
|
1320
|
-
* @param {hash, array} object
|
1321
|
-
* @param {function} callback
|
1322
|
-
* @return {array}
|
1323
|
-
* @api public
|
1324
|
-
*/
|
1325
|
-
|
1326
|
-
map : function(object, callback) {
|
1327
|
-
return inject(object, [], function(memo, key, value){
|
1328
|
-
memo.push(callIterator(callback, key, value))
|
1329
|
-
})
|
1330
|
-
},
|
1331
|
-
|
1332
|
-
/**
|
1333
|
-
* Returns the first matching expression or null.
|
1334
|
-
*
|
1335
|
-
* @param {hash, array} object
|
1336
|
-
* @param {function} callback
|
1337
|
-
* @return {mixed}
|
1338
|
-
* @api public
|
1339
|
-
*/
|
1340
|
-
|
1341
|
-
any : function(object, callback) {
|
1342
|
-
return inject(object, null, function(state, key, value){
|
1343
|
-
if (state == undefined)
|
1344
|
-
return callIterator(callback, key, value) ? value : state
|
1345
|
-
})
|
1346
|
-
},
|
1347
|
-
|
1348
|
-
/**
|
1349
|
-
* Returns an array of values collected when the callback
|
1350
|
-
* given evaluates to true.
|
1351
|
-
*
|
1352
|
-
* @param {hash, array} object
|
1353
|
-
* @return {function} callback
|
1354
|
-
* @return {array}
|
1355
|
-
* @api public
|
1356
|
-
*/
|
1357
|
-
|
1358
|
-
select : function(object, callback) {
|
1359
|
-
return inject(object, [], function(selected, key, value){
|
1360
|
-
if (callIterator(callback, key, value))
|
1361
|
-
selected.push(value)
|
1362
|
-
})
|
1363
|
-
},
|
1364
|
-
|
1365
|
-
/**
|
1366
|
-
* Define matchers.
|
1367
|
-
*
|
1368
|
-
* @param {hash} matchers
|
1369
|
-
* @api public
|
1370
|
-
*/
|
1371
|
-
|
1372
|
-
addMatchers : function(matchers) {
|
1373
|
-
each(matchers, function(name, body){
|
1374
|
-
JSpec.addMatcher(name, body)
|
1375
|
-
})
|
1376
|
-
},
|
1377
|
-
|
1378
|
-
/**
|
1379
|
-
* Define a matcher.
|
1380
|
-
*
|
1381
|
-
* @param {string} name
|
1382
|
-
* @param {hash, function, string} body
|
1383
|
-
* @api public
|
1384
|
-
*/
|
1385
|
-
|
1386
|
-
addMatcher : function(name, body) {
|
1387
|
-
hook('addingMatcher', name, body)
|
1388
|
-
if (name.indexOf(' ') != -1) {
|
1389
|
-
var matchers = name.split(/\s+/)
|
1390
|
-
var prefix = matchers.shift()
|
1391
|
-
each(matchers, function(name) {
|
1392
|
-
JSpec.addMatcher(prefix + '_' + name, body(name))
|
1393
|
-
})
|
1394
|
-
}
|
1395
|
-
this.matchers[name] = this.normalizeMatcherMessage(this.normalizeMatcherBody(body))
|
1396
|
-
this.matchers[name].name = name
|
1397
|
-
},
|
1398
|
-
|
1399
|
-
/**
|
1400
|
-
* Add a root suite to JSpec.
|
1401
|
-
*
|
1402
|
-
* @param {string} description
|
1403
|
-
* @param {body} function
|
1404
|
-
* @api public
|
1405
|
-
*/
|
1406
|
-
|
1407
|
-
describe : function(description, body) {
|
1408
|
-
var suite = new JSpec.Suite(description, body, false)
|
1409
|
-
hook('addingSuite', suite)
|
1410
|
-
this.allSuites.push(suite)
|
1411
|
-
this.suites.push(suite)
|
1412
|
-
},
|
1413
|
-
|
1414
|
-
/**
|
1415
|
-
* Add a shared example suite to JSpec.
|
1416
|
-
*
|
1417
|
-
* @param {string} description
|
1418
|
-
* @param {body} function
|
1419
|
-
* @api public
|
1420
|
-
*/
|
1421
|
-
|
1422
|
-
shared_behaviors_for : function(description, body) {
|
1423
|
-
var suite = new JSpec.Suite(description, body, true)
|
1424
|
-
hook('addingSuite', suite)
|
1425
|
-
this.allSuites.push(suite)
|
1426
|
-
this.suites.push(suite)
|
1427
|
-
},
|
1428
|
-
|
1429
|
-
/**
|
1430
|
-
* Return the contents of a function body.
|
1431
|
-
*
|
1432
|
-
* @param {function} body
|
1433
|
-
* @return {string}
|
1434
|
-
* @api public
|
1435
|
-
*/
|
1436
|
-
|
1437
|
-
contentsOf : function(body) {
|
1438
|
-
return body.toString().match(/^[^\{]*{((.*\n*)*)}/m)[1]
|
1439
|
-
},
|
1440
|
-
|
1441
|
-
/**
|
1442
|
-
* Evaluate a JSpec capture body.
|
1443
|
-
*
|
1444
|
-
* @param {function} body
|
1445
|
-
* @param {string} errorMessage (optional)
|
1446
|
-
* @return {Type}
|
1447
|
-
* @api private
|
1448
|
-
*/
|
1449
|
-
|
1450
|
-
evalBody : function(body, errorMessage) {
|
1451
|
-
var dsl = this.DSL || this.DSLs.snake
|
1452
|
-
var matchers = this.matchers
|
1453
|
-
var context = this.context || this.defaultContext
|
1454
|
-
var contents = this.contentsOf(body)
|
1455
|
-
hook('evaluatingBody', dsl, matchers, context, contents)
|
1456
|
-
with (dsl){ with (context) { with (matchers) { eval(contents) }}}
|
1457
|
-
},
|
1458
|
-
|
1459
|
-
/**
|
1460
|
-
* Pre-process a string of JSpec.
|
1461
|
-
*
|
1462
|
-
* @param {string} input
|
1463
|
-
* @return {string}
|
1464
|
-
* @api private
|
1465
|
-
*/
|
1466
|
-
|
1467
|
-
preprocess : function(input) {
|
1468
|
-
if (typeof input != 'string') return
|
1469
|
-
input = hookImmutable('preprocessing', input)
|
1470
|
-
return input.
|
1471
|
-
replace(/\t/g, ' ').
|
1472
|
-
replace(/\r\n|\n|\r/g, '\n').
|
1473
|
-
split('__END__')[0].
|
1474
|
-
replace(/([\w\.]+)\.(stub|destub)\((.*?)\)$/gm, '$2($1, $3)').
|
1475
|
-
replace(/describe\s+(.*?)$/gm, 'describe($1, function(){').
|
1476
|
-
replace(/shared_behaviors_for\s+(.*?)$/gm, 'shared_behaviors_for($1, function(){').
|
1477
|
-
replace(/^\s+it\s+(.*?)$/gm, ' it($1, function(){').
|
1478
|
-
replace(/^ *(before_nested|after_nested|before_each|after_each|before|after)(?= |\n|$)/gm, 'JSpec.currentSuite.addHook("$1", function(){').
|
1479
|
-
replace(/^\s*end(?=\s|$)/gm, '});').
|
1480
|
-
replace(/-\{/g, 'function(){').
|
1481
|
-
replace(/(\d+)\.\.(\d+)/g, function(_, a, b){ return range(a, b) }).
|
1482
|
-
replace(/\.should([_\.]not)?[_\.](\w+)(?: |;|$)(.*)$/gm, '.should$1_$2($3)').
|
1483
|
-
replace(/([\/\s]*)(.+?)\.(should(?:[_\.]not)?)[_\.](\w+)\((.*)\)\s*;?$/gm, '$1 expect($2).$3($4, $5)').
|
1484
|
-
replace(/, \)/g, ')').
|
1485
|
-
replace(/should\.not/g, 'should_not')
|
1486
|
-
},
|
1487
|
-
|
1488
|
-
/**
|
1489
|
-
* Create a range string which can be evaluated to a native array.
|
1490
|
-
*
|
1491
|
-
* @param {int} start
|
1492
|
-
* @param {int} end
|
1493
|
-
* @return {string}
|
1494
|
-
* @api public
|
1495
|
-
*/
|
1496
|
-
|
1497
|
-
range : function(start, end) {
|
1498
|
-
var current = parseInt(start), end = parseInt(end), values = [current]
|
1499
|
-
if (end > current) while (++current <= end) values.push(current)
|
1500
|
-
else while (--current >= end) values.push(current)
|
1501
|
-
return '[' + values + ']'
|
1502
|
-
},
|
1503
|
-
|
1504
|
-
/**
|
1505
|
-
* Report on the results.
|
1506
|
-
*
|
1507
|
-
* @api public
|
1508
|
-
*/
|
1509
|
-
|
1510
|
-
report : function() {
|
1511
|
-
this.duration = Number(new Date) - this.start
|
1512
|
-
hook('reporting', JSpec.options)
|
1513
|
-
new (JSpec.options.reporter || JSpec.reporters.DOM)(JSpec, JSpec.options)
|
1514
|
-
},
|
1515
|
-
|
1516
|
-
/**
|
1517
|
-
* Run the spec suites. Options are merged
|
1518
|
-
* with JSpec options when present.
|
1519
|
-
*
|
1520
|
-
* @param {hash} options
|
1521
|
-
* @return {JSpec}
|
1522
|
-
* @api public
|
1523
|
-
*/
|
1524
|
-
|
1525
|
-
run : function(options) {
|
1526
|
-
if (any(hook('running'), haveStopped)) return this
|
1527
|
-
if (options) extend(this.options, options)
|
1528
|
-
this.start = Number(new Date)
|
1529
|
-
each(this.suites, function(suite) { JSpec.runSuite(suite) })
|
1530
|
-
return this
|
1531
|
-
},
|
1532
|
-
|
1533
|
-
/**
|
1534
|
-
* Run a suite.
|
1535
|
-
*
|
1536
|
-
* @param {Suite} suite
|
1537
|
-
* @api public
|
1538
|
-
*/
|
1539
|
-
|
1540
|
-
runSuite : function(suite) {
|
1541
|
-
if (!suite.isShared())
|
1542
|
-
{
|
1543
|
-
this.currentSuite = suite
|
1544
|
-
this.evalBody(suite.body)
|
1545
|
-
suite.ran = true
|
1546
|
-
hook('beforeSuite', suite), suite.hook('before'), suite.hook('before_nested')
|
1547
|
-
each(suite.specs, function(spec) {
|
1548
|
-
hook('beforeSpec', spec)
|
1549
|
-
suite.hook('before_each')
|
1550
|
-
JSpec.runSpec(spec)
|
1551
|
-
hook('afterSpec', spec)
|
1552
|
-
suite.hook('after_each')
|
1553
|
-
})
|
1554
|
-
if (suite.hasSuites()) {
|
1555
|
-
each(suite.suites, function(suite) {
|
1556
|
-
JSpec.runSuite(suite)
|
1557
|
-
})
|
1558
|
-
}
|
1559
|
-
hook('afterSuite', suite), suite.hook('after_nested'), suite.hook('after')
|
1560
|
-
this.stats.suitesFinished++
|
1561
|
-
}
|
1562
|
-
},
|
1563
|
-
|
1564
|
-
/**
|
1565
|
-
* Report a failure for the current spec.
|
1566
|
-
*
|
1567
|
-
* @param {string} message
|
1568
|
-
* @api public
|
1569
|
-
*/
|
1570
|
-
|
1571
|
-
fail : function(message) {
|
1572
|
-
JSpec.currentSpec.fail(message)
|
1573
|
-
},
|
1574
|
-
|
1575
|
-
/**
|
1576
|
-
* Report a passing assertion for the current spec.
|
1577
|
-
*
|
1578
|
-
* @param {string} message
|
1579
|
-
* @api public
|
1580
|
-
*/
|
1581
|
-
|
1582
|
-
pass : function(message) {
|
1583
|
-
JSpec.currentSpec.pass(message)
|
1584
|
-
},
|
1585
|
-
|
1586
|
-
/**
|
1587
|
-
* Run a spec.
|
1588
|
-
*
|
1589
|
-
* @param {Spec} spec
|
1590
|
-
* @api public
|
1591
|
-
*/
|
1592
|
-
|
1593
|
-
runSpec : function(spec) {
|
1594
|
-
this.currentSpec = spec
|
1595
|
-
try { this.evalBody(spec.body) }
|
1596
|
-
catch (e) { fail(e) }
|
1597
|
-
spec.runDeferredAssertions()
|
1598
|
-
destub()
|
1599
|
-
this.stats.specsFinished++
|
1600
|
-
this.stats.assertions += spec.assertions.length
|
1601
|
-
},
|
1602
|
-
|
1603
|
-
/**
|
1604
|
-
* Require a dependency, with optional message.
|
1605
|
-
*
|
1606
|
-
* @param {string} dependency
|
1607
|
-
* @param {string} message (optional)
|
1608
|
-
* @return {JSpec}
|
1609
|
-
* @api public
|
1610
|
-
*/
|
1611
|
-
|
1612
|
-
requires : function(dependency, message) {
|
1613
|
-
hook('requiring', dependency, message)
|
1614
|
-
try { eval(dependency) }
|
1615
|
-
catch (e) { throw 'JSpec depends on ' + dependency + ' ' + message }
|
1616
|
-
return this
|
1617
|
-
},
|
1618
|
-
|
1619
|
-
/**
|
1620
|
-
* Query against the current query strings keys
|
1621
|
-
* or the queryString specified.
|
1622
|
-
*
|
1623
|
-
* @param {string} key
|
1624
|
-
* @param {string} queryString
|
1625
|
-
* @return {string, null}
|
1626
|
-
* @api private
|
1627
|
-
*/
|
1628
|
-
|
1629
|
-
query : function(key, queryString) {
|
1630
|
-
var queryString = (queryString || (main.location ? main.location.search : null) || '').substring(1)
|
1631
|
-
return inject(queryString.split('&'), null, function(value, pair){
|
1632
|
-
parts = pair.split('=')
|
1633
|
-
return parts[0] == key ? parts[1].replace(/%20|\+/gmi, ' ') : value
|
1634
|
-
})
|
1635
|
-
},
|
1636
|
-
|
1637
|
-
/**
|
1638
|
-
* Ad-hoc POST request for JSpec server usage.
|
1639
|
-
*
|
1640
|
-
* @param {string} uri
|
1641
|
-
* @param {string} data
|
1642
|
-
* @api private
|
1643
|
-
*/
|
1644
|
-
|
1645
|
-
post : function(uri, data) {
|
1646
|
-
if (any(hook('posting', uri, data), haveStopped)) return
|
1647
|
-
var request = this.xhr()
|
1648
|
-
request.open('POST', uri, false)
|
1649
|
-
request.setRequestHeader('Content-Type', 'application/json')
|
1650
|
-
request.send(JSpec.JSON.encode(data))
|
1651
|
-
},
|
1652
|
-
|
1653
|
-
/**
|
1654
|
-
* Instantiate an XMLHttpRequest.
|
1655
|
-
*
|
1656
|
-
* Here we utilize IE's lame ActiveXObjects first which
|
1657
|
-
* allow IE access serve files via the file: protocol, otherwise
|
1658
|
-
* we then default to XMLHttpRequest.
|
1659
|
-
*
|
1660
|
-
* @return {XMLHttpRequest, ActiveXObject}
|
1661
|
-
* @api private
|
1662
|
-
*/
|
1663
|
-
|
1664
|
-
xhr : function() {
|
1665
|
-
return this.ieXhr() || new JSpec.request
|
1666
|
-
},
|
1667
|
-
|
1668
|
-
/**
|
1669
|
-
* Return Microsoft piece of crap ActiveXObject.
|
1670
|
-
*
|
1671
|
-
* @return {ActiveXObject}
|
1672
|
-
* @api public
|
1673
|
-
*/
|
1674
|
-
|
1675
|
-
ieXhr : function() {
|
1676
|
-
function object(str) {
|
1677
|
-
try { return new ActiveXObject(str) } catch(e) {}
|
1678
|
-
}
|
1679
|
-
return object('Msxml2.XMLHTTP.6.0') ||
|
1680
|
-
object('Msxml2.XMLHTTP.3.0') ||
|
1681
|
-
object('Msxml2.XMLHTTP') ||
|
1682
|
-
object('Microsoft.XMLHTTP')
|
1683
|
-
},
|
1684
|
-
|
1685
|
-
/**
|
1686
|
-
* Check for HTTP request support.
|
1687
|
-
*
|
1688
|
-
* @return {bool}
|
1689
|
-
* @api private
|
1690
|
-
*/
|
1691
|
-
|
1692
|
-
hasXhr : function() {
|
1693
|
-
return JSpec.request || 'ActiveXObject' in main
|
1694
|
-
},
|
1695
|
-
|
1696
|
-
/**
|
1697
|
-
* Try loading _file_ returning the contents
|
1698
|
-
* string or null. Chain to locate / read a file.
|
1699
|
-
*
|
1700
|
-
* @param {string} file
|
1701
|
-
* @return {string}
|
1702
|
-
* @api public
|
1703
|
-
*/
|
1704
|
-
|
1705
|
-
tryLoading : function(file) {
|
1706
|
-
try { return JSpec.load(file) } catch (e) {}
|
1707
|
-
},
|
1708
|
-
|
1709
|
-
/**
|
1710
|
-
* Load a _file_'s contents.
|
1711
|
-
*
|
1712
|
-
* @param {string} file
|
1713
|
-
* @param {function} callback
|
1714
|
-
* @return {string}
|
1715
|
-
* @api public
|
1716
|
-
*/
|
1717
|
-
|
1718
|
-
load : function(file, callback) {
|
1719
|
-
if (any(hook('loading', file), haveStopped)) return
|
1720
|
-
if ('readFile' in main)
|
1721
|
-
return readFile(file)
|
1722
|
-
else if (this.hasXhr()) {
|
1723
|
-
var request = this.xhr()
|
1724
|
-
request.open('GET', file, false)
|
1725
|
-
request.send(null)
|
1726
|
-
if (request.readyState == 4 &&
|
1727
|
-
(request.status == 0 ||
|
1728
|
-
request.status.toString().charAt(0) == 2))
|
1729
|
-
return request.responseText
|
1730
|
-
}
|
1731
|
-
else
|
1732
|
-
throw new Error("failed to load `" + file + "'")
|
1733
|
-
},
|
1734
|
-
|
1735
|
-
/**
|
1736
|
-
* Load, pre-process, and evaluate a file.
|
1737
|
-
*
|
1738
|
-
* @param {string} file
|
1739
|
-
* @param {JSpec}
|
1740
|
-
* @api public
|
1741
|
-
*/
|
1742
|
-
|
1743
|
-
exec : function(file) {
|
1744
|
-
if (any(hook('executing', file), haveStopped)) return this
|
1745
|
-
eval('with (JSpec){' + this.preprocess(this.load(file)) + '}')
|
1746
|
-
return this
|
1747
|
-
}
|
1748
|
-
}
|
1749
|
-
|
1750
|
-
// --- Node.js support
|
1751
|
-
|
1752
|
-
if (typeof GLOBAL === 'object' && typeof exports === 'object')
|
1753
|
-
quit = process.exit,
|
1754
|
-
print = require('sys').puts,
|
1755
|
-
readFile = require('fs').readFileSync
|
1756
|
-
|
1757
|
-
// --- Utility functions
|
1758
|
-
|
1759
|
-
var main = this,
|
1760
|
-
find = JSpec.any,
|
1761
|
-
utils = 'haveStopped stub hookImmutable hook destub map any last pass fail range each option inject select \
|
1762
|
-
error escape extend puts query strip color does addMatchers callIterator toArray equal'.split(/\s+/)
|
1763
|
-
while (utils.length) eval('var ' + utils[0] + ' = JSpec.' + utils.shift())
|
1764
|
-
if (!main.setTimeout) main.setTimeout = function(callback){ callback() }
|
1765
|
-
|
1766
|
-
// --- Matchers
|
1767
|
-
|
1768
|
-
addMatchers({
|
1769
|
-
equal : "===",
|
1770
|
-
eql : "equal(actual, expected)",
|
1771
|
-
be : "alias equal",
|
1772
|
-
be_greater_than : ">",
|
1773
|
-
be_less_than : "<",
|
1774
|
-
be_at_least : ">=",
|
1775
|
-
be_at_most : "<=",
|
1776
|
-
be_a : "actual.constructor == expected",
|
1777
|
-
be_an : "alias be_a",
|
1778
|
-
be_an_instance_of : "actual instanceof expected",
|
1779
|
-
be_null : "actual == null",
|
1780
|
-
be_true : "actual == true",
|
1781
|
-
be_false : "actual == false",
|
1782
|
-
be_undefined : "typeof actual == 'undefined'",
|
1783
|
-
be_type : "typeof actual == expected",
|
1784
|
-
match : "typeof actual == 'string' ? actual.match(expected) : false",
|
1785
|
-
respond_to : "typeof actual[expected] == 'function'",
|
1786
|
-
have_length : "actual.length == expected",
|
1787
|
-
be_within : "actual >= expected[0] && actual <= last(expected)",
|
1788
|
-
have_length_within : "actual.length >= expected[0] && actual.length <= last(expected)",
|
1789
|
-
|
1790
|
-
receive : { defer : true, match : function(actual, method, times) {
|
1791
|
-
var proxy = new JSpec.ProxyAssertion(actual, method, times, this.negate)
|
1792
|
-
JSpec.currentSpec.assertions.push(proxy)
|
1793
|
-
return proxy
|
1794
|
-
}},
|
1795
|
-
|
1796
|
-
be_empty : function(actual) {
|
1797
|
-
if (actual.constructor == Object && actual.length == undefined)
|
1798
|
-
for (var key in actual)
|
1799
|
-
return false;
|
1800
|
-
return !actual.length
|
1801
|
-
},
|
1802
|
-
|
1803
|
-
include : function(actual) {
|
1804
|
-
for (var state = true, i = 1; i < arguments.length; i++) {
|
1805
|
-
var arg = arguments[i]
|
1806
|
-
switch (actual.constructor) {
|
1807
|
-
case String:
|
1808
|
-
case Number:
|
1809
|
-
case RegExp:
|
1810
|
-
case Function:
|
1811
|
-
state = actual.toString().indexOf(arg) !== -1
|
1812
|
-
break
|
1813
|
-
|
1814
|
-
case Object:
|
1815
|
-
state = arg in actual
|
1816
|
-
break
|
1817
|
-
|
1818
|
-
case Array:
|
1819
|
-
state = any(actual, function(value){ return equal(value, arg) })
|
1820
|
-
break
|
1821
|
-
}
|
1822
|
-
if (!state) return false
|
1823
|
-
}
|
1824
|
-
return true
|
1825
|
-
},
|
1826
|
-
|
1827
|
-
throw_error : { match : function(actual, expected, message) {
|
1828
|
-
try { actual() }
|
1829
|
-
catch (e) {
|
1830
|
-
this.e = e
|
1831
|
-
var assert = function(arg) {
|
1832
|
-
switch (arg.constructor) {
|
1833
|
-
case RegExp : return arg.test(e.message || e.toString())
|
1834
|
-
case String : return arg == (e.message || e.toString())
|
1835
|
-
case Function : return e instanceof arg || e.name == arg.name
|
1836
|
-
}
|
1837
|
-
}
|
1838
|
-
return message ? assert(expected) && assert(message) :
|
1839
|
-
expected ? assert(expected) :
|
1840
|
-
true
|
1841
|
-
}
|
1842
|
-
}, message : function(actual, expected, negate) {
|
1843
|
-
// TODO: refactor when actual is not in expected [0]
|
1844
|
-
var message_for = function(i) {
|
1845
|
-
if (expected[i] == undefined) return 'exception'
|
1846
|
-
switch (expected[i].constructor) {
|
1847
|
-
case RegExp : return 'exception matching ' + puts(expected[i])
|
1848
|
-
case String : return 'exception of ' + puts(expected[i])
|
1849
|
-
case Function : return expected[i].name || 'Error'
|
1850
|
-
}
|
1851
|
-
}
|
1852
|
-
var exception = message_for(1) + (expected[2] ? ' and ' + message_for(2) : '')
|
1853
|
-
return 'expected ' + exception + (negate ? ' not ' : '' ) +
|
1854
|
-
' to be thrown, but ' + (this.e ? 'got ' + puts(this.e) : 'nothing was')
|
1855
|
-
}},
|
1856
|
-
|
1857
|
-
have : function(actual, length, property) {
|
1858
|
-
return actual[property] == null ? false : actual[property].length == length
|
1859
|
-
},
|
1860
|
-
|
1861
|
-
have_at_least : function(actual, length, property) {
|
1862
|
-
return actual[property] == null ? (length === 0) : actual[property].length >= length
|
1863
|
-
},
|
1864
|
-
|
1865
|
-
have_at_most :function(actual, length, property) {
|
1866
|
-
return actual[property] == null || actual[property].length <= length
|
1867
|
-
},
|
1868
|
-
|
1869
|
-
have_within : function(actual, range, property) {
|
1870
|
-
var length = actual[property] == undefined ? 0 : actual[property].length
|
1871
|
-
return length >= range.shift() && length <= range.pop()
|
1872
|
-
},
|
1873
|
-
|
1874
|
-
have_prop : function(actual, property, value) {
|
1875
|
-
var actualVal = actual[property], actualType = typeof actualVal
|
1876
|
-
return (actualType == 'function' || actualType == 'undefined') ? false :
|
1877
|
-
typeof value === 'undefined' ||
|
1878
|
-
does(actual[property],'eql',value)
|
1879
|
-
},
|
1880
|
-
|
1881
|
-
have_property : function(actual, property, value) {
|
1882
|
-
var actualVal = actual[property], actualType = typeof actualVal
|
1883
|
-
return (actualType == 'function' || actualType == 'undefined') ? false :
|
1884
|
-
typeof value === 'undefined' ||
|
1885
|
-
value === actualVal
|
1886
|
-
}
|
1887
|
-
})
|
1888
|
-
|
1889
|
-
})()
|