code cleanup/refactor
This commit is contained in:
parent
5ca348f7d1
commit
86ede5207a
15
.rubocop.yml
15
.rubocop.yml
|
@ -14,22 +14,25 @@ Metrics/LineLength:
|
|||
Enabled: false
|
||||
|
||||
Metrics/AbcSize:
|
||||
Max: 53
|
||||
Enabled: false
|
||||
|
||||
Metrics/ClassLength:
|
||||
Max: 460
|
||||
CountComments: false
|
||||
Enabled: false
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 17
|
||||
Enabled: false
|
||||
|
||||
Metrics/MethodLength:
|
||||
Max: 29
|
||||
CountComments: false
|
||||
Enabled: false
|
||||
|
||||
Metrics/ModuleLength:
|
||||
Max: 194
|
||||
Enabled: false
|
||||
CountComments: false
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
Max: 19
|
||||
Enabled: false
|
||||
|
||||
Lint/UnusedMethodArgument:
|
||||
Exclude:
|
||||
|
|
|
@ -230,17 +230,8 @@ module Capybara
|
|||
end
|
||||
# Allow user to update the CSS style of the file input since they are so often hidden on a page
|
||||
if make_visible
|
||||
make_visible = { opacity: 1, display: 'block', visibility: 'visible' } if make_visible == true
|
||||
ff = find(:file_field, locator, options.merge(visible: :all))
|
||||
_update_style(ff, make_visible)
|
||||
|
||||
raise ExpectationNotMet, "The style changes in :make_visible did not make the file input visible" unless ff.visible?
|
||||
|
||||
begin
|
||||
ff.set(path)
|
||||
ensure
|
||||
_reset_style(ff)
|
||||
end
|
||||
while_visible(ff, make_visible) { |el| el.set(path) }
|
||||
else
|
||||
find(:file_field, locator, options).set(path)
|
||||
end
|
||||
|
@ -248,36 +239,26 @@ module Capybara
|
|||
|
||||
private
|
||||
|
||||
def _update_style(element, style)
|
||||
script = <<-JS
|
||||
var el = arguments[0];
|
||||
el.capybara_style_cache = el.style.cssText;
|
||||
var css = arguments[1];
|
||||
for (var prop in css){
|
||||
if (css.hasOwnProperty(prop)) {
|
||||
el.style[prop] = css[prop]
|
||||
}
|
||||
}
|
||||
JS
|
||||
def while_visible(element, visible_css)
|
||||
visible_css = { opacity: 1, display: 'block', visibility: 'visible' } if visible_css == true
|
||||
_update_style(element, visible_css)
|
||||
raise ExpectationNotMet, "The style changes in :make_visible did not make the file input visible" unless element.visible?
|
||||
begin
|
||||
session.execute_script(script, element, style)
|
||||
rescue Capybara::NotSupportedByDriverError
|
||||
warn "The :make_visible option is not supported by the current driver - ignoring"
|
||||
yield element
|
||||
ensure
|
||||
_reset_style(element)
|
||||
end
|
||||
end
|
||||
|
||||
def _update_style(element, style)
|
||||
session.execute_script(UPDATE_STYLE_SCRIPT, element, style)
|
||||
rescue Capybara::NotSupportedByDriverError
|
||||
warn "The :make_visible option is not supported by the current driver - ignoring"
|
||||
end
|
||||
|
||||
def _reset_style(element)
|
||||
script = <<-JS
|
||||
var el = arguments[0];
|
||||
if (el.hasOwnProperty('capybara_style_cache')) {
|
||||
el.style.cssText = el.capybara_style_cache;
|
||||
delete el.capybara_style_cache;
|
||||
}
|
||||
JS
|
||||
begin
|
||||
session.execute_script(script, element)
|
||||
rescue # swallow extra errors
|
||||
end
|
||||
session.execute_script(RESET_STYLE_SCRIPT, element)
|
||||
rescue # swallow extra errors
|
||||
end
|
||||
|
||||
def _check_with_label(selector, checked, locator, allow_label_click: session_options.automatic_label_click, **options)
|
||||
|
@ -289,14 +270,32 @@ module Capybara
|
|||
raise unless allow_label_click && catch_error?(e)
|
||||
begin
|
||||
el ||= find(selector, locator, options.merge(visible: :all))
|
||||
label = find(:label, for: el, visible: true)
|
||||
label.click unless el.checked? == checked
|
||||
find(:label, for: el, visible: true).click unless el.checked? == checked
|
||||
rescue # swallow extra errors - raise original
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
UPDATE_STYLE_SCRIPT = <<-'JS'.freeze
|
||||
var el = arguments[0];
|
||||
el.capybara_style_cache = el.style.cssText;
|
||||
var css = arguments[1];
|
||||
for (var prop in css){
|
||||
if (css.hasOwnProperty(prop)) {
|
||||
el.style[prop] = css[prop]
|
||||
}
|
||||
}
|
||||
JS
|
||||
|
||||
RESET_STYLE_SCRIPT = <<-'JS'.freeze
|
||||
var el = arguments[0];
|
||||
if (el.hasOwnProperty('capybara_style_cache')) {
|
||||
el.style.cssText = el.capybara_style_cache;
|
||||
delete el.capybara_style_cache;
|
||||
}
|
||||
JS
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -75,18 +75,16 @@ module Capybara
|
|||
# @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
|
||||
#
|
||||
def synchronize(seconds = session_options.default_max_wait_time, errors: nil)
|
||||
start_time = Capybara::Helpers.monotonic_time
|
||||
|
||||
if session.synchronized
|
||||
yield
|
||||
else
|
||||
session.synchronized = true
|
||||
start_time = Capybara::Helpers.monotonic_time
|
||||
begin
|
||||
yield
|
||||
rescue => e
|
||||
session.raise_server_error!
|
||||
raise e unless driver.wait?
|
||||
raise e unless catch_error?(e, errors)
|
||||
raise e unless driver.wait? && catch_error?(e, errors)
|
||||
raise e if (Capybara::Helpers.monotonic_time - start_time) >= seconds
|
||||
sleep(0.05)
|
||||
raise Capybara::FrozenInTime, "time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead" if Capybara::Helpers.monotonic_time == start_time
|
||||
|
|
|
@ -257,10 +257,9 @@ module Capybara
|
|||
minimum_specified = %i[count minimum between].any? { |k| options.key?(k) }
|
||||
options = { minimum: 1 }.merge(options) unless minimum_specified
|
||||
options[:session_options] = session_options
|
||||
args.push(options)
|
||||
query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
|
||||
query = Capybara::Queries::SelectorQuery.new(*args.push(options), &optional_filter_block)
|
||||
result = nil
|
||||
begin
|
||||
result = nil
|
||||
synchronize(query.wait) do
|
||||
result = query.resolve_for(self)
|
||||
raise Capybara::ExpectationNotMet, result.failure_message unless result.matches_count?
|
||||
|
@ -296,22 +295,27 @@ module Capybara
|
|||
|
||||
def synced_resolve(query)
|
||||
synchronize(query.wait) do
|
||||
if query.match == :smart or query.match == :prefer_exact
|
||||
if prefer_exact?(query)
|
||||
result = query.resolve_for(self, true)
|
||||
result = query.resolve_for(self, false) if result.empty? && query.supports_exact? && !query.exact?
|
||||
else
|
||||
result = query.resolve_for(self)
|
||||
end
|
||||
|
||||
if query.match == :one or query.match == :smart and result.size > 1
|
||||
raise Capybara::Ambiguous, "Ambiguous match, found #{result.size} elements matching #{query.description}"
|
||||
end
|
||||
|
||||
raise Capybara::Ambiguous, "Ambiguous match, found #{result.size} elements matching #{query.description}" if ambiguous?(query, result)
|
||||
raise Capybara::ElementNotFound, "Unable to find #{query.description}" if result.empty?
|
||||
|
||||
result.first
|
||||
end.tap(&:allow_reload!)
|
||||
end
|
||||
|
||||
def ambiguous?(query, result)
|
||||
query.match == :one or query.match == :smart and result.size > 1
|
||||
end
|
||||
|
||||
def prefer_exact?(query)
|
||||
query.match == :smart or query.match == :prefer_exact
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -61,25 +61,7 @@ module Capybara
|
|||
when :hidden then return false if node.visible?
|
||||
end
|
||||
|
||||
res = node_filters.all? do |name, filter|
|
||||
if options.key?(name)
|
||||
filter.matches?(node, options[name])
|
||||
elsif filter.default?
|
||||
filter.matches?(node, filter.default)
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
if @filter_block
|
||||
res &&= if node.respond_to?(:session)
|
||||
node.session.using_wait_time(0) { @filter_block.call(node) }
|
||||
else
|
||||
@filter_block.call(node)
|
||||
end
|
||||
end
|
||||
|
||||
res
|
||||
matches_node_filters?(node) && matches_filter_block?(node)
|
||||
rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
|
||||
return false
|
||||
end
|
||||
|
@ -142,6 +124,28 @@ module Capybara
|
|||
VALID_KEYS + custom_keys
|
||||
end
|
||||
|
||||
def matches_node_filters?(node)
|
||||
node_filters.all? do |name, filter|
|
||||
if options.key?(name)
|
||||
filter.matches?(node, options[name])
|
||||
elsif filter.default?
|
||||
filter.matches?(node, filter.default)
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def matches_filter_block?(node)
|
||||
return true unless @filter_block
|
||||
|
||||
if node.respond_to?(:session)
|
||||
node.session.using_wait_time(0) { @filter_block.call(node) }
|
||||
else
|
||||
@filter_block.call(node)
|
||||
end
|
||||
end
|
||||
|
||||
def node_filters
|
||||
if options.key?(:filter_set)
|
||||
::Capybara::Selector::FilterSet.all[options[:filter_set]].node_filters
|
||||
|
|
|
@ -54,31 +54,31 @@ module Capybara
|
|||
message << " in #{@actual_text.inspect}"
|
||||
|
||||
details_message = []
|
||||
|
||||
if @node and !@expected_text.is_a? Regexp
|
||||
insensitive_regexp = Capybara::Helpers.to_regexp(@expected_text, Regexp::IGNORECASE)
|
||||
insensitive_count = @actual_text.scan(insensitive_regexp).size
|
||||
if insensitive_count != @count
|
||||
details_message << "it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
|
||||
end
|
||||
end
|
||||
|
||||
if @node and check_visible_text? and report_on_invisible
|
||||
begin
|
||||
invisible_text = text(@node, :all)
|
||||
invisible_count = invisible_text.scan(@search_regexp).size
|
||||
if invisible_count != @count
|
||||
details_message << "it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
|
||||
end
|
||||
rescue # An error getting the non-visible text (if element goes out of scope) should not affect the response
|
||||
end
|
||||
end
|
||||
details_message << case_insensitive_message if @node and !@expected_text.is_a? Regexp
|
||||
details_message << invisible_message if @node and check_visible_text? and report_on_invisible
|
||||
details_message.compact!
|
||||
|
||||
message << ". (However, #{details_message.join(' and ')}.)" unless details_message.empty?
|
||||
|
||||
message
|
||||
end
|
||||
|
||||
def case_insensitive_message
|
||||
insensitive_regexp = Capybara::Helpers.to_regexp(@expected_text, Regexp::IGNORECASE)
|
||||
insensitive_count = @actual_text.scan(insensitive_regexp).size
|
||||
if insensitive_count != @count
|
||||
"it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
|
||||
end
|
||||
end
|
||||
|
||||
def invisible_message
|
||||
invisible_text = text(@node, :all)
|
||||
invisible_count = invisible_text.scan(@search_regexp).size
|
||||
if invisible_count != @count
|
||||
"it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
|
||||
end
|
||||
rescue # An error getting the non-visible text (if element goes out of scope) should not affect the response
|
||||
end
|
||||
|
||||
def valid_keys
|
||||
COUNT_KEYS + %i[wait exact]
|
||||
end
|
||||
|
|
|
@ -56,9 +56,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|||
raise ArgumentError, "The RackTest driver does not support click options" unless keys.empty? && offset.empty?
|
||||
|
||||
if tag_name == 'a' && !self[:href].nil?
|
||||
method = self["data-method"] if driver.options[:respect_data_method]
|
||||
method ||= :get
|
||||
driver.follow(method, self[:href].to_s)
|
||||
follow_link
|
||||
elsif (tag_name == 'input' and %w[submit image].include?(type)) or
|
||||
(tag_name == 'button' and [nil, "submit"].include?(type))
|
||||
associated_form = form
|
||||
|
@ -66,15 +64,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|||
elsif tag_name == 'input' and %w[checkbox radio].include?(type)
|
||||
set(!checked?)
|
||||
elsif tag_name == 'label'
|
||||
labelled_control = if native[:for]
|
||||
find_xpath("//input[@id='#{native[:for]}']").first
|
||||
else
|
||||
find_xpath(".//input").first
|
||||
end
|
||||
|
||||
if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
|
||||
labelled_control.set(!labelled_control.checked?)
|
||||
end
|
||||
click_label
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -194,6 +184,24 @@ private
|
|||
self[attribute] && !self[attribute].empty?
|
||||
end
|
||||
|
||||
def follow_link
|
||||
method = self["data-method"] if driver.options[:respect_data_method]
|
||||
method ||= :get
|
||||
driver.follow(method, self[:href].to_s)
|
||||
end
|
||||
|
||||
def click_label
|
||||
labelled_control = if native[:for]
|
||||
find_xpath("//input[@id='#{native[:for]}']").first
|
||||
else
|
||||
find_xpath(".//input").first
|
||||
end
|
||||
|
||||
if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
|
||||
labelled_control.set(!labelled_control.checked?)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def checkbox?
|
||||
|
|
|
@ -62,7 +62,6 @@ module Capybara
|
|||
!any?
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def compare_count
|
||||
# Only check filters for as many elements as necessary to determine result
|
||||
if @query.options[:count]
|
||||
|
@ -106,7 +105,6 @@ module Capybara
|
|||
|
||||
return 0
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
def matches_count?
|
||||
compare_count.zero?
|
||||
|
|
|
@ -143,11 +143,11 @@ Capybara.add_selector(:link) do
|
|||
end
|
||||
unless locator.nil?
|
||||
locator = locator.to_s
|
||||
matchers = [XPath.attr(:id).equals(locator),
|
||||
matchers = [XPath.attr(:id) == locator,
|
||||
XPath.string.n.is(locator),
|
||||
XPath.attr(:title).is(locator),
|
||||
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]].reduce(:|)
|
||||
matchers = matchers.or XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
||||
matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
||||
xpath = xpath[matchers]
|
||||
end
|
||||
xpath = xpath[find_by_attr(:title, title)]
|
||||
|
@ -193,14 +193,14 @@ Capybara.add_selector(:button) do
|
|||
unless locator.nil?
|
||||
locator = locator.to_s
|
||||
locator_matches = XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)
|
||||
locator_matches = locator_matches.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
||||
locator_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
||||
|
||||
input_btn_xpath = input_btn_xpath[locator_matches]
|
||||
|
||||
btn_xpath = btn_xpath[locator_matches | XPath.string.n.is(locator) | XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
|
||||
|
||||
alt_matches = XPath.attr(:alt).is(locator)
|
||||
alt_matches = alt_matches.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
||||
alt_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
||||
image_btn_xpath = image_btn_xpath[alt_matches]
|
||||
end
|
||||
|
||||
|
@ -473,7 +473,7 @@ Capybara.add_selector(:label) do
|
|||
xpath = XPath.descendant(:label)
|
||||
xpath = xpath[XPath.string.n.is(locator.to_s) | (XPath.attr(:id) == locator.to_s)] unless locator.nil?
|
||||
if options.key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
|
||||
with_attr = XPath.attr(:for).equals(options[:for].to_s)
|
||||
with_attr = XPath.attr(:for) == options[:for].to_s
|
||||
labelable_elements = %i[button input keygen meter output progress select textarea]
|
||||
wrapped = !XPath.attr(:for) &
|
||||
XPath.descendant(*labelable_elements)[XPath.attr(:id) == options[:for].to_s]
|
||||
|
|
|
@ -237,10 +237,10 @@ module Capybara
|
|||
locate_xpath = xpath # Need to save original xpath for the label wrap
|
||||
if locator
|
||||
locator = locator.to_s
|
||||
attr_matchers = [XPath.attr(:id).equals(locator),
|
||||
XPath.attr(:name).equals(locator),
|
||||
XPath.attr(:placeholder).equals(locator),
|
||||
XPath.attr(:id).equals(XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for))].reduce(:|)
|
||||
attr_matchers = [XPath.attr(:id) == locator,
|
||||
XPath.attr(:name) == locator,
|
||||
XPath.attr(:placeholder) == locator,
|
||||
XPath.attr(:id) == XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for)].reduce(:|)
|
||||
attr_matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
||||
|
||||
locate_xpath = locate_xpath[attr_matchers]
|
||||
|
@ -260,7 +260,7 @@ module Capybara
|
|||
if respond_to?(finder_name, true)
|
||||
send(finder_name, value)
|
||||
else
|
||||
value ? XPath.attr(attribute).equals(value) : nil
|
||||
value ? XPath.attr(attribute) == value : nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -335,7 +335,6 @@ private
|
|||
end
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def insert_modal_handlers(accept, response_text)
|
||||
prompt_response = if accept
|
||||
if response_text.nil?
|
||||
|
@ -389,7 +388,6 @@ private
|
|||
JS
|
||||
execute_script script
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
def within_given_window(handle)
|
||||
original_handle = current_window_handle
|
||||
|
|
|
@ -37,20 +37,17 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|||
# :backspace => send backspace keystrokes to clear the field <br/>
|
||||
# Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]
|
||||
def set(value, **options)
|
||||
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}" if value.is_a?(Array) && !multiple?
|
||||
tag_name = self.tag_name
|
||||
type = self[:type]
|
||||
|
||||
if value.is_a?(Array) && !multiple?
|
||||
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
||||
end
|
||||
|
||||
case tag_name
|
||||
when 'input'
|
||||
case type
|
||||
when 'radio'
|
||||
click
|
||||
when 'checkbox'
|
||||
click if value ^ native.attribute('checked').to_s.eql?("true")
|
||||
click if value ^ checked?
|
||||
when 'file'
|
||||
set_file(value)
|
||||
else
|
||||
|
|
|
@ -511,35 +511,26 @@ module Capybara
|
|||
# @raise [Capybara::ScopeError] if this method is invoked inside `within_frame` method
|
||||
# @return value returned by the block
|
||||
#
|
||||
def within_window(window_or_handle)
|
||||
if window_or_handle.instance_of?(Capybara::Window)
|
||||
original = current_window
|
||||
scopes << nil
|
||||
begin
|
||||
_switch_to_window(window_or_handle) unless original == window_or_handle
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
_switch_to_window(original) unless original == window_or_handle
|
||||
end
|
||||
ensure
|
||||
scopes.pop
|
||||
def within_window(window_or_proc)
|
||||
original = current_window
|
||||
scopes << nil
|
||||
begin
|
||||
case window_or_proc
|
||||
when Capybara::Window
|
||||
_switch_to_window(window_or_proc) unless original == window_or_proc
|
||||
when Proc
|
||||
_switch_to_window { window_or_proc.call }
|
||||
else
|
||||
raise ArgumentError("`#within_window` requires a `Capybara::Window` instance or a lambda")
|
||||
end
|
||||
elsif window_or_handle.is_a?(Proc)
|
||||
original = current_window
|
||||
scopes << nil
|
||||
|
||||
begin
|
||||
_switch_to_window { window_or_handle.call }
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
_switch_to_window(original)
|
||||
end
|
||||
yield
|
||||
ensure
|
||||
scopes.pop
|
||||
_switch_to_window(original) unless original == window_or_proc
|
||||
end
|
||||
else
|
||||
raise ArgumentError("`#within_window` requires a `Capybara::Window` instance or a lambda")
|
||||
ensure
|
||||
scopes.pop
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ Capybara::SpecHelper.spec "#uncheck" do
|
|||
end
|
||||
|
||||
it "should raise original error when no label available" do
|
||||
expect { @session.uncheck('form_cars_ariel') }.to raise_error(Capybara::ElementNotFound, 'Unable to find visible checkbox "form_cars_ariel" that is not disabled')
|
||||
expect { @session.uncheck('form_cars_porsche') }.to raise_error(Capybara::ElementNotFound, 'Unable to find visible checkbox "form_cars_porsche" that is not disabled')
|
||||
end
|
||||
|
||||
it "should raise error if not allowed to click label" do
|
||||
|
|
|
@ -186,6 +186,7 @@ New line after and before textarea tag
|
|||
<input type="checkbox" value="pagani" name="form[cars][]" id="form_cars_pagani" style="position: absolute; left: -9999px"/>
|
||||
<label for="form_cars_pagani">Pagani</label>
|
||||
<input type="checkbox" value="ariel" name="form[cars][]" id="form_cars_ariel" style="display: none"/>
|
||||
<input type="checkbox" value="porsche" name="form[cars][]" id="form_cars_porsche" checked="checked" style="display: none"/>
|
||||
<label>
|
||||
McLaren
|
||||
<input type="checkbox" value="mclaren" name="form[cars][]" id="form_cars_mclaren" style="display: none"/>
|
||||
|
|
Loading…
Reference in New Issue