percy-selenium 1.1.1 → 1.1.2.pre.beta.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1182f98302b6e6a50148711618e377ca36e62ad06d8bfc8a6f3da3b7c34a8256
4
- data.tar.gz: b8125cece8b3929bd8416bc15fcaac70fbf9b9f8832207ed47d95f88a167c013
3
+ metadata.gz: aa2f41ca77fb6671ac305eb3ff0a99f61cef87b290b885ed8c16a462a865bbb9
4
+ data.tar.gz: f45b3651099a2f84d098d4eb48db83b3685646ad3902b85a10b5f865a3b9fd48
5
5
  SHA512:
6
- metadata.gz: 3a5ffdebefdc4844c60e599bfda70a2375602970de16dbbf9dc5ca5be2d2b52a9cb5f9569215430431130345a43815a934037dd93efa934f4a12f67f9db76627
7
- data.tar.gz: 4937624feab8efc33c64d4825204dc4a886dafca14b697be5343826260fb14f49eaad34dd74467a427fc38b7ebb40219b23805c3c43020af9ce844b3c85422d6
6
+ metadata.gz: 821f02d3e049227bb1e294402cfb37ce097c21c775d3c8870871cc650db787784696ef55e0df593d817123a86664e8e868a9171526168a9d3d72835208d08c56
7
+ data.tar.gz: 7e54763dd644d9a65489d2d9c043d0e82bee79637467b812e2ff9f80267d943c0fcf722095ca4c94b35a24258158e8af96c713edb6c27bf14aaee64dd3a86fc0
data/Gemfile CHANGED
@@ -7,7 +7,7 @@ gem "guard-rspec", require: false
7
7
 
8
8
  group :test, :development do
9
9
  gem "webmock"
10
- gem "puma", '~> 5.6'
10
+ gem "puma", '~> 6'
11
11
  gem "rackup"
12
12
  gem "pry"
13
13
  gem "simplecov", require: false
data/lib/cache.rb ADDED
@@ -0,0 +1,49 @@
1
+ class Cache
2
+ CACHE = {} # rubocop:disable Style/MutableConstant
3
+ CACHE_TIMEOUT = 5 * 60 # 300 seconds
4
+ TIMEOUT_KEY = 'last_access_time'.freeze
5
+ MUTEX = Mutex.new
6
+
7
+ # Caching Keys
8
+ CAPABILITIES = 'capabilities'.freeze
9
+ COMMAND_EXECUTOR_URL = 'command_executor_url'.freeze
10
+
11
+ def self.check_types(session_id, property)
12
+ raise TypeError, 'Argument session_id should be string' unless session_id.is_a?(String)
13
+ raise TypeError, 'Argument property should be string' unless property.is_a?(String)
14
+ end
15
+
16
+ def self.set_cache(session_id, property, value)
17
+ check_types(session_id, property)
18
+ MUTEX.synchronize do
19
+ session = CACHE[session_id] || {}
20
+ session[TIMEOUT_KEY] = Time.now.to_f
21
+ session[property] = value
22
+ CACHE[session_id] = session
23
+ end
24
+ end
25
+
26
+ def self.get_cache(session_id, property)
27
+ check_types(session_id, property)
28
+ MUTEX.synchronize do
29
+ cleanup_cache
30
+ session = CACHE[session_id] || {}
31
+ session[property]
32
+ end
33
+ end
34
+
35
+ def self.clear_cache!
36
+ MUTEX.synchronize do
37
+ CACHE.clear
38
+ end
39
+ end
40
+
41
+ def self.cleanup_cache
42
+ now = Time.now.to_f
43
+ CACHE.delete_if do |_, session|
44
+ timestamp = session[TIMEOUT_KEY]
45
+ timestamp && (now - timestamp >= CACHE_TIMEOUT)
46
+ end
47
+ end
48
+ private_class_method :cleanup_cache
49
+ end
@@ -0,0 +1,57 @@
1
+ require_relative 'cache'
2
+
3
+ class DriverMetaData
4
+ def initialize(driver)
5
+ @driver = driver
6
+ end
7
+
8
+ def session_id
9
+ @driver.session_id
10
+ end
11
+
12
+ def command_executor_url
13
+ cached = Cache.get_cache(session_id, Cache::COMMAND_EXECUTOR_URL)
14
+ return cached unless cached.nil?
15
+
16
+ url = nil
17
+ begin
18
+ raw = @driver.send(:bridge).http.send(:server_url)
19
+ url = raw.to_s unless raw.nil?
20
+ rescue StandardError => e
21
+ if defined?(Percy)
22
+ Percy.log("Could not get command_executor_url via bridge.http.server_url: #{e}", 'debug')
23
+ end
24
+ end
25
+
26
+ if url.nil? || url.empty?
27
+ begin
28
+ raw = @driver.send(:bridge).http.instance_variable_get(:@server_url)
29
+ url = raw.to_s unless raw.nil?
30
+ rescue StandardError => e
31
+ Percy.log("Could not get @server_url instance variable: #{e}", 'debug') if defined?(Percy)
32
+ end
33
+ end
34
+
35
+ url ||= ''
36
+ Cache.set_cache(session_id, Cache::COMMAND_EXECUTOR_URL, url)
37
+ url
38
+ end
39
+
40
+ def capabilities
41
+ cached = Cache.get_cache(session_id, Cache::CAPABILITIES)
42
+ return cached unless cached.nil?
43
+
44
+ caps = begin
45
+ @driver.capabilities.as_json
46
+ rescue StandardError
47
+ begin
48
+ @driver.capabilities.to_h
49
+ rescue StandardError
50
+ {}
51
+ end
52
+ end
53
+
54
+ Cache.set_cache(session_id, Cache::CAPABILITIES, caps)
55
+ caps
56
+ end
57
+ end
data/lib/percy.rb CHANGED
@@ -3,15 +3,32 @@ require 'json'
3
3
  require 'version'
