Git Product home page Git Product logo

site_prism's Introduction

SitePrism

SitePrism has now moved to HERE - Please change all repo links and favourite the new location.

FAQs

Q) Why have you moved?

A) SitePrism has outgrown its previous home, and will be soon extending to some additional subgems

Q) What is going to change going forward?

A) Nothing, Nothing at all! We're simply doing this as an administrative way of allowing additional means to contribute and support the project

Q) What plans do you have?

A) Now we have a proper feature deprecator in place, we're going to be supporting and remaining on the 3.x series of SitePrism for a while. But there will be a 4.x version coming in 2020 some time. But that's a way away!

Q) How can I get involved

A) Raise issues on the new issue tracker/use the new PR system. The old repo will be slowly moving to a Read-Only view and no further commenting / issues / PRs will be permitted.

Q) Will you be supporting the old repo?

A) Yes, we plan on supporting the old repo until v3.4 of SitePrism. By which time we feel as though users will have had enough time to migrate over. It is quick and painless, so do it ASAP!

site_prism's People

Contributors

abotalov avatar andyw8 avatar basuneko avatar dnesteryuk avatar hoffi avatar ineverov avatar j16r avatar jmileham avatar jwarykowski avatar luke-hill avatar menge101 avatar mikekelly avatar natritmeyer avatar nitinsurfs avatar petergoldstein avatar ricmatsui avatar robd avatar rustynail avatar soulcutter avatar sponte avatar systho avatar tadashi0713 avatar tgaff avatar themetalcode avatar thespartan1980 avatar tmertens avatar tobithiel avatar tpbowden avatar twalpole avatar whoojemaflip avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

site_prism's Issues

element method should also define rspec matcher

When using Capybara's RSpec matchers the following matcher doesn't wait for default_wait_time till a will appear on the page:

expect(page).not_to have_css('a')

But when using SitePrism the following will invoke has_signup_field? and thus will wait for default_wait_time:

expect(@page).not_to have_signup_field

Capybara defines have_css and other matchers for this exact reason. So SitePrism should define rspec matcher too (in SitePrism::RSpecMatchers module). This rspec matcher has to handle the following situation properly:

class SignUpPage < CapyPage
  element :username_field, '#sign_up__username'
end

class SignInPage < CapyPage
  element :username_field, '#sign_in__username'
end

include SitePrism::RSpecMatchers
expect(@sign_up_page).to have_username_field
expect(@sign_in_page).to have_username_field

So the matcher class would look like:

class ElementMatcher
  include RSpec::Matchers::Composable

  @page_object_class_mapping = {}

  def initialize(page_object_class, element_name, top_level_node, find_args)
    @element_name = element_name

    @page_object_class_mapping[page_object_class] ||= {}
    @page_object_class_mapping[page_object_class][element_name] = find_args
  end

  def matches?(actual)
    find_args = @page_object_class_mapping[actual.class][@element_name]
    @page.assert_selector(*find_args)
  end

  def does_not_match?(actual)
    find_args = @page_object_class_mapping[actual.class][@element_name]
    @page.assert_no_selector(*find_args)
  end
end

elements is an RSpec matcher class, rather than a collection

using the 'elements' method to set a collection of elements on a page. Rather than getting an array of elements, I'm getting an RSpec matcher:

class ManagePages < BasePage
    elements :edit_links, 'a.edit'
end

...
@manage_page = ManagePage.new
@manage_page.edit_links
#<RSpec::Matchers::BuiltIn::All:0x00000104253e88 @matcher="a.edit", @failed_objects={}>

that last should give me an array, not an RSpec matcher ... ?

Including RSpec::Matchers in Page classes will mess up `elements` and `sections`

I want to move some assertions to inside the Page class, and for that I need to include RSpec::Matchers. However, doing so will turn elements and sections into RSpec::Matchers::BuiltIn::All.

For example:

class HomePage < SitePrism::Page
  sections :menu_items, MenuItemClass, "ul#menu li"

  def test_method
    binding.pry
  end
end

In the code above, if I'm at the binding.pry, menu_items will return an array of menu items as expected.

However, if I included RSpec::Matchers as following:

class HomePage < SitePrism::Page
  include RSpec::Matchers

  sections :menu_items, MenuItemClass, "ul#menu li"

  def test_method
    binding.pry
  end
end

And if I try to call menu_items at the binding.pry, I will get an error at lib/site_prism/element_container.rb:33:

