gless 1.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.
- data/.gitignore +18 -0
- data/.rvmrc +34 -0
- data/README.md +242 -0
- data/Rakefile +41 -0
- data/TODO +4 -0
- data/TODO-session +65 -0
- data/examples/test_github/features/support/env.rb +27 -0
- data/examples/test_github/features/support/step_definitions/test_github_steps.rb +27 -0
- data/examples/test_github/features/test_github/test_github.feature +12 -0
- data/examples/test_github/lib/config/development.yml +12 -0
- data/examples/test_github/lib/config/development.yml.example +12 -0
- data/examples/test_github/lib/pages/test_github/blog_page.rb +13 -0
- data/examples/test_github/lib/pages/test_github/explore_page.rb +13 -0
- data/examples/test_github/lib/pages/test_github/features_page.rb +13 -0
- data/examples/test_github/lib/pages/test_github/login_page.rb +15 -0
- data/examples/test_github/lib/pages/test_github/repo_page.rb +14 -0
- data/examples/test_github/lib/pages/test_github/search_page.rb +61 -0
- data/examples/test_github/lib/pages/test_github_base_page.rb +11 -0
- data/examples/test_github/lib/startup.rb +16 -0
- data/examples/test_github/lib/test_github.rb +68 -0
- data/gless.gemspec +25 -0
- data/lib/gless/base_page.rb +339 -0
- data/lib/gless/browser.rb +43 -0
- data/lib/gless/config.rb +80 -0
- data/lib/gless/logger.rb +122 -0
- data/lib/gless/session.rb +319 -0
- data/lib/gless/wrap_watir.rb +164 -0
- data/lib/gless.rb +54 -0
- metadata +173 -0
@@ -0,0 +1,319 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
|
3
|
+
module Gless
|
4
|
+
|
5
|
+
# Provides an abstraction layer between the individual pages of an
|
6
|
+
# website and the high-level application layer, so that the
|
7
|
+
# application layer doesn't have to know about what page it's on
|
8
|
+
# or similar.
|
9
|
+
#
|
10
|
+
# For details, see the README.
|
11
|
+
class Gless::Session
|
12
|
+
include RSpec::Matchers
|
13
|
+
|
14
|
+
# The page class for the page the session thinks we're currently
|
15
|
+
# on.
|
16
|
+
attr_reader :current_page
|
17
|
+
|
18
|
+
# A list of page classes of pages that it's OK for us to be on.
|
19
|
+
# Usually just one, but some site workflows might have more than
|
20
|
+
# one thing that can happen when you click a button or whatever.
|
21
|
+
#
|
22
|
+
# When you assign a value here, a fair bit of processing is
|
23
|
+
# done. Most of the actual work is in check_acceptable_pages
|
24
|
+
#
|
25
|
+
# The user can give us a class, a symbol, or a list of those; no
|
26
|
+
# matter what, we return a list. That list is of possible pages
|
27
|
+
# that, if we turn out to be on one of them, that's OK, and if
|
28
|
+
# not we freak out.
|
29
|
+
#
|
30
|
+
# @param [Class, Symbol, Array] newpages A page class, or a
|
31
|
+
# symbol naming a page class, or an array of those, for which
|
32
|
+
# pages are acceptable.
|
33
|
+
attr_reader :acceptable_pages
|
34
|
+
|
35
|
+
# See docs for :acceptable_pages
|
36
|
+
def acceptable_pages= newpage
|
37
|
+
log.debug "Session: changing acceptable pages list to #{newpage}"
|
38
|
+
@acceptable_pages = (check_acceptable_pages newpage).flatten
|
39
|
+
log.info "Session: acceptable pages list has been changed to: #{@acceptable_pages}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# This exists only to be called by +inherited+ on
|
43
|
+
# Gless::BasePage; see documentation there.
|
44
|
+
def self.add_page_class( klass )
|
45
|
+
@@page_classes ||= []
|
46
|
+
@@page_classes << klass
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# Sets up the session object. As the core abstraction layer that
|
51
|
+
# sits in the middle of everything, this requires a number of
|
52
|
+
# arguments. :)
|
53
|
+
#
|
54
|
+
# @param [Gless::Browser] browser
|
55
|
+
# @param [Gless::EnvConfig] config
|
56
|
+
# @param [Gless::Logger] logger
|
57
|
+
# @param [Object] application See the README for a description
|
58
|
+
# of the stuff the application object is expected to have.
|
59
|
+
def initialize( browser, config, logger, application )
|
60
|
+
@logger = logger
|
61
|
+
|
62
|
+
log.debug "Session: Initializing with #{browser.inspect}"
|
63
|
+
|
64
|
+
@browser = browser
|
65
|
+
@application = application
|
66
|
+
@pages = Hash.new
|
67
|
+
@timeout = 30
|
68
|
+
@acceptable_pages = nil
|
69
|
+
@config = config
|
70
|
+
|
71
|
+
@@page_classes.each do |sc|
|
72
|
+
@pages[sc] = sc.new( @browser, self, @application )
|
73
|
+
end
|
74
|
+
|
75
|
+
log.debug "Session: Final pages table: #{@pages.keys.map { |x| x.name }}"
|
76
|
+
|
77
|
+
return self
|
78
|
+
end
|
79
|
+
|
80
|
+
# Just passes through to the Gless::EnvConfig component's +get+
|
81
|
+
# method.
|
82
|
+
def get_config(*args)
|
83
|
+
@config.get(*args)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Just a shortcut to get to the Gless::Logger object.
|
87
|
+
def log
|
88
|
+
@logger
|
89
|
+
end
|
90
|
+
|
91
|
+
# Anything that we don't otherwise recognize is passed on to the
|
92
|
+
# current underlying page object (i.e. descendant of
|
93
|
+
# Gless::BasePage).
|
94
|
+
#
|
95
|
+
# This gets complicated because of the state checking: we test
|
96
|
+
# extensively that we're on the page that we think we should be
|
97
|
+
# on before passing things on to the page object.
|
98
|
+
def method_missing(m, *args, &block)
|
99
|
+
# Do some logging.
|
100
|
+
if m.inspect =~ /(password|login)/i or args.inspect =~ /(password|login)/i
|
101
|
+
log.debug "Session: Doing something with passwords, redacted."
|
102
|
+
else
|
103
|
+
log.debug "Session: method_missing for #{m} with arguments #{args.inspect}"
|
104
|
+
end
|
105
|
+
|
106
|
+
log.debug "Session: check if we've changed pages: #{@browser.title}, #{@browser.url}, #{@previous_url}, #{@current_page}, #{@acceptable_pages}"
|
107
|
+
|
108
|
+
# Changed URL means we've changed pages. Our current page no
|
109
|
+
# longer being in the acceptable pages list means we *should*
|
110
|
+
# have changed pages. So we check both.
|
111
|
+
if @browser.url == @previous_url && @acceptable_pages.member?( @current_page )
|
112
|
+
log.debug "Session: doesn't look like we've moved."
|
113
|
+
else
|
114
|
+
# See if we're on one of the acceptable pages; wait until we
|
115
|
+
# are for "timeout" seconds.
|
116
|
+
good_page=false
|
117
|
+
new_page=nil
|
118
|
+
@timeout.times do
|
119
|
+
if @acceptable_pages.nil?
|
120
|
+
# If we haven't gone anywhere yet, anything is good
|
121
|
+
good_page = true
|
122
|
+
new_page = @pages[@current_page]
|
123
|
+
break
|
124
|
+
end
|
125
|
+
|
126
|
+
@acceptable_pages.each do |page|
|
127
|
+
log.debug "Session: Checking our current url, #{@browser.url}, for a match in #{page.name}: #{@pages[page].match_url(@browser.url)}"
|
128
|
+
if @pages[page].match_url(@browser.url)
|
129
|
+
good_page = true
|
130
|
+
@current_page = page
|
131
|
+
new_page = @pages[page]
|
132
|
+
log.debug "Session: we seem to be on #{page.name} at #{@browser.url}"
|
133
|
+
break
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
if good_page
|
138
|
+
break
|
139
|
+
end
|
140
|
+
sleep 1
|
141
|
+
end
|
142
|
+
|
143
|
+
good_page.should be_true, "Current URL is #{@browser.url}, which doesn't match any of the acceptable pages: #{@acceptable_pages}"
|
144
|
+
|
145
|
+
log.debug "Session: checking for arrival at #{new_page.class.name}"
|
146
|
+
new_page.arrived?.should be_true
|
147
|
+
|
148
|
+
url=@browser.url
|
149
|
+
log.debug "Session: refreshed browser URL: #{url}"
|
150
|
+
new_page.match_url(url).should be_true
|
151
|
+
|
152
|
+
log.info "Session: We are currently on page #{new_page.class.name}, as we should be"
|
153
|
+
|
154
|
+
@previous_url = url
|
155
|
+
end
|
156
|
+
|
157
|
+
cpage = @pages[@current_page]
|
158
|
+
|
159
|
+
if m.inspect =~ /(password|login)/i or args.inspect =~ /(password|login)/i
|
160
|
+
log.debug "Session: dispatching method #{m} with args [redacted; password maybe] to #{cpage}"
|
161
|
+
else
|
162
|
+
log.debug "Session: dispatching method #{m} with args #{args.inspect} to #{cpage}"
|
163
|
+
end
|
164
|
+
retval = cpage.send(m, *args, &block)
|
165
|
+
log.debug "Session: method returned #{retval}"
|
166
|
+
|
167
|
+
retval
|
168
|
+
end
|
169
|
+
|
170
|
+
# This function is used to go to an intitial entry point for a
|
171
|
+
# website. The page in question must have had set_entry_url run
|
172
|
+
# in its class definition, to define how to do this. This setup
|
173
|
+
# exists because explaining to the session that we really should
|
174
|
+
# be on that page is a bit tricky.
|
175
|
+
#
|
176
|
+
# @param [Class] pklas The class for the page object that has a
|
177
|
+
# set_entry_url that we are using.
|
178
|
+
def enter(pklas)
|
179
|
+
log.info "Session: Entering the site directly using the entry point for the #{pklas.name} page class"
|
180
|
+
@current_page = pklas
|
181
|
+
@pages[pklas].enter
|
182
|
+
# Needs to run through our custom acceptable_pages= method
|
183
|
+
self.acceptable_pages = pklas
|
184
|
+
end
|
185
|
+
|
186
|
+
# Wait for long-term AJAX-style processing, i.e. watch the page
|
187
|
+
# for extended amounts of time until particular events have
|
188
|
+
# occured.
|
189
|
+
#
|
190
|
+
# @param [String] message The text to print to the user each
|
191
|
+
# time the page is not completely loaded.
|
192
|
+
# @param [Hash] opts Various named options.
|
193
|
+
#
|
194
|
+
# @option opts [Integer] numtimes The number of times to test the page.
|
195
|
+
# @option opts [Integer] interval The number of seconds to delay
|
196
|
+
# between each check.
|
197
|
+
# @option opts [Array] any_elements Watir page elements, if any
|
198
|
+
# of them are present, the page load is considered complete.
|
199
|
+
# @option opts all_elements Watir page elements, if all of them
|
200
|
+
# are present, the page load is considered complete.
|
201
|
+
#
|
202
|
+
# @yieldreturn [Boolean] An optional Proc/code block; if
|
203
|
+
# present, it is run before each page check. This is so
|
204
|
+
# simple interactions can occur without waiting for the
|
205
|
+
# timeout, and so the whole process can be short-circuited.
|
206
|
+
# If the block returns true, the long_wait ends successfully.
|
207
|
+
#
|
208
|
+
# @example
|
209
|
+
#
|
210
|
+
# @session.long_wait "Cloud Application: Still waiting for the environment to be deleted.", :any_elements => [ @session.no_environments, @session.environment_deleted ]
|
211
|
+
#
|
212
|
+
# @return [Boolean] Returns true if, on any page test, the
|
213
|
+
# element conditions were met or the block returned true (at
|
214
|
+
# which point it exits immediately), false otherwise.
|
215
|
+
def long_wait message, opts = {}
|
216
|
+
# Merge in the defaults
|
217
|
+
opts = { :numtimes => 120, :interval => 30, :any_elements => nil, :all_elements => nil }.merge(opts)
|
218
|
+
|
219
|
+
begin
|
220
|
+
opts[:numtimes].times do |count|
|
221
|
+
# Run a code block if given; might do other checks, or
|
222
|
+
# click things we need to finish, or whatever
|
223
|
+
if block_given?
|
224
|
+
self.log.debug "Session: long_wait: yielding to passed block."
|
225
|
+
blockout = yield
|
226
|
+
if blockout == true
|
227
|
+
return true
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# If any of these are present, we're done.
|
232
|
+
if opts[:any_elements]
|
233
|
+
opts[:any_elements].each do |elem|
|
234
|
+
self.log.debug "Session: long_wait: in any_elements, looking for #{elem}"
|
235
|
+
if elem.present?
|
236
|
+
self.log.debug "Session: long_wait: completed due to the presence of #{elem}"
|
237
|
+
return true
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
# If all of these are present, we're done.
|
242
|
+
if opts[:all_elements]
|
243
|
+
all_elems=true
|
244
|
+
opts[:all_elements].each do |elem|
|
245
|
+
self.log.debug "Session: long_wait: in all_elements, looking for #{elem}"
|
246
|
+
if ! elem.present?
|
247
|
+
all_elems=false
|
248
|
+
end
|
249
|
+
end
|
250
|
+
if all_elems == true
|
251
|
+
self.log.debug "Session: long_wait: completed due to the presence of all off #{opts[:all_elements]}"
|
252
|
+
return true
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# We're still here, let the user know
|
257
|
+
self.log.info message
|
258
|
+
|
259
|
+
if (((count + 1) % 20) == 0) && (self.get_config :global, :debug)
|
260
|
+
self.log.debug "Session: long_wait: We've waited a multiple of 20 times, so giving you a debugger; 'c' to continue."
|
261
|
+
debugger
|
262
|
+
end
|
263
|
+
|
264
|
+
sleep opts[:interval]
|
265
|
+
end
|
266
|
+
rescue Exception => e
|
267
|
+
self.log.debug "Session: long_wait: Had an exception #{e}"
|
268
|
+
if self.get_config :global, :debug
|
269
|
+
self.log.debug "Session: long_wait: Had an exception in debug mode: #{e.inspect}"
|
270
|
+
self.log.debug "Session: long_wait: Had an exception in debug mode: #{e.message}"
|
271
|
+
self.log.debug "Session: long_wait: Had an exception in debug mode: #{e.backtrace.join("\n")}"
|
272
|
+
|
273
|
+
self.log.debug "Session: long_wait: Had an exception, and you're in debug mode, so giving you a debugger."
|
274
|
+
debugger
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
return false
|
279
|
+
end
|
280
|
+
|
281
|
+
# Deals with popup alerts in the browser (i.e. the javascript
|
282
|
+
# alert() function). Always clicks "ok" or equivalent.
|
283
|
+
#
|
284
|
+
# FIXME: Check the text of the alert to see that it's the one
|
285
|
+
# we want.
|
286
|
+
#
|
287
|
+
# Note that we're using @browser because things can be a bit
|
288
|
+
# wonky during an alert; we don't want to run session's "are we
|
289
|
+
# on the right page?" tests, or even talk to the page object.
|
290
|
+
def handle_alert
|
291
|
+
@browser.alert.wait_until_present
|
292
|
+
|
293
|
+
if @browser.alert.exists?
|
294
|
+
@browser.alert.ok
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Does the heavy lifting, such as it is, for +acceptable_pages=+
|
299
|
+
#
|
300
|
+
# @param [Class, Symbol, Array] newpage A page class, or a
|
301
|
+
# symbol naming a page class, or an array of those, for which
|
302
|
+
# pages are acceptable.
|
303
|
+
#
|
304
|
+
# @return [Array<Gless::BasePage>]
|
305
|
+
def check_acceptable_pages newpage
|
306
|
+
if newpage.kind_of? Class
|
307
|
+
return [ newpage ]
|
308
|
+
elsif newpage.kind_of? Symbol
|
309
|
+
return [ @pages.keys.find { |x| x.name =~ /#{newpage.to_s}$/ } ]
|
310
|
+
elsif newpage.kind_of? Array
|
311
|
+
return newpage.map { |p| check_acceptable_pages p }
|
312
|
+
else
|
313
|
+
raise "You set the acceptable_pages to #{newpage.class.name}; unhandled"
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
|
2
|
+
module Gless
|
3
|
+
|
4
|
+
# This class, as its name sort of implies, is used to wrap Watir
|
5
|
+
# elements. Every element on a Gless page (i.e. any descentant of
|
6
|
+
# Gless::BasePage that uses the "element" class mothed) is not
|
7
|
+
# actually a Watir element but rather a Gless::WrapWatir instead.
|
8
|
+
#
|
9
|
+
# Most things are passed through to the underlying Watir element,
|
10
|
+
# but extensive logging occurs (in fact, if you have debugging on,
|
11
|
+
# this is where screenshots occur), and various extremely
|
12
|
+
# low-level checks are done to try to work around potential
|
13
|
+
# Selenium problems. For example, all text entry is checked at
|
14
|
+
# this level and retried until it works, since Selenium/WebDriver
|
15
|
+
# tends to be flaky about that (and it's even worse if the browser
|
16
|
+
# window gets focus during the text entry).
|
17
|
+
#
|
18
|
+
# This shouldn't ever need to be used by a user; it's done
|
19
|
+
# automatically by the +element+ class method.
|
20
|
+
class Gless::WrapWatir
|
21
|
+
include RSpec::Matchers
|
22
|
+
|
23
|
+
# Sets up the wrapping.
|
24
|
+
#
|
25
|
+
# @param [Gless::Browser] browser
|
26
|
+
# @param [Gless::Session] session
|
27
|
+
# @param [Symbol] orig_type The type of the element; normally
|
28
|
+
# with watir you'd do something like
|
29
|
+
#
|
30
|
+
# watir.button :value, 'Submit'
|
31
|
+
#
|
32
|
+
# In that expression, "button" is the orig_type.
|
33
|
+
# @param [Hash] orig_selector_args In the example
|
34
|
+
# above,
|
35
|
+
#
|
36
|
+
# { :value => 'Submit' }
|
37
|
+
#
|
38
|
+
# is the selector arguments.
|
39
|
+
# @param [Gless::BasePage, Array<Gless::BasePage>] click_destination Optional. A list of pages that are OK places to end up after we click on this element
|
40
|
+
def initialize(browser, session, orig_type, orig_selector_args, click_destination)
|
41
|
+
@browser = browser
|
42
|
+
@session = session
|
43
|
+
@orig_type = orig_type
|
44
|
+
@orig_selector_args = orig_selector_args
|
45
|
+
@elem = @browser.send(@orig_type, @orig_selector_args)
|
46
|
+
@num_retries = 3
|
47
|
+
@wait_time = 30
|
48
|
+
@click_destination = click_destination
|
49
|
+
end
|
50
|
+
|
51
|
+
# Passes everything through to the underlying Watir object, but
|
52
|
+
# with logging.
|
53
|
+
def method_missing(m, *args, &block)
|
54
|
+
wrapper_logging(m, args)
|
55
|
+
@elem.send(m, *args, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Used to log all pass through behaviours. In debug mode,
|
59
|
+
# displays details about what method was passed through, and the
|
60
|
+
# nature of the element in question.
|
61
|
+
def wrapper_logging(m, args)
|
62
|
+
if @orig_selector_args.inspect =~ /password/i
|
63
|
+
@session.log.debug "WrapWatir: Doing something with passwords, redacted."
|
64
|
+
else
|
65
|
+
if @session.get_config :global, :debug
|
66
|
+
@session.log.add_to_replay_log( @browser, @session )
|
67
|
+
end
|
68
|
+
|
69
|
+
@session.log.debug "WrapWatir: Calling #{m} with arguments #{args.inspect} on a #{@elem.class.name} element identified by: #{@orig_selector_args.inspect}"
|
70
|
+
|
71
|
+
if @elem.present? && @elem.class.name == 'Watir::HTMLElement'
|
72
|
+
@session.log.warn "FIXME: You have been lazy and said that something is of type 'element'; its actual type is #{@elem.to_subtype.class.name}; the element is identified by #{@orig_selector_args.inspect}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# A wrapper around Watir's click; handles the changing of
|
78
|
+
# acceptable pages (i.e. click_destination processing, see
|
79
|
+
# {Gless::BasePage} and {Gless::Session} for more details).
|
80
|
+
def click
|
81
|
+
if @click_destination
|
82
|
+
@session.log.debug "WrapWatir: A #{@elem.class.name} element identified by: #{@orig_selector_args.inspect} has a special destination when clicked, #{@click_destination}"
|
83
|
+
@session.acceptable_pages = @click_destination
|
84
|
+
end
|
85
|
+
wrapper_logging('click', nil)
|
86
|
+
@session.log.debug "WrapWatir: Calling click on a #{@elem.class.name} element identified by: #{@orig_selector_args.inspect}"
|
87
|
+
@elem.click
|
88
|
+
end
|
89
|
+
|
90
|
+
# Used by +set+, see description there.
|
91
|
+
def set_retries!(retries)
|
92
|
+
@num_retries=retries
|
93
|
+
|
94
|
+
return self
|
95
|
+
end
|
96
|
+
|
97
|
+
# Used by +set+, see description there.
|
98
|
+
def set_timeout!(timeout)
|
99
|
+
@wait_time=timeout
|
100
|
+
|
101
|
+
return self
|
102
|
+
end
|
103
|
+
|
104
|
+
# A wrapper around Watir's set element that retries operations.
|
105
|
+
# In particular, text fields and radio elements are checked to
|
106
|
+
# make sure that what we intended to enter *actually* got
|
107
|
+
# entered. set_retries! and set_timeout! set the number of
|
108
|
+
# times to try to get things working and the delay between ecah
|
109
|
+
# such try.
|
110
|
+
def set(*args)
|
111
|
+
wrapper_logging('set', args)
|
112
|
+
|
113
|
+
# Double-check text fields
|
114
|
+
if @elem.class.name == 'Watir::TextField'
|
115
|
+
set_text = args[0]
|
116
|
+
@elem.set(set_text)
|
117
|
+
|
118
|
+
@num_retries.times do |x|
|
119
|
+
@session.log.debug "WrapWatir: Checking that text entry worked"
|
120
|
+
@elem = @browser.send(@orig_type, @orig_selector_args)
|
121
|
+
if @elem.value == set_text
|
122
|
+
break
|
123
|
+
else
|
124
|
+
@session.log.debug "WrapWatir: It did not; sleeping for #{@wait_time} seconds"
|
125
|
+
sleep @wait_time
|
126
|
+
@session.log.debug "WrapWatir: Retrying."
|
127
|
+
wrapper_logging('set', set_text)
|
128
|
+
@elem.set(set_text)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
@elem.value.should == set_text
|
132
|
+
@session.log.debug "WrapWatir: The text entry worked"
|
133
|
+
|
134
|
+
return self
|
135
|
+
|
136
|
+
# Double-check radio buttons
|
137
|
+
elsif @elem.class.name == 'Watir::Radio'
|
138
|
+
wrapper_logging('set', [])
|
139
|
+
@elem.set
|
140
|
+
|
141
|
+
@num_retries.times do |x|
|
142
|
+
@session.log.debug "WrapWatir: Checking that the radio selection worked"
|
143
|
+
@elem = @browser.send(@orig_type, @orig_selector_args)
|
144
|
+
if @elem.set? == true
|
145
|
+
break
|
146
|
+
else
|
147
|
+
@session.log.debug "WrapWatir: It did not; sleeping for #{@wait_time} seconds"
|
148
|
+
sleep @wait_time
|
149
|
+
@session.log.debug "WrapWatir: Retrying."
|
150
|
+
wrapper_logging('set', [])
|
151
|
+
@elem.set
|
152
|
+
end
|
153
|
+
end
|
154
|
+
@elem.set?.should be_true
|
155
|
+
@session.log.debug "WrapWatir: The radio set worked"
|
156
|
+
|
157
|
+
return self
|
158
|
+
|
159
|
+
else
|
160
|
+
@elem.set(*args)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
data/lib/gless.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
# The Gless module itself only defines a setup method; all the meat
|
3
|
+
# is in the other classes, especially {Gless::Session}. See the
|
4
|
+
# README for a general overview; it lives at
|
5
|
+
# https://github.com/rlpowell/gless , which is also the home of this
|
6
|
+
# project.
|
7
|
+
module Gless
|
8
|
+
# The current version number.
|
9
|
+
VERSION = '1.0.1'
|
10
|
+
|
11
|
+
# Sets up the config, logger and browser instances, the ordering
|
12
|
+
# of which is slightly tricky.
|
13
|
+
#
|
14
|
+
# @return [Gless::Logger, Gless::EnvConfig, Gless::Browser] logger, config, browser (in that order)
|
15
|
+
def self.setup( tag )
|
16
|
+
logger = Gless::Logger.new( tag )
|
17
|
+
|
18
|
+
# Create the config reading/storage object
|
19
|
+
config = Gless::EnvConfig.new( )
|
20
|
+
|
21
|
+
# Get the whole backtrace, please.
|
22
|
+
if config.get :global, :debug
|
23
|
+
::Cucumber.use_full_backtrace = true
|
24
|
+
|
25
|
+
::RSpec.configure do |config|
|
26
|
+
# RSpec automatically cleans stuff out of backtraces;
|
27
|
+
# sometimes this is annoying when trying to debug something e.g. a gem
|
28
|
+
config.backtrace_clean_patterns = []
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Turn on verbose (info) level logging.
|
33
|
+
if config.get :global, :verbose
|
34
|
+
logger.normal_log.level = ::Logger::INFO
|
35
|
+
logger.replay_log.level = ::Logger::INFO
|
36
|
+
logger.debug "Verbose/info level logging enabled."
|
37
|
+
end
|
38
|
+
|
39
|
+
# Turn on debug level logging.
|
40
|
+
if config.get :global, :debug
|
41
|
+
logger.normal_log.level = ::Logger::DEBUG
|
42
|
+
logger.replay_log.level = ::Logger::DEBUG
|
43
|
+
logger.debug "Debug level logging enabled."
|
44
|
+
end
|
45
|
+
|
46
|
+
# Create the browser.
|
47
|
+
browser = Gless::Browser.new( config, logger )
|
48
|
+
browser.cookies.clear
|
49
|
+
|
50
|
+
return logger, config, browser
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
Dir["#{File.dirname(__FILE__)}/gless/*.rb"].each {|r| load r }
|