unpoly-rails 0.37.0 → 0.50.0

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

Potentially problematic release.


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

Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +127 -25
  3. data/LICENSE +1 -1
  4. data/README_RAILS.md +4 -2
  5. data/Rakefile +6 -1
  6. data/dist/unpoly.js +3192 -2198
  7. data/dist/unpoly.min.js +4 -3
  8. data/lib/assets/javascripts/unpoly/browser.coffee +51 -63
  9. data/lib/assets/javascripts/unpoly/bus.coffee +58 -33
  10. data/lib/assets/javascripts/unpoly/classes/cache.coffee +117 -0
  11. data/lib/assets/javascripts/unpoly/{dom → classes}/extract_cascade.coffee +3 -3
  12. data/lib/assets/javascripts/unpoly/{dom → classes}/extract_plan.coffee +1 -1
  13. data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +57 -0
  14. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +52 -0
  15. data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +95 -0
  16. data/lib/assets/javascripts/unpoly/classes/record.coffee +16 -0
  17. data/lib/assets/javascripts/unpoly/classes/request.coffee +228 -0
  18. data/lib/assets/javascripts/unpoly/classes/response.coffee +138 -0
  19. data/lib/assets/javascripts/unpoly/dom.coffee +151 -142
  20. data/lib/assets/javascripts/unpoly/feedback.coffee +67 -38
  21. data/lib/assets/javascripts/unpoly/form.coffee +156 -139
  22. data/lib/assets/javascripts/unpoly/history.coffee +22 -19
  23. data/lib/assets/javascripts/unpoly/layout.coffee +108 -90
  24. data/lib/assets/javascripts/unpoly/link.coffee +159 -158
  25. data/lib/assets/javascripts/unpoly/log.coffee +5 -5
  26. data/lib/assets/javascripts/unpoly/modal.coffee +93 -81
  27. data/lib/assets/javascripts/unpoly/motion.coffee +291 -250
  28. data/lib/assets/javascripts/unpoly/popup.coffee +67 -53
  29. data/lib/assets/javascripts/unpoly/protocol.coffee +67 -16
  30. data/lib/assets/javascripts/unpoly/proxy.coffee +282 -211
  31. data/lib/assets/javascripts/unpoly/rails.coffee +3 -14
  32. data/lib/assets/javascripts/unpoly/syntax.coffee +54 -49
  33. data/lib/assets/javascripts/unpoly/tooltip.coffee +18 -25
  34. data/lib/assets/javascripts/unpoly/util.coffee +236 -477
  35. data/lib/assets/javascripts/unpoly.coffee +1 -1
  36. data/lib/unpoly/rails/inspector.rb +67 -22
  37. data/lib/unpoly/rails/version.rb +1 -1
  38. data/package.json +1 -1
  39. data/spec_app/Gemfile.lock +13 -13
  40. data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
  41. data/spec_app/app/assets/javascripts/jasmine_specs.coffee +1 -1
  42. data/spec_app/app/assets/stylesheets/jasmine_specs.sass +10 -0
  43. data/spec_app/app/controllers/binding_test_controller.rb +19 -2
  44. data/spec_app/app/controllers/method_test_controller.rb +16 -0
  45. data/spec_app/app/views/layouts/jasmine_rails/spec_runner.html.erb +20 -0
  46. data/spec_app/app/views/method_test/form_target.erb +17 -0
  47. data/spec_app/app/views/method_test/page1.erb +11 -0
  48. data/spec_app/app/views/method_test/page2.erb +6 -0
  49. data/spec_app/app/views/pages/start.erb +33 -19
  50. data/spec_app/config/initializers/assets.rb +5 -0
  51. data/spec_app/config/routes.rb +3 -0
  52. data/spec_app/spec/controllers/binding_test_controller_spec.rb +82 -27
  53. data/spec_app/spec/javascripts/helpers/agent_detector.coffee +17 -0
  54. data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +102 -0
  55. data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -1
  56. data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +5 -2
  57. data/spec_app/spec/javascripts/helpers/promise_state.js +18 -0
  58. data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +9 -0
  59. data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +22 -0
  60. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +11 -3
  61. data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +10 -0
  62. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +5 -0
  63. data/spec_app/spec/javascripts/helpers/to_match_url.coffee +13 -0
  64. data/spec_app/spec/javascripts/helpers/trigger.js.coffee +13 -6
  65. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +92 -33
  66. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +64 -15
  67. data/spec_app/spec/javascripts/up/classes/.keep +0 -0
  68. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +1 -0
  69. data/spec_app/spec/javascripts/up/dom_spec.js.coffee +759 -551
  70. data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +155 -82
  71. data/spec_app/spec/javascripts/up/form_spec.js.coffee +490 -349
  72. data/spec_app/spec/javascripts/up/history_spec.js.coffee +226 -179
  73. data/spec_app/spec/javascripts/up/layout_spec.js.coffee +253 -185
  74. data/spec_app/spec/javascripts/up/link_spec.js.coffee +416 -270
  75. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +459 -330
  76. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +198 -153
  77. data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +9 -0
  78. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +240 -175
  79. data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +38 -0
  80. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +777 -303
  81. data/spec_app/spec/javascripts/up/rails_spec.js.coffee +24 -8
  82. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +40 -23
  83. data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +80 -66
  84. data/spec_app/spec/javascripts/up/util_spec.js.coffee +227 -201
  85. data/spec_app/vendor/asset-libs/es6-promise-4.1.6/es6-promise.auto.js +1159 -0
  86. metadata +30 -7
  87. data/spec_app/spec/javascripts/helpers/reset_path.js.coffee +0 -7
  88. data/spec_app/spec/javascripts/helpers/to_equal_url.coffee +0 -11
