Select2 feature test with Capybara - ruby

I'm trying to write a rspec feature test with capybara but,
I have some trouble with a test on a select2 element.
see my test code.
Using feature test with capybara
feature "Backend Landing Pages" do
let!(:landing_page) { create(:landing_page, country: country) }
let!(:country) { create(:country, id: 2) }
let!(:restaurant) { create(:restaurant_with_locale) }
let!(:landing_page_restaurant) { create(:landing_page_restaurant,landing_page: landing_page, restaurant: restaurant) }
before(:each) do
login
click_on("Website")
click_on("Landing Pages")
end
scenario "user creates a landingpage", js: true do
first(:link,"netherlands").click
fill_in "landing_page_domain", with: landing_page.domain
fill_in "landing_page_slug_nl", with: landing_page.slug_nl
fill_in "landing_page_slug_en", with: landing_page.slug_en
fill_in "landing_page_header_title", with: landing_page.header_title
fill_in "landing_page_title", with: landing_page.title
attach_file "landing_page_header_image",( Rails.root + 'app/assets/images/site_builder/image3.jpg')
page.find('#landing_page_restaurant_select').set('Le Garage - Amsterdam')
page.save_screenshot ("test.png")
click_on("Save")
expect(page).to have_content("successfully created")
expect(LandingPage.count).to eq 2
expect(LandingPage.last.landing_page_restaurants.count).to eq 1
end
scenario "user edits a LandingPage", js: true do
click_on("Edit")
expect(page).to have_content 'Edit landing Page '
page.save_screenshot ("edit.png")
end
end
I'm getting the next error.
Failures:
1) Backend Landing Pages user creates a landingpage
Failure/Error: expect(LandingPage.last.landing_page_restaurants.count).to eq 1
expected: 1
got: 0
(compared using ==)
# ./spec/features/backend/landing_pages_spec.rb:33:in `block (2 levels) in <top (required)>'
Who can see what im doing wrong here
cant figure out why the restaurant is not connected to the landingPages
thanks in advance

In the question you mention you are using a select2, but then you are calling set on what I assume is a regular select element. When you are using JS widgets they will often overwrite the values of the hidden elements they build off on a submit event of the form they're in. Because of this it is probable the value you're setting is getting overridden. Rather than set the values of hidden elements (which most capybara drivers don't allow for this specific reason - not sure which driver you're using) you need to replicate the users behaviors. From the select2 examples page it appears the select2 widget is built from a span element and a ul list with the options in it. Therefore something along the lines of
find('span.select2').click # the selector may need to be more specific to locate the exact span, without your html I don't know what that selector would be
find('li', text: 'Le Garage - Amsterdam').click
This would click on the select2 element opening the dropdown, and then click on the li element with the correct text - thereby selecting it.

Related

Unable to click download button with web scraper

