unpoly-rails 0.37.0 → 0.50.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of unpoly-rails might be problematic. Click here for more details.

Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +127 -25
  3. data/LICENSE +1 -1
  4. data/README_RAILS.md +4 -2
  5. data/Rakefile +6 -1
  6. data/dist/unpoly.js +3192 -2198
  7. data/dist/unpoly.min.js +4 -3
  8. data/lib/assets/javascripts/unpoly/browser.coffee +51 -63
  9. data/lib/assets/javascripts/unpoly/bus.coffee +58 -33
  10. data/lib/assets/javascripts/unpoly/classes/cache.coffee +117 -0
  11. data/lib/assets/javascripts/unpoly/{dom → classes}/extract_cascade.coffee +3 -3
  12. data/lib/assets/javascripts/unpoly/{dom → classes}/extract_plan.coffee +1 -1
  13. data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +57 -0
  14. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +52 -0
  15. data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +95 -0
  16. data/lib/assets/javascripts/unpoly/classes/record.coffee +16 -0
  17. data/lib/assets/javascripts/unpoly/classes/request.coffee +228 -0
  18. data/lib/assets/javascripts/unpoly/classes/response.coffee +138 -0
  19. data/lib/assets/javascripts/unpoly/dom.coffee +151 -142
  20. data/lib/assets/javascripts/unpoly/feedback.coffee +67 -38
  21. data/lib/assets/javascripts/unpoly/form.coffee +156 -139
  22. data/lib/assets/javascripts/unpoly/history.coffee +22 -19
  23. data/lib/assets/javascripts/unpoly/layout.coffee +108 -90
  24. data/lib/assets/javascripts/unpoly/link.coffee +159 -158
  25. data/lib/assets/javascripts/unpoly/log.coffee +5 -5
  26. data/lib/assets/javascripts/unpoly/modal.coffee +93 -81
  27. data/lib/assets/javascripts/unpoly/motion.coffee +291 -250
  28. data/lib/assets/javascripts/unpoly/popup.coffee +67 -53
  29. data/lib/assets/javascripts/unpoly/protocol.coffee +67 -16
  30. data/lib/assets/javascripts/unpoly/proxy.coffee +282 -211
  31. data/lib/assets/javascripts/unpoly/rails.coffee +3 -14
  32. data/lib/assets/javascripts/unpoly/syntax.coffee +54 -49
  33. data/lib/assets/javascripts/unpoly/tooltip.coffee +18 -25
  34. data/lib/assets/javascripts/unpoly/util.coffee +236 -477
  35. data/lib/assets/javascripts/unpoly.coffee +1 -1
  36. data/lib/unpoly/rails/inspector.rb +67 -22
  37. data/lib/unpoly/rails/version.rb +1 -1
  38. data/package.json +1 -1
  39. data/spec_app/Gemfile.lock +13 -13
  40. data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
  41. data/spec_app/app/assets/javascripts/jasmine_specs.coffee +1 -1
  42. data/spec_app/app/assets/stylesheets/jasmine_specs.sass +10 -0
  43. data/spec_app/app/controllers/binding_test_controller.rb +19 -2
  44. data/spec_app/app/controllers/method_test_controller.rb +16 -0
  45. data/spec_app/app/views/layouts/jasmine_rails/spec_runner.html.erb +20 -0
  46. data/spec_app/app/views/method_test/form_target.erb +17 -0
  47. data/spec_app/app/views/method_test/page1.erb +11 -0
  48. data/spec_app/app/views/method_test/page2.erb +6 -0
  49. data/spec_app/app/views/pages/start.erb +33 -19
  50. data/spec_app/config/initializers/assets.rb +5 -0
  51. data/spec_app/config/routes.rb +3 -0
  52. data/spec_app/spec/controllers/binding_test_controller_spec.rb +82 -27
  53. data/spec_app/spec/javascripts/helpers/agent_detector.coffee +17 -0
  54. data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +102 -0
  55. data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -1
  56. data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +5 -2
  57. data/spec_app/spec/javascripts/helpers/promise_state.js +18 -0
  58. data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +9 -0
  59. data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +22 -0
  60. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +11 -3
  61. data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +10 -0
  62. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +5 -0
  63. data/spec_app/spec/javascripts/helpers/to_match_url.coffee +13 -0
  64. data/spec_app/spec/javascripts/helpers/trigger.js.coffee +13 -6
  65. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +92 -33
  66. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +64 -15
  67. data/spec_app/spec/javascripts/up/classes/.keep +0 -0
  68. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +1 -0
  69. data/spec_app/spec/javascripts/up/dom_spec.js.coffee +759 -551
  70. data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +155 -82
  71. data/spec_app/spec/javascripts/up/form_spec.js.coffee +490 -349
  72. data/spec_app/spec/javascripts/up/history_spec.js.coffee +226 -179
  73. data/spec_app/spec/javascripts/up/layout_spec.js.coffee +253 -185
  74. data/spec_app/spec/javascripts/up/link_spec.js.coffee +416 -270
  75. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +459 -330
  76. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +198 -153
  77. data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +9 -0
  78. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +240 -175
  79. data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +38 -0
  80. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +777 -303
  81. data/spec_app/spec/javascripts/up/rails_spec.js.coffee +24 -8
  82. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +40 -23
  83. data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +80 -66
  84. data/spec_app/spec/javascripts/up/util_spec.js.coffee +227 -201
  85. data/spec_app/vendor/asset-libs/es6-promise-4.1.6/es6-promise.auto.js +1159 -0
  86. metadata +30 -7
  87. data/spec_app/spec/javascripts/helpers/reset_path.js.coffee +0 -7
  88. data/spec_app/spec/javascripts/helpers/to_equal_url.coffee +0 -11
