insite 0.0.2 → 0.0.5
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/lib/insite.rb +13 -7
- data/lib/insite/component/component.rb +454 -0
- data/lib/insite/component/component_collection.rb +112 -0
- data/lib/insite/component/component_instance_methods.rb +4 -0
- data/lib/insite/component/component_methods.rb +4 -0
- data/lib/insite/constants.rb +323 -31
- data/lib/insite/element/element.rb +147 -0
- data/lib/insite/element/element_collection.rb +102 -0
- data/lib/insite/element/generated/class_map.rb +244 -0
- data/lib/insite/element/generated/element_classes.rb +721 -0
- data/lib/insite/element/generated/element_instance_methods.rb +1594 -0
- data/lib/insite/errors.rb +2 -0
- data/lib/insite/examples/material_angular_io/components/angular_material_component.rb +13 -0
- data/lib/insite/examples/material_angular_io/components/example_viewer.rb +5 -0
- data/lib/insite/examples/material_angular_io/components/mat_chip.rb +29 -0
- data/lib/insite/examples/material_angular_io/components/mat_chip_list.rb +21 -0
- data/lib/insite/examples/material_angular_io/components/mat_form_field.rb +5 -0
- data/lib/insite/examples/material_angular_io/components/mat_icon.rb +5 -0
- data/lib/insite/examples/material_angular_io/components/mat_input.rb +5 -0
- data/lib/insite/examples/material_angular_io/components/mat_option.rb +3 -0
- data/lib/insite/examples/material_angular_io/components/mat_select.rb +15 -0
- data/lib/insite/examples/material_angular_io/components/mat_select_content.rb +13 -0
- data/lib/insite/examples/material_angular_io/components/no_selector.rb +3 -0
- data/lib/insite/examples/material_angular_io/pages.rb +20 -0
- data/lib/insite/examples/material_angular_io/site.rb +5 -0
- data/lib/insite/examples/material_angular_io/utils.rb +6 -0
- data/lib/insite/examples/material_angular_io/watir_mods.rb +54 -0
- data/lib/insite/examples/material_angular_io_site.rb +54 -0
- data/lib/insite/insite.rb +96 -11
- data/lib/insite/methods/common_methods.rb +26 -37
- data/lib/insite/methods/dom_methods.rb +73 -46
- data/lib/insite/page/defined_page.rb +37 -50
- data/lib/insite/page/undefined_page.rb +12 -1
- data/lib/insite/version.rb +1 -1
- metadata +69 -29
- data/lib/insite/element_container/element_container.rb +0 -42
- data/lib/insite/feature/feature.rb +0 -30
- data/lib/insite/widget/widget.rb +0 -346
- data/lib/insite/widget/widget_methods.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db6875ab42b0dc5e67d04b58ed8e2784688c3638
|
4
|
+
data.tar.gz: a634d3df6da0647fda5665f615a1dec6282f027f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b17d8b1d92af6f10c38b17999cc69a1c9895d408a3a571a34c634bd91ffbd80eda9ab08e662b61c5bcb918feb9adb810a3e06910fc5a1d97bdca0eb5076e191c
|
7
|
+
data.tar.gz: 4f4b65feeaa634894a8a237d0863921524ad5ffed17d8439aa4fdb934d95aff16dea9b6faf66c0b3a5a4da6c6b717ac6cd69d446d56951fa0fafcd1f90a5eb93
|
data/lib/insite.rb
CHANGED
@@ -14,15 +14,21 @@ require "insite/version"
|
|
14
14
|
require "insite/methods/dom_methods"
|
15
15
|
require "insite/methods/common_methods"
|
16
16
|
|
17
|
-
# Files for Insite
|
18
|
-
require "insite/
|
19
|
-
require "insite/
|
17
|
+
# Files for Insite element wrapper classes.
|
18
|
+
require "insite/element/generated/element_instance_methods"
|
19
|
+
require "insite/element/element"
|
20
|
+
require "insite/element/element_collection"
|
21
|
+
require "insite/element/generated/element_classes"
|
22
|
+
require "insite/element/generated/class_map"
|
20
23
|
|
21
|
-
# Files for Insite::
|
22
|
-
require "insite/
|
24
|
+
# Files for Insite::Component.
|
25
|
+
require "insite/component/component_instance_methods"
|
26
|
+
require "insite/component/component"
|
27
|
+
require "insite/component/component_collection"
|
28
|
+
require "insite/component/component_methods"
|
23
29
|
|
24
|
-
# Files for
|
25
|
-
require "insite/
|
30
|
+
# Files for Insite::Feature.
|
31
|
+
# require "insite/feature/feature"
|
26
32
|
|
27
33
|
# Files for pages (defined/undefined.)
|
28
34
|
require "insite/page/defined_page"
|
@@ -0,0 +1,454 @@
|
|
1
|
+
require_relative 'component_methods'
|
2
|
+
require_relative 'component_collection'
|
3
|
+
require_relative 'component_instance_methods'
|
4
|
+
require_relative '../methods/common_methods'
|
5
|
+
require 'pry'
|
6
|
+
# Allows the page object developer to encapsulate common web application features
|
7
|
+
# into components that can be reused across multiple pages.
|
8
|
+
module Insite
|
9
|
+
class Component
|
10
|
+
attr_reader :args, :browser, :non_relative, :selector, :site, :type, :target
|
11
|
+
class_attribute :selector, default: {}
|
12
|
+
self.selector = self.selector.clone
|
13
|
+
|
14
|
+
include Insite::CommonMethods
|
15
|
+
extend Insite::DOMMethods
|
16
|
+
include Insite::ElementInstanceMethods
|
17
|
+
extend Insite::ComponentMethods
|
18
|
+
include Insite::ComponentInstanceMethods
|
19
|
+
alias_method :update_component, :update_object
|
20
|
+
|
21
|
+
class << self
|
22
|
+
attr_reader :component_elements
|
23
|
+
|
24
|
+
# - Don't allow the user to create a component with a name that matches a DOM
|
25
|
+
# element.
|
26
|
+
#
|
27
|
+
# - Don't allow the user to create a component method that references a
|
28
|
+
# collection (because this will be done automatically.)
|
29
|
+
tmp = name.to_s.underscore.to_sym
|
30
|
+
if DOM_METHODS.include?(name.to_s.underscore.to_sym)
|
31
|
+
raise "#{name} cannot be used as a component name, as the methodized version of the class name (#{name.to_s.underscore} conflicts with an Insite DOM method.)"
|
32
|
+
elsif Watir::Browser.methods.include?(name.to_s.underscore.to_sym)
|
33
|
+
raise "#{name} cannot be used as a component name, as the methodized version of the class name (#{name.to_s.underscore} conflicts with a Insite::Browser method.)"
|
34
|
+
end
|
35
|
+
|
36
|
+
if tmp =~ /.*s+/
|
37
|
+
raise "Invalid component type :#{tmp}. You can create a component for the DOM object but it must be for :#{tmp.singularize} (:#{tmp} will be created automatically.)"
|
38
|
+
end
|
39
|
+
|
40
|
+
end # Self.
|
41
|
+
|
42
|
+
extend Forwardable
|
43
|
+
|
44
|
+
def self.collection?
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.inherited(subclass)
|
49
|
+
name_string = subclass.name.demodulize.underscore
|
50
|
+
collection_name = name_string + '_collection'
|
51
|
+
|
52
|
+
if name_string == name_string.pluralize
|
53
|
+
collection_method_name = name_string + 'es'
|
54
|
+
else
|
55
|
+
collection_method_name = name_string.pluralize
|
56
|
+
end
|
57
|
+
|
58
|
+
# Create a collection class for a component when a component is defined.
|
59
|
+
collection_class = Class.new(Insite::ComponentCollection) do
|
60
|
+
attr_reader :collection_member_type
|
61
|
+
@collection_member_type = subclass
|
62
|
+
end
|
63
|
+
Insite.const_set(collection_name.camelize, collection_class)
|
64
|
+
|
65
|
+
# Defines class-level methods for defining component accessor methods.
|
66
|
+
# Does this for both the individual instance of the component AND the
|
67
|
+
# collection. When these methods are call within page objects, they define
|
68
|
+
# accessor methods for components and component collections when an
|
69
|
+
# INSTANCE of the page object is being used.
|
70
|
+
#
|
71
|
+
# If a block is provided when using a method to provide access to a
|
72
|
+
# component a MODIFIED version of the component class is created within
|
73
|
+
# the page object where the method is invoked.
|
74
|
+
#
|
75
|
+
# If no block is provided, the base component class is used without
|
76
|
+
# modifications.
|
77
|
+
{
|
78
|
+
name_string => subclass,
|
79
|
+
collection_method_name => collection_class
|
80
|
+
}.each do |nstring, klass|
|
81
|
+
ComponentMethods.send(:define_method, nstring) do |mname, *a, &block|
|
82
|
+
unless nstring == 'Component'
|
83
|
+
@component_elements ||= []
|
84
|
+
unless @component_elements.include?(mname.to_sym)
|
85
|
+
@component_elements << mname.to_sym
|
86
|
+
end
|
87
|
+
|
88
|
+
# One way or another there must be some arguments to identify the
|
89
|
+
# component.
|
90
|
+
if klass.selector
|
91
|
+
hsh = parse_args(a.to_a).merge(klass.selector)
|
92
|
+
elsif a.present?
|
93
|
+
hsh = parse_args(a)
|
94
|
+
else
|
95
|
+
raise(
|
96
|
+
Insite::Errors::ComponentReferenceError,
|
97
|
+
"Unable to initialize #{nstring}. Base selector options were " \
|
98
|
+
"not defined in the component's class definition and no " \
|
99
|
+
"selector options were defined in the class or class instance " \
|
100
|
+
"method call."
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Accessor instance method gets defined here.
|
105
|
+
define_method(mname) do
|
106
|
+
# If a block is provided then we need to create a modified version
|
107
|
+
# of the component or component collection to contain the added
|
108
|
+
# functionality. This new class gets created within the page object
|
109
|
+
# class and its name is different from the base class.
|
110
|
+
if block
|
111
|
+
# Create the modified class UNLESS it's already there.
|
112
|
+
new_class_name = "#{c}For#{name.to_s.camelcase}"
|
113
|
+
unless self.class.const_defined? new_class_name
|
114
|
+
target_class = Class.new(klass) do
|
115
|
+
class_eval(&block) if block
|
116
|
+
end
|
117
|
+
const_set(new_class_name, new_klass)
|
118
|
+
end
|
119
|
+
else
|
120
|
+
target_class = klass
|
121
|
+
end
|
122
|
+
|
123
|
+
target_class.new(self, hsh)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
ComponentInstanceMethods.send(:define_method, nstring) do |*a|
|
129
|
+
hsh = parse_args(a).merge(subclass.selector)
|
130
|
+
klass.new(self, hsh)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end # self.inherited
|
134
|
+
|
135
|
+
def self.select_by(hsh = {})
|
136
|
+
tmp = selector.clone
|
137
|
+
hsh.each do |k, v|
|
138
|
+
if %i(css, xpath).include? k
|
139
|
+
raise ArgumentError, "The :#{k} selector argument is not currently allowed for component definitions."
|
140
|
+
elsif k == :tag_name && tmp[k] && v && tmp[k] != v
|
141
|
+
raise(
|
142
|
+
ArgumentError,
|
143
|
+
"\n\nInvalid use of the :tag_name selector in the #{self} component class. This component inherits " \
|
144
|
+
"from the #{superclass} component, which already defines #{superclass.selector[:tag_name]} as " \
|
145
|
+
"the tag name. If you are intentionally trying to overwrite the tag name in the inherited class, " \
|
146
|
+
"use #{self}.select_by! in the page definition in place of #{self}.select_by. Warning: The " \
|
147
|
+
"select_by! method arguments overwrite the selector that were inherited from #{superclass}. " \
|
148
|
+
"So if you DO use it you'll need to specify ALL of the selector needed to properly identify the " \
|
149
|
+
"#{self} component.\n\n",
|
150
|
+
caller
|
151
|
+
)
|
152
|
+
elsif tmp[k].is_a?(Array)
|
153
|
+
tmp[k] = ([tmp[k]].flatten + [v].flatten).uniq
|
154
|
+
else
|
155
|
+
tmp[k] = v
|
156
|
+
end
|
157
|
+
end
|
158
|
+
self.selector = tmp
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.select_by!(hsh = {})
|
162
|
+
self.selector = hsh
|
163
|
+
end
|
164
|
+
|
165
|
+
def attributes
|
166
|
+
nokogiri.xpath("//#{selector[:tag_name]}")[0].attributes.values.map do |x|
|
167
|
+
[x.name, x.value]
|
168
|
+
end.to_h
|
169
|
+
end
|
170
|
+
|
171
|
+
def classes
|
172
|
+
attribute('class').split
|
173
|
+
end
|
174
|
+
|
175
|
+
def collection?
|
176
|
+
false
|
177
|
+
end
|
178
|
+
|
179
|
+
# This method gets used 2 different ways. Most of the time, dom_type and args
|
180
|
+
# will be a symbol and a set of hash arguments that will be used to select an
|
181
|
+
# element.
|
182
|
+
#
|
183
|
+
# In some cases, dom_type can also be a Watir DOM object, and in this case, the
|
184
|
+
# args are ignored and the component is initialized using the DOM object.
|
185
|
+
#
|
186
|
+
# TODO: Needs a rewrite, lines between individual and collection are blurred
|
187
|
+
# here and that makes the code more confusing. And there should be a proper
|
188
|
+
# collection class for element collections, with possibly some AR-like accessors.
|
189
|
+
def initialize(parent, *args)
|
190
|
+
# Figure out the correct query scope.
|
191
|
+
parent.respond_to?(:target) ? obj = parent : obj = parent.site
|
192
|
+
@parent = obj
|
193
|
+
|
194
|
+
# @parent = parent
|
195
|
+
@site = parent.class.ancestors.include?(Insite) ? parent : parent.site
|
196
|
+
@browser = @site.browser
|
197
|
+
@component_elements = self.class.component_elements
|
198
|
+
|
199
|
+
if args[0].is_a?(Insite::Element) || args[0].is_a?(Insite::ElementCollection)
|
200
|
+
@dom_type = nil
|
201
|
+
@args = nil
|
202
|
+
@target = args[0].target
|
203
|
+
elsif args[0].is_a?(Watir::Element) || args[0].is_a?(Watir::ElementCollection)
|
204
|
+
@dom_type = nil
|
205
|
+
@args = nil
|
206
|
+
@target = args[0]
|
207
|
+
else
|
208
|
+
unless self.class.selector.present? || parse_args(args).present?
|
209
|
+
raise(
|
210
|
+
Insite::Errors::ComponentSelectorError,
|
211
|
+
"Unable to initialize a #{self.class} Component for #{parent.class}. " \
|
212
|
+
"A Component selector wasn't defined in this Component's class " \
|
213
|
+
"definition and the method call did not include selector arguments.",
|
214
|
+
caller
|
215
|
+
)
|
216
|
+
end
|
217
|
+
|
218
|
+
@selector = self.class.selector.merge(parse_args(args))
|
219
|
+
@args = @selector
|
220
|
+
@non_relative = @args.delete(:non_relative) || false
|
221
|
+
|
222
|
+
if @non_relative
|
223
|
+
# @args = parse_args(args)
|
224
|
+
# @selector = @args
|
225
|
+
@target = @browser.send(@args)
|
226
|
+
else
|
227
|
+
# @args = parse_args(args)
|
228
|
+
|
229
|
+
# # Figure out the correct query scope.
|
230
|
+
# @parent.respond_to?(:target) ? obj = @parent.target : obj = @browser
|
231
|
+
|
232
|
+
# See if there's a Watir DOM method for the class. If not, then
|
233
|
+
# initialize using the default collection.
|
234
|
+
if watir_class = Insite::CLASS_MAP.key(self.class)
|
235
|
+
@target = watir_class.new(@parent.target, @args)
|
236
|
+
# @target = watir_class.new(obj, @args)
|
237
|
+
else
|
238
|
+
@target = Watir::HTMLElement.new(@parent.target, @args)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# New webdriver approach.
|
243
|
+
# begin
|
244
|
+
# @target.scroll.to
|
245
|
+
# sleep 0.1
|
246
|
+
# rescue => e
|
247
|
+
# t = ::Time.now + 2
|
248
|
+
# while ::Time.now <= t do
|
249
|
+
# break if @target.present?
|
250
|
+
# sleep 0.1
|
251
|
+
# end
|
252
|
+
# end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def inspect
|
257
|
+
if @target.selector.present?
|
258
|
+
s = @selector.to_s
|
259
|
+
else
|
260
|
+
s = '{element: (selenium element)}'
|
261
|
+
end
|
262
|
+
"#<#{self.class}: located: #{!!@target.element}; @selector=#{s}>"
|
263
|
+
end
|
264
|
+
|
265
|
+
# def inspect
|
266
|
+
# @selector.empty? ? s = '{element: (selenium element)}' : s = @selector.to_s
|
267
|
+
# "#<#{self.class}: located: #{!!@target.element}; @selector=#{s}>"
|
268
|
+
# end
|
269
|
+
|
270
|
+
# Delegates method calls down to the component's wrapped element if the
|
271
|
+
# element supports the method being called.
|
272
|
+
#
|
273
|
+
# Supports dynamic link methods. Examples:
|
274
|
+
# s.accounts_page account
|
275
|
+
#
|
276
|
+
# # Nav to linked page only.
|
277
|
+
# s.account_actions.edit_account_info
|
278
|
+
#
|
279
|
+
# # Update linked page after nav:
|
280
|
+
# s.account_actions.edit_account_info username: 'foo'
|
281
|
+
#
|
282
|
+
# # Link with modal (if the modal requires args they should be passed as hash keys):
|
283
|
+
# # s.hosted_pages.refresh_urls
|
284
|
+
def method_missing(mth, *args, &block)
|
285
|
+
if @target.respond_to? mth
|
286
|
+
out = @target.send(mth, *args, &block)
|
287
|
+
|
288
|
+
if out == @target
|
289
|
+
self
|
290
|
+
elsif out.is_a?(Watir::Element) || out.is_a?(Watir::ElementCollection)
|
291
|
+
Insite::CLASS_MAP[out.class].new(@parent, out)
|
292
|
+
else
|
293
|
+
out
|
294
|
+
end
|
295
|
+
elsif @target.respond_to?(:to_subtype) &&
|
296
|
+
@target.class.descendants.any? do |klass|
|
297
|
+
klass.instance_methods.include?(mth)
|
298
|
+
end
|
299
|
+
out = @target.to_subtype.send(mth, *args, &block)
|
300
|
+
if klass = Insite::CLASS_MAP[out.class]
|
301
|
+
klass.new(@site, out)
|
302
|
+
else
|
303
|
+
out
|
304
|
+
end
|
305
|
+
else
|
306
|
+
if args[0].is_a? Hash
|
307
|
+
page_arguments = args[0]
|
308
|
+
elsif args.empty?
|
309
|
+
raise NoMethodError, "undefined method `#{mth}' for #{self}: #{self.class}."
|
310
|
+
elsif args[0].nil?
|
311
|
+
raise ArgumentError, "Optional argument for :#{mth} must be a hash. Got NilClass."
|
312
|
+
else
|
313
|
+
raise ArgumentError, "Optional argument must be a hash (got #{args[0].class}.)"
|
314
|
+
end
|
315
|
+
|
316
|
+
if present?
|
317
|
+
# # TODO: Lame and overly specific.
|
318
|
+
# # If it's a component we want to hover over it to ensure links are visible
|
319
|
+
# # before trying to find them.
|
320
|
+
# if self.is_a?(Component)
|
321
|
+
# t = ::Time.now
|
322
|
+
# puts t
|
323
|
+
# loop do
|
324
|
+
# begin
|
325
|
+
# scroll.to
|
326
|
+
# hover
|
327
|
+
# sleep 0.2
|
328
|
+
# break
|
329
|
+
# rescue => e
|
330
|
+
# break if ::Time.now > t + 10
|
331
|
+
# sleep 0.2
|
332
|
+
# end
|
333
|
+
#
|
334
|
+
# break if present?
|
335
|
+
# break if ::Time.now > t + 10
|
336
|
+
# end
|
337
|
+
# end
|
338
|
+
|
339
|
+
# Dynamic helper method, returns DOM object for link (no validation).
|
340
|
+
if mth.to_s =~ /_link$/
|
341
|
+
return a(text: /^#{mth.to_s.sub(/_link$/, '').gsub('_', '.*')}/i)
|
342
|
+
# Dynamic helper method, returns DOM object for button (no validation).
|
343
|
+
elsif mth.to_s =~ /_button$/
|
344
|
+
return button(value: /^#{mth.to_s.sub(/_button$/, '').gsub('_', '.*')}/i)
|
345
|
+
# Dynamic helper method for links. If a match is found, clicks on the
|
346
|
+
# link and performs follow up actions. Start by seeing if there's a
|
347
|
+
# matching button and treat it as a method call if so.
|
348
|
+
elsif !collection? && elem = as.to_a.find { |x| x.text =~ /^#{mth.to_s.gsub('_', '.*')}/i }
|
349
|
+
elem.click
|
350
|
+
sleep 1
|
351
|
+
|
352
|
+
current_page = @site.page
|
353
|
+
|
354
|
+
if page_arguments.present?
|
355
|
+
|
356
|
+
if current_page.respond_to?(:submit)
|
357
|
+
current_page.submit page_arguments
|
358
|
+
elsif @browser.input(xpath: "//div[starts-with(@class,'Row') and last()]//input[@type='submit' and last()]").present?
|
359
|
+
current_page.update_page page_arguments
|
360
|
+
@browser.input(xpath: "//div[starts-with(@class,'Row') and last()]//input[@type='submit' and last()]").click
|
361
|
+
end
|
362
|
+
current_page = @site.page
|
363
|
+
end
|
364
|
+
# Dynamic helper method for buttons. If a match is found, clicks on the link and performs follow up actions.
|
365
|
+
elsif !collection? && elem = buttons.to_a.find { |x| x.text =~ /^#{mth.to_s.gsub('_', '.*')}/i } # See if there's a matching button and treat it as a method call if so.
|
366
|
+
elem.click
|
367
|
+
sleep 1
|
368
|
+
|
369
|
+
# TODO: Legacy support. Revisit.
|
370
|
+
if @site.respond_to?(:modal) && @site.modal.present?
|
371
|
+
@site.modal.continue(page_arguments)
|
372
|
+
else
|
373
|
+
current_page = @site.page
|
374
|
+
|
375
|
+
if page_arguments.present?
|
376
|
+
if current_page.respond_to?(:submit)
|
377
|
+
current_page.submit page_arguments
|
378
|
+
elsif @browser.input(xpath: "//div[starts-with(@class,'Row') and last()]//input[@type='submit' and last()]").present?
|
379
|
+
current_page.update_page page_arguments
|
380
|
+
@browser.input(xpath: "//div[starts-with(@class,'Row') and last()]//input[@type='submit' and last()]").click
|
381
|
+
end
|
382
|
+
current_page = @site.page
|
383
|
+
end
|
384
|
+
end
|
385
|
+
else
|
386
|
+
raise NoMethodError, "undefined method `#{mth}' for #{self.class}.", caller
|
387
|
+
end
|
388
|
+
else
|
389
|
+
raise NoMethodError, "Unhandled method call `#{mth}' for #{self.class} (The component was not present in the DOM at the point that the method was called.)", caller
|
390
|
+
end
|
391
|
+
|
392
|
+
page_arguments.present? ? page_arguments : current_page
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
def present?
|
397
|
+
sleep 0.1
|
398
|
+
begin
|
399
|
+
if @parent
|
400
|
+
if @parent.present? && @target.present?
|
401
|
+
true
|
402
|
+
else
|
403
|
+
false
|
404
|
+
end
|
405
|
+
else
|
406
|
+
if @target.present?
|
407
|
+
true
|
408
|
+
else
|
409
|
+
false
|
410
|
+
end
|
411
|
+
end
|
412
|
+
rescue => e
|
413
|
+
false
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
private
|
418
|
+
def merge_selector_args(other = {})
|
419
|
+
tmp = self.class.selector.clone
|
420
|
+
|
421
|
+
if tmp.empty? && other.empty?
|
422
|
+
raise ArgumentError, "No selector values have been specified for the " \
|
423
|
+
"#{self.class} component. And no selector arguments were specified " \
|
424
|
+
"when calling the instance component's accessor method. "
|
425
|
+
end
|
426
|
+
|
427
|
+
other.each do |k, v|
|
428
|
+
if k == :tag_name && tmp[k] != v
|
429
|
+
raise(
|
430
|
+
ArgumentError,
|
431
|
+
"\n\nInvalid use of the :tag_name selector in the #{self} component class. This component inherits " \
|
432
|
+
"from the #{superclass} component, which already defines #{superclass.selector[:tag_name]} as " \
|
433
|
+
"the tag name. If you are intentionally trying to overwrite the tag name in the inherited class, " \
|
434
|
+
"use #{self}.select_by! in the page definition in place of #{self}.select_by. Warning: The " \
|
435
|
+
"select_by! method arguments overwrite the selector arguments that were inherited from #{superclass}. " \
|
436
|
+
"So if you DO use it you'll need to specify ALL of the selector needed to properly identify the " \
|
437
|
+
"#{self} component (because nothing will be inherited.)\n\n",
|
438
|
+
caller
|
439
|
+
)
|
440
|
+
elsif tmp[k].is_a?(Array) && v.is_a?(Array) # TODO: class check here?
|
441
|
+
tmp[k] = (tmp[k].flatten + [v].flatten).uniq
|
442
|
+
else
|
443
|
+
tmp[k] = v
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
tmp
|
448
|
+
end
|
449
|
+
|
450
|
+
def process_value(value)
|
451
|
+
value.is_a?(Regexp) ? value : /^#{value}/i
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|