for whatever reason mechanize is unable to click this export button to automate downloading a public government csv file, i'm trying to automate downloading a fishing report, does anyone have any ideas on how to get it to work?
agent = Mechanize.new
url = 'https://nrm.dfg.ca.gov/FishPlants/'
log :debug, "reading HTML from #{url}"
page = agent.get(url)
log :debug, 'loaded page'
form = page.search('#aspnetForm').first
button = page.search('.application_button').first
log :debug, 'clicking Export button'
response = agent.submit(form, button)
i get a stack trace with the following error
...
form.add_button_to_query(button) if button
^^^^^^^^^^^^^^^^^^^^
/Users/aronlilland/.rvm/gems/ruby-3.1.2/gems/mechanize-2.8.5/lib/mechanize.rb:581:in `submit'
/Users/aronlilland/Documents/dev/fishing-report/tasks/download/fishing_report.rake:27:in `block (2 levels) in <top (required)>'
Tasks: TOP => download:fishing_report
(See full trace by running task with --trace)
the form is confirmed returns successfully, but the button is an input field
the page seems relatively straight forward, so dont know why i'm unable to scrape it - and its also public data
<form method="post" action="./" id="aspnetForm">
<!-- .... -->
<input
type="submit"
name="ctl00$cphContentMiddle$btnExport"
value="Export"
id="ctl00_cphContentMiddle_btnExport"
class="application_button"
>
<!-- .... -->
</form>
agent.submit got the wrong type for form
The issue here could be seen if you had included the beginning of the stack trace:
.../ruby/gems/3.1.0/gems/mechanize-2.8.5/lib/mechanize.rb:581:in `submit': undefined method `add_button_to_query' for #<Nokogiri::XML::Element:0x1ea14 name="form" attributes=
[#<Nokogiri::XML::Attr:0xbd38 name="method" value="post">, ...
Mechanize expects the submit to act on a Mechanize::Form instance, but instead got an instance of Nokogiri::XML::Element, as you can see by adding this to your code:
form.class # => Nokogiri::XML::Element
If you check the docs for the Mechanize::Form class, you can see the example they give to get you the form object is this:
form = page.forms.first # => Mechanize::Form
as opposed to what you used:
form = page.search('#aspnetForm').first
The call to search here is delegated to Nokogiri, and therefore doesn't return the object type you need, but rather a Nokogiri element.
button also has the wrong type
By the way the same applies to this line:
button = page.search('.application_button').first
If you fix the type of form, you'll get into a similar issue with button not being the expected type. Again, there's an example in the docs showing how a button is found:
agent.submit(page.forms.first, page.forms.first.buttons.first)
You'll need to figure out how to find the specific button you need though, I haven't worked with Mechanize before, so I can't offer a suggestion here. Presumably there's a way to convert the button you find through search to a Mechanize::Form::Button instance.

Selenium Webdriver (Ruby) : Cannot click on button - element not found

This is the page code:
<div class="modal-buttons">
<button class="button-orange" ng-click="cancel()">
<span>
Cancel
</span>
<span class="icon cancel"></span>
</button>
<button class="button-orange" ng-click="apply()">
<span>
Apply
</span>
<span class="icon run"></span>
</button>
As you can see - this modal has two buttons and I have tried a dozen different ways - but I just want to click on the button labeled "apply" No matter which route I take - it just keeps saying element not visible.
Here are some of the things I've tried:
# #driver.find_element(:class, "button-orange")[2].click
# #driver.find_element(:xpath, "//div[4]/div/div[2]/div[3]/button[2]").click
# #driver. find_element(:link_text, 'Apply').click
# #driver. find_element(:tag, 'Apply').click
# #driver.find_element(:css, "input[value='Apply']").click();
# #driver.find_element(:css, "input[value='Apply']").click();
# #driver.find_element(:xpath, "//button[contains(text(),'Apply')]").click
# #driver.find_element(:xpath, "//button[contains(text(),'apply')]").click
# #driver.find_element(:xpath, "//input[#value='Apply']").click();
# #driver.find_element(:class, "button-orange.icon-run").click
# #driver.find_element(:css,'a[class$="button-orange"]').click
# #driver.find_element(:xpath, "").clear
The exact error I get is:
Error: test_login_to_chute(LoginToChute)
Selenium::WebDriver::Error::ElementNotVisibleError: element not visible
(Session info: chrome=36.0.1985.125)
(Driver info: chromedriver=2.10.267521,platform=Windows NT 6.3 x86_64)
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/response.rb:51:in `assert_ok'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/response.rb:15:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/http/common.rb:59:in `new'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/http/common.rb:59:in `create_response'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/http/default.rb:66:in `request'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/http/common.rb:40:in `call'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/bridge.rb:634:in `raw_execute'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/bridge.rb:612:in `execute'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/bridge.rb:369:in `clickElement'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/common/element.rb:54:in `click'
C:/Analytics/AutomatedTesting/DevEnv/Wonderland/Full Regression/2.login_to_chute
.rb:165:in `test_login_to_chute'
162: #driver.find_element(:xpath, "(//input[#type='text'])[8]").clear
163: #driver.find_element(:xpath, "(//input[#type='text'])[8]").send_
keys "25"
164:
=> 165: #driver.find_element(:xpath, "//div[4]/div/div[2]/div[3]/button[2]").c
lick
Thank you all so much for helping.
Here's what ended up being the deal.
Because the website is all one angular page, it's loading a bunch of things in the background. Including multiple other buttons that aren't actually visible.
So here's what I ended up using:
#driver.find_elements(:xpath, "//button")[-1].click
And I updated my find_element variable to only find visible elements.
def find_visible_element(how, what)
elems = #driver.find_elements(how, what).select { |e| e.displayed? }
len = elems.length
if len == 0
raise "No matches found."
elsif len > 1
raise "Ambiguous match. Found #{len} matches."
end
elems.first
end
There are several reasons behind this error. I was getting the same error and here is how I narrowed down the possible causes.
The Item can be located in the iframe, in this case you have to switch to the iFrame and locate the element
Another reason can be the element is not yet displayed and you are performing operations on it. In this case you can apply explicit wait as below.
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until{browser_driver.find_element(:xpath,'***').displayed?}
Using this chunk in your code can help you to wait for maximum 10 seconds till the item is displayed
It is possible that, your xpath is returning more than 1 web element to the driver i.e. more than 1 element can be located with the same xpaths. In this case make sure that your xpath is more specific and returning only one web element. My issue was solved with 3rd possibility.
Wait unti the javascript modal is fully loaded (using a Wait block, for example)
Also, you have to use the correct selector. Frankly, quite of few of your example selectors don't make any sense in the context of the html you posted.
In Chrome's inspector, make sure what it is that a mouseclick would click on. Have you tried selecting the <span>?
Try some of these:
:css => "button.button-orange[ng-click='apply()']"
:xpath => "//button[#ng-click='apply()']"
:xpath => "//button/span[contains(text(),'Apply')]/.."
:css => "button.button-orange[ng-click='apply()'] > span"
:xpath => "//button[#ng-click='apply()']/span"
:xpath => "//button/span[contains(text(),'Apply')]"
By the way, chrome's element inspector has a find function that can search xpath as well as css too for selector testing.

error when clicking a link using ruby/watir

I am new to ruby/watir and am getting an error when trying to click on a link.
The error is:
C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-classic-4.0.1/lib/watir-classic/elemen
t.rb:328:in `assert_exists': Unable to locate element, using {:tag_name=>["a"],
:id=>"My Link"} (Watir::Exception::UnknownObjectException)
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-classic-4.0.1/lib/watir-c
lassic/element.rb:474:in `perform_action'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-classic-4.0.1/lib/watir-c
lassic/element.rb:354:in `click!'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-classic-4.0.1/lib/watir-c
lassic/element.rb:157:in `click'
from Login.rb:22:in `<main>'
The link that I am trying to click looks like this:
<TD><DIV id=div style="DISPLAY: inline"><A id=Hyperlink href="javascript:RunFullScreen('myURL')">My Link</A></DIV>
Also, I tried to write out all links on the page using 'puts', but nothing was written out when the script finished
I used this code to write out the links (not sure if this is correct or not):
browser.links.each {|link| puts link.attribute_value("text") if link.visible?}
The error says tat you tried to find an object with the following properties: {:tag_name=>["a"], :id=>"My Link"}, but you have show us HTML code with A id=Hyperlink. So to find out that HTML you need to specify properly its attributes:
#b.element :tag_name => 'a', :id => 'Hyperlink'
or
#b.a :id => 'Hyperlink'

