How to add a section to a SitePrism page object dynamically?

对着背影说爱祢 提交于 2019-12-05 16:42:26

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 and sections in site-prism each define a number of methods for the class. You could call MyPage.section in your test or add a method that calls self.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:

my_page = MyPage.new
my_page.singleton_class.section(:new_section, NewSection, '#foo')

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:

class DynamicSection < SitePrism::Section
  def self.set_default_locator(locator)
    @default_locator = locator
  end
  def self.default_locator
    @default_locator
  end
end      

class DynamicPage < SitePrism::Page
  # add sections (and related methods) to this instance of the page
  def include_sections(*syms)
    syms.each do |sym|
      klass = sym.to_s.camelize.constantize
      self.singleton_class.section(sym, klass, klass.default_locator)
    end
  end
end

And then you can use these as the parents.

class FooSection < DynamicSection
  set_default_locator '#foo'
  element :username, "#username"
end

class BlogPostPage < DynamicPage
  # elements that exist on every BlogPost
end

In the tests:

@page = BlogPostPage.new
@page.include_sections(:foo_section, :bar_section)
expect(@page.foo_section).to be_visible

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.)

You can add a section to just a page object instance by modifying its singleton class.

Then(/^Some step$/) do
  @blog = PageObjects::BlogPost.new
  @blog.load("some url")

  # You can see that @blog does not have the logo section
  expect(@blog).not_to respond_to(:logo)

  # Add a section to just the one instance of BlogPost
  class << @blog
    section(:logo, MySite::Components::Logo, '.logo')
  end

  # You can now see that #blog has the logo section
  expect(@blog).to respond_to(:logo)
end

This will likely result in duplicate the section definition in multiple steps. To address this, you could create a method within the BlogPost to dynamically add the specified sections.

In the following BlogPost class, a dictionary of available components is created. The class has a method that adds components based on the dictionary definition.

class BlogPost < SitePrism::Page
  COMPONENT_DICTIONARY = {
    logo: {class: MySite::Components::Logo, selector: '.logo'},
    navigation: {class: MySite::Components::Navigation, selector: '.primary-navigation'},
    header: {class: MySite::Components::BlogHeader, selector: '.header'}
  }

  def add_components(*components)
    Array(components).each do |component|
      metaclass = class << self; self; end
      metaclass.section(component, COMPONENT_DICTIONARY[component][:class], COMPONENT_DICTIONARY[component][:selector])
    end
  end
end

As an example of the usage:

# Create a blog post that just has the logo section
@blog = BlogPost.new
@blog.add_components(:logo)

# Create a blog post that has the navigation and header section
@blog2 = BlogPost.new
@blog2.add_components(:navigation, :header)

# Notice that each blog only has the added components
expect(@blog).to respond_to(:logo)
expect(@blog).not_to respond_to(:navigation)
expect(@blog).not_to respond_to(:header)

expect(@blog2).not_to respond_to(:logo)
expect(@blog2).to respond_to(:navigation)
expect(@blog2).to respond_to(:header)

Use page.find for that purpose

class MyPage < SitePrism::Page
  element :static_selector_element, "#some-static-id"
  def dynamic_element(id)
    find "label[for=\"dynamic-value-#{id}\"]"
  end
end

in your test:

RSpec.feature 'My Feature' do
  scenario 'Success' do
    p = MyPage.new 
    p.visit '/'
    p.static_selector_element.click
    p.dynamic_element(SomeObject.fist.id).click
  end
end
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!