Skip to content

Commit 2d7c2c0

Browse files
author
Amir Tocker
committed
Add test helper functions
* `jsonArrayParam` * `uploadParamMatcher` * `apiParamMatcher` * `mockTest` * `mockPromise`
1 parent 72f33d3 commit 2d7c2c0

File tree

2 files changed

+116
-6
lines changed

2 files changed

+116
-6
lines changed

src/utils.coffee

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,6 @@ build_eager = (eager)->
10271027
).join("|")
10281028
ret
10291029

1030-
10311030
hashToQuery = (hash)->
10321031
compact(for key, value of hash
10331032
if isArray(value)
@@ -1039,3 +1038,17 @@ hashToQuery = (hash)->
10391038
"#{querystring.escape(key)}=#{querystring.escape(value)}"
10401039
).sort().join('&')
10411040

1041+
###*
1042+
# Returns a JSON array as String.
1043+
# Yields the array before it is converted to JSON format
1044+
# @api private
1045+
# @param [Hash|String|Array<Hash>] data
1046+
# @return [String|nil] a JSON array string or `nil` if data is `nil`
1047+
###
1048+
jsonArrayParam = (data, callback)->
1049+
return unless data
1050+
1051+
data = JSON.parse(data) if isString(data)
1052+
data = [data] unless isArray(data)
1053+
data = callback(data) if isFunction(callback)
1054+
JSON.stringify(data)

test/spechelper.coffee

Lines changed: 102 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ utils = require("../lib/utils")
88
isFunction = require('lodash/isFunction')
99
cloneDeep = require('lodash/cloneDeep')
1010
http = require('http')
11+
querystring = require('querystring')
12+
sinon = require('sinon')
13+
ClientRequest = require('_http_client').ClientRequest
14+
Q = require('q')
1115

1216
exports.TIMEOUT_SHORT = 5000
1317
exports.TIMEOUT_MEDIUM = 20000
@@ -33,7 +37,7 @@ exports.test_cloudinary_url = (public_id,options,expected_url,expected_options)
3337

3438
expect.Assertion::produceUrl = (url)->
3539
[public_id, options] = @obj
36-
actualOptions = _.cloneDeep(options)
40+
actualOptions = cloneDeep(options)
3741
actual = utils.url(public_id, actualOptions)
3842
@assert(
3943
actual.match(url),
@@ -43,17 +47,17 @@ expect.Assertion::produceUrl = (url)->
4347

4448
expect.Assertion::emptyOptions = ()->
4549
[public_id, options] = @obj
46-
actual = _.cloneDeep(options)
50+
actual = cloneDeep(options)
4751
utils.url(public_id,actual)
4852
@assert(
49-
_.isEmpty(actual),
53+
isEmpty(actual),
5054
()-> "expected '#{public_id}' and #{JSON.stringify(options)} to produce empty options but got #{JSON.stringify(actual)}",
5155
()-> "expected '#{public_id}' and #{JSON.stringify(options)} not to produce empty options")
5256
@
5357

5458
expect.Assertion::beServedByCloudinary = (done)->
5559
[public_id, options] = @obj
56-
actualOptions = _.cloneDeep(options)
60+
actualOptions = cloneDeep(options)
5761
actual = utils.url(public_id, actualOptions)
5862
http.get actual, (res)=>
5963
@assert res.statusCode == 200,
@@ -65,7 +69,7 @@ expect.Assertion::beServedByCloudinary = (done)->
6569
class sharedExamples
6670
constructor: (name, examples)->
6771
@allExamples ?= {}
68-
if _.isFunction(examples)
72+
if isFunction(examples)
6973
@allExamples[name] = examples
7074
examples
7175
else
@@ -82,3 +86,96 @@ exports.itBehavesLike = (name, args...)->
8286
sharedExamples(name).apply(this, args)
8387
exports.includeContext = (name, args...)->
8488
sharedExamples(name).apply(this, args)
89+
90+
###*
91+
Create a matcher method for upload parameters
92+
@private
93+
@function helper.paramMatcher
94+
@param {string} name the parameter name
95+
@param value {Any} the parameter value
96+
@return {(arg)->Boolean} the matcher function
97+
###
98+
exports.uploadParamMatcher = (name, value)->
99+
(arg)->
100+
EncodeFieldPart = (name, value) ->
101+
return_part = 'Content-Disposition: form-data; name="' + name+ '"\r\n\r\n'
102+
return_part += value
103+
new RegExp(return_part).test(arg)
104+
105+
###*
106+
Create a matcher method for api parameters
107+
@private
108+
@function helper.apiParamMatcher
109+
@param {string} name the parameter name
110+
@param value {Any} the parameter value
111+
@return {(arg)->Boolean} the matcher function
112+
###
113+
exports.apiParamMatcher = (name, value)->
114+
params = {}
115+
params[name] = value
116+
expected = querystring.stringify(params)
117+
(arg)->
118+
new RegExp(expected).test(arg)
119+
120+
###*
121+
Escape RegExp characters
122+
@private
123+
@param {string} s the string to escape
124+
@return a new escaped string
125+
###
126+
exports.escapeRegexp = (s)->
127+
s.replace(/[{\[\].*+()}]/g, (c)=>'\\' + c)
128+
129+
###*
130+
@function mockTest
131+
@nodoc
132+
provides a wrapper for mocked tests
133+
@return {object} the mocked objects
134+
###
135+
exports.mockTest = ()->
136+
mock = {}
137+
before ()->
138+
mock.xhr = sinon.useFakeXMLHttpRequest()
139+
mock.writeSpy = sinon.spy(ClientRequest.prototype, 'write')
140+
mock.requestSpy = sinon.spy(http, 'request')
141+
142+
after ()->
143+
mock.requestSpy.restore()
144+
mock.writeSpy.restore()
145+
mock.xhr.restore()
146+
147+
mock
148+
149+
###*
150+
@callback mockBlock
151+
A test block
152+
@param xhr
153+
@param writeSpy
154+
@param requestSpy
155+
@return {*} a promise or a value
156+
###
157+
158+
###*
159+
@function mockPromise
160+
Wraps the test to be mocked using a promise
161+
@param {mockBlock} the test function
162+
###
163+
exports.mockPromise = (mockBlock)->
164+
xhr = undefined
165+
writeSpy = undefined
166+
requestSpy = undefined
167+
Q.Promise((resolve, reject, notify)->
168+
xhr = sinon.useFakeXMLHttpRequest()
169+
writeSpy = sinon.spy(ClientRequest.prototype, 'write')
170+
requestSpy = sinon.spy(http, 'request')
171+
mock = {xhr, writeSpy, requestSpy }
172+
result = mockBlock(xhr, writeSpy, requestSpy)
173+
if isFunction(result?.then)
174+
result.then(resolve)
175+
else
176+
resolve(result)
177+
).finally(()->
178+
requestSpy.restore()
179+
writeSpy.restore()
180+
xhr.restore()
181+
)

0 commit comments

Comments
 (0)