capybara-pageobject 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rvmrc +81 -0
- data/Rakefile +7 -0
- data/capybara-pageobject.gemspec +7 -3
- data/lib/capybara-pageobject/action.rb +15 -0
- data/lib/capybara-pageobject/attribute.rb +29 -0
- data/lib/capybara-pageobject/capybara_helper.rb +15 -0
- data/lib/capybara-pageobject/element.rb +24 -0
- data/lib/capybara-pageobject/page.rb +49 -0
- data/lib/capybara-pageobject/version.rb +2 -2
- data/lib/capybara-pageobject/website.rb +31 -0
- data/lib/capybara-pageobject.rb +36 -2
- data/lib/monkey-patch/object.rb +13 -0
- data/lib/monkey-patch/string.rb +16 -0
- data/spec/action_spec.rb +19 -0
- data/spec/attribute_spec.rb +33 -0
- data/spec/capybara-pageobject_spec.rb +38 -0
- data/spec/element_spec.rb +26 -0
- data/spec/page_spec.rb +104 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/support/matchers/delegate.rb +51 -0
- data/spec/support/pages/pages.yml +7 -0
- data/spec/support/test_website.rb +70 -0
- data/spec/website_spec.rb +51 -0
- metadata +79 -7
data/.gitignore
CHANGED
data/.rvmrc
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
+
# development environment upon cd'ing into the directory
|
5
|
+
|
6
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
|
7
|
+
environment_id="ruby-1.9.3-p0@capybara-pageobject"
|
8
|
+
|
9
|
+
#
|
10
|
+
# Uncomment the following lines if you want to verify rvm version per project
|
11
|
+
#
|
12
|
+
# rvmrc_rvm_version="1.10.1" # 1.10.1 seams as a safe start
|
13
|
+
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
|
14
|
+
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
|
15
|
+
# return 1
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
|
19
|
+
#
|
20
|
+
# Uncomment following line if you want options to be set only for given project.
|
21
|
+
#
|
22
|
+
# PROJECT_JRUBY_OPTS=( --1.9 )
|
23
|
+
#
|
24
|
+
# The variable PROJECT_JRUBY_OPTS requires the following to be run in shell:
|
25
|
+
#
|
26
|
+
# chmod +x ${rvm_path}/hooks/after_use_jruby_opts
|
27
|
+
#
|
28
|
+
|
29
|
+
#
|
30
|
+
# First we attempt to load the desired environment directly from the environment
|
31
|
+
# file. This is very fast and efficient compared to running through the entire
|
32
|
+
# CLI and selector. If you want feedback on which environment was used then
|
33
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
34
|
+
#
|
35
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
|
36
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
37
|
+
then
|
38
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
39
|
+
|
40
|
+
if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
|
41
|
+
then
|
42
|
+
. "${rvm_path:-$HOME/.rvm}/hooks/after_use"
|
43
|
+
fi
|
44
|
+
else
|
45
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
46
|
+
if ! rvm --create "$environment_id"
|
47
|
+
then
|
48
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
49
|
+
return 1
|
50
|
+
fi
|
51
|
+
fi
|
52
|
+
|
53
|
+
#
|
54
|
+
# If you use an RVM gemset file to install a list of gems (*.gems), you can have
|
55
|
+
# it be automatically loaded. Uncomment the following and adjust the filename if
|
56
|
+
# necessary.
|
57
|
+
#
|
58
|
+
# filename=".gems"
|
59
|
+
# if [[ -s "$filename" ]]
|
60
|
+
# then
|
61
|
+
# rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
|
62
|
+
# fi
|
63
|
+
|
64
|
+
# If you use bundler, this might be useful to you:
|
65
|
+
# if [[ -s Gemfile ]] && ! command -v bundle >/dev/null
|
66
|
+
# then
|
67
|
+
# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
|
68
|
+
# gem install bundler
|
69
|
+
# fi
|
70
|
+
# if [[ -s Gemfile ]] && command -v bundle
|
71
|
+
# then
|
72
|
+
# bundle install
|
73
|
+
# fi
|
74
|
+
|
75
|
+
if [[ $- == *i* ]] # check for interactive shells
|
76
|
+
then
|
77
|
+
echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
|
78
|
+
else
|
79
|
+
echo "Using: $GEM_HOME" # don't use colors in interactive shells
|
80
|
+
fi
|
81
|
+
|
data/Rakefile
CHANGED
data/capybara-pageobject.gemspec
CHANGED
@@ -4,12 +4,12 @@ require "capybara-pageobject/version"
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "capybara-pageobject"
|
7
|
-
s.version = Capybara::
|
7
|
+
s.version = Capybara::PageObject::VERSION
|
8
8
|
s.authors = ["dlewis"]
|
9
9
|
s.email = ["deepak.lewis@gmail.com"]
|
10
10
|
s.homepage = ""
|
11
11
|
s.summary = %q{Easily create page objects to abstract UI Pages}
|
12
|
-
s.description = %q{
|
12
|
+
s.description = %q{Introduce page objects to your capybara-based functional tests}
|
13
13
|
|
14
14
|
s.rubyforge_project = "capybara-pageobject"
|
15
15
|
|
@@ -19,6 +19,10 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
21
|
# specify any dependencies here; for example:
|
22
|
-
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
s.add_development_dependency "mocha"
|
24
|
+
s.add_development_dependency("sinatra", [">= 0.9.4"])
|
25
|
+
s.add_development_dependency "capybara"
|
26
|
+
|
23
27
|
s.add_runtime_dependency("capybara", [">= 1.0.0"])
|
24
28
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'capybara/dsl'
|
2
|
+
|
3
|
+
module Capybara
|
4
|
+
module PageObject
|
5
|
+
class Attribute < Element
|
6
|
+
include Capybara::DSL
|
7
|
+
|
8
|
+
def_delegators :element_value, :blank?, :==, :include?
|
9
|
+
def_delegators :element, :set
|
10
|
+
|
11
|
+
def validation_error
|
12
|
+
error_field = @page.all(".field_with_errors").find do |error_field|
|
13
|
+
Capybara.using_wait_time(0) { error_field.has_selector?(@selector) }
|
14
|
+
end
|
15
|
+
error_field.find(".error_message").text if error_field
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"'attribute: #{@name}'"
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def element_value
|
25
|
+
element.tag_name == "input" ? element.value : element.text
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Capybara
|
2
|
+
module PageObject
|
3
|
+
class Element
|
4
|
+
extend Forwardable
|
5
|
+
include CapybaraHelper
|
6
|
+
|
7
|
+
def initialize page, name, selector
|
8
|
+
@page = page
|
9
|
+
@name = name
|
10
|
+
@selector = selector
|
11
|
+
end
|
12
|
+
|
13
|
+
def visible?
|
14
|
+
if_absent(false) { element.visible? }
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def element
|
20
|
+
@page.find(@selector)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Capybara
|
2
|
+
module PageObject
|
3
|
+
class Page
|
4
|
+
include CapybaraHelper
|
5
|
+
|
6
|
+
attr_accessor :context
|
7
|
+
|
8
|
+
def initialize page, page_data
|
9
|
+
@page = page
|
10
|
+
@page_data = page_data
|
11
|
+
page_data["attributes"].present? and page_data["attributes"].each do |attribute, selector|
|
12
|
+
self.class.send(:define_method, attribute) do |value=nil|
|
13
|
+
Capybara::PageObject::Attribute.new(page, attribute, selector).tap { |node| node.set(value) if value }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
page_data["actions"].present? and page_data["actions"].each do |attribute, selector|
|
18
|
+
self.class.send(:define_method, attribute) { Action.new(page, attribute, selector) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def visit
|
23
|
+
@page_data["url"] ? page.visit(@page_data["url"]) : raise("url not defined for page")
|
24
|
+
end
|
25
|
+
|
26
|
+
def visible?
|
27
|
+
@page_data["id"] ? if_absent(false) { page.find(@page_data["id"]).visible? } : raise("id not defined for page")
|
28
|
+
end
|
29
|
+
|
30
|
+
def page_title
|
31
|
+
if_absent("") { page.find("head title").text }
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
"'page: #{@page_data["name"]}'"
|
36
|
+
end
|
37
|
+
|
38
|
+
def method_missing method, *args
|
39
|
+
page.respond_to?(method) ? page.send(method, *args) : context.send(method, *args)
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def page
|
45
|
+
@page
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Capybara
|
2
|
+
module PageObject
|
3
|
+
class Website
|
4
|
+
|
5
|
+
def initialize page, context, page_file
|
6
|
+
@page = page
|
7
|
+
@context = context
|
8
|
+
|
9
|
+
raise "Please specify page file path" unless page_file.present?
|
10
|
+
@pages_data = YAML.load_file(page_file)
|
11
|
+
|
12
|
+
@pages_data.each do |page, page_data|
|
13
|
+
page_class = (page_data["class"] || "Capybara::PageObject::Page").constantize
|
14
|
+
unless page_class.ancestors.include?(Capybara::PageObject::Page)
|
15
|
+
raise "Custom page class '#{page_class}' should extend Capybara::PageObject::Page"
|
16
|
+
end
|
17
|
+
wrapper = lambda { |&page_actions| on_page_perform(page_actions) { page_class.new(@page, page_data.merge("name" => page)) } }
|
18
|
+
self.class.send(:define_method, page, wrapper)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def on_page_perform(page_actions)
|
25
|
+
page = yield
|
26
|
+
page.context = @context
|
27
|
+
page_actions ? page.instance_eval(&page_actions) : page
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/capybara-pageobject.rb
CHANGED
@@ -1,7 +1,41 @@
|
|
1
1
|
require "capybara-pageobject/version"
|
2
|
+
require "monkey-patch/string"
|
3
|
+
require "monkey-patch/object"
|
2
4
|
|
3
5
|
module Capybara
|
4
|
-
module
|
5
|
-
|
6
|
+
module PageObject
|
7
|
+
autoload :Page, 'capybara-pageobject/page'
|
8
|
+
autoload :CapybaraHelper, 'capybara-pageobject/capybara_helper'
|
9
|
+
autoload :Element, 'capybara-pageobject/element'
|
10
|
+
autoload :Attribute, 'capybara-pageobject/attribute'
|
11
|
+
autoload :Action, 'capybara-pageobject/action'
|
12
|
+
autoload :Website, 'capybara-pageobject/website'
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def configure
|
16
|
+
yield self
|
17
|
+
end
|
18
|
+
|
19
|
+
def page_file= file
|
20
|
+
@page_file = file
|
21
|
+
end
|
22
|
+
|
23
|
+
def current_website
|
24
|
+
@website ||= Capybara::PageObject::Website.new(Capybara.current_session, self, @page_file)
|
25
|
+
end
|
26
|
+
|
27
|
+
def website_class= klass
|
28
|
+
raise "website class #{klass} should extend Capybara::PageObject::Website" unless klass.ancestors.include?(Capybara::PageObject::Website)
|
29
|
+
@website = klass.new(Capybara.current_session, self, @page_file)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module IncludedMethods
|
34
|
+
def website
|
35
|
+
Capybara::PageObject.current_website
|
36
|
+
end
|
37
|
+
end
|
6
38
|
end
|
7
39
|
end
|
40
|
+
|
41
|
+
include Capybara::PageObject::IncludedMethods
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module MonkeyPatch
|
2
|
+
module String
|
3
|
+
def constantize
|
4
|
+
names = self.split('::')
|
5
|
+
names.shift if names.empty? || names.first.empty?
|
6
|
+
|
7
|
+
constant = Object
|
8
|
+
names.each do |name|
|
9
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
10
|
+
end
|
11
|
+
constant
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
String.send(:include, MonkeyPatch::String)
|
data/spec/action_spec.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "action" do
|
4
|
+
def action(selector)
|
5
|
+
Capybara::PageObject::Action.new(capybara_page, "action", selector)
|
6
|
+
end
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
capybara_page.visit("/form")
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "delegators" do
|
13
|
+
subject { action("#register_submit") }
|
14
|
+
it { should delegate(:click).to(:element) }
|
15
|
+
end
|
16
|
+
|
17
|
+
it { action("#disabled_button").should be_disabled }
|
18
|
+
it { action("#attr1").to_s.should == "'action: action'" }
|
19
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Attribute" do
|
4
|
+
def attribute(selector)
|
5
|
+
Capybara::PageObject::Attribute.new(capybara_page, "attr", selector)
|
6
|
+
end
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
capybara_page.visit("/form")
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "read value" do
|
13
|
+
it { attribute("#attr1").should == "led zeppelin" }
|
14
|
+
it { attribute("#field1").should == "Creedence Rlearwater Revival" }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "delegators" do
|
18
|
+
subject { attribute("#field1") }
|
19
|
+
it { should delegate(:include?).to(:element_value) }
|
20
|
+
it { should delegate(:blank?).to(:element_value) }
|
21
|
+
it { should delegate(:==).to(:element_value) }
|
22
|
+
it { should delegate(:set).to(:element).with_arguments("foo.bar") }
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "validation_error" do
|
26
|
+
it "should extract validation error from form" do
|
27
|
+
capybara_page.visit("/form_with_rails_validation_errors")
|
28
|
+
attribute("#user_email").validation_error.should == "can't be blank"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it { attribute("#attr1").to_s.should == "'attribute: attr'" }
|
33
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Capybara::PageObject" do
|
4
|
+
describe "current_website" do
|
5
|
+
it "should pass page file to the website constructor" do
|
6
|
+
Capybara::PageObject.page_file = "foo.bar"
|
7
|
+
YAML.expects(:load_file).with('foo.bar').returns({})
|
8
|
+
Capybara::PageObject.current_website
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should memoize website object" do
|
12
|
+
Capybara::PageObject.current_website.should be_equal Capybara::PageObject.current_website
|
13
|
+
Capybara::PageObject.current_website.should be_equal Capybara::PageObject.current_website
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "website_class" do
|
18
|
+
before {
|
19
|
+
Capybara::PageObject.page_file = "blah"
|
20
|
+
YAML.stubs(:load_file).returns({})
|
21
|
+
}
|
22
|
+
|
23
|
+
it "should fail if website_class does not extend Website" do
|
24
|
+
expect { Capybara::PageObject.website_class = Object }.to raise_error(Exception, "website class Object should extend Capybara::PageObject::Website")
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should instantiate website class if provided" do
|
28
|
+
class StubWebsite < Capybara::PageObject::Website
|
29
|
+
end
|
30
|
+
Capybara::PageObject.website_class = StubWebsite
|
31
|
+
Capybara::PageObject.current_website.class.should == StubWebsite
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should create website method in including class" do
|
35
|
+
website.should be
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe "Element" do
|
5
|
+
def element(selector)
|
6
|
+
Capybara::PageObject::Element.new(capybara_page, "attr", selector)
|
7
|
+
end
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
capybara_page.visit("/form")
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "visible?" do
|
14
|
+
context "attribute" do
|
15
|
+
it { element("#attr1").should be_visible }
|
16
|
+
it { element("#hidden_attr").should_not be_visible }
|
17
|
+
it { element("#does_not_exist").should_not be_visible }
|
18
|
+
end
|
19
|
+
|
20
|
+
context "field" do
|
21
|
+
it { element("#field1").should be_visible }
|
22
|
+
it { element("#hidden_field").should_not be_visible }
|
23
|
+
it { element("#does_not_exist").should_not be_visible }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/spec/page_spec.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Page" do
|
4
|
+
def page_object(page_data)
|
5
|
+
Capybara::PageObject::Page.new capybara_page, page_data
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "on form page" do
|
9
|
+
let(:page_data) { {
|
10
|
+
"url" => "/form",
|
11
|
+
"attributes" => {
|
12
|
+
"attr1" => "#attr1",
|
13
|
+
"attr2" => "#hidden_attr",
|
14
|
+
"field1" => "#field1",
|
15
|
+
"field2" => "#hidden_field"
|
16
|
+
},
|
17
|
+
"actions" => {
|
18
|
+
"action1" => "#register_submit",
|
19
|
+
"action2" => "#disabled_button",
|
20
|
+
}
|
21
|
+
} }
|
22
|
+
|
23
|
+
before { capybara_page.visit("/form") }
|
24
|
+
let(:page) { page_object(page_data) }
|
25
|
+
subject { page }
|
26
|
+
|
27
|
+
describe "attributes" do
|
28
|
+
its(:attr1) { should == "led zeppelin" }
|
29
|
+
its(:attr2) { should == "the doors" }
|
30
|
+
|
31
|
+
it "should be able to use getter to also set attribute value" do
|
32
|
+
page.field1 "some_value"
|
33
|
+
page.field1.should == "some_value"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "actions" do
|
38
|
+
its(:action1) { should be_visible }
|
39
|
+
its(:action2) { should be_disabled }
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "responds_to" do
|
43
|
+
it { should respond_to(:attr1) }
|
44
|
+
it { should respond_to(:field1) }
|
45
|
+
it { should respond_to(:action1) }
|
46
|
+
it { should respond_to(:action2) }
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "method_missing" do
|
50
|
+
it "should delegate to capybara_page if it has method" do
|
51
|
+
page_object({}).find_by_id("attr1")
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should raise method missing if both capybar_page and itself don't have method'" do
|
55
|
+
expect { page_object({}).does_not_exist }.to raise_error(NoMethodError, /undefined method `does_not_exist'/)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "visit" do
|
61
|
+
it "should go to page mentioned in url" do
|
62
|
+
page_object({"url" => "/form_with_rails_validation_errors"}).visit
|
63
|
+
capybara_page.should have_content "Email Address"
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should fail if url is not specified" do
|
67
|
+
expect { page_object({}).visit }.to raise_error(Exception, "url not defined for page")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "visible?" do
|
72
|
+
it "should return true if id element is visible" do
|
73
|
+
page = page_object({"url" => "/form_with_rails_validation_errors", "id" => "#registration-form"})
|
74
|
+
page.visit
|
75
|
+
page.should be_visible
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should return false if id element is not present" do
|
79
|
+
page = page_object({"url" => "/form_with_rails_validation_errors", "id" => "#does-not-exist"})
|
80
|
+
page.visit
|
81
|
+
page.should_not be_visible
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should fail if id is not specified" do
|
85
|
+
expect { page_object({}).visible? }.to raise_error(Exception, "id not defined for page")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "page_title" do
|
90
|
+
it "should return page title if page has title in head" do
|
91
|
+
page = page_object({"url" => "/form"})
|
92
|
+
page.visit
|
93
|
+
page.page_title.should == "Classic Rock"
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should return empty if title is not defined" do
|
97
|
+
page = page_object({"url" => "/div"})
|
98
|
+
page.visit
|
99
|
+
page.page_title.should be_empty
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it { page_object({"name" => "page"}).to_s.should == "'page: page'" }
|
104
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
$:.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/support/test_website")
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + "/support/matchers/delegate")
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require "bundler/setup"
|
7
|
+
require 'rspec'
|
8
|
+
require 'capybara'
|
9
|
+
require "mocha"
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.mock_with :mocha
|
13
|
+
|
14
|
+
config.before(:each) do
|
15
|
+
@capybara_page = Capybara::Session.new(:schmoo, TestWebsite)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def capybara_page
|
20
|
+
@capybara_page
|
21
|
+
end
|
22
|
+
|
23
|
+
Capybara.register_driver :schmoo do |app|
|
24
|
+
Capybara::RackTest::Driver.new(app)
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'capybara-pageobject'
|
28
|
+
|
29
|
+
alias :running :lambda
|
30
|
+
|
31
|
+
Capybara.default_wait_time = 0 # less timeout so tests run faster
|
32
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
RSpec::Matchers.define :delegate do |delegated_method|
|
2
|
+
chain :to do |target_method|
|
3
|
+
@target_method = target_method
|
4
|
+
end
|
5
|
+
|
6
|
+
chain :as do |method_on_target|
|
7
|
+
@method_on_target = method_on_target
|
8
|
+
end
|
9
|
+
|
10
|
+
chain :with_arguments do |args|
|
11
|
+
@args = args
|
12
|
+
end
|
13
|
+
|
14
|
+
match do |instance|
|
15
|
+
extend Mocha::API
|
16
|
+
|
17
|
+
@instance = instance
|
18
|
+
@args ||= []
|
19
|
+
return_value = 'stubbed return value'
|
20
|
+
method_on_target = @method_on_target || delegated_method
|
21
|
+
stubbed_target = stub('stubbed_target', method_on_target => return_value)
|
22
|
+
@instance.stubs(@target_method => stubbed_target)
|
23
|
+
begin
|
24
|
+
@instance.send(delegated_method, *@args) == return_value
|
25
|
+
rescue NoMethodError
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
failure_message do
|
31
|
+
if Class === @instance
|
32
|
+
message = "expected #{@instance.name} "
|
33
|
+
prefix = '.'
|
34
|
+
else
|
35
|
+
message = "expected #{@instance.class.name} "
|
36
|
+
prefix = '#'
|
37
|
+
end
|
38
|
+
message << "to delegate #{prefix}#{delegated_method} to #{prefix}#{@target_method}"
|
39
|
+
if @method_on_target
|
40
|
+
message << ".#{@method_on_target}"
|
41
|
+
end
|
42
|
+
message
|
43
|
+
end
|
44
|
+
|
45
|
+
description do
|
46
|
+
d = "delegate #{delegated_method}"
|
47
|
+
d << " to #{@target_method}" if @target_method
|
48
|
+
d << " as #{@method_on_target}" if @method_on_target
|
49
|
+
d
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'rack'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
class TestWebsite < Sinatra::Base
|
6
|
+
set :root, File.dirname(__FILE__)
|
7
|
+
set :static, true
|
8
|
+
|
9
|
+
def page_with
|
10
|
+
<<-BODY
|
11
|
+
<html>
|
12
|
+
<head>
|
13
|
+
<title>Classic Rock</title>
|
14
|
+
</head>
|
15
|
+
<body>
|
16
|
+
<p>Hello World!!</p>
|
17
|
+
#{yield}
|
18
|
+
</body>
|
19
|
+
</html>
|
20
|
+
BODY
|
21
|
+
end
|
22
|
+
|
23
|
+
get '/div' do
|
24
|
+
'<div id="foo1">led zeppelin</div><div id="foo2">the doors</div>'
|
25
|
+
end
|
26
|
+
|
27
|
+
get '/form' do
|
28
|
+
page_with do
|
29
|
+
<<-FORM
|
30
|
+
<div id="attr1">led zeppelin</div>
|
31
|
+
<div id="hidden_attr" style="display:none">the doors</div>
|
32
|
+
<form id="unique_form">
|
33
|
+
<input type="text" id="field1" value="Creedence Rlearwater Revival"/>
|
34
|
+
<input type="text" id="hidden_field" style="display:none"/>
|
35
|
+
<input id="disabled_button" name="commit" type="submit" disabled="disabled" />
|
36
|
+
<input id="register_submit" name="commit" type="submit" value="Register" />
|
37
|
+
<form>
|
38
|
+
FORM
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
get '/form_with_rails_validation_errors' do
|
43
|
+
page_with do
|
44
|
+
<<-FORM
|
45
|
+
<form accept-charset="UTF-8" action="/users" class="standard-form" id="registration-form" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓" />
|
46
|
+
<input name="authenticity_token" type="hidden" value="xi5eg4oEiv3mng8ISy0qyYf/nB49pv0heJ93h3SrNtE=" /></div>
|
47
|
+
<ol>
|
48
|
+
<li class="text">
|
49
|
+
<div class="field_with_errors"><label for="user_email">Email Address</label></div>
|
50
|
+
<div class="field_with_errors"><input id="user_email" name="user[email]" size="30" title="Will be used as your username" type="text" value="" /><span class="error_message">can't be blank</span></div>
|
51
|
+
<span class="required">*</span>
|
52
|
+
</li>
|
53
|
+
<li class="text">
|
54
|
+
<div class="field_with_errors"><label for="user_password">Password</label></div>
|
55
|
+
<div class="field_with_errors"><input id="user_password" name="user[password]" size="30" title="At least 8 characters" type="password" /><span class="error_message">doesn't match confirmation</span></div><span class="required">*</span>
|
56
|
+
</li>
|
57
|
+
<li class="submit">
|
58
|
+
<input id="disabled_button" name="commit" type="submit" disabled="disabled" />
|
59
|
+
<input id="register_submit" name="commit" type="submit" value="Register" />
|
60
|
+
</li>
|
61
|
+
</ol>
|
62
|
+
</form>
|
63
|
+
FORM
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
if __FILE__ == $0
|
69
|
+
Rack::Handler::WEBrick.run TestWebsite, :Port => 8070
|
70
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
def with_yaml page_data
|
4
|
+
YAML.expects(:load_file).with('pages/pages.yml').returns(page_data)
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "Website" do
|
8
|
+
let(:website) { Capybara::PageObject::Website.new(capybara_page, self, 'pages/pages.yml') }
|
9
|
+
subject { website }
|
10
|
+
|
11
|
+
describe "create getters" do
|
12
|
+
before { with_yaml("home_page" => {}, "login_form" => {}) }
|
13
|
+
it { should respond_to :home_page }
|
14
|
+
it { should respond_to :login_form }
|
15
|
+
|
16
|
+
its(:home_page) { should be }
|
17
|
+
its(:login_form) { should be }
|
18
|
+
|
19
|
+
it "should pass page name in page_data hash" do
|
20
|
+
website.home_page.instance_variable_get(:@page_data)["name"].should == "home_page"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should raise error if page_file is not specified" do
|
25
|
+
expect { Capybara::PageObject::Website.new(capybara_page, self, '') }.to raise_error(Exception, "Please specify page file path")
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "custom page class" do
|
29
|
+
it "should instantiate custom class if specified" do
|
30
|
+
class CustomPage < Capybara::PageObject::Page; end
|
31
|
+
with_yaml("home_page" => {"class" => "CustomPage"})
|
32
|
+
website.home_page.class.should == CustomPage
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should fail if custom class does not extend Page class" do
|
36
|
+
with_yaml("home_page" => {"class" => "Object"})
|
37
|
+
expect { Capybara::PageObject::Website.new(capybara_page, self, 'pages/pages.yml') }.to raise_error(Exception, "Custom page class 'Object' should extend Capybara::PageObject::Page")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "page block" do
|
42
|
+
it "should evaluate calls within the block on page" do
|
43
|
+
with_yaml("home_page" => {"url" => "/form", "attributes" => {"attribute" => "#attr1", "field" => "#hidden_field"}})
|
44
|
+
website.home_page.visit
|
45
|
+
website.home_page do
|
46
|
+
attribute.should == "led zeppelin"
|
47
|
+
field.should_not be_visible
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: capybara-pageobject
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,55 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-02-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &2152272000 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2152272000
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: mocha
|
27
|
+
requirement: &2152270840 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2152270840
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: sinatra
|
38
|
+
requirement: &2152267160 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.9.4
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2152267160
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: capybara
|
49
|
+
requirement: &2152190040 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2152190040
|
14
58
|
- !ruby/object:Gem::Dependency
|
15
59
|
name: capybara
|
16
|
-
requirement: &
|
60
|
+
requirement: &2152188600 !ruby/object:Gem::Requirement
|
17
61
|
none: false
|
18
62
|
requirements:
|
19
63
|
- - ! '>='
|
@@ -21,9 +65,8 @@ dependencies:
|
|
21
65
|
version: 1.0.0
|
22
66
|
type: :runtime
|
23
67
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
-
description:
|
26
|
-
functional tests
|
68
|
+
version_requirements: *2152188600
|
69
|
+
description: Introduce page objects to your capybara-based functional tests
|
27
70
|
email:
|
28
71
|
- deepak.lewis@gmail.com
|
29
72
|
executables: []
|
@@ -31,11 +74,30 @@ extensions: []
|
|
31
74
|
extra_rdoc_files: []
|
32
75
|
files:
|
33
76
|
- .gitignore
|
77
|
+
- .rvmrc
|
34
78
|
- Gemfile
|
35
79
|
- Rakefile
|
36
80
|
- capybara-pageobject.gemspec
|
37
81
|
- lib/capybara-pageobject.rb
|
82
|
+
- lib/capybara-pageobject/action.rb
|
83
|
+
- lib/capybara-pageobject/attribute.rb
|
84
|
+
- lib/capybara-pageobject/capybara_helper.rb
|
85
|
+
- lib/capybara-pageobject/element.rb
|
86
|
+
- lib/capybara-pageobject/page.rb
|
38
87
|
- lib/capybara-pageobject/version.rb
|
88
|
+
- lib/capybara-pageobject/website.rb
|
89
|
+
- lib/monkey-patch/object.rb
|
90
|
+
- lib/monkey-patch/string.rb
|
91
|
+
- spec/action_spec.rb
|
92
|
+
- spec/attribute_spec.rb
|
93
|
+
- spec/capybara-pageobject_spec.rb
|
94
|
+
- spec/element_spec.rb
|
95
|
+
- spec/page_spec.rb
|
96
|
+
- spec/spec_helper.rb
|
97
|
+
- spec/support/matchers/delegate.rb
|
98
|
+
- spec/support/pages/pages.yml
|
99
|
+
- spec/support/test_website.rb
|
100
|
+
- spec/website_spec.rb
|
39
101
|
homepage: ''
|
40
102
|
licenses: []
|
41
103
|
post_install_message:
|
@@ -60,4 +122,14 @@ rubygems_version: 1.8.15
|
|
60
122
|
signing_key:
|
61
123
|
specification_version: 3
|
62
124
|
summary: Easily create page objects to abstract UI Pages
|
63
|
-
test_files:
|
125
|
+
test_files:
|
126
|
+
- spec/action_spec.rb
|
127
|
+
- spec/attribute_spec.rb
|
128
|
+
- spec/capybara-pageobject_spec.rb
|
129
|
+
- spec/element_spec.rb
|
130
|
+
- spec/page_spec.rb
|
131
|
+
- spec/spec_helper.rb
|
132
|
+
- spec/support/matchers/delegate.rb
|
133
|
+
- spec/support/pages/pages.yml
|
134
|
+
- spec/support/test_website.rb
|
135
|
+
- spec/website_spec.rb
|