Browser.back is not working - ruby

Using watir, I've written scripts to check multiple links are being directed to the right page as below.
Links= ["Link", "Link1"]
Links.each do |LinkValue|
#browser.link(:text => LinkValue).wait_until_present.click
fail unless #browser.text.include?(LinkValue)
#browser.back
end
What I am trying is:
maintaining Linktext in an array
iterating with each linktext
verify
navigate to the previous page to start verifying with next linktext.
But the script is not working. It is not executing after first value and also not navigating back.

The following scrip working for me
require 'watir'
browser = Watir::Browser.new(:firefox) # :chrome also work
browser.goto 'https://www.google.com/'
browser.link(text: 'Gmail').wait_until_present.click
sleep(10)
browser.back
sleep(10)

You are calling Kernel::Fail, which will raise an exception if the condition isn't satisfied.
In this case, it looks like you are expecting that the destination page will contain the same link text that was clicked on the originating page. If that's not true, then the script will raise an exception and terminate.
Here's a contrived "working" example (which only "works" because the link text exists on both originating and destination pages):
require 'watir'
b = Watir::Browser.new :chrome
b.goto "http://www.iana.org/domains/reserved"
links = ["Overview", "Root Zone Management"]
links.each do |link|
b.link(:text => link).click
fail unless b.text.include? link
b.back
end
b.close
Some observations:
I wouldn't use fail here. You should investigate a testing framework like Minitest or rspec, which have assertion methods for validating application behavior.
In ruby, variables (and methods and symbols) should be in snake_case.

Related

How to confirm a JavaScript popup using Nokgiri or Mechanize

I'm running a script that will open up on my localhost. My local server is a vulnerable web app test suite.
I'm trying to confirm a XSS popup from a JavaScript alert. For example:
http://127.0.0.1:65412/v?=0.2<script>alert("TEST");</script>
I need to confirm the popup happened using either Mechanize or Nokogiri. Is it possible to confirm that the popup is there with Nokogiri or Mechanize?
For example:
def page(site)
Nokogiri::HTML(RestClient.get(site))
end
puts page('http://127.0.0.1:65412/v?=0.2<script>alert("TEST");</script>')
Nokogiri, and Mechanize because it is built on top of Nokogiri, can parse the HTML and return the <script> tag's contents. The tag's content is text so at that point it's necessary to look inside the text to find what you want:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<html>
<head>
<script>alert("TEST");</script>
</head>
</html>
EOT
script_content = doc.at('script').content # => "alert(\"TEST\");"
It's easy to check to see if a sub-string exists at that point:
script_content['alert("TEST");'] # => "alert(\"TEST\");"
or:
!!script_content['alert("TEST");'] # => true
Note: It's not possible with Nokogiri, or Mechanize, to tell if a pop-up occurred as that'd happen inside a browser as it runs the JavaScript. Neither Nokogiri or Mechanize understand or interpret JavaScript. Only a tool like Watir or that interprets JavaScript could do that.
Definitely not, and that's because neither Mechanize or Nokogiri run Javascript.
Instead, you could use Selenium.
Something like this:
require 'selenium-webdriver'
class AlertChecker
Driver = Selenium::WebDriver.for :firefox
def initialize(url)
Driver.navigate.to url
end
def raise_alert(text)
Driver.execute_script "alert('#{text}')"
self
end
def safely_get_alert
begin
Driver.switch_to.alert
rescue Selenium::WebDriver::Error::NoAlertOpenError
end
end
end
Usage:
alert_checker = AlertChecker.new("http://my.website")
alert = alert_checker.safely_get_alert
# => nil
alert_checker.raise_alert("hack")
alert = alert_checker.safely_get_alert
puts alert.text
# => 'hack'
# As far as I'm aware Selenium doesn't have a built-in way
# to tell you if it's an alert, confirm, or prompt.
# But you know it's a prompt, for example, you could also send
# keys before accepting or dismissing
alert.accept
alert = alert_checker.safely_get_alert
# => nil
There are some tricky things with Selenium's handling of alerts, though.
There's no way for your code to detect the type (prompt, confirm, or alert) without using something like rescue or try. Everything is reached through switch_to.alert.
Also, if your browser has an alert open you cannot run any subsequent commands unless you handle alert. Say you try and navigate.to while the alert is open; you'd get an error along the lines of You didn't handle the alert and your navigate.to command would have to be rerun. When this error is raised, the alert object will be lost as well.
It's a little unappealing to use rescue as a control structure in this way but I'm not aware of any other option

(Ruby) Cannot use selenium commands in cucumber tests?