@@ -0,0 +1,102 @@
1
+ u = up.util
2
+
3
+ LOG_ENABLED = false
4
+
5
+ unstubbedSetTimeout = window.setTimeout
6
+
7
+ window.asyncSpec = (args...) ->
8
+ (originalDone) ->
9
+
10
+ done = ->
11
+ # For some reason Jasmine ignores done() calls if its own clock is stubbed
12
+ jasmine.clock().uninstall()
13
+ originalDone()
14
+
15
+ fail = (args...) ->
16
+ # For some reason Jasmine ignores done() calls if its own clock is stubbed
17
+ jasmine.clock().uninstall()
18
+ originalDone.fail(args...)
19
+
20
+ plan = args.pop()
21
+
22
+ queue = []
23
+
24
+ insertCursor = 0
25
+
26
+ log = (args...) ->
27
+ if LOG_ENABLED
28
+ args[0] = "[asyncSpec] #{args[0]}"
29
+ up.log.debug(args...)
30
+
31
+ insertAtCursor = (task) ->
32
+ log('Inserting task at index %d: %o', insertCursor, task)
33
+ # We insert at pointer instead of pushing to the end.
34
+ # This way tasks can insert additional tasks at runtime.
35
+ queue.splice(insertCursor, 0, task)
36
+ insertCursor++
37
+
38
+ next = (block) ->
39
+ insertAtCursor [0, block, 'sync']
40
+
41
+ next.next = next # alternative API
42
+
43
+ next.after = (delay, block) ->
44
+ insertAtCursor [delay, block, 'sync']
45
+
46
+ next.await = (block) ->
47
+ insertAtCursor [0, block, 'async']
48
+
49
+ next.fail = fail
50
+
51
+ # Call example body
52
+ plan.call(this, next)
53
+
54
+ runBlockSyncAndPoke = (block) ->
55
+ try
56
+ log('runBlockSync')
57
+ block()
58
+ pokeQueue()
59
+ catch e
60
+ fail(e)
61
+ throw e
62
+
63
+ runBlockAsyncThenPoke = (blockOrPromise) ->
64
+ log('runBlockAsync')
65
+ # On plan-level people will usually pass a function returning a promise.
66
+ # During runtime people will usually pass a promise to delay the next step.
67
+ promise = if u.isPromise(blockOrPromise) then blockOrPromise else blockOrPromise()
68
+ promise.then -> pokeQueue()
69
+ promise.catch (e) -> fail(e)
70
+
71
+ pokeQueue = ->
72
+ if entry = queue[runtimeCursor]
73
+ log('Playing task at index %d', runtimeCursor)
74
+ runtimeCursor++
75
+ insertCursor++
76
+
77
+ timing = entry[0]
78
+ block = entry[1]
79
+ callStyle = entry[2]
80
+
81
+ log('Task is %s after %d ms: %o', callStyle, timing, block)
82
+
83
+ switch timing
84
+ when 'now'
85
+ runBlockSyncAndPoke(block)
86
+ else
87
+ fun = ->
88
+ # Move the block behind the microtask queue of that frame
89
+ Promise.resolve().then ->
90
+ if callStyle == 'sync'
91
+ runBlockSyncAndPoke(block)
92
+ else # async
93
+ runBlockAsyncThenPoke(block)
94
+
95
+ # Also move to the next frame
96
+ unstubbedSetTimeout(fun, timing)
97
+ else
98
+ log('calling done()')
99
+ done()
100
+
101
+ runtimeCursor = insertCursor = 0
102
+ pokeQueue()
@@ -13,7 +13,7 @@ beforeEach ->
13
13
  options = args[0] || {}
