insite 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: '094a111c4101a5cb18a2f76c6d46625ba32b255f'
4
+ data.tar.gz: c850f4e79fb102e8ab477d0de0821b6ed4deeb5e
5
+ SHA512:
6
+ metadata.gz: 97aa7fa4c708b17b6b9bea7ea4408fa1aa566de7e2b4fa543329a06d55a9bde2ca0c9e73dc461ea2c3dd2c8305849b6b2774972f258a2f97e565104a8c955d41
7
+ data.tar.gz: a42caf8bf52e5f90a28e6f1909e1d14a6b5938fbe26fcaa427ba8112a3a9cecc6da47bdbe47c5ebd87864cd8632527e6b9935a6fcfed6433983802a9af3e5c49
@@ -0,0 +1,29 @@
1
+ require 'watir'
2
+ require 'watir-scroll'
3
+ require 'active_support'
4
+ require 'active_support/core_ext'
5
+ require 'addressable/template'
6
+ require 'nokogiri'
7
+
8
+ require "insite/constants"
9
+ require "insite/insite"
10
+ require "insite/errors"
11
+ require "insite/version"
12
+
13
+ # Modules with method definitions used by UI metaclasses.
14
+ require "insite/methods/dom_methods"
15
+ require "insite/methods/common_methods"
16
+
17
+ # Files for Insite::Widget.
18
+ require "insite/widget/widget"
19
+ require "insite/widget/widget_methods"
20
+
21
+ # Files for Insite::Feature.
22
+ require "insite/feature/feature"
23
+
24
+ # Files for ElementContainer.
25
+ require "insite/element_container/element_container"
26
+
27
+ # Files for pages (defined/undefined.)
28
+ require "insite/page/defined_page"
29
+ require "insite/page/undefined_page"
@@ -0,0 +1,32 @@
1
+ # DOM_ELEMENTS
2
+ # DOM_COLLECTIONS
3
+
4
+ # Watir DOM methods. This data is used to create class-level element accessors
5
+ # for page objects.
6
+ DOM_METHODS = %i(
7
+ a br datalists elements forms hidden li meshpatches params rect source tbody tspan
8
+ abbr brs datas ellipse frame hiddens line meshrow path rects sources tbodys tspans
9
+ abbrs bs date_field ellipses frames hr linear_gradient meshrows paths rp span td u
10
+ address button date_fields em frameset hrs linear_gradients meta pattern rps spans tds ul
11
+ addresses buttons date_time_field embed framesets htmls lines metadata patterns rt ss template uls
12
+ area canvas date_time_fields embeds g i link metadatas picture rtc stop templates us
13
+ areas canvases dd ems gs iframe links metas pictures rtcs stops text_field use
14
+ article caption dds extract_selector h1 iframes lis meter polygon rts strong text_fields uses
15
+ articles captions defs field_set h1s image main meters polygons rubies strongs text_path var
16
+ as checkbox defss field_sets h2 images mains nav polyline ruby style text_paths vars
17
+ aside checkboxes del fieldset h2s img map navs polylines s styles textarea video
18
+ asides circle dels fieldsets h3 imgs maps noscript pre samp sub textareas videos
19
+ audio circles desc figcaption h3s input mark noscripts pres samps subs tfoot view
20
+ audios cite descs figcaptions h4 inputs marker object progress script summaries tfoots views
21
+ b cites details figure h4s ins markers objects progresses scripts summary th wbr
22
+ base code detailses figures h5 inses marks ol ps section sup thead wbrs
23
+ bases codes dfn file_field h5s is menu ols q sections sups theads
24
+ bdi col dfns file_fields h6 kbd menuitem optgroup qs select svg ths
25
+ bdis colgroup div font h6s kbds menuitems optgroups radial_gradient select_list svgs time
26
+ bdo colgroups divs fonts hatchpath keygen menus option radial_gradients select_lists switch times
27
+ bdos cols dl footer hatchpaths keygens mesh options radio selects switches titles
28
+ blockquote cursor dls footers head label meshes output radio_set small symbol tr
29
+ blockquotes cursors dt foreign_object header labels meshgradient outputs radios smalls symbols track
30
+ body data dts foreign_objects headers legend meshgradients p rb solidcolor table tracks
31
+ bodys datalist element form heads legends meshpatch param rbs solidcolors tables trs
32
+ ).freeze
@@ -0,0 +1,42 @@
1
+ class ElementContainer
2
+ attr_reader :target, :site
3
+
4
+ include Insite::CommonMethods
5
+ extend Forwardable
6
+
7
+ class << self
8
+ include Insite::DOMMethods
9
+ end # self
10
+
11
+ def initialize(site, element)
12
+ @site = site
13
+ @browser = @site.browser
14
+ @target = element
15
+
16
+ # Temporary replacement for custom wait_until.
17
+ # TODO: Continue looking at scolling solutions.
18
+ if @target.present?
19
+ @target.scroll.to
20
+ t = Time.now + 2
21
+ while Time.now <= t do
22
+ break if @target.present?
23
+ sleep 0.1
24
+ end
25
+ end
26
+
27
+ @target
28
+ end
29
+
30
+ # For page widget code.
31
+ def method_missing(sym, *args, &block)
32
+ if @target.respond_to? sym
33
+ if @target.is_a? Watir::ElementCollection
34
+ @target.map { |x| self.class.new(x) }.send(sym, *args, &block)
35
+ else
36
+ @target.send(sym, *args, &block)
37
+ end
38
+ else
39
+ super
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,16 @@
1
+ module Insite
2
+ module Errors
3
+ class Insite::Errors::BrowserClosedError < StandardError; end
4
+ class Insite::Errors::BrowserLibraryNotSupportedError < StandardError; end
5
+ class Insite::Errors::BrowserNotAvailableError < StandardError; end
6
+ class Insite::Errors::BrowserNotOpenError < StandardError; end
7
+ class Insite::Errors::BrowserNotValidError < StandardError; end
8
+ class Insite::Errors::BrowserResponseError < StandardError; end
9
+ class Insite::Errors::PageConfigError < StandardError; end
10
+ class Insite::Errors::PageInitError < StandardError; end
11
+ class Insite::Errors::PageNavigationError < StandardError; end
12
+ class Insite::Errors::PageNavigationNotAllowedError < StandardError; end
13
+ class Insite::Errors::SiteInitError < StandardError; end
14
+ class Insite::Errors::WrongPageError < StandardError; end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ module Insite
2
+ class Feature
3
+ attr_reader :args, :page, :page_elements, :browser
4
+
5
+ include Insite::CommonMethods
6
+
7
+ class << self
8
+ attr_accessor :alias, :page_elements
9
+
10
+ include Insite::DOMMethods
11
+ include Insite::WidgetMethods
12
+ end # Self.
13
+
14
+ def initialize(site, **args)
15
+ if self.class.ancestors.include?(Insite::DefinedPage)
16
+ # if site.is_a? Insite:DefinedPage # TODO: Bandaid.
17
+ @site = site.site
18
+ @page = site
19
+ elsif site.class.ancestors.include?(Insite)
20
+ @site = site
21
+ @page = site.page
22
+ end
23
+
24
+ @args = args
25
+ @page = page
26
+ @browser = @site.browser
27
+ @page_elements = @site.page_elements
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,294 @@
1
+ require 'timeout'
2
+
3
+ # Usage:
4
+ # require 'insite'
5
+ #
6
+ # class MySite
7
+ # include Insite
8
+ # end
9
+ module Insite
10
+ attr_reader :base_url, :unique_methods, :browser_type
11
+ attr_accessor :pages, :browser, :arguments, :most_recent_page
12
+
13
+ # Automatically sets up a Page class when Insite is included. Probably overkill
14
+ # but it protects against the case where two different sites are used at the
15
+ # same time: Each site will use its own page objects only.
16
+ def self.included(base)
17
+ mod = Module.new
18
+ base.const_set('WidgetMethods', mod)
19
+
20
+ klass = Class.new(DefinedPage)
21
+ base.const_set('Page', klass)
22
+ base::send(:extend, WidgetMethods)
23
+
24
+ klass = Class.new(UndefinedPage)
25
+ base.const_set('UndefinedPage', klass)
26
+ end
27
+
28
+ # Returns true if there's an open browser (that's also responding.) False if not.
29
+ def browser?
30
+ begin
31
+ @browser.exists?
32
+ rescue => e
33
+ false
34
+ end
35
+ end
36
+
37
+ # Closes the site object's browser/driver.
38
+ def close
39
+ @browser.close
40
+ end
41
+
42
+ def describe
43
+ puts <<-EOF
44
+ Wrapper class for all pages defined for the site.
45
+ Site:\t#{self.class} (#{__FILE__})
46
+ Base URL:\t#{@base_url}
47
+ Browser:\t#{@browser}
48
+ Current Page:\t#{page.class}
49
+
50
+ Page Accessor Methods (Use '?' with name to check for presence.)
51
+ -----------------------------------------------------------------
52
+ #{
53
+ tmp = []
54
+ max = pages.map(&:to_s).max { |x| x.length }
55
+ if max.length > 40
56
+ pages.map(&:to_s).sort.map(&:underscore).join("\n")
57
+ else
58
+ pages.map(&:to_s).sort.map(&:underscore).each_slice(2) do |arr|
59
+ tmp << arr[0].to_s.ljust(40) + arr[1].to_s.ljust(40)
60
+ end
61
+ tmp.join("\n")
62
+ end
63
+ }
64
+ EOF
65
+ end
66
+
67
+ # Returns a Selenium driver object.
68
+ def driver
69
+ @browser.driver
70
+ end
71
+
72
+ # Returns true if there's an open driver (that's also responding.) False if not.
73
+ def driver?
74
+ browser?
75
+ end
76
+
77
+ # Creates a site object, which will have accessor methods for all pages that
78
+ # you have defined for the site. This object takes a hash argument. There is
79
+ # only one required value (the base_url for the site.) Example:
80
+ #
81
+ # class MySite
82
+ # include Insite
83
+ # end
84
+ #
85
+ # # Note: This base URL can be overridden when defining a page.
86
+ # site = MySite.new("https://foo.com")
87
+ #
88
+ # You can also specify any other arguments that you want for later use:
89
+ #
90
+ # site = MySite.new("http://foo.com", arg1, arg2, key1: val1, key2: val2)
91
+ #
92
+ # site.foo
93
+ # => true
94
+ # site.bar
95
+ # => 1
96
+ # TODO: Sort args.
97
+ def initialize(base_url, **hsh)
98
+ @arguments = hsh.with_indifferent_access
99
+ @base_url = base_url
100
+ @browser_type = (@arguments[:browser] ? @arguments[:browser].to_sym : nil)
101
+ @pages = self.class::DefinedPage.descendants.reject { |p| p.page_template? }
102
+
103
+ # Set up accessor methods for each page and page checking methods..
104
+ @pages.each do |current_page|
105
+ unless current_page.page_template?
106
+ current_page.set_url_template(@base_url)
107
+
108
+ if current_page.url_matcher
109
+ unless current_page.url_matcher.is_a? Regexp
110
+ raise Insite::Errors::PageConfigError,
111
+ "A url_matcher was defined for the #{current_page} page but it was not a " \
112
+ "regular expression. Check the value provided to the set_url_matcher method " \
113
+ "in the class definition for this page. Object provided was a " \
114
+ "#{current_page.url_matcher.class.name}"
115
+ end
116
+ end
117
+
118
+ self.class.class_eval do
119
+ define_method(current_page.to_s.underscore) do |args = nil, block = nil|
120
+ current_page.new(self, args)
121
+ end
122
+
123
+ define_method("#{current_page.to_s.underscore}?") do
124
+ on_page? current_page
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ visited = Set.new
131
+ tmp = @pages.map {|p| p.instance_methods }.flatten
132
+ tmp.each do |element|
133
+ if visited.include?(element)
134
+ else
135
+ visited << element
136
+ end
137
+ end
138
+ @unique_methods = visited
139
+ end
140
+
141
+ # Custom inspect method so that console output doesn't get in the way when debugging.
142
+ def inspect
143
+ "#<#{self.class.name}:0x#{object_id}\n @base_url=\"#{@base_url}\" " \
144
+ "@most_recent_page=#{@most_recent_page}>"
145
+ end
146
+
147
+ # In cases where Insite doesn't recognize a method call it will try to do the following:
148
+ # - Delegate the method call to the most recently accessed page, which is stored in
149
+ # Insite#most_recent_page.
150
+ #
151
+ # - If the cached page doesn't respond to the method, Insite will update the cache and
152
+ # then try to delegate the method again.
153
+ #
154
+ # If delegation doesn't work then a NoMethodError will be raised with some details about
155
+ # what was attempted.
156
+ def method_missing(sym, *args, &block)
157
+ original_page = @most_recent_page
158
+ if original_page.respond_to?(sym)
159
+ original_page.public_send(sym, *args, &block)
160
+ else
161
+ new_page = page
162
+ if new_page.respond_to?(sym)
163
+ page.public_send(sym, *args, &block)
164
+ else
165
+ raise(
166
+ NoMethodError,
167
+ "Unable to apply #{sym}. The site object doesn't support it and the" \
168
+ "currently displayed page doesn't support it either.\n" \
169
+ "Page:\t\t#{new_page.class}\n" \
170
+ "Current URL:\t#{@browser.url}\n\n",
171
+ caller
172
+ )
173
+ end
174
+ end
175
+ end
176
+
177
+ # Returns true or false depending on whether the specified page is displayed. You can use a page
178
+ # object or a PageObject class name to identify the page you are looking for. Examples:
179
+ #
180
+ # page = site.account_summary_page
181
+ # =>#<AccountSummaryPage:70341126478080 ...>
182
+ # site.on_page? page
183
+ # =>true
184
+ #
185
+ # site.on_page? AccountSummaryPage
186
+ # =>true
187
+ #
188
+ # If no arguments are provided, the currently displayed page will be checked. If a
189
+ # matching page object can be found then true will be returned. if there's no matching
190
+ # page object, false will be returned.
191
+ #
192
+ # Insite caches the most recently accessed page. This method updates that cached value,
193
+ # which can be accessed by calling Insite#most_recent_page.
194
+ def on_page?(page_arg = nil)
195
+ if page_arg
196
+ if pages.include?(page_arg) # See if the currently displayed page has the same class.
197
+ if @most_recent_page == page_arg && @most_recent_page.on_page?
198
+ true
199
+ else
200
+ @most_recent_page = page
201
+ @most_recent_page.class == page_arg
202
+ end
203
+ else # See if the currently displayed page is the same type of object.
204
+ if @most_recent_page == page_arg
205
+ @most_recent_page.on_page?
206
+ else
207
+ @most_recent_page = page
208
+ @most_recent_page == page_arg
209
+ end
210
+ end
211
+ else # Just see if the currently displayed page has been defined.
212
+ if @most_recent_page.defined? && @most_recent_page.on_page?
213
+ true
214
+ else
215
+ @most_recent_page = page
216
+ @most_recent_page.on_page?
217
+ end
218
+ end
219
+ end
220
+
221
+ # Opens a browser. The arguments used here get passed down to the browser
222
+ # constructor. Example:
223
+ # s = SomeSite.new("http://foo.com")
224
+ # s.open :firefox
225
+ def open(btype = nil, *args)
226
+ browser_platform = btype ||= @browser_type
227
+
228
+ if browser_platform
229
+ self.instance_variable_set(
230
+ :@browser,
231
+ Watir::Browser.new(browser_platform, *args)
232
+ )
233
+ else
234
+ self.instance_variable_set(
235
+ :@browser,
236
+ Watir::Browser.new(*args)
237
+ )
238
+ end
239
+
240
+ Watir.logger.level = :error
241
+ self
242
+ end
243
+
244
+ # Looks at the page currently being displayed in the browser and tries to
245
+ # return a page object for it. Does this by looking at the currently displayed
246
+ # URL in the browser.
247
+ #
248
+ # If a matching page can't be found then Insite will return an "undefined page"
249
+ # object. See the Un class for more details.
250
+ def page
251
+ return @most_recent_page if @most_recent_page && @most_recent_page.on_page?
252
+ url = @browser.url
253
+ found_page = nil
254
+ @pages.each do |pg|
255
+ if pg.url_matcher && pg.url_matcher =~ url
256
+ found_page = pg
257
+ elsif !pg.url_matcher && pg.url_template.match(url)
258
+ found_page = pg
259
+ else
260
+ next
261
+ end
262
+
263
+ break if found_page
264
+ end
265
+
266
+ if found_page && found_page.required_arguments.present?
267
+ if hsh = found_page.url_template.extract(url)
268
+ return found_page.new(self, found_page.url_template.extract(url))
269
+ else
270
+ return found_page.new(self, found_page.url_template.extract(url.split(/(\?|#)/)[0]))
271
+ end
272
+ elsif found_page
273
+ return found_page.new(self)
274
+ else
275
+ return UndefinedPage.new(self)
276
+ end
277
+ end
278
+
279
+ def text
280
+ @browser.text
281
+ end
282
+
283
+ # Returns an Addressable::URI object for the current browser URL.
284
+ def uri
285
+ Addressable::URI.parse(@browser.url)
286
+ end
287
+
288
+ # Returns the current browser URL.
289
+ def url
290
+ @browser.url
291
+ end
292
+
293
+ at_exit { @browser.close if @browser }
294
+ end