4
4
  require 'net/http'
5
5
  require 'selenium-webdriver'
6
+ require_relative 'driver_metadata'
6
7
 
7
8
  module Percy
8
9
  CLIENT_INFO = "percy-selenium-ruby/#{VERSION}".freeze
9
10
  ENV_INFO = "selenium/#{Selenium::WebDriver::VERSION} ruby/#{RUBY_VERSION}".freeze
10
11
 
12
+ SESSION_TYPE_AUTOMATE = 'automate'.freeze
13
+ SESSION_TYPE_WEB = 'web'.freeze
14
+
11
15
  PERCY_DEBUG = ENV['PERCY_LOGLEVEL'] == 'debug'
12
16
  PERCY_SERVER_ADDRESS = ENV['PERCY_SERVER_ADDRESS'] || 'http://localhost:5338'
13
17
  LABEL = "[\u001b[35m" + (PERCY_DEBUG ? 'percy:ruby' : 'percy') + "\u001b[39m]"
14
- RESONSIVE_CAPTURE_SLEEP_TIME = ENV['RESONSIVE_CAPTURE_SLEEP_TIME']
18
+ RESPONSIVE_CAPTURE_SLEEP_TIME = ENV['RESPONSIVE_CAPTURE_SLEEP_TIME'] ||
19
+ ENV['RESONSIVE_CAPTURE_SLEEP_TIME']
20
+
21
+ def self.responsive_capture_reload_page?
22
+ val = ENV['PERCY_RESPONSIVE_CAPTURE_RELOAD_PAGE'] ||
23
+ ENV['PERCY_RESONSIVE_CAPTURE_RELOAD_PAGE'] || 'false'
24
+ val.casecmp('true') == 0
25
+ end
26
+
27
+ def self.responsive_capture_min_height?
28
+ val = ENV['PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT'] ||
29
+ ENV['PERCY_RESONSIVE_CAPTURE_MIN_HEIGHT'] || 'false'
30
+ val.casecmp('true') == 0
31
+ end
15
32
 
