1 | _ = require 'underscore'
|
2 |
|
3 | isFunction = (obj) ->
|
4 | typeof obj == 'function'
|
5 |
|
6 | createThroughSpy = (getValue, bondApi) ->
|
7 | spy = ->
|
8 | args = Array::slice.call(arguments)
|
9 | spy.calledArgs[spy.called] = args
|
10 | spy.called++
|
11 |
|
12 | result = getValue.apply(this, args)
|
13 |
|
14 | isConstructor = (this instanceof arguments.callee)
|
15 | return this if isConstructor and typeof result != 'object'
|
16 | result
|
17 |
|
18 | enhanceSpy(spy, getValue, bondApi)
|
19 |
|
20 | createReturnSpy = (getValue, bondApi) ->
|
21 | spy = ->
|
22 | args = Array::slice.call(arguments)
|
23 | spy.calledArgs[spy.called] = args
|
24 | spy.called++
|
25 |
|
26 | getValue.apply(this, args)
|
27 |
|
28 | enhanceSpy(spy, getValue, bondApi)
|
29 |
|
30 | createAnonymousSpy = ->
|
31 | returnValue = null
|
32 |
|
33 | spy = ->
|
34 | args = Array::slice.call(arguments)
|
35 | spy.calledArgs[spy.called] = args
|
36 | spy.called++
|
37 |
|
38 | returnValue
|
39 |
|
40 | enhanceSpy(spy)
|
41 |
|
42 | spy.return = (newReturnValue) ->
|
43 | returnValue = newReturnValue
|
44 | spy
|
45 | spy
|
46 |
|
47 | enhanceSpy = (spy, original, bondApi) ->
|
48 | spy.prototype = original?.prototype
|
49 | spy.called = 0
|
50 | spy.calledArgs = []
|
51 | spy.calledWith = (args...) ->
|
52 | for calledArgs in spy.calledArgs
|
53 | return true if _.isEqual(args, calledArgs)
|
54 | false
|
55 |
|
56 | spy[k] = v for k, v of bondApi if bondApi
|
57 |
|
58 | spy
|
59 |
|
60 | nextTick = do ->
|
61 | return process.nextTick if isFunction(process?.nextTick)
|
62 | return setImmediate if setImmediate? && isFunction(setImmediate)
|
63 |
|
64 | (fn) ->
|
65 | setTimeout(fn, 0)
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 | _registry = []
|
72 | _find = (obj) ->
|
73 | for store in _registry
|
74 | if store.obj == obj
|
75 | return store
|
76 |
|
77 | store =
|
78 | obj: obj
|
79 | props: {}
|
80 | _registry.push(store)
|
81 | store
|
82 |
|
83 | registry =
|
84 | set: (obj, prop, value, newValue) ->
|
85 | store = _find(obj)
|
86 |
|
87 |
|
88 | if !store.props[prop]?
|
89 | store.props[prop] = value
|
90 |
|
91 | get: (obj, prop) ->
|
92 | _find(obj).props[prop]
|
93 |
|
94 | restore: (obj, prop) ->
|
95 | obj[prop] = _find(obj).props[prop]
|
96 |
|
97 | restoreAll: ->
|
98 | for store in _registry
|
99 | for prop, value of store.props
|
100 | store.obj[prop] = value
|
101 |
|
102 | _registry = []
|
103 |
|
104 |
|
105 |
|
106 | allStubs = []
|
107 | registered = false
|
108 | registerCleanupHook = ->
|
109 | return if registered
|
110 |
|
111 | after = afterEach ? testDone ? QUnit?.testDone ? bond.cleanup ? ->
|
112 | throw new Error('bond.cleanup must be specified if your test runner does not use afterEach or testDone')
|
113 |
|
114 | after ->
|
115 | registry.restoreAll()
|
116 |
|
117 | registered = true
|
118 |
|
119 | bond = (obj, property) ->
|
120 | registerCleanupHook()
|
121 | return createAnonymousSpy() if arguments.length == 0
|
122 |
|
123 | previous = obj[property]
|
124 |
|
125 | registerRestore = ->
|
126 | registry.set obj, property, previous
|
127 |
|
128 | restore = ->
|
129 | registry.restore(obj, property)
|
130 |
|
131 | to = (newValue) ->
|
132 | registerRestore()
|
133 |
|
134 | if isFunction(newValue)
|
135 | newValue = createThroughSpy(newValue, this)
|
136 |
|
137 | obj[property] = newValue
|
138 | obj[property]
|
139 |
|
140 | returnMethod = (returnValue) ->
|
141 | registerRestore()
|
142 | returnValueFn = -> returnValue
|
143 | obj[property] = createReturnSpy(returnValueFn, this)
|
144 | obj[property]
|
145 |
|
146 | asyncReturn = (returnValues...) ->
|
147 | to (args..., callback) ->
|
148 | if !isFunction(callback)
|
149 | throw new Error('asyncReturn expects last argument to be a function')
|
150 |
|
151 | nextTick ->
|
152 | callback(returnValues...)
|
153 |
|
154 | through = ->
|
155 | registerRestore()
|
156 | obj[property] = createThroughSpy(previous, this)
|
157 | obj[property]
|
158 |
|
159 | {
|
160 | 'to': to
|
161 | 'return': returnMethod
|
162 | 'asyncReturn': asyncReturn
|
163 | 'through': through
|
164 | 'restore': restore
|
165 | }
|
166 |
|
167 | module.exports = bond
|