NoMethodError: undefined method `collect' for #RSpec::Matchers::BuiltIn::All:0x007fd462d29ae0

So if included RSpec::Matcher, instead of returning an Array, find_all(*find_args, *runtime_args) in the sections method will return a RSpec::Matchers::BuiltIn::All which does respond_to :collect.

def sections(section_collection_name, *args, &block)
    section_class, find_args = extract_section_options args, &block
    build section_collection_name, *find_args do
      define_method section_collection_name do |*runtime_args|
        find_all(*find_args, *runtime_args).collect do |element|
          section_class.new self, element
        end
      end
    end
  end

Content should be found within a section

Hello guys,

There is a problem with SitePrism. When we write something like:

@products.list.should have_content('Cars') # list object is a section

it will make capybara to look for this text on an entire page. It means we cannot make sure our text is found within a correct element. In addition, it slows down our tests since Capybara has to parse an entire page to find the text we are looking for.

At first, I had wanted to create a pull request, because I have the solution (at least it is some working idea), but then I decided to discuss it. My solution:

module SitePrism
  class Section
    alias_method :origin_should, :should

    def should(matcher, &block)
      meth_name = matcher.instance_variable_get(:@expected).to_s.sub("have_","has_")
      meth_name << '?'

      if self.respond_to?(meth_name)
        self.origin_should(matcher, &block)
      else
        self.root_element.should(matcher, &block)
      end
    end
  end
end

I suggest to redefine the should method for the SitePrism::Section class and check if our section has a method which should be used by matcher, we pass matcher to the original should, but if a section doesn't have a method which should be used by a matcher, we pass a matcher to the should method of an original element of Capybara. By using this way, a searched looked content will be searched within a correct section.

What do you think? (please, ignore ugly way for getting the expected method name)

Update for RSpec 3.0

RSpec 3.0.0.beta1 was released about 1 hour ago - definitely would love for SitePrism to be ready for when 3.0.0 hits.

SitePrism should use Capybara's implicit wait functionality

Natritmeyer,

I started using SitePrism fairly recently to rewrite existing Capybara tests in order to abstract page element locators from the individual test cases. In the relatively short time I have been working with it, I have found myself working around a number of test failures as a result of SitePrism explicitly overriding capybara's implicit wait functionality, and not passing through a sufficient amount of Capybara behavior to implement various Capybara search options in the tests.

Given that SitePrism is a wrapper for Capybara, I would expect that SitePrism would do its best to mimic and/or passthrough the default Capybara behavior wherever possible rather than replace it.

The biggest issue I have seen with Prism is its override of the Capybara implicit wait in methods such as has_#{elementname}?. This goes against the default Capybara behavior. See this article about why wait_until was removed from Capybara 2.0: http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara Capybara's implicit waits are good and SitePrism shouldn't be overriding them without explicitly being told to by the tester.

Take for example this snippet of test code (this is a fabricated example to demonstrate a few different potential pitfalls of SitePrism):

feature 'My Index Page', js: true do
   scenario 'displays a list of articles' do
      my_index_page.load
      # See Issue 1 below
      campaigns_page.should have_articles
   end

   scenario 'displays multiple pages of articles' do
      my_index_page.load
     # This line does take advantage of capybara's implicit waits because current_page returns a capybara object:
      my_index_page.pagination.current_page.should have_content "Page 1 of 3"
      my_index_page.pagination.last_page_link.click

      # See Issue #2 below
      my_index_page.should be_displayed

     # See Issue #3 below
      my_index_page.articles.count.should eql 25
   end
end

Issue 1

If an element or section takes time to appear after page load, the method page.has_element_name? will fail because it explicitly sets capybara's wait for the block to 0, rather than allowing Capybara's implicit waiting behavior to pass through to SitePrism's behavior.

Issue 2

This issue is already addressed in PR #40 but is listed here for convenience.

page.displayed? fails because displayed? has no implicit wait, so if an action causes the page to redirect, then displayed? fails on the next expected page because the method hits a race condition between when the prior action occurred and when the next page is actually loaded in the browser.

Issue 3

We are not able to pass advanced Capybara selectors through site prism objects when attempting to access them.

The common example for this is, as in the above example, expecting some count of an elements or sections array to be returned. In the above case, we expect the count to equal 25; however, this presumes that when the my_index_page.articles array is returned that all 25 articles have already finished loading. With capybara we could tell it to expect 25 articles with the :count => 25 filter; however, with SitePrism this is not possible without predefining it in our page file; however, it is not always possible how many elements to expect in an array when the element object is created in the Page class, and it makes that element extremely inflexible when the number of elements returned are the dynamic result of something like a database query.

It would be ideal to be able to supply a base locator in the Page class when the element/section is defined, and when we later access that element/section to be able to supply additional capybara filters. For example:

page.should have_articles(:count => 25)

In this case the count filter should be appended to the default search parameters for the element and passed through to Capybara to ensure it implicitly waits for 25 articles to be displayed.

Declaring scoped methods on `element` and the like

Hey, this is something that I think might be useful, and would be definitely willing to contribute myself. I'm thinking of some mechanism for providing scoped methods on element, section and the like. Similar to ActiveRecord associations. Maybe best explained with a (contrived) use-case:

element :something, '.something_identifying' do
  def errored?
    self.has_css?('.with_errors')
  end
end

... and the usage

my_page.something.errored?

Thoughts on the acceptance of such a patch?

parameters in url can not be defined outside the page file

We have a scenario to test the same webpage with different languages.

and we use the parameter "lng" to switch languages for the page.
e.g. www.example.com/home.php?lng=en means the english version of the page
www.example.com/home.php?lng=jp means the japanese version of the page.

with site_prism, i can define a page object for the page www.example.com/home.php. but in my test case, i need to open this same page with different languages

but page.load did not accept any parameters. it will be good if the load method will accept a group of parameters and the parameters will be added to the url

Inconsistent methods naming in page objects

  • Devise::Confirmations::New#submit_form
  • Devise::Passwords::New#recover_password
  • Devise::Registrations::Edit#submit_user_form
  • Devise::Registrations::Edit#register
  • Devise::Sessions::New#sign_in

Wait_for checker with a count parameter

The count option on the wait_for methods does not seem to be getting passed through as expected.

@group_page.feed.wait_for_threads count: 11

Expected: This would return once there are 11 threads on the page

Actual: This returns as soon as there is one thread on the page.

Is this expected to work? If not, is it possible to add support for it?

Glancing through the code, it seems like this ought to work as it appears you're passing through the additional find_args.

Some additional info:

  • This method does work as expected

group_page.feed.has_threads? count: 11

  • From the feed object itself, if I directly call element_exists, then I get the expected response as well

element_exists? ".thread_selector", count: 11

Any ideas what might be going on here?

(Also thank you so much for this awesome framework! We just switched to it and are absolutely loving it)

Timeouts don't fire if you've stopped time

This one took ages to debug:

describe "test the button link! (This doesn't work yet)" do
  @page.button.click_link # Link is broken because I haven't implemented it yet
  expect(@page2).to be_displayed # Spec hangs here forever
end

What I want to happen: RSpec fails the test because the button link doesn't work.
What actually happens: RSpec hangs at line 3 forever.

If you've used Timecop to freeze time at a specific point, and if the link is broken, SitePrism will just wait for page2 to appear forever, irrespective of any timeouts you've set. This is because Timecop has stopped time and SitePrism's timeouts are susceptible to that. (Perhaps there's a better, non-timecop-susceptible way of doing timeouts?)

(Feature) Conditional elements or sections

I have a need for pages to include conditional elements or sections. How would I go about doing that in site_prism, or is that something you can add functionality for?

As an example we have some code that looks like this when not logged in:

<div class="header_content_right">
  <div class="not_logged_in">
    <a href="/user/login">Log In</a>
    <a class="standard_button size_small style_primary rounded" href="/signup">Signup!</a>
  </div>
</div>

that turns into this when logged in:

<div class="header_content_right distraction">

  <ul id="user_menu">
    <li class="user_section">
      <a class="control_room_link" href="/control_room">Control Room</a>
    </li>
.....
    <li id="user_actions_link" class="user_section last">
      <a class="user_thumb section_button" href="/profile"><span><img height="30px" title="Profile Pic" src="../image/thumb/1344351443_02789-1.jpg" alt="Profile Pic"><em></em></span><strong>User Account</strong></a>
      <div class="user_section_content block_links navigation_container menu">
        <ul>
          <li><a href="/profile">View My Profile</a></li>
          <li><a href="/messages">Messages (1)</a></li>
          <li><a href="/account">Account</a></li>
          <li>
            <a href="/logout">Log Out</a>
          </li>
        </ul>
      </div>
    </li>

  </ul>
</div>

Basically, I am unsure of how to structure my page object to interact with both DOM structures.

Do I need to create two separate sections and in my RSpec code use tests to determine if one section is available while the other is not?

This is something I have had trouble wrapping my brain around when considering using Page Objects, the question of dynamic content.

Thanks!

Brian

Window Maximize

Hi Nat, I've been using site prism for my client and it looks really good. The one thing I'm struggling to do is find a DSL to maximize the browser window. Currently am using

Capybara.current_session_driver.browser.manage.window.maximize

to maximize the window and it kind of feels a bit odd to use the Capybara one as site_prism as i currently understand provides a wrapper around capybara.

Apologies if this is not the right place for this or there is already one available to do this functionality

Inability to use #has_no_element? because it looks at the whole page

Consider the following code:

it 'is not shown for not-done tasks' do
  expect(task.done.checked?).to eq false
  expect(task.root_element.has_no_css? '.recurrent').to eq true
end

Because #has_no_element? works on the whole page, I can't use it to write #has_no_recurrent? here. And there's also duplication, as I have to write .recurrent both in SitePrism page definition and here.

It would be nice if #has_no_element only worked within the element, not on the whole page.

iframe issues

Unfortunately, I am stuck with a bit of a quandary:

I need to be able to access and interact inside of an iframe which has no id or name.
I know that your wiki indicates that the iframe requires an id or name to be located properly, as a restriction of Capybara. However, after looking through the Capybara code, it looks like the id locator is passed right into the Selenium WebDriver instance, which accepts id, name, or even a Web Element. Thus, in WebDriver I could do:

iframe_element = @driver.find_element(:css, "iframe.secure_iframe")
@driver.switch_to.frame(iframe_element)

I would like to be able to do the same sort of thing with SitePrism, so that something like this would pass the element instance into the iframe method:

      element :iframe_element, "iframe.secure_iframe"
      iframe :inside_iframe, BodyIframe, :iframe_element

The other work-around I attempted was to write an inject_id method that would use execute_script to inject a dynamically created id into the iframe element, but unfortunately, SitePrism and Capybara don't permit arguments to be included in the execute_script method to pass to WebDriver (which would allow you to directly reference the already found element in the Javascript code through arguments[0]).

Any ideas?

Thanks!

Enable elements to be created using Capybara's finders for links, buttons and fields

Capybara provides nice finders that are able to match buttons, links and fields by their text or label - find_button, find_link and find_field.

It would be nice if you could add elements to site_prism in the same way - I imagine you could have #link, #button and #field methods which would work much like the existing #element method, but allow you to match on a string. This would be particularly useful now that the :contains pseudo-selector seems to no longer be supported in Capybara.

I'm happy to submit a PR for this if you think it is worth doing.

Using Capybara Scoping Abilities (iframes interaction)

This may not be an issue per se, but I don't see how to use the Caypbara scoping mechanism as part of the pages or sections, where it seems like it would most logically go. For example, it seems most sensible to do this:

class LoginPage < SitePrism::Page
  within("#loginSection") do
    element :clientID, "input[id='clientID']"
    element :logIn, "input[id='btnSubmit']"
  end
end

Here I'm saying that some elements are within a frame context.

Of course doing this as is will result in the following error:

undefined method 'within' for LoginPage:Class (NoMethodError)

I tried making my own within method in the page.rb class. Something like this:

def self.within locator, &block
  block.call locator
end

That, however, does not work. The object won't be found because it's in a frame. Then I tried using a section for the frame:

class LoginForm < SitePrism::Section
  element :clientID, "input[id='clientID']"
  element :logIn, "input[id='btnSubmit']"
end

My class then uses that section with the identifier that is the root of the frame:

class Home < SitePrism::Page
  section :login, LoginForm, "#loginSection"
end

Again, though, that does not work. So I'm not sure how to do Capybara scoping at all in this context hence I figured I would treat this as an issue for now and see what you thought about it.

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

Problem with Inheritance section and page

Hi natritmeyer. Thanks for your amazing job with site_prism.
I'm newbie with site_prism, ruby...etc
I read the doc of site_prism, and I implement site_prism in my tests , but when I trying to use pages with section occurs an error.

An example: I execute this command bundle exec cucumber features/events.feature
When I run this command the Iterm shows that error: uninitialized constant Admin::Menu (NameError)

I have the class Admin < SitePrism::Page

And I have the class Menu < SitePrism::Section

The class Admin contains these code:

section :menu, Menu, "menu-wrapper"
section :section_crudAllRules, CRUDRules, :class, "row rules" 

I really appreciate your job.

Thanks for help.

Site Prism Doesn't have logging feature

With Logging feature , we might have to log the actions and data used during these actions to be logged at a place. This helps in debugging , Analyse the failures. These features are implemented if we write our own page object gem.

I think this can be a good addition. As currently we are using Builder Pattern(to generate our test data). This require Logging of this runtime data for future analysis

Remove RSpec from Gemspec

I am using site_prism along with Minitest, and it by default brings in RSpec as a dependency. Most users would already have RSpec in their Gemfile, so things should just work for them.

dynamic sections with dynamic filter #text returns text for section without filter

Bug in sections(text: 'something').first.text -> returns the text without the filter. Capybara doesn't do this.

I need to track this down still, but it's looking like:

sections :some_section, 'tbody.users tr' do 
  element :some_link, 'a'
end

........


some_sections(text: 'User1').last.text.match /User3/ # true?
# versus
page.all('tbody.users tr', text: 'User1').last.text.match /User3/ # false

Logging this to investigate and reproduce. (self#todo)

Title selector returning nil

The page loads successfully but @somepage.title returns nil.
Using Capybara's page.title (or @somepage.page.title) returns the string of the page title.

[IFRAMES] has_selector? and wait_for_selector? dont seem to work for iframes

The helper functions added by site prism do not appear to work for iFrames.

For example, say I have the following section:

class MySection < SitePrism::Section
  class PreviewIframe < SitePrism::Page
  end
  iframe :preview, PreviewIframe, "#preview"
end

This is returning false even if the preview is displayed on the page.

my_section.has_preview?

wait_for_preview doesn't appear to work either.

I verified that the iframe works in that I can do actions within it. It's just the wait_for_preview and has_preview? methods that aren't behaving.

Is this as designed? It'd be great to have these working for iframes also. Thanks!

Selecting elements by text

Does anyone think an additional method to select elements/sections by text would be helpful? For example, say you have a list of users:

<ul>
    <li><strong>Luke Skywalker</strong></li>
    <li><strong>Darth Vader</strong></li>
    <li><strong>Han Solo</strong></li>
</ul>

And a sections definition:

section :users, UserSection, "ul li"

It might be nice to be able to select a subset of the elements via something like:

page.users_with_text("Darth Vader")

Pretty easy to implement using Capybara's find method, I would think.

Or, is it recommended to just parse through a returned array using normal ruby Array methods to find the same subset?

CSS selector can't find button[title='Search']

Given the HTML below:
<button class="button" title="Search" type="submit">
<span>
<span>Search</span>
</span>
</button>

And the code:

class SearchSection < SitePrism::Section
element :search_field, "input[name='q']"
element :search_button, "button[title='Search']"
end

When I execute via cucumber I get the following:
Unable to find css "button[title='Search']" (Capybara::ElementNotFound)

NB: The search_field is found OK

Deprecate wait_for_ matcher

I propose to deprecate wait_for_* method.

Reasons:

  • it violates standard practice to end methods that return boolean values with ?
  • it does exactly the same what has_* method does

SitePrism exposes design that is very similar to Capybara's design (I mean has_, has_no, have). wait_for just doesn't fit. There's no use case when it may be preferable to has_*?

Selector-definition-API fragility

When defining an element (for example), if you misspell the selector type, the default will be used.

Example:

# oops, :css will be used here
element :first_name, :xpathh, "//div[@id='signup']//input[@name='first-name']"

I think the 2nd arg needs to be checked so only :css or :xpath is allowed.

Edit: after grepping the site_prism code, I'm guessing you're passing this arg directly to Capybara. Should I open a bug there instead?

SitePrism does not follow default Capybara wait behaviour

SitePrism elements call into 'first' rather than 'find' from Capybara. This causes two issues:

  • Capybara 'find' implements wait behavior automatically so the tests don't need to be littered with wait commands (see: http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara)
  • Capybara 2.1 provides 'config.match' to define how you want ambiguity to resolved, but using 'first' forces ambiguity to always be resolved by selecting the first element.

These could be fixed by calling into find instead of first.

Uninitialized constant Capybara::DSL

I got this error straight after installation if I try to run any specs

/Users/mfilimon/.rvm/gems/ruby-1.9.3-p194@sales_catalog/gems/site_prism-1.3/lib/site_prism/page.rb:3:in `<class:Page>': uninitialized constant Capybara::DSL (
NameError)
from /Users/mfilimon/.rvm/gems/ruby-1.9.3-p194@sales_catalog/gems/site_prism-1.3/lib/site_prism/page.rb:2:in `<module:SitePrism>'
from /Users/mfilimon/.rvm/gems/ruby-1.9.3-p194@sales_catalog/gems/site_prism-1.3/lib/site_prism/page.rb:1:in `<top (required)>'
from /Users/mfilimon/.rvm/gems/ruby-1.9.3-p194@sales_catalog/gems/site_prism-1.3/lib/site_prism.rb:5:in `require'
from /Users/mfilimon/.rvm/gems/ruby-1.9.3-p194@sales_catalog/gems/site_prism-1.3/lib/site_prism.rb:5:in `<top (required)>'