I am trying to save object definitions in a "home page" file and simply call those methods whenever I need to use that button/link/image/etc. But for some reason the selenium commands are bringing up a NoMethodError. When I simply run the cucumber command while on the features folder in the terminal, I get these errors:
When I click on Site Management # features/step_definitions/steps.rb:17
undefined method `find_element' for nil:NilClass (NoMethodError)
./features/lib/pages/home.rb:3:in `siteMgmt'
./features/step_definitions/steps.rb:18:in `/^I click on Site Management$/'
features/test.feature:6:in `When I click on Site Management'
So in other words, it's trying to "click on site management," the code moves to the Home class, the SiteMgmt method (great!) and then fails when trying to run the selenium find_method method. I thought I might have to add a require selenium-webdriver at the top of home.rb, but a) that's NOT the case in steps.rb and, even if I add it, it doesn't work.
Here is the folder structure:
features/
--test.feature
lib/
pages/
--home.rb
step_definitions/
--steps.rb
support/
--env.rb
env.rb
require 'selenium-webdriver'
Dir[File.dirname(__FILE__) + "/../lib/pages/*.rb"].each {|file| require file }
Before do |scenario|
#driver = Selenium::WebDriver.for :chrome
#url = "URL goes here"
end
After do |scenario|
#driver.quit
end
test.feature
Feature: Proof of Concept
Stack overflow help!
Scenario:
Given I am logged into the site
When I click on Site Management
Then the Site Management page should load
steps.rb
Given(/^I am logged into AMP$/) do
#driver.get #amp_url
end
When(/^I click on Site Management$/) do
link = Home.new.siteMgmt
link.click
end
Then(/^the Site Management page should load$/) do
# assert here
end
home.rb
class Home
def siteMgmt
elem = #driver.find_element(:xpath, '//*[#id="body"]/section[2]/ul/li[1]/h3/a')
return elem
end
end
Thanks for all your help!
The #driver instance variable that's created in the Before block isn't available to an instantiated Home object. You could add a parameter to the site_mgmt method and pass the #driver instance variable in. Here's a contrived example:
class Home
def site_mgmt(driver)
elem = driver.find_element(:id, "logo")
end
end
require 'selenium-webdriver'
#driver = Selenium::WebDriver.for :chrome
#driver.navigate.to "http://www.iana.org/domains/reserved"
link = Home.new.site_mgmt(#driver)
link.click
A couple of notes: 1) variables in ruby are snake_case'd (i.e. site_mgmt instead of siteMgmt; and 2) return elem in site_mgmt isn't needed because ruby methods implicitly return.
Well, it turns out all I had to do is turn #driver to $driver. I'm still learning Ruby and didn't realize the difference.

Rails not loading in rspec file gives error "NameError: uninitialized constant" when accessing a model