SyntaxError on pageobject while accessing a select_list within fieldset

I'm trying to access a select_list within a fieldset though Cheezy's pageobject.
The total html is far too long to post (well over 200 lines for just the fieldset), but I can supply the lines with all of the id's and such.
fieldset:
<fieldset class="dartPayer-Insurance" style="width: 730px;">
select_list:
<select id="dartPayer-Payer" style="width: 235px;">
Line in the pageobject I am attempting to use:
select_list(:payer_insurance){ element(:class => "dartPayer-Insurance").select_list_element(:id => "dartPayer-PayerList") }
The error I am getting when I try to run my cucumber test:
(eval):1: syntax error, unexpected '(', expecting $end
{:id=>"dartPayer-Insurance"}(identifier)
^ (SyntaxError)
This error occurs when I try to set the select_list with this line:
self.send(field, input) (Where field is "payer_insurance=" and input is "UMA")
This line works for other pages, so I am fairly certain this is not part of the problem. I'm sure it's a simple bit of syntax in the pageobject line, but I can't find any documentation for using the pageobject quite like I'm trying to. The only reference I can find is within a previous question I asked: Accessing a table within a table (Watir/PageObject)
Could anyone please tell me what I have done wrong?
Thank you in advance for your help.
Update: An example that reproduces the problem:
Given a page with the html:
<fieldset class="dartPayer-Insurance" style="width: 730px;">
<select id="dartPayer-Payer" style="width: 235px;">
<option value="UMA">UMA</option>
</select>
</fieldset>
And a page object defined as:
class MyPage
include PageObject
select_list(:payer_insurance){ element(:class => "dartPayer-Insurance").select_list_element(:id => "dartPayer-PayerList") }
def input_payer(field, input)
self.send(field, input)
end
end
Running the following code:
browser = Watir::Browser.new
browser.goto('C:\Scripts\Misc\Programming\PageObject\test.htm')
page = MyPage.new(browser)
field = "payer_insurance="
input = "UMA"
page.input_payer(field, input)
Generates the following exception:
C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object/platforms/watir_webdriver/page_object.rb:968:in `instance_eval': (eval):1: syntax error, unexpected '(', expecting $end (SyntaxError)
{:class=>"dartPayer-Insurance"}(identifier)
^
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object/platforms/watir_webdriver/page_object.rb:968:in `find_watir_element'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object/platforms/watir_webdriver/page_object.rb:907:in `element_for'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object/element_locators.rb:11:in `element'
from pageobject.rb:7:in `block in <class:MyPage>'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object.rb:379:in `instance_eval'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object.rb:379:in `call_block'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object/accessors.rb:1089:in `block in standard_methods'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object/accessors.rb:246:in `block in select_list'
from pageobject.rb:10:in `input_payer'
from pageobject.rb:25:in `<main>'
Solution
The accessor you want for the select list is:
select_list(:payer_insurance){ element(:fieldset, :class => "dartPayer-Insurance").select_list_element(:id => "dartPayer-Payer") }
Problem
You were getting the syntax error due to the following part:
element(:class => "dartPayer-Insurance")
In the API docs for element, you can see that method definition is:
(Object) element(tag, identifier = {:index => 0})
Finds an element
Parameters:
the (Symbol) — name of the tag for the element
identifier (Hash) (defaults to: {:index => 0}) — how we find an element. You can use a multiple paramaters by combining of any of the following except xpath
The original code was missing the tag parameter, which caused the exception.
Note that the select list id was also incorrect - using dartPayer-PayerList instead of dartPayer-Payer.

