unpoly-rails 0.62.1 → 2.0.0.pre.rc6

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 (340) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +60 -1
  4. data/README.md +3 -11
  5. data/README_RAILS.md +295 -14
  6. data/dist/unpoly-bootstrap3.css +9 -14
  7. data/dist/unpoly-bootstrap3.js +4 -18
  8. data/dist/unpoly-bootstrap3.min.css +1 -1
  9. data/dist/unpoly-bootstrap3.min.js +1 -1
  10. data/dist/unpoly-bootstrap4.css +9 -0
  11. data/dist/unpoly-bootstrap4.js +16 -0
  12. data/dist/unpoly-bootstrap4.min.css +1 -0
  13. data/dist/unpoly-bootstrap4.min.js +1 -0
  14. data/dist/unpoly-bootstrap5.css +9 -0
  15. data/dist/unpoly-bootstrap5.js +14 -0
  16. data/dist/unpoly-bootstrap5.min.css +1 -0
  17. data/dist/unpoly-bootstrap5.min.js +1 -0
  18. data/dist/unpoly-migrate.js +1225 -0
  19. data/dist/unpoly-migrate.min.js +1 -0
  20. data/dist/unpoly.css +109 -140
  21. data/dist/unpoly.js +15805 -10558
  22. data/dist/unpoly.min.css +1 -1
  23. data/dist/unpoly.min.js +6 -4
  24. data/lib/unpoly-rails.rb +9 -3
  25. data/lib/unpoly/rails/change.rb +372 -0
  26. data/lib/unpoly/rails/change/cache.rb +26 -0
  27. data/lib/unpoly/rails/change/context.rb +80 -0
  28. data/lib/unpoly/rails/change/field.rb +117 -0
  29. data/lib/unpoly/rails/change/field_definition.rb +74 -0
  30. data/lib/unpoly/rails/change/layer.rb +60 -0
  31. data/lib/unpoly/rails/controller.rb +47 -0
  32. data/lib/unpoly/rails/error.rb +5 -0
  33. data/lib/unpoly/rails/request_echo_headers.rb +2 -2
  34. data/lib/unpoly/rails/version.rb +1 -1
  35. data/lib/unpoly/tasks.rb +55 -0
  36. metadata +43 -313
  37. data/.gitignore +0 -10
  38. data/.ruby-version +0 -1
  39. data/Gemfile +0 -8
  40. data/Gemfile.lock +0 -45
  41. data/Rakefile +0 -145
  42. data/bower.json +0 -27
  43. data/design/animation-ghosting.txt +0 -72
  44. data/design/design.txt +0 -34
  45. data/design/draft.html.erb +0 -48
  46. data/design/draft.rb +0 -9
  47. data/design/es6.js +0 -32
  48. data/design/ghost-debugging.txt +0 -118
  49. data/design/homepage.txt +0 -236
  50. data/design/ie11.txt +0 -9
  51. data/design/measure_import_node.js +0 -330
  52. data/design/measure_jquery/element_list.js +0 -41
  53. data/design/measure_jquery/up.on_vs_addEventListener.js +0 -56
  54. data/design/positioning.txt +0 -28
  55. data/design/query-params-in-form-actions/cases.html +0 -125
  56. data/design/rename.txt +0 -0
  57. data/design/test_rejected_promise.txt +0 -5
  58. data/design/unpoly errors.txt +0 -19
  59. data/lib/assets/javascripts/unpoly-bootstrap3.coffee +0 -2
  60. data/lib/assets/javascripts/unpoly-bootstrap3/feedback-ext.coffee +0 -5
  61. data/lib/assets/javascripts/unpoly-bootstrap3/form-ext.coffee +0 -1
  62. data/lib/assets/javascripts/unpoly-bootstrap3/modal-ext.coffee +0 -14
  63. data/lib/assets/javascripts/unpoly-bootstrap3/viewport-ext.coffee +0 -5
  64. data/lib/assets/javascripts/unpoly.coffee +0 -28
  65. data/lib/assets/javascripts/unpoly/browser.coffee.erb +0 -240
  66. data/lib/assets/javascripts/unpoly/classes/body_shifter.coffee +0 -45
  67. data/lib/assets/javascripts/unpoly/classes/cache.coffee +0 -127
  68. data/lib/assets/javascripts/unpoly/classes/compile_pass.coffee +0 -93
  69. data/lib/assets/javascripts/unpoly/classes/config.coffee +0 -9
  70. data/lib/assets/javascripts/unpoly/classes/css_transition.coffee +0 -118
  71. data/lib/assets/javascripts/unpoly/classes/divertible_chain.coffee +0 -39
  72. data/lib/assets/javascripts/unpoly/classes/event_listener.coffee +0 -116
  73. data/lib/assets/javascripts/unpoly/classes/extract_cascade.coffee +0 -86
  74. data/lib/assets/javascripts/unpoly/classes/extract_plan.coffee +0 -111
  75. data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +0 -80
  76. data/lib/assets/javascripts/unpoly/classes/focus_follower.coffee +0 -29
  77. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +0 -64
  78. data/lib/assets/javascripts/unpoly/classes/html_parser.coffee +0 -46
  79. data/lib/assets/javascripts/unpoly/classes/motion_controller.coffee +0 -157
  80. data/lib/assets/javascripts/unpoly/classes/params.coffee.erb +0 -544
  81. data/lib/assets/javascripts/unpoly/classes/record.coffee +0 -22
  82. data/lib/assets/javascripts/unpoly/classes/rect.js +0 -21
  83. data/lib/assets/javascripts/unpoly/classes/request.coffee +0 -246
  84. data/lib/assets/javascripts/unpoly/classes/response.coffee +0 -157
  85. data/lib/assets/javascripts/unpoly/classes/reveal_motion.coffee +0 -102
  86. data/lib/assets/javascripts/unpoly/classes/scroll_motion.coffee +0 -67
  87. data/lib/assets/javascripts/unpoly/classes/selector.coffee +0 -60
  88. data/lib/assets/javascripts/unpoly/classes/store/memory.coffee +0 -26
  89. data/lib/assets/javascripts/unpoly/classes/store/session.coffee +0 -59
  90. data/lib/assets/javascripts/unpoly/classes/tether.coffee +0 -105
  91. data/lib/assets/javascripts/unpoly/classes/url_set.coffee +0 -32
  92. data/lib/assets/javascripts/unpoly/cookie.coffee +0 -56
  93. data/lib/assets/javascripts/unpoly/element.coffee.erb +0 -1126
  94. data/lib/assets/javascripts/unpoly/event.coffee.erb +0 -445
  95. data/lib/assets/javascripts/unpoly/feedback.coffee +0 -353
  96. data/lib/assets/javascripts/unpoly/form.coffee.erb +0 -1075
  97. data/lib/assets/javascripts/unpoly/fragment.coffee.erb +0 -928
  98. data/lib/assets/javascripts/unpoly/framework.coffee +0 -67
  99. data/lib/assets/javascripts/unpoly/history.coffee +0 -268
  100. data/lib/assets/javascripts/unpoly/legacy.coffee +0 -60
  101. data/lib/assets/javascripts/unpoly/link.coffee.erb +0 -622
  102. data/lib/assets/javascripts/unpoly/log.coffee +0 -248
  103. data/lib/assets/javascripts/unpoly/modal.coffee.erb +0 -827
  104. data/lib/assets/javascripts/unpoly/motion.coffee.erb +0 -668
  105. data/lib/assets/javascripts/unpoly/namespace.coffee.erb +0 -5
  106. data/lib/assets/javascripts/unpoly/popup.coffee.erb +0 -515
  107. data/lib/assets/javascripts/unpoly/protocol.coffee +0 -298
  108. data/lib/assets/javascripts/unpoly/proxy.coffee +0 -672
  109. data/lib/assets/javascripts/unpoly/radio.coffee +0 -60
  110. data/lib/assets/javascripts/unpoly/rails.coffee +0 -24
  111. data/lib/assets/javascripts/unpoly/syntax.coffee.erb +0 -476
  112. data/lib/assets/javascripts/unpoly/toast.coffee +0 -67
  113. data/lib/assets/javascripts/unpoly/tooltip.coffee +0 -276
  114. data/lib/assets/javascripts/unpoly/util.coffee.erb +0 -1676
  115. data/lib/assets/javascripts/unpoly/viewport.coffee.erb +0 -830
  116. data/lib/assets/stylesheets/unpoly-bootstrap3.sass +0 -1
  117. data/lib/assets/stylesheets/unpoly-bootstrap3/modal-ext.sass +0 -27
  118. data/lib/assets/stylesheets/unpoly.sass +0 -1
  119. data/lib/assets/stylesheets/unpoly/close.sass +0 -2
  120. data/lib/assets/stylesheets/unpoly/dom.sass +0 -5
  121. data/lib/assets/stylesheets/unpoly/layout.sass +0 -2
  122. data/lib/assets/stylesheets/unpoly/link.sass +0 -2
  123. data/lib/assets/stylesheets/unpoly/modal.sass +0 -116
  124. data/lib/assets/stylesheets/unpoly/popup.sass +0 -7
  125. data/lib/assets/stylesheets/unpoly/toast.sass +0 -33
  126. data/lib/assets/stylesheets/unpoly/tooltip.sass +0 -62
  127. data/lib/unpoly/rails/inspector.rb +0 -149
  128. data/lib/unpoly/rails/inspector_accessor.rb +0 -30
  129. data/package.json +0 -38
  130. data/spec_app/.firefox-version +0 -1
  131. data/spec_app/.gitignore +0 -17
  132. data/spec_app/.rspec +0 -2
  133. data/spec_app/Gemfile +0 -30
  134. data/spec_app/Gemfile.lock +0 -229
  135. data/spec_app/README.rdoc +0 -28
  136. data/spec_app/Rakefile +0 -6
  137. data/spec_app/app/assets/images/.keep +0 -0
  138. data/spec_app/app/assets/images/favicon.png +0 -0
  139. data/spec_app/app/assets/images/grid.png +0 -0
  140. data/spec_app/app/assets/javascripts/bootstrap_manifest.coffee +0 -6
  141. data/spec_app/app/assets/javascripts/integration_test.coffee +0 -6
  142. data/spec_app/app/assets/javascripts/jasmine_specs.coffee +0 -6
  143. data/spec_app/app/assets/stylesheets/_helpers.sass +0 -5
  144. data/spec_app/app/assets/stylesheets/bootstrap_manifest.sass +0 -2
  145. data/spec_app/app/assets/stylesheets/integration_test.sass +0 -88
  146. data/spec_app/app/assets/stylesheets/jasmine_specs.sass +0 -20
  147. data/spec_app/app/controllers/application_controller.rb +0 -14
  148. data/spec_app/app/controllers/binding_test_controller.rb +0 -51
  149. data/spec_app/app/controllers/compiler_test_controller.rb +0 -5
  150. data/spec_app/app/controllers/css_test_controller.rb +0 -5
  151. data/spec_app/app/controllers/error_test_controller.rb +0 -5
  152. data/spec_app/app/controllers/form_test/basics_controller.rb +0 -14
  153. data/spec_app/app/controllers/form_test/redirects_controller.rb +0 -17
  154. data/spec_app/app/controllers/form_test/uploads_controller.rb +0 -15
  155. data/spec_app/app/controllers/hash_test_controller.rb +0 -5
  156. data/spec_app/app/controllers/method_test_controller.rb +0 -16
  157. data/spec_app/app/controllers/motion_test_controller.rb +0 -5
  158. data/spec_app/app/controllers/pages_controller.rb +0 -9
  159. data/spec_app/app/controllers/replace_test_controller.rb +0 -5
  160. data/spec_app/app/controllers/reveal_test_controller.rb +0 -5
  161. data/spec_app/app/controllers/scroll_test_controller.rb +0 -5
  162. data/spec_app/app/helpers/application_helper.rb +0 -2
  163. data/spec_app/app/mailers/.keep +0 -0
  164. data/spec_app/app/models/concerns/.keep +0 -0
  165. data/spec_app/app/views/compiler_test/timestamp.erb +0 -9
  166. data/spec_app/app/views/css_test/modal.erb +0 -47
  167. data/spec_app/app/views/css_test/modal_contents.erb +0 -5
  168. data/spec_app/app/views/css_test/modal_contents_wide.erb +0 -5
  169. data/spec_app/app/views/css_test/popup.erb +0 -81
  170. data/spec_app/app/views/css_test/popup_contents.erb +0 -5
  171. data/spec_app/app/views/css_test/tooltip.erb +0 -48
  172. data/spec_app/app/views/error_test/trigger.erb +0 -80
  173. data/spec_app/app/views/error_test/unexpected_response.erb +0 -3
  174. data/spec_app/app/views/form_test/basics/new.erb +0 -60
  175. data/spec_app/app/views/form_test/redirects/new.erb +0 -27
  176. data/spec_app/app/views/form_test/redirects/target.erb +0 -4
  177. data/spec_app/app/views/form_test/submission_result.erb +0 -30
  178. data/spec_app/app/views/form_test/uploads/new.erb +0 -44
  179. data/spec_app/app/views/hash_test/unpoly.erb +0 -30
  180. data/spec_app/app/views/hash_test/vanilla.erb +0 -13
  181. data/spec_app/app/views/layouts/integration_test.erb +0 -22
  182. data/spec_app/app/views/layouts/jasmine_rails/spec_runner.html.erb +0 -20
  183. data/spec_app/app/views/method_test/form_target.erb +0 -17
  184. data/spec_app/app/views/method_test/page1.erb +0 -11
  185. data/spec_app/app/views/method_test/page2.erb +0 -6
  186. data/spec_app/app/views/motion_test/animations.erb +0 -16
  187. data/spec_app/app/views/motion_test/transitions.erb +0 -13
  188. data/spec_app/app/views/pages/start.erb +0 -79
  189. data/spec_app/app/views/replace_test/_nav.erb +0 -6
  190. data/spec_app/app/views/replace_test/page1.erb +0 -14
  191. data/spec_app/app/views/replace_test/page2.erb +0 -14
  192. data/spec_app/app/views/replace_test/table.erb +0 -16
  193. data/spec_app/app/views/reveal_test/long1.erb +0 -17
  194. data/spec_app/app/views/reveal_test/long2.erb +0 -17
  195. data/spec_app/app/views/reveal_test/within_document_viewport.erb +0 -24
  196. data/spec_app/app/views/reveal_test/within_overflowing_div_viewport.erb +0 -28
  197. data/spec_app/app/views/scroll_test/long1.erb +0 -30
  198. data/spec_app/bin/bundle +0 -3
  199. data/spec_app/bin/rails +0 -8
  200. data/spec_app/bin/rake +0 -8
  201. data/spec_app/bin/setup +0 -29
  202. data/spec_app/bin/spring +0 -18
  203. data/spec_app/config.ru +0 -4
  204. data/spec_app/config/application.rb +0 -28
  205. data/spec_app/config/boot.rb +0 -3
  206. data/spec_app/config/database.yml +0 -25
  207. data/spec_app/config/environment.rb +0 -5
  208. data/spec_app/config/environments/development.rb +0 -41
  209. data/spec_app/config/environments/production.rb +0 -79
  210. data/spec_app/config/environments/test.rb +0 -42
  211. data/spec_app/config/initializers/assets.rb +0 -19
  212. data/spec_app/config/initializers/backtrace_silencers.rb +0 -7
  213. data/spec_app/config/initializers/bower_rails.rb +0 -16
  214. data/spec_app/config/initializers/cookies_serializer.rb +0 -3
  215. data/spec_app/config/initializers/filter_parameter_logging.rb +0 -4
  216. data/spec_app/config/initializers/inflections.rb +0 -16
  217. data/spec_app/config/initializers/mime_types.rb +0 -4
  218. data/spec_app/config/initializers/session_store.rb +0 -3
  219. data/spec_app/config/initializers/wrap_parameters.rb +0 -14
  220. data/spec_app/config/locales/en.yml +0 -23
  221. data/spec_app/config/routes.rb +0 -30
  222. data/spec_app/config/secrets.yml +0 -22
  223. data/spec_app/db/schema.rb +0 -23
  224. data/spec_app/db/seeds.rb +0 -7
  225. data/spec_app/lib/assets/.keep +0 -0
  226. data/spec_app/lib/tasks/.keep +0 -0
  227. data/spec_app/lib/tasks/cucumber.rake +0 -65
  228. data/spec_app/log/.keep +0 -0
  229. data/spec_app/public/404.html +0 -67
  230. data/spec_app/public/422.html +0 -67
  231. data/spec_app/public/500.html +0 -66
  232. data/spec_app/public/favicon.ico +0 -0
  233. data/spec_app/public/robots.txt +0 -5
  234. data/spec_app/script/cucumber +0 -10
  235. data/spec_app/spec/controllers/binding_test_controller_spec.rb +0 -248
  236. data/spec_app/spec/javascripts/helpers/agent_detector.coffee +0 -20
  237. data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +0 -103
  238. data/spec_app/spec/javascripts/helpers/browser_switches.js.coffee +0 -21
  239. data/spec_app/spec/javascripts/helpers/enable_logging.js.coffee +0 -2
  240. data/spec_app/spec/javascripts/helpers/fixture.js.coffee +0 -25
  241. data/spec_app/spec/javascripts/helpers/index.js.coffee +0 -1
  242. data/spec_app/spec/javascripts/helpers/jquery_no_conflict.js +0 -1
  243. data/spec_app/spec/javascripts/helpers/knife.js.coffee +0 -69
  244. data/spec_app/spec/javascripts/helpers/last_request.js.coffee +0 -25
  245. data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +0 -8
  246. data/spec_app/spec/javascripts/helpers/mock_clock.js.coffee +0 -2
  247. data/spec_app/spec/javascripts/helpers/parse_form_data.js.coffee +0 -24
  248. data/spec_app/spec/javascripts/helpers/promise_state.js +0 -18
  249. data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +0 -12
  250. data/spec_app/spec/javascripts/helpers/remove_body_margin.js.coffee +0 -8
  251. data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +0 -23
  252. data/spec_app/spec/javascripts/helpers/reset_knife.js.coffee +0 -2
  253. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +0 -25
  254. data/spec_app/spec/javascripts/helpers/restore_body_scroll.js.coffee +0 -5
  255. data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +0 -12
  256. data/spec_app/spec/javascripts/helpers/spec_util.coffee +0 -47
  257. data/spec_app/spec/javascripts/helpers/to_be_around.js.coffee +0 -8
  258. data/spec_app/spec/javascripts/helpers/to_be_array.coffee +0 -5
  259. data/spec_app/spec/javascripts/helpers/to_be_attached.coffee +0 -9
  260. data/spec_app/spec/javascripts/helpers/to_be_blank.js.coffee +0 -8
  261. data/spec_app/spec/javascripts/helpers/to_be_detached.coffee +0 -9
  262. data/spec_app/spec/javascripts/helpers/to_be_element.js.coffee +0 -8
  263. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +0 -8
  264. data/spec_app/spec/javascripts/helpers/to_be_given.js.coffee +0 -8
  265. data/spec_app/spec/javascripts/helpers/to_be_hidden.js.coffee +0 -8
  266. data/spec_app/spec/javascripts/helpers/to_be_jquery.js.coffee +0 -5
  267. data/spec_app/spec/javascripts/helpers/to_be_missing.js.coffee +0 -8
  268. data/spec_app/spec/javascripts/helpers/to_be_present.js.coffee +0 -8
  269. data/spec_app/spec/javascripts/helpers/to_be_scrolled_to.coffee +0 -11
  270. data/spec_app/spec/javascripts/helpers/to_be_visible.js.coffee +0 -9
  271. data/spec_app/spec/javascripts/helpers/to_contain.js.coffee +0 -8
  272. data/spec_app/spec/javascripts/helpers/to_end_with.js.coffee +0 -11
  273. data/spec_app/spec/javascripts/helpers/to_equal_jquery.js.coffee +0 -8
  274. data/spec_app/spec/javascripts/helpers/to_equal_node_list.coffee +0 -7
  275. data/spec_app/spec/javascripts/helpers/to_equal_via_is_equal.js.coffee +0 -7
  276. data/spec_app/spec/javascripts/helpers/to_have_class.js.coffee +0 -10
  277. data/spec_app/spec/javascripts/helpers/to_have_descendant.js.coffee +0 -10
  278. data/spec_app/spec/javascripts/helpers/to_have_length.js.coffee +0 -8
  279. data/spec_app/spec/javascripts/helpers/to_have_opacity.coffee +0 -15
  280. data/spec_app/spec/javascripts/helpers/to_have_own_property.js.coffee +0 -8
  281. data/spec_app/spec/javascripts/helpers/to_have_request_method.js.coffee +0 -16
  282. data/spec_app/spec/javascripts/helpers/to_have_text.js.coffee +0 -9
  283. data/spec_app/spec/javascripts/helpers/to_have_unhandled_rejections.coffee +0 -18
  284. data/spec_app/spec/javascripts/helpers/to_match_list.coffee +0 -14
  285. data/spec_app/spec/javascripts/helpers/to_match_selector.coffee +0 -8
  286. data/spec_app/spec/javascripts/helpers/to_match_text.js.coffee +0 -13
  287. data/spec_app/spec/javascripts/helpers/to_match_url.coffee +0 -14
  288. data/spec_app/spec/javascripts/helpers/trigger.js.coffee +0 -200
  289. data/spec_app/spec/javascripts/helpers/wait_until_dom_ready.js.coffee +0 -5
  290. data/spec_app/spec/javascripts/support/jasmine.yml +0 -51
  291. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +0 -150
  292. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +0 -82
  293. data/spec_app/spec/javascripts/up/classes/config_spec.coffee +0 -24
  294. data/spec_app/spec/javascripts/up/classes/divertible_chain_spec.coffee +0 -45
  295. data/spec_app/spec/javascripts/up/classes/focus_tracker_spec.coffee +0 -34
  296. data/spec_app/spec/javascripts/up/classes/params_spec.coffee +0 -557
  297. data/spec_app/spec/javascripts/up/classes/request_spec.coffee +0 -50
  298. data/spec_app/spec/javascripts/up/classes/scroll_motion_spec.js.coffee +0 -51
  299. data/spec_app/spec/javascripts/up/classes/store/memory_spec.js.coffee +0 -70
  300. data/spec_app/spec/javascripts/up/classes/store/session_spec.js.coffee +0 -114
  301. data/spec_app/spec/javascripts/up/element_spec.coffee +0 -897
  302. data/spec_app/spec/javascripts/up/event_spec.js.coffee +0 -530
  303. data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +0 -401
  304. data/spec_app/spec/javascripts/up/form_spec.js.coffee +0 -1527
  305. data/spec_app/spec/javascripts/up/fragment_spec.js.coffee +0 -2624
  306. data/spec_app/spec/javascripts/up/history_spec.js.coffee +0 -340
  307. data/spec_app/spec/javascripts/up/jquery_spec.js.coffee +0 -4
  308. data/spec_app/spec/javascripts/up/legacy_spec.js.coffee +0 -27
  309. data/spec_app/spec/javascripts/up/link_spec.js.coffee +0 -1098
  310. data/spec_app/spec/javascripts/up/log_spec.js.coffee +0 -119
  311. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +0 -939
  312. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +0 -582
  313. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +0 -508
  314. data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +0 -39
  315. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +0 -1137
  316. data/spec_app/spec/javascripts/up/radio_spec.js.coffee +0 -212
  317. data/spec_app/spec/javascripts/up/rails_spec.js.coffee +0 -118
  318. data/spec_app/spec/javascripts/up/spec_spec.js.coffee +0 -9
  319. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +0 -304
  320. data/spec_app/spec/javascripts/up/toast_spec.js.coffee +0 -37
  321. data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +0 -163
  322. data/spec_app/spec/javascripts/up/util_spec.js.coffee +0 -1420
  323. data/spec_app/spec/javascripts/up/viewport_spec.js.coffee +0 -655
  324. data/spec_app/spec/spec_helper.rb +0 -62
  325. data/spec_app/test/controllers/.keep +0 -0
  326. data/spec_app/test/fixtures/.keep +0 -0
  327. data/spec_app/test/helpers/.keep +0 -0
  328. data/spec_app/test/integration/.keep +0 -0
  329. data/spec_app/test/mailers/.keep +0 -0
  330. data/spec_app/test/models/.keep +0 -0
  331. data/spec_app/test/test_helper.rb +0 -10
  332. data/spec_app/vendor/asset-libs/es6-promise-4.1.6/es6-promise.auto.js +0 -1159
  333. data/spec_app/vendor/asset-libs/jasmine-ajax-3.3.1/.bower.json +0 -43
  334. data/spec_app/vendor/asset-libs/jasmine-ajax-3.3.1/.gitignore +0 -6
  335. data/spec_app/vendor/asset-libs/jasmine-ajax-3.3.1/.npmignore +0 -10
  336. data/spec_app/vendor/asset-libs/jasmine-ajax-3.3.1/.pairs +0 -6
  337. data/spec_app/vendor/asset-libs/jasmine-ajax-3.3.1/.travis.yml +0 -56
  338. data/spec_app/vendor/asset-libs/jasmine-ajax-3.3.1/jasmine-ajax.js +0 -790
  339. data/spec_app/vendor/assets/.keep +0 -0
  340. data/unpoly-rails.gemspec +0 -24
