unpoly-rails 0.57.0 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of unpoly-rails might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +393 -1
- data/Gemfile.lock +5 -2
- data/README.md +1 -1
- data/README_RAILS.md +1 -1
- data/Rakefile +10 -1
- data/design/es6.js +32 -0
- data/design/ie11.txt +9 -0
- data/design/measure_jquery/element_list.js +41 -0
- data/design/measure_jquery/up.on_vs_addEventListener.js +56 -0
- data/design/todo_jquery.txt +13 -0
- data/dist/unpoly-bootstrap3.js +8 -8
- data/dist/unpoly-bootstrap3.min.js +1 -1
- data/dist/unpoly.css +22 -20
- data/dist/unpoly.js +6990 -5336
- data/dist/unpoly.min.css +1 -1
- data/dist/unpoly.min.js +4 -4
- data/lib/assets/javascripts/unpoly-bootstrap3/viewport-ext.coffee +5 -0
- data/lib/assets/javascripts/unpoly.coffee +8 -6
- data/lib/assets/javascripts/unpoly/browser.coffee.erb +23 -118
- data/lib/assets/javascripts/unpoly/classes/body_shifter.coffee +36 -0
- data/lib/assets/javascripts/unpoly/classes/cache.coffee +4 -4
- data/lib/assets/javascripts/unpoly/classes/compile_pass.coffee +45 -39
- data/lib/assets/javascripts/unpoly/classes/config.coffee +9 -0
- data/lib/assets/javascripts/unpoly/classes/css_transition.coffee +18 -27
- data/lib/assets/javascripts/unpoly/classes/divertible_chain.coffee +39 -0
- data/lib/assets/javascripts/unpoly/classes/event_listener.coffee +116 -0
- data/lib/assets/javascripts/unpoly/classes/extract_cascade.coffee +8 -8
- data/lib/assets/javascripts/unpoly/classes/extract_plan.coffee +19 -19
- data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +54 -31
- data/lib/assets/javascripts/unpoly/classes/{focus_tracker.coffee → focus_follower.coffee} +2 -2
- data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +25 -25
- data/lib/assets/javascripts/unpoly/classes/html_parser.coffee +4 -11
- data/lib/assets/javascripts/unpoly/classes/motion_controller.coffee +157 -0
- data/lib/assets/javascripts/unpoly/classes/params.coffee.erb +525 -0
- data/lib/assets/javascripts/unpoly/classes/record.coffee +8 -2
- data/lib/assets/javascripts/unpoly/classes/rect.js +21 -0
- data/lib/assets/javascripts/unpoly/classes/request.coffee +41 -35
- data/lib/assets/javascripts/unpoly/classes/response.coffee +7 -3
- data/lib/assets/javascripts/unpoly/classes/reveal_motion.coffee +102 -0
- data/lib/assets/javascripts/unpoly/classes/scroll_motion.coffee +67 -0
- data/lib/assets/javascripts/unpoly/classes/selector.coffee +60 -0
- data/lib/assets/javascripts/unpoly/classes/tether.coffee +105 -0
- data/lib/assets/javascripts/unpoly/classes/url_set.coffee +12 -7
- data/lib/assets/javascripts/unpoly/element.coffee.erb +1126 -0
- data/lib/assets/javascripts/unpoly/event.coffee.erb +437 -0
- data/lib/assets/javascripts/unpoly/feedback.coffee +73 -94
- data/lib/assets/javascripts/unpoly/form.coffee.erb +188 -181
- data/lib/assets/javascripts/unpoly/{dom.coffee.erb → fragment.coffee.erb} +250 -283
- data/lib/assets/javascripts/unpoly/framework.coffee +67 -0
- data/lib/assets/javascripts/unpoly/history.coffee +29 -28
- data/lib/assets/javascripts/unpoly/legacy.coffee +60 -0
- data/lib/assets/javascripts/unpoly/link.coffee.erb +127 -119
- data/lib/assets/javascripts/unpoly/log.coffee +99 -19
- data/lib/assets/javascripts/unpoly/modal.coffee.erb +95 -118
- data/lib/assets/javascripts/unpoly/motion.coffee.erb +158 -138
- data/lib/assets/javascripts/unpoly/namespace.coffee.erb +0 -5
- data/lib/assets/javascripts/unpoly/popup.coffee.erb +119 -102
- data/lib/assets/javascripts/unpoly/protocol.coffee +11 -15
- data/lib/assets/javascripts/unpoly/proxy.coffee +62 -65
- data/lib/assets/javascripts/unpoly/radio.coffee +3 -5
- data/lib/assets/javascripts/unpoly/rails.coffee +8 -9
- data/lib/assets/javascripts/unpoly/syntax.coffee.erb +173 -125
- data/lib/assets/javascripts/unpoly/toast.coffee +25 -24
- data/lib/assets/javascripts/unpoly/tooltip.coffee +89 -79
- data/lib/assets/javascripts/unpoly/util.coffee.erb +579 -1074
- data/lib/assets/javascripts/unpoly/{layout.coffee.erb → viewport.coffee.erb} +334 -264
- data/lib/assets/stylesheets/unpoly/dom.sass +1 -1
- data/lib/assets/stylesheets/unpoly/layout.sass +2 -0
- data/lib/assets/stylesheets/unpoly/popup.sass +0 -1
- data/lib/assets/stylesheets/unpoly/tooltip.sass +17 -12
- data/lib/unpoly/rails/version.rb +1 -1
- data/package.json +1 -2
- data/spec_app/Gemfile +2 -1
- data/spec_app/Gemfile.lock +38 -27
- data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
- data/spec_app/app/assets/javascripts/jasmine_specs.coffee +1 -2
- data/spec_app/app/assets/stylesheets/integration_test.sass +14 -1
- data/spec_app/app/controllers/scroll_test_controller.rb +5 -0
- data/spec_app/app/views/css_test/modal.erb +6 -6
- data/spec_app/app/views/css_test/popup.erb +44 -18
- data/spec_app/app/views/css_test/tooltip.erb +23 -4
- data/spec_app/app/views/error_test/trigger.erb +1 -1
- data/spec_app/app/views/form_test/basics/new.erb +1 -3
- data/spec_app/app/views/pages/start.erb +9 -2
- data/spec_app/app/views/reveal_test/long1.erb +1 -1
- data/spec_app/app/views/reveal_test/long2.erb +1 -1
- data/spec_app/app/views/reveal_test/within_document_viewport.erb +24 -0
- data/spec_app/app/views/reveal_test/within_overflowing_div_viewport.erb +28 -0
- data/spec_app/app/views/scroll_test/long1.erb +30 -0
- data/spec_app/config/routes.rb +1 -0
- data/spec_app/spec/javascripts/helpers/agent_detector.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +1 -0
- data/spec_app/spec/javascripts/helpers/browser_switches.js.coffee +17 -5
- data/spec_app/spec/javascripts/helpers/enable_logging.js.coffee +1 -1
- data/spec_app/spec/javascripts/helpers/fixture.js.coffee +25 -0
- data/spec_app/spec/javascripts/helpers/jquery_no_conflict.js +1 -0
- data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -0
- data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +1 -1
- data/spec_app/spec/javascripts/helpers/parse_form_data.js.coffee +2 -2
- data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +4 -1
- data/spec_app/spec/javascripts/helpers/remove_body_margin.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +2 -1
- data/spec_app/spec/javascripts/helpers/reset_knife.js.coffee +2 -2
- data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +18 -11
- data/spec_app/spec/javascripts/helpers/restore_body_scroll.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/spec_util.coffee +47 -0
- data/spec_app/spec/javascripts/helpers/to_be_around.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_array.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_be_attached.coffee +6 -2
- data/spec_app/spec/javascripts/helpers/to_be_blank.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_detached.coffee +6 -2
- data/spec_app/spec/javascripts/helpers/to_be_element.js.coffee +8 -0
- data/spec_app/spec/javascripts/helpers/to_be_error.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_given.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_hidden.js.coffee +8 -0
- data/spec_app/spec/javascripts/helpers/to_be_missing.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_present.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_scrolled_to.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_visible.js.coffee +9 -0
- data/spec_app/spec/javascripts/helpers/to_contain.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_end_with.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_equal_jquery.js.coffee +1 -2
- data/spec_app/spec/javascripts/helpers/to_equal_node_list.coffee +7 -0
- data/spec_app/spec/javascripts/helpers/to_equal_via_is_equal.js.coffee +7 -0
- data/spec_app/spec/javascripts/helpers/to_have_class.js.coffee +10 -0
- data/spec_app/spec/javascripts/helpers/to_have_descendant.js.coffee +10 -0
- data/spec_app/spec/javascripts/helpers/to_have_length.js.coffee +8 -0
- data/spec_app/spec/javascripts/helpers/to_have_opacity.coffee +7 -3
- data/spec_app/spec/javascripts/helpers/to_have_own_property.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_have_request_method.js.coffee +1 -0
- data/spec_app/spec/javascripts/helpers/to_have_text.js.coffee +9 -0
- data/spec_app/spec/javascripts/helpers/to_have_unhandled_rejections.coffee +0 -21
- data/spec_app/spec/javascripts/helpers/to_match_list.coffee +14 -0
- data/spec_app/spec/javascripts/helpers/to_match_selector.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_match_text.js.coffee +4 -1
- data/spec_app/spec/javascripts/helpers/to_match_url.coffee +1 -0
- data/spec_app/spec/javascripts/helpers/trigger.js.coffee +91 -7
- data/spec_app/spec/javascripts/helpers/wait_until_dom_ready.js.coffee +3 -0
- data/spec_app/spec/javascripts/up/browser_spec.js.coffee +23 -90
- data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +3 -0
- data/spec_app/spec/javascripts/up/classes/config_spec.coffee +24 -0
- data/spec_app/spec/javascripts/up/classes/divertible_chain_spec.coffee +45 -0
- data/spec_app/spec/javascripts/up/classes/focus_tracker_spec.coffee +5 -2
- data/spec_app/spec/javascripts/up/classes/params_spec.coffee +557 -0
- data/spec_app/spec/javascripts/up/classes/request_spec.coffee +7 -4
- data/spec_app/spec/javascripts/up/classes/scroll_motion_spec.js.coffee +51 -0
- data/spec_app/spec/javascripts/up/classes/store/memory_spec.js.coffee +3 -0
- data/spec_app/spec/javascripts/up/classes/store/session_spec.js.coffee +3 -2
- data/spec_app/spec/javascripts/up/element_spec.coffee +897 -0
- data/spec_app/spec/javascripts/up/event_spec.js.coffee +496 -0
- data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +69 -48
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +252 -194
- data/spec_app/spec/javascripts/up/{dom_spec.js.coffee → fragment_spec.js.coffee} +381 -388
- data/spec_app/spec/javascripts/up/history_spec.js.coffee +21 -19
- data/spec_app/spec/javascripts/up/jquery_spec.js.coffee +4 -0
- data/spec_app/spec/javascripts/up/legacy_spec.js.coffee +27 -0
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +163 -160
- data/spec_app/spec/javascripts/up/log_spec.js.coffee +85 -12
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +141 -123
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +117 -113
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +60 -77
- data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +1 -0
- data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +85 -78
- data/spec_app/spec/javascripts/up/radio_spec.js.coffee +29 -22
- data/spec_app/spec/javascripts/up/rails_spec.js.coffee +14 -13
- data/spec_app/spec/javascripts/up/spec_spec.js.coffee +9 -0
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +96 -66
- data/spec_app/spec/javascripts/up/toast_spec.js.coffee +37 -0
- data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +31 -47
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +725 -562
- data/spec_app/spec/javascripts/up/{layout_spec.js.coffee → viewport_spec.js.coffee} +175 -149
- metadata +57 -19
- data/lib/assets/javascripts/unpoly-bootstrap3/layout-ext.coffee +0 -5
- data/lib/assets/javascripts/unpoly/bus.coffee.erb +0 -518
- data/lib/assets/javascripts/unpoly/classes/extract_step.coffee +0 -4
- data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +0 -125
- data/lib/assets/javascripts/unpoly/params.coffee.erb +0 -522
- data/spec_app/spec/javascripts/helpers/append_fixture.js.coffee +0 -8
- data/spec_app/spec/javascripts/up/bus_spec.js.coffee +0 -210
- data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +0 -9
- data/spec_app/spec/javascripts/up/params_spec.coffee +0 -768
- data/spec_app/vendor/asset-libs/jasmine-fixture-1.3.4/jasmine-fixture.js +0 -433
- data/spec_app/vendor/asset-libs/jasmine-jquery-2.1.1/.bower.json +0 -26
- data/spec_app/vendor/asset-libs/jasmine-jquery-2.1.1/jasmine-jquery.js +0 -838
@@ -0,0 +1,37 @@
|
|
1
|
+
u = up.util
|
2
|
+
$ = jQuery
|
3
|
+
|
4
|
+
describe 'up.toast', ->
|
5
|
+
|
6
|
+
describe 'JavaScript functions', ->
|
7
|
+
|
8
|
+
describe 'up.toast.open()', ->
|
9
|
+
|
10
|
+
it 'opens a toast box with the given message', ->
|
11
|
+
up.toast.open('This is a message')
|
12
|
+
expect('.up-toast').toBeAttached()
|
13
|
+
expect($('.up-toast-message').text()).toContain('This is a message')
|
14
|
+
|
15
|
+
it 'opens a toast box with a close link', ->
|
16
|
+
up.toast.open('This is a message')
|
17
|
+
expect('.up-toast').toBeAttached()
|
18
|
+
$closeButton = $('.up-toast-action:contains("Close")')
|
19
|
+
expect($closeButton).toBeAttached()
|
20
|
+
|
21
|
+
Trigger.clickSequence($closeButton)
|
22
|
+
|
23
|
+
expect('.up-toast').not.toBeAttached()
|
24
|
+
|
25
|
+
it 'opens a toast box with the given custom action', ->
|
26
|
+
action =
|
27
|
+
label: 'Custom action'
|
28
|
+
callback: jasmine.createSpy('action callback')
|
29
|
+
up.toast.open('This is a message', { action })
|
30
|
+
$actionButton = $('.up-toast-action:contains("Custom action")')
|
31
|
+
expect($actionButton).toBeAttached()
|
32
|
+
expect(action.callback).not.toHaveBeenCalled()
|
33
|
+
|
34
|
+
Trigger.clickSequence($actionButton)
|
35
|
+
|
36
|
+
expect(action.callback).toHaveBeenCalled()
|
37
|
+
|
@@ -1,36 +1,38 @@
|
|
1
|
-
|
1
|
+
u = up.util
|
2
|
+
e = up.element
|
3
|
+
$ = jQuery
|
2
4
|
|
3
|
-
|
5
|
+
describe 'up.tooltip', ->
|
4
6
|
|
5
7
|
describe 'JavaScript functions', ->
|
6
8
|
|
7
9
|
describe 'up.tooltip.attach', ->
|
8
10
|
|
9
11
|
it 'opens a tooltip with the given text', (done) ->
|
10
|
-
$link =
|
12
|
+
$link = $fixture('span')
|
11
13
|
up.tooltip.attach($link, html: 'tooltip text').then ->
|
12
14
|
$tooltip = $('.up-tooltip')
|
13
15
|
expect($tooltip).toHaveText('tooltip text')
|
14
16
|
done()
|
15
17
|
|
16
18
|
it 'allows HTML for the tooltip text when contents are given as { html } option', (done) ->
|
17
|
-
$link =
|
19
|
+
$link = $fixture('span')
|
18
20
|
up.tooltip.attach($link, html: '<b>text</b>').then ->
|
19
21
|
$tooltip = $('.up-tooltip')
|
20
|
-
expect($tooltip.html()).
|
22
|
+
expect($tooltip.html()).toContain('<b>text</b>')
|
21
23
|
done()
|
22
24
|
|
23
25
|
it 'escapes HTML for the tooltip text when contents given as { text } option', (done) ->
|
24
|
-
$link =
|
26
|
+
$link = $fixture('span')
|
25
27
|
up.tooltip.attach($link, text: '<b>text</b>').then ->
|
26
28
|
$tooltip = $('.up-tooltip')
|
27
|
-
expect($tooltip.html()).
|
29
|
+
expect($tooltip.html()).toContain('<b>text</b>')
|
28
30
|
done()
|
29
31
|
|
30
32
|
describe 'positioning', ->
|
31
33
|
|
32
34
|
beforeEach ->
|
33
|
-
@$link =
|
35
|
+
@$link = $fixture('span').text('button label')
|
34
36
|
@$link.css(
|
35
37
|
position: 'absolute'
|
36
38
|
left: '200px'
|
@@ -40,10 +42,12 @@ describe 'up.tooltip', ->
|
|
40
42
|
)
|
41
43
|
|
42
44
|
beforeEach ->
|
43
|
-
@restoreBodyHeight =
|
45
|
+
@restoreBodyHeight = e.setTemporaryStyle(document.body, minHeight: '3000px')
|
44
46
|
|
45
47
|
afterEach ->
|
46
48
|
@restoreBodyHeight()
|
49
|
+
|
50
|
+
distanceFromOrigin = 10
|
47
51
|
|
48
52
|
describe 'with { position: "top" }', ->
|
49
53
|
|
@@ -52,8 +56,8 @@ describe 'up.tooltip', ->
|
|
52
56
|
up.tooltip.attach(@$link, html: 'tooltip text', position: 'top').then =>
|
53
57
|
$tooltip = $('.up-tooltip')
|
54
58
|
tooltipBox = $tooltip.get(0).getBoundingClientRect()
|
55
|
-
expect(tooltipBox.top).toBeAround(@linkBox.top - tooltipBox.height,
|
56
|
-
expect(tooltipBox.left).toBeAround(@linkBox.left + 0.5 * (@linkBox.width - tooltipBox.width),
|
59
|
+
expect(tooltipBox.top).toBeAround(@linkBox.top - tooltipBox.height - distanceFromOrigin, 1)
|
60
|
+
expect(tooltipBox.left).toBeAround(@linkBox.left + 0.5 * (@linkBox.width - tooltipBox.width), 1)
|
57
61
|
done()
|
58
62
|
|
59
63
|
describe 'with { position: "right" }', ->
|
@@ -63,8 +67,8 @@ describe 'up.tooltip', ->
|
|
63
67
|
up.tooltip.attach(@$link, html: 'tooltip text', position: 'right').then =>
|
64
68
|
$tooltip = $('.up-tooltip')
|
65
69
|
tooltipBox = $tooltip.get(0).getBoundingClientRect()
|
66
|
-
expect(tooltipBox.top).toBeAround(@linkBox.top + 0.5 * (@linkBox.height - tooltipBox.height),
|
67
|
-
expect(tooltipBox.left).toBeAround(@linkBox.left + @linkBox.width,
|
70
|
+
expect(tooltipBox.top).toBeAround(@linkBox.top + 0.5 * (@linkBox.height - tooltipBox.height), 1)
|
71
|
+
expect(tooltipBox.left).toBeAround(@linkBox.left + @linkBox.width + distanceFromOrigin, 1)
|
68
72
|
done()
|
69
73
|
|
70
74
|
describe 'with { position: "bottom" }', ->
|
@@ -74,8 +78,8 @@ describe 'up.tooltip', ->
|
|
74
78
|
up.tooltip.attach(@$link, html: 'tooltip text', position: 'bottom').then =>
|
75
79
|
$tooltip = $('.up-tooltip')
|
76
80
|
tooltipBox = $tooltip.get(0).getBoundingClientRect()
|
77
|
-
expect(tooltipBox.top).toBeAround(@linkBox.top + @linkBox.height,
|
78
|
-
expect(tooltipBox.left).toBeAround(@linkBox.left + 0.5 * (@linkBox.width - tooltipBox.width),
|
81
|
+
expect(tooltipBox.top).toBeAround(@linkBox.top + @linkBox.height + distanceFromOrigin, 1)
|
82
|
+
expect(tooltipBox.left).toBeAround(@linkBox.left + 0.5 * (@linkBox.width - tooltipBox.width), 1)
|
79
83
|
done()
|
80
84
|
|
81
85
|
describe 'with { position: "left" }', ->
|
@@ -85,33 +89,13 @@ describe 'up.tooltip', ->
|
|
85
89
|
up.tooltip.attach(@$link, html: 'tooltip text', position: 'left').then =>
|
86
90
|
$tooltip = $('.up-tooltip')
|
87
91
|
tooltipBox = $tooltip.get(0).getBoundingClientRect()
|
88
|
-
expect(tooltipBox.top).toBeAround(@linkBox.top + 0.5 * (@linkBox.height - tooltipBox.height),
|
89
|
-
expect(tooltipBox.left).toBeAround(@linkBox.left - tooltipBox.width,
|
92
|
+
expect(tooltipBox.top).toBeAround(@linkBox.top + 0.5 * (@linkBox.height - tooltipBox.height), 1)
|
93
|
+
expect(tooltipBox.left).toBeAround(@linkBox.left - tooltipBox.width - distanceFromOrigin, 1)
|
90
94
|
done()
|
91
95
|
|
92
|
-
it 'gives the tooltip { position: "fixed" } if the given link is fixed', (done) ->
|
93
|
-
# Let's test the harder case where the document is scrolled
|
94
|
-
up.layout.scroll(document, 50)
|
95
|
-
@$link.css(position: 'fixed')
|
96
|
-
@linkBox = @$link.get(0).getBoundingClientRect()
|
97
|
-
|
98
|
-
up.tooltip.attach(@$link, html: 'tooltip text', position: 'top').then =>
|
99
|
-
$tooltip = $('.up-tooltip')
|
100
|
-
tooltipBox = $tooltip.get(0).getBoundingClientRect()
|
101
|
-
expect($tooltip.css('position')).toEqual('fixed')
|
102
|
-
expect(tooltipBox.top).toBeAround(@linkBox.top - tooltipBox.height, 15)
|
103
|
-
expect(tooltipBox.left).toBeAround(@linkBox.left + 0.5 * (@linkBox.width - tooltipBox.width), 15)
|
104
|
-
done()
|
105
|
-
|
106
|
-
it 'closes an existing tooltip'
|
107
|
-
|
108
|
-
describe 'with position option', ->
|
109
|
-
|
110
|
-
it 'anchors the tooltip at a different edge of the element'
|
111
|
-
|
112
96
|
describe 'up.tooltip.close', ->
|
113
97
|
|
114
|
-
it '
|
98
|
+
it 'closes an existing tooltip'
|
115
99
|
|
116
100
|
describe 'unobtrusive behavior', ->
|
117
101
|
|
@@ -129,7 +113,7 @@ describe 'up.tooltip', ->
|
|
129
113
|
up.motion.config.enabled = false
|
130
114
|
|
131
115
|
it 'closes the tooltip', asyncSpec (next) ->
|
132
|
-
$link =
|
116
|
+
$link = $fixture('.link')
|
133
117
|
up.tooltip.attach($link, text: 'Tooltip text')
|
134
118
|
|
135
119
|
next =>
|
@@ -140,9 +124,9 @@ describe 'up.tooltip', ->
|
|
140
124
|
expect(up.tooltip.isOpen()).toBe(false)
|
141
125
|
|
142
126
|
it 'closes the tooltip when a an [up-instant] link removes its parent (and thus a click event never bubbles up to the document)', asyncSpec (next) ->
|
143
|
-
$parent =
|
127
|
+
$parent = $fixture('.parent')
|
144
128
|
$parentReplacingLink = $parent.affix('a[href="/foo"][up-target=".parent"][up-instant]')
|
145
|
-
$tooltipOpener =
|
129
|
+
$tooltipOpener = $fixture('.link')
|
146
130
|
up.tooltip.attach($tooltipOpener, text: 'Tooltip text')
|
147
131
|
|
148
132
|
next =>
|
@@ -153,9 +137,9 @@ describe 'up.tooltip', ->
|
|
153
137
|
expect(up.tooltip.isOpen()).toBe(false)
|
154
138
|
|
155
139
|
it 'closes a tooltip when the user clicks on an [up-target] link outside the tooltip', asyncSpec (next) ->
|
156
|
-
$target =
|
157
|
-
$outsideLink =
|
158
|
-
$tooltipOpener =
|
140
|
+
$target = $fixture('.target')
|
141
|
+
$outsideLink = $fixture('a[href="/foo"][up-target=".target"]')
|
142
|
+
$tooltipOpener = $fixture('.link')
|
159
143
|
up.tooltip.attach($tooltipOpener, text: 'Tooltip text')
|
160
144
|
|
161
145
|
next =>
|
@@ -166,9 +150,9 @@ describe 'up.tooltip', ->
|
|
166
150
|
expect(up.tooltip.isOpen()).toBe(false)
|
167
151
|
|
168
152
|
it 'closes a tooltip when the user clicks on an [up-instant] link outside the tooltip', asyncSpec (next) ->
|
169
|
-
$target =
|
170
|
-
$outsideLink =
|
171
|
-
$tooltipOpener =
|
153
|
+
$target = $fixture('.target')
|
154
|
+
$outsideLink = $fixture('a[href="/foo"][up-target=".target"][up-instant]')
|
155
|
+
$tooltipOpener = $fixture('.link')
|
172
156
|
up.tooltip.attach($tooltipOpener, text: 'Tooltip text')
|
173
157
|
|
174
158
|
next =>
|
@@ -1,15 +1,321 @@
|
|
1
|
-
|
1
|
+
u = up.util
|
2
|
+
e = up.element
|
3
|
+
$ = jQuery
|
2
4
|
|
3
|
-
|
5
|
+
describe 'up.util', ->
|
4
6
|
|
5
7
|
describe 'JavaScript functions', ->
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
describe 'up.util.isEqual', ->
|
10
|
+
|
11
|
+
describe 'for an Element', ->
|
12
|
+
|
13
|
+
it 'returns true for the same Element reference', ->
|
14
|
+
div = document.createElement('div')
|
15
|
+
expect(up.util.isEqual(div, div)).toBe(true)
|
16
|
+
|
17
|
+
it 'returns false for a different Element reference', ->
|
18
|
+
div1 = document.createElement('div')
|
19
|
+
div2 = document.createElement('div')
|
20
|
+
expect(up.util.isEqual(div1, div2)).toBe(false)
|
21
|
+
|
22
|
+
it 'returns false for a value this is no Element', ->
|
23
|
+
div = document.createElement('div')
|
24
|
+
expect(up.util.isEqual(div, 'other')).toBe(false)
|
25
|
+
|
26
|
+
describe 'for an Array', ->
|
27
|
+
|
28
|
+
it 'returns true for a different Array reference with the same elements', ->
|
29
|
+
array1 = ['foo', 'bar']
|
30
|
+
array2 = ['foo', 'bar']
|
31
|
+
expect(up.util.isEqual(array1, array2)).toBe(true)
|
32
|
+
|
33
|
+
it 'returns false for an Array with different elements', ->
|
34
|
+
array1 = ['foo', 'bar']
|
35
|
+
array2 = ['foo', 'qux']
|
36
|
+
expect(up.util.isEqual(array1, array2)).toBe(false)
|
37
|
+
|
38
|
+
it 'returns false for an Array that is a suffix', ->
|
39
|
+
array1 = ['foo', 'bar']
|
40
|
+
array2 = [ 'bar']
|
41
|
+
expect(up.util.isEqual(array1, array2)).toBe(false)
|
42
|
+
|
43
|
+
it 'returns false for an Array that is a prefix', ->
|
44
|
+
array1 = ['foo', 'bar']
|
45
|
+
array2 = ['foo' ]
|
46
|
+
expect(up.util.isEqual(array1, array2)).toBe(false)
|
47
|
+
|
48
|
+
it 'returns true for a NodeList with the same elements', ->
|
49
|
+
parent = e.affix(document.body, '.parent')
|
50
|
+
child1 = e.affix(parent, '.child.one')
|
51
|
+
child2 = e.affix(parent, '.child.two')
|
52
|
+
|
53
|
+
array = [child1, child2]
|
54
|
+
nodeList = parent.querySelectorAll('.child')
|
55
|
+
|
56
|
+
expect(up.util.isEqual(array, nodeList)).toBe(true)
|
57
|
+
|
58
|
+
it 'returns true for a HTMLCollection with the same elements', ->
|
59
|
+
parent = e.affix(document.body, '.parent')
|
60
|
+
child1 = e.affix(parent, '.child.one')
|
61
|
+
child2 = e.affix(parent, '.child.two')
|
62
|
+
|
63
|
+
array = [child1, child2]
|
64
|
+
htmlCollection = parent.children
|
65
|
+
|
66
|
+
expect(up.util.isEqual(array, htmlCollection)).toBe(true)
|
67
|
+
|
68
|
+
it 'returns true for an arguments object with the same elements', ->
|
69
|
+
toArguments = -> return arguments
|
70
|
+
array = ['foo', 'bar']
|
71
|
+
args = toArguments('foo', 'bar')
|
72
|
+
|
73
|
+
expect(up.util.isEqual(array, args)).toBe(true)
|
74
|
+
|
75
|
+
it 'returns false for a value that is no Array', ->
|
76
|
+
array = ['foo', 'bar']
|
77
|
+
expect(up.util.isEqual(array, 'foobar')).toBe(false)
|
78
|
+
|
79
|
+
describe 'for a string', ->
|
80
|
+
|
81
|
+
it 'returns true for a different string reference with the same characters', ->
|
82
|
+
string1 = 'bar'
|
83
|
+
string2 = 'bar'
|
84
|
+
expect(up.util.isEqual(string1, string2)).toBe(true)
|
85
|
+
|
86
|
+
it 'returns false for a string with different characters', ->
|
87
|
+
string1 = 'foo'
|
88
|
+
string2 = 'bar'
|
89
|
+
expect(up.util.isEqual(string1, string2)).toBe(false)
|
90
|
+
|
91
|
+
it 'returns true for a String() object with the same characters', ->
|
92
|
+
stringLiteral = 'bar'
|
93
|
+
stringObject = new String('bar')
|
94
|
+
expect(up.util.isEqual(stringLiteral, stringObject)).toBe(true)
|
95
|
+
|
96
|
+
it 'returns false for a String() object with different characters', ->
|
97
|
+
stringLiteral = 'foo'
|
98
|
+
stringObject = new String('bar')
|
99
|
+
expect(up.util.isEqual(stringLiteral, stringObject)).toBe(false)
|
100
|
+
|
101
|
+
it 'returns false for a value that is no string', ->
|
102
|
+
expect(up.util.isEqual('foo', ['foo'])).toBe(false)
|
103
|
+
|
104
|
+
describe 'for a number', ->
|
105
|
+
|
106
|
+
it 'returns true for a different number reference with the same integer value', ->
|
107
|
+
number1 = 123
|
108
|
+
number2 = 123
|
109
|
+
expect(up.util.isEqual(number1, number2)).toBe(true)
|
110
|
+
|
111
|
+
it 'returns true for a different number reference with the same floating point value', ->
|
112
|
+
number1 = 123.4
|
113
|
+
number2 = 123.4
|
114
|
+
expect(up.util.isEqual(number1, number2)).toBe(true)
|
115
|
+
|
116
|
+
it 'returns false for a number with a different value', ->
|
117
|
+
number1 = 123
|
118
|
+
number2 = 124
|
119
|
+
expect(up.util.isEqual(number1, number2)).toBe(false)
|
120
|
+
|
121
|
+
it 'returns true for a Number() object with the same value', ->
|
122
|
+
numberLiteral = 123
|
123
|
+
numberObject = new Number(123)
|
124
|
+
expect(up.util.isEqual(numberLiteral, numberObject)).toBe(true)
|
125
|
+
|
126
|
+
it 'returns false for a Number() object with a different value', ->
|
127
|
+
numberLiteral = 123
|
128
|
+
numberObject = new Object(124)
|
129
|
+
expect(up.util.isEqual(numberLiteral, numberObject)).toBe(false)
|
130
|
+
|
131
|
+
it 'returns false for a value that is no number', ->
|
132
|
+
expect(up.util.isEqual(123, '123')).toBe(false)
|
133
|
+
|
134
|
+
describe 'for undefined', ->
|
135
|
+
|
136
|
+
it 'returns true for undefined', ->
|
137
|
+
expect(up.util.isEqual(undefined, undefined)).toBe(true)
|
138
|
+
|
139
|
+
it 'returns false for null', ->
|
140
|
+
expect(up.util.isEqual(undefined, null)).toBe(false)
|
141
|
+
|
142
|
+
it 'returns false for NaN', ->
|
143
|
+
expect(up.util.isEqual(undefined, NaN)).toBe(false)
|
144
|
+
|
145
|
+
it 'returns false for an empty Object', ->
|
146
|
+
expect(up.util.isEqual(undefined, {})).toBe(false)
|
147
|
+
|
148
|
+
it 'returns false for an empty string', ->
|
149
|
+
expect(up.util.isEqual(undefined, '')).toBe(false)
|
150
|
+
|
151
|
+
describe 'for null', ->
|
152
|
+
|
153
|
+
it 'returns true for null', ->
|
154
|
+
expect(up.util.isEqual(null, null)).toBe(true)
|
155
|
+
|
156
|
+
it 'returns false for undefined', ->
|
157
|
+
expect(up.util.isEqual(null, undefined)).toBe(false)
|
158
|
+
|
159
|
+
it 'returns false for NaN', ->
|
160
|
+
expect(up.util.isEqual(null, NaN)).toBe(false)
|
161
|
+
|
162
|
+
it 'returns false for an empty Object', ->
|
163
|
+
expect(up.util.isEqual(null, {})).toBe(false)
|
164
|
+
|
165
|
+
it 'returns false for an empty string', ->
|
166
|
+
expect(up.util.isEqual(null, '')).toBe(false)
|
167
|
+
|
168
|
+
describe 'for NaN', ->
|
169
|
+
|
170
|
+
it "returns false for NaN because it represents multiple values", ->
|
171
|
+
expect(up.util.isEqual(NaN, NaN)).toBe(false)
|
172
|
+
|
173
|
+
it 'returns false for null', ->
|
174
|
+
expect(up.util.isEqual(NaN, null)).toBe(false)
|
175
|
+
|
176
|
+
it 'returns false for undefined', ->
|
177
|
+
expect(up.util.isEqual(NaN, undefined)).toBe(false)
|
178
|
+
|
179
|
+
it 'returns false for an empty Object', ->
|
180
|
+
expect(up.util.isEqual(NaN, {})).toBe(false)
|
181
|
+
|
182
|
+
it 'returns false for an empty string', ->
|
183
|
+
expect(up.util.isEqual(NaN, '')).toBe(false)
|
184
|
+
|
185
|
+
describe 'for a Date', ->
|
186
|
+
|
187
|
+
it 'returns true for another Date object that points to the same millisecond', ->
|
188
|
+
d1 = new Date('1995-12-17T03:24:00')
|
189
|
+
d2 = new Date('1995-12-17T03:24:00')
|
190
|
+
expect(up.util.isEqual(d1, d2)).toBe(true)
|
191
|
+
|
192
|
+
it 'returns false for another Date object that points to another millisecond', ->
|
193
|
+
d1 = new Date('1995-12-17T03:24:00')
|
194
|
+
d2 = new Date('1995-12-17T03:24:01')
|
195
|
+
expect(up.util.isEqual(d1, d2)).toBe(false)
|
196
|
+
|
197
|
+
it 'returns true for another Date object that points to the same millisecond in another time zone', ->
|
198
|
+
d1 = new Date('2019-01-20T17:35:00+01:00')
|
199
|
+
d2 = new Date('2019-01-20T16:35:00+00:00')
|
200
|
+
expect(up.util.isEqual(d1, d2)).toBe(true)
|
201
|
+
|
202
|
+
it 'returns false for a value that is not a Date', ->
|
203
|
+
d1 = new Date('1995-12-17T03:24:00')
|
204
|
+
d2 = '1995-12-17T03:24:00'
|
205
|
+
expect(up.util.isEqual(d1, d2)).toBe(false)
|
206
|
+
|
207
|
+
describe 'for a plain Object', ->
|
208
|
+
|
209
|
+
it 'returns true for the same reference', ->
|
210
|
+
obj = {}
|
211
|
+
reference = obj
|
212
|
+
expect(up.util.isEqual(obj, reference)).toBe(true)
|
213
|
+
|
214
|
+
it 'returns true for another plain object with the same keys and values', ->
|
215
|
+
obj1 = { foo: 'bar', baz: 'bam' }
|
216
|
+
obj2 = { foo: 'bar', baz: 'bam' }
|
217
|
+
expect(up.util.isEqual(obj1, obj2)).toBe(true)
|
218
|
+
|
219
|
+
it 'returns false for another plain object with the same keys, but different values', ->
|
220
|
+
obj1 = { foo: 'bar', baz: 'bam' }
|
221
|
+
obj2 = { foo: 'bar', baz: 'qux' }
|
222
|
+
expect(up.util.isEqual(obj1, obj2)).toBe(false)
|
223
|
+
|
224
|
+
it 'returns false for another plain object that is missing a key', ->
|
225
|
+
obj1 = { foo: 'bar', baz: 'bam' }
|
226
|
+
obj2 = { foo: 'bar' }
|
227
|
+
expect(up.util.isEqual(obj1, obj2)).toBe(false)
|
228
|
+
|
229
|
+
it 'returns false for another plain object that has an additional key', ->
|
230
|
+
obj1 = { foo: 'bar' }
|
231
|
+
obj2 = { foo: 'bar', baz: 'bam' }
|
232
|
+
expect(up.util.isEqual(obj1, obj2)).toBe(false)
|
233
|
+
|
234
|
+
it 'returns false for a non-plain Object, even if it has the same keys and values', ->
|
235
|
+
class Account
|
236
|
+
constructor: (@email) ->
|
237
|
+
|
238
|
+
accountInstance = new Account('foo@example.com')
|
239
|
+
accountPlain = {}
|
240
|
+
for key, value of accountInstance
|
241
|
+
accountPlain[key] = value
|
242
|
+
expect(up.util.isEqual(accountPlain, accountInstance)).toBe(false)
|
243
|
+
|
244
|
+
it 'returns false for a value that is no object', ->
|
245
|
+
obj = { foo: 'bar' }
|
246
|
+
expect(up.util.isEqual(obj, 'foobar')).toBe(false)
|
247
|
+
|
248
|
+
describe 'for a non-Plain object', ->
|
249
|
+
|
250
|
+
it 'returns true for the same reference', ->
|
251
|
+
obj = new FormData()
|
252
|
+
reference = obj
|
253
|
+
expect(up.util.isEqual(obj, reference)).toBe(true)
|
254
|
+
|
255
|
+
it 'returns false for different references', ->
|
256
|
+
obj1 = new FormData()
|
257
|
+
obj2 = new FormData()
|
258
|
+
expect(up.util.isEqual(obj1, obj2)).toBe(false)
|
259
|
+
|
260
|
+
it 'returns false for a different object with the same keys and values', ->
|
261
|
+
class Account
|
262
|
+
constructor: (@email) ->
|
263
|
+
|
264
|
+
account1 = new Account('foo@example.com')
|
265
|
+
account2 = new Account('bar@example.com')
|
266
|
+
|
267
|
+
expect(up.util.isEqual(account1, account2)).toBe(false)
|
268
|
+
|
269
|
+
it 'allows the object to hook into the comparison protocol by implementing a method called `up.util.isEqual.key`', ->
|
270
|
+
class Account
|
271
|
+
constructor: (@email) ->
|
272
|
+
"#{up.util.isEqual.key}": (other) ->
|
273
|
+
@email == other.email
|
274
|
+
|
275
|
+
account1 = new Account('foo@example.com')
|
276
|
+
account2 = new Account('bar@example.com')
|
277
|
+
account3 = new Account('foo@example.com')
|
278
|
+
|
279
|
+
expect(up.util.isEqual(account1, account2)).toBe(false)
|
280
|
+
expect(up.util.isEqual(account1, account3)).toBe(true)
|
281
|
+
|
282
|
+
it 'returns false for a value that is no object', ->
|
283
|
+
class Account
|
284
|
+
constructor: (@email) ->
|
285
|
+
|
286
|
+
account = new Account('foo@example.com')
|
287
|
+
|
288
|
+
expect(up.util.isEqual(account, 'foo@example.com')).toBe(false)
|
289
|
+
|
290
|
+
describe 'up.util.flatMap', ->
|
291
|
+
|
292
|
+
it 'collects the Array results of the given map function, then concatenates the result arrays into one flat array', ->
|
293
|
+
fun = (x) -> [x, x]
|
294
|
+
result = up.util.flatMap([1, 2, 3], fun)
|
295
|
+
expect(result).toEqual([1, 1, 2, 2, 3, 3])
|
296
|
+
|
297
|
+
it 'builds an array from mixed function return values of scalar values and lists', ->
|
298
|
+
fun = (x) ->
|
299
|
+
if x == 1
|
300
|
+
1
|
301
|
+
else
|
302
|
+
[x, x]
|
303
|
+
|
304
|
+
result = up.util.flatMap([0, 1, 2], fun)
|
305
|
+
expect(result).toEqual [0, 0, 1, 2, 2]
|
306
|
+
|
307
|
+
|
308
|
+
it 'flattens return values that are NodeLists', ->
|
309
|
+
fun = (selector) -> document.querySelectorAll(selector)
|
310
|
+
|
311
|
+
foo1 = $fixture('.foo-element')[0]
|
312
|
+
foo2 = $fixture('.foo-element')[0]
|
313
|
+
bar = $fixture('.bar-element')[0]
|
314
|
+
|
315
|
+
result = up.util.flatMap(['.foo-element', '.bar-element'], fun)
|
316
|
+
|
317
|
+
expect(result).toEqual [foo1, foo2, bar]
|
318
|
+
|
13
319
|
|
14
320
|
describe 'up.util.uniq', ->
|
15
321
|
|
@@ -92,6 +398,58 @@ describe 'up.util', ->
|
|
92
398
|
# path = up.util.parsePath('foo.bar[baz]["bam"][\'qux\']')
|
93
399
|
# expect(path).toEqual ['foo', 'bar', 'baz', 'bam', 'qux']
|
94
400
|
|
401
|
+
describe 'up.util.parseUrl', ->
|
402
|
+
|
403
|
+
it 'parses a full URL', ->
|
404
|
+
url = up.util.parseUrl('https://subdomain.domain.tld:123/path?search#hash')
|
405
|
+
expect(url.protocol).toEqual('https:')
|
406
|
+
expect(url.hostname).toEqual('subdomain.domain.tld')
|
407
|
+
expect(url.port).toEqual('123')
|
408
|
+
expect(url.pathname).toEqual('/path')
|
409
|
+
expect(url.search).toEqual('?search')
|
410
|
+
expect(url.hash).toEqual('#hash')
|
411
|
+
|
412
|
+
it 'parses an absolute path', ->
|
413
|
+
url = up.util.parseUrl('/qux/foo?search#bar')
|
414
|
+
expect(url.protocol).toEqual(location.protocol)
|
415
|
+
expect(url.hostname).toEqual(location.hostname)
|
416
|
+
expect(url.port).toEqual(location.port)
|
417
|
+
expect(url.pathname).toEqual('/qux/foo')
|
418
|
+
expect(url.search).toEqual('?search')
|
419
|
+
expect(url.hash).toEqual('#bar')
|
420
|
+
|
421
|
+
it 'parses a relative path', ->
|
422
|
+
up.history.config.enabled = true
|
423
|
+
up.history.replace('/qux/')
|
424
|
+
url = up.util.parseUrl('foo?search#bar')
|
425
|
+
expect(url.protocol).toEqual(location.protocol)
|
426
|
+
expect(url.hostname).toEqual(location.hostname)
|
427
|
+
expect(url.port).toEqual(location.port)
|
428
|
+
expect(url.pathname).toEqual('/qux/foo')
|
429
|
+
expect(url.search).toEqual('?search')
|
430
|
+
expect(url.hash).toEqual('#bar')
|
431
|
+
|
432
|
+
it 'allows to pass a link element', ->
|
433
|
+
link = document.createElement('a')
|
434
|
+
link.href = '/qux/foo?search#bar'
|
435
|
+
url = up.util.parseUrl(link)
|
436
|
+
expect(url.protocol).toEqual(location.protocol)
|
437
|
+
expect(url.hostname).toEqual(location.hostname)
|
438
|
+
expect(url.port).toEqual(location.port)
|
439
|
+
expect(url.pathname).toEqual('/qux/foo')
|
440
|
+
expect(url.search).toEqual('?search')
|
441
|
+
expect(url.hash).toEqual('#bar')
|
442
|
+
|
443
|
+
it 'allows to pass a link element as a jQuery collection', ->
|
444
|
+
$link = $('<a></a>').attr(href: '/qux/foo?search#bar')
|
445
|
+
url = up.util.parseUrl($link)
|
446
|
+
expect(url.protocol).toEqual(location.protocol)
|
447
|
+
expect(url.hostname).toEqual(location.hostname)
|
448
|
+
expect(url.port).toEqual(location.port)
|
449
|
+
expect(url.pathname).toEqual('/qux/foo')
|
450
|
+
expect(url.search).toEqual('?search')
|
451
|
+
expect(url.hash).toEqual('#bar')
|
452
|
+
|
95
453
|
describe 'up.util.map', ->
|
96
454
|
|
97
455
|
it 'creates a new array of values by calling the given function on each item of the given array', ->
|
@@ -109,9 +467,19 @@ describe 'up.util', ->
|
|
109
467
|
mapped = up.util.map(array, (element, i) -> i)
|
110
468
|
expect(mapped).toEqual [0, 1, 2]
|
111
469
|
|
470
|
+
describe 'up.util.mapObject', ->
|
471
|
+
|
472
|
+
it 'creates an object from the given array and pairer', ->
|
473
|
+
array = ['foo', 'bar', 'baz']
|
474
|
+
object = up.util.mapObject(array, (str) -> ["#{str}Key", "#{str}Value"])
|
475
|
+
expect(object).toEqual
|
476
|
+
fooKey: 'fooValue'
|
477
|
+
barKey: 'barValue'
|
478
|
+
bazKey: 'bazValue'
|
479
|
+
|
112
480
|
describe 'up.util.each', ->
|
113
481
|
|
114
|
-
it 'calls the given function once for each
|
482
|
+
it 'calls the given function once for each item of the given array', ->
|
115
483
|
args = []
|
116
484
|
array = ["apple", "orange", "cucumber"]
|
117
485
|
up.util.each array, (item) -> args.push(item)
|
@@ -123,23 +491,43 @@ describe 'up.util', ->
|
|
123
491
|
up.util.each array, (item, index) -> args.push(index)
|
124
492
|
expect(args).toEqual [0, 1, 2]
|
125
493
|
|
126
|
-
|
494
|
+
it 'iterates over an array-like value', ->
|
495
|
+
one = fixture('.qwertz')
|
496
|
+
two = fixture('.qwertz')
|
497
|
+
nodeList = document.querySelectorAll('.qwertz')
|
498
|
+
|
499
|
+
callback = jasmine.createSpy()
|
500
|
+
|
501
|
+
up.util.each nodeList, callback
|
502
|
+
expect(callback.calls.allArgs()).toEqual [[one, 0], [two, 1]]
|
503
|
+
|
504
|
+
describe 'up.util.filter', ->
|
127
505
|
|
128
506
|
it 'returns an array of those elements in the given array for which the given function returns true', ->
|
129
507
|
array = ["foo", "orange", "cucumber"]
|
130
|
-
results = up.util.
|
508
|
+
results = up.util.filter array, (item) -> item.length > 3
|
131
509
|
expect(results).toEqual ['orange', 'cucumber']
|
132
510
|
|
133
511
|
it 'passes the iteration index as second argument to the given function', ->
|
134
512
|
array = ["apple", "orange", "cucumber", "banana"]
|
135
|
-
results = up.util.
|
513
|
+
results = up.util.filter array, (item, index) -> index % 2 == 0
|
136
514
|
expect(results).toEqual ['apple', 'cucumber']
|
137
515
|
|
138
516
|
it 'accepts a property name instead of a function, which checks that property from each item', ->
|
139
517
|
array = [ { name: 'a', prop: false }, { name: 'b', prop: true } ]
|
140
|
-
results = up.util.
|
518
|
+
results = up.util.filter array, 'prop'
|
141
519
|
expect(results).toEqual [{ name: 'b', prop: true }]
|
142
520
|
|
521
|
+
it 'iterates over an array-like value', ->
|
522
|
+
one = fixture('.qwertz')
|
523
|
+
two = fixture('.qwertz')
|
524
|
+
nodeList = document.querySelectorAll('.qwertz')
|
525
|
+
|
526
|
+
callback = jasmine.createSpy()
|
527
|
+
|
528
|
+
up.util.filter nodeList, callback
|
529
|
+
expect(callback.calls.allArgs()).toEqual [[one, 0], [two, 1]]
|
530
|
+
|
143
531
|
describe 'up.util.reject', ->
|
144
532
|
|
145
533
|
it 'returns an array of those elements in the given array for which the given function returns false', ->
|
@@ -157,6 +545,16 @@ describe 'up.util', ->
|
|
157
545
|
results = up.util.reject array, 'prop'
|
158
546
|
expect(results).toEqual [{ name: 'a', prop: false }]
|
159
547
|
|
548
|
+
it 'iterates over an array-like value', ->
|
549
|
+
one = fixture('.qwertz')
|
550
|
+
two = fixture('.qwertz')
|
551
|
+
nodeList = document.querySelectorAll('.qwertz')
|
552
|
+
|
553
|
+
callback = jasmine.createSpy()
|
554
|
+
|
555
|
+
up.util.reject nodeList, callback
|
556
|
+
expect(callback.calls.allArgs()).toEqual [[one, 0], [two, 1]]
|
557
|
+
|
160
558
|
describe 'up.util.previewable', ->
|
161
559
|
|
162
560
|
it 'wraps a function into a proxy function with an additional .promise attribute', ->
|
@@ -171,10 +569,10 @@ describe 'up.util', ->
|
|
171
569
|
proxy = up.util.previewable(fun)
|
172
570
|
callback = jasmine.createSpy('promise callback')
|
173
571
|
proxy.promise.then(callback)
|
174
|
-
u.
|
572
|
+
u.task ->
|
175
573
|
expect(callback).not.toHaveBeenCalled()
|
176
574
|
proxy()
|
177
|
-
u.
|
575
|
+
u.task ->
|
178
576
|
expect(callback).toHaveBeenCalledWith('return value')
|
179
577
|
done()
|
180
578
|
|
@@ -185,141 +583,13 @@ describe 'up.util', ->
|
|
185
583
|
callback = jasmine.createSpy('promise callback')
|
186
584
|
proxy.promise.then(callback)
|
187
585
|
proxy()
|
188
|
-
u.
|
586
|
+
u.task ->
|
189
587
|
expect(callback).not.toHaveBeenCalled()
|
190
588
|
funDeferred.resolve('return value')
|
191
|
-
u.
|
589
|
+
u.task ->
|
192
590
|
expect(callback).toHaveBeenCalledWith('return value')
|
193
591
|
done()
|
194
592
|
|
195
|
-
describe 'up.util.kebabCase', ->
|
196
|
-
|
197
|
-
it 'converts a string of multiple words from camel-case to kebap-case', ->
|
198
|
-
result = up.util.kebabCase('fooBarBaz')
|
199
|
-
expect(result).toEqual('foo-bar-baz')
|
200
|
-
|
201
|
-
it 'does not change a single word', ->
|
202
|
-
result = up.util.kebabCase('foo')
|
203
|
-
expect(result).toEqual('foo')
|
204
|
-
|
205
|
-
it 'downcases the first word when it starts with a capital letter', ->
|
206
|
-
result = up.util.kebabCase('FooBar')
|
207
|
-
expect(result).toEqual('foo-bar')
|
208
|
-
|
209
|
-
it 'does not change a string that is already in kebab-case', ->
|
210
|
-
result = up.util.kebabCase('foo-bar-baz')
|
211
|
-
expect(result).toEqual('foo-bar-baz')
|
212
|
-
|
213
|
-
describe 'up.util.camelCase', ->
|
214
|
-
|
215
|
-
it 'converts a string of multiple words from kebap-case to camel-case', ->
|
216
|
-
result = up.util.camelCase('foo-bar-baz')
|
217
|
-
expect(result).toEqual('fooBarBaz')
|
218
|
-
|
219
|
-
it 'does not change a single word', ->
|
220
|
-
result = up.util.camelCase('foo')
|
221
|
-
expect(result).toEqual('foo')
|
222
|
-
|
223
|
-
it 'downcases the first word when it starts with a capital letter', ->
|
224
|
-
result = up.util.camelCase('Foo-Bar')
|
225
|
-
expect(result).toEqual('fooBar')
|
226
|
-
|
227
|
-
it 'does not change a string that is already in camel-case', ->
|
228
|
-
result = up.util.camelCase('fooBarBaz')
|
229
|
-
expect(result).toEqual('fooBarBaz')
|
230
|
-
|
231
|
-
describe 'up.util.kebabCaseKeys', ->
|
232
|
-
|
233
|
-
it "converts the given object's keys from camel-case to kebab-case", ->
|
234
|
-
input =
|
235
|
-
fooBar: 'one'
|
236
|
-
barBaz: 'two'
|
237
|
-
result = up.util.kebabCaseKeys(input)
|
238
|
-
expect(result).toEqual
|
239
|
-
'foo-bar': 'one'
|
240
|
-
'bar-baz': 'two'
|
241
|
-
|
242
|
-
it "does not change an object whose keys are already kebab-case", ->
|
243
|
-
input =
|
244
|
-
'foo-bar': 'one'
|
245
|
-
'bar-baz': 'two'
|
246
|
-
result = up.util.kebabCaseKeys(input)
|
247
|
-
expect(result).toEqual
|
248
|
-
'foo-bar': 'one'
|
249
|
-
'bar-baz': 'two'
|
250
|
-
|
251
|
-
describe 'up.util.camelCaseKeys', ->
|
252
|
-
|
253
|
-
it "converts the given object's keys from kebab-case to camel-case", ->
|
254
|
-
input =
|
255
|
-
'foo-bar': 'one'
|
256
|
-
'bar-baz': 'two'
|
257
|
-
result = up.util.camelCaseKeys(input)
|
258
|
-
expect(result).toEqual
|
259
|
-
fooBar: 'one'
|
260
|
-
barBaz: 'two'
|
261
|
-
|
262
|
-
it "does not change an object whose keys are already camel-case", ->
|
263
|
-
input =
|
264
|
-
fooBar: 'one'
|
265
|
-
barBaz: 'two'
|
266
|
-
result = up.util.camelCaseKeys(input)
|
267
|
-
expect(result).toEqual
|
268
|
-
fooBar: 'one'
|
269
|
-
barBaz: 'two'
|
270
|
-
|
271
|
-
# describe 'up.util.lowerCaseKeys', ->
|
272
|
-
#
|
273
|
-
# it "returns a copy of the given object will all keys in lower case", ->
|
274
|
-
# input =
|
275
|
-
# 'A-B': 'C-D'
|
276
|
-
# 'E-F': 'G-H'
|
277
|
-
# result = up.util.lowerCaseKeys(input)
|
278
|
-
# expect(result).toEqual
|
279
|
-
# 'a-b': 'C-D'
|
280
|
-
# 'e-f': 'G-H'
|
281
|
-
|
282
|
-
describe 'up.util.DivertibleChain', ->
|
283
|
-
|
284
|
-
it "instantiates a task queue whose (2..n)th tasks can be changed by calling '.asap'", (done) ->
|
285
|
-
chain = new up.util.DivertibleChain()
|
286
|
-
|
287
|
-
timer1Spy = jasmine.createSpy('timer1 has been called')
|
288
|
-
timer1 = ->
|
289
|
-
timer1Spy()
|
290
|
-
u.promiseTimer(50)
|
291
|
-
|
292
|
-
timer2Spy = jasmine.createSpy('timer2 has been called')
|
293
|
-
timer2 = ->
|
294
|
-
timer2Spy()
|
295
|
-
u.promiseTimer(50)
|
296
|
-
|
297
|
-
timer3Spy = jasmine.createSpy('timer3 has been called')
|
298
|
-
timer3 = ->
|
299
|
-
timer3Spy()
|
300
|
-
u.promiseTimer(50)
|
301
|
-
|
302
|
-
timer4Spy = jasmine.createSpy('timer4 has been called')
|
303
|
-
timer4 = ->
|
304
|
-
timer4Spy()
|
305
|
-
u.promiseTimer(50)
|
306
|
-
|
307
|
-
chain.asap(timer1)
|
308
|
-
u.nextFrame ->
|
309
|
-
expect(timer1Spy).toHaveBeenCalled()
|
310
|
-
chain.asap(timer2)
|
311
|
-
u.nextFrame ->
|
312
|
-
# timer2 is still waiting for timer1 to finish
|
313
|
-
expect(timer2Spy).not.toHaveBeenCalled()
|
314
|
-
# Override the (2..n)th tasks. This unschedules timer2.
|
315
|
-
chain.asap(timer3, timer4)
|
316
|
-
u.setTimer 80, ->
|
317
|
-
expect(timer2Spy).not.toHaveBeenCalled()
|
318
|
-
expect(timer3Spy).toHaveBeenCalled()
|
319
|
-
u.setTimer 70, ->
|
320
|
-
expect(timer4Spy).toHaveBeenCalled()
|
321
|
-
done()
|
322
|
-
|
323
593
|
describe 'up.util.sequence', ->
|
324
594
|
|
325
595
|
it 'combines the given functions into a single function', ->
|
@@ -327,128 +597,52 @@ describe 'up.util', ->
|
|
327
597
|
one = -> values.push('one')
|
328
598
|
two = -> values.push('two')
|
329
599
|
three = -> values.push('three')
|
330
|
-
sequence = up.util.sequence(one, two, three)
|
600
|
+
sequence = up.util.sequence([one, two, three])
|
331
601
|
expect(values).toEqual([])
|
332
602
|
sequence()
|
333
603
|
expect(values).toEqual(['one', 'two', 'three'])
|
334
604
|
|
335
|
-
describe 'up.util.
|
336
|
-
|
337
|
-
it '
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
expect($element.querySelector('body div').textContent).toEqual('line')
|
370
|
-
|
371
|
-
it 'does not run forever if a page has a <head> without a <title> (bugfix)', ->
|
372
|
-
html = """
|
373
|
-
<!doctype html>
|
374
|
-
<html>
|
375
|
-
<head>
|
376
|
-
<meta charset="utf-8" />
|
377
|
-
<meta name="format-detection" content="telephone=no">
|
378
|
-
<link href='/images/favicon.png' rel='shortcut icon' type='image/png'>
|
379
|
-
<meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'>
|
380
|
-
|
381
|
-
<base href="/examples/update-fragment/" />
|
382
|
-
<link href='http://fonts.googleapis.com/css?family=Orbitron:400|Ubuntu+Mono:400,700|Source+Sans+Pro:300,400,700,400italic,700italic' rel='stylesheet' type='text/css'>
|
383
|
-
<link href="//netdna.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet">
|
384
|
-
<link href="/stylesheets/example/all.css" rel="stylesheet" />
|
385
|
-
<script src="/javascripts/example.js"></script>
|
386
|
-
</head>
|
387
|
-
<body>
|
388
|
-
<div class="page">
|
389
|
-
<div class="story">
|
390
|
-
|
391
|
-
<h1>Full story</h1>
|
392
|
-
<p>Lorem ipsum dolor sit amet.</p>
|
393
|
-
|
394
|
-
<a href="preview.html" up-target=".story">
|
395
|
-
Read summary
|
396
|
-
</a>
|
397
|
-
</div>
|
398
|
-
|
399
|
-
</div>
|
400
|
-
</body>
|
401
|
-
</html>
|
402
|
-
"""
|
403
|
-
element = up.util.createElementFromHtml(html)
|
404
|
-
expect(element.querySelector("title")).toBeMissing()
|
405
|
-
expect(element.querySelector("h1").textContent).toEqual('Full story')
|
406
|
-
|
407
|
-
it 'can parse HTML without a <head>', ->
|
408
|
-
html = """
|
409
|
-
<html>
|
410
|
-
<body>
|
411
|
-
<h1>Full story</h1>
|
412
|
-
</body>
|
413
|
-
</html>
|
414
|
-
"""
|
415
|
-
element = up.util.createElementFromHtml(html)
|
416
|
-
expect(element.querySelector("title")).toBeMissing()
|
417
|
-
expect(element.querySelector("h1").textContent).toEqual('Full story')
|
418
|
-
|
419
|
-
it 'can parse a HTML fragment without a <body>', ->
|
420
|
-
html = """
|
421
|
-
<h1>Full story</h1>
|
422
|
-
"""
|
423
|
-
element = up.util.createElementFromHtml(html)
|
424
|
-
expect(element.querySelector("title")).toBeMissing()
|
425
|
-
expect(element.querySelector("h1").textContent).toEqual('Full story')
|
426
|
-
|
427
|
-
describe 'up.util.isFixed', ->
|
428
|
-
|
429
|
-
it 'returns true if the given element or one of its ancestors has a "fixed" CSS position', ->
|
430
|
-
$grandGrandParent = affix('.grand-parent')
|
431
|
-
$grandParent = $grandGrandParent.affix('.grand-parent')
|
432
|
-
$parent = $grandParent.affix('.parent')
|
433
|
-
$child = $parent.affix('.child')
|
434
|
-
$grandParent.css(position: 'fixed')
|
435
|
-
expect(up.util.isFixed($child)).toBe(true)
|
436
|
-
expect(up.util.isFixed($parent)).toBe(true)
|
437
|
-
expect(up.util.isFixed($grandParent)).toBe(true)
|
438
|
-
expect(up.util.isFixed($grandGrandParent)).toBe(false)
|
439
|
-
|
440
|
-
it 'returns false if the given element and its ancestors all have a non-"fixed" CSS position', ->
|
441
|
-
$element = affix('.element')
|
442
|
-
expect(up.util.isFixed($element)).toBe(false)
|
443
|
-
|
444
|
-
describe 'up.util.setTimer', ->
|
605
|
+
describe 'up.util.muteRejection', ->
|
606
|
+
|
607
|
+
it 'returns a promise that fulfills when the given promise fulfills', (done) ->
|
608
|
+
fulfilledPromise = Promise.resolve()
|
609
|
+
mutedPromise = up.util.muteRejection(fulfilledPromise)
|
610
|
+
|
611
|
+
u.task ->
|
612
|
+
promiseState(mutedPromise).then (result) ->
|
613
|
+
expect(result.state).toEqual('fulfilled')
|
614
|
+
done()
|
615
|
+
|
616
|
+
it 'returns a promise that fulfills when the given promise rejects', (done) ->
|
617
|
+
rejectedPromise = Promise.reject()
|
618
|
+
mutedPromise = up.util.muteRejection(rejectedPromise)
|
619
|
+
|
620
|
+
u.task ->
|
621
|
+
promiseState(mutedPromise).then (result) ->
|
622
|
+
expect(result.state).toEqual('fulfilled')
|
623
|
+
done()
|
624
|
+
|
625
|
+
describe 'up.util.simpleEase', ->
|
626
|
+
|
627
|
+
it 'returns 0 for 0', ->
|
628
|
+
expect(up.util.simpleEase(0)).toBe(0)
|
629
|
+
|
630
|
+
it 'returns 1 for 1', ->
|
631
|
+
expect(up.util.simpleEase(1)).toBe(1)
|
632
|
+
|
633
|
+
it 'returns steadily increasing values between 0 and 1', ->
|
634
|
+
expect(up.util.simpleEase(0.25)).toBeAround(0.25, 0.2)
|
635
|
+
expect(up.util.simpleEase(0.50)).toBeAround(0.50, 0.2)
|
636
|
+
expect(up.util.simpleEase(0.75)).toBeAround(0.75, 0.2)
|
637
|
+
|
638
|
+
describe 'up.util.timer', ->
|
445
639
|
|
446
640
|
it 'calls the given function after waiting the given milliseconds', (done) ->
|
447
641
|
callback = jasmine.createSpy()
|
448
642
|
expectNotCalled = -> expect(callback).not.toHaveBeenCalled()
|
449
643
|
expectCalled = -> expect(callback).toHaveBeenCalled()
|
450
644
|
|
451
|
-
up.util.
|
645
|
+
up.util.timer(100, callback)
|
452
646
|
|
453
647
|
expectNotCalled()
|
454
648
|
setTimeout(expectNotCalled, 50)
|
@@ -459,7 +653,7 @@ describe 'up.util', ->
|
|
459
653
|
|
460
654
|
it 'calls the given function in the next execution frame', ->
|
461
655
|
callback = jasmine.createSpy()
|
462
|
-
up.util.
|
656
|
+
up.util.timer(0, callback)
|
463
657
|
expect(callback).not.toHaveBeenCalled()
|
464
658
|
|
465
659
|
setTimeout((-> expect(callback).toHaveBeenCalled()), 0)
|
@@ -470,12 +664,6 @@ describe 'up.util', ->
|
|
470
664
|
# fun = ($element, data) ->
|
471
665
|
# expect(up.util.argNames(fun)).toEqual(['$element', 'data'])
|
472
666
|
|
473
|
-
describe 'up.util.trim', ->
|
474
|
-
|
475
|
-
it 'removes leading and trailing whitespace from the given string', ->
|
476
|
-
string = "\t\n\r abc \r\n\t"
|
477
|
-
expect(up.util.trim(string)).toEqual('abc')
|
478
|
-
|
479
667
|
describe 'up.util.only', ->
|
480
668
|
|
481
669
|
it 'returns a copy of the given object with only the given whitelisted properties', ->
|
@@ -502,91 +690,6 @@ describe 'up.util', ->
|
|
502
690
|
expect(whitelisted).toHaveOwnProperty('foo')
|
503
691
|
expect(whitelisted).not.toHaveOwnProperty('bar')
|
504
692
|
|
505
|
-
describe 'up.util.readInlineStyle', ->
|
506
|
-
|
507
|
-
describe 'with a string as second argument', ->
|
508
|
-
|
509
|
-
it 'returns a CSS value string from an inline [style] attribute', ->
|
510
|
-
$div = affix('div').attr('style', 'background-color: #ff0000')
|
511
|
-
style = up.util.readInlineStyle($div, 'backgroundColor')
|
512
|
-
# Browsers convert colors to rgb() values, even IE11
|
513
|
-
expect(style).toEqual('rgb(255, 0, 0)')
|
514
|
-
|
515
|
-
it 'returns a blank value if the element does not have the given property in the [style] attribute', ->
|
516
|
-
$div = affix('div').attr('style', 'background-color: red')
|
517
|
-
style = up.util.readInlineStyle($div, 'color')
|
518
|
-
expect(style).toBeBlank()
|
519
|
-
|
520
|
-
it 'returns a blank value the given property is a computed property, but not in the [style] attribute', ->
|
521
|
-
$div = affix('div[class="red-background"]')
|
522
|
-
inlineStyle = up.util.readInlineStyle($div, 'backgroundColor')
|
523
|
-
computedStyle = up.util.readComputedStyle($div, 'backgroundColor')
|
524
|
-
expect(computedStyle).toEqual('rgb(255, 0, 0)')
|
525
|
-
expect(inlineStyle).toBeBlank()
|
526
|
-
|
527
|
-
describe 'with an array as second argument', ->
|
528
|
-
|
529
|
-
it 'returns an object with the given inline [style] properties', ->
|
530
|
-
$div = affix('div').attr('style', 'background-color: #ff0000; color: #0000ff')
|
531
|
-
style = up.util.readInlineStyle($div, ['backgroundColor', 'color'])
|
532
|
-
expect(style).toEqual
|
533
|
-
backgroundColor: 'rgb(255, 0, 0)'
|
534
|
-
color: 'rgb(0, 0, 255)'
|
535
|
-
|
536
|
-
it 'returns blank keys if the element does not have the given property in the [style] attribute', ->
|
537
|
-
$div = affix('div').attr('style', 'background-color: #ff0000')
|
538
|
-
style = up.util.readInlineStyle($div, ['backgroundColor', 'color'])
|
539
|
-
expect(style).toHaveOwnProperty('color')
|
540
|
-
expect(style.color).toBeBlank()
|
541
|
-
|
542
|
-
it 'returns a blank value the given property is a computed property, but not in the [style] attribute', ->
|
543
|
-
$div = affix('div[class="red-background"]')
|
544
|
-
inlineStyleHash = up.util.readInlineStyle($div, ['backgroundColor'])
|
545
|
-
computedBackground = up.util.readComputedStyle($div, 'backgroundColor')
|
546
|
-
expect(computedBackground).toEqual('rgb(255, 0, 0)')
|
547
|
-
expect(inlineStyleHash).toHaveOwnProperty('backgroundColor')
|
548
|
-
expect(inlineStyleHash.backgroundColor).toBeBlank()
|
549
|
-
|
550
|
-
describe 'up.util.writeInlineStyle', ->
|
551
|
-
|
552
|
-
it "sets the given style properties as the given element's [style] attribute", ->
|
553
|
-
$div = affix('div')
|
554
|
-
up.util.writeInlineStyle($div, { color: 'red', backgroundColor: 'blue' })
|
555
|
-
style = $div.attr('style')
|
556
|
-
expect(style).toContain('color: red')
|
557
|
-
expect(style).toContain('background-color: blue')
|
558
|
-
|
559
|
-
it "merges the given style properties into the given element's existing [style] value", ->
|
560
|
-
$div = affix('div[style="color: red"]')
|
561
|
-
up.util.writeInlineStyle($div, { backgroundColor: 'blue' })
|
562
|
-
style = $div.attr('style')
|
563
|
-
expect(style).toContain('color: red')
|
564
|
-
expect(style).toContain('background-color: blue')
|
565
|
-
|
566
|
-
it "converts the values of known length properties to px values automatically", ->
|
567
|
-
$div = affix('div')
|
568
|
-
up.util.writeInlineStyle($div, { paddingTop: 100 })
|
569
|
-
style = $div.attr('style')
|
570
|
-
expect(style).toContain('padding-top: 100px')
|
571
|
-
|
572
|
-
describe 'up.util.writeTemporaryStyle', ->
|
573
|
-
|
574
|
-
it "sets the given inline styles and returns a function that will restore the previous inline styles", ->
|
575
|
-
$div = affix('div[style="color: red"]')
|
576
|
-
restore = up.util.writeTemporaryStyle($div, { color: 'blue' })
|
577
|
-
expect($div.attr('style')).toContain('color: blue')
|
578
|
-
expect($div.attr('style')).not.toContain('color: red')
|
579
|
-
restore()
|
580
|
-
expect($div.attr('style')).not.toContain('color: blue')
|
581
|
-
expect($div.attr('style')).toContain('color: red')
|
582
|
-
|
583
|
-
it "does not restore inherited styles", ->
|
584
|
-
$div = affix('div[class="red-background"]')
|
585
|
-
restore = up.util.writeTemporaryStyle($div, { backgroundColor: 'blue' })
|
586
|
-
expect($div.attr('style')).toContain('background-color: blue')
|
587
|
-
restore()
|
588
|
-
expect($div.attr('style')).not.toContain('background-color')
|
589
|
-
|
590
693
|
describe 'up.util.except', ->
|
591
694
|
|
592
695
|
it 'returns a copy of the given object but omits the given blacklisted properties', ->
|
@@ -606,133 +709,19 @@ describe 'up.util', ->
|
|
606
709
|
baz: 'baz-value'
|
607
710
|
bam: 'bam-value'
|
608
711
|
|
609
|
-
describe 'up.util.
|
610
|
-
|
611
|
-
it "prefers using the element's 'up-id' attribute to using the element's ID", ->
|
612
|
-
$element = affix('div[up-id=up-id-value]#id-value')
|
613
|
-
expect(up.util.selectorForElement($element)).toBe('[up-id="up-id-value"]')
|
614
|
-
|
615
|
-
it "prefers using the element's ID to using the element's name", ->
|
616
|
-
$element = affix('div#id-value[name=name-value]')
|
617
|
-
expect(up.util.selectorForElement($element)).toBe("#id-value")
|
618
|
-
|
619
|
-
it "selects the ID with an attribute selector if the ID contains a slash", ->
|
620
|
-
$element = affix('div').attr(id: 'foo/bar')
|
621
|
-
expect(up.util.selectorForElement($element)).toBe('[id="foo/bar"]')
|
622
|
-
|
623
|
-
it "selects the ID with an attribute selector if the ID contains a space", ->
|
624
|
-
$element = affix('div').attr(id: 'foo bar')
|
625
|
-
expect(up.util.selectorForElement($element)).toBe('[id="foo bar"]')
|
626
|
-
|
627
|
-
it "selects the ID with an attribute selector if the ID contains a dot", ->
|
628
|
-
$element = affix('div').attr(id: 'foo.bar')
|
629
|
-
expect(up.util.selectorForElement($element)).toBe('[id="foo.bar"]')
|
630
|
-
|
631
|
-
it "selects the ID with an attribute selector if the ID contains a quote", ->
|
632
|
-
$element = affix('div').attr(id: 'foo"bar')
|
633
|
-
expect(up.util.selectorForElement($element)).toBe('[id="foo\\"bar"]')
|
634
|
-
|
635
|
-
it "prefers using the element's tagName + [name] to using the element's classes", ->
|
636
|
-
$element = affix('input[name=name-value].class1.class2')
|
637
|
-
expect(up.util.selectorForElement($element)).toBe('input[name="name-value"]')
|
638
|
-
|
639
|
-
it "prefers using the element's classes to using the element's ARIA label", ->
|
640
|
-
$element = affix('div.class1.class2[aria-label="ARIA label value"]')
|
641
|
-
expect(up.util.selectorForElement($element)).toBe(".class1.class2")
|
642
|
-
|
643
|
-
it 'does not use Unpoly classes to compose a class selector', ->
|
644
|
-
$element = affix('div.class1.up-current.class2')
|
645
|
-
expect(up.util.selectorForElement($element)).toBe(".class1.class2")
|
646
|
-
|
647
|
-
it "prefers using the element's ARIA label to using the element's tag name", ->
|
648
|
-
$element = affix('div[aria-label="ARIA label value"]')
|
649
|
-
expect(up.util.selectorForElement($element)).toBe('[aria-label="ARIA label value"]')
|
650
|
-
|
651
|
-
it "uses the element's tag name if no better description is available", ->
|
652
|
-
$element = affix('div')
|
653
|
-
expect(up.util.selectorForElement($element)).toBe("div")
|
654
|
-
|
655
|
-
it 'escapes quotes in attribute selector values', ->
|
656
|
-
$element = affix('div')
|
657
|
-
$element.attr('aria-label', 'foo"bar')
|
658
|
-
expect(up.util.selectorForElement($element)).toBe('[aria-label="foo\\"bar"]')
|
659
|
-
|
660
|
-
|
661
|
-
describe 'up.util.addTemporaryClass', ->
|
662
|
-
|
663
|
-
it 'adds the given class to the given element', ->
|
664
|
-
$element = affix('.foo.bar')
|
665
|
-
element = $element.get(0)
|
666
|
-
|
667
|
-
expect(element.className).toEqual('foo bar')
|
668
|
-
|
669
|
-
up.util.addTemporaryClass(element, 'baz')
|
670
|
-
|
671
|
-
expect(element.className).toEqual('foo bar baz')
|
672
|
-
|
673
|
-
it 'returns a function that restores the original class', ->
|
674
|
-
$element = affix('.foo.bar')
|
675
|
-
element = $element.get(0)
|
676
|
-
|
677
|
-
restoreClass = up.util.addTemporaryClass(element, 'baz')
|
678
|
-
expect(element.className).toEqual('foo bar baz')
|
679
|
-
|
680
|
-
restoreClass()
|
681
|
-
expect(element.className).toEqual('foo bar')
|
682
|
-
|
683
|
-
|
684
|
-
describe 'up.util.castedAttr', ->
|
685
|
-
|
686
|
-
it 'returns true if the attribute value is the string "true"', ->
|
687
|
-
$element = affix('div').attr('foo', 'true')
|
688
|
-
expect(up.util.castedAttr($element, 'foo')).toBe(true)
|
689
|
-
|
690
|
-
it 'returns true if the attribute value is the name of the attribute', ->
|
691
|
-
$element = affix('div').attr('foo', 'foo')
|
692
|
-
expect(up.util.castedAttr($element, 'foo')).toBe(true)
|
693
|
-
|
694
|
-
it 'returns false if the attribute value is the string "false"', ->
|
695
|
-
$element = affix('div').attr('foo', 'false')
|
696
|
-
expect(up.util.castedAttr($element, 'foo')).toBe(false)
|
697
|
-
|
698
|
-
it 'returns undefined if the element has no such attribute', ->
|
699
|
-
$element = affix('div')
|
700
|
-
expect(up.util.castedAttr($element, 'foo')).toBe(undefined)
|
701
|
-
|
702
|
-
it 'returns the attribute value unchanged if the value is some string', ->
|
703
|
-
$element = affix('div').attr('foo', 'some text')
|
704
|
-
expect(up.util.castedAttr($element, 'foo')).toBe('some text')
|
705
|
-
|
706
|
-
describe 'up.util.any', ->
|
707
|
-
|
708
|
-
it 'returns true if an element in the array returns true for the given function', ->
|
709
|
-
result = up.util.any [null, undefined, 'foo', ''], up.util.isPresent
|
710
|
-
expect(result).toBe(true)
|
711
|
-
|
712
|
-
it 'returns false if no element in the array returns true for the given function', ->
|
713
|
-
result = up.util.any [null, undefined, ''], up.util.isPresent
|
714
|
-
expect(result).toBe(false)
|
715
|
-
|
716
|
-
it 'short-circuits once an element returns true', ->
|
717
|
-
count = 0
|
718
|
-
up.util.any [null, undefined, 'foo', ''], (element) ->
|
719
|
-
count += 1
|
720
|
-
up.util.isPresent(element)
|
721
|
-
expect(count).toBe(3)
|
722
|
-
|
723
|
-
describe 'up.util.all', ->
|
712
|
+
describe 'up.util.every', ->
|
724
713
|
|
725
714
|
it 'returns true if all element in the array returns true for the given function', ->
|
726
|
-
result = up.util.
|
715
|
+
result = up.util.every ['foo', 'bar', 'baz'], up.util.isPresent
|
727
716
|
expect(result).toBe(true)
|
728
717
|
|
729
718
|
it 'returns false if an element in the array returns false for the given function', ->
|
730
|
-
result = up.util.
|
719
|
+
result = up.util.every ['foo', 'bar', null, 'baz'], up.util.isPresent
|
731
720
|
expect(result).toBe(false)
|
732
721
|
|
733
722
|
it 'short-circuits once an element returns false', ->
|
734
723
|
count = 0
|
735
|
-
up.util.
|
724
|
+
up.util.every ['foo', 'bar', '', 'baz'], (element) ->
|
736
725
|
count += 1
|
737
726
|
up.util.isPresent(element)
|
738
727
|
expect(count).toBe(3)
|
@@ -740,7 +729,7 @@ describe 'up.util', ->
|
|
740
729
|
it 'passes the iteration index as second argument to the given function', ->
|
741
730
|
array = ["apple", "orange", "cucumber"]
|
742
731
|
args = []
|
743
|
-
up.util.
|
732
|
+
up.util.every array, (item, index) ->
|
744
733
|
args.push(index)
|
745
734
|
true
|
746
735
|
expect(args).toEqual [0, 1, 2]
|
@@ -748,8 +737,8 @@ describe 'up.util', ->
|
|
748
737
|
it 'accepts a property name instead of a function, which collects that property from each item', ->
|
749
738
|
allTrue = [ { prop: true }, { prop: true } ]
|
750
739
|
someFalse = [ { prop: true }, { prop: false } ]
|
751
|
-
expect(up.util.
|
752
|
-
expect(up.util.
|
740
|
+
expect(up.util.every(allTrue, 'prop')).toBe(true)
|
741
|
+
expect(up.util.every(someFalse, 'prop')).toBe(false)
|
753
742
|
|
754
743
|
# describe 'up.util.none', ->
|
755
744
|
#
|
@@ -782,20 +771,20 @@ describe 'up.util', ->
|
|
782
771
|
# expect(up.util.none(allFalse, 'prop')).toBe(true)
|
783
772
|
# expect(up.util.none(someTrue, 'prop')).toBe(false)
|
784
773
|
|
785
|
-
describe 'up.util.
|
774
|
+
describe 'up.util.some', ->
|
786
775
|
|
787
776
|
it 'returns true if at least one element in the array returns true for the given function', ->
|
788
|
-
result = up.util.
|
777
|
+
result = up.util.some ['', 'bar', null], up.util.isPresent
|
789
778
|
expect(result).toBe(true)
|
790
779
|
|
791
780
|
it 'returns false if no element in the array returns true for the given function', ->
|
792
|
-
result = up.util.
|
781
|
+
result = up.util.some ['', null, undefined], up.util.isPresent
|
793
782
|
expect(result).toBe(false)
|
794
783
|
|
795
784
|
it 'passes the iteration index as second argument to the given function', ->
|
796
785
|
array = ["apple", "orange", "cucumber"]
|
797
786
|
args = []
|
798
|
-
up.util.
|
787
|
+
up.util.some array, (item, index) ->
|
799
788
|
args.push(index)
|
800
789
|
false
|
801
790
|
expect(args).toEqual [0, 1, 2]
|
@@ -803,8 +792,57 @@ describe 'up.util', ->
|
|
803
792
|
it 'accepts a property name instead of a function, which collects that property from each item', ->
|
804
793
|
someTrue = [ { prop: true }, { prop: false } ]
|
805
794
|
allFalse = [ { prop: false }, { prop: false } ]
|
806
|
-
expect(up.util.
|
807
|
-
expect(up.util.
|
795
|
+
expect(up.util.some(someTrue, 'prop')).toBe(true)
|
796
|
+
expect(up.util.some(allFalse, 'prop')).toBe(false)
|
797
|
+
|
798
|
+
it 'short-circuits once an element returns true', ->
|
799
|
+
count = 0
|
800
|
+
up.util.some [null, undefined, 'foo', ''], (element) ->
|
801
|
+
count += 1
|
802
|
+
up.util.isPresent(element)
|
803
|
+
expect(count).toBe(3)
|
804
|
+
|
805
|
+
it 'iterates over an array-like value', ->
|
806
|
+
one = fixture('.qwertz')
|
807
|
+
two = fixture('.qwertz')
|
808
|
+
nodeList = document.querySelectorAll('.qwertz')
|
809
|
+
|
810
|
+
callback = jasmine.createSpy()
|
811
|
+
|
812
|
+
up.util.some nodeList, callback
|
813
|
+
expect(callback.calls.allArgs()).toEqual [[one, 0], [two, 1]]
|
814
|
+
|
815
|
+
describe 'up.util.findResult', ->
|
816
|
+
|
817
|
+
it 'consecutively applies the function to each array element and returns the first truthy return value', ->
|
818
|
+
map = {
|
819
|
+
a: '',
|
820
|
+
b: null,
|
821
|
+
c: undefined,
|
822
|
+
d: 'DEH',
|
823
|
+
e: 'EH'
|
824
|
+
}
|
825
|
+
fn = (el) -> map[el]
|
826
|
+
|
827
|
+
result = up.util.findResult ['a', 'b', 'c', 'd', 'e'], fn
|
828
|
+
expect(result).toEqual('DEH')
|
829
|
+
|
830
|
+
it 'returns undefined if the function does not return a truthy value for any element in the array', ->
|
831
|
+
map = {}
|
832
|
+
fn = (el) -> map[el]
|
833
|
+
|
834
|
+
result = up.util.findResult ['a', 'b', 'c'], fn
|
835
|
+
expect(result).toBeUndefined()
|
836
|
+
|
837
|
+
it 'iterates over an array-like value', ->
|
838
|
+
one = fixture('.qwertz')
|
839
|
+
two = fixture('.qwertz')
|
840
|
+
nodeList = document.querySelectorAll('.qwertz')
|
841
|
+
|
842
|
+
callback = jasmine.createSpy()
|
843
|
+
|
844
|
+
up.util.findResult nodeList, callback
|
845
|
+
expect(callback.calls.allArgs()).toEqual [[one, 0], [two, 1]]
|
808
846
|
|
809
847
|
describe 'up.util.isBlank', ->
|
810
848
|
|
@@ -847,10 +885,26 @@ describe 'up.util', ->
|
|
847
885
|
it 'returns true for an object with at least one key', ->
|
848
886
|
expect(up.util.isBlank({key: 'value'})).toBe(false)
|
849
887
|
|
888
|
+
it 'returns true for an object with an [up.util.isBlank.key] method that returns true', ->
|
889
|
+
value = {}
|
890
|
+
value[up.util.isBlank.key] = -> true
|
891
|
+
expect(up.util.isBlank(value)).toBe(true)
|
892
|
+
|
893
|
+
it 'returns false for an object with an [up.util.isBlank.key] method that returns false', ->
|
894
|
+
value = {}
|
895
|
+
value[up.util.isBlank.key] = -> false
|
896
|
+
expect(up.util.isBlank(value)).toBe(false)
|
897
|
+
|
898
|
+
it 'returns false for a DOM element', ->
|
899
|
+
value = document.body
|
900
|
+
expect(up.util.isBlank(value)).toBe(false)
|
901
|
+
|
850
902
|
describe 'up.util.normalizeUrl', ->
|
851
903
|
|
852
904
|
it 'normalizes a relative path', ->
|
853
|
-
|
905
|
+
up.history.config.enabled = true
|
906
|
+
up.history.replace('/qux/')
|
907
|
+
expect(up.util.normalizeUrl('foo')).toBe("http://#{location.hostname}:#{location.port}/qux/foo")
|
854
908
|
|
855
909
|
it 'normalizes an absolute path', ->
|
856
910
|
expect(up.util.normalizeUrl('/foo')).toBe("http://#{location.hostname}:#{location.port}/foo")
|
@@ -879,45 +933,17 @@ describe 'up.util', ->
|
|
879
933
|
it 'puts a #hash behind the query string', ->
|
880
934
|
expect(up.util.normalizeUrl('http://example.com/foo/bar?key=value#fragment', hash: true)).toBe('http://example.com/foo/bar?key=value#fragment')
|
881
935
|
|
882
|
-
describe 'up.util.
|
936
|
+
describe 'up.util.find', ->
|
883
937
|
|
884
938
|
it 'finds the first element in the given array that matches the given tester', ->
|
885
939
|
array = ['foo', 'bar', 'baz']
|
886
940
|
tester = (element) -> element[0] == 'b'
|
887
|
-
expect(up.util.
|
941
|
+
expect(up.util.find(array, tester)).toEqual('bar')
|
888
942
|
|
889
943
|
it "returns undefined if the given array doesn't contain a matching element", ->
|
890
944
|
array = ['foo', 'bar', 'baz']
|
891
945
|
tester = (element) -> element[0] == 'z'
|
892
|
-
expect(up.util.
|
893
|
-
|
894
|
-
describe 'up.util.config', ->
|
895
|
-
|
896
|
-
it 'creates an object with the given attributes', ->
|
897
|
-
object = up.util.config(a: 1, b: 2)
|
898
|
-
expect(object.a).toBe(1)
|
899
|
-
expect(object.b).toBe(2)
|
900
|
-
|
901
|
-
it 'does not allow to set a key that was not included in the factory settings', ->
|
902
|
-
object = up.util.config(a: 1)
|
903
|
-
object.b = 2
|
904
|
-
expect(object.b).toBeUndefined()
|
905
|
-
|
906
|
-
describe '#reset', ->
|
907
|
-
|
908
|
-
it 'resets the object to its original state', ->
|
909
|
-
object = up.util.config(a: 1)
|
910
|
-
expect(object.b).toBeUndefined()
|
911
|
-
object.a = 2
|
912
|
-
expect(object.a).toBe(2)
|
913
|
-
object.reset()
|
914
|
-
expect(object.a).toBe(1)
|
915
|
-
|
916
|
-
it 'does not remove the #reset or #update method from the object', ->
|
917
|
-
object = up.util.config(a: 1)
|
918
|
-
object.b = 2
|
919
|
-
object.reset()
|
920
|
-
expect(object.reset).toBeDefined()
|
946
|
+
expect(up.util.find(array, tester)).toBeUndefined()
|
921
947
|
|
922
948
|
describe 'up.util.remove', ->
|
923
949
|
|
@@ -966,60 +992,41 @@ describe 'up.util', ->
|
|
966
992
|
expect(object.b).toBe('b value')
|
967
993
|
expect(object.c).toBe('a value')
|
968
994
|
|
969
|
-
describe 'up.util.
|
970
|
-
|
971
|
-
it '
|
972
|
-
$
|
973
|
-
$
|
974
|
-
$
|
975
|
-
$
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
describe 'up.util.selectInDynasty', ->
|
1007
|
-
|
1008
|
-
it 'finds the selector in both ancestors and descendants of the given element', ->
|
1009
|
-
$grandMother = affix('.grand-mother.match')
|
1010
|
-
$mother = $grandMother.affix('.mother')
|
1011
|
-
$element = $mother.affix('.element')
|
1012
|
-
$child = $element.affix('.child.match')
|
1013
|
-
$grandChild = $child.affix('.grand-child.match')
|
1014
|
-
|
1015
|
-
$matches = up.util.selectInDynasty($element, '.match')
|
1016
|
-
$expected = $grandMother.add($child).add($grandChild)
|
1017
|
-
expect($matches).toEqual $expected
|
1018
|
-
|
1019
|
-
it 'finds the element itself if it matches the selector', ->
|
1020
|
-
$element = affix('.element.match')
|
1021
|
-
$matches = up.util.selectInDynasty($element, '.match')
|
1022
|
-
expect($matches).toEqual $element
|
995
|
+
# describe 'up.util.offsetParent', ->
|
996
|
+
#
|
997
|
+
# it 'returns the first ascendant that has a "position" style', ->
|
998
|
+
# $a = $fixture('.a')
|
999
|
+
# $b = $a.affix('.b').css(position: 'relative')
|
1000
|
+
# $c = $b.affix('.c')
|
1001
|
+
# $d = $c.affix('.d')
|
1002
|
+
#
|
1003
|
+
# expect(up.util.offsetParent($d[0])).toBe($b[0])
|
1004
|
+
#
|
1005
|
+
# it 'does not return the given element, even when it has position', ->
|
1006
|
+
# $a = $fixture('.a').css(position: 'absolute')
|
1007
|
+
# $b = $a.affix('.b').css(position: 'relative')
|
1008
|
+
#
|
1009
|
+
# expect(up.util.offsetParent($b[0])).toBe($a[0])
|
1010
|
+
#
|
1011
|
+
# it 'returns the <body> element if there is no closer offset parent', ->
|
1012
|
+
# $a = $fixture('.a')
|
1013
|
+
# $b = $a.affix('.b')
|
1014
|
+
#
|
1015
|
+
# expect(up.util.offsetParent($b[0])).toBe(document.body)
|
1016
|
+
#
|
1017
|
+
# it 'returns the offset parent for a detached element', ->
|
1018
|
+
# $a = $fixture('.a').detach()
|
1019
|
+
# $b = $a.affix('.b').css(position: 'relative')
|
1020
|
+
# $c = $b.affix('.c')
|
1021
|
+
# $d = $c.affix('.d')
|
1022
|
+
#
|
1023
|
+
# expect(up.util.offsetParent($d[0])).toBe($b[0])
|
1024
|
+
#
|
1025
|
+
# it 'returns a missing value (and not <body>) if the given detached element has no ancestor with position', ->
|
1026
|
+
# $a = $fixture('.a').detach()
|
1027
|
+
# $b = $a.affix('.b')
|
1028
|
+
#
|
1029
|
+
# expect(up.util.offsetParent($b[0])).toBeMissing()
|
1023
1030
|
|
1024
1031
|
describe 'up.util.isCrossDomain', ->
|
1025
1032
|
|
@@ -1240,4 +1247,160 @@ describe 'up.util', ->
|
|
1240
1247
|
|
1241
1248
|
expect(target).toEqual { a: 1, b: 2, c: 3, d: 4, e: 5 }
|
1242
1249
|
|
1250
|
+
describe 'up.util.copy', ->
|
1251
|
+
|
1252
|
+
it 'returns a shallow copy of the given array', ->
|
1253
|
+
original = ['a', { b: 'c' }, 'd']
|
1254
|
+
|
1255
|
+
copy = up.util.copy(original)
|
1256
|
+
expect(copy).toEqual(original)
|
1257
|
+
|
1258
|
+
# Test that changes to copy don't change original
|
1259
|
+
copy.pop()
|
1260
|
+
expect(copy.length).toBe(2)
|
1261
|
+
expect(original.length).toBe(3)
|
1262
|
+
|
1263
|
+
# Test that the copy is shallow
|
1264
|
+
copy[1].x = 'y'
|
1265
|
+
expect(original[1].x).toEqual('y')
|
1266
|
+
|
1267
|
+
it 'returns a shallow copy of the given plain object', ->
|
1268
|
+
original = {a: 'b', c: [1, 2], d: 'e'}
|
1269
|
+
|
1270
|
+
copy = up.util.copy(original)
|
1271
|
+
expect(copy).toEqual(original)
|
1272
|
+
|
1273
|
+
# Test that changes to copy don't change original
|
1274
|
+
copy.f = 'g'
|
1275
|
+
expect(original.f).toBeMissing()
|
1276
|
+
|
1277
|
+
# Test that the copy is shallow
|
1278
|
+
copy.c.push(3)
|
1279
|
+
expect(original.c).toEqual [1, 2, 3]
|
1280
|
+
|
1281
|
+
it 'allows custom classes to hook into the copy protocol by implementing a method named `up.util.copy.key`', ->
|
1282
|
+
class TestClass
|
1283
|
+
"#{up.util.copy.key}": ->
|
1284
|
+
return "custom copy"
|
1285
|
+
|
1286
|
+
instance = new TestClass()
|
1287
|
+
expect(up.util.copy(instance)).toEqual("custom copy")
|
1288
|
+
|
1289
|
+
it 'copies the given jQuery collection into an array', ->
|
1290
|
+
$one = $fixture('.one')
|
1291
|
+
$two = $fixture('.two')
|
1292
|
+
$collection = $one.add($two)
|
1293
|
+
|
1294
|
+
copy = up.util.copy($collection)
|
1295
|
+
|
1296
|
+
copy[0] = document.body
|
1297
|
+
expect($collection[0]).toBe($one[0])
|
1298
|
+
|
1299
|
+
it 'copies the given arguments object into an array', ->
|
1300
|
+
args = undefined
|
1301
|
+
(-> args = arguments)(1)
|
1302
|
+
|
1303
|
+
copy = up.util.copy(args)
|
1304
|
+
expect(copy).toBeArray()
|
1305
|
+
|
1306
|
+
copy[0] = 2
|
1307
|
+
expect(args[0]).toBe(1)
|
1308
|
+
|
1309
|
+
it 'returns the given string (which is immutable)', ->
|
1310
|
+
str = "foo"
|
1311
|
+
copy = up.util.copy(str)
|
1312
|
+
expect(copy).toBe(str)
|
1313
|
+
|
1314
|
+
it 'returns the given number (which is immutable)', ->
|
1315
|
+
number = 123
|
1316
|
+
copy = up.util.copy(number)
|
1317
|
+
expect(copy).toBe(number)
|
1318
|
+
|
1319
|
+
it 'copies the given Date object', ->
|
1320
|
+
date = new Date('1995-12-17T03:24:00')
|
1321
|
+
expect(date.getFullYear()).toBe(1995)
|
1322
|
+
|
1323
|
+
copy = up.util.copy(date)
|
1324
|
+
|
1325
|
+
expect(copy.getFullYear()).toBe(1995)
|
1326
|
+
expect(copy.getHours()).toBe(3)
|
1327
|
+
expect(copy.getMinutes()).toBe(24)
|
1328
|
+
|
1329
|
+
date.setFullYear(2018)
|
1330
|
+
|
1331
|
+
expect(copy.getFullYear()).toBe(1995)
|
1332
|
+
|
1333
|
+
|
1334
|
+
describe 'up.util.deepCopy', ->
|
1335
|
+
|
1336
|
+
it 'returns a deep copy of the given array', ->
|
1337
|
+
original = ['a', { b: 'c' }, 'd']
|
1338
|
+
|
1339
|
+
copy = up.util.deepCopy(original)
|
1340
|
+
expect(copy).toEqual(original)
|
1341
|
+
|
1342
|
+
# Test that changes to copy don't change original
|
1343
|
+
copy.pop()
|
1344
|
+
expect(copy.length).toBe(2)
|
1345
|
+
expect(original.length).toBe(3)
|
1346
|
+
|
1347
|
+
# Test that the copy is deep
|
1348
|
+
copy[1].x = 'y'
|
1349
|
+
expect(original[1].x).toBeUndefined()
|
1350
|
+
|
1351
|
+
it 'returns a deep copy of the given object', ->
|
1352
|
+
original = {a: 'b', c: [1, 2], d: 'e'}
|
1353
|
+
|
1354
|
+
copy = up.util.deepCopy(original)
|
1355
|
+
expect(copy).toEqual(original)
|
1356
|
+
|
1357
|
+
# Test that changes to copy don't change original
|
1358
|
+
copy.f = 'g'
|
1359
|
+
expect(original.f).toBeMissing()
|
1360
|
+
|
1361
|
+
# Test that the copy is deep
|
1362
|
+
copy.c.push(3)
|
1363
|
+
expect(original.c).toEqual [1, 2]
|
1364
|
+
|
1365
|
+
describe 'up.util.isList', ->
|
1366
|
+
|
1367
|
+
it 'returns true for an array', ->
|
1368
|
+
value = [1, 2, 3]
|
1369
|
+
expect(up.util.isList(value)).toBe(true)
|
1370
|
+
|
1371
|
+
it 'returns true for an HTMLCollection', ->
|
1372
|
+
value = document.getElementsByTagName('div')
|
1373
|
+
expect(up.util.isList(value)).toBe(true)
|
1374
|
+
|
1375
|
+
it 'returns true for a NodeList', ->
|
1376
|
+
value = document.querySelectorAll('div')
|
1377
|
+
expect(up.util.isList(value)).toBe(true)
|
1378
|
+
|
1379
|
+
it 'returns true for an arguments object', ->
|
1380
|
+
value = undefined
|
1381
|
+
(-> value = arguments)()
|
1382
|
+
expect(up.util.isList(value)).toBe(true)
|
1383
|
+
|
1384
|
+
it 'returns false for an object', ->
|
1385
|
+
value = { foo: 'bar' }
|
1386
|
+
expect(up.util.isList(value)).toBe(false)
|
1387
|
+
|
1388
|
+
it 'returns false for a string', ->
|
1389
|
+
value = 'foo'
|
1390
|
+
expect(up.util.isList(value)).toBe(false)
|
1391
|
+
|
1392
|
+
it 'returns false for a number', ->
|
1393
|
+
value = 123
|
1394
|
+
expect(up.util.isList(value)).toBe(false)
|
1395
|
+
|
1396
|
+
it 'returns false for undefined', ->
|
1397
|
+
value = undefined
|
1398
|
+
expect(up.util.isList(value)).toBe(false)
|
1399
|
+
|
1400
|
+
it 'returns false for null', ->
|
1401
|
+
value = null
|
1402
|
+
expect(up.util.isList(value)).toBe(false)
|
1243
1403
|
|
1404
|
+
it 'returns false for NaN', ->
|
1405
|
+
value = NaN
|
1406
|
+
expect(up.util.isList(value)).toBe(false)
|