14
14
  else
15
15
  options = firstArg
16
- responseText = options.responseText
16
+ responseText = options.responseText || 'response-text'
17
17
  request = options.request || @lastRequest()
18
18
  request.respondWith
19
19
  status: options.status || 200
@@ -1,5 +1,8 @@
1
1
  beforeEach ->
2
2
  jasmine.Ajax.install()
3
3
 
4
- afterEach ->
5
- jasmine.Ajax.uninstall()
4
+ afterEach (done) ->
5
+ up.util.nextFrame ->
6
+ jasmine.Ajax.uninstall()
7
+ done()
8
+
@@ -0,0 +1,18 @@
1
+ function promiseState(promise) {
2
+ var uniqueValue = window['Symbol'] ? Symbol('unique') : Math.random().toString(36)
3
+
4
+ function notifyPendingOrResolved(value) {
5
+ if (value === uniqueValue) {
6
+ return Promise.resolve({ state: 'pending' })
7
+ } else {
8
+ return Promise.resolve({ state: 'fulfilled', value: value })
9
+ }
10
+ }
11
+
12
+ function notifyRejected(reason) {
13
+ return Promise.resolve({ state: 'rejected', value: reason })
14
+ }
15
+
16
+ var race = [promise, Promise.resolve(uniqueValue)]
17
+ return Promise.race(race).then(notifyPendingOrResolved, notifyRejected)
18
+ }
@@ -0,0 +1,9 @@
1
+ beforeEach ->
2
+ up.dom.config.fallbacks = ['.default-fallback']
3
+ up.history.config.popTargets = ['.default-fallback']
4
+ $element = $('<div class="default-fallback"></div>')
5
+ $element.appendTo(document.body)
6
+
7
+ afterEach ->
8
+ up.destroy('.default-fallback', log: false)
9
+
@@ -0,0 +1,22 @@
1
+ u = up.util
2
+
3
+ replaceStateHelperCount = 0
4
+
5
+ beforeAll ->
6
+ @hrefBeforeSuite = location.href
7
+ @titleBeforeSuite = document.title
8
+
9
+ afterAll (done) ->
10
+ up.util.nextFrame =>
11
+ history.replaceState?({ fromResetPathHelper: true }, '', @hrefBeforeSuite)
12
+ document.title = @titleBeforeSuite
13
+ done()
14
+
15
+ beforeEach ->
16
+ # Webkit ignores replaceState() calls after 100 calls / 30 sec.
17
+ # So specs need to explicitely activate history handling.
18
+ up.history.config.enabled = false
19
+
20
+ # Store original URL and title so we can restore it in afterEach.
21
+ @hrefBeforeExample = location.href
22
+ @titleBeforeExample = document.title
@@ -1,5 +1,13 @@
1
- afterEach ->
2
- up.reset()
3
- $('.up-error').remove()
1
+ afterEach (done) ->
2
+ # Wait one more frame so pending callbacks have a chance to run.
3
+ # Pending callbacks might change the URL or cause errors that bleed into
4
+ # the next example.
4
5
 
6
+ up.util.nextFrame =>
7
+ up.reset()
5
8
 
