2016-03-07 16:52:19 -08:00
# frozen_string_literal: true
2018-02-28 16:11:41 -08:00
2016-01-06 17:11:00 -08:00
require 'spec_helper'
RSpec . describe Capybara do
describe 'Selectors' do
let :string do
Capybara . string <<-STRING
< html >
< head >
< title > selectors < / title>
< / head>
< body >
< div class = " a " id = " page " >
< div class = " b " id = " content " >
< h1 class = " a " > Totally awesome < / h1>
< p > Yes it is < / p>
< / div>
2018-05-29 16:46:04 -07:00
< p class = " b c " > Some Content < / p>
< p class = " b d " > < / p>
2016-01-06 17:11:00 -08:00
< / div>
2016-09-22 16:55:54 -07:00
< div id = " # special " >
< / div>
2018-05-26 10:26:44 -07:00
< div class = " some random words " id = " random_words " >
Something
< / div>
2016-09-22 16:55:54 -07:00
< input id = " 2checkbox " class = " 2checkbox " type = " checkbox " / >
2016-03-24 11:18:12 -07:00
< input type = " radio " / >
2016-09-22 16:55:54 -07:00
< label for = " my_text_input " > My Text Input < / label>
< input type = " text " name = " form[my_text_input] " placeholder = " my text " id = " my_text_input " / >
< input type = " file " id = " file " class = " .special file " / >
2016-10-02 18:28:52 -07:00
< input type = " hidden " id = " hidden_field " value = " this is hidden " / >
2018-04-11 09:23:59 -07:00
< input type = " submit " value = " click me " title = " submit button " / >
2016-03-24 11:18:12 -07:00
< a href = " # " > link < / a>
< fieldset > < / fieldset>
2016-10-05 15:16:00 -07:00
< select id = " select " >
2016-03-24 11:18:12 -07:00
< option value = " a " > A < / option>
2016-03-24 14:50:42 -07:00
< option value = " b " disabled > B < / option>
< option value = " c " selected > C < / option>
2016-03-24 11:18:12 -07:00
< / select>
< table >
< tr > < td > < / td>< / tr >
2018-05-26 10:26:44 -07:00
< / table>
2016-01-06 17:11:00 -08:00
< / body>
< / html>
STRING
end
before do
Capybara . add_selector :custom_selector do
css { | css_class | " div. #{ css_class } " }
2018-05-29 14:02:03 -07:00
node_filter ( :not_empty , boolean : true , default : true , skip_if : :all ) { | node , value | value ^ ( node . text == '' ) }
2016-01-06 17:11:00 -08:00
end
2016-09-22 16:55:54 -07:00
Capybara . add_selector :custom_css_selector do
css { | selector | selector }
end
2018-05-26 10:26:44 -07:00
Capybara . add_selector :custom_xpath_selector do
xpath { | selector | selector }
end
2016-01-06 17:11:00 -08:00
end
2018-07-10 14:18:39 -07:00
it 'supports `filter` as an alias for `node_filter`' do
2018-05-29 14:02:03 -07:00
expect do
Capybara . add_selector :filter_alias_selector do
2018-07-10 14:18:39 -07:00
css { | _unused | 'div' }
2018-05-29 14:02:03 -07:00
filter ( :something ) { | _node , _value | true }
end
end . not_to raise_error
end
2018-07-10 14:18:39 -07:00
describe 'adding a selector' do
it 'can set default visiblity' do
2016-10-02 18:28:52 -07:00
Capybara . add_selector :hidden_field do
visible :hidden
2018-02-28 16:11:41 -08:00
css { | _sel | 'input[type="hidden"]' }
2016-10-02 18:28:52 -07:00
end
expect ( string ) . to have_no_css ( 'input[type="hidden"]' )
expect ( string ) . to have_selector ( :hidden_field )
end
end
2018-07-10 14:18:39 -07:00
describe 'modify_selector' do
it 'allows modifying a selector' do
2016-01-06 17:11:00 -08:00
el = string . find ( :custom_selector , 'a' )
expect ( el . tag_name ) . to eq 'div'
Capybara . modify_selector :custom_selector do
css { | css_class | " h1. #{ css_class } " }
end
el = string . find ( :custom_selector , 'a' )
expect ( el . tag_name ) . to eq 'h1'
end
it " doesn't change existing filters " do
Capybara . modify_selector :custom_selector do
2018-02-28 16:11:41 -08:00
css { | css_class | " p. #{ css_class } " }
2016-01-06 17:11:00 -08:00
end
expect ( string ) . to have_selector ( :custom_selector , 'b' , count : 1 )
expect ( string ) . to have_selector ( :custom_selector , 'b' , not_empty : false , count : 1 )
expect ( string ) . to have_selector ( :custom_selector , 'b' , not_empty : :all , count : 2 )
end
end
2016-03-24 11:18:12 -07:00
2018-07-10 14:18:39 -07:00
describe 'builtin selectors' do
context 'when locator is nil' do
it 'devolves to just finding element types' do
2016-03-24 14:50:42 -07:00
selectors = {
field : " .//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')] " ,
2018-07-10 14:18:39 -07:00
fieldset : './/fieldset' ,
link : './/a[./@href]' ,
2018-02-28 16:11:41 -08:00
link_or_button : " .//a[./@href] | .//input[./@type = 'submit' or ./@type = 'reset' or ./@type = 'image' or ./@type = 'button'] | .//button " ,
2016-03-24 14:50:42 -07:00
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'] " ,
2018-07-10 14:18:39 -07:00
select : './/select' ,
option : './/option' ,
2016-03-24 14:50:42 -07:00
file_field : " .//input[./@type = 'file'] " ,
2018-07-10 14:18:39 -07:00
table : './/table'
2016-03-24 14:50:42 -07:00
}
selectors . each do | selector , xpath |
2018-02-28 16:11:41 -08:00
results = string . all ( selector , nil ) . to_a . map ( & :native )
2016-03-24 14:50:42 -07:00
expect ( results . size ) . to be > 0
expect ( results ) . to eq string . all ( :xpath , xpath ) . to_a . map ( & :native )
end
end
end
2018-07-10 14:18:39 -07:00
context 'with :id option' do
it 'works with compound css selectors' do
expect ( string . all ( :custom_css_selector , 'div, h1' , id : 'page' ) . size ) . to eq 1
expect ( string . all ( :custom_css_selector , 'h1, div' , id : 'page' ) . size ) . to eq 1
2016-09-22 16:55:54 -07:00
end
it " works with 'special' characters " do
2018-07-10 14:18:39 -07:00
expect ( string . find ( :custom_css_selector , 'div' , id : '#special' ) [ :id ] ) . to eq '#special'
expect ( string . find ( :custom_css_selector , 'input' , id : '2checkbox' ) [ :id ] ) . to eq '2checkbox'
2016-09-22 16:55:54 -07:00
end
2018-05-26 10:26:44 -07:00
2018-07-10 14:18:39 -07:00
it 'accepts XPath expression for xpath based selectors' do
2018-05-26 10:26:44 -07:00
expect ( string . find ( :custom_xpath_selector , './/div' , id : XPath . contains ( 'peci' ) ) [ :id ] ) . to eq '#special'
expect ( string . find ( :custom_xpath_selector , './/input' , id : XPath . ends_with ( 'box' ) ) [ :id ] ) . to eq '2checkbox'
end
2018-07-10 14:18:39 -07:00
it 'errors XPath expression for CSS based selectors' do
expect { string . find ( :custom_css_selector , 'div' , id : XPath . contains ( 'peci' ) ) }
2018-05-26 10:26:44 -07:00
. to raise_error ( ArgumentError , / not supported / )
end
2016-09-22 16:55:54 -07:00
end
2018-07-10 14:18:39 -07:00
context 'with :class option' do
it 'works with compound css selectors' do
expect ( string . all ( :custom_css_selector , 'div, h1' , class : 'a' ) . size ) . to eq 2
expect ( string . all ( :custom_css_selector , 'h1, div' , class : 'a' ) . size ) . to eq 2
2016-09-22 16:55:54 -07:00
end
2018-07-10 14:18:39 -07:00
it 'handles negated classes' do
expect ( string . all ( :custom_css_selector , 'div, p' , class : [ 'b' , '!c' ] ) . size ) . to eq 2
expect ( string . all ( :custom_css_selector , 'div, p' , class : [ '!c' , '!d' , 'b' ] ) . size ) . to eq 1
2018-05-29 16:46:04 -07:00
expect ( string . all ( :custom_xpath_selector , XPath . descendant ( :div , :p ) , class : [ 'b' , '!c' ] ) . size ) . to eq 2
expect ( string . all ( :custom_xpath_selector , XPath . descendant ( :div , :p ) , class : [ '!c' , '!d' , 'b' ] ) . size ) . to eq 1
end
2016-09-22 16:55:54 -07:00
it " works with 'special' characters " do
2018-07-10 14:18:39 -07:00
expect ( string . find ( :custom_css_selector , 'input' , class : '.special' ) [ :id ] ) . to eq 'file'
expect ( string . find ( :custom_css_selector , 'input' , class : '2checkbox' ) [ :id ] ) . to eq '2checkbox'
2016-09-22 16:55:54 -07:00
end
2018-05-26 10:26:44 -07:00
2018-07-10 14:18:39 -07:00
it 'accepts XPath expression for xpath based selectors' do
2018-05-26 10:26:44 -07:00
expect ( string . find ( :custom_xpath_selector , './/div' , class : XPath . contains ( 'dom wor' ) ) [ :id ] ) . to eq 'random_words'
expect ( string . find ( :custom_xpath_selector , './/div' , class : XPath . ends_with ( 'words' ) ) [ :id ] ) . to eq 'random_words'
end
2018-07-10 14:18:39 -07:00
it 'errors XPath expression for CSS based selectors' do
expect { string . find ( :custom_css_selector , 'div' , class : XPath . contains ( 'random' ) ) }
2018-05-26 10:26:44 -07:00
. to raise_error ( ArgumentError , / not supported / )
end
2016-09-22 16:55:54 -07:00
end
# :css, :xpath, :id, :field, :fieldset, :link, :button, :link_or_button, :fillable_field, :radio_button, :checkbox, :select,
# :option, :file_field, :label, :table, :frame
2018-07-10 14:18:39 -07:00
describe ':css selector' do
it 'finds by CSS locator' do
expect ( string . find ( :css , 'input#my_text_input' ) [ :name ] ) . to eq 'form[my_text_input]'
2016-09-22 16:55:54 -07:00
end
end
2018-07-10 14:18:39 -07:00
describe ':xpath selector' do
it 'finds by XPath locator' do
2016-09-22 16:55:54 -07:00
expect ( string . find ( :xpath , './/input[@id="my_text_input"]' ) [ :name ] ) . to eq 'form[my_text_input]'
end
end
2018-07-10 14:18:39 -07:00
describe ':id selector' do
it 'finds by locator' do
expect ( string . find ( :id , 'my_text_input' ) [ :name ] ) . to eq 'form[my_text_input]'
2016-09-22 16:55:54 -07:00
end
end
2018-07-10 14:18:39 -07:00
describe ':field selector' do
it 'finds by locator' do
2016-09-22 16:55:54 -07:00
expect ( string . find ( :field , 'My Text Input' ) [ :id ] ) . to eq 'my_text_input'
expect ( string . find ( :field , 'my_text_input' ) [ :id ] ) . to eq 'my_text_input'
expect ( string . find ( :field , 'form[my_text_input]' ) [ :id ] ) . to eq 'my_text_input'
end
2018-07-10 14:18:39 -07:00
it 'finds by id' do
2016-09-22 16:55:54 -07:00
expect ( string . find ( :field , id : 'my_text_input' ) [ :name ] ) . to eq 'form[my_text_input]'
end
2018-07-10 14:18:39 -07:00
it 'finds by name' do
2016-09-22 16:55:54 -07:00
expect ( string . find ( :field , name : 'form[my_text_input]' ) [ :id ] ) . to eq 'my_text_input'
end
2018-07-10 14:18:39 -07:00
it 'finds by placeholder' do
2016-09-22 16:55:54 -07:00
expect ( string . find ( :field , placeholder : 'my text' ) [ :id ] ) . to eq 'my_text_input'
end
2018-07-10 14:18:39 -07:00
it 'finds by type' do
2016-09-22 16:55:54 -07:00
expect ( string . find ( :field , type : 'file' ) [ :id ] ) . to eq 'file'
2016-10-05 15:16:00 -07:00
expect ( string . find ( :field , type : 'select' ) [ :id ] ) . to eq 'select'
2016-09-22 16:55:54 -07:00
end
end
2018-07-10 14:18:39 -07:00
describe ':option selector' do
it 'finds disabled options' do
2016-03-24 14:50:42 -07:00
expect ( string . find ( :option , disabled : true ) . value ) . to eq 'b'
end
2018-07-10 14:18:39 -07:00
it 'finds selected options' do
2016-03-24 14:50:42 -07:00
expect ( string . find ( :option , selected : true ) . value ) . to eq 'c'
end
2018-07-10 14:18:39 -07:00
it 'finds not selected and not disabled options' do
2016-03-24 14:50:42 -07:00
expect ( string . find ( :option , disabled : false , selected : false ) . value ) . to eq 'a'
2016-03-24 11:18:12 -07:00
end
end
2018-04-11 09:23:59 -07:00
2018-07-10 14:18:39 -07:00
describe ':button selector' do
it 'finds by value' do
2018-04-11 09:23:59 -07:00
expect ( string . find ( :button , 'click me' ) . value ) . to eq 'click me'
end
2018-07-10 14:18:39 -07:00
it 'finds by title' do
2018-04-11 09:23:59 -07:00
expect ( string . find ( :button , 'submit button' ) . value ) . to eq 'click me'
end
2018-07-10 14:18:39 -07:00
it 'includes non-matching parameters in failure message' do
2018-04-11 09:23:59 -07:00
expect { string . find ( :button , 'click me' , title : 'click me' ) } . to raise_error ( / with title click me / )
end
end
2018-05-24 15:27:34 -07:00
2018-07-10 14:18:39 -07:00
describe ':element selector' do
it 'finds by any attributes' do
2018-05-24 15:27:34 -07:00
expect ( string . find ( :element , 'input' , type : 'submit' ) . value ) . to eq 'click me'
end
2018-07-10 14:18:39 -07:00
it 'still works with system keys' do
2018-05-24 15:27:34 -07:00
expect { string . all ( :element , 'input' , type : 'submit' , count : 1 ) } . not_to raise_error
end
2018-08-15 10:11:32 -07:00
it 'works without element type' do
expect ( string . find ( :element , type : 'submit' ) . value ) . to eq 'click me'
end
2018-08-15 10:44:34 -07:00
it 'validates attribute presence when true' do
expect ( string . find ( :element , name : true ) [ :id ] ) . to eq 'my_text_input'
end
it 'validates attribute absence when false' do
expect ( string . find ( :element , 'option' , disabled : false , selected : false ) . value ) . to eq 'a'
end
2018-07-10 14:18:39 -07:00
it 'includes wildcarded keys in description' do
2018-08-15 10:44:34 -07:00
expect { string . find ( :element , 'input' , not_there : 'bad' , presence : true , absence : false , count : 1 ) }
2018-05-24 15:27:34 -07:00
. to ( raise_error do | e |
expect ( e ) . to be_a ( Capybara :: ElementNotFound )
2018-07-10 14:18:39 -07:00
expect ( e . message ) . to include 'not_there => bad'
2018-08-15 10:44:34 -07:00
expect ( e . message ) . to include 'with presence attribute'
expect ( e . message ) . to include 'without absence attribute'
2018-07-10 14:18:39 -07:00
expect ( e . message ) . not_to include 'count 1'
2018-05-24 15:27:34 -07:00
end )
end
2018-07-10 14:18:39 -07:00
it 'accepts XPath::Expression' do
2018-05-24 15:27:34 -07:00
expect ( string . find ( :element , 'input' , type : XPath . starts_with ( 'subm' ) ) . value ) . to eq 'click me'
expect ( string . find ( :element , 'input' , type : XPath . ends_with ( 'ext' ) ) [ :type ] ) . to eq 'text'
expect ( string . find ( :element , 'input' , type : XPath . contains ( 'ckb' ) ) [ :type ] ) . to eq 'checkbox'
expect ( string . find ( :element , 'input' , title : XPath . contains_word ( 'submit' ) ) [ :type ] ) . to eq 'submit'
expect ( string . find ( :element , 'input' , title : XPath . contains_word ( 'button' ) ) [ :type ] ) . to eq 'submit'
end
end
2016-03-24 11:18:12 -07:00
end
2016-01-06 17:11:00 -08:00
end
end