16
33
  def self.create_region(
17
34
  bounding_box: nil, element_xpath: nil, element_css: nil, padding: nil,
@@ -49,16 +66,23 @@ module Percy
49
66
  region
50
67
  end
51
68
 
52
- # Take a DOM snapshot and post it to the snapshot endpoint
53
69
  def self.snapshot(driver, name, options = {})
54
70
  return unless percy_enabled?
55
71
 
72
+ if @session_type == SESSION_TYPE_AUTOMATE
73
+ raise StandardError, 'Invalid function call - percy_snapshot(). ' \
74
+ 'Please use percy_screenshot() function while using Percy with Automate. ' \
75
+ 'For more information on usage of percy_screenshot(), ' \
76
+ 'refer https://www.browserstack.com/docs/percy/integrate/functional-and-visual'
77
+ end
78
+
56
79
  begin
57
- driver.execute_script(fetch_percy_dom)
80
+ percy_dom_script = fetch_percy_dom
81
+ driver.execute_script(percy_dom_script)
58
82
  dom_snapshot = if responsive_snapshot_capture?(options)
59
- capture_responsive_dom(driver, options)
83
+ capture_responsive_dom(driver, options, percy_dom_script: percy_dom_script)
60
84
  else
61
- get_serialized_dom(driver, options)
85
+ get_serialized_dom(driver, options, percy_dom_script: percy_dom_script)
62
86
  end
63
87
 
64
88
  response = fetch('percy/snapshot',
@@ -69,11 +93,11 @@ module Percy
69
93
  environment_info: ENV_INFO,
70
94
  **options,)
71
95
 
72
- unless response.body.to_json['success']
73
- raise StandardError, data['error']
96
+ body = JSON.parse(response.body)
97
+ unless body['success']
98
+ raise StandardError, body['error']
74
99
  end
75
100
 
76
- body = JSON.parse(response.body)
77
101
  body['data']
78
102
  rescue StandardError => e
79
103
  log("Could not take DOM snapshot '#{name}'")
@@ -82,7 +106,6 @@ module Percy
82
106
  end
83
107
 
84
108
  def self.get_browser_instance(driver)
85
- # this means it is a capybara session
86
109
  if driver.respond_to?(:driver) && driver.driver.respond_to?(:browser)
87
110
  return driver.driver.browser.manage
88
111
  end
@@ -90,28 +113,131 @@ module Percy
90
113
  driver.manage
91
114
  end
92
115
 
93
- def self.get_serialized_dom(driver, options)
116
+ def self.get_serialized_dom(driver, options, percy_dom_script: nil)
94
117
  dom_snapshot = driver.execute_script("return PercyDOM.serialize(#{options.to_json})")
118
+ begin
119
+ page_origin = get_origin(driver.current_url)
120
+ iframes = percy_dom_script ? driver.find_elements(:tag_name, 'iframe') : []
121
+ if iframes.any?
122
+ processed_frames = []
123
+ iframes.each do |frame|
124
+ frame_src = frame.attribute('src')
125
+ next if unsupported_iframe_src?(frame_src)
126
+
127
+ begin
128
+ frame_origin = get_origin(URI.join(driver.current_url, frame_src).to_s)
129
+ rescue StandardError => e
130
+ log("Skipping iframe \"#{frame_src}\": #{e}", 'debug')
131
+ next
132
+ end
133
+
134
+ next if frame_origin == page_origin
135
+
136
+ result = process_frame(driver, frame, options, percy_dom_script)
137
+ processed_frames << result if result
138
+ end
139
+ dom_snapshot['corsIframes'] = processed_frames if processed_frames.any?
140
+ end
141
+ rescue StandardError => e
142
+ log("Failed to process cross-origin iframes: #{e}", 'debug')
143
+ begin
144
+ driver.switch_to.default_content
145
+ rescue StandardError
146
+ nil
147
+ end
148
+ end
95
149
 
96
150
  dom_snapshot['cookies'] = get_browser_instance(driver).all_cookies
97
151
  dom_snapshot
98
152
  end
99
153
 
100
- def self.get_widths_for_multi_dom(options)
101
- user_passed_widths = options[:widths] || []
154
+ def self.unsupported_iframe_src?(src)
155
+ src.nil? || src.empty? || src == 'about:blank' ||
156
+ src.start_with?('javascript:') || src.start_with?('data:') || src.start_with?('vbscript:')
157
+ end
102
158
 
103
- # Deep copy mobile widths otherwise it will get overridden
104
- all_widths = @eligible_widths['mobile']&.dup || []
105
- if user_passed_widths.any?
106
- all_widths.concat(user_passed_widths)
107
- else
108
- all_widths.concat(@eligible_widths['config'] || [])
159
+ def self.get_origin(url)
160
+ uri = URI.parse(url)
161
+ raise URI::InvalidURIError, "no host in #{url}" if uri.host.nil?
162
+
163
+ netloc = uri.host.to_s
164
+ default_ports = {'http' => 80, 'https' => 443}
165
+ netloc += ":#{uri.port}" if uri.port && uri.port != default_ports[uri.scheme]
166
+ "#{uri.scheme}://#{netloc}"
167
+ end
168
+
169
+ def self.process_frame(driver, frame_element, options, percy_dom_script)
170
+ frame_url = frame_element.attribute('src') || 'unknown-src'
171
+ iframe_snapshot = nil
172
+
173
+ begin
174
+ driver.switch_to.frame(frame_element)
175
+ begin
176
+ driver.execute_script(percy_dom_script)
177
+ iframe_options = options.merge('enableJavaScript' => true)
178
+ iframe_snapshot =
179
+ driver.execute_script("return PercyDOM.serialize(#{iframe_options.to_json})")
180
+ rescue StandardError => e
181
+ log("Failed to process cross-origin frame #{frame_url}: #{e}", 'debug')
182
+ ensure
183
+ begin
184
+ driver.switch_to.default_content
185
+ rescue StandardError
186
+ begin
187
+ driver.switch_to.parent_frame
188
+ rescue StandardError
189
+ nil
190
+ end
191
+ end
192
+ end
193
+ rescue StandardError => e
194
+ log("Failed to switch to frame #{frame_url}: #{e}", 'debug')
195
+ begin
196
+ driver.switch_to.default_content
197
+ rescue StandardError
198
+ nil
199
+ end
200
+ return nil
201
+ end
202
+
203
+ return nil if iframe_snapshot.nil?
204
+
205
+ percy_element_id = frame_element.attribute('data-percy-element-id')
206
+ unless percy_element_id
207
+ log("Skipping frame #{frame_url}: no matching percyElementId found", 'debug')
208
+ return nil
109
209
  end
110
210
 
111
- all_widths.uniq
211
+ {
212
+ 'iframeData' => {'percyElementId' => percy_element_id},
213
+ 'iframeSnapshot' => iframe_snapshot,
214
+ 'frameUrl' => frame_url,
215
+ }
216
+ end
217
+
218
+ def self.get_responsive_widths(widths = [])
219
+ begin
220
+ widths_list = widths.is_a?(Array) ? widths : []
221
+ query_param = widths_list.any? ? "?widths=#{widths_list.join(',')}" : ''
222
+ response = fetch("percy/widths-config#{query_param}")
223
+ data = JSON.parse(response.body)
224
+ widths_data = data['widths']
225
+ unless widths_data.is_a?(Array)
226
+ msg = 'Update Percy CLI to the latest version to use responsiveSnapshotCapture'
227
+ raise StandardError, msg
228
+ end
229
+
230
+ widths_data
231
+ rescue StandardError => e
232
+ log("Failed to get responsive widths: #{e}.", 'debug')
233
+ raise StandardError, 'Update Percy CLI to the latest version to use ' \
234
+ 'responsiveSnapshotCapture'
235
+ end
112
236
  end
113
237
 
114
238
  def self.change_window_dimension_and_wait(driver, width, height, resize_count)
239
+ log("Attempting to resize window to #{width}x#{height}", 'debug')
240
+
115
241
  begin
116
242
  if driver.capabilities.browser_name == 'chrome' && driver.respond_to?(:execute_cdp)
117
243
  driver.execute_cdp('Emulation.setDeviceMetricsOverride', {
@@ -119,50 +245,89 @@ module Percy
119
245
  },)
120
246
  else
121
247
  get_browser_instance(driver).window.resize_to(width, height)
248
+ driver.execute_script("window.dispatchEvent(new Event('resize'));")
122
249
  end
123
250
  rescue StandardError => e
124
251
  log("Resizing using cdp failed, falling back to driver for width #{width} #{e}", 'debug')
125
252
  get_browser_instance(driver).window.resize_to(width, height)
253
+ driver.execute_script("window.dispatchEvent(new Event('resize'));")
126
254
  end
127
255
 
128
256
  begin
129
257
  wait = Selenium::WebDriver::Wait.new(timeout: 1)
130
258
  wait.until { driver.execute_script('return window.resizeCount') == resize_count }
259
+ actual_size = driver.execute_script('return { w: window.innerWidth, h: window.innerHeight }')
260
+ log("Resize successful. New Viewport Size: #{actual_size['w']}x#{actual_size['h']}", 'debug')
131
261
  rescue Selenium::WebDriver::Error::TimeoutError
132
262
  log("Timed out waiting for window resize event for width #{width}", 'debug')
133
263
  end
134
264
  end
135
265
 
136
- def self.capture_responsive_dom(driver, options)
137
- widths = get_widths_for_multi_dom(options)
266
+ def self.capture_responsive_dom(driver, options, percy_dom_script: nil)
267
+ widths = get_responsive_widths(options[:widths] || [])
138
268
  dom_snapshots = []
139
269
  window_size = get_browser_instance(driver).window.size
140
270
  current_width = window_size.width
141
271
  current_height = window_size.height
142
272
  last_window_width = current_width
273
+ last_window_height = current_height
143
274
  resize_count = 0
144
275
  driver.execute_script('PercyDOM.waitForResize()')
276
+ target_height = current_height
145
277
 
146
- widths.each do |width|
147
- if last_window_width != width
148
- resize_count += 1
149
- change_window_dimension_and_wait(driver, width, current_height, resize_count)
150
- last_window_width = width
278
+ if responsive_capture_min_height?
279
+ min = options[:minHeight] || @cli_config&.dig('snapshot', 'minHeight')
280
+ if min
281
+ target_height = min
282
+ else
283
+ log('PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT is enabled but no minHeight value ' \
284
+ 'was provided in options or CLI config; using current window height', 'debug',)
151
285
  end
286
+ end
152
287
 
153
- sleep(RESONSIVE_CAPTURE_SLEEP_TIME.to_i) if defined?(RESONSIVE_CAPTURE_SLEEP_TIME)
154
-
155
- dom_snapshot = get_serialized_dom(driver, options)
156
- dom_snapshot['width'] = width
157
- dom_snapshots << dom_snapshot
288
+ begin
289
+ widths.each do |width_dict|
290
+ width = width_dict['width']
291
+ height = width_dict['height'] || target_height
292
+
293
+ if last_window_width != width || last_window_height != height
294
+ resize_count += 1
295
+ change_window_dimension_and_wait(driver, width, height, resize_count)
296
+ last_window_width = width
297
+ last_window_height = height
298
+ end
299
+
300
+ if responsive_capture_reload_page?
301
+ log("Reloading page for width: #{width}", 'debug')
302
+ begin
303
+ driver.navigate.refresh
304
+ rescue StandardError
305
+ begin
306
+ driver.driver.browser.navigate.refresh
307
+ rescue StandardError => e
308
+ log("Failed to refresh page: #{e}", 'debug')
309
+ end
310
+ end
311
+ percy_dom_script = fetch_percy_dom
312
+ driver.execute_script(percy_dom_script)
313
+ driver.execute_script('PercyDOM.waitForResize()')
314
+ resize_count = 0
315
+ end
316
+
317
+ sleep(RESPONSIVE_CAPTURE_SLEEP_TIME.to_i) if RESPONSIVE_CAPTURE_SLEEP_TIME
318
+
319
+ dom_snapshot = get_serialized_dom(driver, options, percy_dom_script: percy_dom_script)
320
+ dom_snapshot['width'] = width
321
+ dom_snapshots << dom_snapshot
322
+ end
323
+ ensure
324
+ change_window_dimension_and_wait(driver, current_width, current_height, resize_count + 1)
158
325
  end
159
326
 
160
- change_window_dimension_and_wait(driver, current_width, current_height, resize_count + 1)
161
327
  dom_snapshots
162
328
  end
163
329
 
164
330
  def self.responsive_snapshot_capture?(options)
165
- # Don't run responsive snapshot capture when defer uploads is enabled
166
331
  return false if @cli_config&.dig('percy', 'deferUploads')
167
332
 
168
333
  options[:responsive_snapshot_capture] ||
@@ -170,7 +335,6 @@ module Percy
170
335
  @cli_config&.dig('snapshot', 'responsiveSnapshotCapture')
171
336
  end
172
337
 
173
- # Determine if the Percy server is running, caching the result so it is only checked once
174
338
  def self.percy_enabled?
175
339
  return @percy_enabled unless @percy_enabled.nil?
176
340
 
@@ -194,8 +358,8 @@ module Percy
194
358
  end
195
359
 
196
360
  response_body = JSON.parse(response.body)
197
- @eligible_widths = response_body['widths']
198
361
  @cli_config = response_body['config']
362
+ @session_type = response_body['type']
199
363
  @percy_enabled = true
200
364
  true
201
365
  rescue StandardError => e
@@ -206,7 +370,6 @@ module Percy
206
370
  end
207
371
  end
208
372
 
209
- # Fetch the @percy/dom script, caching the result so it is only fetched once
210
373
  def self.fetch_percy_dom
211
374
  return @percy_dom unless @percy_dom.nil?
212
375
 
@@ -229,8 +392,6 @@ module Percy
229
392
  end
230
393
  end
231
394
 
232
- # Make an HTTP request (GET,POST) using Ruby's Net::HTTP. If `data` is present,
233
- # `fetch` will POST as JSON.
234
395
  def self.fetch(url, data = nil)
235
396
  uri = URI("#{PERCY_SERVER_ADDRESS}/#{url}")
236
397
 
@@ -251,10 +412,71 @@ module Percy
251
412
  response
252
413
  end
253
414
 
415
+ def self.percy_screenshot(driver, name, options = {})
416
+ return unless percy_enabled?
417
+
418
+ unless @session_type == SESSION_TYPE_AUTOMATE
419
+ raise StandardError, 'Invalid function call - percy_screenshot(). ' \
420
+ 'Please use percy_snapshot() function for taking screenshot. ' \
421
+ 'percy_screenshot() should be used only while using Percy with Automate. ' \
422
+ 'For more information on usage of percy_snapshot(), ' \
423
+ 'refer doc for your language https://www.browserstack.com/docs/percy/integrate/overview'
424
+ end
425
+
426
+ begin
427
+ options = options.dup
428
+ metadata = get_driver_metadata(driver)
429
+
430
+ if options.key?(:ignoreRegionSeleniumElements)
431
+ options[:ignore_region_selenium_elements] = options.delete(:ignoreRegionSeleniumElements)
432
+ end
433
+ if options.key?(:considerRegionSeleniumElements)
434
+ options[:consider_region_selenium_elements] =
435
+ options.delete(:considerRegionSeleniumElements)
436
+ end
437
+
438
+ ignore_region_elements =
439
+ get_element_ids(options.delete(:ignore_region_selenium_elements) || [])
440
+ consider_region_elements =
441
+ get_element_ids(options.delete(:consider_region_selenium_elements) || [])
442
+
443
+ options[:ignore_region_elements] = ignore_region_elements
444
+ options[:consider_region_elements] = consider_region_elements
445
+
446
+ response = fetch('percy/automateScreenshot',
447
+ client_info: CLIENT_INFO,
448
+ environment_info: ENV_INFO,
449
+ sessionId: metadata.session_id,
450
+ commandExecutorUrl: metadata.command_executor_url,
451
+ capabilities: metadata.capabilities,
452
+ snapshotName: name,
453
+ options: options,)
454
+
455
+ body = JSON.parse(response.body)
456
+ unless body['success']
457
+ raise StandardError, body['error']
458
+ end
459
+
460
+ body['data']
461
+ rescue StandardError => e
462
+ log("Could not take Screenshot '#{name}'")
463
+ log(e, 'debug')
464
+ end
465
+ end
466
+
467
+ def self.get_driver_metadata(driver)
468
+ DriverMetaData.new(driver)
469
+ end
470
+
471
+ def self.get_element_ids(elements)
472
+ elements.map(&:id)
473
+ end
474
+
254
475
  def self._clear_cache!
255
476
  @percy_dom = nil
256
477
  @percy_enabled = nil
257
- @eligible_widths = nil
258
478
  @cli_config = nil
479
+ @session_type = nil
480
+ Cache.clear_cache!
259
481
  end
260
482
  end
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Percy
2
- VERSION = '1.1.1'.freeze
2
+ VERSION = '1.1.2-beta.0'.freeze
3
3
  end
data/package.json CHANGED
@@ -4,6 +4,6 @@
4
4
  "test": "percy exec --testing -- bundle exec rspec"
5
5
  },
6
6
  "devDependencies": {
7
- "@percy/cli": "1.30.9"
7
+ "@percy/cli": "1.31.10"
8
8
  }
9
9
  }