lapis_lazuli 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0b5527d2ff09b634197137d35cbe04a7b67c4a60
4
- data.tar.gz: 262d2f7c7b6201f250dcce19971027c0eda5267a
3
+ metadata.gz: c231db7a9cb304150e2ca2eccf956be02c367482
4
+ data.tar.gz: 3eb2d929510b34cfd70f2e944eb2590b6059f6e6
5
5
  SHA512:
6
- metadata.gz: 77883ebc45311366dd20819a803013d703c1116d2c8e98ea81de43868a1ee43b77a6d7c7262657abb28f152c5ad3e029e87e631146d8f963a1caf981fb60f8d0
7
- data.tar.gz: 4563b9880ed1c985e0d88155b752a1d55c2480f0d7768e3d82b820e40abbcaba3464602617de29d29ffd6b5226a8e3aa45c93c78b9368eda67bbc63d0182ae7d
6
+ metadata.gz: cede96cad3beb40c9a3f331c47cc930906ccd707283b393aaddccd4d713120233b3e01c4b3d7f15dbf1f117fde4b1c9d3593f1ae43d8189b2d6316345d2e4b16
7
+ data.tar.gz: d941873ef4a11ef98524f994c931b0acea5348dc261b6392dddabdff7212b1d678b91a311b18fbe1d21cd58c986100e386e88de270299093e6b0c580b28a9915
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lapis_lazuli (0.6.2)
4
+ lapis_lazuli (0.7.0)
5
5
  facets (~> 2.9)
6
6
  faraday (~> 0.9.0)
7
7
  faraday_middleware (~> 0.9.1)
@@ -31,7 +31,7 @@ GEM
31
31
  multi_json (~> 1.0)
32
32
  simplecov-html (~> 0.9.0)
33
33
  simplecov-html (0.9.0)
34
- teelogger (0.4.0)
34
+ teelogger (0.4.1)
35
35
  thor (0.19.1)
36
36
 
37
37
  PLATFORMS
@@ -18,6 +18,7 @@ require 'lapis_lazuli/browser/find'
18
18
  require "lapis_lazuli/browser/wait"
19
19
  require "lapis_lazuli/browser/screenshots"
20
20
  require "lapis_lazuli/browser/interaction"
21
+ require "lapis_lazuli/browser/remote"
21
22
  require 'lapis_lazuli/generic/xpath'
22
23
  require 'lapis_lazuli/generic/assertions'
23
24
 
@@ -26,7 +27,7 @@ module LapisLazuli
26
27
  # Extension to the Watir browser
27
28
  #
28
29
  # This class handles initialization, for the most part. BrowserModules
29
- # included here can rely on @world being set to the current cucumber world
30
+ # included here can rely on world being set to the current cucumber world
30
31
  # object, and for some WorldModules to exist in it (see assertions in
31
32
  # constructor).
32
33
  class Browser
@@ -37,29 +38,54 @@ module LapisLazuli
37
38
  include LapisLazuli::BrowserModule::Wait
38
39
  include LapisLazuli::BrowserModule::Screenshots
39
40
  include LapisLazuli::BrowserModule::Interaction
41
+ include LapisLazuli::BrowserModule::Remote
40
42
  include LapisLazuli::GenericModule::XPath
41
- include LapisLazuli::GenericModule::Assertions
42
43
 
43
- @world
44
- @browser
45
- @cached_browser_wanted
46
- @cached_optional_data
44
+ @@world=nil
45
+ @@cached_browser_options={}
46
+ @@browsers=[]
47
+ class << self
48
+ include LapisLazuli::GenericModule::Assertions
49
+
50
+ def browsers
51
+ return @@browsers
52
+ end
53
+
54
+ def add_browser(b)
55
+ # Add destructor for all browsers
56
+ Runtime.instance.set_if(self, :browsers, LapisLazuli::Browser.method(:close_all))
57
+ @@browsers.push(b)
58
+ end
59
+
60
+ def remove_browser(b)
61
+ @@browsers.delete(b)
62
+ end
63
+
64
+ def set_world(w)
65
+ @@world = w
66
+ end
47
67
 
68
+ def check_world?
69
+ assert @@world.respond_to?(:config), "Need to include LapisLazuli::WorldModule::Config in your cucumber world."
70
+ assert @@world.respond_to?(:log), "Need to include LapisLazuli::WorldModule::Logging in your cucumber world."
71
+ assert @@world.respond_to?(:error), "Need to include LapisLazuli::WorldModule::Error in your cucumber world."
72
+ assert @@world.respond_to?(:has_proxy?), "Need to include LapisLazuli::WorldModule::Proxy in your cucumber world."
73
+ end
74
+ end
75
+
76
+ @browser
48
77
  @browser_name
49
- attr_reader :browser_name
78
+ @browser_wanted
79
+ @optional_data
80
+
81
+ attr_reader :browser_name, :browser_wanted, :optional_data
50
82
 
51
- def initialize(world, *args)
83
+ def initialize(*args)
52
84
  # The class only works with some modules loaded; they're loaded by the
53
85
  # Browser module, but we can't be sure that's been used.
54
- assert world.respond_to?(:config), "Need to include LapisLazuli::WorldModule::Config in your cucumber world."
55
- assert world.respond_to?(:log), "Need to include LapisLazuli::WorldModule::Logging in your cucumber world."
56
- assert world.respond_to?(:error), "Need to include LapisLazuli::WorldModule::Error in your cucumber world."
57
- assert world.respond_to?(:has_proxy?), "Need to include LapisLazuli::WorldModule::Proxy in your cucumber world."
86
+ LapisLazuli::Browser.check_world?
58
87
 