Rails rails 4.0.2 and Ruby 2.0.0
I am using watir-webdriver with rspec to test my web app. I want to pick the last item in a list where presence of an associated model is true. Basically the model on the page is Category, and in the list of categories I want to select the last one that has Tools associated with it. A Category has many Tools. My spec file:
require 'watir'
require 'watir-webdriver'
browser = Watir::Browser.new
browser.window.maximize
RSpec.configure do |config|
config.before(:each) { #browser = browser }
config.after(:suite) { browser.close unless browser.nil? }
end
url = 'localhost:3000'
serial = Time.now
describe 'it should log in and CRUD menu items' do
it 'should log in' do
#browser.goto url
#browser.text_field(id: 'username').set Logins::user
#browser.text_field(id: 'password').set Logins::password(url)
#browser.button(:text, 'Login').click
#browser.link(:text, 'Menu Settings').click
end
it 'should not delete a category with tools still associated with it' do
category_name = Category.joins(:tools).where(true).order(name: :asc).last
#browser.link(:text, category_name).click
#browser.h4(class: 'tool_list')
#browser.link(text: 'Delete').click
#browser.driver.switch_to.alert.accept
#browser.p(text: 'You can not destroy this menu item until it is empty').present?
end
end
The line that is giving me the error is:
category_name = Category.joins(:tools).where(true).order(name: :asc).last
Not sure why I can't do a simple query in my spec file. I think I'm missing something really obvious.
edit
I updated my rspec version from 2 to 3 and ran:
rails generate rspec:install
because I was missing the spec/rails_helper.rb file. Still getting the same errors.
The problem isn't in Watir, but perhaps you have to require the file how defined your model ...
I think, perhaps, you have to require needed gems; like Active Record,
it's configuration, and initialisations files.

Why the object references are not correctly passed in this RSpec script?

I have to say I am new both to Ruby and to RSpec. Anyway I completed one RSpec script but after refactoring it failed. Here is the original working version:
describe Site do
browser = Watir::Browser.new :ie
site = Site.new(browser, "http://localhost:8080/site")
it "can navigate to any page at the site" do
site.pages_names.each do |page_name|
site.goto(page_name)
site.actual_page.name.should eq page_name
end
end
browser.close
end
and here is the modified version - I wanted to have reported all the pages which were visited during the test:
describe Site do
browser = Watir::Browser.new :ie
site = Site.new(browser, "http://localhost:8080/site")
site.pages_names.each do |page_name|
it "can navigate to #{page_name}" do
site.goto(page_name)
site.actual_page.name.should eq page_name
end
end
browser.close
end
The problem in the latter case is that site gets evaluated to nil within the code block associated with 'it' method.
But when I did this:
...
s = site
it "can navigate to #{page_name}" do
s.goto(page_name)
s.actual_page.name.should eq page_name
end
...
the nil problem was gone but tests failed with the reason "browser was closed"
Apparently I am missing something very basic Ruby knowledge - because the browser reference is not working correctly in modified script. Where did I go wrong? What refactoring shall be applied to make this work?
Thanks for your help!
It's important to understand that RSpec, like many ruby programs, has two runtime stages:
During the first stage, RSpec loads each of your spec files, and executes each of the describe and context blocks. During this stage, the execution of your code defines your examples, the hooks, etc. But your examples and hooks are NOT executed during this stage.
Once RSpec has finished loading the spec files (and all examples have been defined), it executes them.
So...trimming down your example to a simpler form, here's what you've got:
describe Site do
browser = Watir::Browser.new :ie
it 'does something with the browser' do
# do something with the browser
end
browser.close
end
While visually it looks like the browser instance is instantiated, then used in the example, then closed, here's what's really happening:
The browser instance is instantiated
The example is defined (but not run)
The browser is closed
(Later, after all examples have been defined...) The example is run
As O.Powell's answer shows, you can close the browser in an after(:all) hook to delay the closing until after all examples in this example group have run. That said, I'd question if you really need the browser instance at example definition time. Generally you're best off lazily creating resources (such as the browser instance) when examples need them as they are running, rather than during the example definition phase.
I replicated your code above using fake classes for Site and Watir. It worked perfectly. My only conclusion then is that the issue must lie with either one of the above classes. I noticed the Site instance only had to visit one page in your first working version, but has to visit multiple pages in the non working version. There may be an issue there involving the mutation happening inside the instance.
See if this makes a difference:
describe Site do
uri = "http://localhost:8080/site"
browser = Watir::Browser.new :ie
page_names = Site.new(browser, uri).page_names
before(:each) { #site = Site.new(browser, uri) }
after(:all) { browser.close }
pages_names.each do |page_name|
it "can navigate to #{page_name}" do
#site.goto(page_name)
#site.actual_page.name.should eq page_name
end
end
end

How do I use Nokogiri to parse a bit.ly stats page?

I'm trying to parse the Twitter usernames from a bit.ly stats page using Nokogiri:
require 'rubygems'
require 'nokogiri'
require 'open-uri'
doc = Nokogiri::HTML(open('http://bitly.com/U026ue+/global'))
twitter_accounts = []
shares = doc.xpath('//*[#id="tweets"]/li')
shares.map do |tweet|
twitter_accounts << tweet.at_css('.conv.tweet.a')
end
puts twitter_accounts
My understanding is that Nokogiri will save shares in some form of tree structure, which I can use to drill down into, but my mileage is varying.
That data is coming in from an Ajax request with a JSON response. It's pretty easy to get at though:
require 'json'
url = 'http://search.twitter.com/search.json?_usragnt=Bitly&include_entities=true&rpp=100&q=nowness.com%2Fday%2F2012%2F12%2F6%2F2643'
hash = JSON.parse open(url).read
puts hash['results'].map{|x| x['from_user']}
I got that URL by loading the page in Chrome and then looking at the network panel, I also removed the timestamp and callback parameters just to clean things up a bit.
Actually, Eric Walker was onto something. If you look at doc, the section where the tweets are supposed to be look like:
<h2>Tweets</h2>
<ul id="tweets"></ul>
</div>
This is likely because they're generated by some JavaScript call which Nokogiri isn't executing. One possible solution is to use watir to traverse to the page, load the JavaScript and then save the HTML.
Here is a script that accomplishes just that. Note that you had some issues with your XPath arguments which I've since solved, and that watir will open a new browser every time you run this script:
require 'watir'
require 'nokogiri'
browser = Watir::Browser.new
browser.goto 'http://bitly.com/U026ue+/global'
doc = Nokogiri::HTML.parse(browser.html)
twitter_accounts = []
shares = doc.xpath('//li[contains(#class, "tweet")]/a')
shares.each do |tweet|
twitter_accounts << tweet.attr('title')
end
puts twitter_accounts
browser.close
You can also use headless to prevent a window from opening.

Resources