page_magic 1.0.0.alpha10 → 1.0.0.alpha11
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.
- checksums.yaml +4 -4
- data/README.md +45 -30
- data/VERSION +1 -1
- data/lib/page_magic/element.rb +62 -28
- data/lib/page_magic/element_context.rb +9 -38
- data/spec/element_spec.rb +168 -154
- data/spec/page_magic/element_context_spec.rb +48 -102
- data/spec/page_magic/elements_spec.rb +1 -1
- data/spec/spec_helper.rb +16 -9
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df390dca05439be5b971179c257c5b8d6e73ade2
|
4
|
+
data.tar.gz: 63e5f1d5579f2981e69ff1dbc8dc31d3505f2e9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e635b5836e8a2376b28507c87240d09b870a2d6720aa3e94eac6fc5723e1269c85659a935f7dada8b1543afa48d47b0e8c0dcdf87f065c9937523aed73207b63
|
7
|
+
data.tar.gz: a710f169e452e3408652064347feedf1f81ddc2d4d9093fd948299f683f51f6fb1a71d00601e71ce7a6508761994206d885922010f257ff22886dd01cc4b1686
|
data/README.md
CHANGED
@@ -4,10 +4,10 @@ PageMagic is an API for testing web applications.
|
|
4
4
|
It has a simple but powerful DSL which makes modelling and interacting with your pages easy.
|
5
5
|
|
6
6
|
Wouldn't it be great if there was a framework that could:
|
7
|
-
- Model your pages
|
8
|
-
-
|
9
|
-
-
|
10
|
-
- Be
|
7
|
+
- [Model your pages](#modelling-pages)
|
8
|
+
- [Fluently define interaction hooks / waiters on page elements](#interaction-hooks)
|
9
|
+
- [Provide urls to pages to avoid awkward page object switching](#page-mapping)
|
10
|
+
- [Be super dynamic](#dynamic-selectors)
|
11
11
|
|
12
12
|
Well PageMagic might just be the answer!
|
13
13
|
|
@@ -58,35 +58,13 @@ Out of the box, PageMagic knows how to work with:
|
|
58
58
|
- poltergeist
|
59
59
|
- RackTest - Read more on testing rack compliant object's directly later on
|
60
60
|
|
61
|
-
Under the hood, PageMagic is using [Capybara](https://github.com/jnicklas/capybara) so you can register any Capybara specific driver you want. See below for how to do this.
|
61
|
+
Under the hood, PageMagic is using [Capybara](https://github.com/jnicklas/capybara) so you can register any Capybara specific driver you want. See [below](#registering-a-custom-driver) for how to do this.
|
62
62
|
|
63
63
|
**Note:** We don't want to impose particular driver versions so PageMagic does not list any as dependencies. Therefore you will need add the requiste gem to your Gemfile.
|
64
64
|
|
65
|
-
##
|
66
|
-
To define something that PageMagic can work with, simply include PageMagic in to a class. Here are the
|
67
|
-
```ruby
|
68
|
-
class LoginPage
|
69
|
-
include PageMagic
|
70
|
-
end
|
71
|
-
|
72
|
-
class MailBox
|
73
|
-
include PageMagic
|
74
|
-
end
|
75
|
-
|
76
|
-
class MessageView
|
77
|
-
include PageMagic
|
78
|
-
end
|
79
|
-
```
|
65
|
+
## Modeling pages
|
66
|
+
To define something that PageMagic can work with, simply include PageMagic in to a class. Your you will also want to model the elements on your page so that you can interact with them. Here are we are modelling the login page we would need for the example above
|
80
67
|
|
81
|
-
## Visiting a page
|
82
|
-
To use a page ojbect you need to 'visit' it.
|
83
|
-
```ruby
|
84
|
-
session.visit(LoginPage, url: 'https://21st-century-mail.com')
|
85
|
-
```
|
86
|
-
**Note:** soon you won't even have to specify the page class :)
|
87
|
-
|
88
|
-
##Defining elements
|
89
|
-
Your pages are going to have elements on them that you will want to interact with. In the case of the Login page, it's easy to imagine that it will have text fields for a username and password and a button to login in with.
|
90
68
|
```ruby
|
91
69
|
class LoginPage
|
92
70
|
include PageMagic
|
@@ -95,6 +73,8 @@ class LoginPage
|
|
95
73
|
button(:login_button, text: 'login')
|
96
74
|
end
|
97
75
|
```
|
76
|
+
In the case of the Login page, it's easy to imagine that it will have text fields for a username and password and a button to login in with.
|
77
|
+
|
98
78
|
##Interacting with elements
|
99
79
|
Elements are defined with a id which is the name of the method you will use to reference it. In the above example, the textfields and button were defined with the id's, `:username`, `:password`, and `:login_button`
|
100
80
|
|
@@ -104,7 +84,24 @@ session.username.set 'joe@blogs.com'
|
|
104
84
|
session.password.set 'passw0rd'
|
105
85
|
session.login_button.click
|
106
86
|
```
|
107
|
-
|
87
|
+
|
88
|
+
## Defining Pages
|
89
|
+
To define something that PageMagic can work with, simply include PageMagic in to a class. Here are the classes we would need for the example above.
|
90
|
+
```ruby
|
91
|
+
class LoginPage
|
92
|
+
include PageMagic
|
93
|
+
end
|
94
|
+
|
95
|
+
class MailBox
|
96
|
+
include PageMagic
|
97
|
+
end
|
98
|
+
|
99
|
+
class MessageView
|
100
|
+
include PageMagic
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
## Helper methods
|
108
105
|
Using elements that are defined on a page is great, but if you are enacting a procedure through interacting with a few of them then your code could end up with some pretty repetitive code. In this case you can define helper methods instead.
|
109
106
|
|
110
107
|
In the above [example](#an example) we used a helper called `login`.
|
@@ -183,6 +180,24 @@ browser.define_page_mappings %r{/messages/\d+} => MessagePage,
|
|
183
180
|
```
|
184
181
|
You can use even use regular expressions to map multiple paths to the same page. In the above example we are mapping paths that that starts with '/messages/' and are followed by one ore more digits to the `MessagePage` class.
|
185
182
|
|
183
|
+
## Registering a custom driver
|
184
|
+
You can register any Capybara compliant driver as follows
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
#1. Define driver, constructor to PageMagic::Driver takes a list of browser aliases.
|
188
|
+
# Selenium Webdriver for example supports driving more than one.
|
189
|
+
Webkit = PageMagic::Driver.new(:webkit) do |app, options, browser_alias_chosen|
|
190
|
+
# Write the code necessary to initialise the driver you have chosen
|
191
|
+
require 'capybara/webkit'
|
192
|
+
Capybara::Webkit::Driver.new(app, options)
|
193
|
+
end
|
194
|
+
|
195
|
+
#2. Register driver
|
196
|
+
PageMagic.drivers.register Webkit
|
197
|
+
|
198
|
+
#3. Use registered driver
|
199
|
+
session = PageMagic.session(browser: webkit)
|
200
|
+
```
|
186
201
|
##What else can you do with PageMagic?
|
187
202
|
PageMagic has lots of other useful features. I'm writing up the documentation so check back here soon!
|
188
203
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.0.
|
1
|
+
1.0.0.alpha11
|
data/lib/page_magic/element.rb
CHANGED
@@ -10,6 +10,7 @@ module PageMagic
|
|
10
10
|
end
|
11
11
|
|
12
12
|
class Element
|
13
|
+
EVENT_TYPES = [:set, :select, :select_option, :unselect_option, :click]
|
13
14
|
attr_reader :type, :name, :selector, :browser_element
|
14
15
|
|
15
16
|
include Elements
|
@@ -26,7 +27,7 @@ module PageMagic
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def initialize(name, parent_page_element, options, &block)
|
29
|
-
options = {
|
30
|
+
options = {type: :element, selector: {}, browser_element: nil}.merge(options)
|
30
31
|
@browser_element = options[:browser_element]
|
31
32
|
@selector = options[:selector]
|
32
33
|
|
@@ -67,13 +68,17 @@ module PageMagic
|
|
67
68
|
@after_hook = block
|
68
69
|
end
|
69
70
|
|
70
|
-
def method_missing
|
71
|
-
element_context(args).send(method, args.first)
|
72
|
-
rescue ElementMissingException
|
71
|
+
def method_missing method, *args, &block
|
73
72
|
begin
|
74
|
-
|
75
|
-
rescue
|
76
|
-
|
73
|
+
ElementContext.new(self, browser_element, self, *args).send(method, args.first, &block)
|
74
|
+
rescue ElementMissingException
|
75
|
+
if browser_element.respond_to?(method)
|
76
|
+
browser_element.send(method, *args, &block)
|
77
|
+
elsif @parent_page_element.respond_to?(method)
|
78
|
+
@parent_page_element.send(method, *args, &block)
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
77
82
|
end
|
78
83
|
end
|
79
84
|
|
@@ -91,34 +96,63 @@ module PageMagic
|
|
91
96
|
options = selector_copy
|
92
97
|
|
93
98
|
finder_method, selector_type, selector_arg = case method
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
99
|
+
when :id
|
100
|
+
[:find, "##{selector}"]
|
101
|
+
when :xpath
|
102
|
+
[:find, :xpath, selector]
|
103
|
+
when :name
|
104
|
+
[:find, "*[name='#{selector}']"]
|
105
|
+
when :css
|
106
|
+
[:find, :css, selector]
|
107
|
+
when :label
|
108
|
+
[:find_field, selector]
|
109
|
+
when :text
|
110
|
+
if @type == :link
|
111
|
+
[:find_link, selector]
|
112
|
+
elsif @type == :button
|
113
|
+
[:find_button, selector]
|
114
|
+
else
|
115
|
+
fail UnsupportedSelectorException
|
116
|
+
end
|
117
|
+
|
109
118
|
else
|
110
119
|
fail UnsupportedSelectorException
|
111
|
-
end
|
112
|
-
|
113
|
-
else
|
114
|
-
fail UnsupportedSelectorException
|
115
120
|
end
|
116
121
|
|
117
122
|
finder_args = [selector_type, selector_arg].compact
|
118
123
|
finder_args << options unless options.empty?
|
119
|
-
@browser_element = @parent_page_element.browser_element.send(finder_method, *finder_args)
|
124
|
+
@browser_element = @parent_page_element.browser_element.send(finder_method, *finder_args).tap do |browser_element|
|
125
|
+
EVENT_TYPES.each do |action_method|
|
126
|
+
apply_hooks(page_element: browser_element,
|
127
|
+
capybara_method: action_method,
|
128
|
+
before_hook: before,
|
129
|
+
after_hook: after)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
120
133
|
end
|
121
|
-
|
134
|
+
end
|
135
|
+
|
136
|
+
def apply_hooks(options)
|
137
|
+
_self = self
|
138
|
+
page_element = options[:page_element]
|
139
|
+
capybara_method = options[:capybara_method]
|
140
|
+
if page_element.respond_to?(capybara_method)
|
141
|
+
original_method = page_element.method(capybara_method)
|
142
|
+
|
143
|
+
page_element.define_singleton_method capybara_method do |*arguments, &block|
|
144
|
+
_self.call_hook &options[:before_hook]
|
145
|
+
original_method.call *arguments, &block
|
146
|
+
_self.call_hook &options[:after_hook]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def call_hook(&block)
|
152
|
+
@executing_hooks = true
|
153
|
+
result = instance_exec @browser, &block
|
154
|
+
@executing_hooks = false
|
155
|
+
result
|
122
156
|
end
|
123
157
|
|
124
158
|
private
|
@@ -3,6 +3,8 @@ module PageMagic
|
|
3
3
|
end
|
4
4
|
|
5
5
|
class ElementContext
|
6
|
+
EVENT_TYPES = [:set, :select, :select_option, :unselect_option, :click]
|
7
|
+
|
6
8
|
attr_reader :caller, :page_element
|
7
9
|
|
8
10
|
def initialize(page_element, browser, caller, *_args)
|
@@ -11,55 +13,24 @@ module PageMagic
|
|
11
13
|
@caller = caller
|
12
14
|
end
|
13
15
|
|
14
|
-
def method_missing(method, *args)
|
15
|
-
return
|
16
|
-
return @page_element.send(method, *args) if @page_element.methods.include?(method)
|
16
|
+
def method_missing(method, *args, &block)
|
17
|
+
return page_element.send(method, *args, &block) if page_element.methods.include?(method)
|
17
18
|
|
18
|
-
element_locator_factory =
|
19
|
+
element_locator_factory = page_element.element_definitions[method]
|
19
20
|
|
20
|
-
|
21
|
+
raise ElementMissingException, "Could not find: #{method}" unless element_locator_factory
|
21
22
|
|
22
23
|
if args.empty?
|
23
|
-
element_locator = element_locator_factory.call(
|
24
|
+
element_locator = element_locator_factory.call(page_element, nil)
|
24
25
|
else
|
25
|
-
element_locator = element_locator_factory.call(
|
26
|
-
end
|
27
|
-
|
28
|
-
[:set, :select_option, :unselect_option, :click].each do |action_method|
|
29
|
-
apply_hooks(page_element: element_locator.browser_element,
|
30
|
-
capybara_method: action_method,
|
31
|
-
before_hook: element_locator.before,
|
32
|
-
after_hook: element_locator.after
|
33
|
-
)
|
26
|
+
element_locator = element_locator_factory.call(page_element, *args)
|
34
27
|
end
|
35
28
|
|
36
29
|
element_locator.section? ? element_locator : element_locator.browser_element
|
37
30
|
end
|
38
31
|
|
39
32
|
def respond_to?(*args)
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
def apply_hooks(options)
|
44
|
-
_self = self
|
45
|
-
page_element = options[:page_element]
|
46
|
-
capybara_method = options[:capybara_method]
|
47
|
-
if page_element.respond_to?(capybara_method)
|
48
|
-
original_method = page_element.method(capybara_method)
|
49
|
-
|
50
|
-
page_element.define_singleton_method capybara_method do |*arguments, &block|
|
51
|
-
_self.call_hook &options[:before_hook]
|
52
|
-
original_method.call *arguments, &block
|
53
|
-
_self.call_hook &options[:after_hook]
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def call_hook(&block)
|
59
|
-
@executing_hooks = true
|
60
|
-
result = instance_exec @browser, &block
|
61
|
-
@executing_hooks = false
|
62
|
-
result
|
33
|
+
page_element.element_definitions.keys.include?(args.first)
|
63
34
|
end
|
64
35
|
end
|
65
36
|
end
|
data/spec/element_spec.rb
CHANGED
@@ -1,218 +1,232 @@
|
|
1
|
-
|
2
|
-
before :each do
|
3
|
-
Capybara.app = Class.new(Sinatra::Base) do
|
4
|
-
get '/' do
|
5
|
-
<<-HTML
|
6
|
-
<label>enter text
|
7
|
-
<input id='field_id' name='field_name' class='input_class' type='text' value='filled in'/>
|
8
|
-
</label>
|
9
|
-
<a id=my_link href='#'>my link</a>
|
10
|
-
<button id=my_button href='#'>my button</button>
|
11
|
-
HTML
|
12
|
-
end
|
13
|
-
end
|
1
|
+
module PageMagic
|
14
2
|
|
15
|
-
|
16
|
-
end
|
3
|
+
describe Element do
|
17
4
|
|
18
|
-
describe 'inheriting' do
|
19
5
|
include_context :webapp
|
20
6
|
|
21
|
-
|
22
|
-
|
23
|
-
selector css: '.form'
|
24
|
-
|
25
|
-
link :form_link, id: 'form_link'
|
26
|
-
def self.name
|
27
|
-
'Form'
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
page = Class.new do
|
7
|
+
let(:page_class) do
|
8
|
+
Class.new do
|
32
9
|
include PageMagic
|
33
10
|
url '/elements'
|
34
|
-
section custom_element
|
35
11
|
end
|
12
|
+
end
|
36
13
|
|
37
|
-
|
38
|
-
page
|
39
|
-
|
14
|
+
let(:page) do
|
15
|
+
page_class.new.tap do |page|
|
16
|
+
page.visit
|
17
|
+
end
|
40
18
|
end
|
41
|
-
end
|
42
19
|
|
43
|
-
|
44
|
-
|
45
|
-
|
20
|
+
describe 'inheriting' do
|
21
|
+
|
22
|
+
it 'lets you create custom elements' do
|
23
|
+
custom_element = Class.new(described_class) do
|
24
|
+
text_field :form_field, id: 'field_id'
|
25
|
+
|
26
|
+
def self.name
|
27
|
+
'Form'
|
28
|
+
end
|
29
|
+
end
|
46
30
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
31
|
+
page_class.class_eval do
|
32
|
+
section custom_element, css: '.form'
|
33
|
+
end
|
34
|
+
|
35
|
+
expect(page.form.form_field).to be_visible
|
51
36
|
end
|
52
37
|
end
|
53
|
-
it 'checks for methods on self' do
|
54
|
-
expect(subject.respond_to?(:session)).to eq(true)
|
55
|
-
end
|
56
38
|
|
57
|
-
it '
|
58
|
-
expect
|
39
|
+
it 'should raise an error if a selector has not been specified' do
|
40
|
+
expect { described_class.new(:name, Object.new, type: :element).browser_element }.to raise_error(PageMagic::UndefinedSelectorException)
|
59
41
|
end
|
60
42
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
43
|
+
describe '#respond_to?' do
|
44
|
+
subject do
|
45
|
+
described_class.new(:name, Object.new, type: :element, browser_element: double(element_method: '')) do
|
46
|
+
element :sub_element, css: '.sub-element'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
it 'checks for methods on self' do
|
50
|
+
expect(subject.respond_to?(:session)).to eq(true)
|
51
|
+
end
|
65
52
|
|
66
|
-
|
67
|
-
|
68
|
-
let!(:page) do
|
69
|
-
page_class = Class.new do
|
70
|
-
include PageMagic
|
53
|
+
it 'checks against registered elements' do
|
54
|
+
expect(subject.respond_to?(:sub_element)).to eq(true)
|
71
55
|
end
|
72
|
-
page_class.new
|
73
|
-
end
|
74
56
|
|
75
|
-
|
76
|
-
|
77
|
-
options = { key: :value }
|
78
|
-
xpath_selector = '//input'
|
79
|
-
expect(Capybara.current_session).to receive(:find).with(:xpath, xpath_selector, options)
|
80
|
-
PageMagic::Element.new(:my_input, page, type: :text_field, selector: { xpath: xpath_selector }.merge(options)).browser_element
|
57
|
+
it 'checks for the method of the browser_element' do
|
58
|
+
expect(subject.respond_to?(:element_method)).to eq(true)
|
81
59
|
end
|
82
60
|
end
|
83
61
|
|
84
|
-
|
85
|
-
|
86
|
-
element.value == 'filled in'
|
87
|
-
end
|
62
|
+
describe '#browser_element' do
|
63
|
+
let!(:browser) { double('browser') }
|
88
64
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
65
|
+
context 'options supplied to selector' do
|
66
|
+
it 'passes them on to the cappybara finder method' do
|
67
|
+
options = {count: 1}
|
68
|
+
xpath_selector = '//div/input'
|
69
|
+
expect(Capybara.current_session).to receive(:find).with(:xpath, xpath_selector, options)
|
70
|
+
described_class.new(:my_input, page, type: :text_field, selector: {xpath: xpath_selector}.merge(options)).browser_element
|
71
|
+
end
|
72
|
+
end
|
93
73
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
74
|
+
it 'should find by xpath' do
|
75
|
+
element = described_class.new(:my_input, page, type: :text_field, selector: {xpath: '//div/label/input'}).browser_element
|
76
|
+
expect(element.value).to eq('filled in')
|
77
|
+
end
|
98
78
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
79
|
+
it 'should locate an element using its id' do
|
80
|
+
element = described_class.new(:my_input, page, type: :text_field, selector: {id: 'field_id'}).browser_element
|
81
|
+
expect(element.value).to eq('filled in')
|
82
|
+
end
|
103
83
|
|
104
|
-
|
105
|
-
|
106
|
-
|
84
|
+
it 'should locate an element using its name' do
|
85
|
+
element = described_class.new(:my_input, page, type: :text_field, selector: {name: 'field_name'}).browser_element
|
86
|
+
expect(element.value).to eq('filled in')
|
87
|
+
end
|
107
88
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
89
|
+
it 'should locate an element using its label' do
|
90
|
+
element = described_class.new(:my_link, page, type: :link, selector: {label: 'enter text'}).browser_element
|
91
|
+
expect(element[:id]).to eq('field_id')
|
92
|
+
end
|
112
93
|
|
113
|
-
|
114
|
-
|
115
|
-
|
94
|
+
it 'should raise an exception when finding another element using its text' do
|
95
|
+
expect { described_class.new(:my_link, page, type: :text_field, selector: {text: 'my link'}).browser_element }.to raise_error(PageMagic::UnsupportedSelectorException)
|
96
|
+
end
|
116
97
|
|
117
|
-
|
118
|
-
|
119
|
-
|
98
|
+
it 'should locate an element using css' do
|
99
|
+
element = described_class.new(:my_link, page, type: :link, selector: {css: "input[name='field_name']"}).browser_element
|
100
|
+
expect(element[:id]).to eq('field_id')
|
101
|
+
end
|
120
102
|
|
121
|
-
|
122
|
-
|
123
|
-
element = PageMagic::Element.new(:my_link, page, type: :link, selector: { text: 'my link' }).browser_element
|
124
|
-
element[:id].should == 'my_link'
|
103
|
+
it 'should return a prefetched value' do
|
104
|
+
described_class.new(:help, page, type: :link, browser_element: :prefetched_object).browser_element.should == :prefetched_object
|
125
105
|
end
|
126
106
|
|
127
|
-
it 'should
|
128
|
-
|
129
|
-
element[:id].should == 'my_button'
|
107
|
+
it 'should raise errors for unsupported selectors' do
|
108
|
+
expect { described_class.new(:my_link, page, type: :link, selector: {unsupported: ''}).browser_element }.to raise_error(PageMagic::UnsupportedSelectorException)
|
130
109
|
end
|
131
|
-
end
|
132
|
-
end
|
133
110
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
111
|
+
context 'text selector' do
|
112
|
+
it 'should locate a link' do
|
113
|
+
element = described_class.new(:my_link, page, type: :link, selector: {text: 'link in a form'}).browser_element
|
114
|
+
expect(element[:id]).to eq('form_link')
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should locate a button' do
|
118
|
+
element = described_class.new(:my_button, page, type: :button, selector: {text: 'a button'}).browser_element
|
119
|
+
element[:id].should == 'form_button'
|
139
120
|
end
|
140
|
-
end
|
141
|
-
it 'returns true' do
|
142
|
-
expect(subject.section?).to eq(true)
|
143
121
|
end
|
144
122
|
end
|
145
123
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
124
|
+
describe '#section?' do
|
125
|
+
context 'element definitions exist' do
|
126
|
+
subject do
|
127
|
+
described_class.new(:my_link, :page, type: :link, selector: {text: 'my link'}) do
|
128
|
+
element :thing, text: 'text'
|
129
|
+
end
|
130
|
+
end
|
131
|
+
it 'returns true' do
|
132
|
+
expect(subject.section?).to eq(true)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'method defined' do
|
137
|
+
subject do
|
138
|
+
described_class.new(:my_link, :page, type: :link, selector: {text: 'my link'}) do
|
139
|
+
def custom_method
|
140
|
+
end
|
150
141
|
end
|
151
142
|
end
|
143
|
+
|
144
|
+
it 'returns true' do
|
145
|
+
expect(subject.section?).to eq(true)
|
146
|
+
end
|
152
147
|
end
|
153
148
|
|
154
|
-
|
155
|
-
|
149
|
+
context 'neither method or elements defined' do
|
150
|
+
subject do
|
151
|
+
described_class.new(:my_link, :page, type: :link, selector: {text: 'my link'})
|
152
|
+
end
|
153
|
+
it 'returns false' do
|
154
|
+
expect(subject.section?).to eq(false)
|
155
|
+
end
|
156
156
|
end
|
157
157
|
end
|
158
158
|
|
159
|
-
|
159
|
+
describe 'session' do
|
160
|
+
it 'should have a handle to the session' do
|
161
|
+
expect(described_class.new(:help, page, type: :link, selector: :selector).session).to eq(page.session)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe 'hooks' do
|
160
166
|
subject do
|
161
|
-
|
167
|
+
described_class.new(:my_button, page, type: :button, selector: {id: 'my_button'}) do
|
168
|
+
before do
|
169
|
+
call_in_before_hook
|
170
|
+
end
|
171
|
+
end
|
162
172
|
end
|
163
|
-
|
164
|
-
|
173
|
+
context 'method called in before hook' do
|
174
|
+
it 'calls methods on the page element' do
|
175
|
+
expect(page.browser).to receive(:find).and_return(double('button', click: true))
|
176
|
+
expect(subject).to receive(:call_in_before_hook)
|
177
|
+
subject.click
|
178
|
+
end
|
165
179
|
end
|
166
|
-
end
|
167
|
-
end
|
168
180
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
181
|
+
context 'method called in before hook' do
|
182
|
+
subject do
|
183
|
+
described_class.new(:my_button, page, type: :button, selector: {id: 'my_button'}) do
|
184
|
+
after do
|
185
|
+
call_in_after_hook
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
it 'calls methods on the page element' do
|
190
|
+
expect(page.browser).to receive(:find).and_return(double('button', click: true))
|
191
|
+
expect(subject).to receive(:call_in_after_hook)
|
192
|
+
subject.click
|
193
|
+
end
|
173
194
|
end
|
174
|
-
page = page_class.new
|
175
195
|
|
176
|
-
PageMagic::Element.new(:help, page, type: :link, selector: :selector).session.should == page.session
|
177
196
|
end
|
178
|
-
end
|
179
197
|
|
180
|
-
|
181
|
-
include_context :webapp
|
182
|
-
|
183
|
-
before :each do
|
184
|
-
@elements_page = elements_page.new
|
185
|
-
@elements_page.visit
|
186
|
-
end
|
198
|
+
describe '#method_missing' do
|
187
199
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
selector css: '.form'
|
194
|
-
link(:link_in_form, text: 'a in a form')
|
200
|
+
before do
|
201
|
+
page_class.class_eval do
|
202
|
+
section :form_by_css, css: '.form' do
|
203
|
+
link(:link_in_form, text: 'a in a form')
|
204
|
+
end
|
195
205
|
end
|
206
|
+
end
|
196
207
|
|
197
|
-
|
198
|
-
|
199
|
-
link(:link_in_form, text: 'a in a form')
|
200
|
-
end
|
208
|
+
it 'can delegate to capybara' do
|
209
|
+
expect(page.form_by_css).to be_visible
|
201
210
|
end
|
202
|
-
end
|
203
211
|
|
204
|
-
|
205
|
-
|
206
|
-
|
212
|
+
context 'method not on capybara browser element' do
|
213
|
+
it 'uses the parent page element' do
|
214
|
+
page_class.class_eval do
|
215
|
+
def parent_method
|
216
|
+
:called
|
217
|
+
end
|
218
|
+
end
|
219
|
+
expect(page.form_by_css.parent_method).to eq(:called)
|
220
|
+
end
|
207
221
|
end
|
208
222
|
|
209
|
-
|
210
|
-
|
223
|
+
context 'no element definition and not a capybara method' do
|
224
|
+
it 'throws and exception' do
|
225
|
+
expect { page.form_by_css.bobbins }.to raise_exception NoMethodError
|
226
|
+
end
|
211
227
|
end
|
212
|
-
end
|
213
228
|
|
214
|
-
it 'can have elements' do
|
215
|
-
@elements_page.form_by_css.link_in_form.visible?.should be_true
|
216
229
|
end
|
230
|
+
|
217
231
|
end
|
218
|
-
end
|
232
|
+
end
|
@@ -1,126 +1,72 @@
|
|
1
|
-
|
2
|
-
include_context :webapp
|
1
|
+
module PageMagic
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
describe ElementContext do
|
4
|
+
include_context :webapp
|
5
|
+
|
6
|
+
let!(:elements_page) do
|
7
|
+
Class.new do
|
8
|
+
include PageMagic
|
9
|
+
url '/elements'
|
10
|
+
link(:a_link, text: 'a link')
|
11
|
+
end
|
9
12
|
end
|
10
|
-
end
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
include PageMagic
|
15
|
-
url '/elements'
|
16
|
-
link(:a_link, text: 'a link')
|
14
|
+
let!(:session) do
|
15
|
+
double('session', raw_session: double('browser'))
|
17
16
|
end
|
18
|
-
end
|
19
17
|
|
20
|
-
|
21
|
-
double('session', raw_session: double('browser'))
|
22
|
-
end
|
18
|
+
describe '#method_missing' do
|
23
19
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
link(:link, :selector) do
|
28
|
-
fail('should not have been evaluated')
|
20
|
+
let(:page) do
|
21
|
+
elements_page.new.tap do |page|
|
22
|
+
page.visit
|
29
23
|
end
|
30
24
|
end
|
31
|
-
page = page1.new
|
32
|
-
page.visit
|
33
|
-
|
34
|
-
described_class.new(page, page.browser, self).next
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'should raise an error if an element is not found' do
|
39
|
-
expect { described_class.new(page1.new(session), session, self).missing_thing }.to raise_error PageMagic::ElementMissingException
|
40
|
-
end
|
41
25
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
26
|
+
context 'neither a method or page element are defined' do
|
27
|
+
it 'raises an error' do
|
28
|
+
expect { described_class.new(page, page.browser, self).missing_thing }.to raise_error PageMagic::ElementMissingException
|
29
|
+
end
|
46
30
|
end
|
47
|
-
end
|
48
|
-
|
49
|
-
described_class.new(page1.new(session), session, self).page_method.should == :called
|
50
|
-
end
|
51
31
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
32
|
+
context 'method is a element defintion' do
|
33
|
+
it 'returns the sub page element' do
|
34
|
+
element = described_class.new(page, page.browser, self).a_link
|
35
|
+
#TODO - returns the capybara object. maybe we should think about wrapping this.
|
36
|
+
expect(element.text).to eq('a link')
|
37
|
+
end
|
56
38
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
39
|
+
it 'does not evaluate any of the other definitions' do
|
40
|
+
elements_page.class_eval do
|
41
|
+
link(:another_link, :selector) do
|
42
|
+
fail('should not have been evaluated')
|
43
|
+
end
|
44
|
+
end
|
61
45
|
|
62
|
-
|
63
|
-
it 'should go through page sections' do
|
64
|
-
elements_page.class_eval do
|
65
|
-
section :form do
|
66
|
-
selector css: '.form'
|
67
|
-
link :form_link, text: 'in a form'
|
46
|
+
described_class.new(page, page.browser, self).a_link
|
68
47
|
end
|
69
48
|
end
|
70
49
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
# TODO: call page method, look for subelement, delagate to capybara object
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
describe 'hooks' do
|
83
|
-
subject(:page) do
|
84
|
-
elements_page.new.tap(&:visit)
|
85
|
-
end
|
86
|
-
|
87
|
-
before do
|
88
|
-
browser = page.browser
|
89
|
-
browser.should_receive(:call_in_before_hook)
|
90
|
-
browser.should_receive(:call_in_after_before_hook)
|
91
|
-
end
|
92
|
-
|
93
|
-
context 'section' do
|
94
|
-
it 'applies the hooks' do
|
95
|
-
elements_page.section(:form, id: 'form') do
|
96
|
-
link :form_link, id: 'form_link'
|
50
|
+
context 'method found on page_element' do
|
51
|
+
it 'calls page_element method' do
|
52
|
+
elements_page.class_eval do
|
53
|
+
def page_method
|
54
|
+
:called
|
55
|
+
end
|
56
|
+
end
|
97
57
|
|
98
|
-
|
99
|
-
|
100
|
-
after(&:call_in_after_before_hook)
|
58
|
+
expect(described_class.new(page, :browser, self).page_method).to eq(:called)
|
101
59
|
end
|
102
|
-
|
103
|
-
described_class.new(page, page.browser, self).form.click
|
104
60
|
end
|
105
61
|
end
|
106
62
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
63
|
+
describe '#respond_to?' do
|
64
|
+
subject do
|
65
|
+
described_class.new(elements_page.new(session), session, self)
|
66
|
+
end
|
67
|
+
it 'checks against the names of the elements passed in' do
|
68
|
+
expect(subject.respond_to?(:a_link)).to eq(true)
|
112
69
|
end
|
113
|
-
|
114
|
-
described_class.new(page, page.browser, self).create.click
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
describe '#respond_to?' do
|
119
|
-
subject do
|
120
|
-
described_class.new(page1.new(session), session, self)
|
121
|
-
end
|
122
|
-
it 'checks against the names of the elements passed in' do
|
123
|
-
expect(subject.respond_to?(:next)).to eq(true)
|
124
70
|
end
|
125
71
|
end
|
126
|
-
end
|
72
|
+
end
|
@@ -118,7 +118,7 @@ describe PageMagic::Elements do
|
|
118
118
|
describe 'location' do
|
119
119
|
context 'a prefetched object' do
|
120
120
|
it 'should add a section' do
|
121
|
-
expected_section = PageMagic::Element.new(:
|
121
|
+
expected_section = PageMagic::Element.new(:page_section, parent_page_element, type: :element, browser_element: :object)
|
122
122
|
page_elements.element :page_section, :object
|
123
123
|
expected_section.should == page_elements.elements(parent_page_element).first
|
124
124
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -12,6 +12,7 @@ require 'helpers/capybara'
|
|
12
12
|
|
13
13
|
shared_context :files do
|
14
14
|
require 'tmpdir'
|
15
|
+
|
15
16
|
def scratch_dir
|
16
17
|
@dir ||= Dir.mktmpdir
|
17
18
|
end
|
@@ -40,19 +41,20 @@ RSpec.configure do
|
|
40
41
|
end
|
41
42
|
end
|
42
43
|
alias_method :initialize_backup, :initialize
|
44
|
+
|
43
45
|
def initialize(*args, &block)
|
44
46
|
initialize_backup *args, &block
|
45
|
-
@before_hook = self.class.default_before_hook
|
46
|
-
@after_hook = self.class.default_after_hook
|
47
|
+
# @before_hook = self.class.default_before_hook
|
48
|
+
# @after_hook = self.class.default_after_hook
|
47
49
|
end
|
48
50
|
|
49
51
|
def ==(page_element)
|
50
52
|
page_element.is_a?(Element) &&
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
@before_hook == page_element.before &&
|
55
|
-
|
53
|
+
@type == page_element.type &&
|
54
|
+
@name == page_element.name &&
|
55
|
+
@selector == page_element.selector
|
56
|
+
# @before_hook == page_element.before &&
|
57
|
+
# @after_hook == page_element.after
|
56
58
|
end
|
57
59
|
end
|
58
60
|
end
|
@@ -72,11 +74,16 @@ RSpec.configure do
|
|
72
74
|
get '/elements' do
|
73
75
|
<<-ELEMENTS
|
74
76
|
<a href='#'>a link</a>
|
75
|
-
|
77
|
+
|
76
78
|
|
77
79
|
<div id='form' class="form">
|
78
|
-
<a id='form_link' href='/page2'>
|
80
|
+
<a id='form_link' href='/page2'>link in a form</a>
|
81
|
+
<label>enter text
|
82
|
+
<input id='field_id' name='field_name' class='input_class' type='text' value='filled in'/>
|
83
|
+
</label>
|
84
|
+
<input id='form_button' type='submit' value='a button'/>
|
79
85
|
</form>
|
86
|
+
|
80
87
|
ELEMENTS
|
81
88
|
end
|
82
89
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: page_magic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.alpha11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Leon Davis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|