I'm using a cucumber/ruby/capybara/siteprism framework and I'm having problems identifying elements as either we're missing the ids, names, etc or they create them with a in real time.
I was mainly trying to define some of those elements in a siteprism page object model. For example, I was trying to enter some data in the 'input' field for 'First Name' below:
<div class="control-group">
<label class="control-label" for="input_field_dec_<random_number>">
First Name
<span class="required"></span>
</label>
<div class="controls">
<input id="input_field_dec_<random_number>" class=" span5" type="text" value="" scripttofire="SetUserFirstName('input_field_dec_<random_number>')" required="required" name="input_field_dec_<random_number>" data-val-required="First Name is required" data-val-regex-pattern="^[a-zA-Z0-9_ \-\']*$" data-val-regex="Only alphabetic and numeric characters allowed" data-val="true">
<span class="field-validation-valid help-inline" data-valmsg-for="input_field_dec_<random_number>" data-valmsg-replace="true"></span>
</div>
</div>
Is there a way to pass the label text (eg: 'First Name' - ignoring the spaces around, something like - contains='First Name') and then find the input element inside to set it up?
I was thinking something along the lines of:
element :first_name_field, :xpath, "//label[contains(text()='Continue'])/<and here something to find the input field?>" but cannot figure it out...
Capybara provides a bunch of built-in "selectors" that can be used for this, and you can add your own if you find it necessary. You can see the provided selectors by either building the Capybara docs yourself (rubydocs doesn't run the custom yard code used to generate that part of the docs) or by browsing the file where they are implemented - https://github.com/teamcapybara/capybara/blob/master/lib/capybara/selector.rb#L47
For your original example you can use the :field selector
element :first_name_field, :field, 'First Name'
which will match on the inputs associated label text. For you second example (from the comments) where the input and label have no connection (wrapped or for
attribute) you should be able to do something like
element :some_field, :xpath, ".//label[contains(normalize-space(string(.)), 'label text')]/following-sibling::*[1]/self::input"
If you wanted to make that reusable you could add your own "selector" like
Capybara.add_selector(:sibling_input) do
label "Label adjacent sibling input"
xpath do |locator|
XPath.descendant(:label)[XPath.string.n.is(locator)].next_sibling(:input)
end
end
which could then be used as
element :some_field, :sibling_input, 'label text'