Pass dynamic content to template in Middleman

I'm building a static site using Middleman that has a portfolio section of all the client's recent projects.
The portfolio section will display project thumbnail images in a 3 X 3 gallery fashion and, when clicked on, will open their co-responding html page inside a lightbox.
The layout for the pages inside the light box is the same so rather than markup each individual page, I thought there would be a way for Middleman handling the content served from a yaml data file (projects.yml) using [a link.
Here's what I've got in my config.rb file
###
# Page options, layouts, aliases and proxies
###
# A path which all have the same layout
with_layout :popup do
page "/projects/*"
end
# Proxy (fake) files
# page "/this-page-has-no-template.html", :proxy => "/template-file.html" do
# #which_fake_page = "Rendering a fake page with a variable"
# end
data.projects.details.each do |pd|
proxy "/projects/#{pd[:client_name]}.html", "/projects/template.html", locals: { project: pd }, ignore: true
end
Ok so after some digging I came across the two posts below which helped me under stand how dynamic pages work in middleman. (Unfortunately there's not a lot of doco and the Middleman example for Dynamic pages is really basic)
http://benfrain.com/understanding-middleman-the-static-site-generator-for-faster-prototyping/
http://forum.middlemanapp.com/discussion/134/best-way-to-use-yaml-same-html-but-parameter-driven-data-fixed/p1
My solution...
data/projects.yml (contains project details)
details:
- client: "Company X"
title: "Company X Event"
video_url: ""
logo:
- "logo_companyx.gif"
image_path: "/img/projects/companyx"
total_images: 10
content: "<p>Blah blah blah</p>"
responsibilities:
"<li>Something</li>
<li>Some task</li>"
config.rb:
data.projects.details.each do |pd|
proxy "/projects/#{pd[:client]}.html", "/projects/template.html", :layout => false, :locals => { :project => pd }, :ignore => true
end
The trick with the snippet above is passing the entire project data object to the template via a proxy using locals and setting the layout to false so it doesn't inherit the default site layout (as I - or the client rather - want to display these in a lightbox popup)
The last step in the process was to create /projects/template.html.erb (in the source folder), declaring the following at the top of the template
<% p = locals[:project] %>
This allowed me to output each property of the p object within template.html.erb.
eg:
<%= p[:title] %>
I hope this helps someone as it took me a few days of playing around and LOTS of searching online for example or hints.

Resources