lapis_lazuli 0.6.3 → 0.7.0
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/lib/lapis_lazuli/browser.rb +200 -108
- data/lib/lapis_lazuli/browser/error.rb +5 -5
- data/lib/lapis_lazuli/browser/find.rb +5 -5
- data/lib/lapis_lazuli/browser/interaction.rb +1 -1
- data/lib/lapis_lazuli/browser/remote.rb +140 -0
- data/lib/lapis_lazuli/browser/screenshots.rb +15 -16
- data/lib/lapis_lazuli/browser/wait.rb +2 -2
- data/lib/lapis_lazuli/runtime.rb +15 -1
- data/lib/lapis_lazuli/version.rb +1 -1
- data/lib/lapis_lazuli/world/browser.rb +3 -2
- data/lib/lapis_lazuli/world/config.rb +92 -7
- data/lib/lapis_lazuli/world/hooks.rb +14 -5
- data/lib/lapis_lazuli/world/proxy.rb +3 -3
- data/test/Gemfile +1 -2
- data/test/features/config.feature +14 -0
- data/test/features/step_definitions/interaction_steps.rb +6 -3
- data/test/features/step_definitions/validation_steps.rb +19 -2
- data/test/features/support/env.rb +0 -3
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c231db7a9cb304150e2ca2eccf956be02c367482
|
4
|
+
data.tar.gz: 3eb2d929510b34cfd70f2e944eb2590b6059f6e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
34
|
+
teelogger (0.4.1)
|
35
35
|
thor (0.19.1)
|
36
36
|
|
37
37
|
PLATFORMS
|
data/lib/lapis_lazuli/browser.rb
CHANGED
@@ -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
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
78
|
+
@browser_wanted
|
79
|
+
@optional_data
|
80
|
+
|
81
|
+
attr_reader :browser_name, :browser_wanted, :optional_data
|
50
82
|
|
51
|
-
def initialize(
|
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
|
-
|
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
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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(
|
110
|
+
return Browser.new(*args)
|
101
111
|
end
|
102
112
|
|
103
|
-
|
104
|
-
|
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 =
|
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
|
-
|
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
|
-
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
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
|
-
|
28
|
+
world.log.debug("#{error["message"]} #{error["url"]} #{error["line"]} #{error["column"]}\n#{error["stack"]}")
|
29
29
|
else
|
30
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
|
495
|
+
world.error(opts)
|
496
496
|
end
|
497
497
|
end
|
498
498
|
end # module Find
|
@@ -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 =
|
18
|
+
dir = world.env_or_config("screenshot_dir")
|
19
19
|
|
20
20
|
# Generate the file name according to the new or old scheme.
|
21
|
-
|
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
|
25
|
-
if not
|
26
|
-
name =
|
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 = "#{
|
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
|
32
|
-
if not
|
33
|
-
name =
|
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 =
|
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 =
|
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
|
-
|
63
|
+
world.log.debug "Screenshot saved: #{fileloc}"
|
65
64
|
|
66
65
|
# Try to store the screenshot name
|
67
|
-
if
|
68
|
-
|
66
|
+
if world.respond_to? :annotate
|
67
|
+
world.annotate :screenshot => fileloc
|
69
68
|
end
|
70
69
|
rescue RuntimeError => e
|
71
|
-
|
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
|
-
|
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
|
-
|
115
|
+
world.error(options)
|
116
116
|
end
|
117
117
|
|
118
118
|
# Set if the underlying find function returns single results
|
data/lib/lapis_lazuli/runtime.rb
CHANGED
@@ -39,7 +39,10 @@ module LapisLazuli
|
|
39
39
|
return @objects[name]
|
40
40
|
end
|
41
41
|
|
42
|
-
obj =
|
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}."
|
data/lib/lapis_lazuli/version.rb
CHANGED
@@ -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
|
-
|
53
|
+
LapisLazuli::Browser.set_world(self)
|
54
|
+
|
54
55
|
# Create & return a new browser object
|
55
|
-
LapisLazuli::Browser.new(*
|
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
|
-
|
259
|
-
|
260
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
27
|
-
return !
|
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
|
-
|
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'
|
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,
|
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
|
-
|
58
|
-
|
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.
|
234
|
-
|
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.
|
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-
|
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
|