My Gemfile is:

group :development, :test do
  gem "factory_girl_rails", "~> 4.0"
  gem 'rspec'
  gem 'rspec-rails'
  gem 'pry'
  gem 'pry-debugger'
  gem 'guard'
  gem 'guard-livereload'
  gem 'capybara'
  gem 'capybara_rails'
  gem 'site_prism'
end

spec_helper:

require 'capybara/rspec'

Any ideas?

Can't seem to use element.visible? if the element doesn't exist.

Hi, I have the following codes:

 element :header_upgrade_subscription, :xpath, "//a[text()='Upgrade subscription']"
 element :header_change_subscription, :xpath, "//a[text()='Change subscription']"

 if header_upgrade_subscription.visible?
change_subscription = header_upgrade_subscription 
 else
change_subscription = header_change_subscription 
 end

The problem is if header_upgrade_subscription doesn't exist, it just fails with:

Capybara::ElementNotFound Exception: Unable to find xpath "//a[text()='Upgrade subscription']"

I know that in Capybara, you can do:

(rdb:1) first(:xpath, "//a[text()='Upgrade subscription']")
nil

and it would return nil if it doesn't exist. How would I use "first" method against the SitePrism element? This is what I get:

(rdb:1) first(header_upgrade_subscription)
Capybara::ElementNotFound Exception: Unable to find xpath "//a[text()='Upgrade subscription']"

