insite 0.0.1

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.
@@ -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