move xpath gem html definitions into capybara
This commit is contained in:
parent
5695789460
commit
2eda1540b9
|
@ -5,6 +5,7 @@ Release date: unreleased
|
||||||
* Element#visible?/checked?/disabled?/selected? Now return boolean
|
* Element#visible?/checked?/disabled?/selected? Now return boolean
|
||||||
as expected when using the rack_test driver [Thomas Walpole]
|
as expected when using the rack_test driver [Thomas Walpole]
|
||||||
* The rack_test driver now considers \<input type="hidden"> elements as non-visible [Thomas Walpole]
|
* The rack_test driver now considers \<input type="hidden"> elements as non-visible [Thomas Walpole]
|
||||||
|
* A nil locator passed to the built-in html type selectors now behaves consistently, and finds all elements of the correct type [Thomas Walpole]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
* :multiple filter added to relevant selectors [Thomas Walpole]
|
* :multiple filter added to relevant selectors [Thomas Walpole]
|
||||||
|
|
|
@ -37,6 +37,7 @@ Gem::Specification.new do |s|
|
||||||
s.add_development_dependency("cucumber", [">= 0.10.5"])
|
s.add_development_dependency("cucumber", [">= 0.10.5"])
|
||||||
s.add_development_dependency("rake", ["< 11.0"])
|
s.add_development_dependency("rake", ["< 11.0"])
|
||||||
s.add_development_dependency("pry")
|
s.add_development_dependency("pry")
|
||||||
|
s.add_development_dependency("byebug")
|
||||||
|
|
||||||
if RUBY_ENGINE == 'rbx' then
|
if RUBY_ENGINE == 'rbx' then
|
||||||
s.add_development_dependency("racc")
|
s.add_development_dependency("racc")
|
||||||
|
|
|
@ -119,6 +119,17 @@ module Capybara
|
||||||
def describe &block
|
def describe &block
|
||||||
@description = block
|
@description = block
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def locate_field(xpath, locator)
|
||||||
|
locate_field = xpath[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))]
|
||||||
|
locate_field += XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath)
|
||||||
|
locate_field
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -135,7 +146,11 @@ Capybara.add_selector(:id) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.add_selector(:field) do
|
Capybara.add_selector(:field) do
|
||||||
xpath { |locator| XPath::HTML.field(locator) }
|
xpath do |locator|
|
||||||
|
xpath = XPath.descendant(:input, :textarea, :select)[~XPath.attr(:type).one_of('submit', 'image', 'hidden')]
|
||||||
|
xpath = locate_field(xpath, locator.to_s) unless locator.nil?
|
||||||
|
xpath
|
||||||
|
end
|
||||||
filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) }
|
filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) }
|
||||||
filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) }
|
filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) }
|
||||||
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
||||||
|
@ -164,18 +179,26 @@ Capybara.add_selector(:field) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.add_selector(:fieldset) do
|
Capybara.add_selector(:fieldset) do
|
||||||
xpath { |locator| XPath::HTML.fieldset(locator) }
|
xpath do |locator|
|
||||||
|
xpath = XPath.descendant(:fieldset)
|
||||||
|
xpath = xpath[XPath.attr(:id).equals(locator.to_s) | XPath.child(:legend)[XPath.string.n.is(locator.to_s)]] unless locator.nil?
|
||||||
|
xpath
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.add_selector(:link_or_button) do
|
|
||||||
label "link or button"
|
|
||||||
xpath { |locator| XPath::HTML.link_or_button(locator) }
|
|
||||||
filter(:disabled, default: false, boolean: true) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
|
|
||||||
describe { |options| " that is disabled" if options[:disabled] }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.add_selector(:link) do
|
Capybara.add_selector(:link) do
|
||||||
xpath { |locator| XPath::HTML.link(locator) }
|
xpath do |locator|
|
||||||
|
xpath = XPath.descendant(:a)[XPath.attr(:href)]
|
||||||
|
unless locator.nil?
|
||||||
|
locator = locator.to_s
|
||||||
|
xpath = xpath[XPath.attr(:id).equals(locator) |
|
||||||
|
XPath.string.n.is(locator) |
|
||||||
|
XPath.attr(:title).is(locator) |
|
||||||
|
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
|
||||||
|
end
|
||||||
|
xpath
|
||||||
|
end
|
||||||
|
|
||||||
filter(:href) do |node, href|
|
filter(:href) do |node, href|
|
||||||
if href.is_a? Regexp
|
if href.is_a? Regexp
|
||||||
node[:href].match href
|
node[:href].match href
|
||||||
|
@ -183,20 +206,53 @@ Capybara.add_selector(:link) do
|
||||||
node.first(:xpath, XPath.axis(:self)[XPath.attr(:href).equals(href.to_s)], minimum: 0)
|
node.first(:xpath, XPath.axis(:self)[XPath.attr(:href).equals(href.to_s)], minimum: 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe { |options| " with href #{options[:href].inspect}" if options[:href] }
|
describe { |options| " with href #{options[:href].inspect}" if options[:href] }
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.add_selector(:button) do
|
Capybara.add_selector(:button) do
|
||||||
xpath { |locator| XPath::HTML.button(locator) }
|
xpath do |locator|
|
||||||
|
input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
|
||||||
|
btn_xpath = XPath.descendant(:button)
|
||||||
|
image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).equals('image')]
|
||||||
|
|
||||||
|
unless locator.nil?
|
||||||
|
locator = locator.to_s
|
||||||
|
input_btn_xpath = input_btn_xpath[XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)]
|
||||||
|
btn_xpath = btn_xpath[XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.string.n.is(locator) | XPath.attr(:title).is(locator)]
|
||||||
|
image_btn_xpath = image_btn_xpath[XPath.attr(:alt).is(locator)]
|
||||||
|
end
|
||||||
|
|
||||||
|
input_btn_xpath + btn_xpath + image_btn_xpath
|
||||||
|
end
|
||||||
|
|
||||||
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
||||||
|
|
||||||
describe { |options| " that is disabled" if options[:disabled] == true }
|
describe { |options| " that is disabled" if options[:disabled] == true }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Capybara.add_selector(:link_or_button) do
|
||||||
|
label "link or button"
|
||||||
|
xpath do |locator|
|
||||||
|
self.class.all.values_at(:link, :button).map {|selector| selector.xpath.call(locator)}.reduce(:+)
|
||||||
|
end
|
||||||
|
|
||||||
|
filter(:disabled, default: false, boolean: true) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
|
||||||
|
|
||||||
|
describe { |options| " that is disabled" if options[:disabled] }
|
||||||
|
end
|
||||||
|
|
||||||
Capybara.add_selector(:fillable_field) do
|
Capybara.add_selector(:fillable_field) do
|
||||||
label "field"
|
label "field"
|
||||||
xpath { |locator| XPath::HTML.fillable_field(locator) }
|
xpath do |locator|
|
||||||
|
xpath = XPath.descendant(:input, :textarea)[~XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
|
||||||
|
xpath = locate_field(xpath, locator.to_s) unless locator.nil?
|
||||||
|
xpath
|
||||||
|
end
|
||||||
|
|
||||||
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
||||||
filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) }
|
filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) }
|
||||||
|
|
||||||
describe do |options|
|
describe do |options|
|
||||||
desc = String.new
|
desc = String.new
|
||||||
desc << " that is disabled" if options[:disabled] == true
|
desc << " that is disabled" if options[:disabled] == true
|
||||||
|
@ -208,11 +264,17 @@ end
|
||||||
|
|
||||||
Capybara.add_selector(:radio_button) do
|
Capybara.add_selector(:radio_button) do
|
||||||
label "radio button"
|
label "radio button"
|
||||||
xpath { |locator| XPath::HTML.radio_button(locator) }
|
xpath do |locator|
|
||||||
|
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('radio')]
|
||||||
|
xpath = locate_field(xpath, locator.to_s) unless locator.nil?
|
||||||
|
xpath
|
||||||
|
end
|
||||||
|
|
||||||
filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) }
|
filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) }
|
||||||
filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) }
|
filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) }
|
||||||
filter(:option) { |node, value| node.value == value.to_s }
|
filter(:option) { |node, value| node.value == value.to_s }
|
||||||
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
||||||
|
|
||||||
describe do |options|
|
describe do |options|
|
||||||
desc, states = String.new, []
|
desc, states = String.new, []
|
||||||
desc << " with value #{options[:option].inspect}" if options[:option]
|
desc << " with value #{options[:option].inspect}" if options[:option]
|
||||||
|
@ -225,11 +287,17 @@ Capybara.add_selector(:radio_button) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.add_selector(:checkbox) do
|
Capybara.add_selector(:checkbox) do
|
||||||
xpath { |locator| XPath::HTML.checkbox(locator) }
|
xpath do |locator|
|
||||||
|
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('checkbox')]
|
||||||
|
xpath = locate_field(xpath, locator.to_s) unless locator.nil?
|
||||||
|
xpath
|
||||||
|
end
|
||||||
|
|
||||||
filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) }
|
filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) }
|
||||||
filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) }
|
filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) }
|
||||||
filter(:option) { |node, value| node.value == value.to_s }
|
filter(:option) { |node, value| node.value == value.to_s }
|
||||||
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
||||||
|
|
||||||
describe do |options|
|
describe do |options|
|
||||||
desc, states = String.new, []
|
desc, states = String.new, []
|
||||||
desc << " with value #{options[:option].inspect}" if options[:option]
|
desc << " with value #{options[:option].inspect}" if options[:option]
|
||||||
|
@ -243,7 +311,12 @@ end
|
||||||
|
|
||||||
Capybara.add_selector(:select) do
|
Capybara.add_selector(:select) do
|
||||||
label "select box"
|
label "select box"
|
||||||
xpath { |locator| XPath::HTML.select(locator) }
|
xpath do |locator|
|
||||||
|
xpath = XPath.descendant(:select)
|
||||||
|
xpath = locate_field(xpath, locator.to_s) unless locator.nil?
|
||||||
|
xpath
|
||||||
|
end
|
||||||
|
|
||||||
filter(:options) do |node, options|
|
filter(:options) do |node, options|
|
||||||
if node.visible?
|
if node.visible?
|
||||||
actual = node.all(:xpath, './/option').map { |option| option.text }
|
actual = node.all(:xpath, './/option').map { |option| option.text }
|
||||||
|
@ -265,6 +338,7 @@ Capybara.add_selector(:select) do
|
||||||
end
|
end
|
||||||
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
||||||
filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) }
|
filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) }
|
||||||
|
|
||||||
describe do |options|
|
describe do |options|
|
||||||
desc = String.new
|
desc = String.new
|
||||||
desc << " with options #{options[:options].inspect}" if options[:options]
|
desc << " with options #{options[:options].inspect}" if options[:options]
|
||||||
|
@ -278,14 +352,24 @@ Capybara.add_selector(:select) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.add_selector(:option) do
|
Capybara.add_selector(:option) do
|
||||||
xpath { |locator| XPath::HTML.option(locator) }
|
xpath do |locator|
|
||||||
|
xpath = XPath.descendant(:option)
|
||||||
|
xpath = xpath[XPath.string.n.is(locator.to_s)] unless locator.nil?
|
||||||
|
xpath
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.add_selector(:file_field) do
|
Capybara.add_selector(:file_field) do
|
||||||
label "file field"
|
label "file field"
|
||||||
xpath { |locator| XPath::HTML.file_field(locator) }
|
xpath do |locator|
|
||||||
|
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('file')]
|
||||||
|
xpath = locate_field(xpath, locator.to_s) unless locator.nil?
|
||||||
|
xpath
|
||||||
|
end
|
||||||
|
|
||||||
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
||||||
filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) }
|
filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) }
|
||||||
|
|
||||||
describe do |options|
|
describe do |options|
|
||||||
desc = String.new
|
desc = String.new
|
||||||
desc << " that is disabled" if options[:disabled] == true
|
desc << " that is disabled" if options[:disabled] == true
|
||||||
|
@ -296,5 +380,9 @@ Capybara.add_selector(:file_field) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.add_selector(:table) do
|
Capybara.add_selector(:table) do
|
||||||
xpath { |locator| XPath::HTML.table(locator) }
|
xpath do |locator|
|
||||||
|
xpath = XPath.descendant(:table)
|
||||||
|
xpath = xpath[XPath.attr(:id).equals(locator.to_s) | XPath.descendant(:caption).is(locator.to_s)] unless locator.nil?
|
||||||
|
xpath
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,6 +18,18 @@ RSpec.describe Capybara do
|
||||||
<p class="b">Some Content</p>
|
<p class="b">Some Content</p>
|
||||||
<p class="b"></p>
|
<p class="b"></p>
|
||||||
</div>
|
</div>
|
||||||
|
<input type="checkbox"/>
|
||||||
|
<input type="radio"/>
|
||||||
|
<input type="text"/>
|
||||||
|
<input type="file"/>
|
||||||
|
<a href="#">link</a>
|
||||||
|
<fieldset></fieldset>
|
||||||
|
<select>
|
||||||
|
<option value="a">A</option>
|
||||||
|
</select>
|
||||||
|
<table>
|
||||||
|
<tr><td></td></tr>
|
||||||
|
</table
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
STRING
|
STRING
|
||||||
|
@ -50,5 +62,28 @@ RSpec.describe Capybara do
|
||||||
expect(string).to have_selector(:custom_selector, 'b', not_empty: :all, count: 2)
|
expect(string).to have_selector(:custom_selector, 'b', not_empty: :all, count: 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "builtin selectors with nil locators" do
|
||||||
|
it "devolves to just finding element types" do
|
||||||
|
selectors = {
|
||||||
|
field: ".//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]",
|
||||||
|
fieldset: ".//fieldset",
|
||||||
|
link: ".//a[./@href]",
|
||||||
|
link_or_button: ".//a[./@href] | .//input[./@type = 'submit' or ./@type = 'reset' or ./@type = 'image' or ./@type = 'button'] | .//button" ,
|
||||||
|
fillable_field: ".//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')]",
|
||||||
|
radio_button: ".//input[./@type = 'radio']",
|
||||||
|
checkbox: ".//input[./@type = 'checkbox']",
|
||||||
|
select: ".//select",
|
||||||
|
option: ".//option",
|
||||||
|
file_field: ".//input[./@type = 'file']",
|
||||||
|
table: ".//table"
|
||||||
|
}
|
||||||
|
selectors.each do |selector, xpath|
|
||||||
|
results = string.all(selector,nil).to_a.map &:native
|
||||||
|
expect(results.size).to be > 0
|
||||||
|
expect(results).to eq string.all(:xpath, xpath).to_a.map(&:native)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue