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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +127 -25
- data/LICENSE +1 -1
- data/README_RAILS.md +4 -2
- data/Rakefile +6 -1
- data/dist/unpoly.js +3192 -2198
- data/dist/unpoly.min.js +4 -3
- data/lib/assets/javascripts/unpoly/browser.coffee +51 -63
- data/lib/assets/javascripts/unpoly/bus.coffee +58 -33
- data/lib/assets/javascripts/unpoly/classes/cache.coffee +117 -0
- data/lib/assets/javascripts/unpoly/{dom → classes}/extract_cascade.coffee +3 -3
- data/lib/assets/javascripts/unpoly/{dom → classes}/extract_plan.coffee +1 -1
- data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +57 -0
- data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +52 -0
- data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +95 -0
- data/lib/assets/javascripts/unpoly/classes/record.coffee +16 -0
- data/lib/assets/javascripts/unpoly/classes/request.coffee +228 -0
- data/lib/assets/javascripts/unpoly/classes/response.coffee +138 -0
- data/lib/assets/javascripts/unpoly/dom.coffee +151 -142
- data/lib/assets/javascripts/unpoly/feedback.coffee +67 -38
- data/lib/assets/javascripts/unpoly/form.coffee +156 -139
- data/lib/assets/javascripts/unpoly/history.coffee +22 -19
- data/lib/assets/javascripts/unpoly/layout.coffee +108 -90
- data/lib/assets/javascripts/unpoly/link.coffee +159 -158
- data/lib/assets/javascripts/unpoly/log.coffee +5 -5
- data/lib/assets/javascripts/unpoly/modal.coffee +93 -81
- data/lib/assets/javascripts/unpoly/motion.coffee +291 -250
- data/lib/assets/javascripts/unpoly/popup.coffee +67 -53
- data/lib/assets/javascripts/unpoly/protocol.coffee +67 -16
- data/lib/assets/javascripts/unpoly/proxy.coffee +282 -211
- data/lib/assets/javascripts/unpoly/rails.coffee +3 -14
- data/lib/assets/javascripts/unpoly/syntax.coffee +54 -49
- data/lib/assets/javascripts/unpoly/tooltip.coffee +18 -25
- data/lib/assets/javascripts/unpoly/util.coffee +236 -477
- data/lib/assets/javascripts/unpoly.coffee +1 -1
- data/lib/unpoly/rails/inspector.rb +67 -22
- data/lib/unpoly/rails/version.rb +1 -1
- data/package.json +1 -1
- data/spec_app/Gemfile.lock +13 -13
- data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
- data/spec_app/app/assets/javascripts/jasmine_specs.coffee +1 -1
- data/spec_app/app/assets/stylesheets/jasmine_specs.sass +10 -0
- data/spec_app/app/controllers/binding_test_controller.rb +19 -2
- data/spec_app/app/controllers/method_test_controller.rb +16 -0
- data/spec_app/app/views/layouts/jasmine_rails/spec_runner.html.erb +20 -0
- data/spec_app/app/views/method_test/form_target.erb +17 -0
- data/spec_app/app/views/method_test/page1.erb +11 -0
- data/spec_app/app/views/method_test/page2.erb +6 -0
- data/spec_app/app/views/pages/start.erb +33 -19
- data/spec_app/config/initializers/assets.rb +5 -0
- data/spec_app/config/routes.rb +3 -0
- data/spec_app/spec/controllers/binding_test_controller_spec.rb +82 -27
- data/spec_app/spec/javascripts/helpers/agent_detector.coffee +17 -0
- data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +102 -0
- data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -1
- data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +5 -2
- data/spec_app/spec/javascripts/helpers/promise_state.js +18 -0
- data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +9 -0
- data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +22 -0
- data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +11 -3
- data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +10 -0
- data/spec_app/spec/javascripts/helpers/to_be_error.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_match_url.coffee +13 -0
- data/spec_app/spec/javascripts/helpers/trigger.js.coffee +13 -6
- data/spec_app/spec/javascripts/up/browser_spec.js.coffee +92 -33
- data/spec_app/spec/javascripts/up/bus_spec.js.coffee +64 -15
- data/spec_app/spec/javascripts/up/classes/.keep +0 -0
- data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +1 -0
- data/spec_app/spec/javascripts/up/dom_spec.js.coffee +759 -551
- data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +155 -82
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +490 -349
- data/spec_app/spec/javascripts/up/history_spec.js.coffee +226 -179
- data/spec_app/spec/javascripts/up/layout_spec.js.coffee +253 -185
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +416 -270
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +459 -330
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +198 -153
- data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +9 -0
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +240 -175
- data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +38 -0
- data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +777 -303
- data/spec_app/spec/javascripts/up/rails_spec.js.coffee +24 -8
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +40 -23
- data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +80 -66
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +227 -201
- data/spec_app/vendor/asset-libs/es6-promise-4.1.6/es6-promise.auto.js +1159 -0
- metadata +30 -7
- data/spec_app/spec/javascripts/helpers/reset_path.js.coffee +0 -7
- 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
|
@@ -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
|
-
|
3
|
-
|
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,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 = (
|
5
|
+
mouseover = (element, options) ->
|
6
|
+
$element = $(element)
|
6
7
|
event = createMouseEvent('mouseover', options)
|
7
8
|
dispatch($element, event)
|
8
9
|
|
9
|
-
mousedown = (
|
10
|
+
mousedown = (element, options) ->
|
11
|
+
$element = $(element)
|
10
12
|
event = createMouseEvent('mousedown', options)
|
11
13
|
dispatch($element, event)
|
12
14
|
|
13
|
-
mouseup = (
|
15
|
+
mouseup = (element, options) ->
|
16
|
+
$element = $(element)
|
14
17
|
event = createMouseEvent('mouseup', options)
|
15
18
|
dispatch($element, event)
|
16
19
|
|
17
|
-
click = (
|
20
|
+
click = (element, options) ->
|
21
|
+
$element = $(element)
|
18
22
|
event = createMouseEvent('click', options)
|
19
23
|
dispatch($element, event)
|
20
24
|
|
21
|
-
focus = (
|
25
|
+
focus = (element, options) ->
|
26
|
+
$element = $(element)
|
22
27
|
$element.focus()
|
23
28
|
|
24
|
-
clickSequence = (
|
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.
|
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
|
14
|
+
describe "for GET requests", ->
|
15
15
|
|
16
|
-
it
|
17
|
-
|
18
|
-
up.browser.
|
19
|
-
expect(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
expect(
|
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¶m2=param2%20value')
|
23
|
+
# No params should be left in the form
|
24
|
+
expect($form.find('input')).not.toExist()
|
25
25
|
|
26
|
-
describe
|
26
|
+
describe "for POST requests", ->
|
27
27
|
|
28
|
-
it
|
29
|
-
submitForm = up.browser
|
30
|
-
up.browser.
|
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')).
|
35
|
-
expect($form.attr('method')).toEqual('
|
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
|
57
|
-
up.browser.
|
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('
|
62
|
-
expect($form.find('input[name="_method"]').val()).toEqual(method
|
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
|
-
|
18
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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', ->
|