9
+ # Give async reset behavior another frame to play out,
10
+ # then start the next example.
11
+ up.util.nextFrame ->
12
+ $('.up-toast').remove()
13
+ done()
@@ -0,0 +1,10 @@
1
+ showVersions = ->
2
+ $('.jasmine-version').text """
3
+ jQuery #{$.fn.jquery}
4
+ Unpoly #{up.version}
5
+ Jasmine #{jasmine.version}
6
+ """
7
+
8
+ $ ->
9
+ # Give Jasmine time to initialize
10
+ setTimeout(showVersions, 0)
@@ -0,0 +1,5 @@
1
+ beforeEach ->
2
+ jasmine.addMatchers
3
+ toBeError: (util, customEqualityTesters) ->
4
+ compare: (actual, message) ->
5
+ pass: (actual instanceof Error) && (!message || (message instanceof RegExp && actual.message =~ message) || actual.message == message)
@@ -0,0 +1,13 @@
1
+ u = up.util
2
+
3
+ beforeEach ->
4
+ jasmine.addMatchers
5
+ toMatchUrl: (util, customEqualityTesters) ->
6
+ compare: (actual, expected, normalizeOptions = {}) ->
7
+ pass = true
8
+ pass &&= u.isString(actual)
9
+ pass &&= u.isString(expected)
10
+ pass &&= u.normalizeUrl(actual, normalizeOptions) == u.normalizeUrl(expected, normalizeOptions)
11
+ { pass }
12
+
13
+
@@ -2,26 +2,33 @@
2
2
 
3
3
  u = up.util
4
4
 
5
- mouseover = ($element, options) ->
5
+ mouseover = (element, options) ->
6
+ $element = $(element)
6
7
  event = createMouseEvent('mouseover', options)
7
8
  dispatch($element, event)
8
9
 
9
- mousedown = ($element, options) ->
10
+ mousedown = (element, options) ->
11
+ $element = $(element)
10
12
  event = createMouseEvent('mousedown', options)
11
13
  dispatch($element, event)
12
14
 
13
- mouseup = ($element, options) ->
15
+ mouseup = (element, options) ->
16
+ $element = $(element)
14
17
  event = createMouseEvent('mouseup', options)
15
18
  dispatch($element, event)
16
19
 
17
- click = ($element, options) ->
20
+ click = (element, options) ->
21
+ $element = $(element)
18
22
  event = createMouseEvent('click', options)
19
23
  dispatch($element, event)
20
24
 
21
- focus = ($element, options) ->
25
+ focus = (element, options) ->
26
+ $element = $(element)
22
27
  $element.focus()
23
28
 
24
- clickSequence = ($element, options) ->
29
+ clickSequence = (element, options) ->
30
+ $element = $(element)
31
+ mouseover($element, options)
25
32
  mousedown($element, options)
26
33
  focus($element, options)
27
34
  mouseup($element, options)
@@ -4,62 +4,87 @@ describe 'up.browser', ->
4
4
 
5
5
  describe 'JavaScript functions', ->
6
6
 
7
- describe 'up.browser.loadPage', ->
7
+ describe 'up.browser.navigate', ->
8
8
 
9
9
  afterEach ->
10
10
  # We're preventing the form to be submitted during tests,
11
11
  # so we need to remove it manually after each example.
12
12
  $('form.up-page-loader').remove()
13
13
 
14
- describe 'for GET requests', ->
14
+ describe "for GET requests", ->
15
15
 
16
- it 'sets location.href to the given URL', ->
17
- hrefSetter = up.browser.knife.mock('setLocationHref')
18
- up.browser.loadPage('/foo')
19
- expect(hrefSetter).toHaveBeenCalledWith('/foo')
20
-
21
- it 'encodes { data } params into the URL', ->
22
- hrefSetter = up.browser.knife.mock('setLocationHref')
23
- up.browser.loadPage('/foo', data: { param1: 'param1 value', param2: 'param2 value' })
24
- expect(hrefSetter).toHaveBeenCalledWith('/foo?param1=param1%20value&param2=param2%20value')
16
+ it "creates a GET form, adds all { data } params to the form action and submits the form", ->
17
+ submitForm = spyOn(up.browser, 'submitForm')
18
+ up.browser.navigate('/foo', method: 'GET', data: { param1: 'param1 value', param2: 'param2 value' })
19
+ expect(submitForm).toHaveBeenCalled()
20
+ $form = $('form.up-page-loader')
21
+ expect($form).toExist()
22
+ expect($form.attr('action')).toMatchUrl('/foo?param1=param1%20value&param2=param2%20value')
23
+ # No params should be left in the form
24
+ expect($form.find('input')).not.toExist()
25
25
 
26
- describe 'for POST requests', ->
26
+ describe "for POST requests", ->
27
27
 
