capybara-page-object 0.2.1 → 0.3.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/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
|