aboutsummaryrefslogtreecommitdiffstats
path: root/actionview
diff options
context:
space:
mode:
Diffstat (limited to 'actionview')
-rw-r--r--actionview/CHANGELOG.md189
-rw-r--r--actionview/Rakefile70
-rw-r--r--actionview/app/assets/javascripts/MIT-LICENSE20
-rw-r--r--actionview/app/assets/javascripts/README.md49
-rw-r--r--actionview/app/assets/javascripts/config.coffee37
-rw-r--r--actionview/app/assets/javascripts/rails-ujs.coffee95
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/BANNER.js5
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/features/confirm.coffee (renamed from actionview/app/assets/javascripts/features/confirm.coffee)0
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/features/disable.coffee (renamed from actionview/app/assets/javascripts/features/disable.coffee)4
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/features/method.coffee (renamed from actionview/app/assets/javascripts/features/method.coffee)0
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/features/remote.coffee (renamed from actionview/app/assets/javascripts/features/remote.coffee)12
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/start.coffee70
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee (renamed from actionview/app/assets/javascripts/utils/ajax.coffee)0
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/utils/csrf.coffee (renamed from actionview/app/assets/javascripts/utils/csrf.coffee)0
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/utils/dom.coffee (renamed from actionview/app/assets/javascripts/utils/dom.coffee)0
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/utils/event.coffee (renamed from actionview/app/assets/javascripts/utils/event.coffee)2
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/utils/form.coffee36
-rw-r--r--actionview/app/assets/javascripts/utils/form.coffee61
-rw-r--r--actionview/coffeelint.json135
-rw-r--r--actionview/lib/action_view/digestor.rb6
-rw-r--r--actionview/lib/action_view/gem_version.rb2
-rw-r--r--actionview/lib/action_view/helpers/sanitize_helper.rb14
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb2
-rw-r--r--actionview/lib/action_view/template.rb2
-rw-r--r--actionview/lib/action_view/test_case.rb8
-rw-r--r--actionview/package.json6
-rw-r--r--actionview/test/activerecord/polymorphic_routes_test.rb49
-rw-r--r--actionview/test/template/date_helper_test.rb2
-rw-r--r--actionview/test/template/digestor_test.rb4
-rw-r--r--actionview/test/template/erb/tag_helper_test.rb4
-rw-r--r--actionview/test/template/form_helper_test.rb4
-rw-r--r--actionview/test/template/sanitize_helper_test.rb4
-rw-r--r--actionview/test/ujs/config.ru1
-rw-r--r--actionview/test/ujs/public/test/call-remote-callbacks.js196
-rw-r--r--actionview/test/ujs/public/test/data-confirm.js28
-rw-r--r--actionview/test/ujs/public/test/data-remote.js17
-rw-r--r--actionview/test/ujs/public/test/override.js2
-rw-r--r--actionview/test/ujs/public/test/settings.js8
-rw-r--r--actionview/test/ujs/server.rb37
-rw-r--r--actionview/test/ujs/views/layouts/application.html.erb25
-rw-r--r--actionview/test/ujs/views/tests/index.html.erb16
41 files changed, 559 insertions, 663 deletions
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index b071b260c9..c514e757c8 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,188 +1 @@
-* Change the ERB handler from Erubis to Erubi.
-
- Erubi is an Erubis fork that's svelte, simple, and currently maintained.
- Plus it supports `--enable-frozen-string-literal` in Ruby 2.3+.
-
- Compatibility: Drops support for `<%===` tags for debug output.
- These were an unused, undocumented side effect of the Erubis
- implementation.
-
- Deprecation: The Erubis handler will be removed in Rails 5.2, for the
- handful of folks using it directly.
-
- *Jeremy Evans*
-
-* Allow render locals to be assigned to instance variables in a view.
-
- Fixes #27480.
-
- *Andrew White*
-
-* Add `check_parameters` option to `current_page?` which makes it more strict.
-
- *Maksym Pugach*
-
-* Return correct object name in form helper method after `fields_for`.
-
- Fixes #26931.
-
- *Yuji Yaginuma*
-
-* Use `ActionView::Resolver.caching?` (`config.action_view.cache_template_loading`)
- to enable template recompilation.
-
- Before it was enabled by `consider_all_requests_local`, which caused
- recompilation in tests.
-
- *Max Melentiev*
-
-* Add `form_with` to unify `form_tag` and `form_for` usage.
-
- Used like `form_tag` (where just the open tag is output):
-
- ```erb
- <%= form_with scope: :post, url: super_special_posts_path %>
- ```
-
- Used like `form_for`:
-
- ```erb
- <%= form_with model: @post do |form| %>
- <%= form.text_field :title %>
- <% end %>
- ```
-
- *Kasper Timm Hansen*, *Marek Kirejczyk*
-
-* Add `fields` form helper method.
-
- ```erb
- <%= fields :comment, model: @comment do |fields| %>
- <%= fields.text_field :title %>
- <% end %>
- ```
-
- Can also be used within form helpers such as `form_with`.
-
- *Kasper Timm Hansen*
-
-* Removed deprecated `#original_exception` in `ActionView::Template::Error`.
-
- *Rafael Mendonça França*
-
-* Render now accepts any keys for locals, including reserved keywords.
-
- Only locals with valid variable names get set directly. Others
- will still be available in `local_assigns`.
-
- Example of render with reserved keywords:
-
- ```erb
- <%= render "example", class: "text-center", message: "Hello world!" %>
-
- <!-- _example.html.erb: -->
- <%= tag.div class: local_assigns[:class] do %>
- <p><%= message %></p>
- <% end %>
- ```
-
- *Peter Schilling*, *Matthew Draper*
-
-* Show cache hits and misses when rendering partials.
-
- Partials using the `cache` helper will show whether a render hit or missed
- the cache:
-
- ```
- Rendered messages/_message.html.erb in 1.2 ms [cache hit]
- Rendered recordings/threads/_thread.html.erb in 1.5 ms [cache miss]
- ```
-
- This removes the need for the old fragment cache logging:
-
- ```
- Read fragment views/v1/2914079/v1/2914079/recordings/70182313-20160225015037000000/d0bdf2974e1ef6d31685c3b392ad0b74 (0.6ms)
- Rendered messages/_message.html.erb in 1.2 ms [cache hit]
- Write fragment views/v1/2914079/v1/2914079/recordings/70182313-20160225015037000000/3b4e249ac9d168c617e32e84b99218b5 (1.1ms)
- Rendered recordings/threads/_thread.html.erb in 1.5 ms [cache miss]
- ```
-
- Though that full output can be reenabled with
- `config.action_controller.enable_fragment_cache_logging = true`.
-
- *Stan Lo*
-
-* Changed partial rendering with a collection to allow collections which
- implement `to_a`.
-
- Extracting the collection option had an optimization to avoid unnecessary
- queries of ActiveRecord Relations by calling `#to_ary` on the given
- collection. Instances of `Enumerator` or `Enumerable` are valid
- collections, but they do not implement `#to_ary`. By changing this to
- `#to_a`, they will now be extracted and rendered as expected.
-
- *Steven Harman*
-
-* New syntax for tag helpers. Avoid positional parameters and support HTML5 by default.
- Example usage of tag helpers before:
-
- ```ruby
- tag(:br, nil, true)
- content_tag(:div, content_tag(:p, "Hello world!"), class: "strong")
-
- <%= content_tag :div, class: "strong" do -%>
- Hello world!
- <% end -%>
- ```
-
- Example usage of tag helpers after:
-
- ```ruby
- tag.br
- tag.div tag.p("Hello world!"), class: "strong"
-
- <%= tag.div class: "strong" do %>
- Hello world!
- <% end %>
- ```
-
- *Marek Kirejczyk*, *Kasper Timm Hansen*
-
-* Change `datetime_field` and `datetime_field_tag` to generate `datetime-local` fields.
-
- As a new specification of the HTML 5 the text field type `datetime` will no longer exist
- and it is recommended to use `datetime-local`.
- Ref: https://html.spec.whatwg.org/multipage/forms.html#local-date-and-time-state-(type=datetime-local)
-
- *Herminio Torres*
-
-* Raw template handler (which is also the default template handler in Rails 5) now outputs
- HTML-safe strings.
-
- In Rails 5 the default template handler was changed to the raw template handler. Because
- the ERB template handler escaped strings by default this broke some applications that
- expected plain JS or HTML files to be rendered unescaped. This fixes the issue caused
- by changing the default handler by changing the Raw template handler to output HTML-safe
- strings.
-
- *Eileen M. Uchitelle*
-
-* `select_tag`'s `include_blank` option for generation for blank option tag, now adds an empty space label,
- when the value as well as content for option tag are empty, so that we conform with html specification.
- Ref: https://www.w3.org/TR/html5/forms.html#the-option-element.
-
- Generation of option before:
-
- ```html
- <option value=""></option>
- ```
-
- Generation of option after:
-
- ```html
- <option value="" label=" "></option>
- ```
-
- *Vipul A M*
-
-Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/actionview/CHANGELOG.md) for previous changes.
+Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/actionview/CHANGELOG.md) for previous changes.
diff --git a/actionview/Rakefile b/actionview/Rakefile
index cba4684076..de588ad25c 100644
--- a/actionview/Rakefile
+++ b/actionview/Rakefile
@@ -1,9 +1,13 @@
require "rake/testtask"
+require "fileutils"
+require "open3"
+
+dir = File.dirname(__FILE__)
desc "Default Task"
task default: :test
-task package: "assets:compile"
+task package: %w( assets:compile assets:verify )
# Run the unit tests
@@ -25,6 +29,32 @@ namespace :test do
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
end
+ task :ujs do
+ begin
+ Dir.mkdir("log")
+ pid = spawn("bundle exec rackup test/ujs/config.ru -p 4567 -s puma > log/test.log 2>&1")
+
+ start_time = Time.now
+
+ loop do
+ break if system("lsof -i :4567 >/dev/null")
+
+ if Time.now - start_time > 5
+ puts "Timed out after 5 seconds"
+ exit 1
+ end
+ end
+
+ system("npm run lint && phantomjs ../ci/phantomjs.js http://localhost:4567/")
+ status = $?.to_i
+ ensure
+ Process.kill("KILL", pid) if pid
+ FileUtils.rm_f("log")
+ end
+
+ exit status
+ end
+
namespace :integration do
desc "ActiveRecord Integration Tests"
Rake::TestTask.new(:active_record) do |t|
@@ -57,8 +87,46 @@ namespace :assets do
desc "Compile Action View assets"
task :compile do
require "blade"
+ require "sprockets"
+ require "sprockets/export"
Blade.build
end
+
+ desc "Verify compiled Action View assets"
+ task :verify do
+ file = "lib/assets/compiled/rails-ujs.js"
+ pathname = Pathname.new("#{dir}/#{file}")
+
+ print "[verify] #{file} exists "
+ if pathname.exist?
+ puts "[OK]"
+ else
+ $stderr.puts "[FAIL]"
+ fail
+ end
+
+ print "[verify] #{file} is a UMD module "
+ if pathname.read =~ /module\.exports.*define\.amd/m
+ puts "[OK]"
+ else
+ $stderr.puts "[FAIL]"
+ fail
+ end
+
+ print "[verify] #{dir} can be required as a module "
+ js = <<-JS
+ window = { Event: class {} }
+ class Element {}
+ require('#{dir}')
+ JS
+ stdout, stderr, status = Open3.capture3("node", "--print", js)
+ if status.success?
+ puts "[OK]"
+ else
+ $stderr.puts "[FAIL]\n#{stderr}"
+ fail
+ end
+ end
end
task :lines do
diff --git a/actionview/app/assets/javascripts/MIT-LICENSE b/actionview/app/assets/javascripts/MIT-LICENSE
new file mode 100644
index 0000000000..befcbdc7b7
--- /dev/null
+++ b/actionview/app/assets/javascripts/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2007-2017 Rails Core team
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/actionview/app/assets/javascripts/README.md b/actionview/app/assets/javascripts/README.md
new file mode 100644
index 0000000000..92f3e8a3b3
--- /dev/null
+++ b/actionview/app/assets/javascripts/README.md
@@ -0,0 +1,49 @@
+Ruby on Rails unobtrusive scripting adapter.
+========================================
+
+This unobtrusive scripting support file is developed for the Ruby on Rails framework, but is not strictly tied to any specific backend. You can drop this into any application to:
+
+- force confirmation dialogs for various actions;
+- make non-GET requests from hyperlinks;
+- make forms or hyperlinks submit data asynchronously with Ajax;
+- have submit buttons become automatically disabled on form submit to prevent double-clicking.
+
+These features are achieved by adding certain ["data" attributes][data] to your HTML markup. In Rails, they are added by the framework's template helpers.
+
+Requirements
+------------
+
+- HTML5 doctype (optional).
+
+If you don't use HTML5, adding "data" attributes to your HTML4 or XHTML pages might make them fail [W3C markup validation][validator]. However, this shouldn't create any issues for web browsers or other user agents.
+
+Installation using npm
+------------
+
+Run `npm install rails-ujs --save` to install the rails-ujs package.
+
+Installation using Yarn
+------------
+
+Run `yarn add rails-ujs` to install the rails-ujs package.
+
+Usage
+------------
+
+Require `rails-ujs` into your application.js manifest.
+
+```javascript
+//= require rails-ujs
+```
+
+How to run tests
+------------
+
+Run `bundle exec rake ujs:server` first, and then run the web tests by visiting [[http://localhost:4567]] in your browser.
+
+## License
+rails-ujs is released under the [MIT License](MIT-LICENSE).
+
+[data]: http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes "Embedding custom non-visible data with the data-* attributes"
+[validator]: http://validator.w3.org/
+[csrf]: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html
diff --git a/actionview/app/assets/javascripts/config.coffee b/actionview/app/assets/javascripts/config.coffee
deleted file mode 100644
index 3d4706b0e1..0000000000
--- a/actionview/app/assets/javascripts/config.coffee
+++ /dev/null
@@ -1,37 +0,0 @@
-#= export Rails
-
-@Rails =
- # Link elements bound by jquery-ujs
- linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]'
-
- # Button elements bound by jquery-ujs
- buttonClickSelector:
- selector: 'button[data-remote]:not([form]), button[data-confirm]:not([form])'
- exclude: 'form button'
-
- # Select elements bound by jquery-ujs
- inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]'
-
- # Form elements bound by jquery-ujs
- formSubmitSelector: 'form'
-
- # Form input elements bound by jquery-ujs
- formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])'
-
- # Form input elements disabled during form submission
- formDisableSelector: 'input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled'
-
- # Form input elements re-enabled after form submission
- formEnableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled'
-
- # Form required input elements
- requiredInputSelector: 'input[name][required]:not([disabled]), textarea[name][required]:not([disabled])'
-
- # Form file input elements
- fileInputSelector: 'input[name][type=file]:not([disabled])'
-
- # Link onClick disable selector with possible reenable after remote submission
- linkDisableSelector: 'a[data-disable-with], a[data-disable]'
-
- # Button onClick disable selector with possible reenable after remote submission
- buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]'
diff --git a/actionview/app/assets/javascripts/rails-ujs.coffee b/actionview/app/assets/javascripts/rails-ujs.coffee
index f96d2eb6fd..bd6e9bb881 100644
--- a/actionview/app/assets/javascripts/rails-ujs.coffee
+++ b/actionview/app/assets/javascripts/rails-ujs.coffee
@@ -1,76 +1,39 @@
-#
-# Unobtrusive JavaScript
-# https://github.com/rails/rails-ujs
-#
-# Released under the MIT license
-#
-#= require ./config
-#= require_tree ./utils
-#= require_tree ./features
+#= require ./rails-ujs/BANNER
+#= export Rails
+#= require_self
+#= require_tree ./rails-ujs/utils
+#= require_tree ./rails-ujs/features
+#= require ./rails-ujs/start
-{
- fire, delegate
- getData, $
- refreshCSRFTokens, CSRFProtection
- enableElement, disableElement
- handleConfirm
- handleRemote, validateForm, formSubmitButtonClick, handleMetaClick
- handleMethod
-} = Rails
+@Rails =
+ # Link elements bound by rails-ujs
+ linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]'
-# For backward compatibility
-if jQuery? and not jQuery.rails
- jQuery.rails = Rails
- jQuery.ajaxPrefilter (options, originalOptions, xhr) ->
- CSRFProtection(xhr) unless options.crossDomain
+ # Button elements bound by rails-ujs
+ buttonClickSelector:
+ selector: 'button[data-remote]:not([form]), button[data-confirm]:not([form])'
+ exclude: 'form button'
-Rails.start = ->
- # Cut down on the number of issues from people inadvertently including jquery_ujs twice
- # by detecting and raising an error when it happens.
- throw new Error('jquery-ujs has already been loaded!') if window._rails_loaded
+ # Select elements bound by rails-ujs
+ inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]'
- # This event works the same as the load event, except that it fires every
- # time the page is loaded.
- # See https://github.com/rails/jquery-ujs/issues/357
- # See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
- window.addEventListener 'pageshow', ->
- $(Rails.formEnableSelector).forEach (el) ->
- enableElement(el) if getData(el, 'ujs:disabled')
- $(Rails.linkDisableSelector).forEach (el) ->
- enableElement(el) if getData(el, 'ujs:disabled')
+ # Form elements bound by rails-ujs
+ formSubmitSelector: 'form'
- delegate document, Rails.linkDisableSelector, 'ajax:complete', enableElement
- delegate document, Rails.linkDisableSelector, 'ajax:stopped', enableElement
- delegate document, Rails.buttonDisableSelector, 'ajax:complete', enableElement
- delegate document, Rails.buttonDisableSelector, 'ajax:stopped', enableElement
+ # Form input elements bound by rails-ujs
+ formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])'
- delegate document, Rails.linkClickSelector, 'click', handleConfirm
- delegate document, Rails.linkClickSelector, 'click', handleMetaClick
- delegate document, Rails.linkClickSelector, 'click', disableElement
- delegate document, Rails.linkClickSelector, 'click', handleRemote
- delegate document, Rails.linkClickSelector, 'click', handleMethod
+ # Form input elements disabled during form submission
+ formDisableSelector: 'input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled'
- delegate document, Rails.buttonClickSelector, 'click', handleConfirm
- delegate document, Rails.buttonClickSelector, 'click', disableElement
- delegate document, Rails.buttonClickSelector, 'click', handleRemote
+ # Form input elements re-enabled after form submission
+ formEnableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled'
- delegate document, Rails.inputChangeSelector, 'change', handleConfirm
- delegate document, Rails.inputChangeSelector, 'change', handleRemote
+ # Form file input elements
+ fileInputSelector: 'input[name][type=file]:not([disabled])'
- delegate document, Rails.formSubmitSelector, 'submit', handleConfirm
- delegate document, Rails.formSubmitSelector, 'submit', validateForm
- delegate document, Rails.formSubmitSelector, 'submit', handleRemote
- # Normal mode submit
- # Slight timeout so that the submit button gets properly serialized
- delegate document, Rails.formSubmitSelector, 'submit', (e) -> setTimeout((-> disableElement(e)), 13)
- delegate document, Rails.formSubmitSelector, 'ajax:send', disableElement
- delegate document, Rails.formSubmitSelector, 'ajax:complete', enableElement
+ # Link onClick disable selector with possible reenable after remote submission
+ linkDisableSelector: 'a[data-disable-with], a[data-disable]'
- delegate document, Rails.formInputClickSelector, 'click', handleConfirm
- delegate document, Rails.formInputClickSelector, 'click', formSubmitButtonClick
-
- document.addEventListener('DOMContentLoaded', refreshCSRFTokens)
- window._rails_loaded = true
-
-if window.Rails is Rails and fire(document, 'rails:attachBindings')
- Rails.start()
+ # Button onClick disable selector with possible reenable after remote submission
+ buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]'
diff --git a/actionview/app/assets/javascripts/rails-ujs/BANNER.js b/actionview/app/assets/javascripts/rails-ujs/BANNER.js
new file mode 100644
index 0000000000..47ecd66003
--- /dev/null
+++ b/actionview/app/assets/javascripts/rails-ujs/BANNER.js
@@ -0,0 +1,5 @@
+/*
+Unobtrusive JavaScript
+https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts
+Released under the MIT license
+ */
diff --git a/actionview/app/assets/javascripts/features/confirm.coffee b/actionview/app/assets/javascripts/rails-ujs/features/confirm.coffee
index 72b5aaa218..72b5aaa218 100644
--- a/actionview/app/assets/javascripts/features/confirm.coffee
+++ b/actionview/app/assets/javascripts/rails-ujs/features/confirm.coffee
diff --git a/actionview/app/assets/javascripts/features/disable.coffee b/actionview/app/assets/javascripts/rails-ujs/features/disable.coffee
index e8cce7da40..90aa3bdf0e 100644
--- a/actionview/app/assets/javascripts/features/disable.coffee
+++ b/actionview/app/assets/javascripts/rails-ujs/features/disable.coffee
@@ -2,6 +2,10 @@
{ matches, getData, setData, stopEverything, formElements } = Rails
+Rails.handleDisabledElement = (e) ->
+ element = this
+ stopEverything(e) if element.disabled
+
# Unified function to enable an element (link, button and form)
Rails.enableElement = (e) ->
element = if e instanceof Event then e.target else e
diff --git a/actionview/app/assets/javascripts/features/method.coffee b/actionview/app/assets/javascripts/rails-ujs/features/method.coffee
index d04d9414dd..d04d9414dd 100644
--- a/actionview/app/assets/javascripts/features/method.coffee
+++ b/actionview/app/assets/javascripts/rails-ujs/features/method.coffee
diff --git a/actionview/app/assets/javascripts/features/remote.coffee b/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee
index 30a5dc21fa..852587042c 100644
--- a/actionview/app/assets/javascripts/features/remote.coffee
+++ b/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee
@@ -4,7 +4,7 @@
matches, getData, setData
fire, stopEverything
ajax, isCrossDomain
- blankInputs, serializeElement
+ serializeElement
} = Rails
# Checks "data-remote" if true to handle the request through a XHR request.
@@ -71,16 +71,6 @@ Rails.handleRemote = (e) ->
)
stopEverything(e)
-# Check whether any required fields are empty
-# In both ajax mode and normal mode
-Rails.validateForm = (e) ->
- form = this
- return if form.noValidate or getData(form, 'ujs:formnovalidate-button')
- # Skip other logic when required values are missing or file upload is present
- blankRequiredInputs = blankInputs(form, Rails.requiredInputSelector, false)
- if blankRequiredInputs.length > 0 and fire(form, 'ajax:aborted:required', [blankRequiredInputs])
- stopEverything(e)
-
Rails.formSubmitButtonClick = (e) ->
button = this
form = button.form
diff --git a/actionview/app/assets/javascripts/rails-ujs/start.coffee b/actionview/app/assets/javascripts/rails-ujs/start.coffee
new file mode 100644
index 0000000000..5746a22287
--- /dev/null
+++ b/actionview/app/assets/javascripts/rails-ujs/start.coffee
@@ -0,0 +1,70 @@
+{
+ fire, delegate
+ getData, $
+ refreshCSRFTokens, CSRFProtection
+ enableElement, disableElement, handleDisabledElement
+ handleConfirm
+ handleRemote, formSubmitButtonClick, handleMetaClick
+ handleMethod
+} = Rails
+
+# For backward compatibility
+if jQuery? and not jQuery.rails
+ jQuery.rails = Rails
+ jQuery.ajaxPrefilter (options, originalOptions, xhr) ->
+ CSRFProtection(xhr) unless options.crossDomain
+
+Rails.start = ->
+ # Cut down on the number of issues from people inadvertently including
+ # rails-ujs twice by detecting and raising an error when it happens.
+ throw new Error('rails-ujs has already been loaded!') if window._rails_loaded
+
+ # This event works the same as the load event, except that it fires every
+ # time the page is loaded.
+ # See https://github.com/rails/jquery-ujs/issues/357
+ # See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
+ window.addEventListener 'pageshow', ->
+ $(Rails.formEnableSelector).forEach (el) ->
+ enableElement(el) if getData(el, 'ujs:disabled')
+ $(Rails.linkDisableSelector).forEach (el) ->
+ enableElement(el) if getData(el, 'ujs:disabled')
+
+ delegate document, Rails.linkDisableSelector, 'ajax:complete', enableElement
+ delegate document, Rails.linkDisableSelector, 'ajax:stopped', enableElement
+ delegate document, Rails.buttonDisableSelector, 'ajax:complete', enableElement
+ delegate document, Rails.buttonDisableSelector, 'ajax:stopped', enableElement
+
+ delegate document, Rails.linkClickSelector, 'click', handleDisabledElement
+ delegate document, Rails.linkClickSelector, 'click', handleConfirm
+ delegate document, Rails.linkClickSelector, 'click', handleMetaClick
+ delegate document, Rails.linkClickSelector, 'click', disableElement
+ delegate document, Rails.linkClickSelector, 'click', handleRemote
+ delegate document, Rails.linkClickSelector, 'click', handleMethod
+
+ delegate document, Rails.buttonClickSelector, 'click', handleDisabledElement
+ delegate document, Rails.buttonClickSelector, 'click', handleConfirm
+ delegate document, Rails.buttonClickSelector, 'click', disableElement
+ delegate document, Rails.buttonClickSelector, 'click', handleRemote
+
+ delegate document, Rails.inputChangeSelector, 'change', handleDisabledElement
+ delegate document, Rails.inputChangeSelector, 'change', handleConfirm
+ delegate document, Rails.inputChangeSelector, 'change', handleRemote
+
+ delegate document, Rails.formSubmitSelector, 'submit', handleDisabledElement
+ delegate document, Rails.formSubmitSelector, 'submit', handleConfirm
+ delegate document, Rails.formSubmitSelector, 'submit', handleRemote
+ # Normal mode submit
+ # Slight timeout so that the submit button gets properly serialized
+ delegate document, Rails.formSubmitSelector, 'submit', (e) -> setTimeout((-> disableElement(e)), 13)
+ delegate document, Rails.formSubmitSelector, 'ajax:send', disableElement
+ delegate document, Rails.formSubmitSelector, 'ajax:complete', enableElement
+
+ delegate document, Rails.formInputClickSelector, 'click', handleDisabledElement
+ delegate document, Rails.formInputClickSelector, 'click', handleConfirm
+ delegate document, Rails.formInputClickSelector, 'click', formSubmitButtonClick
+
+ document.addEventListener('DOMContentLoaded', refreshCSRFTokens)
+ window._rails_loaded = true
+
+if window.Rails is Rails and fire(document, 'rails:attachBindings')
+ Rails.start()
diff --git a/actionview/app/assets/javascripts/utils/ajax.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee
index 9af515beda..9af515beda 100644
--- a/actionview/app/assets/javascripts/utils/ajax.coffee
+++ b/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee
diff --git a/actionview/app/assets/javascripts/utils/csrf.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/csrf.coffee
index 4eb5ebb414..4eb5ebb414 100644
--- a/actionview/app/assets/javascripts/utils/csrf.coffee
+++ b/actionview/app/assets/javascripts/rails-ujs/utils/csrf.coffee
diff --git a/actionview/app/assets/javascripts/utils/dom.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/dom.coffee
index 6bef618147..6bef618147 100644
--- a/actionview/app/assets/javascripts/utils/dom.coffee
+++ b/actionview/app/assets/javascripts/rails-ujs/utils/dom.coffee
diff --git a/actionview/app/assets/javascripts/utils/event.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/event.coffee
index d25fe8e546..8d3ff007ea 100644
--- a/actionview/app/assets/javascripts/utils/event.coffee
+++ b/actionview/app/assets/javascripts/rails-ujs/utils/event.coffee
@@ -6,7 +6,7 @@
# https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
CustomEvent = window.CustomEvent
-if typeof CustomEvent is 'function'
+if typeof CustomEvent isnt 'function'
CustomEvent = (event, params) ->
evt = document.createEvent('CustomEvent')
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/form.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/form.coffee
new file mode 100644
index 0000000000..5fa337b518
--- /dev/null
+++ b/actionview/app/assets/javascripts/rails-ujs/utils/form.coffee
@@ -0,0 +1,36 @@
+#= require ./dom
+
+{ matches } = Rails
+
+toArray = (e) -> Array.prototype.slice.call(e)
+
+Rails.serializeElement = (element, additionalParam) ->
+ inputs = [element]
+ inputs = toArray(element.elements) if matches(element, 'form')
+ params = []
+
+ inputs.forEach (input) ->
+ return unless input.name
+ if matches(input, 'select')
+ toArray(input.options).forEach (option) ->
+ params.push(name: input.name, value: option.value) if option.selected
+ else if input.checked or ['radio', 'checkbox', 'submit'].indexOf(input.type) == -1
+ params.push(name: input.name, value: input.value)
+
+ params.push(additionalParam) if additionalParam
+
+ params.map (param) ->
+ if param.name?
+ "#{encodeURIComponent(param.name)}=#{encodeURIComponent(param.value)}"
+ else
+ param
+ .join('&')
+
+# Helper function that returns form elements that match the specified CSS selector
+# If form is actually a "form" element this will return associated elements outside the from that have
+# the html form attribute set
+Rails.formElements = (form, selector) ->
+ if matches(form, 'form')
+ toArray(form.elements).filter (el) -> matches(el, selector)
+ else
+ toArray(form.querySelectorAll(selector))
diff --git a/actionview/app/assets/javascripts/utils/form.coffee b/actionview/app/assets/javascripts/utils/form.coffee
deleted file mode 100644
index 251113deda..0000000000
--- a/actionview/app/assets/javascripts/utils/form.coffee
+++ /dev/null
@@ -1,61 +0,0 @@
-#= require ./dom
-
-{ matches } = Rails
-
-toArray = (e) -> Array.prototype.slice.call(e)
-
-Rails.serializeElement = (element, additionalParam) ->
- inputs = [element]
- inputs = toArray(element.elements) if matches(element, 'form')
- params = []
-
- inputs.forEach (input) ->
- return unless input.name
- if matches(input, 'select')
- toArray(input.options).forEach (option) ->
- params.push(name: input.name, value: option.value) if option.selected
- else if input.type isnt 'radio' and input.type isnt 'checkbox' or input.checked
- params.push(name: input.name, value: input.value)
-
- params.push(additionalParam) if additionalParam
-
- params.map (param) ->
- if param.name?
- "#{encodeURIComponent(param.name)}=#{encodeURIComponent(param.value)}"
- else
- param
- .join('&')
-
-# Helper function that returns form elements that match the specified CSS selector
-# If form is actually a "form" element this will return associated elements outside the from that have
-# the html form attribute set
-Rails.formElements = (form, selector) ->
- if matches(form, 'form')
- toArray(form.elements).filter (el) -> matches(el, selector)
- else
- toArray(form.querySelectorAll(selector))
-
-# Helper function which checks for blank inputs in a form that match the specified CSS selector
-Rails.blankInputs = (form, selector, nonBlank) ->
- foundInputs = []
- requiredInputs = toArray(form.querySelectorAll(selector or 'input, textarea'))
- checkedRadioButtonNames = {}
-
- requiredInputs.forEach (input) ->
- if input.type is 'radio'
- # Don't count unchecked required radio as blank if other radio with same name is checked,
- # regardless of whether same-name radio input has required attribute or not. The spec
- # states https://www.w3.org/TR/html5/forms.html#the-required-attribute
- radioName = input.name
- # Skip if we've already seen the radio with this name.
- unless checkedRadioButtonNames[radioName]
- # If none checked
- if form.querySelectorAll("input[type=radio][name='#{radioName}']:checked").length == 0
- radios = form.querySelectorAll("input[type=radio][name='#{radioName}']")
- foundInputs = foundInputs.concat(toArray(radios))
- # We only need to check each name once.
- checkedRadioButtonNames[radioName] = radioName
- else
- valueToCheck = if input.type is 'checkbox' then input.checked else !!input.value
- foundInputs.push(input) if valueToCheck is nonBlank
- foundInputs
diff --git a/actionview/coffeelint.json b/actionview/coffeelint.json
new file mode 100644
index 0000000000..cf8bf2171b
--- /dev/null
+++ b/actionview/coffeelint.json
@@ -0,0 +1,135 @@
+{
+ "arrow_spacing": {
+ "level": "warn"
+ },
+ "braces_spacing": {
+ "level": "warn",
+ "spaces": 1,
+ "empty_object_spaces": 0
+ },
+ "camel_case_classes": {
+ "level": "error"
+ },
+ "coffeescript_error": {
+ "level": "error"
+ },
+ "colon_assignment_spacing": {
+ "level": "warn",
+ "spacing": {
+ "left": 0,
+ "right": 1
+ }
+ },
+ "cyclomatic_complexity": {
+ "level": "warn",
+ "value": 10
+ },
+ "duplicate_key": {
+ "level": "error"
+ },
+ "empty_constructor_needs_parens": {
+ "level": "warn"
+ },
+ "ensure_comprehensions": {
+ "level": "warn"
+ },
+ "eol_last": {
+ "level": "warn"
+ },
+ "indentation": {
+ "value": 2,
+ "level": "error"
+ },
+ "line_endings": {
+ "level": "warn",
+ "value": "unix"
+ },
+ "max_line_length": {
+ "value": 80,
+ "level": "ignore",
+ "limitComments": true
+ },
+ "missing_fat_arrows": {
+ "level": "ignore"
+ },
+ "newlines_after_classes": {
+ "value": 3,
+ "level": "warn"
+ },
+ "no_backticks": {
+ "level": "error"
+ },
+ "no_debugger": {
+ "level": "warn",
+ "console": false
+ },
+ "no_empty_functions": {
+ "level": "warn"
+ },
+ "no_empty_param_list": {
+ "level": "warn"
+ },
+ "no_implicit_braces": {
+ "level": "ignore",
+ "strict": true
+ },
+ "no_implicit_parens": {
+ "level": "ignore",
+ "strict": true
+ },
+ "no_interpolation_in_single_quotes": {
+ "level": "warn"
+ },
+ "no_nested_string_interpolation": {
+ "level": "warn"
+ },
+ "no_plusplus": {
+ "level": "warn"
+ },
+ "no_private_function_fat_arrows": {
+ "level": "warn"
+ },
+ "no_stand_alone_at": {
+ "level": "warn"
+ },
+ "no_tabs": {
+ "level": "error"
+ },
+ "no_this": {
+ "level": "warn"
+ },
+ "no_throwing_strings": {
+ "level": "error"
+ },
+ "no_trailing_semicolons": {
+ "level": "error"
+ },
+ "no_trailing_whitespace": {
+ "level": "error",
+ "allowed_in_comments": false,
+ "allowed_in_empty_lines": true
+ },
+ "no_unnecessary_double_quotes": {
+ "level": "warn"
+ },
+ "no_unnecessary_fat_arrows": {
+ "level": "warn"
+ },
+ "non_empty_constructor_needs_parens": {
+ "level": "warn"
+ },
+ "prefer_english_operator": {
+ "level": "ignore",
+ "doubleNotLevel": "warn"
+ },
+ "space_operators": {
+ "level": "warn"
+ },
+ "spacing_after_comma": {
+ "level": "warn"
+ },
+ "transform_messes_up_line_numbers": {
+ "level": "warn"
+ }
+}
+
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb
index 0658d8601d..ba189e23fe 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -62,8 +62,10 @@ module ActionView
node
end
else
- logger.error " '#{name}' file doesn't exist, so no dependencies"
- logger.error " Couldn't find template for digesting: #{name}"
+ unless name.include?('#') # Dynamic template partial names can never be tracked
+ logger.error " Couldn't find template for digesting: #{name}"
+ end
+
seen[name] ||= Missing.new(name, logical_name, nil)
end
end
diff --git a/actionview/lib/action_view/gem_version.rb b/actionview/lib/action_view/gem_version.rb
index 5fc4f3f1b9..92e21d7a4f 100644
--- a/actionview/lib/action_view/gem_version.rb
+++ b/actionview/lib/action_view/gem_version.rb
@@ -6,7 +6,7 @@ module ActionView
module VERSION
MAJOR = 5
- MINOR = 1
+ MINOR = 2
TINY = 0
PRE = "alpha"
diff --git a/actionview/lib/action_view/helpers/sanitize_helper.rb b/actionview/lib/action_view/helpers/sanitize_helper.rb
index 1e9b813d3d..0abd5bc5dc 100644
--- a/actionview/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionview/lib/action_view/helpers/sanitize_helper.rb
@@ -13,6 +13,7 @@ module ActionView
# It also strips href/src attributes with unsafe protocols like
# <tt>javascript:</tt>, while also protecting against attempts to use Unicode,
# ASCII, and hex character references to work around these protocol filters.
+ # All special characters will be escaped.
#
# The default sanitizer is Rails::Html::WhiteListSanitizer. See {Rails HTML
# Sanitizers}[https://github.com/rails/rails-html-sanitizer] for more information.
@@ -20,8 +21,7 @@ module ActionView
# Custom sanitization rules can also be provided.
#
# Please note that sanitizing user-provided text does not guarantee that the
- # resulting markup is valid or even well-formed. For example, the output may still
- # contain unescaped characters like <tt><</tt>, <tt>></tt>, or <tt>&</tt>.
+ # resulting markup is valid or even well-formed.
#
# ==== Options
#
@@ -86,7 +86,7 @@ module ActionView
self.class.white_list_sanitizer.sanitize_css(style)
end
- # Strips all HTML tags from +html+, including comments.
+ # Strips all HTML tags from +html+, including comments and special characters.
#
# strip_tags("Strip <i>these</i> tags!")
# # => Strip these tags!
@@ -96,8 +96,11 @@ module ActionView
#
# strip_tags("<div id='top-bar'>Welcome to my website!</div>")
# # => Welcome to my website!
+ #
+ # strip_tags("> A quote from Smith & Wesson")
+ # # => &gt; A quote from Smith &amp; Wesson
def strip_tags(html)
- self.class.full_sanitizer.sanitize(html, encode_special_chars: false)
+ self.class.full_sanitizer.sanitize(html)
end
# Strips all link tags from +html+ leaving just the link text.
@@ -110,6 +113,9 @@ module ActionView
#
# strip_links('Blog: <a href="http://www.myblog.com/" class="nav" target=\"_blank\">Visit</a>.')
# # => Blog: Visit.
+ #
+ # strip_links('<<a href="https://example.org">malformed & link</a>')
+ # # => &lt;malformed &amp; link
def strip_links(html)
self.class.link_sanitizer.sanitize(html)
end
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index a306903c60..304db38060 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -622,7 +622,7 @@ module ActionView
def to_form_params(attribute, namespace = nil)
attribute = if attribute.respond_to?(:permitted?)
unless attribute.permitted?
- raise ArgumentError, "Attempting to generate a buttom from non-sanitized request parameters!" \
+ raise ArgumentError, "Attempting to generate a button from non-sanitized request parameters!" \
" Whitelist and sanitize passed parameters to be secure."
end
diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb
index c067031d2d..b0e2f1e54e 100644
--- a/actionview/lib/action_view/template.rb
+++ b/actionview/lib/action_view/template.rb
@@ -345,7 +345,7 @@ module ActionView
end
def instrument(action, &block) # :doc:
- ActiveSupport::Notifications.instrument("#{action}.action_view".freeze, instrument_payload, &block)
+ ActiveSupport::Notifications.instrument("#{action}.action_view", instrument_payload, &block)
end
def instrument_render_template(&block)
diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb
index 2b981caa65..ae4fec4337 100644
--- a/actionview/lib/action_view/test_case.rb
+++ b/actionview/lib/action_view/test_case.rb
@@ -124,6 +124,10 @@ module ActionView
@_rendered_views ||= RenderedViewsCollection.new
end
+ def _routes
+ @controller._routes if @controller.respond_to?(:_routes)
+ end
+
# Need to experiment if this priority is the best one: rendered => output_buffer
class RenderedViewsCollection
def initialize
@@ -258,10 +262,6 @@ module ActionView
end]
end
- def _routes
- @controller._routes if @controller.respond_to?(:_routes)
- end
-
def method_missing(selector, *args)
begin
routes = @controller.respond_to?(:_routes) && @controller._routes
diff --git a/actionview/package.json b/actionview/package.json
index ec3306c299..85f4ddacbe 100644
--- a/actionview/package.json
+++ b/actionview/package.json
@@ -1,6 +1,6 @@
{
"name": "rails-ujs",
- "version": "0.0.1",
+ "version": "5.2.0-alpha",
"description": "Ruby on Rails unobtrusive scripting adapter",
"main": "lib/assets/compiled/rails-ujs.js",
"files": [
@@ -11,8 +11,8 @@
},
"scripts": {
"build": "bundle exec blade build",
- "test": "echo \"See the README: https://github.com/rails/rails-ujs#how-to-run-tests\" && exit 1",
- "lint": "coffeelint src && eslint test/public/test",
+ "test": "echo \"See the README: https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts#how-to-run-tests\" && exit 1",
+ "lint": "coffeelint app/assets/javascripts && eslint test/public/test"
},
"repository": {
"type": "git",
diff --git a/actionview/test/activerecord/polymorphic_routes_test.rb b/actionview/test/activerecord/polymorphic_routes_test.rb
index dcad0b424c..b2e0fb08c4 100644
--- a/actionview/test/activerecord/polymorphic_routes_test.rb
+++ b/actionview/test/activerecord/polymorphic_routes_test.rb
@@ -86,8 +86,7 @@ class PolymorphicRoutesTest < ActionController::TestCase
def test_string
with_test_routes do
- # FIXME: why are these different? Symbol case passes through to
- # `polymorphic_url`, but the String case doesn't.
+ assert_equal "/projects", polymorphic_path("projects")
assert_equal "http://example.com/projects", polymorphic_url("projects")
assert_equal "projects", url_for("projects")
end
@@ -731,3 +730,49 @@ class PolymorphicPathRoutesTest < PolymorphicRoutesTest
assert_equal url.sub(/http:\/\/#{host}/, ""), url_for(args)
end
end
+
+class DirectRoutesTest < ActionView::TestCase
+ class Linkable
+ attr_reader :id
+
+ def self.name
+ super.demodulize
+ end
+
+ def initialize(id)
+ @id = id
+ end
+
+ def linkable_type
+ self.class.name.underscore
+ end
+ end
+
+ class Category < Linkable; end
+ class Collection < Linkable; end
+ class Product < Linkable; end
+
+ Routes = ActionDispatch::Routing::RouteSet.new
+ Routes.draw do
+ resources :categories, :collections, :products
+ direct(:linkable) { |linkable| [:"#{linkable.linkable_type}", { id: linkable.id }] }
+ end
+
+ include Routes.url_helpers
+
+ def setup
+ @category = Category.new("1")
+ @collection = Collection.new("2")
+ @product = Product.new("3")
+ end
+
+ def test_direct_routes
+ assert_equal "/categories/1", linkable_path(@category)
+ assert_equal "/collections/2", linkable_path(@collection)
+ assert_equal "/products/3", linkable_path(@product)
+
+ assert_equal "http://test.host/categories/1", linkable_url(@category)
+ assert_equal "http://test.host/collections/2", linkable_url(@collection)
+ assert_equal "http://test.host/products/3", linkable_url(@product)
+ end
+end
diff --git a/actionview/test/template/date_helper_test.rb b/actionview/test/template/date_helper_test.rb
index bfd3ecd6fd..d257147e1f 100644
--- a/actionview/test/template/date_helper_test.rb
+++ b/actionview/test/template/date_helper_test.rb
@@ -832,7 +832,7 @@ class DateHelperTest < ActionView::TestCase
def test_select_date_with_too_big_range_between_start_year_and_end_year
assert_raise(ArgumentError) { select_date(Time.mktime(2003, 8, 16), start_year: 2000, end_year: 20000, prefix: "date[first]", order: [:month, :day, :year]) }
- assert_raise(ArgumentError) { select_date(Time.mktime(2003, 8, 16), start_year: Date.today.year - 100.years, end_year: 2000, prefix: "date[first]", order: [:month, :day, :year]) }
+ assert_raise(ArgumentError) { select_date(Time.mktime(2003, 8, 16), start_year: 100, end_year: 2000, prefix: "date[first]", order: [:month, :day, :year]) }
end
def test_select_date_can_have_more_then_1000_years_interval_if_forced_via_parameter
diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb
index a814cab686..e225c3de09 100644
--- a/actionview/test/template/digestor_test.rb
+++ b/actionview/test/template/digestor_test.rb
@@ -122,13 +122,13 @@ class TemplateDigestorTest < ActionView::TestCase
end
def test_logging_of_missing_template_for_dependencies
- assert_logged "'messages/something_missing' file doesn't exist, so no dependencies" do
+ assert_logged "Couldn't find template for digesting: messages/something_missing" do
dependencies("messages/something_missing")
end
end
def test_logging_of_missing_template_for_nested_dependencies
- assert_logged "'messages/something_missing' file doesn't exist, so no dependencies" do
+ assert_logged "Couldn't find template for digesting: messages/something_missing" do
nested_dependencies("messages/something_missing")
end
end
diff --git a/actionview/test/template/erb/tag_helper_test.rb b/actionview/test/template/erb/tag_helper_test.rb
index 84e328d8be..233f37c48a 100644
--- a/actionview/test/template/erb/tag_helper_test.rb
+++ b/actionview/test/template/erb/tag_helper_test.rb
@@ -18,8 +18,8 @@ module ERBTest
end
test "percent equals works with form tags" do
- expected_output = %r{<form.*action="foo".*method="post">.*hello*</form>}
- assert_match expected_output, render_content("form_tag('foo')", "<%= 'hello' %>")
+ expected_output = %r{<form.*action="/foo".*method="post">.*hello*</form>}
+ assert_match expected_output, render_content("form_tag('/foo')", "<%= 'hello' %>")
end
test "percent equals works with fieldset tags" do
diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb
index 490df5e4d9..b3a180b28a 100644
--- a/actionview/test/template/form_helper_test.rb
+++ b/actionview/test/template/form_helper_test.rb
@@ -257,11 +257,11 @@ class FormHelperTest < ActionView::TestCase
end
def test_label_with_non_active_record_object
- form_for(OpenStruct.new(name: "ok"), as: "person", url: "an_url", html: { id: "create-person" }) do |f|
+ form_for(OpenStruct.new(name: "ok"), as: "person", url: "/an", html: { id: "create-person" }) do |f|
f.label(:name)
end
- expected = whole_form("an_url", "create-person", "new_person", method: "post") do
+ expected = whole_form("/an", "create-person", "new_person", method: "post") do
'<label for="person_name">Name</label>'
end
diff --git a/actionview/test/template/sanitize_helper_test.rb b/actionview/test/template/sanitize_helper_test.rb
index c8963fee9c..4d4ed3c35c 100644
--- a/actionview/test/template/sanitize_helper_test.rb
+++ b/actionview/test/template/sanitize_helper_test.rb
@@ -1,6 +1,6 @@
require "abstract_unit"
-# The exhaustive tests are in test/controller/html/sanitizer_test.rb.
+# The exhaustive tests are in the rails-html-sanitizer gem.
# This tests that the helpers hook up correctly to the sanitizer classes.
class SanitizeHelperTest < ActionView::TestCase
tests ActionView::Helpers::SanitizeHelper
@@ -10,6 +10,7 @@ class SanitizeHelperTest < ActionView::TestCase
assert_equal "on my mind\nall day long", strip_links("<a href='almost'>on my mind</a>\n<A href='almost'>all day long</A>")
assert_equal "Magic", strip_links("<a href='http://www.rubyonrails.com/'>Mag<a href='http://www.ruby-lang.org/'>ic")
assert_equal "My mind\nall <b>day</b> long", strip_links("<a href='almost'>My mind</a>\n<A href='almost'>all <b>day</b> long</A>")
+ assert_equal "&lt;malformed &amp; link", strip_links('<<a href="https://example.org">malformed & link</a>')
end
def test_sanitize_form
@@ -26,6 +27,7 @@ class SanitizeHelperTest < ActionView::TestCase
assert_equal("Dont touch me", strip_tags("Dont touch me"))
assert_equal("This is a test.", strip_tags("<p>This <u>is<u> a <a href='test.html'><strong>test</strong></a>.</p>"))
assert_equal "This has a here.", strip_tags("This has a <!-- comment --> here.")
+ assert_equal("Jekyll &amp; Hyde", strip_tags("Jekyll & Hyde"))
assert_equal "", strip_tags("<script>")
end
diff --git a/actionview/test/ujs/config.ru b/actionview/test/ujs/config.ru
index 414c2063c3..48b7a4b53a 100644
--- a/actionview/test/ujs/config.ru
+++ b/actionview/test/ujs/config.ru
@@ -1,3 +1,4 @@
$LOAD_PATH.unshift File.expand_path("..", __FILE__)
require "server"
+
run UJS::Server
diff --git a/actionview/test/ujs/public/test/call-remote-callbacks.js b/actionview/test/ujs/public/test/call-remote-callbacks.js
index 082d10bfbd..707e21541d 100644
--- a/actionview/test/ujs/public/test/call-remote-callbacks.js
+++ b/actionview/test/ujs/public/test/call-remote-callbacks.js
@@ -108,202 +108,6 @@ asyncTest('stopping the "ajax:beforeSend" event aborts the request', 1, function
})
})
-asyncTest('blank required form input field should abort request and trigger "ajax:aborted:required" event', 5, function() {
- $(document).bind('iframe:loading', function() {
- ok(false, 'form should not get submitted')
- })
-
- var form = $('form[data-remote]')
- .append($('<input type="text" name="user_name" required="required">'))
- .append($('<textarea name="user_bio" required="required"></textarea>'))
- .bindNative('ajax:beforeSend', function() {
- ok(false, 'ajax:beforeSend should not run')
- })
- .bindNative('ajax:aborted:required', function(e, data) {
- data = $(data)
- ok(data.length == 2, 'ajax:aborted:required event is passed all blank required inputs (jQuery objects)')
- ok(data.first().is('input[name="user_name"]'), 'ajax:aborted:required adds blank required input to data')
- ok(data.last().is('textarea[name="user_bio"]'), 'ajax:aborted:required adds blank required textarea to data')
- ok(true, 'ajax:aborted:required should run')
- })
- .triggerNative('submit')
-
- setTimeout(function() {
- form.find('input[required],textarea[required]').val('Tyler')
- form.unbind('ajax:beforeSend')
- submit()
- }, 13)
-})
-
-asyncTest('blank required form input for non-remote form should abort normal submission', 1, function() {
- var form = $('form[data-remote]')
- .append($('<input type="text" name="user_name" required="required">'))
- .removeAttr('data-remote')
- .bindNative('ujs:everythingStopped', function() {
- ok(true, 'ujs:everythingStopped should run')
- })
- .triggerNative('submit')
-
- setTimeout(function() {
- start()
- }, 13)
-})
-
-asyncTest('form should be submitted with blank required fields if handler is bound to "ajax:aborted:required" event that returns false', 1, function() {
- var form = $('form[data-remote]')
- .append($('<input type="text" name="user_name" required="required">'))
- .bindNative('ajax:beforeSend', function() {
- ok(true, 'ajax:beforeSend should run')
- })
- .bindNative('ajax:aborted:required', function() {
- return false
- })
- .triggerNative('submit')
-
- setTimeout(function() {
- start()
- }, 13)
-})
-
-asyncTest('disabled fields should not be included in blank required check', 2, function() {
- var form = $('form[data-remote]')
- .append($('<input type="text" name="user_name" required="required" disabled="disabled">'))
- .append($('<textarea name="user_bio" required="required" disabled="disabled"></textarea>'))
- .bindNative('ajax:beforeSend', function() {
- ok(true, 'ajax:beforeSend should run')
- })
- .bindNative('ajax:aborted:required', function() {
- ok(false, 'ajax:aborted:required should not run')
- })
-
- submit()
-})
-
-asyncTest('form should be submitted with blank required fields if it has the "novalidate" attribute', 2, function() {
- var form = $('form[data-remote]')
- .append($('<input type="text" name="user_name" required="required">'))
- .attr('novalidate', 'novalidate')
- .bindNative('ajax:beforeSend', function() {
- ok(true, 'ajax:beforeSend should run')
- })
- .bindNative('ajax:aborted:required', function() {
- ok(false, 'ajax:aborted:required should not run')
- })
-
- submit()
-})
-
-asyncTest('form should be submitted with blank required fields if the button has the "formnovalidate" attribute', 2, function() {
- var submit_button = $('<input type="submit" formnovalidate>')
- var form = $('form[data-remote]')
- .append($('<input type="text" name="user_name" required="required">'))
- .append(submit_button)
- .bindNative('ajax:beforeSend', function() {
- ok(true, 'ajax:beforeSend should run')
- })
- .bindNative('ajax:aborted:required', function() {
- ok(false, 'ajax:aborted:required should not run')
- })
-
- submit_with_button(submit_button)
-})
-
-asyncTest('blank required form input for non-remote form with "novalidate" attribute should not abort normal submission', 1, function() {
- $(document).bind('iframe:loading', function() {
- ok(true, 'form should get submitted')
- })
-
- var form = $('form[data-remote]')
- .append($('<input type="text" name="user_name" required="required">'))
- .removeAttr('data-remote')
- .attr('novalidate', 'novalidate')
- .triggerNative('submit')
-
- setTimeout(function() {
- start()
- }, 13)
-})
-
-asyncTest('unchecked required checkbox should abort form submission', 1, function() {
- var form = $('form[data-remote]')
- .append($('<input type="checkbox" name="agree" required="required">'))
- .removeAttr('data-remote')
- .bindNative('ujs:everythingStopped', function() {
- ok(true, 'ujs:everythingStopped should run')
- })
- .triggerNative('submit')
-
- setTimeout(function() {
- start()
- }, 13)
-})
-
-asyncTest('unchecked required radio should abort form submission', 3, function() {
- var form = $('form[data-remote]')
- .append($('<input type="radio" name="yes_no_none" required="required" value=1>'))
- .append($('<input type="radio" name="yes_no_none" required="required" value=2>'))
- .removeAttr('data-remote')
- .bindNative('ujs:everythingStopped', function() {
- ok(true, 'ujs:everythingStopped should run')
- })
- .bindNative('ajax:aborted:required', function(e, data) {
- data = $(data)
- equal(data.length, 2, 'blankRequiredInputs should include both radios')
- ok(data.first().is('input[type=radio][value=1]'), 'blankRequiredInputs[0] should be the first radio')
- })
- .triggerNative('submit')
-
- setTimeout(function() {
- start()
- }, 13)
-})
-
-asyncTest('required radio should only require one to be checked', 1, function() {
- $(document).bind('iframe:loading', function() {
- ok(true, 'form should get submitted')
- })
-
- var form = $('form[data-remote]')
- .append($('<input type="radio" name="yes_no" required="required" value=1 id="checkme">'))
- .append($('<input type="radio" name="yes_no" required="required" value=2>'))
- .removeAttr('data-remote')
- .bindNative('ujs:everythingStopped', function() {
- ok(false, 'ujs:everythingStopped should not run')
- })
- .find('#checkme').prop('checked', true)
- .end()
- .triggerNative('submit')
-
- setTimeout(function() {
- start()
- }, 13)
-})
-
-asyncTest('required radio should only require one to be checked if not all radios are required', 1, function() {
- $(document).bind('iframe:loading', function() {
- ok(true, 'form should get submitted')
- })
-
- var form = $('form[data-remote]')
- // Check the radio that is not required
- .append($('<input type="radio" name="yes_no_maybe" value=1 >'))
- // Check the radio that is not required
- .append($('<input type="radio" name="yes_no_maybe" value=2 id="checkme">'))
- // Only one needs to be required
- .append($('<input type="radio" name="yes_no_maybe" required="required" value=3>'))
- .removeAttr('data-remote')
- .bindNative('ujs:everythingStopped', function() {
- ok(false, 'ujs:everythingStopped should not run')
- })
- .find('#checkme').prop('checked', true)
- .end()
- .triggerNative('submit')
-
- setTimeout(function() {
- start()
- }, 13)
-})
-
function skipIt() {
// This test cannot work due to the security feature in browsers which makes the value
// attribute of file input fields readonly, so it cannot be set with default value.
diff --git a/actionview/test/ujs/public/test/data-confirm.js b/actionview/test/ujs/public/test/data-confirm.js
index 28190c2250..229b9e1466 100644
--- a/actionview/test/ujs/public/test/data-confirm.js
+++ b/actionview/test/ujs/public/test/data-confirm.js
@@ -26,6 +26,13 @@ module('data-confirm', {
'data-confirm': 'Are you absolutely sure?'
}))
+ $('#qunit-fixture').append($('<button />', {
+ type: 'submit',
+ form: 'confirm',
+ disabled: 'disabled',
+ 'data-confirm': 'Are you absolutely sure?'
+ }))
+
this.windowConfirm = window.confirm
},
teardown: function() {
@@ -286,3 +293,24 @@ asyncTest('clicking on the children of a link should also trigger a confirm', 6,
.find('strong')
.triggerNative('click')
})
+
+asyncTest('clicking on the children of a disabled button should not trigger a confirm.', 1, function() {
+ var message
+ // auto-decline:
+ window.confirm = function(msg) { message = msg; return false }
+
+ $('button[data-confirm][disabled]')
+ .html("<strong>Click me</strong>")
+ .bindNative('confirm', function() {
+ App.assertCallbackNotInvoked('confirm')
+ })
+ .find('strong')
+ .bindNative('click', function() {
+ App.assertCallbackInvoked('click')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ start()
+ }, 50)
+})
diff --git a/actionview/test/ujs/public/test/data-remote.js b/actionview/test/ujs/public/test/data-remote.js
index a51aa10417..b756add24e 100644
--- a/actionview/test/ujs/public/test/data-remote.js
+++ b/actionview/test/ujs/public/test/data-remote.js
@@ -73,7 +73,6 @@ asyncTest('clicking on a link with data-remote attribute', 5, function() {
.bindNative('ajax:success', function(e, data, status, xhr) {
App.assertCallbackInvoked('ajax:success')
App.assertRequestPath(data, '/echo')
- console.log(data.params)
equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value')
equal(data.params.data2, 'value2', 'ajax arguments should have key data2 with right value')
App.assertGetRequest(data)
@@ -398,3 +397,19 @@ asyncTest('form should be serialized correctly', 6, function() {
})
.triggerNative('submit')
})
+
+asyncTest('form buttons should only be serialized when clicked', 4, function() {
+ $('form')
+ .append('<input type="submit" name="submit1" value="submit1" />')
+ .append('<button name="submit2" value="submit2" />')
+ .append('<button name="submit3" value="submit3" />')
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ equal(data.params.submit1, undefined)
+ equal(data.params.submit2, 'submit2')
+ equal(data.params.submit3, undefined)
+ equal(data['rack.request.form_vars'], 'user_name=john&submit2=submit2')
+
+ start()
+ })
+ .find('[name=submit2]').triggerNative('click')
+})
diff --git a/actionview/test/ujs/public/test/override.js b/actionview/test/ujs/public/test/override.js
index be6ec7749b..299c7018cc 100644
--- a/actionview/test/ujs/public/test/override.js
+++ b/actionview/test/ujs/public/test/override.js
@@ -46,7 +46,7 @@ asyncTest('the event selector strings are overridable', 1, function() {
start()
})
-asyncTest('including jquery-ujs multiple times throws error', 1, function() {
+asyncTest('including rails-ujs multiple times throws error', 1, function() {
throws(function() {
Rails.start()
}, 'appending rails.js again throws error')
diff --git a/actionview/test/ujs/public/test/settings.js b/actionview/test/ujs/public/test/settings.js
index c68ca24d6a..299c71bb00 100644
--- a/actionview/test/ujs/public/test/settings.js
+++ b/actionview/test/ujs/public/test/settings.js
@@ -63,12 +63,12 @@ $(document).bind('submit', function(e) {
}
})
-var MouseEvent = window.MouseEvent
+var _MouseEvent = window.MouseEvent
try {
- new MouseEvent()
+ new _MouseEvent()
} catch (e) {
- MouseEvent = function(type, options) {
+ _MouseEvent = function(type, options) {
var evt = document.createEvent('MouseEvents')
evt.initMouseEvent(type, options.bubbles, options.cancelable, window, options.detail, 0, 0, 80, 20, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, null)
return evt
@@ -81,7 +81,7 @@ $.fn.extend({
var el = this[0],
event,
Evt = {
- 'click': MouseEvent,
+ 'click': _MouseEvent,
'change': Event,
'pageshow': PageTransitionEvent,
'submit': Event
diff --git a/actionview/test/ujs/server.rb b/actionview/test/ujs/server.rb
index 25f70baf5f..7deb208af0 100644
--- a/actionview/test/ujs/server.rb
+++ b/actionview/test/ujs/server.rb
@@ -1,11 +1,10 @@
+require "rack"
require "rails"
require "action_controller/railtie"
require "action_view/railtie"
require "blade"
require "json"
-JQUERY_VERSIONS = %w[ 1.8.0 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.10.0 1.10.1 1.10.2 1.11.0 2.0.0 2.1.0].freeze
-
module UJS
class Server < Rails::Application
routes.append do
@@ -18,7 +17,7 @@ module UJS
config.cache_classes = false
config.eager_load = false
config.secret_key_base = "59d7a4dbd349fa3838d79e330e39690fc22b931e7dc17d9162f03d633d526fbb92dfdb2dc9804c8be3e199631b9c1fbe43fc3e4fc75730b515851849c728d5c7"
- config.paths["app/views"].unshift("#{Rails.root / "views"}")
+ config.paths["app/views"].unshift("#{Rails.root}/views")
config.public_file_server.enabled = true
config.logger = Logger.new(STDOUT)
config.log_level = :error
@@ -26,32 +25,6 @@ module UJS
end
module TestsHelper
- def jquery_link(version)
- if params[:version] == version
- "[#{version}]"
- else
- "<a href='/?version=#{version}&cdn=#{params[:cdn]}'>#{version}</a>".html_safe
- end
- end
-
- def cdn_link(cdn)
- if params[:cdn] == cdn
- "[#{cdn}]"
- else
- "<a href='/?version=#{params[:version]}&cdn=#{cdn}'>#{cdn}</a>".html_safe
- end
- end
-
- def jquery_src
- if params[:version] == "edge"
- "/vendor/jquery.js"
- elsif params[:cdn] && params[:cdn] == "googleapis"
- "https://ajax.googleapis.com/ajax/libs/jquery/#{params[:version]}/jquery.min.js"
- else
- "http://code.jquery.com/jquery-#{params[:version]}.js"
- end
- end
-
def test_to(*names)
names = ["/vendor/qunit.js", "settings"] + names
names.map { |name| script_tag name }.join("\n").html_safe
@@ -61,10 +34,6 @@ module TestsHelper
src = "/test/#{src}.js" unless src.index("/")
%(<script src="#{src}" type="text/javascript"></script>).html_safe
end
-
- def jquery_versions
- JQUERY_VERSIONS
- end
end
class TestsController < ActionController::Base
@@ -72,8 +41,6 @@ class TestsController < ActionController::Base
layout "application"
def index
- params[:version] ||= ENV["JQUERY_VERSION"] || "1.11.0"
- params[:cdn] ||= "jquery"
render :index
end
diff --git a/actionview/test/ujs/views/layouts/application.html.erb b/actionview/test/ujs/views/layouts/application.html.erb
index 74fa3bd06d..a69cd2d739 100644
--- a/actionview/test/ujs/views/layouts/application.html.erb
+++ b/actionview/test/ujs/views/layouts/application.html.erb
@@ -3,30 +3,15 @@
<head>
<title><%= @title %></title>
<link href="/vendor/qunit.css" media="screen" rel="stylesheet" type="text/css" media="screen, projection" />
- <style>
- #jquery-cdn, #jquery-version {
- padding: 0 2em .8em 0;
- text-align: right;
- font-family: sans-serif;
- line-height: 1;
- color: #8699A4;
- background-color: #0d3349;
- }
- #jquery-cdn a, #jquery-version a {
- color: white;
- text-decoration: underline;
- }
- </style>
-
- <%= script_tag jquery_src %>
+ <%= script_tag "http://code.jquery.com/jquery-2.2.0.js" %>
<script>
// This is for test in override.js.
- // Must go after jQuery is loaded, but before jquery-ujs.
- $(document).bind('rails:attachBindings', function() {
- $.rails.linkClickSelector += ', a[data-custom-remote-link]';
+ // Must go before rails-ujs.
+ document.addEventListener('rails:attachBindings', function() {
+ window.Rails.linkClickSelector += ', a[data-custom-remote-link]';
// Hijacks link click before ujs binds any handlers
// This is only used for ctrl-clicking test on remote links
- $.rails.delegate(document, '#qunit-fixture a', 'click', function(e) {
+ window.Rails.delegate(document, '#qunit-fixture a', 'click', function(e) {
e.preventDefault();
});
});
diff --git a/actionview/test/ujs/views/tests/index.html.erb b/actionview/test/ujs/views/tests/index.html.erb
index 393a5ee235..8de6cd0695 100644
--- a/actionview/test/ujs/views/tests/index.html.erb
+++ b/actionview/test/ujs/views/tests/index.html.erb
@@ -1,22 +1,8 @@
-<% @title = "jquery-ujs test" %>
+<% @title = "rails-ujs test" %>
<%= test_to 'data-confirm', 'data-remote', 'data-disable', 'data-disable-with', 'call-remote', 'call-remote-callbacks', 'data-method', 'override', 'csrf-refresh', 'csrf-token' %>
<h1 id="qunit-header"><%= @title %></h1>
-<div id="jquery-cdn">
- CDN:
- <%= cdn_link 'jquery' %> •
- <%= cdn_link 'googleapis' %>
-</div>
-<div id="jquery-version">
- jQuery version:
-
- <% jquery_versions.each do |v| %>
- <%= ' • ' if v != jquery_versions.first %>
- <%= jquery_link v %>
- <% end %>
- <%= (' • ' + jquery_link('edge')) if File.exist?(Rails.root + '/public/vendor/jquery.js') %>
-</div>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>