capybara-page-object 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +5 -0
- data/Gemfile +8 -9
- data/Gemfile.lock +27 -17
- data/README.markdown +32 -12
- data/Rakefile +0 -7
- data/VERSION +1 -1
- data/capybara-page-object.gemspec +41 -34
- data/lib/capybara-page-object.rb +12 -11
- data/lib/capybara-page-object/collections.rb +4 -0
- data/lib/capybara-page-object/component.rb +5 -0
- data/lib/{delegators.rb → capybara-page-object/delegators.rb} +0 -0
- data/lib/capybara-page-object/element.rb +6 -0
- data/lib/capybara-page-object/elements.rb +15 -0
- data/lib/capybara-page-object/elements/anchor.rb +6 -0
- data/lib/{element → capybara-page-object/elements}/form.rb +8 -16
- data/lib/capybara-page-object/elements/form_field.rb +10 -0
- data/lib/capybara-page-object/elements/head.rb +7 -0
- data/lib/capybara-page-object/elements/image.rb +7 -0
- data/lib/{element → capybara-page-object/elements}/input.rb +5 -2
- data/lib/{element → capybara-page-object/elements}/list.rb +1 -1
- data/lib/{element → capybara-page-object/elements}/list_item.rb +1 -1
- data/lib/capybara-page-object/elements/meta.rb +6 -0
- data/lib/{element → capybara-page-object/elements}/select.rb +0 -0
- data/lib/{element → capybara-page-object/elements}/table.rb +1 -1
- data/lib/capybara-page-object/elements/table_header.rb +4 -0
- data/lib/{element → capybara-page-object/elements}/table_row.rb +1 -1
- data/lib/{element → capybara-page-object/elements}/text_based_input.rb +0 -0
- data/lib/{element → capybara-page-object/elements}/textarea.rb +1 -3
- data/lib/capybara-page-object/extensions/capybara.rb +7 -0
- data/lib/capybara-page-object/extensions/enumerable.rb +10 -0
- data/lib/{html5_data.rb → capybara-page-object/html5_data.rb} +0 -0
- data/lib/{key_value.rb → capybara-page-object/key_value.rb} +0 -0
- data/lib/{node.rb → capybara-page-object/node.rb} +14 -10
- data/lib/capybara-page-object/page.rb +77 -0
- data/spec/common_spec.rb +6 -6
- data/spec/element/anchor_spec.rb +8 -3
- data/spec/element/form_field_spec.rb +7 -1
- data/spec/element/form_spec.rb +27 -17
- data/spec/helper.rb +13 -0
- data/spec/node_spec.rb +9 -0
- data/spec/page_spec.rb +90 -30
- metadata +58 -43
- data/lib/collections.rb +0 -17
- data/lib/element.rb +0 -14
- data/lib/element/TODO.md +0 -11
- data/lib/element/anchor.rb +0 -7
- data/lib/element/form_field.rb +0 -15
- data/lib/element/head.rb +0 -15
- data/lib/element/image.rb +0 -15
- data/lib/element/meta.rb +0 -11
- data/lib/element/table_header.rb +0 -4
- data/lib/page.rb +0 -40
@@ -0,0 +1,7 @@
|
|
1
|
+
module CapybaraPageObject
|
2
|
+
class Head < CapybaraPageObject::Element
|
3
|
+
field(:title) { find('title').text }
|
4
|
+
field(:meta_description) { find('meta[@name="description"]')['content'] }
|
5
|
+
field(:meta_keywords) { find('meta[@name="keywords"]')['content'].split(',').collect(&:strip) }
|
6
|
+
end
|
7
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'element/text_based_input'
|
2
|
-
|
3
1
|
module CapybaraPageObject
|
4
2
|
class Input < CapybaraPageObject::FormField
|
5
3
|
include TextBasedInput
|
@@ -7,6 +5,11 @@ module CapybaraPageObject
|
|
7
5
|
BUTTON_TYPES = ['submit', 'reset', 'button']
|
8
6
|
CHECKABLE_TYPES = ['radio', 'checkbox']
|
9
7
|
|
8
|
+
# TODO this doesn't belong here
|
9
|
+
def checked?
|
10
|
+
source.checked?
|
11
|
+
end
|
12
|
+
|
10
13
|
def untyped?
|
11
14
|
source[:type].nil?
|
12
15
|
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -1,18 +1,13 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
require 'html5_data'
|
4
|
-
require 'delegators'
|
1
|
+
require 'capybara-page-object/delegators'
|
2
|
+
|
5
3
|
|
6
4
|
module CapybaraPageObject
|
7
|
-
class Node
|
5
|
+
class Node
|
8
6
|
include Delegators
|
9
|
-
include CapybaraPageObject::Collections
|
10
|
-
include CapybaraPageObject::HTML5Data
|
11
7
|
|
12
8
|
attr_accessor :source
|
13
9
|
|
14
10
|
def initialize(source=nil)
|
15
|
-
source ||= Capybara.current_session
|
16
11
|
@source = source
|
17
12
|
end
|
18
13
|
|
@@ -24,8 +19,17 @@ module CapybaraPageObject
|
|
24
19
|
classes_list = source[:class] or return []
|
25
20
|
classes_list.split(' ')
|
26
21
|
end
|
27
|
-
end
|
28
22
|
|
29
|
-
|
23
|
+
def source
|
24
|
+
@source ||= Capybara.current_session
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.element(name, &block)
|
28
|
+
define_method(name, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.field(name, &block)
|
32
|
+
define_method(name, &block)
|
33
|
+
end
|
30
34
|
end
|
31
35
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'capybara-page-object/node'
|
2
|
+
require 'capybara-page-object/collections'
|
3
|
+
|
4
|
+
module CapybaraPageObject
|
5
|
+
|
6
|
+
module InstanceMethods
|
7
|
+
def path(*args)
|
8
|
+
raise MissingPath, "You need to override #path in #{self.class}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def prefix
|
12
|
+
'/'
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_path(attr)
|
16
|
+
target = prefix + path
|
17
|
+
if attr.kind_of?(String)
|
18
|
+
target += attr
|
19
|
+
elsif attr.kind_of?(Hash)
|
20
|
+
pairs = attr.map { |k, v| "#{k}=#{v}" }
|
21
|
+
target += '?' + pairs.join('&') if pairs.any?
|
22
|
+
elsif attr != nil
|
23
|
+
raise ArgumentError
|
24
|
+
end
|
25
|
+
source.visit target
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
def from_string(string, target)
|
31
|
+
new(Capybara.string(string).find(target))
|
32
|
+
end
|
33
|
+
|
34
|
+
def current?
|
35
|
+
page = new
|
36
|
+
page.source.current_path == page.prefix + page.path
|
37
|
+
end
|
38
|
+
|
39
|
+
def visit(attr={})
|
40
|
+
page = new
|
41
|
+
page.visit_path(attr)
|
42
|
+
page
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Page < CapybaraPageObject::Node
|
47
|
+
# TODO why doesn't this work when in the module?
|
48
|
+
def tables
|
49
|
+
all('table').each_with_object({}) do |e, hash|
|
50
|
+
t = CapybaraPageObject::Table.new(e)
|
51
|
+
hash[t.key] = t
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def forms
|
56
|
+
all('form').each_with_object({}) do |e, hash|
|
57
|
+
f = CapybaraPageObject::Form.new(e)
|
58
|
+
hash[f.key] = f
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.path(p)
|
63
|
+
define_method(:path) { p }
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.component(name, &block)
|
67
|
+
define_method(name, &block)
|
68
|
+
end
|
69
|
+
|
70
|
+
include CapybaraPageObject::Collections
|
71
|
+
include CapybaraPageObject::InstanceMethods
|
72
|
+
extend CapybaraPageObject::ClassMethods
|
73
|
+
end
|
74
|
+
|
75
|
+
class MissingPath < RuntimeError
|
76
|
+
end
|
77
|
+
end
|
data/spec/common_spec.rb
CHANGED
@@ -3,20 +3,20 @@ require 'helper'
|
|
3
3
|
describe "Page" do
|
4
4
|
before do
|
5
5
|
html = File.open(File.dirname(__FILE__) + '/fixtures/node.html').read
|
6
|
-
@page = CapybaraPageObject::
|
6
|
+
@page = CapybaraPageObject::Page.from_string html, 'body'
|
7
7
|
end
|
8
8
|
|
9
9
|
context "#tables" do
|
10
10
|
it "returns a hash of Tables keyed by ID" do
|
11
11
|
tables = @page.tables
|
12
|
-
tables.keys.should
|
12
|
+
tables.keys.should =~ ['table_1', 'table_2']
|
13
13
|
tables['table_1'].class.should == CapybaraPageObject::Table
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
17
|
context "#forms" do
|
18
|
-
|
19
|
-
@page.forms.should
|
18
|
+
it "returns a hash of Forms keyed by ID" do
|
19
|
+
@page.forms.keys.should =~ ['form_1', 'form_2']
|
20
20
|
@page.forms['form_1'].class.should == CapybaraPageObject::Form
|
21
21
|
end
|
22
22
|
end
|
@@ -24,13 +24,13 @@ describe "Page" do
|
|
24
24
|
context "#data" do
|
25
25
|
it "returns an empty hash if the element has no HTML5 data attributes" do
|
26
26
|
html = '<div foo="bar"/>'
|
27
|
-
@fragment = CapybaraPageObject::
|
27
|
+
@fragment = CapybaraPageObject::Element.from_string html, 'div'
|
28
28
|
@fragment.data.should == {}
|
29
29
|
end
|
30
30
|
|
31
31
|
it "returns a hash of the elements HTML5 data attributes" do
|
32
32
|
html = '<div id="data" data-foo="a" data-bar="b" data-cat>Some data</div>'
|
33
|
-
@fragment = CapybaraPageObject::
|
33
|
+
@fragment = CapybaraPageObject::Element.from_string html, 'div'
|
34
34
|
h = {'foo' => 'a', 'bar' => 'b', 'cat' => ''}
|
35
35
|
@fragment.data.should == h
|
36
36
|
end
|
data/spec/element/anchor_spec.rb
CHANGED
@@ -7,9 +7,14 @@ describe "Anchor" do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
context "#link" do
|
10
|
-
it "return
|
11
|
-
@anchor.link.
|
12
|
-
|
10
|
+
it "return the link" do
|
11
|
+
@anchor.link.should == 'hello.html'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "#uri" do
|
16
|
+
it "return the link as a URI" do
|
17
|
+
@anchor.uri.should == URI('hello.html')
|
13
18
|
end
|
14
19
|
end
|
15
20
|
end
|
@@ -2,11 +2,17 @@ require 'helper'
|
|
2
2
|
|
3
3
|
describe "FormField" do
|
4
4
|
before do
|
5
|
-
@input = CapybaraPageObject::FormField.from_string '<input value="hello">', 'input'
|
5
|
+
@input = CapybaraPageObject::FormField.from_string '<input name="greeting" value="hello">', 'input'
|
6
6
|
@textarea = CapybaraPageObject::FormField.from_string '<textarea>foo</textarea>', 'textarea'
|
7
7
|
@blank_textarea = CapybaraPageObject::FormField.from_string '<textarea></textarea>', 'textarea'
|
8
8
|
end
|
9
9
|
|
10
|
+
context "#key" do
|
11
|
+
it "returns the name of the form field" do
|
12
|
+
@input.key.should == 'greeting'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
10
16
|
context "#value" do
|
11
17
|
it "delegates" do
|
12
18
|
@input.value.should == 'hello'
|
data/spec/element/form_spec.rb
CHANGED
@@ -42,23 +42,33 @@ describe "Form" do
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
45
|
+
context "#inputs" do
|
46
|
+
it "returns the inputs on the page (excluding buttons)" do
|
47
|
+
@form.inputs.keys.should == ["input_1", "text_input", "password_input", "checkbox_1",
|
48
|
+
"checkbox_2", "radio_button_1", "radio_button_2"]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "#textareas" do
|
53
|
+
it "returns the textareas on the page" do
|
54
|
+
@form.textareas.keys.should == ["essay"]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "#selects" do
|
59
|
+
it "returns the selects on the page" do
|
60
|
+
@form.selects.keys.should == ["colour", "countries"]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "#fields" do
|
65
|
+
it "merges inputs, textareas and selects" do
|
66
|
+
@form.stub!(:inputs => {'a' => 1})
|
67
|
+
@form.stub!(:textareas => {'b' => 2})
|
68
|
+
@form.stub!(:selects => {'c' => 3})
|
69
|
+
@form.fields.should == {'a' => 1, 'b' => 2, 'c' => 3}
|
70
|
+
end
|
71
|
+
end
|
62
72
|
|
63
73
|
# it "provides direct read access to form's fields" do
|
64
74
|
# @form.essay.should == 'hello world'
|
data/spec/helper.rb
CHANGED
@@ -12,4 +12,17 @@ require 'rspec'
|
|
12
12
|
|
13
13
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
14
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
|
16
|
+
unless ENV["SKIP_COVERAGE"]
|
17
|
+
require 'simplecov'
|
18
|
+
|
19
|
+
SimpleCov.start
|
20
|
+
SimpleCov.at_exit do
|
21
|
+
SimpleCov.result.format!
|
22
|
+
if SimpleCov.result.covered_percent < 100
|
23
|
+
`open coverage/index.html`
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
15
28
|
require 'capybara-page-object'
|
data/spec/node_spec.rb
CHANGED
@@ -18,5 +18,14 @@ describe "Page" do
|
|
18
18
|
anchor = CapybaraPageObject::Anchor.from_string '<a/>', 'a'
|
19
19
|
anchor.classes.should be_empty
|
20
20
|
end
|
21
|
+
|
22
|
+
it "has a nice DSL" do
|
23
|
+
source = mock
|
24
|
+
source.should_receive(:find_button).with('place_order')
|
25
|
+
class Bar < CapybaraPageObject::Node
|
26
|
+
element(:place_order) { source.find_button('place_order') }
|
27
|
+
end
|
28
|
+
Bar.new(source).place_order
|
29
|
+
end
|
21
30
|
end
|
22
31
|
end
|
data/spec/page_spec.rb
CHANGED
@@ -1,37 +1,97 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require 'active_support/ordered_hash'
|
2
|
+
require File.dirname(__FILE__) + '/../lib/capybara-page-object/page'
|
3
|
+
|
4
|
+
describe "a class which extends CapybaraPageObject::Page" do
|
5
|
+
|
6
|
+
before do
|
7
|
+
class FooShow < CapybaraPageObject::Page
|
8
|
+
def path
|
9
|
+
'foos/'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
class FooIndex < CapybaraPageObject::Page
|
13
|
+
def path
|
14
|
+
'foos'
|
15
|
+
end
|
16
|
+
end
|
4
17
|
end
|
5
|
-
end
|
6
18
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
it "supports key-value pairs" do
|
17
|
-
mock_source = mock()
|
18
|
-
mock_source.should_receive(:current_path)
|
19
|
-
mock_source.should_receive(:visit).with('/foo?a=1&b=2')
|
20
|
-
# intential mix of string and symbol keys below
|
21
|
-
@page = FooPage.new({'a' => 1, :b => 2}, mock_source)
|
22
|
-
end
|
23
|
-
|
24
|
-
it "raises an exception if revisiting" do
|
25
|
-
s = stub('Capybara', :current_path => '/foo', :visit => nil)
|
26
|
-
lambda do
|
27
|
-
@page = FooPage.new(nil, s)
|
28
|
-
end.should raise_error(RuntimeError, 'Detected repeat of page load - use #reload instead')
|
19
|
+
context ".current?" do
|
20
|
+
it "is true if the path matches the current session path" do
|
21
|
+
Capybara.stub :current_session => mock(:current_path => '/foos')
|
22
|
+
FooIndex.should be_current
|
23
|
+
end
|
24
|
+
|
25
|
+
it "is false if the path doesn't match the current session path" do
|
26
|
+
Capybara.stub :current_session => mock(:current_path => '/foos/1')
|
27
|
+
FooIndex.should_not be_current
|
29
28
|
end
|
30
29
|
end
|
31
|
-
|
32
|
-
context "
|
33
|
-
|
34
|
-
|
30
|
+
|
31
|
+
context ".visit" do
|
32
|
+
it "visits the appropriate path" do
|
33
|
+
session = mock
|
34
|
+
session.should_receive(:visit).with('/foos')
|
35
|
+
Capybara.stub :current_session => session
|
36
|
+
FooIndex.visit
|
37
|
+
end
|
38
|
+
|
39
|
+
it "throws an error if path hasn't been overridden" do
|
40
|
+
lambda do
|
41
|
+
CapybaraPageObject::Page.visit
|
42
|
+
end.should raise_error(CapybaraPageObject::MissingPath, 'You need to override #path in CapybaraPageObject::Page')
|
43
|
+
end
|
44
|
+
|
45
|
+
it "converts a supplied hash into querystring paramaters" do
|
46
|
+
session = mock
|
47
|
+
session.should_receive(:visit).with('/foos?a=1&b=2')
|
48
|
+
Capybara.stub :current_session => session
|
49
|
+
# using an OrderedHash so we can guarantee the order on Ruby 1.8
|
50
|
+
# also, the mix of string and symbol keys is intentional
|
51
|
+
# for some reason, passing in the hash to the initializer fails when
|
52
|
+
# running via Rake
|
53
|
+
key_values = ActiveSupport::OrderedHash.new
|
54
|
+
key_values['a'] = 1
|
55
|
+
key_values[:b] = 2
|
56
|
+
@page = FooIndex.visit key_values
|
57
|
+
end
|
58
|
+
|
59
|
+
it "converts a supplied string into a resource identifier" do
|
60
|
+
session = mock()
|
61
|
+
session.should_receive(:visit).with('/foos/bar')
|
62
|
+
Capybara.stub :current_session => session
|
63
|
+
@page = FooShow.visit 'bar'
|
64
|
+
end
|
65
|
+
|
66
|
+
it "returns an instance of the page object" do
|
67
|
+
Capybara.stub :current_session => stub.as_null_object
|
68
|
+
FooIndex.visit.class.should == FooIndex
|
69
|
+
end
|
70
|
+
|
71
|
+
it "throws an error if the supplied argument is something other a string or hash" do
|
72
|
+
lambda do
|
73
|
+
FooIndex.visit Object
|
74
|
+
end.should raise_error ArgumentError
|
75
|
+
end
|
76
|
+
|
77
|
+
it "has a DSL for setting the path" do
|
78
|
+
class Blah < CapybaraPageObject::Page
|
79
|
+
path 'xyz'
|
80
|
+
end
|
81
|
+
session = mock()
|
82
|
+
session.should_receive(:visit).with('/xyz')
|
83
|
+
Capybara.stub :current_session => session
|
84
|
+
@page = Blah.visit
|
85
|
+
end
|
86
|
+
|
87
|
+
it "has a DSL for setting components" do
|
88
|
+
class Blah < CapybaraPageObject::Page
|
89
|
+
component(:navigation) { source.find('#navigation') }
|
90
|
+
end
|
91
|
+
session = mock
|
92
|
+
session.should_receive(:find).with('#navigation')
|
93
|
+
Capybara.stub :current_session => session
|
94
|
+
Blah.new.navigation
|
35
95
|
end
|
36
96
|
end
|
37
97
|
end
|