28
- it 'creates a form, adds all { data } params a hidden fields and submits the form', ->
29
- submitForm = up.browser.knife.mock('submitForm')
30
- up.browser.loadPage('/foo', method: 'post', data: { param1: 'param1 value', param2: 'param2 value' })
28
+ it "creates a POST form, adds all { data } params a hidden fields and submits the form", ->
29
+ submitForm = spyOn(up.browser, 'submitForm')
30
+ up.browser.navigate('/foo', method: 'POST', data: { param1: 'param1 value', param2: 'param2 value' })
31
31
  expect(submitForm).toHaveBeenCalled()
32
32
  $form = $('form.up-page-loader')
33
33
  expect($form).toExist()
34
- expect($form.attr('action')).toEqual('/foo')
35
- expect($form.attr('method')).toEqual('post')
34
+ expect($form.attr('action')).toMatchUrl('/foo')
35
+ expect($form.attr('method')).toEqual('POST')
36
36
  expect($form.find('input[name="param1"][value="param1 value"]')).toExist()
37
37
  expect($form.find('input[name="param2"][value="param2 value"]')).toExist()
38
38
 
39
- it 'submits the Rails CSRF token as another hidden field', ->
40
- submitForm = up.browser.knife.mock('submitForm')
41
- spyOn(up.rails, 'csrfField').and.returnValue
42
- name: 'authenticity-param-name',
43
- value: 'authenticity-token'
44
- up.browser.loadPage('/foo', method: 'post')
45
- expect(submitForm).toHaveBeenCalled()
46
- $form = $('form.up-page-loader')
47
- $tokenInput = $form.find('input[name="authenticity-param-name"]')
48
- expect($tokenInput).toExist()
49
- expect($tokenInput.val()).toEqual('authenticity-token')
50
-
51
39
  u.each ['PUT', 'PATCH', 'DELETE'], (method) ->
52
40
 
53
41
  describe "for #{method} requests", ->
54
42
 
55
43
  it "uses a POST form and sends the actual method as a { _method } param", ->
56
- submitForm = up.browser.knife.mock('submitForm')
57
- up.browser.loadPage('/foo', method: method)
44
+ submitForm = spyOn(up.browser, 'submitForm')
45
+ up.browser.navigate('/foo', method: method)
58
46
  expect(submitForm).toHaveBeenCalled()
59
47
  $form = $('form.up-page-loader')
60
48
  expect($form).toExist()
61
- expect($form.attr('method')).toEqual('post')
62
- expect($form.find('input[name="_method"]').val()).toEqual(method.toLowerCase())
49
+ expect($form.attr('method')).toEqual('POST')
50
+ expect($form.find('input[name="_method"]').val()).toEqual(method)
51
+
52
+ describe 'CSRF', ->
53
+
54
+ beforeEach ->
55
+ up.protocol.config.csrfToken = -> 'csrf-token'
56
+ up.protocol.config.csrfParam = -> 'csrf-param'
57
+ @submitForm = spyOn(up.browser, 'submitForm')
58
+
59
+ it 'submits an CSRF token as another hidden field', ->
60
+ up.browser.navigate('/foo', method: 'post')
61
+ expect(@submitForm).toHaveBeenCalled()
62
+ $form = $('form.up-page-loader')
63
+ $tokenInput = $form.find('input[name="csrf-param"]')
64
+ expect($tokenInput).toExist()
65
+ expect($tokenInput.val()).toEqual('csrf-token')
66
+
67
+ it 'does not add a CSRF token if there is none', ->
68
+ up.protocol.config.csrfToken = -> ''
69
+ up.browser.navigate('/foo', method: 'post')
70
+ expect(@submitForm).toHaveBeenCalled()
71
+ $form = $('form.up-page-loader')
72
+ $tokenInput = $form.find('input[name="csrf-param"]')
73
+ expect($tokenInput).not.toExist()
74
+
75
+ it 'does not add a CSRF token for GET requests', ->
76
+ up.browser.navigate('/foo', method: 'get')
77
+ expect(@submitForm).toHaveBeenCalled()
78
+ $form = $('form.up-page-loader')
79
+ $tokenInput = $form.find('input[name="csrf-param"]')
80
+ expect($tokenInput).not.toExist()
81
+
82
+ it 'does not add a CSRF token when loading content from another domain', ->
83
+ up.browser.navigate('http://other-domain.tld/foo', method: 'get')
84
+ expect(@submitForm).toHaveBeenCalled()
85
+ $form = $('form.up-page-loader')
86
+ $tokenInput = $form.find('input[name="csrf-param"]')
87
+ expect($tokenInput).not.toExist()
63
88
 
