UNPKG

10.6 kBtext/coffeescriptView Raw
1# Function::bind polyfill for rewirejs + phantomjs
2unless Function::bind
3 Function::bind = (oThis) ->
4
5 # closest thing possible to the ECMAScript 5
6 # internal IsCallable function
7 throw new TypeError('Function.prototype.bind - what is trying to be bound
8 is not callable') if typeof this isnt 'function'
9 aArgs = Array::slice.call(arguments, 1)
10 fToBind = this
11 fNOP = -> null
12
13 fBound = ->
14 fToBind.apply (if this instanceof fNOP and oThis then this else oThis),
15 aArgs.concat(Array::slice.call(arguments))
16
17 fNOP.prototype = this.prototype
18 fBound:: = new fNOP()
19 fBound
20
21deferredFactory = ->
22 resolve = null
23 reject = null
24 promise = new Promise (_resolve, _reject) ->
25 resolve = _resolve
26 reject = _reject
27 promise.resolve = resolve
28 promise.reject = reject
29
30 return promise
31
32should = require('clay-chai').should()
33Promise = require 'bluebird'
34Promiz = require 'promiz'
35_ = require 'lodash'
36rewire = require 'rewire'
37
38packageConfig = require '../package.json'
39ClayRoot = rewire 'index'
40Clay = ClayRoot.__get__ 'Clay'
41
42TRUSTED_DOMAIN = process.env.TRUSTED_DOMAIN or 'clay.io'
43
44postRoutes = {}
45
46ClayRoot.__set__ 'window.parent.postMessage', (messageString, targetOrigin) ->
47 targetOrigin.should.be '*'
48 message = JSON.parse messageString
49 _.isNumber(message.id).should.be true
50 message._portal.should.be true
51 message.jsonrpc.should.be '2.0'
52
53 postRoutes[message.method].should.exist
54
55 if postRoutes[message.method].timeout
56 return
57
58 result = postRoutes[message.method].data
59
60 if _.isFunction result
61 result = result(message)
62
63 e = document.createEvent 'Event'
64 e.initEvent 'message', true, true
65
66 e.origin = postRoutes[message.method].origin or ('http://' + TRUSTED_DOMAIN)
67 e.data = JSON.stringify _.defaults(
68 {id: message.id, _portal: true}
69 result
70 )
71
72 window.dispatchEvent e
73
74routePost = (method, {origin, data, timeout}) ->
75 postRoutes[method] = {origin, data, timeout}
76
77routePost 'ping', {data: result: 'pong'}
78routePost 'auth.getStatus',
79 data:
80 result: {accessToken: 1}
81
82describe 'sdk', ->
83 @timeout 200
84
85 describe 'version', ->
86 it 'has version', (done) ->
87 Clay 'version', (err, v) ->
88 v.should.be 'v' + packageConfig.version
89
90 done(err)
91
92 describe 'signature', ->
93 it 'errors on invalid root method', (done) ->
94 Clay 'something', (err) ->
95 err.message.should.be 'Method not found'
96 done()
97
98 describe 'init()', ->
99 describe 'status', ->
100 it 'returns access token', (done) ->
101 Clay 'init', {gameId: '1'}, (err, status) ->
102 status.accessToken.should.be.a.Number
103 done(err)
104
105 describe 'config', ->
106 it 'sets gameId', (done) ->
107 cfg = deferredFactory()
108 ClayRoot.__set__ 'config', cfg
109
110 routePost 'auth.getStatus',
111 data:
112 result: {accessToken: 1}
113
114 Clay 'init', {gameId: '2'}
115
116 cfg.then (config) ->
117 config.gameId.should.be '2'
118 .then (x) -> done()
119 .catch done
120
121 describe 'signature', ->
122 it 'gameId type checks undefined', (done) ->
123 Clay 'init', {}, (err) ->
124 err.message.should.be 'Missing or invalid gameId'
125 done()
126
127 it 'gameId type checks number', (done) ->
128 Clay 'init', {gameId: 1}, (err) ->
129 err.message.should.be 'Missing or invalid gameId'
130 done()
131
132 describe 'domain verification when framed', ->
133 it 'dissallows invalid domains', (done) ->
134
135 routePost 'auth.getStatus', origin: 'http://evil.io', data: result: '1'
136
137 Clay 'init', {gameId: '1'}, (err) ->
138 err.should.exist
139 done()
140
141 it 'allows invalid domains in debug mode', (done) ->
142 routePost 'auth.getStatus', origin: 'http://evil.io', data: result: '1'
143 Clay 'init', {gameId: '1', debug: true}, done
144
145 describe 'client()', ->
146 describe 'state errors', ->
147 before ->
148 ClayRoot.__set__ 'initHasBeenCalled', false
149
150 it 'errors if init hasn\'t been called', (done) ->
151 Clay 'client.kik.send', (err) ->
152 err.message.should.be 'Must call Clay(\'init\') first'
153 done()
154
155 describe 'signature', ->
156 before ->
157 ClayRoot.__set__ 'initHasBeenCalled', true
158 ClayRoot.__set__ 'config', Promiz.resolve {gameId: '1'}
159
160 it 'errors if method missing', (done) ->
161 Clay 'client', (err) ->
162 err.message.should.be 'Missing or invalid method'
163 done()
164
165 it 'succeeds if params is an object', (done) ->
166 routePost 'share.any',
167 origin: 'http://clay.io'
168 data:
169 result: {test: true}
170
171 Clay 'client.share.any', {text: 'test'}, (err) ->
172 done(err)
173
174 it 'errors if params is a string', (done) ->
175 Clay 'client.kik.send', 'param', (err) ->
176 err.message.should.be 'Params must be an array'
177 done()
178
179 it 'errors if params is a number', (done) ->
180 Clay 'client.kik.send', 123, (err) ->
181 err.message.should.be 'Params must be an array'
182 done()
183
184 describe 'Posting', ->
185 before ->
186 ClayRoot.__set__ 'initHasBeenCalled', true
187 ClayRoot.__set__ 'config', Promiz.resolve {gameId: '1'}
188
189 it 'posts to parent frame', (done) ->
190 routePost 'kik.getUser',
191 origin: 'http://clay.io'
192 data:
193 result: {test: true}
194
195 Clay 'client.kik.getUser', (err, user) ->
196 should.not.exist err
197 user.test.should.be true
198 done()
199
200 it 'recieved errors', (done) ->
201 routePost 'kik.getUser',
202 origin: 'http://clay.io'
203 data:
204 error: {message: 'abc'}
205
206 Clay 'client.kik.getUser', (err) ->
207 err.message.should.be 'abc'
208 done()
209
210 it 'times out', (done) ->
211 portal = ClayRoot.__get__ 'portal'
212 routePost 'infinite.loop', timeout: true
213
214 portal.down()
215 portal.up timeout: 1
216
217 Clay 'client.infinite.loop', (err) ->
218 err.message.should.be 'Message Timeout'
219 done()
220
221 describe 'share.any', ->
222 describe 'framed', ->
223 before ->
224 ClayRoot.__set__ 'initHasBeenCalled', true
225 ClayRoot.__set__ 'config', Promiz.resolve {gameId: '1'}
226
227 it 'posts to parent', (done) ->
228 routePost 'share.any',
229 origin: 'http://clay.io'
230 data: (message) ->
231 message.params[0].gameId.should.be '1'
232 result: {test: true}
233
234 Clay 'client.share.any', [{text: 'Hello World'}], (err, res) ->
235 res.test.should.be true
236 done(err)
237
238 it 'falls back to local if parent fails', (done) ->
239 routePost 'share.any',
240 origin: 'http://clay.io'
241 data:
242 error: {message: 'something went wrong'}
243
244 openCnt = 0
245 ClayRoot.__set__ 'window.open', (url) ->
246 openCnt += 1
247 url.should.be 'https://twitter.com/intent/tweet?text=Hello%20World'
248
249 Clay 'client.share.any', [{text: 'Hello World'}], (err, res) ->
250 openCnt.should.be 1
251 done(err)
252
253 it 'errors if missing text', (done) ->
254 routePost 'share.any',
255 origin: 'http://clay.io'
256 data:
257 error: {message: 'something went wrong'}
258
259 Clay 'client.share.any', (err) ->
260 err.message.should.be 'text parameter is missing or invalid'
261 done()
262
263 describe 'local', ->
264 before ->
265 ClayRoot.__set__ 'initHasBeenCalled', true
266 ClayRoot.__set__ 'config', Promiz.resolve {gameId: '1'}
267
268 it 'tweets', (done) ->
269 openCnt = 0
270 ClayRoot.__set__ 'window.open', (url) ->
271 openCnt += 1
272 url.should.be 'https://twitter.com/intent/tweet?text=Hello%20World'
273
274 Clay 'client.share.any', [{text: 'Hello World'}], (err, res) ->
275 openCnt.should.be 1
276 done(err)
277
278 describe 'register', ->
279 before ->
280 ClayRoot.__set__ 'initHasBeenCalled', false
281
282 it 'registers function', (done) ->
283 rootConfig = deferredFactory()
284 ClayRoot.__set__ 'config', rootConfig
285
286 Clay 'register', {method: 'ui', fn: (config) ->
287 (method, params, cb) ->
288 config.then ({gameId}) ->
289 {
290 test: true
291 hello: params[0].hello
292 gameId: gameId
293 }
294 .then (x) -> cb null, x
295 .catch cb
296 }
297
298 rootConfig.resolve {gameId: '1'}
299
300 Clay 'ui.test', {hello: 'world'}, (err, res) ->
301 res.test.should.be true
302 res.hello.should.be 'world'
303 res.gameId.should.be '1'
304 done(err)
305
306 describe 'domain verification', ->
307 @timeout 1000
308 before ->
309 Clay 'init', {gameId: '1'}
310 ClayRoot.__set__ 'initHasBeenCalled', true
311 ClayRoot.__set__ 'config', Promiz.resolve {gameId: '1'}
312
313 it 'Succeeds on valid domains', (done) ->
314 domains = [
315 "http://#{TRUSTED_DOMAIN}/"
316 "https://#{TRUSTED_DOMAIN}/"
317 "http://#{TRUSTED_DOMAIN}"
318 "https://#{TRUSTED_DOMAIN}"
319
320 # Sub domains
321 "http://sub.#{TRUSTED_DOMAIN}/"
322 "https://sub.#{TRUSTED_DOMAIN}/"
323 "http://sub.#{TRUSTED_DOMAIN}"
324 "https://sub.#{TRUSTED_DOMAIN}"
325
326 "http://sub.sub.#{TRUSTED_DOMAIN}/"
327 "https://sub.sub.#{TRUSTED_DOMAIN}/"
328 "http://sub.sub.#{TRUSTED_DOMAIN}"
329 "https://sub.sub.#{TRUSTED_DOMAIN}"
330 ]
331
332 Promise.map domains, (domain) ->
333 routePost 'domain.test',
334 origin: domain
335 data:
336 result: {test: true}
337
338 Clay 'client.domain.test', (err, user) ->
339 if err
340 throw err
341 user.test.should.be true
342 .then -> done()
343 .catch done
344
345 it 'Errors on invalid domains', (done) ->
346
347 domains = [
348 'http://evil.io/'
349 'http://sub.evil.io/'
350 'http://sub.sub.evil.io/'
351 "http://evil.io/http://#{TRUSTED_DOMAIN}/"
352 ]
353
354 Promise.each domains, (domain) ->
355 new Promise (resolve, reject) ->
356 routePost 'kik.getUser',
357 origin: domain
358 data:
359 result: {test: true}
360
361 Clay 'client.kik.getUser', (err) ->
362 unless err
363 reject new Error 'Missing error'
364
365 resolve()
366 .then -> done()
367 .catch done