Skip to content

Commit

Permalink
Merge pull request #1 from jdenen/enable-multiple-contexts
Browse files Browse the repository at this point in the history
Enable multiple contexts per page-object instance
  • Loading branch information
Johnson Denen committed Feb 21, 2014
2 parents f963016 + 7d33a6d commit e41d421
Show file tree
Hide file tree
Showing 16 changed files with 145 additions and 164 deletions.
78 changes: 32 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Mobilify
[![Gem Version](https://badge.fury.io/rb/mobilify.png)](http://badge.fury.io/rb/mobilify)

Mobilify allows you to create one page object and write one test for your web application, but execute different methods as part of that test given the context. Just change context when initializing your object.
Invoke one page-object method name but execute different definitions of that method based on the page-object's context.

We use it at [Manta](www.manta.com) to keep the number of test scripts low while testing against multiple versions of the same feature (legacy version, new desktop version, new mobile version, etc).

## Usage
To Mobilify your page objects, ```include Mobilify``` in the page class. For each method requiring a mobile replacement, create an element (or method) definition with your context prepended to the original's name (like `mobile_`).
To contextualize your page objects, ```include Mobilify``` in the page class. For each method requiring a contextual replacement, create an element (or method) definition with your context prepended to the original's name (like `mobile_` or `legacy_`).

```ruby
# method
Expand All @@ -20,66 +22,50 @@ Mobilify will replace called methods with their contextual counterparts if two c
my_page = Page.new(@browser, :context => :mobile)
```

To navigate to your page object during initialization, pass the key-value pair ```:visit => true``` to the constructor.
Mobilify also supports multiple contexts per page-object instance, invoking the first applicable context it matches in a given array.

```ruby
# visiting
my_page = Page.new(@browser, :visit => true)
my_page = Page.new(@browser, :visit => true, :context => :mobile)
# multiple context constructor
my_page = Page.new(@browser, :context => [:legacy, :mobile])
```

#### Example

You're testing a responsive page with a link to your registration form. But you can only identify the link with XPath, and the XPath changes between your desktop-sized application and your mobile-sized application.
I want to test a login page, but my there are basically three versions of the same page in some cycle of development. There's legacy code, new responsive code at the desktop size, and new responsive code at the mobile size. All three have different identifiers for the login form.

I'll use Mobilify to write one script and test all browsers and environments.

```ruby
# spec/page.rb
require 'mobilify'
# login.rb
require "mobilify"

class Page
class Login
include Mobilify

page_url "http://my-page.com"
text_field(:user, :id => "email")
text_field(:mobile_user, :id => "xs-email")
text_field(:legacy_user, :id => "member-email")

link(:to_registration, :xpath => '//xpath/to/desktop/register/link')
link(:mobile_to_registration, :xpath => '//xpath/to/mobile/register/link')
end
```

```ruby
# spec/spec_helper.rb
require 'watir-webdriver'
require 'webdriver-user-agent'

RSpec.configure do |config|
text_field(:password, :id => "password")
text_field(:mobile_password, :id => "xs-password")
text_field(:legacy_password, :id => "member-password")

config.before :all do
case ENV['BROWSER']
when 'desktop'
@browser = Watir::Browser.new :firefox
@my_context = :desktop
when 'mobile'
driver = Webdriver::UserAgent.driver(:browser => :firefox, :agent => :iphone)
@browser = Watir::Browser.new driver
@my_context = :mobile
end
end
button(:submit, :id => "submit")
end
```

```ruby
# spec/registration_link_spec.rb
require 'spec_helper'
require 'page'

describe Page do
let(:page) { Page.new(@browser, :context => @my_context, :visit => true) }

describe "#to_registration" do
it "takes me to the registration form" do
page.to_registration
@browser.url.should == "http://my-page.com/registration"
end
end
# login_spec.rb
require "rspec-given"
require "support/login"

describe "logging in" do
Given(:login) { Login.new(@browser, :context => [:mobile, :legacy]) }
When { login.goto }
When { login.user = "member@example.com" }
When { login.password = "password123" }
When { login.submit }
Then { ... }
end
```

Expand Down
20 changes: 0 additions & 20 deletions features/desktop_link.feature

This file was deleted.

20 changes: 0 additions & 20 deletions features/mobile_link.feature

This file was deleted.

27 changes: 0 additions & 27 deletions features/step_definitions/link_steps.rb

This file was deleted.

14 changes: 0 additions & 14 deletions features/step_definitions/navigation_step.rb

This file was deleted.

6 changes: 0 additions & 6 deletions features/support/env.rb

This file was deleted.

3 changes: 0 additions & 3 deletions features/support/hooks.rb

This file was deleted.

10 changes: 0 additions & 10 deletions features/support/manta_page.rb

This file was deleted.

32 changes: 18 additions & 14 deletions lib/mobilify.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'mobilify/version'
require 'page-object'

module Mobilify
Expand All @@ -6,7 +7,7 @@ module Mobilify
def initialize(browser, opts = {})
super(browser, opts[:visit] || false)
@context = opts[:context]
mobilify! if context?
mobilify! if @context
end

def self.included(klass)
Expand All @@ -17,20 +18,23 @@ def context?
@context
end

private

def mobilify!
context = @context.to_s

methods.
select { |m| m.to_s.start_with? "#{context}_" }.
select { |m| respond_to? m.to_s.gsub("#{context}_", '') }.
map { |m| self.method(m) }.each do |method|
(class << self; self; end).class_eval do
define_method(method.name.to_s.gsub("#{context}_", ''), method)
match = false
(contexts = []) << @context

contexts.flatten.each do |c|
next if match
context = c.to_s
methods.
select { |m| m.to_s.start_with? "#{context}_" }.
select { |m| respond_to? m.to_s.gsub("#{context}_", '') }.
map { |m| self.method(m) }.each do |method|
(class << self; self; end).class_eval do
define_method(method.name.to_s.gsub("#{context}_", ''), method)
match = true
end
end
end
end
end
end

require 'mobilify/version'
end
2 changes: 1 addition & 1 deletion lib/mobilify/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Mobilify
VERSION = "1.1.1"
VERSION = "1.2.0"
end
7 changes: 4 additions & 3 deletions mobilify.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
spec.version = Mobilify::VERSION
spec.authors = ["Johnson Denen"]
spec.email = ["jdenen@manta.com"]
spec.description = %q{switch seamlessly between page-object methods when testing your web app in different contexts}
spec.summary = %q{switch seamlessly between page-object methods when testing your web app in different contexts}
spec.description = %q{page-object methods invoked with one call but defined contextually}
spec.summary = %q{page-object methods invoked with one call but defined contextually}
spec.homepage = "http://github.com/jdenen/mobilify"
spec.license = "MIT"

Expand All @@ -24,5 +24,6 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "rake"
spec.add_development_dependency "watir-webdriver"
spec.add_development_dependency "webdriver-user-agent"
spec.add_development_dependency "cucumber"
spec.add_development_dependency "rspec"
spec.add_development_dependency "rspec-given"
end
29 changes: 29 additions & 0 deletions spec/multiple_context_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'spec_helper'

describe Mobilify do
after :each do
@browser.close
end

describe "uses default method when given contexts do not apply" do
Given(:desktop) { KhanAcademy.new(desktop_browser, { :visit => true, :context => [:no_match, :wrong] }) }
Then { desktop.login_element.inspect.should include("log-in-link ellipsis highlight") }
end

describe "uses only applicable context given" do
context "when applicable context is first in the array" do
Given(:mobile) { KhanAcademy.new(mobile_browser, { :visit => true, :context => [:mobile, :wrong] }) }
Then { mobile.login_element.inspect.should include("simple-button big-button") }
end

context "when applicable context is second in the array" do
Given(:mobile) { KhanAcademy.new(mobile_browser, { :visit => true, :context => [:wrong, :mobile] }) }
Then { mobile.login_element.inspect.should include("simple-button big-button") }
end
end

describe "uses first context given when multiple applicable contexts are given" do
Given(:mobile) { KhanAcademy.new(mobile_browser, { :visit => true, :context => [:mobile, :ignored] }) }
Then { mobile.login_element.inspect.should include("simple-button big-button") }
end
end
11 changes: 11 additions & 0 deletions spec/page_object_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require 'spec_helper'

describe Mobilify do

describe "includes PageObject when included" do
Given { class Page; end }
When { Page.send :include, Mobilify }
Then { Page.included_modules[1].should == PageObject }
end

end
23 changes: 23 additions & 0 deletions spec/simple_context_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require 'spec_helper'

describe Mobilify do
after :each do
@browser.close
end

describe "uses default method when no context is given" do
Given(:desktop) { KhanAcademy.new(desktop_browser, { :visit => true }) }
Then { desktop.login_element.inspect.should include("log-in-link ellipsis highlight") }
end

describe "uses default method when non-applicable context is given" do
Given(:desktop) { KhanAcademy.new(desktop_browser, { :visit => true, :context => :wrong }) }
Then { desktop.login_element.inspect.should include("log-in-link ellipsis highlight") }
end

describe "uses contextual method for the given context" do
Given(:mobile) { KhanAcademy.new(mobile_browser, { :context => :mobile, :visit => true }) }
Then { mobile.login_element.inspect.should include("simple-button big-button") }
end
end

15 changes: 15 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require 'rspec'
require 'rspec-given'
require 'watir-webdriver'
require 'webdriver-user-agent'
require './lib/mobilify'
require 'support/khan_academy'

def desktop_browser
@browser = Watir::Browser.new :firefox
end

def mobile_browser
device = Webdriver::UserAgent.driver(:browser => :firefox, :agent => :iphone)
@browser = Watir::Browser.new device
end
12 changes: 12 additions & 0 deletions spec/support/khan_academy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require "./lib/mobilify"

class KhanAcademy
include Mobilify

page_url "http://www.khanacademy.org"

link(:login, :class => "log-in-link ellipsis highlight")
link(:mobile_login, :class => "simple-button big-button")
link(:ignored_login, :class => "simple-button primary big-button")
end

0 comments on commit e41d421

Please sign in to comment.