capybara-page-object 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -1
- data/Gemfile.lock +15 -2
- data/Rakefile +27 -13
- data/VERSION +1 -1
- data/capybara-page-object.gemspec +55 -27
- data/lib/capybara-page-object.rb +6 -8
- data/lib/collections.rb +11 -0
- data/lib/element.rb +11 -0
- data/lib/element/anchor.rb +11 -0
- data/lib/element/form.rb +64 -0
- data/lib/element/form_field.rb +20 -0
- data/lib/element/image.rb +20 -0
- data/lib/element/input.rb +28 -0
- data/lib/element/list.rb +21 -0
- data/lib/element/listitem.rb +7 -0
- data/lib/element/meta.rb +16 -0
- data/lib/element/select.rb +16 -0
- data/lib/{table.rb → element/table.rb} +1 -3
- data/lib/element/textarea.rb +16 -0
- data/lib/{extractors → extractor}/common.rb +3 -11
- data/lib/{extractors → extractor}/page_level.rb +3 -13
- data/lib/html5_data.rb +14 -0
- data/lib/key_value.rb +11 -0
- data/lib/navigation.rb +16 -0
- data/lib/node.rb +76 -0
- data/spec/common_spec.rb +46 -0
- data/spec/element/anchor_spec.rb +15 -0
- data/spec/element/base_spec.rb +19 -0
- data/spec/element/form_field_spec.rb +28 -0
- data/spec/element/form_spec.rb +69 -0
- data/spec/element/image_spec.rb +30 -0
- data/spec/element/input_spec.rb +49 -0
- data/spec/element/list_spec.rb +47 -0
- data/spec/element/listitem_spec.rb +25 -0
- data/spec/element/meta_spec.rb +23 -0
- data/spec/element/select_spec.rb +36 -0
- data/spec/element/table_spec.rb +30 -0
- data/spec/element/textarea_spec.rb +19 -0
- data/{test/page.html → spec/fixtures/node.html} +0 -0
- data/{test → spec}/helper.rb +2 -11
- data/spec/node_spec.rb +45 -0
- metadata +120 -109
- data/lib/base.rb +0 -29
- data/lib/image.rb +0 -13
- data/lib/page.rb +0 -15
- data/test/image.html +0 -2
- data/test/table.html +0 -10
- data/test/test_image.rb +0 -20
- data/test/test_page.rb +0 -59
- data/test/test_table.rb +0 -20
data/lib/element/list.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module CapybaraPageObject
|
2
|
+
class List < CapybaraPageObject::Node
|
3
|
+
|
4
|
+
# TODO what about DL ?
|
5
|
+
def element_names
|
6
|
+
['ul', 'ol']
|
7
|
+
end
|
8
|
+
|
9
|
+
def child_node
|
10
|
+
'li'
|
11
|
+
end
|
12
|
+
|
13
|
+
def items(*args)
|
14
|
+
children(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def key
|
18
|
+
root_node[:id]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/element/meta.rb
ADDED
@@ -1,4 +1,4 @@
|
|
1
|
-
module
|
1
|
+
module Extractor
|
2
2
|
|
3
3
|
module Common
|
4
4
|
def extract(element, attr={}, page_object_class=nil)
|
@@ -28,16 +28,8 @@ module Extractors
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
-
|
32
|
-
|
33
|
-
r = {}
|
34
|
-
native.attributes.each do |k, v|
|
35
|
-
next unless k.start_with?('data-')
|
36
|
-
r[k.gsub('data-', '')] = v.value
|
37
|
-
end
|
38
|
-
r
|
39
|
-
end
|
40
|
-
|
31
|
+
|
32
|
+
# TODO use method_missing o handle these?
|
41
33
|
def all(*args)
|
42
34
|
source.all(*args)
|
43
35
|
end
|
@@ -1,19 +1,9 @@
|
|
1
|
-
module
|
2
|
-
module PageLevel
|
3
|
-
include Common
|
4
|
-
|
1
|
+
module Extractor
|
2
|
+
module PageLevel
|
5
3
|
def title
|
6
4
|
find('title').text
|
7
5
|
end
|
8
6
|
|
9
|
-
def tables(attr={})
|
10
|
-
extract('table', attr, CapybaraPageObject::Table)
|
11
|
-
end
|
12
|
-
|
13
|
-
def forms(attr={})
|
14
|
-
extract('form', attr)
|
15
|
-
end
|
16
|
-
|
17
7
|
def meta_description
|
18
8
|
extract('meta', :by => 'name')['description']['content']
|
19
9
|
end
|
@@ -22,4 +12,4 @@ module Extractors
|
|
22
12
|
extract('meta', :by => 'name')['keywords']['content'].split(',').collect(&:strip)
|
23
13
|
end
|
24
14
|
end
|
25
|
-
end
|
15
|
+
end
|
data/lib/html5_data.rb
ADDED
data/lib/key_value.rb
ADDED
data/lib/navigation.rb
ADDED
data/lib/node.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/collections'
|
2
|
+
require File.dirname(__FILE__) + '/navigation'
|
3
|
+
require File.dirname(__FILE__) + '/key_value'
|
4
|
+
require File.dirname(__FILE__) + '/html5_data'
|
5
|
+
require File.dirname(__FILE__) + '/extractor/common'
|
6
|
+
require File.dirname(__FILE__) + '/extractor/page_level'
|
7
|
+
|
8
|
+
module CapybaraPageObject
|
9
|
+
class Node
|
10
|
+
include Extractor::Common
|
11
|
+
include Extractor::PageLevel
|
12
|
+
include CapybaraPageObject::Collections
|
13
|
+
include CapybaraPageObject::Navigation
|
14
|
+
include CapybaraPageObject::HTML5Data
|
15
|
+
include Extractor::PageLevel
|
16
|
+
|
17
|
+
attr_accessor :source
|
18
|
+
|
19
|
+
def initialize(source=nil)
|
20
|
+
source ||= Capybara.current_session
|
21
|
+
@source = source
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.from_string(string)
|
25
|
+
new(Capybara.string(string))
|
26
|
+
end
|
27
|
+
|
28
|
+
def respond_to?(sym)
|
29
|
+
source.respond_to?(sym) || super(sym)
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(sym, *args, &block)
|
33
|
+
if source.respond_to?(sym)
|
34
|
+
source.send(sym)
|
35
|
+
else
|
36
|
+
super(sym, *args, &block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def classes
|
41
|
+
classes_list = root_node[:class] or return []
|
42
|
+
classes_list.split(' ')
|
43
|
+
end
|
44
|
+
|
45
|
+
def children(opts={})
|
46
|
+
factory = opts[:factory]
|
47
|
+
return all(child_node) unless factory
|
48
|
+
r = []
|
49
|
+
all(child_node).each do |li|
|
50
|
+
r << factory.new(li)
|
51
|
+
end
|
52
|
+
r
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def element_names
|
58
|
+
raise "You need to override element_names"
|
59
|
+
end
|
60
|
+
|
61
|
+
def root_node
|
62
|
+
# TODO still don't fully understand why we need both approaches
|
63
|
+
element_names.each do |element_name|
|
64
|
+
if source.native.name == element_name
|
65
|
+
return source.native
|
66
|
+
elsif source.has_css?(element_name)
|
67
|
+
return find(element_name)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
raise "No element matches for #{element_names.join(', ')}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class MissingPath < RuntimeError
|
75
|
+
end
|
76
|
+
end
|
data/spec/common_spec.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
describe "Page" do
|
4
|
+
before do
|
5
|
+
html = File.open(File.dirname(__FILE__) + '/fixtures/node.html').read
|
6
|
+
@page = CapybaraPageObject::Node.from_string html
|
7
|
+
end
|
8
|
+
|
9
|
+
context "#tables" do
|
10
|
+
it "returns an array of Table objects" do
|
11
|
+
tables = @page.tables
|
12
|
+
tables[0].class.should == CapybaraPageObject::Table
|
13
|
+
tables[0].class.should == CapybaraPageObject::Table
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns a hash of Tables keyed by id" do
|
17
|
+
tables = @page.tables(:by => 'id')
|
18
|
+
tables['table_1'].class.should == CapybaraPageObject::Table
|
19
|
+
tables['table_2'].class.should == CapybaraPageObject::Table
|
20
|
+
# TODO: support @page.tables[:table_1] ? perhaps have a default_key override?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "#forms" do
|
25
|
+
it "returns the ids for the forms" do
|
26
|
+
@page.forms(:keys => 'id').should =~ ['form_1', 'form_2']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "return the elements as a hash" do
|
31
|
+
@page.tables(:keys => 'id').should =~ ['table_1', 'table_2']
|
32
|
+
@page.tables(:keys => 'class').should =~ ['table_1_class', 'table_2_class']
|
33
|
+
end
|
34
|
+
|
35
|
+
context "#data" do
|
36
|
+
it "returns an empty hash if the element has no HTML5 data attributes" do
|
37
|
+
e = Capybara.string('<foo/>')
|
38
|
+
e.data.should == {}
|
39
|
+
end
|
40
|
+
|
41
|
+
it "returns a hash of the elements HTML5 data attributes" do
|
42
|
+
h = {'foo' => 'a', 'bar' => 'b', 'cat' => ''}
|
43
|
+
@page.find('#data').data.should == h
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../helper'
|
2
|
+
|
3
|
+
describe "Anchor" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@anchor = CapybaraPageObject::Anchor.from_string '<a href="hello.html">'
|
7
|
+
end
|
8
|
+
|
9
|
+
context "#link" do
|
10
|
+
it "return a URI object" do
|
11
|
+
@anchor.link.class.should == URI::Generic
|
12
|
+
@anchor.link.path.should == 'hello.html'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../helper'
|
2
|
+
|
3
|
+
describe "Base" do
|
4
|
+
before do
|
5
|
+
@tag = CapybaraPageObject::Anchor.from_string '<a id="bar">content</a>'
|
6
|
+
end
|
7
|
+
|
8
|
+
context "#key" do
|
9
|
+
it "default to the ID if not overridden" do
|
10
|
+
@tag.key.should == 'bar'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "#value" do
|
15
|
+
it "default to the text if not overridden" do
|
16
|
+
@tag.value.should == 'content'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../helper'
|
2
|
+
|
3
|
+
describe "FormField" do
|
4
|
+
before do
|
5
|
+
@input = CapybaraPageObject::FormField.from_string '<input value="hello">'
|
6
|
+
@textarea = CapybaraPageObject::FormField.from_string '<textarea>foo</textarea>'
|
7
|
+
@blank_textarea = CapybaraPageObject::FormField.from_string '<textarea></textarea>'
|
8
|
+
end
|
9
|
+
|
10
|
+
context "#value" do
|
11
|
+
it "delegates" do
|
12
|
+
@input.value.should == 'hello'
|
13
|
+
@textarea.value.should == 'foo'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "#blank" do
|
18
|
+
it "is false if there is an input value" do
|
19
|
+
@input.should_not be_blank
|
20
|
+
end
|
21
|
+
it "is false if there is textarea content" do
|
22
|
+
@textarea.should_not be_blank
|
23
|
+
end
|
24
|
+
it "is true if there is no textarea content" do
|
25
|
+
@blank_textarea.should be_blank
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../helper'
|
2
|
+
|
3
|
+
describe "Form" do
|
4
|
+
before do
|
5
|
+
@form = CapybaraPageObject::Form.from_string <<-EOF
|
6
|
+
<form>
|
7
|
+
<input name="input_1" value="value_1">
|
8
|
+
<input type="text" name="text_input" value="text_input_value">
|
9
|
+
|
10
|
+
<input type="password" name="password_input" value="password">
|
11
|
+
|
12
|
+
<select name="colour">
|
13
|
+
<option>red</option>
|
14
|
+
<option selected>blue</option>
|
15
|
+
</select>
|
16
|
+
|
17
|
+
<select name="countries" multiple>
|
18
|
+
<option>france</option>
|
19
|
+
<option selected>spain</option>
|
20
|
+
<option selected>germany</option>
|
21
|
+
</select>
|
22
|
+
|
23
|
+
<textarea name="essay">hello world</textarea>
|
24
|
+
<input type="checkbox" name="checkbox_1" value="checkbox_value_1">
|
25
|
+
<input type="checkbox" name="checkbox_2" value="checkbox_value_2" checked>
|
26
|
+
|
27
|
+
<input type="radio" name="radio_button_1" value="radio_value_1">
|
28
|
+
<input type="radio" name="radio_button_2" value="radio_value_2" checked>
|
29
|
+
|
30
|
+
<input type="submit" value="submit button"></input>
|
31
|
+
<input type="reset" value="reset button"></input>
|
32
|
+
<input type="button" value="button button"></input>
|
33
|
+
<button>button 2</button>
|
34
|
+
</form>
|
35
|
+
EOF
|
36
|
+
end
|
37
|
+
|
38
|
+
context "#buttons" do
|
39
|
+
it "return the buttons contained in the form" do
|
40
|
+
@form.buttons.size.should == 4
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "#fields" do
|
45
|
+
it "return the fields contained in the form - inputs, selects and textareas. Excludes button type inputs (submit, reset, etc.)" do
|
46
|
+
f = {
|
47
|
+
'input_1' => 'value_1',
|
48
|
+
'text_input' => 'text_input_value',
|
49
|
+
'password_input' => 'password',
|
50
|
+
'colour' => 'blue',
|
51
|
+
'essay' => 'hello world',
|
52
|
+
'checkbox_1' => false,
|
53
|
+
'checkbox_2' => true,
|
54
|
+
'radio_button_1' => false,
|
55
|
+
'radio_button_2' => true,
|
56
|
+
'countries' => ['spain', 'germany']
|
57
|
+
}
|
58
|
+
@form.fields.should == f
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it "provides direct read access to form's fields" do
|
63
|
+
@form.essay.should == 'hello world'
|
64
|
+
end
|
65
|
+
|
66
|
+
it "doesn't respond to nonexistant fields" do
|
67
|
+
@form.should_not respond_to(:nonexistant_field)
|
68
|
+
end
|
69
|
+
end
|