gusto 1.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e33005d29c816fd1b04d2272759ed17e03d13931
4
+ data.tar.gz: 2142ceb53d542831d3044edece5c5d106db6d469
5
+ SHA512:
6
+ metadata.gz: 880e94182f4961bf38608dc257b44e0483622eb877469222575bd179a4476e24d7c7679862ab09a1485b4883ecb048e5b27b16a399e9ed30fb2ed91ae6786a17
7
+ data.tar.gz: ad17587ff01547549a8c6a983c3331745703300857334c79169a6ffde81b93ba3c5eb0033a03259c238990c0bc92b4cc260934c23cba62d821bcc31003fda325
data/bin/gusto ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require 'optparse'
5
+
6
+ options = {}
7
+ parser = OptionParser.new do |o|
8
+ o.banner = "Usage: #{$0} [options] mode\nPossible modes: server, cli, auto"
9
+
10
+ options[:port] = nil
11
+ o.on '-p', '--port PORT', 'Override server port' do |port|
12
+ options[:port] = port
13
+ end
14
+
15
+ options[:version] = false
16
+ o.on '-v', '--version', 'Show version' do
17
+ options[:version] = true
18
+ end
19
+
20
+ o.on_tail '-h', '--help', 'Display this screen' do
21
+ puts o
22
+ exit
23
+ end
24
+ end
25
+ parser.parse!
26
+
27
+ require "#{File.dirname(__FILE__)}/../lib/gusto/runner.rb"
28
+ Gusto::Runner.new ARGV.first, options, parser
@@ -0,0 +1,63 @@
1
+ class window.HtmlReport
2
+ constructor: (element) ->
3
+ @element = element
4
+
5
+ run: ->
6
+ root = new Spec.Suite()
7
+ for suite in Spec.suites
8
+ root.add suite
9
+
10
+ @report = root.run([])
11
+ @element.innerHTML = @html()
12
+
13
+ html: ->
14
+ @resultSummary(@report) + @testResults(@report)
15
+
16
+ resultSummary: (report) ->
17
+ "
18
+ <div class=\"result-summary\">
19
+ #{@resultSummaryCount 'total', report.counts[0] + report.counts[1] + report.counts[2]}
20
+ #{@resultSummaryCount 'passed', report.counts[0]}
21
+ #{@resultSummaryCount 'pending', report.counts[1]}
22
+ #{@resultSummaryCount 'failed', report.counts[2]}
23
+ </div>
24
+ "
25
+
26
+ resultSummaryCount: (name, count) ->
27
+ "
28
+ <div class=\"result-summary--count result-summary--#{name}\">
29
+ <span class=\"result-summary--label\">#{name.toUpperCase()}</span>
30
+ <span class=\"result-summary--number\">#{count}</span>
31
+ </div>
32
+ "
33
+
34
+ testResults: (report) ->
35
+ "
36
+ <div class=\"test-results\">
37
+ #{@testResultsReports report.subreports}
38
+ </div>
39
+ "
40
+
41
+ testResultsReports: (reports) ->
42
+ html = '<ul class=\"test-results--list\">'
43
+ for report in reports
44
+ html += @testResultsReport report
45
+ html + '</ul>'
46
+
47
+ testResultsReport: (report) ->
48
+ "
49
+ <li class=\"test-results--test test-results--test--#{@testResultsStatusClass report.status}\">
50
+ <div class=\"test-results--title\">#{report.title}</div>
51
+ #{if report.error then @testResultsErrorReport(report) else ''}
52
+ #{if report.subreports.length then @testResultsReports(report.subreports) else ''}
53
+ </li>
54
+ "
55
+
56
+ testResultsErrorReport: (report) ->
57
+ "<div class=\"test-results--error-message\">#{report.error}</div>"
58
+
59
+ testResultsStatusClass: (status) ->
60
+ switch status
61
+ when Spec.Report.Passed then 'passed'
62
+ when Spec.Report.Pending then 'pending'
63
+ when Spec.Report.Failed then 'failed'
@@ -0,0 +1,119 @@
1
+ window.Spec ||= {}
2
+
3
+ window.Spec.DSL = DSL =
4
+ # Prepares a sub-test of the current test case
5
+ describe: (title, definition) ->
6
+ @__spec_definingSuite.add new Spec.Suite(title, definition)
7
+
8
+ # Adds a setup step to the current test case
9
+ before: (action) ->
10
+ @__spec_definingSuite.filter null, action
11
+
12
+ # Allows an assertion on a non-object value
13
+ expect: (object) ->
14
+ to: (matcher) ->
15
+ result = matcher(object)
16
+ throw new Spec.ExpectationError("expected #{result[1]}") unless result[0]
17
+ notTo: (matcher) ->
18
+ result = matcher(object)
19
+ throw new Spec.ExpectationError("expected not #{result[1]}") if result[0]
20
+
21
+ # Syntactic sugar to create a before method that prepares a variable
22
+ #
23
+ # Example:
24
+ # given 'dog', -> new Dog()
25
+ given: (name, definition) ->
26
+ @__spec_definingSuite.filter name, -> @[name] = definition.call this
27
+
28
+ # Creates a specificaition
29
+ it: (args...) ->
30
+ test = switch args.length
31
+ when 1
32
+ if typeof args[0] == 'function'
33
+ # Test with automatically generated title
34
+ new Spec.Test(Spec.Util.descriptionize(args[0]), args[0])
35
+ else
36
+ # Pending test
37
+ new Spec.Test(args[0], -> pending() )
38
+ when 2
39
+ # Test with manual title
40
+ new Spec.Test(args...)
41
+ @__spec_definingSuite.add test if test
42
+
43
+ pending: (message=null) ->
44
+ throw new Spec.PendingError(message)
45
+
46
+ # Creates a specification that tests an attribute of subject
47
+ #
48
+ # Example:
49
+ # subject -> new Employee('Fred')
50
+ # its 'name', -> should equal('Fred')
51
+ its: (attribute, definition) ->
52
+ root = this
53
+ it "#{attribute} #{Spec.Util.descriptionize definition}", ->
54
+ root.__spec_subject = @subject = Spec.Util.dereference @subject[attribute]
55
+ definition.call this
56
+
57
+ # Runs a test against @subject
58
+ #
59
+ # Example
60
+ # subject -> new Employee()
61
+ # it -> should beAnInstanceOf(Employee)
62
+ should: (matcher) ->
63
+ expect(@__spec_subject).to matcher
64
+
65
+ # Runs a negative test against @subject
66
+ #
67
+ # Example
68
+ # subject -> new Employee()
69
+ # it -> shouldNot be(null)
70
+ shouldNot: (matcher) ->
71
+ expect(@__spec_subject).notTo matcher
72
+
73
+ # Creates a new mock object
74
+ #
75
+ # Pass in a hash of method stubs to add to your mock.
76
+ #
77
+ # `mock(boots: 'cats')` gives an object that has the method:
78
+ # `boots: -> 'cats'`
79
+ #
80
+ # Optionally, you can pass a name to identify this mock as the
81
+ # first parameter.
82
+ #
83
+ mock: (args...) ->
84
+ name = args.shift() if typeof args[0] is 'string'
85
+ stubs = args.pop() || {}
86
+ new Spec.MockObject(name, stubs)
87
+
88
+ # Defines the subject of your test.
89
+ #
90
+ # Pass in a definition method which returns an object to be your
91
+ # test subject. This will be assigined to the instance variable
92
+ # @subject before your test runs. This subject will be used for
93
+ # any `should` or `shouldNot` tests you specify using the global
94
+ # `should` and `shouldNot` methods.
95
+ #
96
+ # subject -> new Client()
97
+ #
98
+ # it { should beA Client }
99
+ #
100
+ # Optionally, you can specify a name for your subject as the
101
+ # first parameter. This lets you call the subject by its name,
102
+ # making your tests more readable.
103
+ #
104
+ # subject 'foo', -> ...
105
+ #
106
+ # it 'gets prepared' ->
107
+ # @foo.prepare
108
+ # should 'bePrepared'
109
+ #
110
+ subject: (args...) ->
111
+ root = this
112
+ definition = args.pop()
113
+ name = args.pop()
114
+ before ->
115
+ root.__spec_subject = @subject = definition.call this
116
+ @[name] = @subject if name
117
+
118
+ DSL.context = DSL.describe
119
+ DSL.specify = DSL.it
@@ -0,0 +1,61 @@
1
+ window.Spec ||= {}
2
+
3
+ # A delayed expectation keeps track of an event that is expected to
4
+ # occur during the course of a test.
5
+ #
6
+ # To set a delayed expectation use window.expectation
7
+ class window.Spec.DelayedExpectation
8
+ @expectations: []
9
+
10
+ @add: (message) ->
11
+ exp = new Spec.DelayedExpectation(message)
12
+ @expectations.push exp
13
+ exp
14
+
15
+ @assert: ->
16
+ asserting = @expectations
17
+ @expectations = []
18
+ for expectation in asserting
19
+ expectation.assert()
20
+
21
+ @reset: ->
22
+ @expectations = []
23
+
24
+ constructor: (@message) ->
25
+ @met = 0
26
+ @desired = 1
27
+
28
+ # Specifies that this expectation must be met twice to count
29
+ # as a success.
30
+ twice: ->
31
+ @desired = 2
32
+ this
33
+
34
+ # Specifies how many times this expectation should be run to
35
+ # count as a success.
36
+ #
37
+ # Always use this with the format `.exactly(x).times` for better
38
+ # readability.
39
+ exactly: (count) ->
40
+ @desired = count
41
+ {times: this}
42
+
43
+ # Call when this expectation has been met.
44
+ meet: ->
45
+ @met += 1
46
+
47
+ # Raises an error unless this expecation has been met the right number of times.
48
+ assert: ->
49
+ unless @met is @desired
50
+ throw new Spec.ExpectationError("expected to #{@message} #{@_timesString @desired}, actually happened #{@_timesString @met}")
51
+
52
+ _timesString: (times) ->
53
+ switch times
54
+ when 0
55
+ 'not at all'
56
+ when 1
57
+ 'once'
58
+ when 2
59
+ 'twice'
60
+ else
61
+ "#{times} times"
@@ -0,0 +1,69 @@
1
+ window.Spec ||= {}
2
+
3
+ window.Spec.Matchers =
4
+ # Tests if matched value === expected value
5
+ be: (expected) ->
6
+ (value) ->
7
+ [value is expected, "to be #{Spec.Util.inspect expected}, actual #{Spec.Util.inspect value}"]
8
+
9
+ # Tests that value type matches specified class
10
+ beA: (klass) ->
11
+ switch klass
12
+ when Boolean then _haveType 'boolean'
13
+ when Function then _haveType 'function'
14
+ when Number then _haveType 'number'
15
+ when String then _haveType 'string'
16
+ when Object then _haveType 'object'
17
+ else _beAnInstanceOf klass
18
+
19
+ # Tests if matched value == expected value
20
+ equal: (expected) ->
21
+ (value) ->
22
+ [String(value) == String(expected), "“#{String value}” to equal “#{String expected}” – #{$.trim diffString(String(value), String(expected))}"]
23
+
24
+ # All-purpose inclusion matcher
25
+ include: (expected) ->
26
+ if expected instanceof Array
27
+ (value) ->
28
+ match = true
29
+ for test in expected
30
+ match = false unless (value.indexOf && value.indexOf(test) >= 0) || value[test]?
31
+ [match, "to include #{Spec.Util.inspect expected}, actual #{Spec.Util.inspect value}"]
32
+ else if typeof expected == 'object'
33
+ (value) ->
34
+ missing = {}
35
+ match = true
36
+ for test of expected
37
+ if expected.hasOwnProperty test
38
+ unless value[test] isnt undefined && String(value[test]) == String(expected[test])
39
+ match = false
40
+ missing[test] = expected[test]
41
+ [match, "to include #{Spec.Util.inspect expected}, actual #{Spec.Util.inspect value}, missing #{Spec.Util.inspect missing}"]
42
+ else
43
+ include([expected])
44
+
45
+ # Tests if a function causes an error to be thrown when called
46
+ throwError: (message) ->
47
+ (fn) ->
48
+ thrown = false
49
+ try
50
+ fn()
51
+ catch e
52
+ thrown = e.message
53
+ finally
54
+ if thrown
55
+ return [thrown == message, "to throw an error with message “#{String thrown}”, actual message “#{String message}” – #{$.trim diffString(String(thrown), String(message))}"]
56
+ else
57
+ return [false, "to throw an error with message “#{message}”, no error thrown"]
58
+
59
+ # Tests a value type using typeof
60
+ _haveType: (type) ->
61
+ (value) ->
62
+ [typeof value is type, "to have type “#{type}”, actual “#{typeof value}”"]
63
+
64
+ # Tests if matched value is an instance of class
65
+ _beAnInstanceOf: (klass) ->
66
+ (value) ->
67
+ [value instanceof klass, "to be an instance of “#{klass}”"]
68
+
69
+ window.Spec.Matchers.beAn = window.Spec.Matchers.beA
@@ -0,0 +1,81 @@
1
+ # Represents a possible call path for a MethodStub - that is a combination
2
+ # of expected arguments and return values.
3
+ class window.Spec.MethodStub.PossibleCall
4
+ constructor: (@original) ->
5
+
6
+ # Defines the expected arguments for this PossibleCall. By default a
7
+ # PossibleCall will accept any or no arguments. When you specify arguments
8
+ # here, the PossibleCall will respond only to those arguments, and a test
9
+ # failure will be recorded if it is called with incorrect arguments.
10
+ with: (args...) ->
11
+ @arguments = args
12
+ this
13
+
14
+ # Provides a return value for this PossibleCall
15
+ andReturn: (value) ->
16
+ @return = Spec.Util.reference(value)
17
+ this
18
+
19
+ # Causes this PossibleCall to pass through to the original method on the
20
+ # object before it was stubbed out.
21
+ andPassthrough: ->
22
+ @return = @original
23
+
24
+ # Sets an expectation that this PossibleCall be called as part of the test.
25
+ #
26
+ # This is used by window#shouldReceive, and doesn't need to be called
27
+ # directly.
28
+ expect: ->
29
+ # TODO: Make this description better, possibly with name of
30
+ # the method that should have been called
31
+ @expectation ||= Spec.DelayedExpectation.add('get called')
32
+ this
33
+
34
+ # Delegates to expectation
35
+ twice: ->
36
+ @expectation.twice()
37
+ this
38
+
39
+ # Delegates to expectation
40
+ exactly: (times) ->
41
+ @expectation.exactly times
42
+ {times: this}
43
+
44
+ # Checks if this PossibleCall matches the given array of arguments.
45
+ #
46
+ # A match is defined as having the same number of arguments, and each
47
+ # argument having an equal string representation to its counterpart.
48
+ matchesArguments: (args) ->
49
+ if !@arguments
50
+ true
51
+ else if @arguments.length isnt args.length
52
+ false
53
+ else
54
+ @_arraysMatch @arguments, args
55
+
56
+ # Causes this PossibleCall to fulfil a call on the stubbed method. Fails
57
+ # the test if the called arguments don't match expected arguments.
58
+ call: (object, method, args) ->
59
+ if @matchesArguments(args)
60
+ @expectation.meet() if @expectation
61
+ @return.apply object, args if @return
62
+ else
63
+ @_failOnInvalidArguments method, args
64
+ null
65
+
66
+ _arraysMatch: (a, b) ->
67
+ for i in [0..a.length]
68
+ return false if String(a[i]) isnt String(b[i])
69
+ true
70
+
71
+ _failOnInvalidArguments: (method, args) ->
72
+ throw new Spec.ExpectationError(
73
+ "expected ##{method} to be called#{@_argumentsString()}, " +
74
+ "actual arguments: “#{args.join ', '}”"
75
+ )
76
+
77
+ _argumentsString: ->
78
+ if @arguments
79
+ " with arguments “#{@arguments.join ', '}”"
80
+ else
81
+ ''
@@ -0,0 +1,55 @@
1
+ window.Spec ||= {}
2
+
3
+ # Replaces a method on an object with a stub method, a fake method which can
4
+ # be configured to return predetermined responses, as well as set
5
+ # expectations on how it is called.
6
+ #
7
+ # These are created using window#stub, window#shouldReceive and
8
+ # window#shouldNotReceive. You shouldn't need to create one manually.
9
+ class window.Spec.MethodStub
10
+ @stubs: []
11
+
12
+ @reset: ->
13
+ while stub = @stubs.pop()
14
+ stub.remove()
15
+
16
+ constructor: (@object, @method) ->
17
+ @possibleCalls = []
18
+ @_replaceMethodOnObject()
19
+ Spec.MethodStub.stubs.push this
20
+
21
+ # Makes a new PossibleCall and adds it to the list
22
+ possibleCall: ->
23
+ call = new Spec.MethodStub.PossibleCall(@original)
24
+ @possibleCalls.unshift call
25
+ call
26
+
27
+ remove: ->
28
+ @object[@method] = @original
29
+
30
+ # Generates a new stub method to inject into the object, and sets the
31
+ # _stub property on it to point back to this MethodStub.
32
+ #
33
+ # _stub is used by window#stub to find an existing MethodStub for a method
34
+ # and add more possible calls to it, instead of writing over it with
35
+ # a new MethodStub.
36
+ #
37
+ # The code inside the stub method is the same for each MethodStub, but
38
+ # we create a fresh copy of it so we can assign a unique _stub property.
39
+ _stubMethod: ->
40
+ method = @method
41
+ stubMethod = (args...) ->
42
+ if call = arguments.callee._stub._findPossibleCall(args)
43
+ call.call this, method, args
44
+
45
+ stubMethod._stub = this
46
+ stubMethod
47
+
48
+ _findPossibleCall: (args) ->
49
+ for call in @possibleCalls
50
+ break if call.matchesArguments(arguments)
51
+ call
52
+
53
+ _replaceMethodOnObject: ->
54
+ @original = @object[@method]
55
+ @object[@method] = @_stubMethod()
@@ -0,0 +1,6 @@
1
+ window.Spec ||= {}
2
+
3
+ class window.Spec.MockObject
4
+ constructor: (@name, stubs) ->
5
+ for name, value of stubs
6
+ @stub(name).andReturn(value)
@@ -0,0 +1,26 @@
1
+ window.Spec ||= {}
2
+
3
+ window.Spec.ObjectDSL =
4
+ # Stubs a method on object
5
+ stub: (method) ->
6
+ stub = if @[method] && @[method]._stub
7
+ @[method]._stub
8
+ else
9
+ new Spec.MethodStub(this, method)
10
+ stub.possibleCall()
11
+
12
+ # Tests for a positive match
13
+ should: (matcher) ->
14
+ expect(this).to matcher
15
+
16
+ # Tests for a negative match
17
+ shouldNot: (matcher) ->
18
+ expect(this).notTo matcher
19
+
20
+ # Creates a stub method with an expectation
21
+ shouldReceive: (method) ->
22
+ @stub(method).expect()
23
+
24
+ # Creates a stub method, with an expectation of no calls
25
+ shouldNotReceive: (name) ->
26
+ @shouldReceive(name).exactly(0).times
@@ -0,0 +1,23 @@
1
+ class window.Spec.Report
2
+ @Passed: 0
3
+ @Pending: 1
4
+ @Failed: 2
5
+
6
+ constructor: (@title) ->
7
+ @status = Spec.Report.Passed
8
+ @error = null
9
+ @subreports = []
10
+ @counts = [0, 0, 0]
11
+
12
+ result: (result, error=null) ->
13
+ @status = result
14
+ @error = error
15
+ @counts[result]++
16
+
17
+ addSubreport: (subreport) ->
18
+ @subreports.push subreport
19
+ @counts[i] += subreport.counts[i] for i in [0..2]
20
+ @_updateStatus(subreport.status)
21
+
22
+ _updateStatus: (value) ->
23
+ @status = Math.max(@status, value)
@@ -0,0 +1,30 @@
1
+ class window.Spec.Suite
2
+ constructor: (@title, @definition) ->
3
+ @filters = []
4
+ @components = []
5
+ @loaded = false
6
+
7
+ load: ->
8
+ if @definition
9
+ window.__spec_definingSuite = this
10
+ @definition()
11
+
12
+ delete window.__spec_definingSuite
13
+ @loaded = true
14
+
15
+ add: (component) ->
16
+ @components.push component
17
+
18
+ filter: (name, definition) ->
19
+ @filters.push definition
20
+
21
+ run: (filters)->
22
+ @load() unless @loaded
23
+ allFilters = filters.concat(@filters)
24
+ report = new Spec.Report(@title)
25
+ report.status = Spec.Report.Pending unless @components.length
26
+
27
+ for component in @components
28
+ report.addSubreport component.run(allFilters)
29
+
30
+ report
@@ -0,0 +1,21 @@
1
+ class window.Spec.Test
2
+ constructor: (@title, @definition) ->
3
+
4
+ run: (filters) ->
5
+ report = new Spec.Report(@title)
6
+ try
7
+ env = {}
8
+ filter.call(env) for filter in filters
9
+ @definition.call(env)
10
+ Spec.DelayedExpectation.assert()
11
+ report.result Spec.Report.Passed
12
+ catch error
13
+ report.result(
14
+ error.status || Spec.Report.Failed,
15
+ error.message
16
+ )
17
+ report.location = error.fileName + ':' + error.lineNumber
18
+ finally
19
+ Spec.DelayedExpectation.reset()
20
+ Spec.MethodStub.reset()
21
+ report
@@ -0,0 +1,86 @@
1
+ window.Spec ||= {}
2
+
3
+ window.Spec.Util =
4
+ extend: (object, extensions...) ->
5
+ for extension in extensions
6
+ for key, value of extension
7
+ object[key] = value
8
+
9
+ unextend: (object, extensions...) ->
10
+ for extension in extensions
11
+ for key, value of extension
12
+ delete object[key]
13
+
14
+ reference: (value) ->
15
+ if typeof value is 'function'
16
+ value
17
+ else
18
+ -> value
19
+
20
+ dereference: (value, context) ->
21
+ if typeof value is 'function'
22
+ value.call context
23
+ else
24
+ value
25
+
26
+ # Tries to format definition source code as readable test description
27
+ descriptionize: (definition) ->
28
+ # Get function source code
29
+ definition = String definition
30
+
31
+ # Remove function boilerplate from beginning
32
+ definition = definition.replace(/^\s*function\s*\([^\)]*\)\s*\{\s*(return\s*)?/, '')
33
+
34
+ # Remove function boilerplate from end
35
+ definition = definition.replace(/\s*;\s*\}\s*$/, '')
36
+
37
+ # Replace symbols with whitespace
38
+ definition = definition.replace(/[\s\(\)\{\}_\-\.'";]+/g, ' ')
39
+
40
+ # Split camelCased terms into seperate words
41
+ definition = definition.replace(/([a-z])([A-Z])/g, (s, a, b) -> "#{a} #{b.toLowerCase()}")
42
+
43
+ # Replace the word return with "it" (only for functions that are more complex than a simple return)
44
+ definition = definition.replace ' return ', ' it '
45
+
46
+ $.trim definition
47
+
48
+ # Returns an HTML representation of any kind of object
49
+ inspect: (object) ->
50
+ if object instanceof Array
51
+ s = '['
52
+ first = true
53
+ for item in object
54
+ if first
55
+ first = false
56
+ else
57
+ first += ', '
58
+ s += "“#{@escape(String(item))}”"
59
+ s + ']'
60
+ else if object is null
61
+ 'null'
62
+ else if object is undefined
63
+ 'undefined'
64
+ else if object is true
65
+ 'true'
66
+ else if object is false
67
+ 'false'
68
+ else if typeof object == 'object'
69
+ s = "{"
70
+ first = true
71
+ for key of object
72
+ # Access hasOwnProperty through Object.prototype to work around bug
73
+ # in IE6/7/8 when calling hasOwnProperty on a DOM element
74
+ if Object.prototype.hasOwnProperty.call(object, key)
75
+ if first
76
+ first = false
77
+ else
78
+ s += ", "
79
+ s += @escape(key) + ': “' + @escape(String(object[key])) + '”'
80
+ s + "}"
81
+ else
82
+ "“#{@escape(object)}”"
83
+
84
+ # Escapes text for HTML
85
+ escape: (string) ->
86
+ $('<div/>').text(String(string)).html()