page_magic 1.2.6 → 1.2.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +27 -7
- data/README.md +46 -14
- data/VERSION +1 -1
- data/lib/page_magic/drivers.rb +1 -1
- data/lib/page_magic/element/query.rb +24 -33
- data/lib/page_magic/element/query_builder.rb +48 -0
- data/lib/page_magic/element.rb +1 -1
- data/lib/page_magic/element_context.rb +7 -12
- data/lib/page_magic/element_definition_builder.rb +6 -3
- data/lib/page_magic/elements.rb +11 -5
- data/lib/page_magic/exceptions.rb +1 -32
- data/page_magic.gemspec +5 -3
- data/spec/page_magic/element/query_builder_spec.rb +108 -0
- data/spec/page_magic/element/query_spec.rb +33 -77
- data/spec/page_magic/element/selector_spec.rb +10 -5
- data/spec/page_magic/element_context_spec.rb +2 -9
- data/spec/page_magic/element_definition_builder_spec.rb +1 -1
- data/spec/page_magic/elements_spec.rb +1 -1
- data/spec/support/shared_contexts/webapp_fixture_context.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3958a4d16d7ed2f76f9b58dd90e88c0e30a29dfb
|
4
|
+
data.tar.gz: 55f5d9af299438f3630ac2208a3c7e38eb02b735
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dca1340ab4d09dd01f8b86614c33dd8e079ddc58d7897ebdb1a1d8dd44b750aa2aea85a7ec49935a8a79cb6f2533a7db3791cee1aa0226a806c513fb48148762
|
7
|
+
data.tar.gz: 415b1f023d0902de1a9933a426de9a5604a43765c09dd1a3af6e01fa29f876930f9c79dc23509441c8bdf1cad0ed0493a78202da56574816faa1e2549c7887a9
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -11,6 +11,10 @@ GEM
|
|
11
11
|
arbre (1.1.1)
|
12
12
|
activesupport (>= 3.0.0)
|
13
13
|
ast (2.3.0)
|
14
|
+
axiom-types (0.1.1)
|
15
|
+
descendants_tracker (~> 0.0.4)
|
16
|
+
ice_nine (~> 0.11.0)
|
17
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
14
18
|
builder (3.2.2)
|
15
19
|
capybara (2.7.1)
|
16
20
|
addressable
|
@@ -19,30 +23,36 @@ GEM
|
|
19
23
|
rack (>= 1.0.0)
|
20
24
|
rack-test (>= 0.5.4)
|
21
25
|
xpath (~> 2.0)
|
22
|
-
certifi (2016.02
|
26
|
+
certifi (2016.08.02)
|
23
27
|
childprocess (0.5.9)
|
24
28
|
ffi (~> 1.0, >= 1.0.11)
|
25
29
|
cliver (0.3.2)
|
30
|
+
codeclimate-engine-rb (0.3.1)
|
31
|
+
virtus (~> 1.0)
|
26
32
|
codeclimate-test-reporter (0.6.0)
|
27
33
|
simplecov (>= 0.7.1, < 1.0.0)
|
34
|
+
coercible (1.0.0)
|
35
|
+
descendants_tracker (~> 0.0.1)
|
28
36
|
descendants_tracker (0.0.4)
|
29
37
|
thread_safe (~> 0.3, >= 0.3.1)
|
30
38
|
diff-lcs (1.2.5)
|
31
39
|
docile (1.1.5)
|
40
|
+
equalizer (0.0.11)
|
32
41
|
faraday (0.9.2)
|
33
42
|
multipart-post (>= 1.2, < 3)
|
34
43
|
ffi (1.9.14)
|
35
44
|
git (1.3.0)
|
36
45
|
github-markup (1.4.0)
|
37
|
-
github_api (0.14.
|
46
|
+
github_api (0.14.5)
|
38
47
|
addressable (~> 2.4.0)
|
39
48
|
descendants_tracker (~> 0.0.4)
|
40
49
|
faraday (~> 0.8, < 0.10)
|
41
50
|
hashie (>= 3.4)
|
42
|
-
oauth2 (~> 1.0
|
51
|
+
oauth2 (~> 1.0)
|
43
52
|
hashie (3.4.4)
|
44
53
|
highline (1.7.8)
|
45
54
|
i18n (0.7.0)
|
55
|
+
ice_nine (0.11.2)
|
46
56
|
jeweler (2.1.1)
|
47
57
|
builder
|
48
58
|
bundler (>= 1.0)
|
@@ -66,12 +76,12 @@ GEM
|
|
66
76
|
nokogiri (1.6.8)
|
67
77
|
mini_portile2 (~> 2.1.0)
|
68
78
|
pkg-config (~> 1.1.7)
|
69
|
-
oauth2 (1.
|
79
|
+
oauth2 (1.2.0)
|
70
80
|
faraday (>= 0.8, < 0.10)
|
71
81
|
jwt (~> 1.0)
|
72
82
|
multi_json (~> 1.3)
|
73
83
|
multi_xml (~> 0.5)
|
74
|
-
rack (
|
84
|
+
rack (>= 1.2, < 3)
|
75
85
|
parser (2.3.1.2)
|
76
86
|
ast (~> 2.2)
|
77
87
|
pkg-config (1.1.7)
|
@@ -93,11 +103,15 @@ GEM
|
|
93
103
|
rdoc (4.2.2)
|
94
104
|
json (~> 1.4)
|
95
105
|
redcarpet (3.3.4)
|
106
|
+
reek (4.2.3)
|
107
|
+
codeclimate-engine-rb (~> 0.3.1)
|
108
|
+
parser (~> 2.3.1, >= 2.3.1.2)
|
109
|
+
rainbow (~> 2.0)
|
96
110
|
rspec (3.5.0)
|
97
111
|
rspec-core (~> 3.5.0)
|
98
112
|
rspec-expectations (~> 3.5.0)
|
99
113
|
rspec-mocks (~> 3.5.0)
|
100
|
-
rspec-core (3.5.
|
114
|
+
rspec-core (3.5.2)
|
101
115
|
rspec-support (~> 3.5.0)
|
102
116
|
rspec-expectations (3.5.0)
|
103
117
|
diff-lcs (>= 1.2.0, < 2.0)
|
@@ -133,7 +147,12 @@ GEM
|
|
133
147
|
tzinfo (1.2.2)
|
134
148
|
thread_safe (~> 0.1)
|
135
149
|
unicode-display_width (1.1.0)
|
136
|
-
|
150
|
+
virtus (1.0.5)
|
151
|
+
axiom-types (~> 0.1)
|
152
|
+
coercible (~> 1.0)
|
153
|
+
descendants_tracker (~> 0.0, >= 0.0.3)
|
154
|
+
equalizer (~> 0.0, >= 0.0.9)
|
155
|
+
watir-webdriver (0.9.3)
|
137
156
|
selenium-webdriver (>= 2.46.2)
|
138
157
|
websocket (1.2.3)
|
139
158
|
websocket-driver (0.6.4)
|
@@ -156,6 +175,7 @@ DEPENDENCIES
|
|
156
175
|
poltergeist
|
157
176
|
pullreview-coverage
|
158
177
|
redcarpet (~> 3.3)
|
178
|
+
reek
|
159
179
|
rspec
|
160
180
|
rubocop (~> 0.34)
|
161
181
|
simplecov
|
data/README.md
CHANGED
@@ -26,8 +26,9 @@ we are not aware of so your feedback/pull requests are greatly appreciated!
|
|
26
26
|
- [Quick Start](#quick-start)
|
27
27
|
- [Defining Pages](#defining-pages)
|
28
28
|
- [Elements](#elements)
|
29
|
+
- [Singlular Results](#singular-results)
|
30
|
+
- [Multiple Results](#multiple-results)
|
29
31
|
- [Interacting with elements](#interacting-with-elements)
|
30
|
-
- [Multple Results](#multiple-results)
|
31
32
|
- [Sub elements](#sub-elements)
|
32
33
|
- [Custom elements](#custom-elements)
|
33
34
|
- [Hooks](#hooks)
|
@@ -95,7 +96,12 @@ class Github
|
|
95
96
|
end
|
96
97
|
```
|
97
98
|
## Elements
|
98
|
-
Defining elements is easy.
|
99
|
+
Defining elements is easy. Just give the:
|
100
|
+
- element_type
|
101
|
+
- id to refer to it by
|
102
|
+
- selector to find it in the page
|
103
|
+
|
104
|
+
The following example defines a text field called 'search_field' that can be found using its name which is 'q'
|
99
105
|
|
100
106
|
```ruby
|
101
107
|
class Github
|
@@ -105,27 +111,53 @@ class Github
|
|
105
111
|
end
|
106
112
|
```
|
107
113
|
|
108
|
-
|
109
|
-
|
110
|
-
|
114
|
+
Element types supported by PageMagic are:
|
115
|
+
- text_field
|
116
|
+
- button
|
117
|
+
- link
|
118
|
+
- checkbox
|
119
|
+
- select_list
|
120
|
+
- radio
|
121
|
+
- textarea
|
111
122
|
|
112
|
-
|
123
|
+
PageMagic is very powerful and provides a number of different ways to define a page element so for more details please look at the [API](http://www.rubydoc.info/gems/page_magic/PageMagic%2FElements:element)
|
113
124
|
|
114
|
-
|
115
|
-
|
116
|
-
```
|
125
|
+
### Singular Results
|
126
|
+
The element types written above are the method names you need to use in order to define elements of those types on your pages.
|
117
127
|
|
118
|
-
|
119
|
-
|
128
|
+
Using them as they are written above will tell PageMagic to expect to find only one element using the selector you supply. Finding more than one result will cause PageMagic to raise an `AmbiguousQueryException`
|
129
|
+
|
130
|
+
### Multiple Results
|
131
|
+
Applying an 's' to the end of the element definition method name will tell PageMagic that more than one result can be returned using the given selector.
|
132
|
+
|
133
|
+
results are returned in an `Array`.
|
120
134
|
```ruby
|
121
135
|
class ResultsPage
|
122
136
|
include PageMagic
|
123
|
-
|
137
|
+
elements :results, css: '.repo-list-item'
|
124
138
|
end
|
125
139
|
|
126
140
|
page.results #=> Array<Element>
|
127
141
|
```
|
128
142
|
|
143
|
+
|
144
|
+
### Interacting with elements
|
145
|
+
```ruby
|
146
|
+
class Github
|
147
|
+
include PageMagic
|
148
|
+
|
149
|
+
text_field :search_field, name: 'q'
|
150
|
+
end
|
151
|
+
```
|
152
|
+
Elements are defined with an id which is the name of the method you will use to reference it. In the above example,
|
153
|
+
the text field was defined with the id `:search_field`.
|
154
|
+
|
155
|
+
After visiting a page you are will get a `Session` object. Elements can be accessed through the session itself.
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
page.search_field.set 'page_magic'
|
159
|
+
```
|
160
|
+
|
129
161
|
### Sub Elements
|
130
162
|
If your pages are complex you can use PageMagic to compose pages, their elements and subelements to as many levels as
|
131
163
|
you need to.
|
@@ -251,13 +283,13 @@ end
|
|
251
283
|
In the above example the selector looks for an element that has a link containing text that includes that organisation.
|
252
284
|
The example uses a named parameter and is invoked as follows.
|
253
285
|
```ruby
|
254
|
-
page.results(organisation: '
|
286
|
+
page.results(organisation: 'lvlup')
|
255
287
|
```
|
256
288
|
|
257
289
|
# Starting a session
|
258
290
|
To start a PageMagic session simply decide what browser you want to use and pass it to PageMagic's `.session` method
|
259
291
|
```ruby
|
260
|
-
session = PageMagic.session(browser: :chrome, url: 'https://www.github.com)
|
292
|
+
session = PageMagic.session(browser: :chrome, url: 'https://www.github.com')
|
261
293
|
```
|
262
294
|
|
263
295
|
Your session won't do much besides navigating to the given url until you have [mapped pages](#page-mapping) to it, so
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.2.
|
1
|
+
1.2.7
|
data/lib/page_magic/drivers.rb
CHANGED
@@ -1,45 +1,36 @@
|
|
1
|
-
require 'capybara/query'
|
2
1
|
module PageMagic
|
3
2
|
class Element
|
4
|
-
# class Query -
|
5
|
-
# - requirements on element type
|
6
|
-
# - selection criteria, modeled through the Selector class
|
7
|
-
# - options
|
3
|
+
# class Query - executes query on capybara driver
|
8
4
|
class Query
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def find(type)
|
14
|
-
query = constants.find { |constant| constant.to_s.casecmp(type.to_s).zero? }
|
15
|
-
return ELEMENT unless query
|
16
|
-
const_get(query)
|
17
|
-
end
|
18
|
-
end
|
5
|
+
# Message template for execptions raised as a result of calling method_missing
|
6
|
+
ELEMENT_NOT_FOUND_MSG = 'Unable to find %s'.freeze
|
7
|
+
|
8
|
+
attr_reader :args, :multiple_results
|
19
9
|
|
20
|
-
|
10
|
+
alias multiple_results? multiple_results
|
21
11
|
|
22
|
-
|
23
|
-
|
24
|
-
@
|
12
|
+
def initialize(args, multiple_results: false)
|
13
|
+
@args = args
|
14
|
+
@multiple_results = multiple_results
|
25
15
|
end
|
26
16
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
17
|
+
def execute(capybara_element)
|
18
|
+
if multiple_results
|
19
|
+
capybara_element.all(*args).to_a.tap do |result|
|
20
|
+
raise Capybara::ElementNotFound if result.empty?
|
21
|
+
end
|
22
|
+
else
|
23
|
+
capybara_element.find(*args)
|
24
|
+
end
|
25
|
+
rescue Capybara::Ambiguous => e
|
26
|
+
raise AmbiguousQueryException, e.message
|
27
|
+
rescue Capybara::ElementNotFound => e
|
28
|
+
raise ElementMissingException, e.message
|
37
29
|
end
|
38
30
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
BUTTON = Query.new(:button)
|
31
|
+
def ==(other)
|
32
|
+
other.respond_to?(:args) && args == other.args
|
33
|
+
end
|
43
34
|
end
|
44
35
|
end
|
45
36
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'capybara/query'
|
2
|
+
require 'page_magic/element/query'
|
3
|
+
module PageMagic
|
4
|
+
class Element
|
5
|
+
# class QueryBuilder - builds query to be executed on capybara driver, queries can include:
|
6
|
+
# - requirements on element type
|
7
|
+
# - selection criteria, modeled through the Selector class
|
8
|
+
# - options
|
9
|
+
class QueryBuilder
|
10
|
+
class << self
|
11
|
+
# Find a query using it's name
|
12
|
+
# @param [Symbol] type the name of the required query in snakecase format
|
13
|
+
# @return [QueryBuilder] returns the predefined query with the given name
|
14
|
+
def find(type)
|
15
|
+
query = constants.find { |constant| constant.to_s.casecmp(type.to_s).zero? }
|
16
|
+
return ELEMENT unless query
|
17
|
+
const_get(query)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :type
|
22
|
+
|
23
|
+
# @param type -
|
24
|
+
def initialize(type = nil)
|
25
|
+
@type = type
|
26
|
+
end
|
27
|
+
|
28
|
+
# Build query parameters for Capybara's find method
|
29
|
+
# @param [Hash] locator the location method e.g. text: 'button text'
|
30
|
+
# @param [Hash] capybara_options additional options to be provided to Capybara. e.g. count: 3
|
31
|
+
# @return [Array] list of compatible capybara query parameters.
|
32
|
+
def build(locator, capybara_options = {}, multiple_results: false)
|
33
|
+
args = [].tap do |array|
|
34
|
+
selector = Selector.find(locator.keys.first)
|
35
|
+
array << selector.build(type, locator.values.first)
|
36
|
+
array << capybara_options unless capybara_options.empty?
|
37
|
+
end.flatten
|
38
|
+
|
39
|
+
Query.new(args, multiple_results: multiple_results)
|
40
|
+
end
|
41
|
+
|
42
|
+
ELEMENT = QueryBuilder.new
|
43
|
+
TEXT_FIELD = CHECKBOX = SELECT_LIST = RADIO = TEXTAREA = QueryBuilder.new(:field)
|
44
|
+
LINK = QueryBuilder.new(:link)
|
45
|
+
BUTTON = QueryBuilder.new(:button)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/page_magic/element.rb
CHANGED
@@ -2,7 +2,7 @@ require 'forwardable'
|
|
2
2
|
require 'page_magic/element/selector_methods'
|
3
3
|
require 'page_magic/element/locators'
|
4
4
|
require 'page_magic/element/selector'
|
5
|
-
require 'page_magic/element/
|
5
|
+
require 'page_magic/element/query_builder'
|
6
6
|
module PageMagic
|
7
7
|
# class Element - represents an element in a html page.
|
8
8
|
class Element
|
@@ -1,9 +1,6 @@
|
|
1
1
|
module PageMagic
|
2
2
|
# class ElementContext - resolves which element definition to use when accessing the browser.
|
3
3
|
class ElementContext
|
4
|
-
# Message template for execptions raised as a result of calling method_missing
|
5
|
-
ELEMENT_NOT_FOUND_MSG = 'Unable to find %s'.freeze
|
6
|
-
|
7
4
|
attr_reader :page_element
|
8
5
|
|
9
6
|
def initialize(page_element)
|
@@ -24,8 +21,7 @@ module PageMagic
|
|
24
21
|
prefecteched_element = builder.element
|
25
22
|
return builder.build(prefecteched_element) if prefecteched_element
|
26
23
|
|
27
|
-
|
28
|
-
elements.size == 1 ? elements.first : elements
|
24
|
+
find(builder)
|
29
25
|
end
|
30
26
|
|
31
27
|
def respond_to?(*args)
|
@@ -35,15 +31,14 @@ module PageMagic
|
|
35
31
|
private
|
36
32
|
|
37
33
|
def find(builder)
|
38
|
-
|
39
|
-
result = page_element.browser_element
|
34
|
+
query = builder.build_query
|
35
|
+
result = query.execute(page_element.browser_element)
|
40
36
|
|
41
|
-
if
|
42
|
-
|
43
|
-
|
37
|
+
if query.multiple_results?
|
38
|
+
result.collect { |e| builder.build(e) }
|
39
|
+
else
|
40
|
+
builder.build(result)
|
44
41
|
end
|
45
|
-
|
46
|
-
result.to_a.collect { |e| builder.build(e) }
|
47
42
|
end
|
48
43
|
end
|
49
44
|
end
|
@@ -2,7 +2,7 @@ module PageMagic
|
|
2
2
|
# Builder for creating ElementDefinitions
|
3
3
|
class ElementDefinitionBuilder
|
4
4
|
INVALID_SELECTOR_MSG = 'Pass a locator/define one on the class'.freeze
|
5
|
-
attr_reader :definition_class, :options, :selector, :type, :element
|
5
|
+
attr_reader :definition_class, :options, :selector, :type, :element, :query_builder
|
6
6
|
|
7
7
|
def initialize(definition_class:, selector:, type:, options: {}, element: nil)
|
8
8
|
unless element
|
@@ -13,13 +13,16 @@ module PageMagic
|
|
13
13
|
@definition_class = definition_class
|
14
14
|
@selector = selector
|
15
15
|
@type = type
|
16
|
-
@
|
16
|
+
@query_builder = Element::QueryBuilder.find(type)
|
17
|
+
|
18
|
+
@options = { multiple_results: false }.merge(options)
|
17
19
|
@element = element
|
18
20
|
end
|
19
21
|
|
20
22
|
# @return [Capybara::Query] query to find this element in the browser
|
21
23
|
def build_query
|
22
|
-
|
24
|
+
multiple_results = options.delete(:multiple_results)
|
25
|
+
query_builder.build(selector, options, multiple_results: multiple_results)
|
23
26
|
end
|
24
27
|
|
25
28
|
# Create new instance of the ElementDefinition modeled by this builder
|
data/lib/page_magic/elements.rb
CHANGED
@@ -14,7 +14,9 @@ module PageMagic
|
|
14
14
|
|
15
15
|
INVALID_METHOD_NAME_MSG = 'a method already exists with this method name'.freeze
|
16
16
|
|
17
|
-
TYPES = [:text_field, :button, :link, :checkbox, :select_list, :
|
17
|
+
TYPES = [:text_field, :button, :link, :checkbox, :select_list, :radio, :textarea].collect do |type|
|
18
|
+
[type, :"#{type}s"]
|
19
|
+
end.flatten.freeze
|
18
20
|
|
19
21
|
class << self
|
20
22
|
def extended(clazz)
|
@@ -52,8 +54,8 @@ module PageMagic
|
|
52
54
|
# @param [Hash] selector a key value pair defining the method for locating this element. See above for details
|
53
55
|
def element(*args, &block)
|
54
56
|
block ||= proc {}
|
55
|
-
options = compute_options(args.dup)
|
56
|
-
|
57
|
+
options = compute_options(args.dup, __callee__)
|
58
|
+
|
57
59
|
section_class = options.delete(:section_class)
|
58
60
|
|
59
61
|
add_element_definition(options.delete(:name)) do |parent_element, *e_args|
|
@@ -75,13 +77,17 @@ module PageMagic
|
|
75
77
|
|
76
78
|
private
|
77
79
|
|
78
|
-
def compute_options(args)
|
80
|
+
def compute_options(args, type)
|
79
81
|
section_class = remove_argument(args, Class) || Element
|
82
|
+
|
80
83
|
{ name: compute_name(args, section_class),
|
84
|
+
type: type,
|
81
85
|
selector: compute_selector(args, section_class),
|
82
86
|
options: compute_argument(args, Hash),
|
83
87
|
element: args.delete_at(0),
|
84
|
-
section_class: section_class }
|
88
|
+
section_class: section_class }.tap do |hash|
|
89
|
+
hash[:options][:multiple_results] = type.to_s.end_with?('s')
|
90
|
+
end
|
85
91
|
end
|
86
92
|
|
87
93
|
def add_element_definition(name, &block)
|
@@ -2,38 +2,7 @@ module PageMagic
|
|
2
2
|
class ElementMissingException < RuntimeError
|
3
3
|
end
|
4
4
|
|
5
|
-
class
|
6
|
-
end
|
7
|
-
|
8
|
-
class InvalidMethodNameException < RuntimeError
|
9
|
-
end
|
10
|
-
|
11
|
-
class InvalidURLException < RuntimeError
|
12
|
-
end
|
13
|
-
|
14
|
-
class MatcherInvalidException < RuntimeError
|
15
|
-
end
|
16
|
-
|
17
|
-
class TimeoutException < RuntimeError
|
18
|
-
end
|
19
|
-
|
20
|
-
class UnspportedBrowserException < RuntimeError
|
21
|
-
end
|
22
|
-
|
23
|
-
class UnsupportedCriteriaException < RuntimeError
|
24
|
-
end
|
25
|
-
|
26
|
-
class UnsupportedSelectorException < RuntimeError
|
27
|
-
end
|
28
|
-
|
29
|
-
class UndefinedSelectorException < RuntimeError
|
30
|
-
end
|
31
|
-
|
32
|
-
class NotSupportedException < RuntimeError
|
33
|
-
end
|
34
|
-
end
|
35
|
-
module PageMagic
|
36
|
-
class ElementMissingException < RuntimeError
|
5
|
+
class AmbiguousQueryException < RuntimeError
|
37
6
|
end
|
38
7
|
|
39
8
|
class InvalidElementNameException < RuntimeError
|
data/page_magic.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: page_magic 1.2.
|
5
|
+
# stub: page_magic 1.2.7 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "page_magic"
|
9
|
-
s.version = "1.2.
|
9
|
+
s.version = "1.2.7"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Leon Davis"]
|
14
|
-
s.date = "
|
14
|
+
s.date = "2017-02-17"
|
15
15
|
s.description = "Framework for modeling and interacting with webpages which wraps capybara"
|
16
16
|
s.email = "info@lad-tech.com"
|
17
17
|
s.extra_rdoc_files = [
|
@@ -40,6 +40,7 @@ Gem::Specification.new do |s|
|
|
40
40
|
"lib/page_magic/element.rb",
|
41
41
|
"lib/page_magic/element/locators.rb",
|
42
42
|
"lib/page_magic/element/query.rb",
|
43
|
+
"lib/page_magic/element/query_builder.rb",
|
43
44
|
"lib/page_magic/element/selector.rb",
|
44
45
|
"lib/page_magic/element/selector_methods.rb",
|
45
46
|
"lib/page_magic/element_context.rb",
|
@@ -62,6 +63,7 @@ Gem::Specification.new do |s|
|
|
62
63
|
"spec/page_magic/drivers/selenium_spec.rb",
|
63
64
|
"spec/page_magic/drivers_spec.rb",
|
64
65
|
"spec/page_magic/element/locators_spec.rb",
|
66
|
+
"spec/page_magic/element/query_builder_spec.rb",
|
65
67
|
"spec/page_magic/element/query_spec.rb",
|
66
68
|
"spec/page_magic/element/selector_spec.rb",
|
67
69
|
"spec/page_magic/element_context_spec.rb",
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module PageMagic
|
2
|
+
class Element
|
3
|
+
describe QueryBuilder do
|
4
|
+
it 'has a predefined query for each element type' do
|
5
|
+
missing = PageMagic::Elements::TYPES.dup.delete_if { |type| type.to_s.end_with?('s') }.find_all do |type|
|
6
|
+
described_class.constants.include?(type)
|
7
|
+
end
|
8
|
+
expect(missing).to be_empty
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.find' do
|
12
|
+
it 'finds the constant with the given name' do
|
13
|
+
expect(described_class.find(:button)).to be(described_class::BUTTON)
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'constant not found' do
|
17
|
+
it 'returns a default' do
|
18
|
+
expect(described_class.find(:billy)).to be(described_class::ELEMENT)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#build' do
|
24
|
+
let(:selector) { Selector.new }
|
25
|
+
before do
|
26
|
+
expect(Selector).to receive(:find).with(:css).and_return(selector)
|
27
|
+
end
|
28
|
+
let(:locator) { { css: '.css' } }
|
29
|
+
|
30
|
+
it 'builds a query using the correct selector' do
|
31
|
+
expected = Query.new(locator.values)
|
32
|
+
expect(subject.build(locator)).to eq(expected)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'adds options to the result' do
|
36
|
+
expected = Query.new(locator.values.concat([:options]))
|
37
|
+
expect(subject.build(locator, :options)).to eq(expected)
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'selector support element type' do
|
41
|
+
subject do
|
42
|
+
described_class.new(:field)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'passes element type through to the selector' do
|
46
|
+
expect(selector).to receive(:build).with(:field, '.css').and_call_original
|
47
|
+
subject.build(locator)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class QueryBuilder
|
54
|
+
describe BUTTON do
|
55
|
+
it 'has an element type' do
|
56
|
+
expect(described_class.type).to eq(:button)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe ELEMENT do
|
61
|
+
it ' does not has an element type' do
|
62
|
+
expect(described_class.type).to be_nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe LINK do
|
67
|
+
it 'has an element type' do
|
68
|
+
expect(described_class.type).to eq(:link)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe TEXT_FIELD do
|
73
|
+
it 'has an element type' do
|
74
|
+
expect(described_class.type).to eq(:field)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'the same as all form field types' do
|
78
|
+
expect(described_class).to eq(CHECKBOX).and eq(SELECT_LIST).and eq(RADIO).and eq(TEXTAREA)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'integration' do
|
84
|
+
include_context :webapp_fixture
|
85
|
+
let(:capybara_session) { Capybara::Session.new(:rack_test, rack_app).tap { |s| s.visit('/elements') } }
|
86
|
+
|
87
|
+
it 'finds fields' do
|
88
|
+
query = QueryBuilder.find(:text_field).build(name: 'field_name')
|
89
|
+
expect(query.execute(capybara_session).tag_name).to eq('input')
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'finds buttons' do
|
93
|
+
query = QueryBuilder.find(:button).build(text: 'a button')
|
94
|
+
expect(query.execute(capybara_session).tag_name).to eq('button')
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'finds links' do
|
98
|
+
query = QueryBuilder.find(:link).build(text: 'a link')
|
99
|
+
expect(query.execute(capybara_session).tag_name).to eq('a')
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'finds elements' do
|
103
|
+
query = QueryBuilder.find(:element).build(name: 'field_name')
|
104
|
+
expect(query.execute(capybara_session).tag_name).to eq('input')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -1,101 +1,57 @@
|
|
1
1
|
module PageMagic
|
2
2
|
class Element
|
3
3
|
describe Query do
|
4
|
-
|
5
|
-
missing = PageMagic::Elements::TYPES.find_all do |type|
|
6
|
-
!described_class.constants.include?(type.upcase.to_sym)
|
7
|
-
end
|
8
|
-
expect(missing).to be_empty
|
9
|
-
end
|
4
|
+
include_context :webapp_fixture
|
10
5
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
context 'constant not found' do
|
17
|
-
it 'returns a default' do
|
18
|
-
expect(Query.find(:billy)).to be(described_class::ELEMENT)
|
19
|
-
end
|
6
|
+
let(:page) do
|
7
|
+
elements_page = Class.new do
|
8
|
+
include PageMagic
|
9
|
+
url '/elements'
|
20
10
|
end
|
11
|
+
elements_page.visit(application: rack_app).current_page
|
21
12
|
end
|
22
13
|
|
23
|
-
describe '#
|
24
|
-
|
25
|
-
before do
|
26
|
-
expect(Selector).to receive(:find).with(:css).and_return(selector)
|
27
|
-
end
|
28
|
-
let(:locator) { { css: '.css' } }
|
29
|
-
|
30
|
-
it 'uses the locator to find the correct selector builder' do
|
31
|
-
expect(subject.build(locator)).to eq(locator.values)
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'adds options to the result' do
|
35
|
-
expect(subject.build(locator, :options)).to eq(locator.values.concat([:options]))
|
36
|
-
end
|
37
|
-
|
38
|
-
context 'selector support element type' do
|
14
|
+
describe '#execute' do
|
15
|
+
context 'no results found' do
|
39
16
|
subject do
|
40
|
-
|
17
|
+
QueryBuilder.find(:link).build(css: 'wrong')
|
41
18
|
end
|
42
19
|
|
43
|
-
it '
|
44
|
-
|
45
|
-
subject.
|
20
|
+
it 'raises an error' do
|
21
|
+
expected_message = 'Unable to find css "wrong"'
|
22
|
+
expect { subject.execute(page.browser) }.to raise_exception(ElementMissingException, expected_message)
|
46
23
|
end
|
47
24
|
end
|
48
|
-
end
|
49
|
-
end
|
50
25
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
56
|
-
end
|
26
|
+
context 'to many results returned' do
|
27
|
+
subject do
|
28
|
+
QueryBuilder.find(:link).build(css: 'a')
|
29
|
+
end
|
57
30
|
|
58
|
-
|
59
|
-
|
60
|
-
|
31
|
+
it 'raises an error' do
|
32
|
+
expected_message = 'Ambiguous match, found 2 elements matching css "a"'
|
33
|
+
expect { subject.execute(page.browser) }.to raise_error AmbiguousQueryException, expected_message
|
34
|
+
end
|
61
35
|
end
|
62
|
-
end
|
63
36
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
37
|
+
context 'multiple results found' do
|
38
|
+
subject do
|
39
|
+
QueryBuilder.find(:link).build({ css: 'a' }, {}, multiple_results: true)
|
40
|
+
end
|
69
41
|
|
70
|
-
|
71
|
-
|
72
|
-
|
42
|
+
it 'returns an array' do
|
43
|
+
result = subject.execute(page.browser)
|
44
|
+
expect(result).to be_a(Array)
|
45
|
+
expect(result.size).to eq(2)
|
46
|
+
end
|
73
47
|
end
|
74
48
|
|
75
|
-
it 'the
|
76
|
-
|
49
|
+
it 'returns the result of the capybara query' do
|
50
|
+
query = QueryBuilder.find(:link).build(id: 'form_link')
|
51
|
+
result = query.execute(page.browser)
|
52
|
+
expect(result.text).to eq('link in a form')
|
77
53
|
end
|
78
54
|
end
|
79
55
|
end
|
80
|
-
|
81
|
-
context 'integration' do
|
82
|
-
include_context :webapp_fixture
|
83
|
-
let(:capybara_session) { Capybara::Session.new(:rack_test, rack_app).tap { |s| s.visit('/elements') } }
|
84
|
-
it 'finds fields' do
|
85
|
-
expect(capybara_session.all(*Query.find(:text_field).build(name: 'field_name')).size).to eq(1)
|
86
|
-
end
|
87
|
-
|
88
|
-
it 'finds buttons' do
|
89
|
-
expect(capybara_session.all(*Query.find(:button).build(text: 'a button')).size).to eq(1)
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'finds links' do
|
93
|
-
expect(capybara_session.all(*Query.find(:link).build(text: 'a link')).size).to eq(1)
|
94
|
-
end
|
95
|
-
|
96
|
-
it 'finds elements' do
|
97
|
-
expect(capybara_session.all(*Query.find(:element).build(name: 'field_name')).size).to eq(1)
|
98
|
-
end
|
99
|
-
end
|
100
56
|
end
|
101
57
|
end
|
@@ -128,23 +128,28 @@ module PageMagic
|
|
128
128
|
let(:capybara_session) { Capybara::Session.new(:rack_test, rack_app).tap { |s| s.visit('/elements') } }
|
129
129
|
|
130
130
|
it 'finds elements by name' do
|
131
|
-
|
131
|
+
query = QueryBuilder.find(:text_field).build(name: 'field_name')
|
132
|
+
expect(query.execute(capybara_session)[:name]).to eq('field_name')
|
132
133
|
end
|
133
134
|
|
134
135
|
it 'finds elements by xpath' do
|
135
|
-
|
136
|
+
query = QueryBuilder.find(:element).build(xpath: '//div/label/input')
|
137
|
+
expect(query.execute(capybara_session)[:name]).to eq('field_name')
|
136
138
|
end
|
137
139
|
|
138
140
|
it 'finds elements by id' do
|
139
|
-
|
141
|
+
query = QueryBuilder.find(:text_field).build(id: 'field_id')
|
142
|
+
expect(query.execute(capybara_session)[:name]).to eq('field_name')
|
140
143
|
end
|
141
144
|
|
142
145
|
it 'finds elements by label' do
|
143
|
-
|
146
|
+
query = QueryBuilder.find(:text_field).build(label: 'enter text')
|
147
|
+
expect(query.execute(capybara_session)[:name]).to eq('field_name')
|
144
148
|
end
|
145
149
|
|
146
150
|
it 'finds elements by text' do
|
147
|
-
|
151
|
+
query = QueryBuilder.find(:link).build(text: 'a link')
|
152
|
+
expect(query.execute(capybara_session).text).to eq('a link')
|
148
153
|
end
|
149
154
|
end
|
150
155
|
end
|
@@ -31,7 +31,7 @@ module PageMagic
|
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'passes arguments through to the element definition' do
|
34
|
-
elements_page.
|
34
|
+
elements_page.links :pass_through, css: 'a' do |args|
|
35
35
|
args[:passed_through] = true
|
36
36
|
end
|
37
37
|
args = {}
|
@@ -51,19 +51,12 @@ module PageMagic
|
|
51
51
|
|
52
52
|
context 'more than one match found in the browser' do
|
53
53
|
it 'returns an array of element definitions' do
|
54
|
-
elements_page.
|
54
|
+
elements_page.links :links, css: 'a'
|
55
55
|
links = described_class.new(page).links
|
56
56
|
expect(links.find_all { |e| e.class == Element }.size).to eq(2)
|
57
57
|
expect(links.collect(&:text)).to eq(['a link', 'link in a form'])
|
58
58
|
end
|
59
59
|
end
|
60
|
-
context 'no results found' do
|
61
|
-
it 'raises an error' do
|
62
|
-
elements_page.link :missing, css: 'wrong'
|
63
|
-
expected_message = described_class::ELEMENT_NOT_FOUND_MSG % 'css "wrong"'
|
64
|
-
expect { described_class.new(page).missing }.to raise_exception(ElementMissingException, expected_message)
|
65
|
-
end
|
66
|
-
end
|
67
60
|
end
|
68
61
|
|
69
62
|
context 'method found on page_element' do
|
@@ -22,7 +22,7 @@ module PageMagic
|
|
22
22
|
expected_definition = ElementDefinitionBuilder.new(definition_class: Element,
|
23
23
|
type: :text_field,
|
24
24
|
selector: child_selector,
|
25
|
-
options: {})
|
25
|
+
options: { multiple_results: false })
|
26
26
|
subject.text_field :alias, child_selector
|
27
27
|
expect(instance.element_by_name(:alias)).to eq(expected_definition)
|
28
28
|
end
|
@@ -21,7 +21,7 @@ shared_context :webapp_fixture do
|
|
21
21
|
<label>enter text
|
22
22
|
<input id='field_id' name='field_name' class='input_class' type='text' value='filled in'/>
|
23
23
|
</label>
|
24
|
-
<
|
24
|
+
<button id='form_button' type='submit' value='a button'/>
|
25
25
|
</form>
|
26
26
|
|
27
27
|
ELEMENTS
|
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.2.
|
4
|
+
version: 1.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Leon Davis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|
@@ -137,6 +137,7 @@ files:
|
|
137
137
|
- lib/page_magic/element.rb
|
138
138
|
- lib/page_magic/element/locators.rb
|
139
139
|
- lib/page_magic/element/query.rb
|
140
|
+
- lib/page_magic/element/query_builder.rb
|
140
141
|
- lib/page_magic/element/selector.rb
|
141
142
|
- lib/page_magic/element/selector_methods.rb
|
142
143
|
- lib/page_magic/element_context.rb
|
@@ -159,6 +160,7 @@ files:
|
|
159
160
|
- spec/page_magic/drivers/selenium_spec.rb
|
160
161
|
- spec/page_magic/drivers_spec.rb
|
161
162
|
- spec/page_magic/element/locators_spec.rb
|
163
|
+
- spec/page_magic/element/query_builder_spec.rb
|
162
164
|
- spec/page_magic/element/query_spec.rb
|
163
165
|
- spec/page_magic/element/selector_spec.rb
|
164
166
|
- spec/page_magic/element_context_spec.rb
|