insite 0.0.2 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|