59
- @world = world
60
-
61
- # Create a new browser with optional arguments
62
- @browser = self.init(*args)
88
+ self.start(*args)
63
89
 
64
90
  # Add registered world modules.
65
91
  if not LapisLazuli::WorldModule::Browser.browser_modules.nil?
@@ -69,92 +95,23 @@ module LapisLazuli
69
95
  end
70
96
  end
71
97
 
72
- ##
73
- # The main browser window for testing
74
- def init(browser_wanted=(no_browser_wanted=true;nil), optional_data=(no_optional_data=true;nil))
75
- # Store the optional data so on restart of the browser it still has the
76
- # correct configuration
77
- if no_optional_data and optional_data.nil? and @cached_optional_data
78
- optional_data = @cached_optional_data
79
- elsif optional_data.nil?
80
- optional_data = {}
81
- else
82
- # Duplicate the data as Webdriver modifies it
83
- @cached_optional_data = optional_data.dup
84
- end
85
-
86
- # Do the same caching stuff for the browser
87
- if no_browser_wanted and browser_wanted.nil? and @cached_browser_wanted
88
- browser_wanted = @cached_browser_wanted
89
- else
90
- @cached_browser_wanted = browser_wanted
91
- end
92
-
93
- # Create the browser
94
- self.create_internal(browser_wanted, optional_data)
98
+ # Support browser.dup to create a duplicate
99
+ def initialize_copy(source)
100
+ super
101
+ @optional_data = @optional_data.dup
102
+ @browser = create_driver(@browser_wanted, @optional_data)
103
+ # Add this browser to the list of all browsers
104
+ LapisLazuli::Browser.add_browser(self)
95
105
  end
96
106
 
97
107
  ##
98
108
  # Creates a new browser instance.
99
109
  def create(*args)
100
- return Browser.new(@world, *args)
110
+ return Browser.new(*args)
101
111
  end
102
112
 
