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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5f5e27b2a270ba1333f9ae3b00500892ca2db6ea
4
- data.tar.gz: adb6f8c204b92c581ae8d05198899f1fe54decb9
3
+ metadata.gz: 3958a4d16d7ed2f76f9b58dd90e88c0e30a29dfb
4
+ data.tar.gz: 55f5d9af299438f3630ac2208a3c7e38eb02b735
5
5
  SHA512:
6
- metadata.gz: 220f194ecca9dc7ddcb6f912d0ea45caf922dc2704011e4fc5d897382d8af1d0a2a9725db585767d23a9913ad0f387e258dc0d9b73545290a25bb181b9e82578
7
- data.tar.gz: aeefe035655fc201c01d4539026be9162fea73fe7e173314ce0fe7389529d089507af383299727307598b1e0c406a576a55f160f38f5128df201dde38f2fa167
6
+ metadata.gz: dca1340ab4d09dd01f8b86614c33dd8e079ddc58d7897ebdb1a1d8dd44b750aa2aea85a7ec49935a8a79cb6f2533a7db3791cee1aa0226a806c513fb48148762
7
+ data.tar.gz: 415b1f023d0902de1a9933a426de9a5604a43765c09dd1a3af6e01fa29f876930f9c79dc23509441c8bdf1cad0ed0493a78202da56574816faa1e2549c7887a9
data/Gemfile CHANGED
@@ -11,6 +11,7 @@ group :test do
11
11
  gem 'poltergeist'
12
12
  gem 'codeclimate-test-reporter', group: :test, require: nil
13
13
  gem 'pullreview-coverage', require: false
14
+ gem 'reek'
14
15
  gem 'arbre'
15
16
  end
16
17
 
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.28)
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.4)
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.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.0.0)
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 (~> 1.2)
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.1)
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
- watir-webdriver (0.9.1)
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. The following example defines a text field called 'search_field' that can be found using its name which is 'q'
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
- ### Interacting with elements
109
- Elements are defined with an id which is the name of the method you will use to reference it. In the above example,
110
- the text field was defined with the ide `:search_field`.
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
- After visiting a page you are will get a `Session` object. Elements can be accessed through the session itself.
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
- ```ruby
115
- page.search_field.set 'page_magic'
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
- #### Multiple Results
119
- Where an element has been scoped to return multple results, these will be returned in an array.
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
- element :results, css: '.repo-list-item'
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: 'Ladtech')
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.6
1
+ 1.2.7
@@ -35,7 +35,7 @@ module PageMagic
35
35
  # @param [Object] other subject of equality check
36
36
  # @return [Boolean] true if the object is a match
37
37
  def ==(other)
38
- other.is_a?(Drivers) && all == other.all
38
+ other.respond_to?(:all) && all == other.all
39
39
  end
40
40
  end
41
41
  end
@@ -1,45 +1,36 @@
1
- require 'capybara/query'
2
1
  module PageMagic
3
2
  class Element
4
- # class Query - models overall queries for Capybara, queries can include:
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
- class << self
10
- # Find a query using it's name
11
- # @param [Symbol] type the name of the required query in snakecase format
12
- # @return [Query] returns the predefined query with the given name
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
- attr_reader :type
10
+ alias multiple_results? multiple_results
21
11
 
22
- # @param type -
23
- def initialize(type = nil)
24
- @type = type
12
+ def initialize(args, multiple_results: false)
13
+ @args = args
14
+ @multiple_results = multiple_results
25
15
  end
26
16
 
27
- # Build query parameters for Capybara's find method
28
- # @param [Hash] locator the location method e.g. text: 'button text'
29
- # @param [Hash] options additional options to be provided to Capybara. e.g. count: 3
30
- # @return [Array] list of compatible capybara query parameters.
31
- def build(locator, options = {})
32
- [].tap do |array|
33
- selector = Selector.find(locator.keys.first)
34
- array << selector.build(type, locator.values.first)
35
- array << options unless options.empty?
36
- end.flatten
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
- ELEMENT = Query.new
40
- TEXT_FIELD = CHECKBOX = SELECT_LIST = RADIOS = TEXTAREA = Query.new(:field)
41
- LINK = Query.new(:link)
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
@@ -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/query'
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
- elements = find(builder)
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
- query_args = builder.build_query
39
- result = page_element.browser_element.all(*query_args)
34
+ query = builder.build_query
35
+ result = query.execute(page_element.browser_element)
40
36
 
41
- if result.empty?
42
- query = Capybara::Query.new(*query_args)
43
- raise ElementMissingException, ELEMENT_NOT_FOUND_MSG % query.description
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
- @options = options
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
- Element::Query.find(type).build(selector, options)
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
@@ -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, :radios, :textarea].freeze
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
- options[:type] = __callee__
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 InvalidElementNameException < RuntimeError
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.6 ruby lib
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.6"
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 = "2016-07-28"
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
- it 'has a predefined query for each element type' do
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
- describe '.find' do
12
- it 'finds the constant with the given name' do
13
- expect(Query.find(:button)).to be(described_class::BUTTON)
14
- end
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 '#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 '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
- described_class.new(:field)
17
+ QueryBuilder.find(:link).build(css: 'wrong')
41
18
  end
42
19
 
43
- it 'passes element type through to the selector' do
44
- expect(selector).to receive(:build).with(:field, '.css').and_call_original
45
- subject.build(locator)
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
- class Query
52
- describe BUTTON do
53
- it 'has an element type' do
54
- expect(described_class.type).to eq(:button)
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
- describe ELEMENT do
59
- it ' does not has an element type' do
60
- expect(described_class.type).to be_nil
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
- describe LINK do
65
- it 'has an element type' do
66
- expect(described_class.type).to eq(:link)
67
- end
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
- describe TEXT_FIELD do
71
- it 'has an element type' do
72
- expect(described_class.type).to eq(:field)
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 same as all form field types' do
76
- expect(described_class).to eq(CHECKBOX).and eq(SELECT_LIST).and eq(RADIOS).and eq(TEXTAREA)
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
- expect(capybara_session.all(*Query.find(:text_field).build(name: 'field_name')).size).to eq(1)
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
- expect(capybara_session.all(*Query.find(:element).build(xpath: '//div/input')).size).to eq(1)
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
- expect(capybara_session.all(*Query.find(:text_field).build(id: 'field_id')).size).to eq(1)
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
- expect(capybara_session.all(*Query.find(:text_field).build(label: 'enter text')).size).to eq(1)
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
- expect(capybara_session.all(*Query.find(:link).build(text: 'a link')).size).to eq(1)
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.link :pass_through, css: 'a' do |args|
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.link :links, css: 'a'
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
@@ -69,7 +69,7 @@ module PageMagic
69
69
  element: Object.new,
70
70
  options: options)
71
71
 
72
- expect(builder.build_query).to eq([:xpath, '//xpath', options])
72
+ expect(builder.build_query).to eq(Element::Query.new([:xpath, '//xpath', options]))
73
73
  end
74
74
  end
75
75
  end
@@ -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
- <input id='form_button' type='submit' value='a button'/>
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.6
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: 2016-07-28 00:00:00.000000000 Z
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