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.
File without changes
@@ -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, '&amp;')
1109
- .replace(/"/gmi, '&quot;')
1110
- .replace(/>/gmi, '&gt;')
1111
- .replace(/</gmi, '&lt;')
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
- })()