64
89
  describe 'up.browser.sprintf', ->
65
90
 
@@ -131,3 +156,37 @@ describe 'up.browser', ->
131
156
  object.bar = 'bar'
132
157
  formatted = up.browser.sprintf('before %o after', object)
133
158
  expect(formatted).toEqual('before {"bar":"bar"} after')
159
+
160
+ describe 'up.browser.whenConfirmed', ->
161
+
162
+ it 'shows a confirmation dialog with the given message and fulfills when the user presses OK', (done) ->
163
+ spyOn(window, 'confirm').and.returnValue(true)
164
+ promise = up.browser.whenConfirmed(confirm: 'Do action?')
165
+ promiseState(promise).then (result) ->
166
+ expect(window.confirm).toHaveBeenCalledWith('Do action?')
167
+ expect(result.state).toEqual('fulfilled')
168
+ done()
169
+
170
+ it 'emits the event and rejects the returned promise when any listener calls event.preventDefault()', (done) ->
171
+ spyOn(window, 'confirm').and.returnValue(false)
172
+ promise = up.browser.whenConfirmed(confirm: 'Do action?')
173
+ promiseState(promise).then (result) ->
174
+ expect(window.confirm).toHaveBeenCalledWith('Do action?')
175
+ expect(result.state).toEqual('rejected')
176
+ done()
177
+
178
+ it 'does now show a conformation dialog and fulfills if no { confirm } option is given', (done) ->
179
+ spyOn(window, 'confirm')
180
+ promise = up.browser.whenConfirmed({})
181
+ promiseState(promise).then (result) ->
182
+ expect(window.confirm).not.toHaveBeenCalled()
183
+ expect(result.state).toEqual('fulfilled')
184
+ done()
185
+
186
+ it "does now show a conformation dialog and fulfills if a { confirm } option is given but we're also preloading", (done) ->
187
+ spyOn(window, 'confirm')
188
+ promise = up.browser.whenConfirmed(confirm: 'Do action?', preload: true)
189
+ promiseState(promise).then (result) ->
190
+ expect(window.confirm).not.toHaveBeenCalled()
191
+ expect(result.state).toEqual('fulfilled')
192
+ done()
@@ -4,8 +4,7 @@ describe 'up.bus', ->
4
4
 
5
5
  describe 'up.on', ->
6
6
 
7
- it 'registers a delagating event listener to the document body, which passes the $element as a second argument to the listener', ->
8
-
7
+ it 'registers a delagating event listener to the document body, which passes the $element as a second argument to the listener', asyncSpec (next) ->
9
8
  affix('.container .child')
10
9
  observeClass = jasmine.createSpy()
11
10
  up.on 'click', '.child', (event, $element) ->
@@ -14,20 +13,25 @@ describe 'up.bus', ->
14
13
  Trigger.click($('.container'))
15
14
  Trigger.click($('.child'))
16
15
 
17
- expect(observeClass).not.toHaveBeenCalledWith('container')
18
- expect(observeClass).toHaveBeenCalledWith('child')
16
+ next =>
17
+ expect(observeClass).not.toHaveBeenCalledWith('container')
18
+ expect(observeClass).toHaveBeenCalledWith('child')
19
19
 
20
- it 'returns a method that unregisters the event listener when called', ->
20
+ it 'returns a method that unregisters the event listener when called', asyncSpec (next) ->
21
21
  $child = affix('.child')
22
22
  clickSpy = jasmine.createSpy()
23
23
  unsubscribe = up.on 'click', '.child', clickSpy
24
24
  Trigger.click($('.child'))
25
- expect(clickSpy.calls.count()).toEqual(1)
26
- unsubscribe()
27
- Trigger.click($('.child'))
28
- expect(clickSpy.calls.count()).toEqual(1)
29
25
 
30
- it 'parses an up-data attribute as JSON and passes the parsed object as a third argument to the initializer', ->
26
+ next =>
27
+ expect(clickSpy.calls.count()).toEqual(1)
28
+ unsubscribe()
29
+ Trigger.click($('.child'))
30
+
31
+ next =>
32
+ expect(clickSpy.calls.count()).toEqual(1)
33
+
34
+ it 'parses an up-data attribute as JSON and passes the parsed object as a third argument to the initializer', asyncSpec (next) ->
31
35
  $child = affix('.child')
