page_magic 1.2.8 → 2.0.2
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 +5 -5
- data/.rubocop.yml +23 -4
- data/.simplecov +5 -3
- data/.zsh_config +6 -0
- data/Dockerfile +11 -0
- data/Gemfile +13 -13
- data/Gemfile.lock +136 -148
- data/Makefile +17 -0
- data/README.md +26 -5
- data/Rakefile +12 -2
- data/VERSION +1 -1
- data/circle.yml +3 -1
- data/lib/active_support/core_ext/object/to_query.rb +84 -0
- data/lib/page_magic.rb +31 -24
- data/lib/page_magic/class_methods.rb +5 -2
- data/lib/page_magic/comparator.rb +37 -0
- data/lib/page_magic/comparator/fuzzy.rb +23 -0
- data/lib/page_magic/comparator/literal.rb +22 -0
- data/lib/page_magic/comparator/null.rb +26 -0
- data/lib/page_magic/comparator/parameter_map.rb +52 -0
- data/lib/page_magic/driver.rb +3 -0
- data/lib/page_magic/drivers.rb +6 -5
- data/lib/page_magic/drivers/poltergeist.rb +2 -0
- data/lib/page_magic/drivers/rack_test.rb +3 -1
- data/lib/page_magic/drivers/selenium.rb +4 -2
- data/lib/page_magic/element.rb +35 -15
- data/lib/page_magic/element/locators.rb +8 -5
- data/lib/page_magic/element/not_found.rb +38 -0
- data/lib/page_magic/element/query.rb +21 -20
- data/lib/page_magic/element/query/multiple_results.rb +21 -0
- data/lib/page_magic/element/query/prefetched_result.rb +26 -0
- data/lib/page_magic/element/query/single_result.rb +20 -0
- data/lib/page_magic/element/selector.rb +41 -16
- data/lib/page_magic/element/selector/methods.rb +18 -0
- data/lib/page_magic/element/selector/model.rb +21 -0
- data/lib/page_magic/element_context.rb +7 -21
- data/lib/page_magic/element_definition_builder.rb +20 -24
- data/lib/page_magic/elements.rb +65 -69
- data/lib/page_magic/elements/config.rb +103 -0
- data/lib/page_magic/elements/inheritance_hooks.rb +15 -0
- data/lib/page_magic/elements/types.rb +25 -0
- data/lib/page_magic/exceptions.rb +6 -1
- data/lib/page_magic/instance_methods.rb +8 -3
- data/lib/page_magic/mapping.rb +79 -0
- data/lib/page_magic/session.rb +15 -35
- data/lib/page_magic/session_methods.rb +3 -1
- data/lib/page_magic/transitions.rb +49 -0
- data/lib/page_magic/utils/string.rb +18 -0
- data/lib/page_magic/utils/url.rb +20 -0
- data/lib/page_magic/wait_methods.rb +3 -0
- data/lib/page_magic/watcher.rb +12 -17
- data/lib/page_magic/watchers.rb +31 -15
- data/page_magic.gemspec +15 -11
- data/spec/lib/active_support/core_ext/object/to_query_test.rb +78 -0
- data/spec/page_magic/class_methods_spec.rb +66 -37
- data/spec/page_magic/comparator/fuzzy_spec.rb +44 -0
- data/spec/page_magic/comparator/literal_spec.rb +41 -0
- data/spec/page_magic/comparator/null_spec.rb +35 -0
- data/spec/page_magic/comparator/parameter_map_spec.rb +75 -0
- data/spec/page_magic/driver_spec.rb +26 -28
- data/spec/page_magic/drivers/poltergeist_spec.rb +6 -7
- data/spec/page_magic/drivers/rack_test_spec.rb +6 -9
- data/spec/page_magic/drivers/selenium_spec.rb +11 -12
- data/spec/page_magic/drivers_spec.rb +38 -29
- data/spec/page_magic/element/locators_spec.rb +28 -25
- data/spec/page_magic/element/not_found_spec.rb +24 -0
- data/spec/page_magic/element/query/multiple_results_spec.rb +14 -0
- data/spec/page_magic/element/query/single_result_spec.rb +21 -0
- data/spec/page_magic/element/query_spec.rb +26 -45
- data/spec/page_magic/element/selector_spec.rb +120 -110
- data/spec/page_magic/element_context_spec.rb +47 -87
- data/spec/page_magic/element_definition_builder_spec.rb +14 -71
- data/spec/page_magic/element_spec.rb +256 -0
- data/spec/page_magic/elements/config_spec.rb +203 -0
- data/spec/page_magic/elements_spec.rb +90 -134
- data/spec/page_magic/instance_methods_spec.rb +65 -63
- data/spec/page_magic/mapping_spec.rb +181 -0
- data/spec/page_magic/session_methods_spec.rb +29 -25
- data/spec/page_magic/session_spec.rb +109 -199
- data/spec/page_magic/transitions_spec.rb +43 -0
- data/spec/page_magic/utils/string_spec.rb +29 -0
- data/spec/page_magic/utils/url_spec.rb +9 -0
- data/spec/page_magic/wait_methods_spec.rb +16 -22
- data/spec/page_magic/watcher_spec.rb +22 -0
- data/spec/page_magic/watchers_spec.rb +58 -62
- data/spec/page_magic_spec.rb +37 -29
- data/spec/spec_helper.rb +9 -2
- data/spec/support/shared_contexts.rb +3 -1
- data/spec/support/shared_examples.rb +17 -17
- metadata +101 -48
- data/lib/page_magic/element/query_builder.rb +0 -48
- data/lib/page_magic/element/selector_methods.rb +0 -13
- data/lib/page_magic/matcher.rb +0 -121
- data/spec/element_spec.rb +0 -249
- data/spec/page_magic/element/query_builder_spec.rb +0 -108
- data/spec/page_magic/matcher_spec.rb +0 -336
- data/spec/support/shared_contexts/files_context.rb +0 -7
- data/spec/support/shared_contexts/nested_elements_html_context.rb +0 -16
- data/spec/support/shared_contexts/rack_application_context.rb +0 -9
- data/spec/support/shared_contexts/webapp_fixture_context.rb +0 -39
- data/spec/watcher_spec.rb +0 -61
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PageMagic
|
|
4
|
+
class Comparator
|
|
5
|
+
# class Map - used to model parameter matching requirements
|
|
6
|
+
class ParameterMap < Comparator
|
|
7
|
+
def initialize(map)
|
|
8
|
+
comparator = normalise(map).keys.each_with_object({}) do |key, params|
|
|
9
|
+
params[key] = Comparator.for(map[key])
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
fuzzy = comparator.values.any?(&:fuzzy?)
|
|
13
|
+
super(comparator, fuzzy)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def <=>(other)
|
|
17
|
+
return 0 if empty? && other.empty?
|
|
18
|
+
return 1 if other.empty?
|
|
19
|
+
if (comparator.keys.size <=> other.comparator.keys.size).zero?
|
|
20
|
+
return literal_matchers.size <=> other.literal_matchers.size
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def empty?
|
|
27
|
+
comparator.empty?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def literal_matchers
|
|
31
|
+
comparator.values.find_all { |matcher| !matcher.fuzzy? }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def match?(params)
|
|
35
|
+
params_copy = normalise(params)
|
|
36
|
+
comparator.each do |key, value|
|
|
37
|
+
param = params_copy[key]
|
|
38
|
+
return false unless value&.match?(param)
|
|
39
|
+
end
|
|
40
|
+
true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def normalise(hash)
|
|
46
|
+
hash.keys.each_with_object({}) do |key, map|
|
|
47
|
+
map[key.to_sym] = hash[key]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/page_magic/driver.rb
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module PageMagic
|
|
2
4
|
# class Driver - instances are factories for drivers used by PageMagic
|
|
3
5
|
class Driver
|
|
4
6
|
attr_reader :supported_browsers, :handler
|
|
7
|
+
|
|
5
8
|
# Creates a driver definition
|
|
6
9
|
# @example
|
|
7
10
|
# Driver.new do |rack_application, options|
|
data/lib/page_magic/drivers.rb
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'driver'
|
|
4
|
+
require_relative 'utils/string'
|
|
2
5
|
module PageMagic
|
|
3
6
|
# class Drivers - creates an object that can be used to hold driver definitions
|
|
4
7
|
# These PageMagic gets the user's chosen driver from this object.
|
|
@@ -16,12 +19,10 @@ module PageMagic
|
|
|
16
19
|
# Loads drivers defined in files at the given path
|
|
17
20
|
# @param [String] path where the drivers are located
|
|
18
21
|
def load(path = "#{__dir__}/drivers")
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Dir["#{path}/*.rb"].each do |driver_file|
|
|
22
|
+
Dir["#{path}/*.rb"].sort.each do |driver_file|
|
|
22
23
|
require driver_file
|
|
23
24
|
driver_name = File.basename(driver_file)[/(.*)\.rb$/, 1]
|
|
24
|
-
register self.class.const_get(
|
|
25
|
+
register self.class.const_get(Utils::String.classify(driver_name))
|
|
25
26
|
end
|
|
26
27
|
end
|
|
27
28
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
PageMagic::Drivers::Selenium = PageMagic::Driver.new(:chrome, :firefox) do |app, options, browser|
|
|
2
|
-
require 'watir
|
|
3
|
-
Capybara::Selenium::Driver.new(app, options.dup.merge(browser: browser))
|
|
4
|
+
require 'watir'
|
|
5
|
+
Capybara::Selenium::Driver.new(app, **options.dup.merge(browser: browser))
|
|
4
6
|
end
|
data/lib/page_magic/element.rb
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'forwardable'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
require_relative 'element/not_found'
|
|
5
|
+
require_relative 'element/selector/methods'
|
|
6
|
+
require_relative 'element/locators'
|
|
7
|
+
require_relative 'element/selector'
|
|
8
|
+
require_relative 'element/query'
|
|
6
9
|
module PageMagic
|
|
7
10
|
# class Element - represents an element in a html page.
|
|
8
11
|
class Element
|
|
9
|
-
EVENT_TYPES = [
|
|
12
|
+
EVENT_TYPES = %i[set select select_option unselect_option click].freeze
|
|
10
13
|
DEFAULT_HOOK = proc {}.freeze
|
|
11
|
-
EVENT_NOT_SUPPORTED_MSG = '%s event not supported'
|
|
14
|
+
EVENT_NOT_SUPPORTED_MSG = '%s event not supported'
|
|
12
15
|
|
|
13
|
-
include
|
|
14
|
-
|
|
16
|
+
include Locators
|
|
17
|
+
include WaitMethods
|
|
18
|
+
include SessionMethods
|
|
19
|
+
include Watchers
|
|
20
|
+
include Selector::Methods
|
|
21
|
+
extend Forwardable
|
|
22
|
+
extend Selector::Methods
|
|
23
|
+
extend Elements
|
|
15
24
|
|
|
16
25
|
attr_reader :type, :name, :parent_element, :browser_element, :before_events, :after_events
|
|
17
26
|
|
|
@@ -24,7 +33,7 @@ module PageMagic
|
|
|
24
33
|
# @!method before_events
|
|
25
34
|
# If a block is passed in, it adds it to be run before an event is triggered on an element.
|
|
26
35
|
# @see .after_events
|
|
27
|
-
%i
|
|
36
|
+
%i[after_events before_events].each do |method|
|
|
28
37
|
define_method method do |&block|
|
|
29
38
|
instance_variable_name = "@#{method}".to_sym
|
|
30
39
|
instance_variable_value = instance_variable_get(instance_variable_name) || [DEFAULT_HOOK]
|
|
@@ -38,6 +47,7 @@ module PageMagic
|
|
|
38
47
|
# @return [Element]
|
|
39
48
|
def parent_element(page_element = nil)
|
|
40
49
|
return @parent_page_element unless page_element
|
|
50
|
+
|
|
41
51
|
@parent_page_element = page_element
|
|
42
52
|
end
|
|
43
53
|
|
|
@@ -49,10 +59,16 @@ module PageMagic
|
|
|
49
59
|
clazz.after_events.replace(after_events)
|
|
50
60
|
end
|
|
51
61
|
|
|
62
|
+
def load(source)
|
|
63
|
+
new(Capybara::Node::Simple.new(source))
|
|
64
|
+
end
|
|
65
|
+
|
|
52
66
|
# Defines watchers to be used by instances
|
|
53
67
|
# @see Watchers#watch
|
|
54
68
|
def watch(name, method = nil, &block)
|
|
55
|
-
before_events
|
|
69
|
+
before_events do
|
|
70
|
+
watch(name, method: method, &block)
|
|
71
|
+
end
|
|
56
72
|
end
|
|
57
73
|
|
|
58
74
|
def ==(other)
|
|
@@ -78,9 +94,7 @@ module PageMagic
|
|
|
78
94
|
# @raise [NotSupportedException] if the wrapped Capybara element does not support the method
|
|
79
95
|
EVENT_TYPES.each do |method|
|
|
80
96
|
define_method method do |*args|
|
|
81
|
-
unless browser_element.respond_to?(method)
|
|
82
|
-
raise NotSupportedException, EVENT_NOT_SUPPORTED_MSG % method
|
|
83
|
-
end
|
|
97
|
+
raise NotSupportedException, EVENT_NOT_SUPPORTED_MSG % method unless browser_element.respond_to?(method)
|
|
84
98
|
|
|
85
99
|
browser_element.send(method, *args)
|
|
86
100
|
end
|
|
@@ -91,16 +105,21 @@ module PageMagic
|
|
|
91
105
|
rescue ElementMissingException
|
|
92
106
|
begin
|
|
93
107
|
return browser_element.send(method, *args, &block) if browser_element.respond_to?(method)
|
|
94
|
-
|
|
108
|
+
|
|
109
|
+
parent_element.send(method, *args, &block)
|
|
95
110
|
rescue NoMethodError
|
|
96
111
|
super
|
|
97
112
|
end
|
|
98
113
|
end
|
|
99
114
|
|
|
100
|
-
def
|
|
115
|
+
def respond_to_missing?(*args)
|
|
101
116
|
super || contains_element?(args.first) || browser_element.respond_to?(*args) || parent_element.respond_to?(*args)
|
|
102
117
|
end
|
|
103
118
|
|
|
119
|
+
# def respond_to_missing?(*args)
|
|
120
|
+
# respond_to?(*args)
|
|
121
|
+
# end
|
|
122
|
+
|
|
104
123
|
# @!method session
|
|
105
124
|
# get the current session
|
|
106
125
|
# @return [Session] returns the session of the parent page element.
|
|
@@ -131,6 +150,7 @@ module PageMagic
|
|
|
131
150
|
def wrap_events(raw_element)
|
|
132
151
|
EVENT_TYPES.each do |action_method|
|
|
133
152
|
next unless raw_element.respond_to?(action_method)
|
|
153
|
+
|
|
134
154
|
apply_hooks(raw_element: raw_element,
|
|
135
155
|
capybara_method: action_method,
|
|
136
156
|
before_events: before_events,
|
|
@@ -1,22 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module PageMagic
|
|
2
4
|
# for the benefit of pull review :@
|
|
3
5
|
class Element
|
|
4
6
|
# contains method for finding element definitions
|
|
5
7
|
module Locators
|
|
6
8
|
# message used when raising {ElementMissingException} from methods within this module
|
|
7
|
-
ELEMENT_NOT_DEFINED_MSG = 'Element not defined: %s'
|
|
9
|
+
ELEMENT_NOT_DEFINED_MSG = 'Element not defined: %s'
|
|
8
10
|
|
|
9
11
|
# find an element definition based on its name
|
|
10
12
|
# @param [Symbol] name name of the element
|
|
11
13
|
# @return [Element] element definition with the given name
|
|
12
14
|
# @raise [ElementMissingException] raised when element with the given name is not found
|
|
13
15
|
def element_by_name(name, *args)
|
|
14
|
-
|
|
15
|
-
raise ElementMissingException, (ELEMENT_NOT_DEFINED_MSG % name) unless
|
|
16
|
-
|
|
16
|
+
definition = element_definitions[name]
|
|
17
|
+
raise ElementMissingException, (ELEMENT_NOT_DEFINED_MSG % name) unless definition
|
|
18
|
+
|
|
19
|
+
definition.call(self, *args)
|
|
17
20
|
end
|
|
18
21
|
|
|
19
|
-
# @return [Array] class level defined element definitions
|
|
22
|
+
# @return [Array<Element>] class level defined element definitions
|
|
20
23
|
def element_definitions
|
|
21
24
|
self.class.element_definitions
|
|
22
25
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PageMagic
|
|
4
|
+
class Element
|
|
5
|
+
# class NotFound - Used to represent elements which are missing. All method calls other than
|
|
6
|
+
# to those that check visibility thrown a {PageMagic::ElementMissingException} exception
|
|
7
|
+
class NotFound
|
|
8
|
+
# @private [Capybara::ElementNotFound] exception
|
|
9
|
+
def initialize(exception)
|
|
10
|
+
@exception = exception
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [Boolean] - always false
|
|
14
|
+
def visible?
|
|
15
|
+
false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @return [Boolean] - always false
|
|
19
|
+
def present?
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @raise [PageMagic::ElementMissingException]
|
|
24
|
+
def method_missing(*_args)
|
|
25
|
+
raise ElementMissingException, exception.message
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Boolean] - always true
|
|
29
|
+
def respond_to_missing?(*_args)
|
|
30
|
+
true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
attr_reader :exception
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -1,35 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'query/multiple_results'
|
|
4
|
+
require_relative 'query/single_result'
|
|
5
|
+
require_relative 'query/prefetched_result'
|
|
1
6
|
module PageMagic
|
|
2
7
|
class Element
|
|
3
8
|
# class Query - executes query on capybara driver
|
|
4
9
|
class Query
|
|
5
|
-
|
|
6
|
-
ELEMENT_NOT_FOUND_MSG = 'Unable to find %s'.freeze
|
|
7
|
-
|
|
8
|
-
attr_reader :args, :multiple_results
|
|
10
|
+
attr_reader :selector_args, :options
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
DEFAULT_DECORATOR = proc { |arg| arg }.freeze
|
|
11
13
|
|
|
12
|
-
def initialize(
|
|
13
|
-
@
|
|
14
|
-
@
|
|
14
|
+
def initialize(*selector_args, options: {})
|
|
15
|
+
@selector_args = selector_args
|
|
16
|
+
@options = options
|
|
15
17
|
end
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
rescue Capybara::Ambiguous => e
|
|
26
|
-
raise AmbiguousQueryException, e.message
|
|
19
|
+
# TODO: - test for decoration?
|
|
20
|
+
# Run query against the scope of the given element
|
|
21
|
+
# The supplied block will be used to decorate the results
|
|
22
|
+
# @param [Capybara::Node::Element] capybara_element the element to be searched within
|
|
23
|
+
# @return [Array<Capybara::Node::Element>] the results
|
|
24
|
+
# @return [NullElement] when the element is not found
|
|
25
|
+
def execute(capybara_element, &block)
|
|
26
|
+
find(capybara_element, &(block || DEFAULT_DECORATOR))
|
|
27
27
|
rescue Capybara::ElementNotFound => e
|
|
28
|
-
|
|
28
|
+
NotFound.new(e)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def ==(other)
|
|
32
|
-
other.respond_to?(:
|
|
32
|
+
other.respond_to?(:selector_args) && selector_args == other.selector_args &&
|
|
33
|
+
other.respond_to?(:options) && options == other.options
|
|
33
34
|
end
|
|
34
35
|
end
|
|
35
36
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PageMagic
|
|
4
|
+
class Element
|
|
5
|
+
class Query
|
|
6
|
+
# class MultipleResults - use to query for multiple results
|
|
7
|
+
class MultipleResults < Query
|
|
8
|
+
# Find multiple elements
|
|
9
|
+
# The supplied block will be used to decorate the results
|
|
10
|
+
# @param [Capybara::Node::Element] capybara_element the element to be searched within
|
|
11
|
+
# @return [Array<Capybara::Node::Element>] the results
|
|
12
|
+
def find(capybara_element, &block)
|
|
13
|
+
results = capybara_element.all(*selector_args, **options).to_a.tap do |result|
|
|
14
|
+
raise Capybara::ElementNotFound if result.empty?
|
|
15
|
+
end
|
|
16
|
+
results.collect { |result| block.call(result) }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PageMagic
|
|
4
|
+
class Element
|
|
5
|
+
class Query
|
|
6
|
+
# class PrefetchedResult - used to return element that has already been retrieved
|
|
7
|
+
class PrefetchedResult < Query
|
|
8
|
+
def initialize(prefetched_element)
|
|
9
|
+
super
|
|
10
|
+
@prefetched_element = prefetched_element
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Returns the object provided to `initialize`
|
|
14
|
+
# The supplied block will be used to decorate the results
|
|
15
|
+
# @return [Capybara::Node::Element] the object supplied to `initialize`
|
|
16
|
+
def find(_capybara_element, &block)
|
|
17
|
+
block.call(prefetched_element)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
attr_reader :prefetched_element
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PageMagic
|
|
4
|
+
class Element
|
|
5
|
+
class Query
|
|
6
|
+
# class SingleResult - use to query when only one result should be expected
|
|
7
|
+
class SingleResult < Query
|
|
8
|
+
# Find an element
|
|
9
|
+
# The supplied block will be used to decorate the results
|
|
10
|
+
# @param [Capybara::Node::Element] capybara_element the element to be searched within
|
|
11
|
+
# @return [Object] the results
|
|
12
|
+
def find(capybara_element, &block)
|
|
13
|
+
block.call capybara_element.find(*selector_args, **options)
|
|
14
|
+
rescue Capybara::Ambiguous => e
|
|
15
|
+
raise AmbiguousQueryException, e.message
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -1,40 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'selector/model'
|
|
1
4
|
module PageMagic
|
|
5
|
+
# Capybara::Finder
|
|
2
6
|
class Element
|
|
3
7
|
# class Selector - models the selection criteria understood by Capybara
|
|
4
8
|
class Selector
|
|
5
9
|
class << self
|
|
6
|
-
# Find a
|
|
10
|
+
# Find a Selector using it's name
|
|
7
11
|
# @param [Symbol] name the name of the required Selector in snakecase format. See class constants for available
|
|
8
12
|
# selectors
|
|
9
13
|
# @return [Selector] returns the predefined selector with the given name
|
|
10
14
|
def find(name)
|
|
11
|
-
|
|
12
|
-
raise UnsupportedCriteriaException unless
|
|
13
|
-
|
|
15
|
+
selector_name = selector_constant_name(name)
|
|
16
|
+
raise UnsupportedCriteriaException unless selector_name
|
|
17
|
+
|
|
18
|
+
const_get(selector_name)
|
|
14
19
|
end
|
|
15
|
-
end
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def selector_constant_name(name)
|
|
24
|
+
constants.find { |constant| constant.to_s.casecmp(name.to_s).zero? }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
18
27
|
|
|
28
|
+
# Initialize a new selector
|
|
29
|
+
# a block can be supplied to decorate the query. E.g.
|
|
30
|
+
# @example
|
|
31
|
+
# Selector.new(supports_type: false) do |arg|
|
|
32
|
+
# "*[name='#{arg}']"
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# @param [Symbol] selector the identifier for the selector
|
|
36
|
+
# @param [Boolean] supports_type whether the element type being searched for can be used as part of the query
|
|
37
|
+
# @param [Boolean] exact whether an exact match is required. E.g. element should include exactly the same text
|
|
19
38
|
def initialize(selector = nil, supports_type: false, exact: false, &formatter)
|
|
20
|
-
@
|
|
39
|
+
@selector = selector
|
|
21
40
|
@formatter = formatter || proc { |arg| arg }
|
|
22
41
|
@supports_type = supports_type
|
|
23
|
-
@
|
|
42
|
+
@options = {}.tap do |hash|
|
|
43
|
+
hash[:exact] = true if exact
|
|
44
|
+
end
|
|
24
45
|
end
|
|
25
46
|
|
|
26
47
|
# Build selector query parameters for Capybara's find method
|
|
27
48
|
# @param [Symbol] element_type the type of browser element being found. e.g :link
|
|
28
|
-
# @param [Hash] locator the selection method and its parameter. E.g. text: 'click me'
|
|
29
|
-
def build(element_type, locator)
|
|
30
|
-
[].
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
49
|
+
# @param [Hash<Symbol,String>] locator the selection method and its parameter. E.g. text: 'click me'
|
|
50
|
+
def build(element_type, locator, options: {})
|
|
51
|
+
array = [type(element_type), selector, formatter.call(locator)].compact
|
|
52
|
+
Model.new(array, self.options.merge(options))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def type(element_type)
|
|
58
|
+
supports_type ? element_type : nil
|
|
36
59
|
end
|
|
37
60
|
|
|
61
|
+
attr_reader :supports_type, :options, :selector, :formatter
|
|
62
|
+
|
|
38
63
|
XPATH = Selector.new(:xpath, supports_type: false)
|
|
39
64
|
ID = Selector.new(:id, supports_type: false)
|
|
40
65
|
LABEL = Selector.new(:field, supports_type: false, exact: true)
|