I like using the "first" method as it has no wait time if the element doesn't exist.

Thanks for your help!

all_there? assumes all subelements are always present

The all_there? method expects all elements in a container to always be present. However, there are often page elements and entire sections which are displayed on the page depending on some condition or action of the user.

Examples:

  • Error Message(s)
  • Page sections which appear/disappear based on user interaction.
  • Modal (Lightbox) Windows
  • All descendant sections/elements of aforementioned 'sections'.

It is necessary to have a way of flagging certain elements as optional so that they and any of their descendants do not cause the "all_there?" check against an ancestor element to fail.

Given the page:

class LoginPage < SitePrism::Page
  class LoginForm < SitePrism::Section
    element :password_field, '#password'
    element :username_field, '#username'
    element :submit_button, 'button[type="submit"]'
  end

  class NoticeWindow < SitePrism::Section
    element :notice, ".notice"
    element :alert, ".alert"
  end

  set_url "/login"
  set_url_matcher /login/

  # Page content
  section :login_form, LoginForm, "#loginform"
  element :logo, 'img.logo'
  section :notice_window, NoticeWindow, ".notice-window"
end

It is obvious that login_form and logo should be present on page load, and that password_field, username_field, and submit_button should also be present; however, notice_window and/or its subelements should only be present/displayed when a warning or notice must be displayed on the page such as after an invalid login.