@@ -1,5 +1,6 @@
1
1
  #= require ./unpoly/namespace
2
2
  #= require ./unpoly/util
3
+ #= require_tree ./unpoly/classes
3
4
  #= require ./unpoly/protocol
4
5
  #= require ./unpoly/browser
5
6
  #= require ./unpoly/bus
@@ -9,7 +10,6 @@
9
10
  #= require ./unpoly/history
10
11
  #= require ./unpoly/layout
11
12
  #= require ./unpoly/dom
12
- #= require_tree ./unpoly/dom
13
13
  #= require ./unpoly/motion
14
14
  #= require ./unpoly/proxy
15
15
  #= require ./unpoly/link
@@ -22,21 +22,35 @@ module Unpoly
22
22
  alias_method :unpoly?, :up?
23
23
 
24
24
  ##
25
- # If the current request is a [fragment update](https://unpoly.com/up.replace),
26
- # this returns the CSS selector of the page fragment that should be updated.
25
+ # Returns the CSS selector for a fragment that Unpoly will update in
26
+ # case of a successful response (200 status code).
27
27
  #
28
28
  # The Unpoly frontend will expect an HTML response containing an element
29
- # that matches this selector. If no such element is found, an error is shown
30
- # to the user.
29
+ # that matches this selector.
31
30
  #
32
- # Server-side code is free to optimize its response by only returning HTML
31
+ # Server-side code is free to optimize its successful response by only returning HTML
33
32
  # that matches this selector.
34
33
  def target
35
34
  request.headers['X-Up-Target']
36
35
  end
37
36
 
38
37
  ##
39
- # Tests whether the given CSS selector is targeted by the current fragment update.
38
+ # Returns the CSS selector for a fragment that Unpoly will update in
39
+ # case of an failed response. Server errors or validation failures are
40
+ # all examples for a failed response (non-200 status code).
41
+ #
42
+ # The Unpoly frontend will expect an HTML response containing an element
43
+ # that matches this selector.
44
+ #
45
+ # Server-side code is free to optimize its response by only returning HTML
46
+ # that matches this selector.
47
+ def fail_target
48
+ request.headers['X-Up-Fail-Target']
49
+ end
50
+
51
+ ##
52
+ # Returns whether the given CSS selector is targeted by the current fragment
53
+ # update in case of a successful response (200 status code).
40
54
  #
