I'm using SitePrism to test my web application. I have a number of classes that extend SitePrism::Page
and a number of often-used HTML snippets are represented by matching classes extending SitePrism::Section
class Login < SitePrism::Section
element :username, "#username"
element :password, "#password"
element :sign_in, "button"
end
class Home < SitePrism::Page
section :login, Login, "div.login"
end
The problem is, the application I'm working on is based on a CMS, in which a page can be assembled by selecting a Template based on pre-defined content and then drag-and-dropping any number of available components onto the page.
The initial developers created a Page Object to mirror every available Template. This was fine as long as the number of tests was low and there weren't too many variants of pages that we had to test in our feature files.
With the addition of multiple test cases, the page objects started growing at an alarming rate.
While we can easily mitigate code duplication by defining Sections for every component available in the CMS and reusing them across Page Objects, there's just a lot of properties that rarely get used.
class BlogPost < SitePrism::Page
section :logo, MySite::Components::Logo, '.logo'
section :navigation, MySite::Components::Navigation, '.primary-navigation'
section :header, MySite::Components::BlogHeader, '.header'
section :introduction, MySite::Components::Text, '.text .intro'
# and so on, a lot of dynamic staff that could potentially be dropped onto the page
# but does not neccessarily be there, going in dozens of lines
end
Is there a way in SitePrism to dynamically add a section to an instance of a Page Object as opposed to a whole class?
Then(/^Some step$/) do
@blog = PageObjects::BlogPost.new()
@blog.load("some url")
@blog.somehow_add_a_section_here_dynamically
expect (@blog.some_added_section).to be_visible
end
It also worries me that doing something like this would potentially cause CSS selectors to leak into the step definitions, which is generally a bad practice.
Another way to work around this would be to build Page Objects for specific examples of pages as opposed to the versatile templates. The Template Page Objects could just contain whatever's baked into the templates and be extended by other Page Objects that mirror specific pages, taking care of the differences. It sounds like a much cleaner approach so I'm probably going to write my tests this way
Anyway, the technical part of the question stands. Regardless of how good or bad an idea it is, how could I dynamically extend a page object with an additional section? I'm just curious.
I had at one point wanted to do what you're talking about for pretty much the same reason. We had pages that could have new content-sections dragged into them; making them very dynamic. I experimented with ways to do this and never found any that I particularly liked.
Methods like
element
andsections
in site-prism each define a number of methods for the class. You could callMyPage.section
in your test or add a method that callsself.class.section
and use that to add on new sections. But those will exist for all instances of that page; probably not what you want.You could alternatively tack them on to through the singleton_class:
But that's getting a bit ugly to toss into your tests, right?
I've long thought that Sections should have a default_locator (but tough to get patches accepted)
With that we could generalize this a bit:
And then you can use these as the parents.
In the tests:
On the other-hand it really might be easier to just create a few different variations of the page-object for use in tests. (Are you really going to test that many variations? Maybe..maybe not.)