So, presuming the required fields described above are present on page load, the following should succeed, but does not:

page = LoginPage.new
page.load
page.should be_all_there #=> all_there? returns false and fails validation

Do you have any thoughts on how to proceed with a change to implement this? I've implemented such a mechanism in a testing framework I worked on previously; however, each element was its own object with its own 'required' flag, whereas in SitePrism, individual elements are simply instantiated as a method call to Capybara and held in the containing section/page as a string in the mapped_items array.

IMO each element should be its own object and respond to element#required?, which defaults to true but can be set to false for stateful elements.

Another way to avoid having to write @somepage = SomePageObject.new all over the place

I see at the end of the document you suggest a way to avoid having to instantiate page objects all over the place in your step defs. Here's what I do in Cucumber;-

module SomePageObjectWrapper

   def some_page
      @some_page ||= SomePageObjectWrapper::Page.new
   end
   class Page < SitePrism::Page
    etc
   end
end
# include our module in Cucumber World
World SomePageObjectWrapper

This adds the method 'some_page' to the Cucumber World object and it's then available in all your steps like so;-

Then(/^ I do something to some page$/) do
      some_page.do_something
end

New gem release?

Seems like there has been a fair amount added since the last version released to Rubygems (2.6 on Feb 11, 2014). Are you in a position to release any updates?