41
55
  # Note that the matching logic is very simplistic and does not actually know
42
56
  # how your page layout is structured. It will return `true` if
@@ -45,31 +59,46 @@ module Unpoly
45
59
  #
46
60
  # Always returns `true` if the current request is not an Unpoly fragment update.
47
61
  def target?(tested_target)
48
- if up?
49
- actual_target = target
50
- if actual_target == tested_target
51
- true
52
- elsif actual_target == 'html'
53
- true
54
- elsif actual_target == 'body'
55
- not ['head', 'title', 'meta'].include?(tested_target)
56
- else
57
- false
58
- end
59
- else
60
- true
61
- end
62
+ query_target(target, tested_target)
63
+ end
64
+
65
+ ##
66
+ # Returns whether the given CSS selector is targeted by the current fragment
67
+ # update in case of a failed response (non-200 status code).
68
+ #
69
+ # Note that the matching logic is very simplistic and does not actually know
70
+ # how your page layout is structured. It will return `true` if
71
+ # the tested selector and the requested CSS selector matches exactly, or if the
72
+ # requested selector is `body` or `html`.
73
+ #
74
+ # Always returns `true` if the current request is not an Unpoly fragment update.
75
+ def fail_target?(tested_target)
76
+ query_target(fail_target, tested_target)
77
+ end
78
+
79
+ ##
80
+ # Returns whether the given CSS selector is targeted by the current fragment
81
+ # update for either a success or a failed response.
82
+ #
83
+ # Note that the matching logic is very simplistic and does not actually know
84
+ # how your page layout is structured. It will return `true` if
85
+ # the tested selector and the requested CSS selector matches exactly, or if the
86
+ # requested selector is `body` or `html`.
87
+ #
88
+ # Always returns `true` if the current request is not an Unpoly fragment update.
89
+ def any_target?(tested_target)
90
+ target?(tested_target) || fail_target?(tested_target)
62
91
  end
63
92
 
64
93
  ##
65
94
  # Returns whether the current form submission should be
66
- # [validated](https://unpoly.com/up-validate) (and not be saved to the database).
95
+ # [validated](https://unpoly.com/input-up-validate) (and not be saved to the database).
67
96
  def validate?
68
97
  validate_name.present?
69
98
  end
70
99
 
71
100
  ##
72
- # If the current form submission is a [validation](https://unpoly.com/up-validate),
101
+ # If the current form submission is a [validation](https://unpoly.com/input-up-validate),
73
102
  # this returns the name attribute of the form field that has triggered
74
103
  # the validation.
75
104
  def validate_name
@@ -99,6 +128,22 @@ module Unpoly
99
128
  @controller.response
100
129
  end
101
130
 
131
+ def query_target(actual_target, tested_target)
132
+ if up?
133
+ if actual_target == tested_target
134
+ true
135
+ elsif actual_target == 'html'
136
+ true
137
+ elsif actual_target == 'body'
138
+ not ['head', 'title', 'meta'].include?(tested_target)
139
+ else
140
+ false
141
+ end
142
+ else
143
+ true
144
+ end
145
+ end
146
+
102
147
  end
103
148
  end
104
149
  end
@@ -4,6 +4,6 @@ module Unpoly
4
4
  # The current version of the unpoly-rails gem.
5
5
  # This version number is also used for releases of the Unpoly
6
6
  # frontend code.
7
- VERSION = '0.37.0'
7
+ VERSION = '0.50.0'
8
8
  end
9
9
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unpoly",
3
- "version": "0.37.0",
3
+ "version": "0.50.0",
4
4
  "description": "Unobtrusive JavaScript framework",
