gusto 1.0.0.beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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()