Thanks!

Section Containing Section

Hi,

I have a scenario where a section represents a feature for my site, and in turn it can have multiple sections. I see that in Site Prism the hierarchy is little different. Can we have this as a feature request? or is there any way I can achieve this?

Thanks,
Pushkar Pahare

Better API for url handling

Current SitePrism API for handling urls has a number of design issues:

  1. It's not possible to associate the same page object with several urls/url templates. It's a real situation as it's possible to point different routes to the same view
  2. Addressable provides a useful feature to check if url matches template. So it should be possible to get Addressable::Template from page object to match it easily with url using addressable's #match method. Note that this feature of Template makes set_url_matcher functionality unnecessary as it's possible to define url template that can be used both for loading and matching url of the page.
  3. It's good that Capybara allows to set app_host and it's not necessary to specify hostname when invoking visit after it. Therefore page object may contain relative url as its url: set_url '/path'. Also it would be good to have a way to check if current_url (i.e. absolute url matches url template from page object).

To handle all this I propose the following API (it seems to be backwards compatible from the first glance with existing API):

SitePrism::Page#url(key, expansion = {})  # returns expanded url [String]
SitePrism::Page#absolute_url(key, expansion = {}) # returns absolute url (it's combined using app_host and #url) [String]
SitePrism::Page::url_template(key)        # returns url template [Addressable::Template]
SitePrism::Page::url_templates            # returns array of url templates [Array<Addressable::Template>]
SitePrism::Page#load(key, expansion = {}) # loads expanded url

In the above definitions key is a key of url. It's an optional parameter if page object is associated with only one url. #load/#url would actually take *args as it needs to handle this.

Then this API can be used as:

class SearchPage < SitePrism::Page
  set_url '/search{?query*}'
end

# Open page number 50 in paginator
m = SearchPage.url_template.match(current_path) # current_path is "/search?q=testing&page=1"
m['query']['page'] = 50
SearchPage.new.load(m)  # loads "/search?q=testing&page=50"

# Check if correct url was opened after clicking Next button in paginator
m = SearchPage.url_template.match(current_path)
click_button 'Next'
m['query']['page'] = 2
expect(SearchPage.new.url(m)).to eq(current_path)

# Check if after clicking some button signup page matching particular url template is opened (expansion parameters aren't important):
class SignupPage
  set_url :key1, '/sign_up{?email}'
  set_url :key2, '/users/new'
end
SignupPage.new.load(:key1, email: '[email protected]')
expect(SignupPage.url_template(:key1)).to match(current_path) # I think it should work as RSpec doesn't seem to care about class - https://github.com/rspec/rspec-expectations/blob/master/lib/rspec/matchers/built_in/match.rb#L24

# Check if after clicking some button any signup page opened (don't care about matching particular url template)
expect(SignupPage.url_templates).to include(an_object_matching(current_path))

# Load the same page at different hostnames
class AnotherPage < SitePrism::Page
  set_url '/path'
end
Capybara.app_host = 'https://www.example.com'
AnotherPage.new.load # opens page via https
expect(AnotherPage.absolute_url).to match(current_url) # checks full url (and also that url scheme is https)
Capybara.app_host = 'http://www.example.com'
AnotherPage.new.load # opens page via http
expect(AnotherPage.absolute_url).to match(current_url) # checks full url (and also that url scheme is http)

Please approve.

Usage when submitting forms

Hi Nat!

I have a usage question that I couldn't see covered by the README.

Say you have a form, which redirects on a successful post, so there are two page object classes, NewWidget and ShowWidget.

Is the intention that site_prism automatically knows which page object class to use, based on the URL? Or do you need to be explicit? For example, is this the intended approach:

@new_widget_page = Pages::NewWidget.new
@new_widget_page.load
@new_widget_page.name = "foo"
@new_widget_page.submit

@show_widget_page = Pages::ShowWidget.new
@show_widget_page.load
expect(@show_widget_page).to be_displayed # verify the URL is correct
expect(@show_widget_page.message).to eq 'Widget created!'

all_there? should be recursive

Presently all_there? only checks if the direct child elements of a page or section are present on the page, but does not ensure that all required descendant elements of those child elements are also present.

Provide ability to set locator of root element in section class

Consider the following example (modified from SitePrism README):

class MenuSection < SitePrism::Section
end
class Home < SitePrism::Page
  section :menu, '#gbx3', MenuSection
end
class SearchResults < SitePrism::Page
  section :menu, '#gbx3', MenuSection
end

So #gbx3 should currently be specified in several places which isn't DRY.

IMO in many situations a section (i.e. menu/search bar/authentication form) is likely to have the same locator of root element that is specified in partial.

Therefore, I propose to add ability to do the following:

class Menu < SitePrism::Section
  set_root_locator '#menu'
end
class Home < SitePrism::Page
  section :menu, Menu
end
class SearchResults < SitePrism::Page
  section :menu, Menu
end

For example HTMLElements seem to also provide such ability - https://github.com/yandex-qatools/htmlelements#create-blocks-of-elements (using @Block(@FindBy(xpath = "//form")) annotation

If it will be approved I can do PR.

SitePrism::NoUrlMatcherForPage following documentation example

The documentation shows the following example:

class Account < SitePrism::Page
  set_url "/accounts/{id}{?query*}"
end

It goes onto say that the following test would pass:

@account_page = Account.new
@account_page.load(id: 22, query: { token: "ca2786616a4285bc" })

expect(@account_page.current_url).to end_with "/accounts/22?token=ca2786616a4285bc"
expect(@account_page).to be_displayed

I have a very similar setup I'm trying to test, and I am getting the following error:

Failure/Error: expect(index_page).to be_displayed
SitePrism::NoUrlMatcherForPage:
  SitePrism::NoUrlMatcherForPage
# /Users/gconzett/.gem/ruby/2.0.0/gems/site_prism-2.6/lib/site_prism/page.rb:14:in `displayed?'

The page object is as follows:

require_relative '../sections/widget_section.rb'          
require_relative '../sections/widget_fields_section.rb'   

class WidgetIndexPage < SitePrism::Page                    
  set_url '/widgets'                                      

  section :fields, WidgetFieldsSection, 'form'             
  sections :widgets, WidgetSection, 'tbody tr'    

  element :create_widget_button, '#create-widget'
end

And the spec itself has this simple assertion which fails:

feature 'Creating a widget' do      
  scenario 'with valid input' do             
    index_page = WidgetIndexPage.new 
    index_page.load                          
    expect(index_page).to be_displayed
  end
end

Unfortunately this leads to the above mentioned error. Do I have to define some kind of regex matcher? The coded leads me to believe that it should just use the string for the URL. Any info would be appreciated. Thanks!

Different SitePrism::Page instance.

I've try the following spec, but it didn't pass:

  it "should behave differently" do
    class MyPageWithUriTemplate < SitePrism::Page
      set_url "/users{/username}"
    end

    page_with_url_1 = MyPageWithUriTemplate.new
    page_with_url_2 = MyPageWithUriTemplate.new

    expect { page_with_url_1.load(username: 'foo') }.to_not raise_error
    expect { page_with_url_2.load(username: 'bar') }.to_not raise_error

    expect(page_with_url_1.current_url).to eq('http://www.example.com/users/foo')
    expect(page_with_url_2.current_url).to eq('http://www.example.com/users/bar')
  end

Isn't page_with_url_2.load(username: 'bar') shouldn't change page_with_url_1.current_url?

It's probably has something to do with Capybara::Session, I haven't had chance to dig deeper.

Add find section by element text method and find element with text method to page and section

Hi,
I plan add a method find_<sections_name>_by<element_name>and_<element_name> method to find the section with by element text.

Say the following code

class HomePage < SitePrism::Page
  sections :locations, Location, '.location-row'
end

class Location
  element :name, '.name'
  element :description, '.description'
end

home_page = HomePage.new
location_section = home_page.find_location_section_by_name_and_description('katar', 'nice place')

find_location_section_by_name_and_description will find the location section with name equal to katar and description equal to nice place and returns it.

So as, find_<element_name>_with_text( <expected_element_text> ) to find one element within elements based on the element text.

Say the following code

  class HomePage < SitePrism::Page
    elements :rows, '.row'
  end

  home_page = HomePage.new
  row = home_page.find_row_by_text('Okay!')

find_row_by_text will return a row with Okay! as text

Is it a good idea? Any suggestion for me?

Where should I store classes?

I'm probably missing something obvious but I don't see anything in the docs about where I should be storing classes.

wait for staleness

Hi natritmeyer,
I have an element that I wait for to exist in the DOM but then I want to wait for it to be removed afterwards. So far I call wait_for_element_name but then I need something like wait_until_element_name_stale. Please advise.

Thanks,
Alvin

