Siteprism section won't respond to has_element - ruby

I'm trying to use SitePrism with my ruby/capybara/selenium test suite and I continue to get an error around expecting an element to respond to has_<element_name>? The last line of the spec is what is failing. It is giving me an error saying:
expected #<PageObjects::Pages::SalesPage:0x00000000066782b0> to respond to 'has_toolbar_title?'
sales_page.rb
module PageObjects
module Pages
class SalesPage < SitePrism::Page
set_url "REDACTED"
section :toolbar, PageObjects::Sections::Toolbar, '#qHybridViewToolbar'
end
end
end
toolbar.rb
module PageObjects
module Sections
class Toolbar < SitePrism::Section
element :new_button, '#ToolBtnNew'
element :edit_button, '#ToolBtnUpdate'
element :delete_button, '#ToolBtnDelete'
element :toolbar_title, '#qToolbarViewTitle'
end
end
end
my_spec.rb
require 'spec_helper'
describe 'On sales page' do
context 'without a password' do
it "does the things" do
sales_page = PageObjects::Pages::SalesPage.new
sales_page.load
expect(sales_page).to have_toolbar
sales_page.toolbar.new_button.click
puts sales_page.toolbar.toolbar_title
puts sales_page.toolbar.toolbar_title.text
expect(sales_page).to have_toolbar_title
end
end
end

This error is completely correct. You're asking the page whether it has a toolbar_title, but the toolbar_title is actually on the toolbar. You need to call
expect(sales_page.toolbar).to have_toolbar_title

Related

Need to login before going to a new page

I'm currently using Ruby and Capybara and writing tests. I have a login page already done and it works fine. I'm trying to create a separate test where I want it to already login before going to that page.
My login Code:
class LoginPage < SitePrism::Page
set_url '/'
element :username_field, '#username'
element :password_field, '#password'
element :login_button, '#login_button'
def login()
username_field.send_keys 'Cow'
password_field.send_keys 'dogs'
login_button.click
end
def load_and_login(*args)
self.load
login(*args)
self
end
end
This is my new page. Where I want it to login prior to going to this page.
describe login do
before(:each) do
home = LoginPage.new
home.load
home.login
end
end
class newPage < SitePrism::Page
include RSpec::Matchers
include Capybara::RSpecMatchers
set_url '/new'
end
This is the error that I"m getting:
Failure/Error: Dir['./pages/**/*.rb'].sort.each { |f| require f }
NameError:
undefined local variable or method `login' for main:Object
describe login do -- looks like problem here
Change it to describe '#login' do

How to test whether an element is displayed on the page

The following code checks whether an element is displayed and if the element is present runs a specific action, else the test continues normally:
require "selenium-webdriver"
require "rspec"
require 'rspec/expectations'
describe "Current Expense" do
before(:all) do
#driver = Selenium::WebDriver.for :firefox
#base_url = "http://the-internet.herokuapp.com/disappearing_elements"
#driver.manage.window.maximize
end
after(:all) do
#driver.quit
end
it "Check icon" do
#driver.get(#base_url)
if expect(#driver.find_element(:xpath, "//*[#href='/gallery/']").displayed?).to be_truthy
#driver.find_element(:xpath, "//*[#href='/gallery/']").click
sleep 2
puts "element appears"
else
puts "element NOT appears"
end
end
end
When the element is present, the message appears, but when the element is not present in the page, an error occurs and the else block is not executed. What is causing this error?
I think the problem is that you're using expect when you should just have the conditional #driver.find_element(:xpath, "//*[#href='/gallery/']").displayed?. If the conditional is true you will see the expected message; likewise if it evaluates to false you will see `"element NOT appears".
As currently constructed, if the find_element method returns false then the spec should fail. Please post the error or exception you're seeing so that we can know for sure.
On a side note, what you have right now is fine for a quick and dirty test of whether or not the page is functioning correctly, but you'll probably want to give two cases in your test file: one where you know the icon will be on the page, and one where it shouldn't be on the page, and then test the outcome for each. For example:
#Code omitted
it "has the icon when x is the case" do
# make x be the case
#driver.get(#base_url)
#driver.find_element(:xpath, "//*[#href='/gallery/']").displayed?
#driver.find_element(:xpath, "//*[#href='/gallery/']").click
sleep 2
# code that verifies that the element is on the page
end
it "doesn't have the icon when y is the case" do
# make y be the case
#driver.get(#base_url)
expect {
#driver.find_element(:xpath, "//*[#href='/gallery/']").displayed?
}.to be_false
end
#code omitted
expect is the reason for test failure. Find the below snippet for solution.. Cheers!
it "has the icon when x is the case" do
#driver.get(#base_url)
begin
#driver.find_element(:xpath, "//*[#href='/gallery/']")
#driver.find_element(:xpath, "//*[#href='/gallery/']").click
rescue Selenium::WebDriver::Error::NoSuchElementError
raise 'The Element ' + what + ' is not available'
end
end
it "doesn't have the icon when y is the case" do
#driver.get(#base_url)
begin
#driver.find_element(:xpath, "//*[#href='/gallery/']")
raise 'The Element ' + what + ' is available'
rescue Selenium::WebDriver::Error::NoSuchElementError
expect(true).to be_truthy
end
end

Testing with Rspec - The correct way