@@ -1,2624 +0,0 @@
1
- u = up.util
2
- e = up.element
3
- $ = jQuery
4
-
5
- describe 'up.fragment', ->
6
-
7
- describe 'JavaScript functions', ->
8
-
9
- describe 'up.fragment.first', ->
10
-
11
- it 'returns the first element with the given selector', ->
12
- match = fixture('.match')
13
- noMatch = fixture('.no-match')
14
- result = up.fragment.first('.match')
15
- expect(result).toBe(match)
16
-
17
- it 'returns undefined if there are no matches', ->
18
- result = up.fragment.first('.match')
19
- expect(result).toBeUndefined()
20
-
21
- it 'does not return an element that is currently destroying', ->
22
- match = fixture('.match.up-destroying')
23
- result = up.fragment.first('.match')
24
- expect(result).toBeUndefined()
25
-
26
- describe 'when given a root element for the search', ->
27
-
28
- it 'only matches descendants of that root', ->
29
- parent1 = fixture('.parent1')
30
- parent1Match = e.affix(parent1, '.match')
31
-
32
- parent2 = fixture('.parent1')
33
- parent2Match = e.affix(parent2, '.match')
34
-
35
- expect(up.fragment.first(parent1, '.match')).toBe(parent1Match)
36
- expect(up.fragment.first(parent2, '.match')).toBe(parent2Match)
37
-
38
- describe 'with { origin } option', ->
39
-
40
- it 'resolves an & in the selector string with an selector for the origin'
41
-
42
- it 'prefers to find an element in the same layer as the origin'
43
-
44
- it "returns the element in the top-most layer if there are no matches in the origin's layer"
45
-
46
- describe 'with { layer } option', ->
47
-
48
- it 'only matches elements in that layer'
49
-
50
- describe 'up.replace', ->
51
-
52
- describeCapability 'canPushState', ->
53
-
54
- beforeEach ->
55
-
56
- @$oldBefore = $fixture('.before').text('old-before')
57
- @$oldMiddle = $fixture('.middle').text('old-middle')
58
- @$oldAfter = $fixture('.after').text('old-after')
59
-
60
- @responseText =
61
- """
62
- <div class="before">new-before</div>
63
- <div class="middle">new-middle</div>
64
- <div class="after">new-after</div>
65
- """
66
-
67
- @respond = (options = {}) -> @respondWith(@responseText, options)
68
-
69
- it 'replaces the given selector with the same selector from a freshly fetched page', asyncSpec (next) ->
70
- up.replace('.middle', '/path')
71
-
72
- next =>
73
- @respond()
74
-
75
- next.after 10, =>
76
- expect($('.before')).toHaveText('old-before')
77
- expect($('.middle')).toHaveText('new-middle')
78
- expect($('.after')).toHaveText('old-after')
79
-
80
- it 'returns a promise that will be fulfilled once the server response was received and the fragments were swapped', asyncSpec (next) ->
81
- resolution = jasmine.createSpy()
82
- promise = up.replace('.middle', '/path')
83
- promise.then(resolution)
84
- expect(resolution).not.toHaveBeenCalled()
85
- expect($('.middle')).toHaveText('old-middle')
86
-
87
- next =>
88
- @respond()
89
-
90
- next =>
91
- expect(resolution).toHaveBeenCalled()
92
- expect($('.middle')).toHaveText('new-middle')
93
-
94
- it 'allows to pass an element instead of a selector', asyncSpec (next) ->
95
- up.replace(@$oldMiddle, '/path')
96
-
97
- next =>
98
- @respond()
99
-
100
- next =>
101
- expect($('.before')).toHaveText('old-before')
102
- expect($('.middle')).toHaveText('new-middle')
103
- expect($('.after')).toHaveText('old-after')
104
-
105
- describe 'with { transition } option', ->
106
-
107
- it 'returns a promise that will be fulfilled once the server response was received and the swap transition has completed', asyncSpec (next) ->
108
- resolution = jasmine.createSpy()
109
- promise = up.replace('.middle', '/path', transition: 'cross-fade', duration: 200)
110
- promise.then(resolution)
111
- expect(resolution).not.toHaveBeenCalled()
112
- expect($('.middle')).toHaveText('old-middle')
113
-
114
- next =>
115
- @respond()
116
- expect(resolution).not.toHaveBeenCalled()
117
-
118
- next.after 100, =>
119
- expect(resolution).not.toHaveBeenCalled()
120
-
121
- next.after 200, =>
122
- expect(resolution).toHaveBeenCalled()
123
-
124
- describe 'with { params } option', ->
125
-
126
- it "uses the given params as a non-GET request's payload", asyncSpec (next) ->
127
- givenParams = { 'foo-key': 'foo-value', 'bar-key': 'bar-value' }
128
- up.replace('.middle', '/path', method: 'put', params: givenParams)
129
-
130
- next =>
131
- expect(@lastRequest().data()['foo-key']).toEqual(['foo-value'])
132
- expect(@lastRequest().data()['bar-key']).toEqual(['bar-value'])
133
-
134
- it "encodes the given params into the URL of a GET request", asyncSpec (next) ->
135
- givenParams = { 'foo-key': 'foo-value', 'bar-key': 'bar-value' }
136
- up.replace('.middle', '/path', method: 'get', params: givenParams)
137
- next => expect(@lastRequest().url).toMatchUrl('/path?foo-key=foo-value&bar-key=bar-value')
138
-
139
- it 'uses a HTTP method given as { method } option', asyncSpec (next) ->
140
- up.replace('.middle', '/path', method: 'put')
141
- next => expect(@lastRequest()).toHaveRequestMethod('PUT')
142
-
143
- describe 'when the server responds with an error', ->
144
-
145
- it 'replaces the first fallback instead of the given selector', asyncSpec (next) ->
146
- up.fragment.config.fallbacks = ['.fallback']
147
- $fixture('.fallback')
148
-
149
- # can't have the example replace the Jasmine test runner UI
150
- extractSpy = up.fragment.knife.mock('extract').and.returnValue(Promise.resolve())
151
-
152
- next => up.replace('.middle', '/path')
153
- next => @respond(status: 500)
154
- next => expect(extractSpy).toHaveBeenCalledWith('.fallback', jasmine.any(String), jasmine.any(Object))
155
-
156
- it 'uses a target selector given as { failTarget } option', asyncSpec (next) ->
157
- next =>
158
- up.replace('.middle', '/path', failTarget: '.after')
159
-
160
- next =>
161
- @respond(status: 500)
162
-
163
- next =>
164
- expect($('.middle')).toHaveText('old-middle')
165
- expect($('.after')).toHaveText('new-after')
166
-
167
- it 'rejects the returned promise', (done) ->
168
- $fixture('.after')
169
- promise = up.replace('.middle', '/path', failTarget: '.after')
170
-
171
- u.task =>
172
- promiseState(promise).then (result) =>
173
- expect(result.state).toEqual('pending')
174
-
175
- @respond(status: 500)
176
-
177
- u.task =>
178
- promiseState(promise).then (result) =>
179
- expect(result.state).toEqual('rejected')
180
- done()
181
-
182
- describe 'when the request times out', ->
183
-
184
- it "doesn't crash and rejects the returned promise", asyncSpec (next) ->
185
- jasmine.clock().install() # required by responseTimeout()
186
- $fixture('.target')
187
- promise = up.replace('.middle', '/path', timeout: 50)
188
-
189
- next =>
190
- # See that the correct timeout value has been set on the XHR instance
191
- expect(@lastRequest().timeout).toEqual(50)
192
-
193
- next.await =>
194
- # See that the promise is still pending before the timeout
195
- promiseState(promise).then (result) -> expect(result.state).toEqual('pending')
196
-
197
- next =>
198
- @lastRequest().responseTimeout()
199
-
200
- next.await =>
201
- promiseState(promise).then (result) -> expect(result.state).toEqual('rejected')
202
-
203
- describe 'when there is a network issue', ->
204
-
205
- it "doesn't crash and rejects the returned promise", (done) ->
206
- $fixture('.target')
207
- promise = up.replace('.middle', '/path')
208
-
209
- u.task =>
210
- promiseState(promise).then (result) =>
211
- expect(result.state).toEqual('pending')
212
- @lastRequest().responseError()
213
- u.task =>
214
- promiseState(promise).then (result) =>
215
- expect(result.state).toEqual('rejected')
216
- done()
217
-
218
- describe 'history', ->
219
-
220
- beforeEach ->
221
- up.history.config.enabled = true
222
-
223
- it 'should set the browser location to the given URL', (done) ->
224
- promise = up.replace('.middle', '/path')
225
- @respond()
226
- promise.then ->
227
- expect(location.href).toMatchUrl('/path')
228
- done()
229
-
230
- it 'does not add a history entry after non-GET requests', asyncSpec (next) ->
231
- up.replace('.middle', '/path', method: 'post')
232
- next => @respond()
233
- next => expect(location.href).toMatchUrl(@hrefBeforeExample)
234
-
235
- it 'adds a history entry after non-GET requests if the response includes a { X-Up-Method: "get" } header (will happen after a redirect)', asyncSpec (next) ->
236
- up.replace('.middle', '/requested-path', method: 'post')
237
- next => @respond(responseHeaders:
238
- 'X-Up-Method': 'GET'
239
- 'X-Up-Location': '/signaled-path'
240
- )
241
- next => expect(location.href).toMatchUrl('/signaled-path')
242
-
243
- it 'does not a history entry after a failed GET-request', asyncSpec (next) ->
244
- up.replace('.middle', '/path', method: 'post', failTarget: '.middle')
245
- next => @respond(status: 500)
246
- next => expect(location.href).toMatchUrl(@hrefBeforeExample)
247
-
248
- it 'does not add a history entry with { history: false } option', asyncSpec (next) ->
249
- up.replace('.middle', '/path', history: false)
250
- next => @respond()
251
- next => expect(location.href).toMatchUrl(@hrefBeforeExample)
252
-
253
- it "detects a redirect's new URL when the server sets an X-Up-Location header", asyncSpec (next) ->
254
- up.replace('.middle', '/path')
255
- next => @respond(responseHeaders: { 'X-Up-Location': '/other-path' })
256
- next => expect(location.href).toMatchUrl('/other-path')
257
-
258
- it 'adds params from a { params } option to the URL of a GET request', asyncSpec (next) ->
259
- up.replace('.middle', '/path', params: { 'foo-key': 'foo value', 'bar-key': 'bar value' })
260
- next => @respond()
261
- next => expect(location.href).toMatchUrl('/path?foo-key=foo%20value&bar-key=bar%20value')
262
-
263
- describe 'if a URL is given as { history } option', ->
264
-
265
- it 'uses that URL as the new location after a GET request', asyncSpec (next) ->
266
- up.replace('.middle', '/path', history: '/given-path')
267
- next => @respond(failTarget: '.middle')
268
- next => expect(location.href).toMatchUrl('/given-path')
269
-
270
- it 'adds a history entry after a non-GET request', asyncSpec (next) ->
271
- up.replace('.middle', '/path', method: 'post', history: '/given-path')
272
- next => @respond(failTarget: '.middle')
273
- next => expect(location.href).toMatchUrl('/given-path')
274
-
275
- it 'does not add a history entry after a failed non-GET request', asyncSpec (next) ->
276
- up.replace('.middle', '/path', method: 'post', history: '/given-path', failTarget: '.middle')
277
- next => @respond(failTarget: '.middle', status: 500)
278
- next => expect(location.href).toMatchUrl(@hrefBeforeExample)
279
-
280
- describe 'source', ->
281
-
282
- it 'remembers the source the fragment was retrieved from', (done) ->
283
- promise = up.replace('.middle', '/path')
284
- @respond()
285
- promise.then ->
286
- expect($('.middle').attr('up-source')).toMatch(/\/path$/)
287
- done()
288
-
289
- it 'reuses the previous source for a non-GET request (since that is reloadable)', asyncSpec (next) ->
290
- @$oldMiddle.attr('up-source', '/previous-source')
291
- up.replace('.middle', '/path', method: 'post')
292
- next =>
293
- @respond()
294
- next =>
295
- expect($('.middle')).toHaveText('new-middle')
296
- expect(up.fragment.source('.middle')).toMatchUrl('/previous-source')
297
-
298
- describe 'if a URL is given as { source } option', ->
299
-
300
- it 'uses that URL as the source for a GET request', asyncSpec (next) ->
301
- up.replace('.middle', '/path', source: '/given-path')
302
- next => @respond()
303
- next => expect(up.fragment.source('.middle')).toMatchUrl('/given-path')
304
-
305
- it 'uses that URL as the source after a non-GET request', asyncSpec (next) ->
306
- up.replace('.middle', '/path', method: 'post', source: '/given-path')
307
- next => @respond()
308
- next => expect(up.fragment.source('.middle')).toMatchUrl('/given-path')
309
-
310
- it 'ignores the option and reuses the previous source after a failed non-GET request', asyncSpec (next) ->
311
- @$oldMiddle.attr('up-source', '/previous-source')
312
- up.replace('.middle', '/path', method: 'post', source: '/given-path', failTarget: '.middle')
313
- next => @respond(status: 500)
314
- next => expect(up.fragment.source('.middle')).toMatchUrl('/previous-source')
315
-
316
- describe 'document title', ->
317
-
318
- beforeEach ->
319
- up.history.config.enabled = true
320
-
321
- it "sets the document title to the response <title>", asyncSpec (next) ->
322
- $fixture('.container').text('old container text')
323
- up.replace('.container', '/path')
324
-
325
- next =>
326
- @respondWith """
327
- <html>
328
- <head>
329
- <title>Title from HTML</title>
330
- </head>
331
- <body>
332
- <div class='container'>
333
- new container text
334
- </div>
335
- </body>
336
- </html>
337
- """
338
-
339
- next =>
340
- expect($('.container')).toHaveText('new container text')
341
- expect(document.title).toBe('Title from HTML')
342
-
343
- it "sets the document title to an 'X-Up-Title' header in the response", asyncSpec (next) ->
344
- $fixture('.container').text('old container text')
345
- up.replace('.container', '/path')
346
-
347
- next =>
348
- @respondWith
349
- responseHeaders:
350
- 'X-Up-Title': 'Title from header'
351
- responseText: """
352
- <div class='container'>
353
- new container text
354
- </div>
355
- """
356
-
357
- next =>
358
- expect($('.container')).toHaveText('new container text')
359
- expect(document.title).toBe('Title from header')
360
-
361
- it "prefers the X-Up-Title header to the response <title>", asyncSpec (next) ->
362
- $fixture('.container').text('old container text')
363
- up.replace('.container', '/path')
364
-
365
- next =>
366
- @respondWith
367
- responseHeaders:
368
- 'X-Up-Title': 'Title from header'
369
- responseText: """
370
- <html>
371
- <head>
372
- <title>Title from HTML</title>
373
- </head>
374
- <body>
375
- <div class='container'>
376
- new container text
377
- </div>
378
- </body>
379
- </html>
380
- """
381
-
382
- next =>
383
- expect($('.container')).toHaveText('new container text')
384
- expect(document.title).toBe('Title from header')
385
-
386
- it "sets the document title to the response <title> with { history: false, title: true } options (bugfix)", asyncSpec (next) ->
387
- $fixture('.container').text('old container text')
388
- up.replace('.container', '/path', history: false, title: true)
389
-
390
- next =>
391
- @respondWith """
392
- <html>
393
- <head>
394
- <title>Title from HTML</title>
395
- </head>
396
- <body>
397
- <div class='container'>
398
- new container text
399
- </div>
400
- </body>
401
- </html>
402
- """
403
-
404
- next =>
405
- expect($('.container')).toHaveText('new container text')
406
- expect(document.title).toBe('Title from HTML')
407
-
408
- it 'does not update the document title if the response has a <title> tag inside an inline SVG image (bugfix)', asyncSpec (next) ->
409
- $fixture('.container').text('old container text')
410
- document.title = 'old document title'
411
- up.replace('.container', '/path', history: false, title: true)
412
-
413
- next =>
414
- @respondWith """
415
- <svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
416
- <g>
417
- <title>SVG Title Demo example</title>
418
- <rect x="10" y="10" width="200" height="50" style="fill:none; stroke:blue; stroke-width:1px"/>
419
- </g>
420
- </svg>
421
-
422
- <div class='container'>
423
- new container text
424
- </div>
425
- """
426
-
427
- next =>
428
- expect($('.container')).toHaveText('new container text')
429
- expect(document.title).toBe('old document title')
430
-
431
- it "does not extract the title from the response or HTTP header if history isn't updated", asyncSpec (next) ->
432
- $fixture('.container').text('old container text')
433
- document.title = 'old document title'
434
- up.replace('.container', '/path', history: false)
435
-
436
- next =>
437
- @respondWith
438
- responseHeaders:
439
- 'X-Up-Title': 'Title from header'
440
- responseText: """
441
- <html>
442
- <head>
443
- <title>Title from HTML</title>
444
- </head>
445
- <body>
446
- <div class='container'>
447
- new container text
448
- </div>
449
- </body>
450
- </html>
451
- """
452
-
453
- next =>
454
- expect(document.title).toBe('old document title')
455
-
456
- it 'allows to pass an explicit title as { title } option', asyncSpec (next) ->
457
- $fixture('.container').text('old container text')
458
- up.replace('.container', '/path', title: 'Title from options')
459
-
460
- next =>
461
- @respondWith """
462
- <html>
463
- <head>
464
- <title>Title from HTML</title>
465
- </head>
466
- <body>
467
- <div class='container'>
468
- new container text
469
- </div>
470
- </body>
471
- </html>
472
- """
473
-
474
- next =>
475
- expect($('.container')).toHaveText('new container text')
476
- expect(document.title).toBe('Title from options')
477
-
478
- describe 'selector processing', ->
479
-
480
- it 'replaces multiple selectors separated with a comma', (done) ->
481
- promise = up.replace('.middle, .after', '/path')
482
- @respond()
483
- promise.then ->
484
- expect($('.before')).toHaveText('old-before')
485
- expect($('.middle')).toHaveText('new-middle')
486
- expect($('.after')).toHaveText('new-after')
487
- done()
488
-
489
- describe 'nested selector merging', ->
490
-
491
- it 'replaces a single fragment if a selector contains a subsequent selector in the current page', asyncSpec (next) ->
492
- $outer = $fixture('.outer').text('old outer text')
493
- $inner = $outer.affix('.inner').text('old inner text')
494
-
495
- replacePromise = up.replace('.outer, .inner', '/path')
496
-
497
- next =>
498
- expect(@lastRequest().requestHeaders['X-Up-Target']).toEqual('.outer')
499
-
500
- @respondWith """
501
- <div class="outer">
502
- new outer text
503
- <div class="inner">
504
- new inner text
505
- </div>
506
- </div>
507
- """
508
-
509
- next =>
510
- expect($('.outer')).toBeAttached()
511
- expect($('.outer').text()).toContain('new outer text')
512
- expect($('.inner')).toBeAttached()
513
- expect($('.inner').text()).toContain('new inner text')
514
-
515
- next.await =>
516
- promise = promiseState(replacePromise)
517
- promise.then (result) => expect(result.state).toEqual('fulfilled')
518
-
519
- it 'does not merge selectors if a selector contains a subsequent selector, but prepends instead of replacing', asyncSpec (next) ->
520
- $outer = $fixture('.outer').text('old outer text')
521
- $inner = $outer.affix('.inner').text('old inner text')
522
-
523
- replacePromise = up.replace('.outer:before, .inner', '/path')
524
-
525
- next =>
526
- expect(@lastRequest().requestHeaders['X-Up-Target']).toEqual('.outer:before, .inner')
527
-
528
- @respondWith """
529
- <div class="outer">
530
- new outer text
531
- <div class="inner">
532
- new inner text
533
- </div>
534
- </div>
535
- """
536
-
537
- next =>
538
- expect($('.outer')).toBeAttached()
539
- expect($('.outer').text()).toContain('new outer text')
540
- expect($('.outer').text()).toContain('old outer text')
541
- expect($('.inner')).toBeAttached()
542
- expect($('.inner').text()).toContain('new inner text')
543
-
544
- next.await =>
545
- promise = promiseState(replacePromise)
546
- promise.then (result) => expect(result.state).toEqual('fulfilled')
547
-
548
- it 'does not merge selectors if a selector contains a subsequent selector, but appends instead of replacing', asyncSpec (next) ->
549
- $outer = $fixture('.outer').text('old outer text')
550
- $inner = $outer.affix('.inner').text('old inner text')
551
-
552
- replacePromise = up.replace('.outer:after, .inner', '/path')
553
-
554
- next =>
555
- expect(@lastRequest().requestHeaders['X-Up-Target']).toEqual('.outer:after, .inner')
556
-
557
- @respondWith """
558
- <div class="outer">
559
- new outer text
560
- <div class="inner">
561
- new inner text
562
- </div>
563
- </div>
564
- """
565
-
566
- next =>
567
- expect($('.outer')).toBeAttached()
568
- expect($('.outer').text()).toContain('old outer text')
569
- expect($('.outer').text()).toContain('new outer text')
570
- expect($('.inner')).toBeAttached()
571
- expect($('.inner').text()).toContain('new inner text')
572
-
573
- next.await =>
574
- promise = promiseState(replacePromise)
575
- promise.then (result) => expect(result.state).toEqual('fulfilled')
576
-
577
- it 'does not lose selector pseudo-classes when merging selectors (bugfix)', asyncSpec (next) ->
578
- $outer = $fixture('.outer').text('old outer text')
579
- $inner = $outer.affix('.inner').text('old inner text')
580
-
581
- replacePromise = up.replace('.outer:after, .inner', '/path')
582
-
583
- next =>
584
- expect(@lastRequest().requestHeaders['X-Up-Target']).toEqual('.outer:after, .inner')
585
-
586
- it 'replaces a single fragment if a selector contains a previous selector in the current page', asyncSpec (next) ->
587
- $outer = $fixture('.outer').text('old outer text')
588
- $inner = $outer.affix('.inner').text('old inner text')
589
-
590
- replacePromise = up.replace('.outer, .inner', '/path')
591
-
592
- next =>
593
- expect(@lastRequest().requestHeaders['X-Up-Target']).toEqual('.outer')
594
-
595
- @respondWith """
596
- <div class="outer">
597
- new outer text
598
- <div class="inner">
599
- new inner text
600
- </div>
601
- </div>
602
- """
603
-
604
- next =>
605
- expect($('.outer')).toBeAttached()
606
- expect($('.outer').text()).toContain('new outer text')
607
- expect($('.inner')).toBeAttached()
608
- expect($('.inner').text()).toContain('new inner text')
609
-
610
- next.await =>
611
- promise = promiseState(replacePromise)
612
- promise.then (result) => expect(result.state).toEqual('fulfilled')
613
-
614
- it 'does not lose a { reveal: true } option if the first selector was merged into a subsequent selector', asyncSpec (next) ->
615
- revealStub = up.viewport.knife.mock('reveal')
616
-
617
- $outer = $fixture('.outer').text('old outer text')
618
- $inner = $outer.affix('.inner').text('old inner text')
619
-
620
- up.replace('.inner, .outer', '/path', reveal: true)
621
-
622
- next =>
623
- expect(@lastRequest().requestHeaders['X-Up-Target']).toEqual('.outer')
624
-
625
- @respondWith """
626
- <div class="outer">
627
- new outer text
628
- <div class="inner">
629
- new inner text
630
- </div>
631
- </div>
632
- """
633
-
634
- next =>
635
- expect($('.outer')).toBeAttached()
636
- expect($('.outer').text()).toContain('new outer text')
637
- expect($('.inner')).toBeAttached()
638
- expect($('.inner').text()).toContain('new inner text')
639
-
640
- expect(revealStub).toHaveBeenCalled()
641
-
642
-
643
- it 'does not lose a { reveal: string } option if the first selector was merged into a subsequent selector', asyncSpec (next) ->
644
- revealStub = up.viewport.knife.mock('reveal')
645
-
646
- $outer = $fixture('.outer').text('old outer text')
647
- $inner = $outer.affix('.inner').text('old inner text')
648
-
649
- up.replace('.inner, .outer', '/path', reveal: '.revealee')
650
-
651
- next =>
652
- expect(@lastRequest().requestHeaders['X-Up-Target']).toEqual('.outer')
653
-
654
- @respondWith """
655
- <div class="outer">
656
- new outer text
657
- <div class="inner">
658
- new inner text
659
- <div class="revealee">
660
- revealee text
661
- </div>
662
- </div>
663
- </div>
664
- """
665
-
666
- next =>
667
- expect($('.outer')).toBeAttached()
668
- expect($('.outer').text()).toContain('new outer text')
669
- expect($('.inner')).toBeAttached()
670
- expect($('.inner').text()).toContain('new inner text')
671
-
672
- expect(revealStub).toHaveBeenCalled()
673
- revealArg = revealStub.calls.mostRecent().args[0]
674
- expect(revealArg).toMatchSelector('.revealee')
675
-
676
-
677
- it 'replaces a single fragment if the nesting differs in current page and response', asyncSpec (next) ->
678
- $outer = $fixture('.outer').text('old outer text')
679
- $inner = $outer.affix('.inner').text('old inner text')
680
-
681
- replacePromise = up.replace('.outer, .inner', '/path')
682
-
683
- next =>
684
- @respondWith """
685
- <div class="inner">
686
- new inner text
687
- <div class="outer">
688
- new outer text
689
- </div>
690
- </div>
691
- """
692
-
693
- next =>
694
- expect($('.outer')).toBeAttached()
695
- expect($('.outer').text()).toContain('new outer text')
696
- expect($('.inner')).not.toBeAttached()
697
-
698
- next.await =>
699
- promise = promiseState(replacePromise)
700
- promise.then (result) => expect(result.state).toEqual('fulfilled')
701
-
702
- it 'does not crash if two selectors that are siblings in the current page are nested in the response', asyncSpec (next) ->
703
- $outer = $fixture('.one').text('old one text')
704
- $inner = $fixture('.two').text('old two text')
705
-
706
- replacePromise = up.replace('.one, .two', '/path')
707
-
708
- next =>
709
- @respondWith """
710
- <div class="one">
711
- new one text
712
- <div class="two">
713
- new two text
714
- </div>
715
- </div>
716
- """
717
-
718
- next =>
719
- expect($('.one')).toBeAttached()
720
- expect($('.one').text()).toContain('new one text')
721
- expect($('.two')).toBeAttached()
722
- expect($('.two').text()).toContain('new two text')
723
-
724
- next.await =>
725
- promise = promiseState(replacePromise)
726
- promise.then (result) => expect(result.state).toEqual('fulfilled')
727
-
728
- it 'does not crash if selectors that siblings in the current page are inversely nested in the response', asyncSpec (next) ->
729
- $outer = $fixture('.one').text('old one text')
730
- $inner = $fixture('.two').text('old two text')
731
-
732
- replacePromise = up.replace('.one, .two', '/path')
733
-
734
- next =>
735
- @respondWith """
736
- <div class="two">
737
- new two text
738
- <div class="one">
739
- new one text
740
- </div>
741
- </div>
742
- """
743
-
744
- next =>
745
- expect($('.one')).toBeAttached()
746
- expect($('.one').text()).toContain('new one text')
747
- expect($('.two')).toBeAttached()
748
- expect($('.two').text()).toContain('new two text')
749
-
750
- next.await =>
751
- promise = promiseState(replacePromise)
752
- promise.then (result) => expect(result.state).toEqual('fulfilled')
753
-
754
- it 'updates the first selector if the same element is targeted twice in a single replacement', asyncSpec (next) ->
755
- $one = $fixture('.one.alias').text('old one text')
756
-
757
- replacePromise = up.replace('.one, .alias', '/path')
758
-
759
- next =>
760
- expect(@lastRequest().requestHeaders['X-Up-Target']).toEqual('.one')
761
-
762
- @respondWith """
763
- <div class="one">
764
- new one text
765
- </div>
766
- """
767
-
768
- next =>
769
- expect($('.one')).toBeAttached()
770
- expect($('.one').text()).toContain('new one text')
771
-
772
- next.await =>
773
- promise = promiseState(replacePromise)
774
- promise.then (result) => expect(result.state).toEqual('fulfilled')
775
-
776
- it 'updates the first selector if the same element is prepended or appended twice in a single replacement', asyncSpec (next) ->
777
- $one = $fixture('.one').text('old one text')
778
-
779
- replacePromise = up.replace('.one:before, .one:after', '/path')
780
-
781
- next =>
782
- expect(@lastRequest().requestHeaders['X-Up-Target']).toEqual('.one:before')
783
-
784
- @respondWith """
785
- <div class="one">
786
- new one text
787
- </div>
788
- """
789
-
790
- next =>
791
- expect($('.one')).toBeAttached()
792
- expect($('.one').text()).toMatchText('new one text old one text')
793
-
794
- next.await =>
795
- promise = promiseState(replacePromise)
796
- promise.then (result) => expect(result.state).toEqual('fulfilled')
797
-
798
- it "updates the first selector if the same element is prepended, replaced and appended in a single replacement", asyncSpec (next) ->
799
- $elem = $fixture('.elem.alias1.alias2').text("old text")
800
-
801
- replacePromise = up.replace('.elem:before, .alias1, .alias2:after', '/path')
802
-
803
- next =>
804
- expect(@lastRequest().requestHeaders['X-Up-Target']).toEqual('.elem:before')
805
-
806
- @respondWith """
807
- <div class="elem alias1 alias2">
808
- new text
809
- </div>
810
- """
811
-
812
- next =>
813
- expect('.elem').toBeAttached()
814
- expect($('.elem').text()).toMatchText('new text old text')
815
-
816
- next.await =>
817
- promise = promiseState(replacePromise)
818
- promise.then (result) => expect(result.state).toEqual('fulfilled')
819
-
820
- it 'replaces the body if asked to replace the "html" selector'
821
-
822
- it 'prepends instead of replacing when the target has a :before pseudo-selector', (done) ->
823
- promise = up.replace('.middle:before', '/path')
824
- @respond()
825
- promise.then ->
826
- expect($('.before')).toHaveText('old-before')
827
- expect($('.middle')).toHaveText('new-middleold-middle')
828
- expect($('.after')).toHaveText('old-after')
829
- done()
830
-
831
- it 'appends instead of replacing when the target has a :after pseudo-selector', (done) ->
832
- promise = up.replace('.middle:after', '/path')
833
- @respond()
834
- promise.then ->
835
- expect($('.before')).toHaveText('old-before')
836
- expect($('.middle')).toHaveText('old-middlenew-middle')
837
- expect($('.after')).toHaveText('old-after')
838
- done()
839
-
840
- it "lets the developer choose between replacing/prepending/appending for each selector", (done) ->
841
- promise = up.replace('.before:before, .middle, .after:after', '/path')
842
- @respond()
843
- promise.then ->
844
- expect($('.before')).toHaveText('new-beforeold-before')
845
- expect($('.middle')).toHaveText('new-middle')
846
- expect($('.after')).toHaveText('old-afternew-after')
847
- done()
848
-
849
- it 'understands non-standard CSS selector extensions such as :has(...)', (done) ->
850
- $first = $fixture('.boxx#first')
851
- $firstChild = $('<span class="first-child">old first</span>').appendTo($first)
852
- $second = $fixture('.boxx#second')
853
- $secondChild = $('<span class="second-child">old second</span>').appendTo($second)
854
-
855
- promise = up.replace('.boxx:has(.first-child)', '/path')
856
- @respondWith """
857
- <div class="boxx" id="first">
858
- <span class="first-child">new first</span>
859
- </div>
860
- """
861
-
862
- promise.then ->
863
- expect($('#first span')).toHaveText('new first')
864
- expect($('#second span')).toHaveText('old second')
865
- done()
866
-
867
- describe 'when selectors are missing on the page before the request was made', ->
868
-
869
- beforeEach ->
870
- up.fragment.config.fallbacks = []
871
-
872
- it 'tries selectors from options.fallback before making a request', asyncSpec (next) ->
873
- $fixture('.box').text('old box')
874
- up.replace('.unknown', '/path', fallback: '.box')
875
-
876
- next => @respondWith '<div class="box">new box</div>'
877
- next => expect('.box').toHaveText('new box')
878
-
879
- it 'rejects the promise if all alternatives are exhausted', (done) ->
880
- promise = up.replace('.unknown', '/path', fallback: '.more-unknown')
881
- promise.catch (e) ->
882
- expect(e).toBeError(/Could not find target in current page/i)
883
- done()
884
-
885
- it 'considers a union selector to be missing if one of its selector-atoms are missing', asyncSpec (next) ->
886
- $fixture('.target').text('old target')
887
- $fixture('.fallback').text('old fallback')
888
- up.replace('.target, .unknown', '/path', fallback: '.fallback')
889
-
890
- next =>
891
- @respondWith """
892
- <div class="target">new target</div>
893
- <div class="fallback">new fallback</div>
894
- """
895
-
896
- next =>
897
- expect('.target').toHaveText('old target')
898
- expect('.fallback').toHaveText('new fallback')
899
-
900
- it 'tries a selector from up.fragment.config.fallbacks if options.fallback is missing', asyncSpec (next) ->
901
- up.fragment.config.fallbacks = ['.existing']
902
- $fixture('.existing').text('old existing')
903
- up.replace('.unknown', '/path')
904
- next => @respondWith '<div class="existing">new existing</div>'
905
- next => expect('.existing').toHaveText('new existing')
906
-
907
- it 'does not try a selector from up.fragment.config.fallbacks and rejects the promise if options.fallback is false', (done) ->
908
- up.fragment.config.fallbacks = ['.existing']
909
- $fixture('.existing').text('old existing')
910
- up.replace('.unknown', '/path', fallback: false).catch (e) ->
911
- expect(e).toBeError(/Could not find target in current page/i)
912
- done()
913
-
914
- describe 'when selectors are missing on the page after the request was made', ->
915
-
916
- beforeEach ->
917
- up.fragment.config.fallbacks = []
918
-
919
- it 'tries selectors from options.fallback before swapping elements', asyncSpec (next) ->
920
- $target = $fixture('.target').text('old target')
921
- $fallback = $fixture('.fallback').text('old fallback')
922
- up.replace('.target', '/path', fallback: '.fallback')
923
- $target.remove()
924
-
925
- next =>
926
- @respondWith """
927
- <div class="target">new target</div>
928
- <div class="fallback">new fallback</div>
929
- """
930
-
931
- next =>
932
- expect('.fallback').toHaveText('new fallback')
933
-
934
- it 'rejects the promise if all alternatives are exhausted', (done) ->
935
- $target = $fixture('.target').text('old target')
936
- $fallback = $fixture('.fallback').text('old fallback')
937
- promise = up.replace('.target', '/path', fallback: '.fallback')
938
- $target.remove()
939
- $fallback.remove()
940
-
941
- u.task =>
942
- @respondWith """
943
- <div class="target">new target</div>
944
- <div class="fallback">new fallback</div>
945
- """
946
-
947
- u.task =>
948
- promiseState(promise).then (result) ->
949
- expect(result.state).toEqual('rejected')
950
- expect(result.value).toBeError(/Could not find target in current page/i)
951
- done()
952
-
953
- it 'considers a union selector to be missing if one of its selector-atoms are missing', asyncSpec (next) ->
954
- $target = $fixture('.target').text('old target')
955
- $target2 = $fixture('.target2').text('old target2')
956
- $fallback = $fixture('.fallback').text('old fallback')
957
- up.replace('.target, .target2', '/path', fallback: '.fallback')
958
- $target2.remove()
959
-
960
- next =>
961
- @respondWith """
962
- <div class="target">new target</div>
963
- <div class="target2">new target2</div>
964
- <div class="fallback">new fallback</div>
965
- """
966
- next =>
967
- expect('.target').toHaveText('old target')
968
- expect('.fallback').toHaveText('new fallback')
969
-
970
- it 'tries a selector from up.fragment.config.fallbacks if options.fallback is missing', asyncSpec (next) ->
971
- up.fragment.config.fallbacks = ['.fallback']
972
- $target = $fixture('.target').text('old target')
973
- $fallback = $fixture('.fallback').text('old fallback')
974
- up.replace('.target', '/path')
975
- $target.remove()
976
-
977
- next =>
978
- @respondWith """
979
- <div class="target">new target</div>
980
- <div class="fallback">new fallback</div>
981
- """
982
-
983
- next =>
984
- expect('.fallback').toHaveText('new fallback')
985
-
986
- it 'does not try a selector from up.fragment.config.fallbacks and rejects the promise if options.fallback is false', (done) ->
987
- up.fragment.config.fallbacks = ['.fallback']
988
- $target = $fixture('.target').text('old target')
989
- $fallback = $fixture('.fallback').text('old fallback')
990
- promise = up.replace('.target', '/path', fallback: false)
991
- $target.remove()
992
-
993
- u.task =>
994
- @respondWith """
995
- <div class="target">new target</div>
996
- <div class="fallback">new fallback</div>
997
- """
998
-
999
- promise.catch (e) ->
1000
- expect(e).toBeError(/Could not find target in current page/i)
1001
- done()
1002
-
1003
- describe 'when selectors are missing in the response', ->
1004
-
1005
- beforeEach ->
1006
- up.fragment.config.fallbacks = []
1007
-
1008
- it 'tries selectors from options.fallback before swapping elements', asyncSpec (next) ->
1009
- $target = $fixture('.target').text('old target')
1010
- $fallback = $fixture('.fallback').text('old fallback')
1011
- up.replace('.target', '/path', fallback: '.fallback')
1012
-
1013
- next =>
1014
- @respondWith """
1015
- <div class="fallback">new fallback</div>
1016
- """
1017
-
1018
- next =>
1019
- expect('.target').toHaveText('old target')
1020
- expect('.fallback').toHaveText('new fallback')
1021
-
1022
- describe 'if all alternatives are exhausted', ->
1023
-
1024
- it 'rejects the promise', (done) ->
1025
- $target = $fixture('.target').text('old target')
1026
- $fallback = $fixture('.fallback').text('old fallback')
1027
- promise = up.replace('.target', '/path', fallback: '.fallback')
1028
-
1029
- u.task =>
1030
- @respondWith '<div class="unexpected">new unexpected</div>'
1031
-
1032
- promise.catch (e) ->
1033
- expect(e).toBeError(/Could not find target in response/i)
1034
- done()
1035
-
1036
- it 'shows a link to open the unexpected response', (done) ->
1037
- $target = $fixture('.target').text('old target')
1038
- $fallback = $fixture('.fallback').text('old fallback')
1039
- promise = up.replace('.target', '/path', fallback: '.fallback')
1040
- navigate = spyOn(up.browser, 'navigate')
1041
-
1042
- u.task =>
1043
- @respondWith '<div class="unexpected">new unexpected</div>'
1044
-
1045
- promise.catch (e) ->
1046
- $toast = $('.up-toast')
1047
- expect($toast).toBeAttached()
1048
- $inspectLink = $toast.find(".up-toast-action:contains('Open response')")
1049
- expect($inspectLink).toBeAttached()
1050
- expect(navigate).not.toHaveBeenCalled()
1051
-
1052
- Trigger.clickSequence($inspectLink)
1053
-
1054
- u.task =>
1055
- expect(navigate).toHaveBeenCalledWith('/path', {})
1056
- done()
1057
-
1058
- it 'considers a union selector to be missing if one of its selector-atoms are missing', asyncSpec (next) ->
1059
- $target = $fixture('.target').text('old target')
1060
- $target2 = $fixture('.target2').text('old target2')
1061
- $fallback = $fixture('.fallback').text('old fallback')
1062
- up.replace('.target, .target2', '/path', fallback: '.fallback')
1063
-
1064
- next =>
1065
- @respondWith """
1066
- <div class="target">new target</div>
1067
- <div class="fallback">new fallback</div>
1068
- """
1069
-
1070
- next =>
1071
- expect('.target').toHaveText('old target')
1072
- expect('.target2').toHaveText('old target2')
1073
- expect('.fallback').toHaveText('new fallback')
1074
-
1075
- it 'tries a selector from up.fragment.config.fallbacks if options.fallback is missing', asyncSpec (next) ->
1076
- up.fragment.config.fallbacks = ['.fallback']
1077
- $target = $fixture('.target').text('old target')
1078
- $fallback = $fixture('.fallback').text('old fallback')
1079
- up.replace('.target', '/path')
1080
-
1081
- next =>
1082
- @respondWith '<div class="fallback">new fallback</div>'
1083
-
1084
- next =>
1085
- expect('.target').toHaveText('old target')
1086
- expect('.fallback').toHaveText('new fallback')
1087
-
1088
- it 'does not try a selector from up.fragment.config.fallbacks and rejects the promise if options.fallback is false', (done) ->
1089
- up.fragment.config.fallbacks = ['.fallback']
1090
- $target = $fixture('.target').text('old target')
1091
- $fallback = $fixture('.fallback').text('old fallback')
1092
- promise = up.replace('.target', '/path', fallback: false)
1093
-
1094
- u.task =>
1095
- @respondWith '<div class="fallback">new fallback</div>'
1096
-
1097
- promise.catch (e) ->
1098
- expect(e).toBeError(/Could not find target in response/i)
1099
- done()
1100
-
1101
- describe 'execution of scripts', ->
1102
-
1103
- beforeEach ->
1104
- window.scriptTagExecuted = jasmine.createSpy('scriptTagExecuted')
1105
-
1106
- describe 'inline scripts', ->
1107
-
1108
- it 'does not execute inline script tags', (done) ->
1109
- @responseText = """
1110
- <div class="middle">
1111
- new-middle
1112
- <script type="text/javascript">
1113
- window.scriptTagExecuted()
1114
- </script>
1115
- </div>
1116
- """
1117
-
1118
- promise = up.replace('.middle', '/path')
1119
- @respond()
1120
-
1121
- promise.then ->
1122
- expect(window.scriptTagExecuted).not.toHaveBeenCalled()
1123
- done()
1124
-
1125
- it 'does not crash when the new fragment contains inline script tag that is followed by another sibling (bugfix)', (done) ->
1126
- @responseText = """
1127
- <div class="middle">
1128
- <div>new-middle-before</div>
1129
- <script type="text/javascript">
1130
- window.scriptTagExecuted()
1131
- </script>
1132
- <div>new-middle-after</div>
1133
- </div>
1134
- """
1135
-
1136
- promise = up.replace('.middle', '/path')
1137
- @respond()
1138
-
1139
- u.task ->
1140
- promiseState(promise).then (result) ->
1141
- expect(result.state).toEqual('fulfilled')
1142
- expect(window.scriptTagExecuted).not.toHaveBeenCalled()
1143
- done()
1144
-
1145
- describe 'linked scripts', ->
1146
-
1147
- beforeEach ->
1148
- # Add a cache-buster to each path so the browser cache is guaranteed to be irrelevant
1149
- @linkedScriptPath = "/assets/fixtures/linked_script.js?cache-buster=#{Math.random().toString()}"
1150
-
1151
- it 'does not execute linked scripts to prevent re-inclusion of javascript inserted before the closing body tag', (done) ->
1152
- @responseText = """
1153
- <div class="middle">
1154
- new-middle
1155
- <script type="text/javascript" src="#{@linkedScriptPath}">
1156
- alert("inside")
1157
- </script>
1158
- </div>
1159
- """
1160
-
1161
- promise = up.replace('.middle', '/path')
1162
- @respond()
1163
-
1164
- promise.then =>
1165
-
1166
- # Must respond to this request, since jQuery makes them async: false
1167
- if u.contains(@lastRequest().url, 'linked_script')
1168
- @respondWith('window.scriptTagExecuted()')
1169
-
1170
- # Now wait for jQuery to parse out <script> tags and fetch the linked scripts.
1171
- # This actually happens with jasmine_ajax's fake XHR object.
1172
- u.task =>
1173
- expect(jasmine.Ajax.requests.count()).toEqual(1)
1174
- expect(@lastRequest().url).not.toContain('linked_script')
1175
- expect(window.scriptTagExecuted).not.toHaveBeenCalled()
1176
- done()
1177
-
1178
- describe '<noscript> tags', ->
1179
-
1180
- it 'parses <noscript> contents as text, not DOM nodes (since it will be placed in a scripting-capable browser)', (done) ->
1181
- @responseText = """
1182
- <div class="middle">
1183
- <noscript>
1184
- <img src="foo.png">
1185
- </noscript>
1186
- </div>
1187
- """
1188
-
1189
- promise = up.replace('.middle', '/path')
1190
- @respond()
1191
-
1192
- promise.then ->
1193
- $noscript = $('.middle noscript')
1194
- text = $noscript.text().trim()
1195
- expect(text).toEqual('<img src="foo.png">')
1196
- done()
1197
-
1198
- it 'parses <noscript> contents with multiple lines as text, not DOM nodes', (done) ->
1199
- @responseText = """
1200
- <div class="middle">
1201
- <noscript>
1202
- <img src="foo.png">
1203
- <img src="bar.png">
1204
- </noscript>
1205
- </div>
1206
- """
1207
-
1208
- promise = up.replace('.middle', '/path')
1209
- @respond()
1210
-
1211
- promise.then ->
1212
- $noscript = $('.middle noscript')
1213
- text = $noscript.text().trim()
1214
- expect(text).toMatch(/<img src="foo\.png">\s+<img src="bar\.png">/)
1215
- done()
1216
-
1217
- it 'parses multiple <noscript> tags in the same fragment as text, not DOM nodes', (done) ->
1218
- @responseText = """
1219
- <div class="middle">
1220
- <noscript>
1221
- <img src="foo.png">
1222
- </noscript>
1223
- <noscript>
1224
- <img src="bar.png">
1225
- </noscript>
1226
- </div>
1227
- """
1228
-
1229
- promise = up.replace('.middle', '/path')
1230
- @respond()
1231
-
1232
- promise.then ->
1233
- $noscripts = $('.middle noscript')
1234
- expect($noscripts.length).toBe(2)
1235
- text0 = $noscripts[0].textContent.trim()
1236
- text1 = $noscripts[1].textContent.trim()
1237
- expect(text0).toEqual('<img src="foo.png">')
1238
- expect(text1).toEqual('<img src="bar.png">')
1239
- done()
1240
-
1241
- if up.browser.canCustomElements()
1242
-
1243
- describe 'custom elements', ->
1244
-
1245
- beforeAll ->
1246
- TestComponent = ->
1247
- instance = Reflect.construct(HTMLElement, [], TestComponent)
1248
- instance.innerHTML = 'text from component'
1249
- up.emit('test-component:new')
1250
- instance
1251
- Object.setPrototypeOf(TestComponent.prototype, HTMLElement.prototype)
1252
- Object.setPrototypeOf(TestComponent, HTMLElement)
1253
-
1254
- window.customElements.define('test-component-activation', TestComponent)
1255
-
1256
- it 'activates custom elements in inserted fragments', (done) ->
1257
- @responseText = """
1258
- <div class="middle">
1259
- <test-component-activation></test-component-activation>
1260
- </div>
1261
- """
1262
-
1263
- promise = up.replace('.middle', '/path')
1264
- @respond()
1265
-
1266
- promise.then ->
1267
- expect('.middle test-component-activation').toHaveText('text from component')
1268
- done()
1269
-
1270
- it 'does not activate custom elements outside of inserted fragments', (done) ->
1271
- constructorSpy = jasmine.createSpy('constructor called')
1272
- up.on('test-component:new', constructorSpy)
1273
-
1274
- @responseText = """
1275
- <div class="before">
1276
- <test-component-activation></test-component-activation>
1277
- </div>
1278
- <div class="middle">
1279
- <test-component-activation></test-component-activation>
1280
- </div>
1281
- <div class="after">
1282
- <test-component-activation></test-component-activation>
1283
- </div>
1284
- """
1285
-
1286
- promise = up.replace('.middle', '/path')
1287
- @respond()
1288
-
1289
- promise.then =>
1290
- expect(constructorSpy.calls.count()).toBe(1)
1291
- done()
1292
-
1293
-
1294
- describe 'with { restoreScroll: true } option', ->
1295
-
1296
- beforeEach ->
1297
- up.history.config.enabled = true
1298
-
1299
- it 'restores the scroll positions of all viewports around the target', asyncSpec (next) ->
1300
-
1301
- $viewport = $fixture('div[up-viewport] .element').css
1302
- 'height': '100px'
1303
- 'width': '100px'
1304
- 'overflow-y': 'scroll'
1305
-
1306
- respond = =>
1307
- @lastRequest().respondWith
1308
- status: 200
1309
- contentType: 'text/html'
1310
- responseText: '<div class="element" style="height: 300px"></div>'
1311
-
1312
- up.replace('.element', '/foo')
1313
-
1314
- next => respond()
1315
- next => $viewport.scrollTop(65)
1316
- next => up.replace('.element', '/bar')
1317
- next => respond()
1318
- next => $viewport.scrollTop(0)
1319
- next.await => up.replace('.element', '/foo', restoreScroll: true)
1320
- # No need to respond because /foo has been cached before
1321
- next => expect($viewport.scrollTop()).toEqual(65)
1322
-
1323
- describe 'with { reveal } option', ->
1324
-
1325
- beforeEach ->
1326
- up.history.config.enabled = true
1327
-
1328
- @revealedHTML = []
1329
- @revealedText = []
1330
- @revealOptions = {}
1331
-
1332
- @revealMock = up.viewport.knife.mock('reveal').and.callFake (element, options) =>
1333
- @revealedHTML.push element.outerHTML
1334
- @revealedText.push element.textContent.trim()
1335
- @revealOptions = options
1336
- Promise.resolve()
1337
-
1338
- it 'reveals a new element before it is being replaced', asyncSpec (next) ->
1339
- up.replace('.middle', '/path', reveal: true)
1340
-
1341
- next =>
1342
- @respond()
1343
-
1344
- next =>
1345
- expect(@revealMock).not.toHaveBeenCalledWith(@$oldMiddle[0])
1346
- expect(@revealedText).toEqual ['new-middle']
1347
-
1348
- it 'allows to pass another selector to reveal', asyncSpec (next)->
1349
- $other = $fixture('.other').text('other text')
1350
-
1351
- up.replace('.middle', '/path', reveal: '.other')
1352
-
1353
- next =>
1354
- @respond()
1355
-
1356
- next =>
1357
- expect(@revealedText).toEqual ['other text']
1358
-
1359
- it 'allows to refer to the replacement { origin } as "&" in the { reveal } selector', asyncSpec (next) ->
1360
- $origin = $fixture('.origin').text('origin text')
1361
-
1362
- up.replace('.middle', '/path', reveal: '&', origin: '.origin')
1363
-
1364
- next =>
1365
- @respond()
1366
-
1367
- next =>
1368
- expect(@revealedText).toEqual ['origin text']
1369
-
1370
- describe 'when the server responds with an error code', ->
1371
-
1372
- it 'ignores the { reveal } option', asyncSpec (next) ->
1373
- $failTarget = $fixture('.fail-target')
1374
- up.replace('.middle', '/path', failTarget: '.fail-target', reveal: true)
1375
-
1376
- next =>
1377
- @respond(status: 500)
1378
-
1379
- next =>
1380
- expect(@revealMock).not.toHaveBeenCalled()
1381
-
1382
- it 'accepts a { failReveal } option for error responses', asyncSpec (next) ->
1383
- $failTarget = $fixture('.fail-target').text('old fail target text')
1384
- up.replace('.middle', '/path', failTarget: '.fail-target', reveal: false, failReveal: true)
1385
-
1386
- next =>
1387
- @respondWith
1388
- status: 500
1389
- responseText: """
1390
- <div class="fail-target">
1391
- new fail target text
1392
- </div>
1393
- """
1394
-
1395
- next =>
1396
- expect(@revealedText).toEqual ['new fail target text']
1397
-
1398
- it 'allows to refer to the replacement { origin } as "&" in the { failTarget } selector', asyncSpec (next) ->
1399
- $origin = $fixture('.origin').text('origin text')
1400
- $failTarget = $fixture('.fail-target').text('old fail target text')
1401
- up.replace('.middle', '/path', failTarget: '.fail-target', reveal: false, failReveal: '&', origin: $origin)
1402
-
1403
- next =>
1404
- @respondWith
1405
- status: 500
1406
- responseText: """
1407
- <div class="fail-target">
1408
- new fail target text
1409
- </div>
1410
- """
1411
-
1412
- next =>
1413
- expect(@revealedText).toEqual ['origin text']
1414
-
1415
- describe 'when more than one fragment is replaced', ->
1416
-
1417
- it 'only reveals the first fragment', asyncSpec (next) ->
1418
- up.replace('.middle, .after', '/path', reveal: true)
1419
-
1420
- next =>
1421
- @respond()
1422
-
1423
- next =>
1424
- expect(@revealMock).not.toHaveBeenCalledWith(@$oldMiddle[0])
1425
- expect(@revealedText).toEqual ['new-middle']
1426
-
1427
- describe 'when there is an anchor #hash in the URL', ->
1428
-
1429
- it 'scrolls to the top of an element with the ID of that #hash', asyncSpec (next) ->
1430
- up.replace('.middle', '/path#hash', reveal: true)
1431
- @responseText =
1432
- """
1433
- <div class="middle">
1434
- <div id="hash"></div>
1435
- </div>
1436
- """
1437
-
1438
- next =>
1439
- @respond()
1440
-
1441
- next =>
1442
- expect(@revealedHTML).toEqual ['<div id="hash"></div>']
1443
- expect(@revealOptions).toEqual jasmine.objectContaining(top: true)
1444
-
1445
- it "scrolls to the top of an <a> element with the name of that hash", asyncSpec (next) ->
1446
- up.replace('.middle', '/path#three', reveal: true)
1447
- @responseText =
1448
- """
1449
- <div class="middle">
1450
- <a name="three"></a>
1451
- </div>
1452
- """
1453
-
1454
- next =>
1455
- @respond()
1456
-
1457
- next =>
1458
- expect(@revealedHTML).toEqual ['<a name="three"></a>']
1459
- expect(@revealOptions).toEqual jasmine.objectContaining(top: true)
1460
-
1461
- it "scrolls to a hash that includes a dot character ('.') (bugfix)", asyncSpec (next) ->
1462
- up.replace('.middle', '/path#foo.bar', reveal: true)
1463
- @responseText =
1464
- """
1465
- <div class="middle">
1466
- <a name="foo.bar"></a>
1467
- </div>
1468
- """
1469
-
1470
- next =>
1471
- @respond()
1472
-
1473
- next =>
1474
- expect(@revealedHTML).toEqual ['<a name="foo.bar"></a>']
1475
- expect(@revealOptions).toEqual jasmine.objectContaining(top: true)
1476
-
1477
- it 'does not scroll if { reveal: false } is also set', asyncSpec (next) ->
1478
- up.replace('.middle', '/path#hash', reveal: false)
1479
- @responseText =
1480
- """
1481
- <div class="middle">
1482
- <div id="hash"></div>
1483
- </div>
1484
- """
1485
-
1486
- next =>
1487
- @respond()
1488
-
1489
- next =>
1490
- expect(@revealMock).not.toHaveBeenCalled()
1491
-
1492
- it 'reveals multiple consecutive #hash targets with the same URL (bugfix)', asyncSpec (next) ->
1493
- up.replace('.middle', '/path#two', reveal: true)
1494
- @responseText =
1495
- """
1496
- <div class="middle">
1497
- <div id="one">one</div>
1498
- <div id="two">two</div>
1499
- <div id="three">three</div>
1500
- </div>
1501
- """
1502
-
1503
- next =>
1504
- @respond()
1505
- up.replace('.middle', '/path#three', reveal: true)
1506
- # response is already cached
1507
-
1508
- next =>
1509
- expect(@revealedText).toEqual ['two', 'three']
1510
-
1511
- it "does not scroll if there is no element with the ID of that #hash", asyncSpec (next) ->
1512
- up.replace('.middle', '/path#hash', reveal: true)
1513
- @responseText =
1514
- """
1515
- <div class="middle">
1516
- </div>
1517
- """
1518
-
1519
- next =>
1520
- @respond()
1521
-
1522
- next =>
1523
- expect(@revealMock).not.toHaveBeenCalled()
1524
-
1525
-
1526
- it 'reveals a new element that is being appended', (done) ->
1527
- promise = up.replace('.middle:after', '/path', reveal: true)
1528
- @respond()
1529
- promise.then =>
1530
- expect(@revealMock).not.toHaveBeenCalledWith(@$oldMiddle[0])
1531
- # Text nodes are wrapped in a .up-insertion container so we can
1532
- # animate them and measure their position/size for scrolling.
1533
- # This is not possible for container-less text nodes.
1534
- expect(@revealedHTML).toEqual ['<div class="up-insertion">new-middle</div>']
1535
- # Show that the wrapper is done after the insertion.
1536
- expect($('.up-insertion')).not.toBeAttached()
1537
- done()
1538
-
1539
- it 'reveals a new element that is being prepended', (done) ->
1540
- promise = up.replace('.middle:before', '/path', reveal: true)
1541
- @respond()
1542
- promise.then =>
1543
- expect(@revealMock).not.toHaveBeenCalledWith(@$oldMiddle[0])
1544
- # Text nodes are wrapped in a .up-insertion container so we can
1545
- # animate them and measure their position/size for scrolling.
1546
- # This is not possible for container-less text nodes.
1547
- expect(@revealedHTML).toEqual ['<div class="up-insertion">new-middle</div>']
1548
- # Show that the wrapper is done after the insertion.
1549
- expect($('.up-insertion')).not.toBeAttached()
1550
- done()
1551
-
1552
- it 'uses a { failTransition } option if the request failed'
1553
-
1554
- describeFallback 'canPushState', ->
1555
-
1556
- it 'makes a full page load', asyncSpec (next) ->
1557
- spyOn(up.browser, 'navigate')
1558
- up.replace('.selector', '/path')
1559
-
1560
- next =>
1561
- expect(up.browser.navigate).toHaveBeenCalledWith('/path', jasmine.anything())
1562
-
1563
- describe 'up.extract', ->
1564
-
1565
- it 'Updates a selector on the current page with the same selector from the given HTML string', asyncSpec (next) ->
1566
-
1567
- $fixture('.before').text('old-before')
1568
- $fixture('.middle').text('old-middle')
1569
- $fixture('.after').text('old-after')
1570
-
1571
- html =
1572
- """
1573
- <div class="before">new-before</div>
1574
- <div class="middle">new-middle</div>
1575
- <div class="after">new-after</div>
1576
- """
1577
-
1578
- up.extract('.middle', html)
1579
-
1580
- next ->
1581
- expect($('.before')).toHaveText('old-before')
1582
- expect($('.middle')).toHaveText('new-middle')
1583
- expect($('.after')).toHaveText('old-after')
1584
-
1585
- it "throws an error if the selector can't be found on the current page", (done) ->
1586
- html = '<div class="foo-bar">text</div>'
1587
- promise = up.extract('.foo-bar', html)
1588
- promiseState(promise).then (result) =>
1589
- expect(result.state).toEqual('rejected')
1590
- expect(result.value).toMatch(/Could not find selector in current page, modal or popup/i)
1591
- done()
1592
-
1593
- it "throws an error if the selector can't be found in the given HTML string", (done) ->
1594
- $fixture('.foo-bar')
1595
- promise = up.extract('.foo-bar', '')
1596
- promiseState(promise).then (result) =>
1597
- expect(result.state).toEqual('rejected')
1598
- expect(result.value).toMatch(/Could not find selector in response/i)
1599
- done()
1600
-
1601
- it "ignores an element that matches the selector but also matches .up-destroying", (done) ->
1602
- html = '<div class="foo-bar">text</div>'
1603
- $fixture('.foo-bar.up-destroying')
1604
- promise = up.extract('.foo-bar', html)
1605
- promiseState(promise).then (result) =>
1606
- expect(result.state).toEqual('rejected')
1607
- expect(result.value).toMatch(/Could not find selector/i)
1608
- done()
1609
-
1610
- it "ignores an element that matches the selector but also has a parent matching .up-destroying", (done) ->
1611
- html = '<div class="foo-bar">text</div>'
1612
- $parent = $fixture('.up-destroying')
1613
- $child = $fixture('.foo-bar').appendTo($parent)
1614
- promise = up.extract('.foo-bar', html)
1615
- promiseState(promise).then (result) =>
1616
- expect(result.state).toEqual('rejected')
1617
- expect(result.value).toMatch(/Could not find selector/i)
1618
- done()
1619
-
1620
- it 'only replaces the first element matching the selector', asyncSpec (next) ->
1621
- html = '<div class="foo-bar">text</div>'
1622
- $fixture('.foo-bar')
1623
- $fixture('.foo-bar')
1624
- up.extract('.foo-bar', html)
1625
-
1626
- next =>
1627
- $elements = $('.foo-bar')
1628
- expect($($elements.get(0)).text()).toEqual('text')
1629
- expect($($elements.get(1)).text()).toEqual('')
1630
-
1631
- it 'focuses an [autofocus] element in the new fragment', asyncSpec (next) ->
1632
- $fixture('.foo-bar')
1633
- up.extract '.foo-bar', """
1634
- <form class='foo-bar'>
1635
- <input class="autofocused-input" autofocus>
1636
- </form>
1637
- """
1638
-
1639
- next =>
1640
- input = $('.autofocused-input').get(0)
1641
- expect(input).toBeGiven()
1642
- expect(document.activeElement).toBe(input)
1643
-
1644
-
1645
- # up.extract
1646
- it 'emits an up:fragment:destroyed event on the former parent element after the element has been removed from the DOM', (done) ->
1647
- $parent = $fixture('.parent')
1648
- $element = $parent.affix('.element.v1').text('v1')
1649
- expect($element).toBeAttached()
1650
-
1651
- spy = jasmine.createSpy('event listener')
1652
- $parent[0].addEventListener 'up:fragment:destroyed', (event) ->
1653
- spy(event.target, event.fragment, up.specUtil.isDetached($element))
1654
-
1655
- extractDone = up.extract('.element', '<div class="element v2">v2</div>')
1656
-
1657
- extractDone.then ->
1658
- expect(spy).toHaveBeenCalledWith($parent[0], $element[0], true)
1659
- done()
1660
-
1661
- describe 'cleaning up', ->
1662
-
1663
- it 'calls destructors on the old element', asyncSpec (next) ->
1664
- destructor = jasmine.createSpy('destructor')
1665
- up.$compiler '.container', ($element) ->
1666
- -> destructor($element.text())
1667
- $container = $fixture('.container').text('old text')
1668
- up.hello($container)
1669
- up.extract('.container', '<div class="container">new text</div>')
1670
-
1671
- next =>
1672
- expect('.container').toHaveText('new text')
1673
- expect(destructor).toHaveBeenCalledWith('old text')
1674
-
1675
- it 'calls destructors on the old element after a { transition }', (done) ->
1676
- destructor = jasmine.createSpy('destructor')
1677
- up.$compiler '.container', ($element) ->
1678
- -> destructor($element.text())
1679
- $container = $fixture('.container').text('old text')
1680
- up.hello($container)
1681
-
1682
- up.extract('.container', '<div class="container">new text</div>', transition: 'cross-fade', duration: 100)
1683
-
1684
- u.timer 50, =>
1685
- expect(destructor).not.toHaveBeenCalled()
1686
-
1687
- u.timer 220, =>
1688
- expect('.container').toHaveText('new text')
1689
- expect(destructor).toHaveBeenCalledWith('old text')
1690
- done()
1691
-
1692
- it 'calls destructors when the replaced element is a singleton element like <body> (bugfix)', asyncSpec (next) ->
1693
- # shouldSwapElementsDirectly() is true for body, but can't have the example replace the Jasmine test runner UI
1694
- up.element.knife.mock('isSingleton').and.callFake (element) -> e.matches(element, '.container')
1695
- destructor = jasmine.createSpy('destructor')
1696
- up.$compiler '.container', -> destructor
1697
- $container = $fixture('.container')
1698
- up.hello($container)
1699
- up.extract('.container', '<div class="container">new text</div>')
1700
-
1701
- next =>
1702
- expect('.container').toHaveText('new text')
1703
- expect(destructor).toHaveBeenCalled()
1704
-
1705
- it 'marks the old element as .up-destroying before destructors', (done) ->
1706
- destructor = jasmine.createSpy('destructor')
1707
- up.$compiler '.container', ($element) ->
1708
- -> destructor($element.text(), $element.is('.up-destroying'))
1709
- $container = $fixture('.container').text('old text')
1710
- up.hello($container)
1711
-
1712
- extractDone = up.extract('.container', '<div class="container">new text</div>')
1713
-
1714
- extractDone.then ->
1715
- expect('.container').toHaveText('new text')
1716
- expect(destructor).toHaveBeenCalledWith('old text', true)
1717
- done()
1718
-
1719
- it 'marks the old element as .up-destroying before destructors after a { transition }', (done) ->
1720
- destructor = jasmine.createSpy('destructor')
1721
- up.$compiler '.container', ($element) ->
1722
- -> destructor($element.text(), $element.is('.up-destroying'))
1723
- $container = $fixture('.container').text('old text')
1724
- up.hello($container)
1725
-
1726
- extractDone = up.extract('.container', '<div class="container">new text</div>', transition: 'cross-fade', duration: 100)
1727
-
1728
- extractDone.then ->
1729
- expect('.container').toHaveText('new text')
1730
- expect(destructor).toHaveBeenCalledWith('old text', true)
1731
- done()
1732
-
1733
- it 'calls destructors while the element is still attached to the DOM, so destructors see ancestry and events bubble up', asyncSpec (next) ->
1734
- spy = jasmine.createSpy('parent spy')
1735
- up.$compiler '.element', ($element) ->
1736
- return -> spy($element.text(), $element.parent())
1737
-
1738
- $parent = $fixture('.parent')
1739
- $element = $parent.affix('.element').text('old text')
1740
- up.hello($element)
1741
-
1742
- up.extract '.element', '<div class="element">new text</div>'
1743
-
1744
- next =>
1745
- expect(spy).toHaveBeenCalledWith('old text', $parent)
1746
-
1747
- it 'calls destructors while the element is still attached to the DOM when also using a { transition }', (done) ->
1748
- spy = jasmine.createSpy('parent spy')
1749
- up.$compiler '.element', ($element) ->
1750
- return ->
1751
- # We must seek .parent in our ancestry, because our direct parent() is an .up-bounds container
1752
- spy($element.text(), $element.closest('.parent'))
1753
-
1754
- $parent = $fixture('.parent')
1755
- $element = $parent.affix('.element').text('old text')
1756
- up.hello($element)
1757
-
1758
- extractDone = up.extract('.element', '<div class="element">new text</div>', transition: 'cross-fade', duration: 30)
1759
-
1760
- extractDone.then ->
1761
- expect(spy).toHaveBeenCalledWith('old text', $parent)
1762
- done()
1763
-
1764
-
1765
- describe 'with { transition } option', ->
1766
-
1767
- it 'morphs between the old and new element', asyncSpec (next) ->
1768
- $fixture('.element.v1').text('version 1')
1769
- up.extract('.element', '<div class="element v2">version 2</div>', transition: 'cross-fade', duration: 200, easing: 'linear')
1770
-
1771
- $old = undefined
1772
- $new = undefined
1773
-
1774
- next =>
1775
- $old = $('.element.v1')
1776
- $new = $('.element.v2')
1777
-
1778
- expect($old).toHaveLength(1)
1779
- expect($old).toHaveOpacity(1.0, 0.15)
1780
-
1781
- expect($new).toHaveLength(1)
1782
- expect($new).toHaveOpacity(0.0, 0.15)
1783
-
1784
- next.after 100, =>
1785
- expect($old).toHaveOpacity(0.5, 0.3)
1786
- expect($new).toHaveOpacity(0.5, 0.3)
1787
-
1788
- next.after (100 + 70), =>
1789
- expect($new).toHaveOpacity(1.0, 0.1)
1790
- expect($old).toBeDetached()
1791
-
1792
-
1793
- it 'ignores a { transition } option when replacing a singleton element like <body>', asyncSpec (next) ->
1794
- # shouldSwapElementsDirectly() is true for body, but can't have the example replace the Jasmine test runner UI
1795
- spyOn(up.element, 'isSingleton').and.callFake (element) -> e.matches(element, '.container')
1796
-
1797
- $fixture('.container').text('old text')
1798
-
1799
- extractDone = jasmine.createSpy()
1800
- promise = up.extract('.container', '<div class="container">new text</div>', transition: 'cross-fade', duration: 200)
1801
- promise.then(extractDone)
1802
-
1803
- next =>
1804
- # See that we've already immediately swapped the element and ignored the duration of 200ms
1805
- expect(extractDone).toHaveBeenCalled()
1806
- expect($('.container').length).toEqual(1)
1807
- expect($('.container')).toHaveOpacity(1.0)
1808
-
1809
- it 'marks the old fragment as .up-destroying during the transition', asyncSpec (next) ->
1810
- $fixture('.element').text('version 1')
1811
- up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 200)
1812
-
1813
- next =>
1814
- $version1 = $('.element:contains("version 1")')
1815
- expect($version1).toHaveLength(1)
1816
- expect($version1).toHaveClass('up-destroying')
1817
-
1818
- $version2 = $('.element:contains("version 2")')
1819
- expect($version2).toHaveLength(1)
1820
- expect($version2).not.toHaveClass('up-destroying')
1821
-
1822
- # extract with { transition } option
1823
- it 'emits an up:fragment:destroyed event on the former parent element after the element has been removed from the DOM', (done) ->
1824
- $parent = $fixture('.parent')
1825
- $element = $parent.affix('.element.v1').text('v1')
1826
- expect($element).toBeAttached()
1827
-
1828
- spy = jasmine.createSpy('event listener')
1829
- $parent[0].addEventListener 'up:fragment:destroyed', (event) ->
1830
- spy(event.target, event.fragment, up.specUtil.isDetached($element))
1831
-
1832
- extractDone = up.extract('.element', '<div class="element v2">v2</div>', transition: 'cross-fade', duration: 50)
1833
-
1834
- extractDone.then ->
1835
- expect(spy).toHaveBeenCalledWith($parent[0], $element[0], true)
1836
- done()
1837
-
1838
-
1839
- it 'cancels an existing transition by instantly jumping to the last frame', asyncSpec (next) ->
1840
- $fixture('.element.v1').text('version 1')
1841
-
1842
- up.extract('.element', '<div class="element v2">version 2</div>', transition: 'cross-fade', duration: 200)
1843
-
1844
- next =>
1845
- $ghost1 = $('.element:contains("version 1")')
1846
- expect($ghost1).toHaveLength(1)
1847
- expect($ghost1.css('opacity')).toBeAround(1.0, 0.1)
1848
-
1849
- $ghost2 = $('.element:contains("version 2")')
1850
- expect($ghost2).toHaveLength(1)
1851
- expect($ghost2.css('opacity')).toBeAround(0.0, 0.1)
1852
-
1853
- next =>
1854
- up.extract('.element', '<div class="element v3">version 3</div>', transition: 'cross-fade', duration: 200)
1855
-
1856
- next =>
1857
- $ghost1 = $('.element:contains("version 1")')
1858
- expect($ghost1).toHaveLength(0)
1859
-
1860
- $ghost2 = $('.element:contains("version 2")')
1861
- expect($ghost2).toHaveLength(1)
1862
- expect($ghost2.css('opacity')).toBeAround(1.0, 0.1)
1863
-
1864
- $ghost3 = $('.element:contains("version 3")')
1865
- expect($ghost3).toHaveLength(1)
1866
- expect($ghost3.css('opacity')).toBeAround(0.0, 0.1)
1867
-
1868
-
1869
- it 'delays the resolution of the returned promise until the transition is over', (done) ->
1870
- $fixture('.element').text('version 1')
1871
- resolution = jasmine.createSpy()
1872
- promise = up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 60)
1873
- promise.then(resolution)
1874
- expect(resolution).not.toHaveBeenCalled()
1875
-
1876
- u.timer 20, ->
1877
- expect(resolution).not.toHaveBeenCalled()
1878
-
1879
- u.timer 200, ->
1880
- expect(resolution).toHaveBeenCalled()
1881
- done()
1882
-
1883
- it 'attaches the new element to the DOM before compilers are called, so they can see their parents and trigger bubbling events', asyncSpec (next)->
1884
- $parent = $fixture('.parent')
1885
- $element = $parent.affix('.element').text('old text')
1886
- spy = jasmine.createSpy('parent spy')
1887
- up.$compiler '.element', ($element) -> spy($element.text(), $element.parent())
1888
- up.extract '.element', '<div class="element">new text</div>', transition: 'cross-fade', duration: 50
1889
-
1890
- next =>
1891
- expect(spy).toHaveBeenCalledWith('new text', $parent)
1892
-
1893
-
1894
- describe 'when up.morph() is called from a transition function', ->
1895
-
1896
- it "does not emit multiple replacement events (bugfix)", (done) ->
1897
- $element = $fixture('.element').text('old content')
1898
-
1899
- transition = (oldElement, newElement, options) ->
1900
- up.morph(oldElement, newElement, 'cross-fade', options)
1901
-
1902
- destroyedListener = jasmine.createSpy('listener to up:fragment:destroyed')
1903
- up.on 'up:fragment:destroyed', destroyedListener
1904
- insertedListener = jasmine.createSpy('listener to up:fragment:inserted')
1905
- up.on 'up:fragment:inserted', insertedListener
1906
-
1907
- extractDone = up.extract('.element', '<div class="element">new content</div>', transition: transition, duration: 50, easing: 'linear')
1908
-
1909
- extractDone.then ->
1910
- expect(destroyedListener.calls.count()).toBe(1)
1911
- expect(insertedListener.calls.count()).toBe(1)
1912
- done()
1913
-
1914
- it "does not compile the element multiple times (bugfix)", (done) ->
1915
- $element = $fixture('.element').text('old content')
1916
-
1917
- transition = (oldElement, newElement, options) ->
1918
- up.morph(oldElement, newElement, 'cross-fade', options)
1919
-
1920
- compiler = jasmine.createSpy('compiler')
1921
- up.$compiler '.element', compiler
1922
-
1923
- extractDone = up.extract('.element', '<div class="element">new content</div>', transition: transition, duration: 50, easing: 'linear')
1924
-
1925
- extractDone.then ->
1926
- expect(compiler.calls.count()).toBe(1)
1927
- done()
1928
-
1929
- it "does not call destructors multiple times (bugfix)", (done) ->
1930
- $element = $fixture('.element').text('old content')
1931
-
1932
- transition = (oldElement, newElement, options) ->
1933
- up.morph(oldElement, newElement, 'cross-fade', options)
1934
-
1935
- destructor = jasmine.createSpy('destructor')
1936
- up.$compiler '.element', (element) ->
1937
- return destructor
1938
-
1939
- up.hello($element)
1940
-
1941
- extractDone = up.extract('.element', '<div class="element">new content</div>', transition: transition, duration: 50, easing: 'linear')
1942
-
1943
- extractDone.then ->
1944
- expect(destructor.calls.count()).toBe(1)
1945
- done()
1946
-
1947
-
1948
- describe 'when animation is disabled', ->
1949
-
1950
- beforeEach ->
1951
- up.motion.config.enabled = false
1952
-
1953
- it 'immediately swaps the old and new elements without creating unnecessary ghosts', asyncSpec (next) ->
1954
- $fixture('.element').text('version 1')
1955
- up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 200)
1956
- next =>
1957
- expect($('.element')).toHaveText('version 2')
1958
- expect($('.up-ghost')).toHaveLength(0)
1959
-
1960
- it "replaces the elements directly, since first inserting and then removing would shift scroll positions", asyncSpec (next) ->
1961
- swapDirectlySpy = up.motion.knife.mock('swapElementsDirectly')
1962
- $fixture('.element').text('version 1')
1963
- up.extract('.element', '<div class="element">version 2</div>', transition: false)
1964
-
1965
- next =>
1966
- expect(swapDirectlySpy).toHaveBeenCalled()
1967
-
1968
- describe 'with { scrollBehavior } option', ->
1969
-
1970
- beforeEach ->
1971
- up.viewport.knife.mock('reveal').and.callFake (element, options) =>
1972
- @revealScrollBehavior = options.behavior ? options.scrollBehavior
1973
-
1974
- it 'animates the revealing when prepending an element', asyncSpec (next) ->
1975
- fixture('.element', text: 'version 1')
1976
- up.extract('.element:before', '<div class="element">version 2</div>', reveal: true, scrollBehavior: 'smooth')
1977
- next =>
1978
- expect(@revealScrollBehavior).toEqual('smooth')
1979
-
1980
- it 'animates the revealing when appending an element', asyncSpec (next) ->
1981
- fixture('.element', text: 'version 1')
1982
- up.extract('.element:after', '<div class="element">version 2</div>', reveal: true, scrollBehavior: 'smooth')
1983
- next =>
1984
- expect(@revealScrollBehavior).toEqual('smooth')
1985
-
1986
- it 'does not animate the revealing when swapping out an element', asyncSpec (next) ->
1987
- fixture('.element', text: 'version 1')
1988
- up.extract('.element', '<div class="element">version 2</div>', reveal: true, scrollBehavior: 'smooth')
1989
- next =>
1990
- expect(@revealScrollBehavior).toEqual('auto')
1991
-
1992
- describe 'handling of [up-keep] elements', ->
1993
-
1994
- squish = (string) ->
1995
- if u.isString(string)
1996
- string = string.replace(/^\s+/g, '')
1997
- string = string.replace(/\s+$/g, '')
1998
- string = string.replace(/\s+/g, ' ')
1999
- string
2000
-
2001
- beforeEach ->
2002
- # Need to refactor this spec file so examples don't all share one example
2003
- $('.before, .middle, .after').remove()
2004
-
2005
- it 'keeps an [up-keep] element, but does replace other elements around it', asyncSpec (next) ->
2006
- $container = $fixture('.container')
2007
- $container.affix('.before').text('old-before')
2008
- $container.affix('.middle[up-keep]').text('old-middle')
2009
- $container.affix('.after').text('old-after')
2010
-
2011
- up.extract '.container', """
2012
- <div class='container'>
2013
- <div class='before'>new-before</div>
2014
- <div class='middle' up-keep>new-middle</div>
2015
- <div class='after'>new-after</div>
2016
- </div>
2017
- """
2018
-
2019
- next =>
2020
- expect($('.before')).toHaveText('new-before')
2021
- expect($('.middle')).toHaveText('old-middle')
2022
- expect($('.after')).toHaveText('new-after')
2023
-
2024
- it 'keeps an [up-keep] element, but does replace text nodes around it', asyncSpec (next) ->
2025
- $container = $fixture('.container')
2026
- $container.html """
2027
- old-before
2028
- <div class='element' up-keep>old-inside</div>
2029
- old-after
2030
- """
2031
-
2032
- up.extract '.container', """
2033
- <div class='container'>
2034
- new-before
2035
- <div class='element' up-keep>new-inside</div>
2036
- new-after
2037
- </div>
2038
- """
2039
-
2040
- next =>
2041
- expect(squish($('.container').text())).toEqual('new-before old-inside new-after')
2042
-
2043
- it 'updates an [up-keep] element with { keep: false } option', asyncSpec (next) ->
2044
- $container = $fixture('.container')
2045
- $container.html """
2046
- old-before
2047
- <div class='element' up-keep>old-inside</div>
2048
- old-after
2049
- """
2050
-
2051
- up.extract '.container', """
2052
- <div class='container'>
2053
- new-before
2054
- <div class='element' up-keep>new-inside</div>
2055
- new-after
2056
- </div>
2057
- """,
2058
- keep: false
2059
-
2060
- next =>
2061
- expect(squish($('.container').text())).toEqual('new-before new-inside new-after')
2062
-
2063
- describe 'if an [up-keep] element is itself a direct replacement target', ->
2064
-
2065
- it "keeps that element", asyncSpec (next) ->
2066
- $fixture('.keeper[up-keep]').text('old-inside')
2067
- up.extract '.keeper', "<div class='keeper' up-keep>new-inside</div>"
2068
-
2069
- next =>
2070
- expect($('.keeper')).toHaveText('old-inside')
2071
-
2072
- it "only emits an event up:fragment:kept, but not an event up:fragment:inserted", asyncSpec (next) ->
2073
- insertedListener = jasmine.createSpy('subscriber to up:fragment:inserted')
2074
- keptListener = jasmine.createSpy('subscriber to up:fragment:kept')
2075
- up.on('up:fragment:kept', keptListener)
2076
- up.on('up:fragment:inserted', insertedListener)
2077
- $keeper = $fixture('.keeper[up-keep]').text('old-inside')
2078
- up.extract '.keeper', "<div class='keeper new' up-keep>new-inside</div>"
2079
-
2080
- next =>
2081
- expect(insertedListener).not.toHaveBeenCalled()
2082
- expect(keptListener).toHaveBeenCalledWith(
2083
- jasmine.objectContaining(newFragment: jasmine.objectContaining(className: 'keeper new')),
2084
- $keeper[0],
2085
- jasmine.anything()
2086
- )
2087
-
2088
- it "removes an [up-keep] element if no matching element is found in the response", asyncSpec (next) ->
2089
- barCompiler = jasmine.createSpy()
2090
- barDestructor = jasmine.createSpy()
2091
- up.$compiler '.bar', ($bar) ->
2092
- text = $bar.text()
2093
- barCompiler(text)
2094
- return -> barDestructor(text)
2095
-
2096
- $container = $fixture('.container')
2097
- $container.html """
2098
- <div class='foo'>old-foo</div>
2099
- <div class='bar' up-keep>old-bar</div>
2100
- """
2101
- up.hello($container)
2102
-
2103
- expect(barCompiler.calls.allArgs()).toEqual [['old-bar']]
2104
- expect(barDestructor.calls.allArgs()).toEqual []
2105
-
2106
- up.extract '.container', """
2107
- <div class='container'>
2108
- <div class='foo'>new-foo</div>
2109
- </div>
2110
- """
2111
-
2112
- next =>
2113
- expect($('.container .foo')).toBeAttached()
2114
- expect($('.container .bar')).not.toBeAttached()
2115
-
2116
- expect(barCompiler.calls.allArgs()).toEqual [['old-bar']]
2117
- expect(barDestructor.calls.allArgs()).toEqual [['old-bar']]
2118
-
2119
- it "updates an element if a matching element is found in the response, but that other element is no longer [up-keep]", asyncSpec (next) ->
2120
- barCompiler = jasmine.createSpy()
2121
- barDestructor = jasmine.createSpy()
2122
- up.$compiler '.bar', ($bar) ->
2123
- text = $bar.text()
2124
- barCompiler(text)
2125
- return -> barDestructor(text)
2126
-
2127
- $container = $fixture('.container')
2128
- $container.html """
2129
- <div class='foo'>old-foo</div>
2130
- <div class='bar' up-keep>old-bar</div>
2131
- """
2132
- up.hello($container)
2133
-
2134
- expect(barCompiler.calls.allArgs()).toEqual [['old-bar']]
2135
- expect(barDestructor.calls.allArgs()).toEqual []
2136
-
2137
- up.extract '.container', """
2138
- <div class='container'>
2139
- <div class='foo'>new-foo</div>
2140
- <div class='bar'>new-bar</div>
2141
- </div>
2142
- """
2143
-
2144
- next =>
2145
- expect($('.container .foo')).toHaveText('new-foo')
2146
- expect($('.container .bar')).toHaveText('new-bar')
2147
-
2148
- expect(barCompiler.calls.allArgs()).toEqual [['old-bar'], ['new-bar']]
2149
- expect(barDestructor.calls.allArgs()).toEqual [['old-bar']]
2150
-
2151
- it 'moves a kept element to the ancestry position of the matching element in the response', asyncSpec (next) ->
2152
- $container = $fixture('.container')
2153
- $container.html """
2154
- <div class="parent1">
2155
- <div class="keeper" up-keep>old-inside</div>
2156
- </div>
2157
- <div class="parent2">
2158
- </div>
2159
- """
2160
- up.extract '.container', """
2161
- <div class='container'>
2162
- <div class="parent1">
2163
- </div>
2164
- <div class="parent2">
2165
- <div class="keeper" up-keep>old-inside</div>
2166
- </div>
2167
- </div>
2168
- """
2169
-
2170
- next =>
2171
- expect($('.keeper')).toHaveText('old-inside')
2172
- expect($('.keeper').parent()).toEqual($('.parent2'))
2173
-
2174
- it 'lets developers choose a selector to match against as the value of the up-keep attribute', asyncSpec (next) ->
2175
- $container = $fixture('.container')
2176
- $container.html """
2177
- <div class="keeper" up-keep=".stayer"></div>
2178
- """
2179
- up.extract '.container', """
2180
- <div class='container'>
2181
- <div up-keep class="stayer"></div>
2182
- </div>
2183
- """
2184
-
2185
- next =>
2186
- expect('.keeper').toBeAttached()
2187
-
2188
- it 'does not compile a kept element a second time', asyncSpec (next) ->
2189
- compiler = jasmine.createSpy('compiler')
2190
- up.$compiler('.keeper', compiler)
2191
- $container = $fixture('.container')
2192
- $container.html """
2193
- <div class="keeper" up-keep>old-text</div>
2194
- """
2195
-
2196
- up.hello($container)
2197
- expect(compiler.calls.count()).toEqual(1)
2198
-
2199
- up.extract '.container', """
2200
- <div class='container'>
2201
- <div class="keeper" up-keep>new-text</div>
2202
- </div>
2203
- """
2204
-
2205
- next =>
2206
- expect(compiler.calls.count()).toEqual(1)
2207
- expect('.keeper').toBeAttached()
2208
-
2209
- it 'does not lose jQuery event handlers on a kept element (bugfix)', asyncSpec (next) ->
2210
- handler = jasmine.createSpy('event handler')
2211
- up.$compiler '.keeper', ($keeper) ->
2212
- $keeper.on 'click', handler
2213
-
2214
- $container = $fixture('.container')
2215
- $container.html """
2216
- <div class="keeper" up-keep>old-text</div>
2217
- """
2218
- up.hello($container)
2219
-
2220
- up.extract '.container', """
2221
- <div class='container'>
2222
- <div class="keeper" up-keep>new-text</div>
2223
- </div>
2224
- """
2225
-
2226
- next =>
2227
- expect('.keeper').toHaveText('old-text')
2228
-
2229
- next =>
2230
- Trigger.click('.keeper')
2231
-
2232
- next =>
2233
- expect(handler).toHaveBeenCalled()
2234
-
2235
- it 'does not call destructors on a kept alement', asyncSpec (next) ->
2236
- destructor = jasmine.createSpy('destructor')
2237
- up.$compiler '.keeper', ($keeper) ->
2238
- return destructor
2239
-
2240
- $container = $fixture('.container')
2241
- $container.html """
2242
- <div class="keeper" up-keep>old-text</div>
2243
- """
2244
- up.hello($container)
2245
-
2246
- up.extract '.container', """
2247
- <div class='container'>
2248
- <div class="keeper" up-keep>new-text</div>
2249
- </div>
2250
- """
2251
-
2252
- next =>
2253
- $keeper = $('.keeper')
2254
- expect($keeper).toHaveText('old-text')
2255
- expect(destructor).not.toHaveBeenCalled()
2256
-
2257
- it 'calls destructors when a kept element is eventually removed from the DOM', asyncSpec (next) ->
2258
- handler = jasmine.createSpy('event handler')
2259
- destructor = jasmine.createSpy('destructor')
2260
- up.$compiler '.keeper', ($keeper) ->
2261
- return destructor
2262
-
2263
- $container = $fixture('.container')
2264
- $container.html """
2265
- <div class="keeper" up-keep>old-text</div>
2266
- """
2267
- up.hello($container)
2268
-
2269
- up.extract '.container', """
2270
- <div class='container'>
2271
- <div class="keeper">new-text</div>
2272
- </div>
2273
- """
2274
-
2275
- next =>
2276
- $keeper = $('.keeper')
2277
- expect($keeper).toHaveText('new-text')
2278
- expect(destructor).toHaveBeenCalled()
2279
-
2280
- it 'lets listeners inspect a new element before discarding through properties on an up:fragment:keep event', asyncSpec (next) ->
2281
- $keeper = $fixture('.keeper[up-keep]').text('old-inside')
2282
- listener = jasmine.createSpy('event listener')
2283
- $keeper[0].addEventListener('up:fragment:keep', listener)
2284
- up.extract '.keeper', "<div class='keeper new' up-keep up-data='{ \"key\": \"new-value\" }'>new-inside</div>"
2285
- next =>
2286
- expect(listener).toHaveBeenCalledWith(
2287
- jasmine.objectContaining(
2288
- newFragment: jasmine.objectContaining(className: 'keeper new')
2289
- newData: { key: 'new-value' },
2290
- target: $keeper[0]
2291
- )
2292
- )
2293
-
2294
- it 'lets listeners cancel the keeping by preventing default on an up:fragment:keep event', asyncSpec (next) ->
2295
- $keeper = $fixture('.keeper[up-keep]').text('old-inside')
2296
- $keeper.on 'up:fragment:keep', (event) -> event.preventDefault()
2297
- up.extract '.keeper', "<div class='keeper' up-keep>new-inside</div>"
2298
- next => expect($('.keeper')).toHaveText('new-inside')
2299
-
2300
- it 'lets listeners prevent up:fragment:keep event if the element was kept before (bugfix)', asyncSpec (next) ->
2301
- $keeper = $fixture('.keeper[up-keep]').text('version 1')
2302
- $keeper[0].addEventListener 'up:fragment:keep', (event) ->
2303
- event.preventDefault() if event.newFragment.textContent.trim() == 'version 3'
2304
-
2305
- next => up.extract '.keeper', "<div class='keeper' up-keep>version 2</div>"
2306
- next => expect($('.keeper')).toHaveText('version 1')
2307
- next => up.extract '.keeper', "<div class='keeper' up-keep>version 3</div>"
2308
- next => expect($('.keeper')).toHaveText('version 3')
2309
-
2310
- it 'emits an up:fragment:kept event on a kept element and up:fragment:inserted on the targeted parent parent', asyncSpec (next) ->
2311
- insertedListener = jasmine.createSpy()
2312
- up.on('up:fragment:inserted', insertedListener)
2313
- keptListener = jasmine.createSpy()
2314
- up.on('up:fragment:kept', keptListener)
2315
-
2316
- $container = $fixture('.container')
2317
- $container.html """
2318
- <div class="keeper" up-keep></div>
2319
- """
2320
-
2321
- up.extract '.container', """
2322
- <div class='container'>
2323
- <div class="keeper" up-keep></div>
2324
- </div>
2325
- """
2326
-
2327
- next =>
2328
- expect(insertedListener).toHaveBeenCalledWith(jasmine.anything(), $('.container')[0], jasmine.anything())
2329
- expect(keptListener).toHaveBeenCalledWith(jasmine.anything(), $('.container .keeper')[0], jasmine.anything())
2330
-
2331
- it 'emits an up:fragment:kept event on a kept element with a newData property corresponding to the up-data attribute value of the discarded element', asyncSpec (next) ->
2332
- keptListener = jasmine.createSpy()
2333
- up.on 'up:fragment:kept', (event) -> keptListener(event.target, event.newData)
2334
- $container = $fixture('.container')
2335
- $keeper = $container.affix('.keeper[up-keep]').text('old-inside')
2336
-
2337
- up.extract '.container', """
2338
- <div class='container'>
2339
- <div class='keeper' up-keep up-data='{ "foo": "bar" }'>new-inside</div>
2340
- </div>
2341
- """
2342
-
2343
- next =>
2344
- expect($('.keeper')).toHaveText('old-inside')
2345
- expect(keptListener).toHaveBeenCalledWith($keeper[0], { 'foo': 'bar' })
2346
-
2347
- it 'emits an up:fragment:kept with { newData: {} } if the discarded element had no up-data value', asyncSpec (next) ->
2348
- keptListener = jasmine.createSpy()
2349
- up.on('up:fragment:kept', keptListener)
2350
- $container = $fixture('.container')
2351
- $keeper = $container.affix('.keeper[up-keep]').text('old-inside')
2352
- up.extract '.keeper', """
2353
- <div class='container'>
2354
- <div class='keeper' up-keep>new-inside</div>
2355
- </div>
2356
- """
2357
-
2358
- next =>
2359
- expect($('.keeper')).toHaveText('old-inside')
2360
- expect(keptListener).toEqual(jasmine.anything(), $('.keeper'), {})
2361
-
2362
- it 'reuses the same element and emits up:fragment:kept during multiple extractions', asyncSpec (next) ->
2363
- keptListener = jasmine.createSpy()
2364
- up.on 'up:fragment:kept', (event) -> keptListener(event.target, event.newData)
2365
- $container = $fixture('.container')
2366
- $keeper = $container.affix('.keeper[up-keep]').text('old-inside')
2367
-
2368
- next =>
2369
- up.extract '.keeper', """
2370
- <div class='container'>
2371
- <div class='keeper' up-keep up-data='{ \"key\": \"value1\" }'>new-inside</div>
2372
- </div>
2373
- """
2374
-
2375
- next =>
2376
- up.extract '.keeper', """
2377
- <div class='container'>
2378
- <div class='keeper' up-keep up-data='{ \"key\": \"value2\" }'>new-inside</div>
2379
- """
2380
-
2381
- next =>
2382
- $keeper = $('.keeper')
2383
- expect($keeper).toHaveText('old-inside')
2384
- expect(keptListener).toHaveBeenCalledWith($keeper[0], { key: 'value1' })
2385
- expect(keptListener).toHaveBeenCalledWith($keeper[0], { key: 'value2' })
2386
-
2387
- it "doesn't let the discarded element appear in a transition", (done) ->
2388
- oldTextDuringTransition = undefined
2389
- newTextDuringTransition = undefined
2390
- transition = (oldElement, newElement) ->
2391
- oldTextDuringTransition = squish(oldElement.innerText)
2392
- newTextDuringTransition = squish(newElement.innerText)
2393
- Promise.resolve()
2394
- $container = $fixture('.container')
2395
- $container.html """
2396
- <div class='foo'>old-foo</div>
2397
- <div class='bar' up-keep>old-bar</div>
2398
- """
2399
- newHtml = """
2400
- <div class='container'>
2401
- <div class='foo'>new-foo</div>
2402
- <div class='bar' up-keep>new-bar</div>
2403
- </div>
2404
- """
2405
- promise = up.extract('.container', newHtml, transition: transition)
2406
- promise.then ->
2407
- expect(oldTextDuringTransition).toEqual('old-foo old-bar')
2408
- expect(newTextDuringTransition).toEqual('new-foo old-bar')
2409
- done()
2410
-
2411
-
2412
-
2413
- describe 'up.destroy', ->
2414
-
2415
- it 'removes the element with the given selector', (done) ->
2416
- $fixture('.element')
2417
- up.destroy('.element').then ->
2418
- expect($('.element')).not.toBeAttached()
2419
- done()
2420
-
2421
- it 'runs an animation before removal with { animate } option', asyncSpec (next) ->
2422
- $element = $fixture('.element')
2423
- up.destroy($element, animation: 'fade-out', duration: 200, easing: 'linear')
2424
-
2425
- next ->
2426
- expect($element).toHaveOpacity(1.0, 0.15)
2427
-
2428
- next.after 100, ->
2429
- expect($element).toHaveOpacity(0.5, 0.3)
2430
-
2431
- next.after (100 + 75), ->
2432
- expect($element).toBeDetached()
2433
-
2434
- it 'calls destructors for custom elements', (done) ->
2435
- destructor = jasmine.createSpy('destructor')
2436
- up.$compiler('.element', ($element) -> destructor)
2437
- up.hello(fixture('.element'))
2438
- up.destroy('.element').then ->
2439
- expect(destructor).toHaveBeenCalled()
2440
- done()
2441
-
2442
- it 'does not call destructors twice if up.destroy() is called twice on the same fragment', asyncSpec (next) ->
2443
- destructor = jasmine.createSpy('destructor')
2444
- up.compiler('.element', (element) -> destructor)
2445
-
2446
- element = fixture('.element')
2447
- up.hello(element)
2448
-
2449
- up.destroy(element, animation: 'fade-out', duration: 10)
2450
- up.destroy(element, animation: 'fade-out', duration: 10)
2451
-
2452
- next.after 60, ->
2453
- expect(destructor.calls.count()).toBe(1)
2454
-
2455
- it 'marks the old element as .up-destroying before destructors', (done) ->
2456
- destructor = jasmine.createSpy('destructor')
2457
- up.$compiler '.container', ($element) ->
2458
- -> destructor($element.text(), $element.is('.up-destroying'))
2459
- $container = $fixture('.container').text('old text')
2460
- up.hello($container)
2461
-
2462
- destroyDone = up.destroy('.container')
2463
-
2464
- destroyDone.then ->
2465
- expect(destructor).toHaveBeenCalledWith('old text', true)
2466
- done()
2467
-
2468
- it 'marks the old element as [aria-hidden=true] before destructors', (done) ->
2469
- destructor = jasmine.createSpy('destructor')
2470
- up.$compiler '.container', ($element) ->
2471
- -> destructor($element.text(), $element.is('[aria-hidden=true]'))
2472
- $container = $fixture('.container').text('old text')
2473
- up.hello($container)
2474
-
2475
- destroyDone = up.destroy('.container')
2476
-
2477
- destroyDone.then ->
2478
- expect(destructor).toHaveBeenCalledWith('old text', true)
2479
- done()
2480
-
2481
- it 'marks the old element as .up-destroying before destructors after an { animation }', (done) ->
2482
- destructor = jasmine.createSpy('destructor')
2483
- up.$compiler '.container', ($element) ->
2484
- -> destructor($element.text(), $element.is('.up-destroying'))
2485
- $container = $fixture('.container').text('old text')
2486
- up.hello($container)
2487
-
2488
- destroyDone = up.destroy('.container', animation: 'fade-out', duration: 100)
2489
-
2490
- destroyDone.then ->
2491
- expect(destructor).toHaveBeenCalledWith('old text', true)
2492
- done()
2493
-
2494
- it 'waits until an { animation } is done before calling destructors', asyncSpec (next) ->
2495
- destructor = jasmine.createSpy('destructor')
2496
- up.$compiler '.container', ($element) ->
2497
- -> destructor($element.text())
2498
- $container = $fixture('.container').text('old text')
2499
- up.hello($container)
2500
-
2501
- destroyDone = up.destroy('.container', animation: 'fade-out', duration: 200)
2502
-
2503
- next.after 100, ->
2504
- expect(destructor).not.toHaveBeenCalled()
2505
-
2506
- next.await(destroyDone)
2507
-
2508
- next ->
2509
- expect(destructor).toHaveBeenCalledWith('old text',)
2510
-
2511
-
2512
- it 'allows to pass a new history entry as { history } option', (done) ->
2513
- up.history.config.enabled = true
2514
- $fixture('.element')
2515
- up.destroy('.element', history: '/new-path').then ->
2516
- u.timer 100, ->
2517
- expect(location.href).toMatchUrl('/new-path')
2518
- done()
2519
-
2520
- it 'allows to pass a new document title as { title } option', (done) ->
2521
- up.history.config.enabled = true
2522
- $fixture('.element')
2523
- up.destroy('.element', history: '/new-path', title: 'Title from options').then ->
2524
- expect(document.title).toEqual('Title from options')
2525
- done()
2526
-
2527
- it 'marks the element as .up-destroying while it is animating', asyncSpec (next) ->
2528
- $element = $fixture('.element')
2529
- up.destroy($element, animation: 'fade-out', duration: 80, easing: 'linear')
2530
-
2531
- next ->
2532
- expect($element).toHaveClass('up-destroying')
2533
-
2534
- # up.destroy
2535
- it 'emits an up:fragment:destroyed event on the former parent element after the element has been removed from the DOM', asyncSpec (next) ->
2536
- $parent = $fixture('.parent')
2537
- $element = $parent.affix('.element')
2538
- expect($element).toBeAttached()
2539
-
2540
- listener = jasmine.createSpy('event listener')
2541
-
2542
- $parent[0].addEventListener('up:fragment:destroyed', listener)
2543
-
2544
- destroyDone = up.destroy($element, animation: 'fade-out', duration: 30)
2545
-
2546
- next ->
2547
- expect(listener).not.toHaveBeenCalled()
2548
- expect($element).toBeAttached()
2549
-
2550
- next.await(destroyDone)
2551
-
2552
- next ->
2553
- expect(listener).toHaveBeenCalledWith(
2554
- jasmine.objectContaining(
2555
- target: $parent[0],
2556
- parent: $parent[0]
2557
- fragment: $element[0]
2558
- )
2559
- )
2560
- expect($element).toBeDetached()
2561
-
2562
- it 'removes element-related data from the global jQuery cache (bugfix)', asyncSpec (next) ->
2563
- $element = $fixture('.element')
2564
- $element.data('foo', { foo: '1' })
2565
- expect($element.data('foo')).toEqual({ foo: '1'})
2566
- up.destroy($element)
2567
-
2568
- next ->
2569
- expect($element.data('foo')).toBeMissing()
2570
-
2571
- describe 'up.reload', ->
2572
-
2573
- describeCapability 'canPushState', ->
2574
-
2575
- it 'reloads the given selector from the closest known source URL', asyncSpec (next) ->
2576
- $fixture('.container[up-source="/source"] .element').find('.element').text('old text')
2577
-
2578
- next =>
2579
- up.reload('.element')
2580
-
2581
- next =>
2582
- expect(@lastRequest().url).toMatch(/\/source$/)
2583
- @respondWith """
2584
- <div class="container">
2585
- <div class="element">new text</div>
2586
- </div>
2587
- """
2588
-
2589
- next =>
2590
- expect($('.element')).toHaveText('new text')
2591
-
2592
-
2593
- describeFallback 'canPushState', ->
2594
-
2595
- it 'makes a page load from the closest known source URL', asyncSpec (next) ->
2596
- $fixture('.container[up-source="/source"] .element').find('.element').text('old text')
2597
- spyOn(up.browser, 'navigate')
2598
- up.reload('.element')
2599
-
2600
- next =>
2601
- expect(up.browser.navigate).toHaveBeenCalledWith('/source', jasmine.anything())
2602
-
2603
- describe 'up.fragment.layerOf', ->
2604
-
2605
- it 'returns "popup" for an element in a popup over the page', ->
2606
- $popup = $fixture('.up-popup')
2607
- $element = $popup.affix('.element')
2608
- expect(up.fragment.layerOf($element[0])).toEqual('popup')
2609
-
2610
- it 'returns "popup" for an element in a popup over a modal', ->
2611
- $modal = $fixture('.up-modal')
2612
- $popupInModal = $modal.affix('.up-popup')
2613
- $element = $popupInModal.affix('.element')
2614
- expect(up.fragment.layerOf($element[0])).toEqual('popup')
2615
-
2616
- it 'returns "modal" for an element in a modal', ->
2617
- $modal = $fixture('.up-modal')
2618
- $element = $modal.affix('.element')
2619
- expect(up.fragment.layerOf($element[0])).toEqual('modal')
2620
-
2621
- it 'returns "page" for an element below a modal or popup', ->
2622
- $element = $fixture('.element')
2623
- expect(up.fragment.layerOf($element[0])).toEqual('page')
2624
-