site_prism not working with spork

I'm not sure whether this is an error in my setup, a bug with site_prism or one with spork, but I can't seem to run spork with site_prism. Seems that site_prism is assuming capybara has been required yet, but probably spork doesn't do that at that stage.

Running the cucumber or the rspec commands does not result in this error.

I can provide more information about my setup if needed.

Using RSpec, Rails
Preloading Rails environment
uninitialized constant SitePrism::Page::Capybara (NameError)
/home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/site_prism-2.4/lib/site_prism/page.rb:3:in <class:Page>' /home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/site_prism-2.4/lib/site_prism/page.rb:2:inmodule:SitePrism'
/home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/site_prism-2.4/lib/site_prism/page.rb:1:in <top (required)>' /home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/site_prism-2.4/lib/site_prism.rb:6:inrequire'
/home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/site_prism-2.4/lib/site_prism.rb:6:in <top (required)>' /home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/bundler-1.3.5/lib/bundler/runtime.rb:72:inrequire'
/home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/bundler-1.3.5/lib/bundler/runtime.rb:72:in block (2 levels) in require' /home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/bundler-1.3.5/lib/bundler/runtime.rb:70:ineach'
/home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/bundler-1.3.5/lib/bundler/runtime.rb:70:in block in require' /home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/bundler-1.3.5/lib/bundler/runtime.rb:59:ineach'
/home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/bundler-1.3.5/lib/bundler/runtime.rb:59:in require' /home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/bundler-1.3.5/lib/bundler.rb:132:inrequire'
/home/piru/Desktop/stuts/stuts-orga/config/application.rb:13:in <top (required)>' /home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/spork-rails-3.2.1/lib/spork/app_framework/rails.rb:49:inrequire'
/home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/spork-rails-3.2.1/lib/spork/app_framework/rails.rb:49:in preload_rails' /home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/spork-rails-3.2.1/lib/spork/app_framework/rails.rb:7:inpreload'
/home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/spork-1.0.0rc3/lib/spork/test_framework.rb:134:in block in preload' /home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/spork-1.0.0rc3/lib/spork.rb:62:inexec_prefork'
/home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/spork-1.0.0rc3/lib/spork/test_framework.rb:120:in preload' /home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/spork-1.0.0rc3/lib/spork/run_strategy/forking.rb:25:inpreload'
/home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/spork-1.0.0rc3/lib/spork/runner.rb:74:in run' /home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/spork-1.0.0rc3/lib/spork/runner.rb:10:inrun'
/home/piru/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/spork-1.0.0rc3/bin/spork:10:in `

'

blocks for element?

Hi Nat,

I've been looking a lot at site_prism and I like it. I'm planning to use it in a large suite of tests I'm starting work on. I was wondering, have you considered allowing blocks to be passed to element, elements, iframe etc? I'm thinking there might be good use-cases where you want to do something more complicated than just a css selector.
A few examples I can think of:

  • specifying an element that depends on the values of several others and would be difficult to find through css
    element :the_one_I_really_want do
        if foo
            ...
        elsif bar
            ...
        elsif baz and not qux
            ...
        else
            ....
        end
    end
  • if I really wanted to use xpath or another locator
    element( :using_xpath ) { find( :xpath, "//table/tr" ) }
  • possibly something where you need to execute_script:
    iframe :iframe_without_id, FooFrame do
        page.execute_script( "$('div#second iframe').attr('id','fooframe')" )
        'fooframe'
    end

There are ways one could do some of this with methods. Doing it using element would have the added benefit of allowing you to still use things like all_there? has_foo? etc. One of the goals for the tests I'm writing is to make the framework around them handle a lot of the non-trivial stuff so that testers with less coding experience can write tests for discovered bugs with as little in-depth knowledge as possible. I'm trying to hide the complex stuff in the page objects.

Anyway I was wondering, what are your thoughts on this? Thanks a lot!

Wrong number of arguments (0 for 1) when executing "visit"

Hello, I'm using Site-prism 1.0.3, Capybara 1.1.3 , Cucumber 1.2.1 and Poltergeist 1.0.1.

When I try to load the page, I get the following error message:

wrong number of arguments (0 for 1) (ArgumentError)
/home/rmartin/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/timeout.rb:98:in timeout' ./lib/utils/configs.rb:74:inblock in register_local_driver_poltergeist'
./lib/navigator.rb:15:in load_checkout_page' features/sanity.feature:13:inCuando llego a la pagina de checkout'

If I use Rspec in order to driver the execution instead of cucumber, the page gets loaded correctly, with the same gem versions installed.

The error seems to happen in page.rb:

raise SitePrism::NoUrlForPage if url.nil?
(rdb:1) n
/home/rmartin/.rvm/gems/ruby-1.9.3-p194@checkout-hotels-automation/gems/site_prism-1.3/lib/site_prism/page.rb:9
visit url
(rdb:1) n
/home/rmartin/.rvm/gems/ruby-1.9.3-p194@checkout-hotels-automation/gems/cucumber-1.2.1/lib/cucumber/core_ext/instance_exec.rb:70
rescue Exception => e

Thanks a lot!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.