My weakest point when it comes to coding, is using TDD & BDD methods - I tend to just write code.. but it is something that I am trying to work on.
Could anyone point out the best way to go about the following problem:
Class1:
module TempMod
class MyClass
def initalize(config)
#config = config
end
def process(xml)
if react_upon? xml.something
puts 'yeah'
else
puts 'nah'
end
end
def react_upon?(xml_code)
#code here
end
end
end
So lets say I wanted to test this class, or build it from a TDD point of view so I write my tests:
describe TempMod::MyClass do
let(:config) {double}
let(:myclass) {TempMod::MyClass.new config}
context 'Given that the xml is something we react upon' do
it 'should check that it is valid' do
myclass.process '<some><xml>here</xml></some>'
end
it 'should output yea'
end
end
How do I test that it is calling the react_upon? method. Do I even want to see it is calling it?
Is the proper way to test it, to test all the functions like the react_upon? itself independently of the other functions?
This is properly the main thing that is most confusing me with this sort of testing. Am I testing the whole class, or just individually testing the functions, and not their interactions with the other functions in that class?
Also I realize the the react_upon? might not adhere to the Single responsibility principle and I would probably move that out to its own module/class which I could test using a stub.
If anyone can shed some light on this for me that would be awesome.
edit:
describe TempMod::MyClass do
let (:valid_planning_status_xml) {
'<StatusUpdate> <TitleId>2329</TitleId> <FromStatus>Proposed</FromStatus> <ToStatus>Confirmed</ToStatus> </StatusUpdate>'
}
let(:config) { double }
let(:status_resolver) { double }
subject(:message_processor) { TempMod::MyClass.new config, status_resolver }
context 'Given that the message XML is valid' do
it 'should check the context of the message' do
expect(message_processor.process valid_planning_status_xml).to call :check_me
end
context 'Given that the message is for a planning event update' do
it 'should call something' do
pending
end
end
context 'Given that the message is for a recording job update' do
end
context 'Given that the message is for a video title update' do
end
end
end
Your question confused me a bit is this what you are asking
module TempMod
class MyClass
def initalize(config)
#config = config
end
def process(xml)
react_upon?(xml.something) ? 'yeah' : 'nah'
end
def react_upon?(xml_code)
#code here
end
end
end
Then test like
describe TempMod::MyClass do
let(:config) {double}
let(:myclass) {TempMod::MyClass.new config}
context 'Given that the xml is something we react upon' do
it "should respond to react_upon?" do
expect(myclass).to respond_to(:react_upon?)
end
it "should react_upon? valid xml" do
expect(myclass.react_upon?(YOUR VALID REACTION GOES HERE)).to be_true
end
it "should not react_upon? invalid xml" do
expect(myclass.react_upon?(YOUR INVALID REACTION GOES HERE)).to be_false
end
it "should say 'yeah' if it is valid" do
expect(myclass.process('<some><xml>here</xml></some>')).to eq('yeah')
end
it "should say 'nah' if it is invalid" do
expect(myclass.process('<some><xml>here</some>')).to eq('nah')
end
it 'should check the context of the message' do
expect(myclass).to receive(:react_upon?).with('<some><xml>here</xml></some>')
myclass.process('<some><xml>here</xml></some>')
end
end
end
Right now your tests have no expectations so I added one that expects myclass to respiond_to the react_upon? method and another that expects myclass.process(xml) to respond with a String that equals yeah.

Passing an object as subject to rspec

I am running rspec tests on a catalog object from within a Ruby app, using Rspec::Core::Runner::run:
File.open('/tmp/catalog', 'w') do |out|
YAML.dump(catalog, out)
end
...
unless RSpec::Core::Runner::run(spec_dirs, $stderr, out) == 0
raise Puppet::Error, "Unit tests failed:\n#{out.string}"
end
(The full code can be found at https://github.com/camptocamp/puppet-spec/blob/master/lib/puppet/indirector/catalog/rest_spec.rb)
In order to pass the object I want to test, I dump it as YAML to a file (currently /tmp/catalog) and load it as subject in my tests:
describe 'notrun' do
subject { YAML.load_file('/tmp/catalog') }
it { should contain_package('ppet') }
end
Is there a way I could pass the catalog object as subject to my tests without dumping it to a file?
I am not very clear as to what exactly you are trying to achieve but from my understanding I feel that using a before(:each) hook might be of use to you. You can define variables in this block that are available to all the stories in that scope.
Here is an example:
require "rspec/expectations"
class Thing
def widgets
#widgets ||= []
end
end
describe Thing do
before(:each) do
#thing = Thing.new
end
describe "initialized in before(:each)" do
it "has 0 widgets" do
# #thing is available here
#thing.should have(0).widgets
end
it "can get accept new widgets" do
#thing.widgets << Object.new
end
it "does not share state across examples" do
#thing.should have(0).widgets
end
end
end
You can find more details at:
https://www.relishapp.com/rspec/rspec-core/v/2-2/docs/hooks/before-and-after-hooks#define-before(:each)-block

How to access the describe text in rspec

I'm writing some specs that test the template files in a gem that has generators for Rails. I'd love to access to "admin_layout.html.erb" in the rspec spec below:
require 'spec_helper'
describe "admin_layout.html.erb" do
it "has page title Admin" do
HERES WHERE I WOULD LOVE TO HAVE ACCESS TO "admin_layout.html.erb" AS A VARIABLE
end
end
You can use self.class.description to get this info:
it "has page title Admin" do
layout = self.class.description
# => "admin_layout.html.erb"
end
However, keep in mind this will only put out the first parent's description. So if you have contexts in your describe block, then the examples within the contexts would give the context name for self.class instead of the describe block's name. In that case, you could use metadata:
describe "admin_layout.html.erb", :layout => "admin_layout.html.erb"
context "foo" do
it "has page title Admin" do
layout = example.metadata[:layout]
end
end
end
In case you want the top-level description, you can use self.class.top_level_description:
RSpec.describe "Foo", type: :model do
context "bar" do
it "is part of Foo" do
self.class.top_level_description
# => "Foo"
end
end
end

Resources