32
36
  observeArgs = jasmine.createSpy()
33
37
  up.on 'click', '.child', (event, $element, data) ->
@@ -37,27 +41,33 @@ describe 'up.bus', ->
37
41
  $tag = affix(".child").attr('up-data', JSON.stringify(data))
38
42
 
39
43
  Trigger.click($('.child'))
40
- expect(observeArgs).toHaveBeenCalledWith('child', data)
41
44
 
42
- it 'passes an empty object as a second argument to the listener if there is no up-data attribute', ->
45
+ next =>
46
+ expect(observeArgs).toHaveBeenCalledWith('child', data)
47
+
48
+ it 'passes an empty object as a second argument to the listener if there is no up-data attribute', asyncSpec (next) ->
43
49
  $child = affix('.child')
44
50
  observeArgs = jasmine.createSpy()
45
51
  up.on 'click', '.child', (event, $element, data) ->
46
52
  observeArgs($element.attr('class'), data)
47
53
 
48
54
  Trigger.click($('.child'))
49
- expect(observeArgs).toHaveBeenCalledWith('child', {})
55
+
56
+ next =>
57
+ expect(observeArgs).toHaveBeenCalledWith('child', {})
50
58
 
51
59
  describe 'up.off', ->
52
60
 
53
- it 'unregisters an event listener previously registered through up.on', ->
61
+ it 'unregisters an event listener previously registered through up.on', asyncSpec (next) ->
54
62
  $child = affix('.child')
55
63
  clickSpy = jasmine.createSpy()
56
64
  up.on 'click', '.child', clickSpy
57
65
  Trigger.click($('.child'))
58
66
  up.off 'click', '.child', clickSpy
59
67
  Trigger.click($('.child'))
60
- expect(clickSpy.calls.count()).toEqual(1)
68
+
69
+ next =>
70
+ expect(clickSpy.calls.count()).toEqual(1)
61
71
 
62
72
  it 'throws an error if the given event listener was not registered through up.on', ->
63
73
  someFunction = ->
@@ -121,3 +131,42 @@ describe 'up.bus', ->
121
131
  expect($emittedTarget).toEqual($element)
122
132
 
123
133
  expect(emittedEvent.$element).toEqual($element)
134
+
135
+ describe 'up.bus.renamedEvent', ->
136
+
137
+ it 'prints a warning and registers the event listener for the new event name', ->
138
+ warnSpy = spyOn(up.log, 'warn')
139
+ listener = jasmine.createSpy('listener')
140
+
141
+ # Reister listener for the old event name
142
+ up.on('up:proxy:received', listener)
143
+ expect(warnSpy).toHaveBeenCalled()
144
+
145
+ # Emit event with new name and see that it invokes the legacy listener
146
+ up.emit('up:proxy:loaded')
147
+ expect(listener.calls.count()).toBe(1)
148
+
149
+ # Check that up.off works with the old event name
150
+ up.off('up:proxy:received', listener)
151
+
152
+ up.emit('up:proxy:loaded')
153
+ expect(listener.calls.count()).toBe(1)
154
+
155
+ describe 'up.bus.whenEmitted', ->
156
+
157
+ it 'emits the event and fulfills the returned promise when no listener calls event.preventDefault()', (done) ->
158
+ eventListener = jasmine.createSpy('event listener')
159
+ up.on('my:event', eventListener)
160
+ promise = up.bus.whenEmitted('my:event', key: 'value')
161
+ promiseState(promise).then (result) ->
162
+ expect(eventListener).toHaveBeenCalledWith(jasmine.objectContaining(key: 'value'), jasmine.anything(), jasmine.anything())
163
+ expect(result.state).toEqual('fulfilled')
164
+ done()
165
+
166
+ it 'emits the event and rejects the returned promise when any listener calls event.preventDefault()', (done) ->
167
+ eventListener = (event) -> event.preventDefault()
168
+ up.on('my:event', eventListener)
169
+ promise = up.bus.whenEmitted('my:event', key: 'value')
170
+ promiseState(promise).then (result) ->
171
+ expect(result.state).toEqual('rejected')
172
+ done()
File without changes
@@ -0,0 +1 @@
1
+ describe 'up.Cache', ->