Skip to content

kfamilonidis/page_magic

 
 

Repository files navigation

#PageMagic PageMagic is an API for testing web applications.

It has a simple but powerful DSL which makes modelling and interacting with your pages easy.

Wouldn't it be great if there was a framework that could:

  • Model your pages
  • Define custom wait activity that should occur before or after you interact with a page element at the point the element is defined
  • Map the paths to pages so that when you transition from one page to another, you don't have to do awkward things to work out which page object you need to use next?
  • Be really really dynamic so that you could do even more things at runtime?

Well PageMagic might just be the answer!

Give it a try and let us know what you think! It's there will undoubtedly be things that can be improved and issues that we are not aware of so your feedback/pull requests are greatly appreciated!

Installation

gem install page_magic --pre

An Example

Imagine the scene. You've written a web based mail client and now you want to test it... You have a scenario in mind that goes something along the lines of:

  • Send yourself an email with a unique subject
  • Go to the Login page and login
  • Find the message using it's unique subject and read it
  • delete the message

You're mail client is total 21st century so there is loads of lovely ajax etc...

Writing robust, nice looking code for this could be a real pain... Here's how you might do it with PageMagic (note that the following code would work if we you had a compatible mail web app but in this case is purely illustrative)

What we really want to write is something like

test_subject = send_test_mail('[email protected]')
#Visit your site using a PageMagic session we prepared earlier
session.visit(LoginPage, url: 'https://21st-century-mail.com')

#Login using some handy helper method on our page object
session.login('username', 'password')

#Find the message amongst all the other messages that are on screen and read it
session.message(subject: test_subject).read.click

#Now we are on the message screen lets delete it without having to worry about the ajax.
session.delete_message

fail "message is still there!" if session.message(subject: test_subject).exists?

# Sweet :)

Starting a session

To start a PageMagic session simply decide what browser you want to use and pass it to PageMagic's .session method

session = PageMagic.session(browser: :chrome)

Out of the box, PageMagic knows how to work with:

  • Chrome and Firefox
  • poltergeist
  • RackTest - Read more on testing rack compliant object's directly later on

Under the hood, PageMagic is using Capybara so you can register any Capybara specific driver you want. See below for how to do this.

Note: We don't want to impose particular driver versions so PageMagic does not list any as dependencies. Therefore you will need add the requiste gem to your Gemfile.

Defining Pages

To define something that PageMagic can work with, simply include PageMagic in to a class. Here are the classes we would need for the example above.

class LoginPage
  include PageMagic
end

class MailBox
  include PageMagic
end

class MessageView
  include PageMagic
end

Visiting a page

To use a page ojbect you need to 'visit' it.

session.visit(LoginPage, url: 'https://21st-century-mail.com')

Note: soon you won't even have to specify the page class :)

##Defining elements Your pages are going to have elements on them that you will want to interact with. In the case of the Login page, it's easy to imagine that it will have text fields for a username and password and a button to login in with.

class LoginPage
  include PageMagic
  text_field(:username, label: 'username')
  text_field(:password, label: 'password')
  button(:login_button, text: 'login')
end

##Interacting with elements Elements are defined with a id which is the name of the method you will use to reference it. In the above example, the textfields and button were defined with the id's, :username, :password, and :login_button

After visiting a page with a PageMagic session, you can access all of the elements of that page through the session itself.

session.username.set '[email protected]'
session.password.set 'passw0rd'
session.login_button.click

##Defining helper methods Using elements that are defined on a page is great, but if you are enacting a procedure through interacting with a few of them then your code could end up with some pretty repetitive code. In this case you can define helper methods instead.

In the above [example](#an example) we used a helper called login.

class LoginPage
  # ... code defining elements as shown above
  
  def login(user, pass)
    username.set user
    password.set pass
    login_button.click
  end
end

We can interact with helper in the same way as we did page elements.

session.login('joe', 'blogs')

##Defining sub elements If your pages are complex you can use PageMagic to compose pages, their elements and subelements to as many levels as you need to.

In the example we accessed a read link that resided with a particular message

class MailBox
  include PageMagic
  
  element :message, id: 'message_id' do
    link(:read, text: 'read')
  end
end

Sub elements can be accessed through their parent elements e.g:

session.message.read.click

Dynamic Selectors

In our scenario we actually selected a message based on a subject that was randomly generated. In this case we would not be able to hard code the selector for our message but instead would need to set the selector dynamically.

class MailBox
  include PageMagic
  
  element :message do |subject:|
    selector xpath: '//tr[text()="#{subject}"]' 
    link(:read, text: 'read')
  end
end

Here we have defined the 'message' element using a block that takes subject argument. This is passed in at run time and given to the xpath selector.

session.message(subject: 'test message')

Interaction hooks

Frequently, you are going to have to work with pages that make heavy use of ajax. This means that just because you've clicked something, it doesn't mean that the action is finished. For these occasions PageMagic provides before and after hooks that you use to perform custom actions and wait for things to happen. In the case of our web based mail client, we could imagine that when deleting the email, a fancy spinner is displayed whilst the application sends an ajax request to have the message deleted. In this case we wouldn't want to proceed until this has disappeared.

class MessagePage
  include PageMagic
  ## code defining other elements, such as subject and body
  
  link(:delete, id: 'delete-message') do
    after do
      wait_until{fancy_animation_has_disappeared?}
    end
  end
end

Page Mapping

You will have noticed that, that we have been performing actions that would move us from page to page but have not done anything to tell PageMagic to use the MailBox or MessagePage. With PageMagic you can map which pages should be used to handle which URL paths. This is a pretty killer feature that will remove a lot of the juggling and bring back fluency to your code!

# define what pages map to what
browser.define_page_mappings %r{/messages/\d+} => MessagePage,
                             '/login' => LoginPage,
                             '/' => MailBox

You can use even use regular expressions to map multiple paths to the same page. In the above example we are mapping paths that that starts with '/messages/' and are followed by one ore more digits to the MessagePage class.

##What else can you do with PageMagic? PageMagic has lots of other useful features. I'm writing up the documentation so check back here soon!

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Ruby 100.0%