103
- ##
104
- # Create a new browser depending on settings
105
- # Always cached the supplied arguments
106
- def create_internal(browser_wanted=nil, optional_data=nil)
107
- # No browser? Does the config have a browser? Default to firefox
108
- if browser_wanted.nil?
109
- browser_wanted = @world.env_or_config('browser', 'firefox')
110
- end
111
-
112
- # Select the correct browser
113
- case browser_wanted.to_s.downcase
114
- when 'chrome'
115
- # Check Platform running script
116
- browser = :chrome
117
- when 'safari'
118
- browser = :safari
119
- when 'ie'
120
- require 'rbconfig'
121
- if (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
122
- browser = :ie
123
- else
124
- @world.error("You can't run IE tests on non-Windows machine")
125
- end
126
- when 'ios'
127
- if RUBY_PLATFORM.downcase.include?("darwin")
128
- browser = :iphone
129
- else
130
- @world.error("You can't run IOS tests on non-mac machine")
131
- end
132
- else
133
- browser = :firefox
134
- end
135
-
136
- args = [browser]
137
- @browser_name = browser.to_s
138
- if not optional_data.nil? and not optional_data.empty?
139
- @world.log.debug("Got optional data: #{optional_data}")
140
- args.push(optional_data)
141
- elsif @world.has_proxy?
142
- # Create a session if needed
143
- if !@world.proxy.has_session?
144
- @world.proxy.create()
145
- end
146
-
147
- proxy_url = "#{@world.proxy.ip}:#{@world.proxy.port}"
148
- if browser == :firefox
149
- @world.log.debug("Configuring Firefox proxy: #{proxy_url}")
150
- profile = Selenium::WebDriver::Firefox::Profile.new
151
- profile.proxy = Selenium::WebDriver::Proxy.new :http => proxy_url, :ssl => proxy_url
152
- args.push({:profile => profile})
153
- end
154
- end
155
-
156
- browser_instance = Watir::Browser.new(*args)
157
- return browser_instance
113
+ def world
114
+ @@world
158
115
  end
159
116
 
160
117
  ##
@@ -165,23 +122,25 @@ module LapisLazuli
165
122
 
166
123
  ##
167
124
  # Start the browser if it's not yet open.
168
- def start
125
+ def start(*args)
169
126
  if @browser.nil?
170
- @browser = self.init
127
+ @browser = init(*args)
128
+ # Add this browser to the list of all browsers
129
+ LapisLazuli::Browser.add_browser(self)
171
130
  end
172
131
  end
173
132
 
174
133
  ##
175
134
  # Close and create a new browser
176
135
  def restart
177
- @world.log.debug "Restarting browser"
136
+ world.log.debug "Restarting browser"
178
137
  @browser.close
179
138
  self.start
180
139
  end
181
140
 
182
141
  ##
183
142
  # Closes the browser and updates LL so that it will open a new one if needed
184
- def close(reason = nil)
143
+ def close(reason = nil, remove_from_list=true)
185
144
  if not @browser.nil?
186
145
  if not reason.nil?
187
146
  reason = " after #{reason}"
@@ -189,8 +148,11 @@ module LapisLazuli
189
148
  reason = ""
190
149
  end
191
150
 
192
- @world.log.debug "Closing browser#{reason}: #{@browser}"
151
+ world.log.debug "Closing browser#{reason}: #{@browser}"
193
152
  @browser.close
153
+ if remove_from_list
154
+ LapisLazuli::Browser.remove_browser(self)
155
+ end
194
156
  @browser = nil
195
157
  end
196
158
  end
@@ -209,12 +171,12 @@ module LapisLazuli
209
171
  # Default: feature
210
172
  def close_after_scenario(scenario)
211
173
  # Determine the config
212
- close_browser_after = @world.env_or_config("close_browser_after")
174
+ close_browser_after = world.env_or_config("close_browser_after")
213
175
 
214
176
  case close_browser_after
215
177
  when "scenario"
216
178
  # We always close it
217
- self.close close_browser_after
179
+ LapisLazuli::Browser.close_all close_browser_after
218
180
  when "never"
219
181
  # Do nothing: party time, excellent!
220
182
  when "end"
@@ -222,7 +184,7 @@ module LapisLazuli
222
184
  else
223
185
  if is_last_scenario?(scenario)
224
186
  # Close it
225
- self.close close_browser_after
187
+ LapisLazuli::Browser.close_all close_browser_after
226
188
  end
227
189
  end
228
190
  end
@@ -246,13 +208,143 @@ module LapisLazuli
246
208
  end
247
209
 
248
210
  def destroy(world)
249
- if "end" == world.env_or_config("close_browser_after")
250
- begin
251
- self.close "end"
252
- rescue
253
- world.log.debug("Failed to close the browser, probably chrome")
211
+ # Primary browser should also close other browsers
212
+ LapisLazuli::Browser.close_all("end")
213
+ end
214
+
215
+ def self.close_all(reason=nil)
216
+ # A running browser should exist and we are allowed to close it
217
+ if @@browsers.length != 0 and @@world.env_or_config("close_browser_after") != "never"
218
+ # Notify user
219
+ @@world.log.debug("Closing all browsers")
220
+
221
+ # Close each browser
222
+ @@browsers.each do |b|
223
+ begin
224
+ b.close reason, false
225
+ rescue Exception => err
226
+ # Provide some details
227
+ @@world.log.debug("Failed to close the browser, probably chrome: #{err.to_s}")
228
+ end
254
229
  end
230
+
231
+ # Make sure the array is cleared
232
+ @@browsers = []
255
233
  end
256
234
  end
235
+
236
+ private
237
+ ##
238
+ # The main browser window for testing
239
+ def init(browser_wanted=(no_browser_wanted=true;nil), optional_data=(no_optional_data=true;nil))
240
+ # Store the optional data so on restart of the browser it still has the
241
+ # correct configuration
242
+ if no_optional_data and optional_data.nil? and @@cached_browser_options.has_key?(:optional_data) and (browser_wanted.nil? or browser_wanted == @@cached_browser_options[:browser])
243
+ optional_data = @@cached_browser_options[:optional_data]
244
+ elsif optional_data.nil?
245
+ optional_data = {}
246
+ end
247
+
248
+ # Do the same caching stuff for the browser
249
+ if no_browser_wanted and browser_wanted.nil? and @@cached_browser_options.has_key?(:browser)
250
+ browser_wanted = @@cached_browser_options[:browser]
251
+ end
252
+
253
+
254
+ if !@@cached_browser_options.has_key? :browser
255
+ @@cached_browser_options[:browser] = browser_wanted
256
+ # Duplicate the data as Webdriver modifies it
257
+ @@cached_browser_options[:optional_data] = optional_data.dup
258
+ end
259
+
260
+ @browser_wanted = browser_wanted
261
+ @optional_data = optional_data
262
+ # Create the browser
263
+ create_driver(@browser_wanted, @optional_data)
264
+ end
265
+
266
+ ##
267
+ # Create a new browser depending on settings
268
+ # Always cached the supplied arguments
269
+ def create_driver(browser_wanted=nil, optional_data=nil)
270
+ # No browser? Does the config have a browser? Default to firefox
271
+ if browser_wanted.nil?
272
+ browser_wanted = world.env_or_config('browser', 'firefox')
273
+ end
274
+
275
+ # Select the correct browser
276
+ case browser_wanted.to_s.downcase
277
+ when 'chrome'
278
+ # Check Platform running script
279
+ b = :chrome
280
+ when 'safari'
281
+ b = :safari
282
+ when 'ie'
283
+ require 'rbconfig'
284
+ if (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
285
+ b = :ie
286
+ else
287
+ world.error("You can't run IE tests on non-Windows machine")
288
+ end
289
+ when 'ios'
290
+ if RUBY_PLATFORM.downcase.include?("darwin")
291
+ b = :iphone
292
+ else
293
+ world.error("You can't run IOS tests on non-mac machine")
294
+ end
295
+ when 'remote'
296
+ b = :remote
297
+ else
298
+ b = :firefox
299
+ end
300
+
301
+ args = [b]
302
+ @browser_name = b.to_s
303
+ if b == :remote
304
+ # Get the config
305
+ remote_config = world.env_or_config("remote", {})
306
+
307
+ # The settings we are going to use to create the browser
308
+ remote_settings = {}
309
+
310
+ # Add the config to the settings using downcase string keys
311
+ remote_config.each{|k,v| remote_settings[k.to_s.downcase] = v}
312
+
313
+ if optional_data.is_a? Hash
314
+ # Convert the optional data to downcase string keys
315
+ string_hash = Hash.new
316
+ optional_data.each{|k,v| string_hash[k.to_s.downcase] = v}
317
+
318
+ # Merge them with the settings
319
+ remote_settings.merge! string_hash
320
+ end
321
+
322
+ args.push(remote_browser_config(remote_settings))
323
+ elsif not optional_data.nil? and not optional_data.empty?
324
+ world.log.debug("Got optional data: #{optional_data}")
325
+ args.push(optional_data)
326
+ elsif world.has_proxy?
327
+ # Create a session if needed
328
+ if !world.proxy.has_session?
329
+ world.proxy.create()
330
+ end
331
+
332
+ proxy_url = "#{world.proxy.ip}:#{world.proxy.port}"
333
+ if b == :firefox
334
+ world.log.debug("Configuring Firefox proxy: #{proxy_url}")
335
+ profile = Selenium::WebDriver::Firefox::Profile.new
336
+ profile.proxy = Selenium::WebDriver::Proxy.new :http => proxy_url, :ssl => proxy_url
337
+ args.push({:profile => profile})
338
+ end
339
+ end
340
+
341
+ begin
342
+ browser_instance = Watir::Browser.new(*args)
343
+ rescue Selenium::WebDriver::Error::UnknownError => err
344
+ raise err
345
+ end
346
+ return browser_instance
347
+ end
257
348
  end
349
+
258
350
  end
@@ -25,9 +25,9 @@ module BrowserModule
25
25
  if errors.length > 0 or self.get_http_status.to_i > 299
26
26
  errors.each do |error|
27
27
  if error.is_a? Hash
28
- @world.log.debug("#{error["message"]} #{error["url"]} #{error["line"]} #{error["column"]}\n#{error["stack"]}")
28
+ world.log.debug("#{error["message"]} #{error["url"]} #{error["line"]} #{error["column"]}\n#{error["stack"]}")
29
29
  else
30
- @world.log.debug("#{error}")
30
+ world.log.debug("#{error}")
31
31
  end
32
32
  end
33
33
  return true
@@ -42,12 +42,12 @@ module BrowserModule
42
42
  def get_html_errors
43
43
  result = []
44
44
  # Need some error strings
45
- if @world.has_env_or_config?("error_strings")
45
+ if world.has_env_or_config?("error_strings")
46
46
  begin
47
47
  # Get the HTML of the page
48
48
  page_text = @browser.html
49
49
  # Try to find all errors
50
- @world.env_or_config("error_strings").each {|error|
50
+ world.env_or_config("error_strings").each {|error|
51
51
  if page_text.include? error
52
52
  # Add to the result list
53
53
  result.push error
@@ -55,7 +55,7 @@ module BrowserModule
55
55
  }
56
56
  rescue RuntimeError => err
57
57
  # An error?
58
- @world.log.debug "Cannot read the html for page #{@browser.url}: #{err}"
58
+ world.log.debug "Cannot read the html for page #{@browser.url}: #{err}"
59
59
  end
60
60
  end
61
61
  # By default we don't have errors
@@ -157,7 +157,7 @@ module BrowserModule
157
157
  else
158
158
  options[:message] = optional_message("Invalid :pick value #{pick}.", options)
159
159
  options[:groups] = ['find', 'pick']
160
- @world.error(options)
160
+ world.error(options)
161
161
  end
162
162
  end
163
163
  end
@@ -243,7 +243,7 @@ module BrowserModule
243
243
  if not like_opts.has_key? :element
244
244
  selector[:message] = optional_message("Like selector are missing the :element key.", selector)
245
245
  selector[:groups] = ['find', 'selector']
246
- @world.error(selector)
246
+ world.error(selector)
247
247
  end
248
248
  end
249
249
 
@@ -443,7 +443,7 @@ module BrowserModule
443
443
  res = []
444
444
  lambdas.each do |func|
445
445
  res = func.call
446
- # @world.log.debug("Got: #{res}")
446
+ # world.log.debug("Got: #{res}")
447
447
  if res.length > 0
448
448
  break
449
449
  end
@@ -453,7 +453,7 @@ module BrowserModule
453
453
  else
454
454
  options[:message] = optional_message("Invalid mode '#{options[:mode]}' provided to multi_find_all.", options)
455
455
  options[:groups] = ['find', 'multi', 'mode']
456
- @world.error(options)
456
+ world.error(options)
457
457
  end
458
458
  end
459
459
 
@@ -492,7 +492,7 @@ module BrowserModule
492
492
  rescue RuntimeError => err
493
493
  opts[:message] = optional_message(message, selectors)
494
494
  opts[:exception] = err
495
- @world.error(opts)
495
+ world.error(opts)
496
496
  end
497
497
  end
498
498
  end # module Find
@@ -69,7 +69,7 @@ module BrowserModule
69
69
  end
70
70
 
71
71
  if errors.length > 0
72
- raise "Could not click #{elem} given any of these click types: #{types}: #{errors}"
72
+ world.error("Could not click #{elem} given any of these click types: #{types}: #{errors}")
73
73
  end
74
74
  end
75
75
 
@@ -0,0 +1,140 @@
1
+ #
2
+ # LapisLazuli
3
+ # https://github.com/spriteCloud/lapis-lazuli
4
+ #
5
+ # Copyright (c) 2013-2014 spriteCloud B.V. and other LapisLazuli contributors.
6
+ # All rights reserved.
7
+ #
8
+
9
+ module LapisLazuli
10
+ module BrowserModule
11
+ module Remote
12
+ # Convert settings to a valid remote driver argument
13
+ #
14
+ # Features:
15
+ # - settings hash can be case insensitive "URL","Url", "url"
16
+ # - caps.firefox_profile will be converted to a Selenium::WebDriver::Firefox::Profile
17
+ # - caps.proxy / caps.firefox_profile.proxy will be converted to a Selenium::WebDriver::Proxy
18
+ # - Hashes can have a String or a Symbol as key
19
+ #
20
+ # Example:
21
+ # args = remote_browser_config(
22
+ # {
23
+ # "url"=>"http://test.com",
24
+ # "user"=>"user21",
25
+ # "password"=>"jehwiufhewuf",
26
+ # "caps"=> {
27
+ # "browser_name"=>"firefox",
28
+ # "version"=>"37",
29
+ # "firefox_profile"=>{
30
+ # "plugin.state.flash"=>0,
31
+ # "secure_ssl"=>true,
32
+ # "proxy"=>{"http"=>"test.com:9000"}
33
+ # },
34
+ # "proxy"=>{:http=>"test.com:7000"},
35
+ # :css_selectors_enabled => true
36
+ # }
37
+ # })
38
+ # Watir::Browser.new :remote, args
39
+ def remote_browser_config(settings)
40
+ require "uri"
41
+ require "selenium-webdriver"
42
+
43
+ if !settings.is_a? Hash
44
+ world.error("Missing Remote Browser Settings")
45
+ end
46
+
47
+ # Fetch the URl
48
+ url = hash_get_case_insensitive(settings,"url")
49
+
50
+ # Test if its a valid URL
51
+ if not (url.to_s =~ /\A#{URI::regexp(["http", "https"])}\z/)
52
+ raise "Incorrect Remote URL: '#{url.to_s}'"
53
+ end
54
+
55
+ # Create URI object
56
+ uri = URI.parse(url)
57
+
58
+ # Add user if needed
59
+ user = hash_get_case_insensitive(settings,"user")
60
+ if !user.nil?
61
+ uri.user = user
62
+ end
63
+
64
+ # Add password if needed
65
+ password = hash_get_case_insensitive(settings,"password")
66
+ if !password.nil?
67
+ uri.password = password
68
+ end
69
+
70
+ # Create capabil
71
+ # Check ities
72
+ caps = Selenium::WebDriver::Remote::Capabilities.new
73
+ # Fetch the settings
74
+ caps_settings = hash_get_case_insensitive(settings,"caps")
75
+
76
+ # If we have settings
77
+ if !caps_settings.nil? and caps_settings.is_a? Hash
78
+ caps_settings.each do |key, val|
79
+ # Convert to proxy
80
+ if key.to_s == "proxy"
81
+ set_proxy(caps, val)
82
+ # Convert to FF profile
83
+ elsif key.to_s == "firefox_profile"
84
+ profile = Selenium::WebDriver::Firefox::Profile.new
85
+ # Set all the options
86
+ val.each do |fkey, fval|
87
+ # Convert to proxy
88
+ if fkey.to_s == "proxy"
89
+ set_proxy(profile,fval)
90
+ else
91
+ set_key(profile, fkey, fval)
92
+ end
93
+ end
94
+ # Set the profile
95
+ caps[:firefox_profile] = profile
96
+ else
97
+ # Use set_key to assign the key
98
+ set_key(caps, key, val)
99
+ end
100
+ end
101
+ end
102
+
103
+ world.log.debug("Using remote browser: #{url} (#{uri.user}) #{caps.to_json}")
104
+
105
+ return {
106
+ :url => uri.to_s,
107
+ :desired_capabilities => caps
108
+ }
109
+ end
110
+
111
+ private
112
+ def hash_get_case_insensitive(hash, key)
113
+ new_key = hash.keys.find {|e| e.to_s.casecmp(key.to_s) == 0}
114
+ if new_key.nil?
115
+ return nil
116
+ end
117
+ return hash[new_key]
118
+ end
119
+
120
+ # Sets a selenium proxy to the object
121
+ def set_proxy(object, hash)
122
+ proxy = Selenium::WebDriver::Proxy.new
123
+ hash.each do |key, val|
124
+ set_key(proxy, key, val)
125
+ end
126
+ object.proxy = proxy
127
+ end
128
+
129
+ # Uses function based on key or key itself to store the value in the object
130
+ def set_key(object, key, val)
131
+ if object.respond_to? "#{key}="
132
+ object.send("#{key}=", val)
133
+ else
134
+ object[key] = val
135
+ end
136
+ end
137
+
138
+ end # module Remote
139
+ end # module BrowserModule
140
+ end # module LapisLazuli
@@ -15,24 +15,23 @@ module BrowserModule
15
15
  ##
16
16
  # Returns the name of the screenshot, if take_screenshot is called now.
17
17
  def screenshot_name(suffix="")
18
- dir = @world.env_or_config("screenshot_dir")
18
+ dir = world.env_or_config("screenshot_dir")
19
19
 
20
20
  # Generate the file name according to the new or old scheme.
21
- name = 'screenshot'
22
- case @world.env_or_config("screenshot_scheme")
21
+ case world.env_or_config("screenshot_scheme")
23
22
  when "new"
24
- # For non-cucumber cases: we don't have @world.scenario.data
25
- if not @world.scenario.data.nil?
26
- name = @world.scenario.id
23
+ # For non-cucumber cases: we don't have world.scenario.data
24
+ if not world.scenario.data.nil?
25
+ name = world.scenario.id
27
26
  end
28
27
  # FIXME random makes this non-repeatable, sadly
29
- name = "#{@world.scenario.time[:iso_short]}-#{name}-#{Random.rand(10000).to_s}.png"
28
+ name = "#{world.scenario.time[:iso_short]}-#{@browser.object_id}-#{name}-#{Random.rand(10000).to_s}.png"
30
29
  else # 'old' and default
31
- # For non-cucumber cases: we don't have @world.scenario.data
32
- if not @world.scenario.data.nil?
33
- name = @world.scenario.data.name.gsub(/^.*(\\|\/)/, '').gsub(/[^\w\.\-]/, '_').squeeze('_')
30
+ # For non-cucumber cases: we don't have world.scenario.data
31
+ if not world.scenario.data.nil?
32
+ name = world.scenario.data.name.gsub(/^.*(\\|\/)/, '').gsub(/[^\w\.\-]/, '_').squeeze('_')
34
33
  end
35
- name = @world.time[:timestamp] + "_" + name + '.png'
34
+ name = world.time[:timestamp] + "_" + name + '.png'
36
35
  end
37
36
 
38
37
  # Full file location
@@ -46,7 +45,7 @@ module BrowserModule
46
45
  # Using the name as defined at the start of every scenario
47
46
  def take_screenshot(suffix="")
48
47
  # If the target directory does not exist, create it.
49
- dir = @world.env_or_config("screenshot_dir")
48
+ dir = world.env_or_config("screenshot_dir")
50
49
  begin
51
50
  Dir.mkdir dir
52
51
  rescue SystemCallError => ex
@@ -61,14 +60,14 @@ module BrowserModule
61
60
  begin
62
61
  # Save the screenshot
63
62
  @browser.screenshot.save fileloc
64
- @world.log.debug "Screenshot saved: #{fileloc}"
63
+ world.log.debug "Screenshot saved: #{fileloc}"
65
64
 
66
65
  # Try to store the screenshot name
67
- if @world.respond_to? :annotate
68
- @world.annotate :screenshot => fileloc
66
+ if world.respond_to? :annotate
67
+ world.annotate :screenshot => fileloc
69
68
  end
70
69
  rescue RuntimeError => e
71
- @world.log.debug "Failed to save screenshot to '#{fileloc}'. Error message #{e.message}"
70
+ world.log.debug "Failed to save screenshot to '#{fileloc}'. Error message #{e.message}"
72
71
  end
73
72
  return fileloc
74
73
  end
@@ -103,7 +103,7 @@ module BrowserModule
103
103
  begin
104
104
  res = Watir::Wait.send(condition, timeout, &find_proc)
105
105
  rescue Watir::Wait::TimeoutError => e
106
- @world.log.debug("Caught timeout: #{e}")
106
+ world.log.debug("Caught timeout: #{e}")
107
107
  err = e
108
108
  end
109
109
 
@@ -112,7 +112,7 @@ module BrowserModule
112
112
  # Error handling
113
113
  if not err.nil? and filter_results.empty?
114
114
  options[:exception] = err
115
- @world.error(options)
115
+ world.error(options)
116
116
  end
117
117
 
118
118
  # Set if the underlying find function returns single results
@@ -39,7 +39,10 @@ module LapisLazuli
39
39
  return @objects[name]
40
40
  end
41
41
 
42
- obj = block.call
42
+ obj = nil
43
+ if !block.nil?
44
+ obj = block.call
45
+ end
43
46
 
44
47
  set(world, name, obj, destructor)
45
48
 
@@ -73,6 +76,17 @@ module LapisLazuli
73
76
  return world.send(name).destroy(world)
74
77
  end
75
78
 
79
+ # Try to run destroy on the object itself
80
+ obj = Runtime.get(name)
81
+ if obj.respond_to? :destroy
82
+ return obj.send(:destroy)
83
+ end
84
+
85
+ # If the object has stream/socket functions close ends the connection
86
+ if obj.respond_to? :close
87
+ return obj.send(:close)
88
+ end
89
+
76
90
  # If all else fails, we have to log an error. We can't rely
77
91
  # on log existing in world, though...
78
92
  message = "No destructor available for #{name}."
@@ -6,5 +6,5 @@
6
6
  # All rights reserved.
7
7
  #
8
8
  module LapisLazuli
9
- VERSION = "0.6.3"
9
+ VERSION = "0.7.0"
10
10
  end
@@ -50,9 +50,10 @@ module WorldModule
50
50
  def browser(*args)
51
51
  b = Runtime.instance.set_if(self, :browser) do
52
52
  # Add LL to the arguments for the browser
53
- browser_args = args.unshift(self)
53
+ LapisLazuli::Browser.set_world(self)
54
+
54
55
  # Create & return a new browser object
55
- LapisLazuli::Browser.new(*browser_args)
56
+ LapisLazuli::Browser.new(*args)
56
57
  end
57
58
 
58
59
  if not b.is_open?
@@ -238,7 +238,7 @@ module WorldModule
238
238
  # Returns current environment
239
239
  def current_env
240
240
  init
241
-
241
+
242
242
  return @env
243
243
  end
244
244
 
@@ -255,11 +255,9 @@ module WorldModule
255
255
 
256
256
  # Environment variables for known options override environment specific
257
257
  # options, too
258
- if CONFIG_OPTIONS.has_key? variable
259
- var = variable.upcase
260
- if ENV.has_key? var
261
- return ENV[var]
262
- end
258
+ env_var = var_from_env(variable, default)
259
+ if env_var != default
260
+ return env_var
263
261
  end
264
262
 
265
263
  return self.config("#{@env}.#{variable}",default)
@@ -281,17 +279,104 @@ module WorldModule
281
279
  # Make sure the configured configuration is loaded, if possible
282
280
  init
283
281
 
282
+ # Environment variables for known options override environment specific
283
+ # options, too
284
+ env_var = var_from_env(variable, default)
285
+ if env_var != default
286
+ return env_var
287
+ end
288
+
284
289
  if self.has_env?(variable)
285
290
  return self.env(variable, default)
286
291
  elsif self.has_config?(variable)
287
292
  return self.config(variable, default)
288
293
  else
289
- return nil
294
+ return default
290
295
  end
291
296
  end
292
297
 
298
+ def var_from_env(var, default=nil)
299
+ # Simple solution for single depth variables like "browser"
300
+ if ENV.has_key? var
301
+ return ENV[var]
302
+ end
293
303
 
304
+ value = default
294
305
 
306
+ # Variables like:
307
+ # var_from_env("remote.url","http://test.test")
308
+ if var.is_a? String and
309
+ var.include? "." and
310
+ not default.is_a? Hash
311
+
312
+ # Env variables cannot contain a . replace them by _
313
+ key_wanted = var.gsub(".","__")
314
+
315
+ # Do a case insensitive compare
316
+ ENV.keys.each do |key|
317
+ if key.casecmp(key_wanted) == 0
318
+ value = ENV[key]
319
+ break
320
+ end
321
+ end
322
+
323
+ # Environment:
324
+ # REMOTE__USER=test
325
+ # REMOTE__PASS=test
326
+ # REMOTE__PROXY__HTTP=http://test.com
327
+ #
328
+ # Call:
329
+ # var_from_env("remote",{})
330
+ #
331
+ # Result:
332
+ # {"USER" => "test",
333
+ # "PASS" => "test",
334
+ # "proxy" => {"HTTP" => "http://test.con"}}
335
+ elsif default.is_a? Hash
336
+ # Env variables cannot contain a . replace them by _
337
+ key_wanted = var.gsub(".","__")
338
+ # Use a regular expression starting with the wanted key
339
+ rgx = Regexp.new("^#{key_wanted}","i")
340
+
341
+ result = {}
342
+ # For each key check if it matched the regexp
343
+ ENV.keys.each do |key|
344
+ if (key =~ rgx) == 0
345
+ tmp = result
346
+ # Remove start and split into parts
347
+ parts = key.sub(rgx, "").split("__")
348
+ # Remove empty start if needed
349
+ if parts[0].to_s.empty?
350
+ parts.shift
351
+ end
352
+
353
+ # For each part
354
+ parts.each_with_index do |part, index|
355
+ # Final part should store the value in the hash
356
+ if index == parts.length - 1
357
+ tmp[part] = ENV[key]
358
+ else
359
+ # Otherwise, downcase the partname
360
+ part.downcase!
361
+ # Set it to an object if needed
362
+ if !tmp.has_key? part
363
+ tmp[part] = {}
364
+ end
365
+ # Assign tmp to the new hash
366
+ tmp = tmp[part]
367
+ end
368
+ end
369
+ end
370
+ end
371
+
372
+ # If we have set keys in the result return it
373
+ if result.keys.length > 0
374
+ return result
375
+ end
376
+ end
377
+
378
+ return value
379
+ end
295
380
 
296
381
  end # module Config
297
382
  end # module WorldModule
@@ -77,18 +77,27 @@ module WorldModule
77
77
  end
78
78
 
79
79
  # Did we fail?
80
- if respond_to? :scenario and respond_to? :browser and respond_to? :config
81
- if (cuke_scenario.failed? or (scenario.check_browser_errors and browser.has_error?))
80
+ if respond_to? :scenario and respond_to? :has_browser? and respond_to? :browser and respond_to? :config
81
+ if has_browser? and (cuke_scenario.failed? or (scenario.check_browser_errors and browser.has_error?))
82
82
  # Take a screenshot if needed
83
83
  if has_env_or_config?('screenshot_on_failure')
84
- fileloc = browser.take_screenshot()
84
+ if env_or_config("screenshot_scheme") == "new"
85
+ # Take screenshots on all active browsers
86
+ LapisLazuli::Browser.browsers.each do |b|
87
+ fileloc = b.take_screenshot()
88
+ end
89
+ else
90
+ browser.take_screenshot()
91
+ end
85
92
  end
86
93
  end
87
94
  end
88
95
 
89
96
  # Close browser if needed
90
- if respond_to? :browser
91
- browser.close_after_scenario(cuke_scenario)
97
+ if respond_to? :has_browser? and respond_to? :browser
98
+ if has_browser?
99
+ browser.close_after_scenario(cuke_scenario)
100
+ end
92
101
  end
93
102
  end
94
103
 
@@ -23,8 +23,8 @@ module WorldModule
23
23
  ##
24
24
  # Checks if there is a proxy started
25
25
  def has_proxy?
26
- p = Runtime.instance.get :proxy
27
- return !p.nil?
26
+ proxy = Runtime.instance.get :proxy
27
+ return !proxy.nil?
28
28
  end
29
29
 
30
30
  ##
@@ -46,7 +46,7 @@ module WorldModule
46
46
  end
47
47
 
48
48
  # Try to start the proxy
49
- p = LapisLazuli::Proxy.new(proxy_ip, proxy_port, proxy_master)
49
+ proxy = LapisLazuli::Proxy.new(proxy_ip, proxy_port, proxy_master)
50
50
 
51
51
  log.debug("Found proxy: #{proxy_ip}:#{proxy_port}, spritecloud: #{proxy_master}")
52
52
  rescue StandardError => err
data/test/Gemfile CHANGED
@@ -36,8 +36,7 @@ gem 'cucumber', '>1.3.19'
36
36
  # gem 'cucumber', '=1.3.19'
37
37
 
38
38
  # Testing related
39
- gem 'simplecov', group: :test, require: nil
40
- gem "codeclimate-test-reporter", group: :test, require: nil
39
+ gem 'simplecov'
41
40
 
42
41
  # LapisLazul itself
43
42
  gem 'lapis_lazuli', :github => 'spriteCloud/lapis-lazuli', :branch => 'master'
@@ -0,0 +1,14 @@
1
+ @config @p
2
+ Feature: config
3
+ When I want to test the Lapis Lazuli library
4
+ I want to run a webserver with some test files
5
+ And execute the each library function that handles configurations.
6
+
7
+ @config_01
8
+ Scenario: config_01 - Environment Hash
9
+ Given I set environment variable "configtest__user" to "username"
10
+ And I set environment variable "configtest__pass" to "password"
11
+ Then the environment variable "configtest" has "user" set to "username"
12
+ And the environment variable "configtest.user" is set to "username"
13
+ And the environment variable "configtest" has "pass" set to "password"
14
+ And the environment variable "close_browser_after" is set to "feature"
@@ -49,13 +49,13 @@ Given(/^I create a firefox browser named "(.*?)"( with proxy to "(.*?)")$/) do |
49
49
  else
50
50
  b = browser.create :firefox
51
51
  end
52
- scenario.storage.set(name, browser)
52
+ scenario.storage.set(name, b)
53
53
  end
54
54
 
55
55
  Given(/^I close the browser named "(.*?)"$/) do |name|
56
56
  if scenario.storage.has? name
57
- browser = scenario.storage.get name
58
- browser.close
57
+ b = scenario.storage.get name
58
+ b.close
59
59
  else
60
60
  error("No item in the storage named #{name}")
61
61
  end
@@ -139,6 +139,9 @@ Then(/^I should be able to click the first button by force click$/) do
139
139
  )
140
140
  end
141
141
 
142
+ Given(/^I set environment variable "(.*?)" to "(.*?)"$/) do |var, val|
143
+ ENV[var]=val
144
+ end
142
145
 
143
146
  Given(/^I annotate a step with (.*?)$/) do |data|
144
147
  annotate data
@@ -230,8 +230,14 @@ end
230
230
  Then(/^the firefox browser named "(.*?)" has a profile$/) do |name|
231
231
  if scenario.storage.has? name
232
232
  browser = scenario.storage.get name
233
- if browser.driver.capabilities.firefox_profile.nil?
234
- raise "Profile is not set"
233
+ if browser.browser_name == "remote"
234
+ if browser.driver.capabilities.firefox_profile.nil?
235
+ raise "Remote Firefox Profile is not set"
236
+ end
237
+ else
238
+ if browser.optional_data.has_key? "profile" or browser.optional_data.has_key? :profile
239
+ raise "No profile found in the optional data"
240
+ end
235
241
  end
236
242
  else
237
243
  error("No item in the storage named #{name}")
@@ -348,3 +354,14 @@ Then(/^I expect to use tagname to hash options to (.*?) find element (.*?)$/) do
348
354
  end
349
355
  end
350
356
 
357
+ Then(/^the environment variable "(.*?)" is set to "(.*?)"$/) do |var,val|
358
+ value = env(var)
359
+ assert_equal val, value, "Incorrect value"
360
+ end
361
+
362
+ Then(/^the environment variable "(.*?)" has "(.*?)" set to "(.*?)"$/) do |var, key, val|
363
+ hash = env(var,{})
364
+ assert hash.is_a?(Hash), "Config element is not a Hash: #{hash}"
365
+ assert hash.has_key?(key), "Config is missing #{key}"
366
+ assert_equal hash[key], val, "Config has incorrect value"
367
+ end
@@ -2,9 +2,6 @@
2
2
  # Copyright 2014 spriteCloud B.V. All rights reserved.
3
3
  # Generated by LapisLazuli, version 0.0.1
4
4
  # Author: "Onno Steenbergen" <onno@steenbe.nl>
5
- require "codeclimate-test-reporter"
6
- CodeClimate::TestReporter.start
7
-
8
5
  require 'lapis_lazuli'
9
6
  require 'lapis_lazuli/cucumber'
10
7
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lapis_lazuli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Onno Steenbergen
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2015-04-22 00:00:00.000000000 Z
14
+ date: 2015-05-06 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -196,6 +196,7 @@ files:
196
196
  - lib/lapis_lazuli/browser/error.rb
197
197
  - lib/lapis_lazuli/browser/find.rb
198
198
  - lib/lapis_lazuli/browser/interaction.rb
199
+ - lib/lapis_lazuli/browser/remote.rb
199
200
  - lib/lapis_lazuli/browser/screenshots.rb
200
201
  - lib/lapis_lazuli/browser/wait.rb
201
202
  - lib/lapis_lazuli/cli.rb
@@ -239,6 +240,7 @@ files:
239
240
  - test/features/browser.feature
240
241
  - test/features/button.feature
241
242
  - test/features/click.feature
243
+ - test/features/config.feature
242
244
  - test/features/error.feature
243
245
  - test/features/find.feature
244
246
  - test/features/har.feature
@@ -293,6 +295,7 @@ test_files:
293
295
  - test/features/browser.feature
294
296
  - test/features/button.feature
295
297
  - test/features/click.feature
298
+ - test/features/config.feature
296
299
  - test/features/error.feature
297
300
  - test/features/find.feature
298
301
  - test/features/har.feature