5
5
  "main": "dist/unpoly.js",
6
6
  "files": [
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- unpoly-rails (0.36.2)
4
+ unpoly-rails (0.37.0)
5
5
  rails (>= 3)
6
6
 
7
7
  GEM
@@ -86,14 +86,14 @@ GEM
86
86
  haml (>= 4.0.0.rc.1)
87
87
  hpricot (~> 0.8.6)
88
88
  ruby_parser (~> 3.1.1)
89
- i18n (0.7.0)
90
- jasmine-core (2.5.2)
89
+ i18n (0.8.6)
90
+ jasmine-core (2.8.0)
91
91
  jasmine-rails (0.14.1)
92
92
  jasmine-core (>= 1.3, < 3.0)
93
93
  phantomjs (>= 1.9)
94
94
  railties (>= 3.2.0)
95
95
  sprockets-rails
96
- jquery-rails (4.2.2)
96
+ jquery-rails (4.3.1)
97
97
  rails-dom-testing (>= 1, < 3)
98
98
  railties (>= 4.2.0)
99
99
  thor (>= 0.14, < 2.0)
@@ -104,13 +104,13 @@ GEM
104
104
  mail (2.6.3)
105
105
  mime-types (>= 1.16, < 3)
106
106
  mime-types (2.99)
107
- mini_portile2 (2.1.0)
108
- minitest (5.10.1)
107
+ mini_portile2 (2.2.0)
108
+ minitest (5.10.3)
109
109
  multi_json (1.12.1)
110
- nokogiri (1.7.0.1)
111
- mini_portile2 (~> 2.1.0)
110
+ nokogiri (1.8.0)
111
+ mini_portile2 (~> 2.2.0)
112
112
  phantomjs (2.1.1.0)
113
- rack (1.6.5)
113
+ rack (1.6.8)
114
114
  rack-test (0.6.3)
115
115
  rack (>= 1.0)
116
116
  rails (4.2.0)
@@ -180,10 +180,10 @@ GEM
180
180
  therubyracer (0.12.1)
181
181
  libv8 (~> 3.16.14.0)
182
182
  ref
183
- thor (0.19.4)
184
- thread_safe (0.3.5)
183
+ thor (0.20.0)
184
+ thread_safe (0.3.6)
185
185
  tilt (1.4.1)
186
- tzinfo (1.2.2)
186
+ tzinfo (1.2.3)
187
187
  thread_safe (~> 0.1)
188
188
  uglifier (2.6.0)
189
189
  execjs (>= 0.3.0)
@@ -218,4 +218,4 @@ DEPENDENCIES
218
218
  web-console (~> 2.0)
219
219
 
220
220
  BUNDLED WITH
221
- 1.15.1
221
+ 1.16.0
@@ -1,5 +1,6 @@
1
1
  #= require jquery
2
2
  #= require jquery_ujs
3
+ #= require es6-promise.auto
3
4
  #= require unpoly
4
5
 
5
6
  up.compiler '.compiler', ($element) ->
@@ -1,7 +1,7 @@
1
- #= require jquery2
2
1
  #= require jquery_ujs
3
2
  #= require jasmine-jquery
4
3
  #= require jasmine-fixture
5
4
  #= require jasmine-ajax
6
5
  #= require helpers/knife
6
+ #= require es6-promise.auto
7
7
  #= require unpoly
@@ -4,3 +4,13 @@
4
4
  .up-modal-backdrop
5
5
  background-color: rgba(90, 90, 90, 0.05)
6
6
 
7
+ .default-fallback
8
+ position: fixed
9
+ top: 0
10
+ left: 0
11
+ width: 500px
12
+ height: 300px
13
+ background-color: rgba(255, 255, 255, 0.7)
14
+
15
+ &:empty
16
+ display: none
@@ -8,12 +8,22 @@ class BindingTestController < ActionController::Base
8
8
  render :text => up.target
9
9
  end
10
10
 
11
+ def up_fail_target
12
+ render :text => up.fail_target
13
+ end
14
+
11
15
  def up_is_target
12
- tested_target = params[:tested_target].presence
13
- tested_target or raise "No target given"
14
16
  render :text => up.target?(tested_target).to_s
15
17
  end
16
18
 
19
+ def up_is_fail_target
20
+ render :text => up.fail_target?(tested_target).to_s
21
+ end
22
+
23
+ def up_is_any_target
24
+ render :text => up.any_target?(tested_target).to_s
25
+ end
26
+
17
27
  def is_up_validate
18
28
  render :text => up.validate?.to_s
19
29
  end
@@ -31,4 +41,11 @@ class BindingTestController < ActionController::Base
31
41
  render :text => 'text from controller'
32
42
  end
33
43
 
44
+ private
45
+
46
+ def tested_target
47
+ tested_target = params[:tested_target].presence
48
+ tested_target or raise "No target given"
49
+ end
50
+
34
51
  end
@@ -0,0 +1,16 @@
1
+ class MethodTestController < ApplicationController
2
+
3
+ layout false
4
+
5
+ skip_before_filter :verify_authenticity_token
6
+
7
+ def page1
8
+ end
9
+
10
+ def page2
11
+ end
12
+
13
+ def form_target
14
+ end
15
+
16
+ end
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta content="text/html;charset=UTF-8" http-equiv="Content-Type"/>
5
+ <title>Unpoly Specs</title>
6
+
7
+ <%= stylesheet_link_tag *jasmine_css_files %>
8
+ <%=
9
+ jquery_suffix = params[:jquery].presence || '3'
10
+ jquery_asset = "jquery#{jquery_suffix}"
11
+ jquery_asset = "jquery" if jquery_asset == "jquery1"
12
+ javascript_include_tag jquery_asset
13
+ %>
14
+ <%= javascript_include_tag *jasmine_js_files %>
15
+ </head>
16
+ <body>
17
+ <div id="jasmine_content"></div>
18
+ <%= yield %>
19
+ </body>
20
+ </html>
@@ -0,0 +1,17 @@
1
+ <html>
2
+ <body>
3
+
4
+ <h1>form_target (<%= request.method %>)</h1>
5
+
6
+ <p>
7
+ <a href="javascript:history.pushState({}, 'page 2 title', 'page2')">Add history entry to page2</a>
8
+ </p>
9
+
10
+ <p>
11
+ <a href="javascript:location.reload()">Reload the page</a>
12
+ <br>
13
+ Do you see a "retry form submission" prompt? If yes, this browser is affected by the issue.
14
+ </p>
15
+
16
+ </body>
17
+ </html>
@@ -0,0 +1,11 @@
1
+ <html>
2
+ <body>
3
+
4
+ <h1>page1 (<%= request.method %>)</h1>
5
+
6
+ <form method="post" action="form_target">
7
+ <input type="submit" value="load form_target with POST">
8
+ </form>
9
+
10
+ </body>
11
+ </html>
@@ -0,0 +1,6 @@
1
+ <html>
2
+ <body>
3
+ <h1>page2 (<%= request.method %>)</h1>
4
+
5
+ </body>
6
+ </html>
@@ -5,7 +5,38 @@
5
5
  <h2>Unit tests</h2>
6
6
 
7
7
  <ul>
8
- <li><%= link_to 'Jasmine specs', '/specs' %></li>
8
+ <li>
9
+ <%= link_to 'Jasmine specs with jQuery 3', '/specs?jquery=3' %>
10
+ <ul>
11
+ <% %w[
12
+ up.dom
13
+ up.feedback
14
+ up.form
15
+ up.history
16
+ up.layout
17
+ up.link
18
+ up.log
19
+ up.modal
20
+ up.motion
21
+ up.popup
22
+ up.protocol
23
+ up.proxy
24
+ up.rails
25
+ up.syntax
26
+ up.toast
27
+ up.tooltip
28
+ up.util
29
+ ].each do |mod| %>
30
+ <li><%= link_to mod, "/specs?spec=#{mod}" %></li>
31
+ <% end %>
32
+ </ul>
33
+ </li>
34
+ <li>
35
+ <%= link_to 'Jasmine specs with jQuery 2', '/specs?jquery=2' %>
36
+ </li>
37
+ <li>
38
+ <%= link_to 'Jasmine specs with jQuery 1', '/specs?jquery=1' %>
39
+ </li>
9
40
  </ul>
10
41
 
11
42
  <h2>Integration tests</h2>
@@ -15,35 +46,18 @@
15
46
  <%= link_to 'Tooltip', '/css_test/tooltip' %>
16
47
  (<%= link_to 'with Bootstrap', '/css_test/tooltip?bootstrap=1' %>)
17
48
  </li>
18
- </ul>
19
-
20
- <ul>
21
49
  <li>
22
50
  <%= link_to 'Popup', '/css_test/popup' %>
23
51
  (<%= link_to 'with Bootstrap', '/css_test/popup?bootstrap=1' %>)
24
52
  </li>
25
- </ul>
26
-
27
- <ul>
28
53
  <li>
29
54
  <%= link_to 'Modal', '/css_test/modal' %>
30
55
  (<%= link_to 'with Bootstrap', '/css_test/modal?bootstrap=1' %>)
31
56
  </li>
32
- </ul>
33
-
34
- <ul>
35
57
  <li><%= link_to 'Form (basic)', '/form_test/basic/new' %></li>
36
- </ul>
37
-
38
- <ul>
39
58
  <li><%= link_to 'Form (upload)', '/form_test/upload/new' %></li>
40
- </ul>
41
-
42
- <ul>
43
59
  <li><%= link_to 'Error', '/error_test/trigger' %></li>
44
- </ul>
45
-
46
- <ul>
60
+ <li><%= link_to 'Booting with non-GET method', '/method_test/page1' %></li>
47
61
  <li><%= link_to 'Fragment update', '/replace_test/page1' %></li>
48
62
  </ul>
49
63
 
@@ -11,4 +11,9 @@ Rails.application.config.assets.precompile += %w( application.js application.css
11
11
  Rails.application.config.assets.precompile += %w( jasmine_specs.js jasmine_specs.css )
12
12
  Rails.application.config.assets.precompile += %w( integration_test.js integration_test.css )
13
13
  Rails.application.config.assets.precompile += %w( bootstrap_manifest.js bootstrap_manifest.css )
14
+
15
+ # Precompile jQuery versions from jquery-rails individually, so we can include them as individual <script> tags
16
+ Rails.application.config.assets.precompile += %w( jquery.js jquery2.js jquery3.js )
17
+
18
+ # Precompile Bootstrap bindings individually, so we can include them as individual <script> tags
14
19
  Rails.application.config.assets.precompile += %w( unpoly-bootstrap3.js unpoly-bootstrap3.css )
@@ -3,6 +3,9 @@ Rails.application.routes.draw do
3
3
  mount JasmineRails::Engine => '/specs' if defined?(JasmineRails)
4
4
  root to: 'pages#start'
5
5
 
6
+ get 'method_test/:action', controller: 'method_test'
7
+ post 'method_test/:action', controller: 'method_test'
8
+
6
9
  get 'binding_test/:action', controller: 'binding_test'
7
10
  get 'css_test/:action', controller: 'css_test'
8
11
  get 'error_test/:action', controller: 'error_test'
@@ -19,7 +19,7 @@ describe BindingTestController do
19
19
 
20
20
  describe '#target' do
21
21
 
22
- it 'returns the CSS selector that is requested via Unpoly' do
22
+ it 'returns the CSS selector that Unpoly requested for a sucessful response' do
23
23
  request.headers['X-Up-Target'] = '.foo'
24
24
  get :up_target
25
25
  expect(response.body).to eq('.foo')
@@ -27,87 +27,142 @@ describe BindingTestController do
27
27
 
28
28
  end
29
29
 
30
- describe '#target?' do
30
+ describe '#fail_target' do
31
31
 
32
- it 'returns true if the tested CSS selector is requested via Unpoly' do
32
+ it 'returns the CSS selector that Unpoly requested for an error response' do
33
33
  request.headers['X-Up-Target'] = '.foo'
34
- get :up_is_target, tested_target: '.foo'
34
+ request.headers['X-Up-Fail-Target'] = '.bar'
35
+ get :up_fail_target
36
+ expect(response.body).to eq('.bar')
37
+ end
38
+
39
+ end
40
+
41
+ shared_examples_for 'target query' do |opts|
42
+
43
+ let(:header) { opts.fetch(:header) }
44
+
45
+ let (:action) { opts.fetch(:action)}
46
+
47
+ def set_header(value)
48
+ request.headers[header] = value
49
+ if header != 'X-Up-Target'
50
+ # Make sure that it's considered a fragment update
51
+ request.headers['X-Up-Target'] = '.other-selector'
52
+ end
53
+ end
54
+
55
+ it 'returns true if the tested CSS selector is requested via Unpoly' do
56
+ set_header '.foo'
57
+ get action, tested_target: '.foo'
35
58
  expect(response.body).to eq('true')
36
59
  end
37
60
 
38
61
  it 'returns false if Unpoly is requesting another CSS selector' do
39
- request.headers['X-Up-Target'] = '.bar'
40
- get :up_is_target, tested_target: '.foo'
62
+ set_header '.bar'
63
+ get action, tested_target: '.foo'
41
64
  expect(response.body).to eq('false')
42
65
  end
43
66
 
44
67
  it 'returns true if the request is not an Unpoly request' do
45
- get :up_is_target, tested_target: '.foo'
68
+ get action, tested_target: '.foo'
46
69
  expect(response.body).to eq('true')
47
70
  end
48
71
 
49
72
  it 'returns true if testing a custom selector, and Unpoly requests "body"' do
50
- request.headers['X-Up-Target'] = 'body'
51
- get :up_is_target, tested_target: '.foo'
73
+ set_header 'body'
74
+ get action, tested_target: '.foo'
52
75
  expect(response.body).to eq('true')
53
76
  end
54
77
 
55
78
  it 'returns true if testing a custom selector, and Unpoly requests "html"' do
56
- request.headers['X-Up-Target'] = 'html'
57
- get :up_is_target, tested_target: '.foo'
79
+ set_header 'html'
80
+ get action, tested_target: '.foo'
58
81
  expect(response.body).to eq('true')
59
82
  end
60
83
 
61
84
  it 'returns true if testing "body", and Unpoly requests "html"' do
62
- request.headers['X-Up-Target'] = 'html'
63
- get :up_is_target, tested_target: 'body'
85
+ set_header 'html'
86
+ get action, tested_target: 'body'
64
87
  expect(response.body).to eq('true')
65
88
  end
66
89
 
67
90
  it 'returns true if testing "head", and Unpoly requests "html"' do
68
- request.headers['X-Up-Target'] = 'html'
69
- get :up_is_target, tested_target: 'head'
91
+ set_header 'html'
92
+ get action, tested_target: 'head'
70
93
  expect(response.body).to eq('true')
71
94
  end
72
95
 
73
96
  it 'returns false if the tested CSS selector is "head" but Unpoly requests "body"' do
74
- request.headers['X-Up-Target'] = 'body'
75
- get :up_is_target, tested_target: 'head'
97
+ set_header 'body'
98
+ get action, tested_target: 'head'
76
99
  expect(response.body).to eq('false')
77
100
  end
78
101
 
79
102
  it 'returns false if the tested CSS selector is "title" but Unpoly requests "body"' do
80
- request.headers['X-Up-Target'] = 'body'
81
- get :up_is_target, tested_target: 'title'
103
+ set_header 'body'
104
+ get action, tested_target: 'title'
82
105
  expect(response.body).to eq('false')
83
106
  end
84
107
 
85
108
  it 'returns false if the tested CSS selector is "meta" but Unpoly requests "body"' do
86
- request.headers['X-Up-Target'] = 'body'
87
- get :up_is_target, tested_target: 'meta'
109
+ set_header 'body'
110
+ get action, tested_target: 'meta'
88
111
  expect(response.body).to eq('false')
89
112
  end
90
113
 
91
114
  it 'returns true if the tested CSS selector is "head", and Unpoly requests "html"' do
92
- request.headers['X-Up-Target'] = 'html'
93
- get :up_is_target, tested_target: 'head'
115
+ set_header 'html'
116
+ get action, tested_target: 'head'
94
117
  expect(response.body).to eq('true')
95
118
  end
96
119
 
97
120
  it 'returns true if the tested CSS selector is "title", Unpoly requests "html"' do
98
- request.headers['X-Up-Target'] = 'html'
99
- get :up_is_target, tested_target: 'title'
121
+ set_header 'html'
122
+ get action, tested_target: 'title'
100
123
  expect(response.body).to eq('true')
101
124
  end
102
125
 
103
126
  it 'returns true if the tested CSS selector is "meta", and Unpoly requests "html"' do
104
- request.headers['X-Up-Target'] = 'html'
105
- get :up_is_target, tested_target: 'meta'
127
+ set_header 'html'
128
+ get action, tested_target: 'meta'
106
129
  expect(response.body).to eq('true')
107
130
  end
108
131
 
109
132
  end
110
133
 
134
+ describe '#target?' do
135
+ it_behaves_like 'target query', action: :up_is_target, header: 'X-Up-Target'
136
+ end
137
+
138
+ describe '#fail_target?' do
139
+ it_behaves_like 'target query', action: :up_is_fail_target, header: 'X-Up-Fail-Target'
140
+ end
141
+
142
+ describe '#any_target?' do
143
+
144
+ before :each do
145
+ request.headers['X-Up-Target'] = '.success'
146
+ request.headers['X-Up-Fail-Target'] = '.failure'
147
+ end
148
+
149
+ it 'returns true if the tested CSS selector is the target for a successful response' do
150
+ get :up_is_any_target, tested_target: '.success'
151
+ expect(response.body).to eq('true')
152
+ end
153
+
154
+ it 'returns true if the tested CSS selector is the target for a failed response' do
155
+ get :up_is_any_target, tested_target: '.failure'
156
+ expect(response.body).to eq('true')
157
+ end
158
+
159
+ it 'returns false if the tested CSS selector is a target for neither successful nor failed response' do
160
+ get :up_is_any_target, tested_target: '.other'
161
+ expect(response.body).to eq('false')
162
+ end
163
+
164
+ end
165
+
111
166
  describe '#validate?' do
112
167
 
113
168
  it 'returns true the request is an Unpoly validation call' do
@@ -0,0 +1,17 @@
1
+ @AgentDetector = do ->
2
+
3
+ match = (regexp) ->
4
+ navigator.userAgent.match(regexp)
5
+
6
+ isIE = ->
7
+ match(/\bTrident\b/)
8
+
9
+ isEdge = ->
10
+ match(/\bEdge\b/)
11
+
12
+ isSafari = ->
13
+ match(/\bSafari\b/) && !match(/\bChrome\b/)
14
+
15
+ isIE: isIE
16
+ isEdge: isEdge
17
+ isSafari: isSafari