percy-selenium 1.0.2 → 1.1.0.pre.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21d5089e6a2652952602feef700b0e9aa1a1345ef6c8b464c2c85646e8a36679
4
- data.tar.gz: 2c9a17360becb96df488b8ae678172a89bad4ffbb596b05cf560e9a0a66fbcd6
3
+ metadata.gz: 84e0ed534fb2da64081b19d4c0b13e837e4c8800d3ed12e61c4e827e80c94e70
4
+ data.tar.gz: 8785427dd5e88ba487e55d14b3c0d634680737eee895971dd742e81efe7a3aca
5
5
  SHA512:
6
- metadata.gz: bca3d0f5ebce8d35f9bffc7727b8af1b42c5fb19c30b6a578a1ff7573b50e5d3613f26885227f3d906c8cd5b7677302af4cb31b20ae0651564a61fc7c5f784b4
7
- data.tar.gz: 0f6631b9d1ca037ff738c59ff5a8d3fd9abecde33a71b4bbae266748bd00b8269bb0cc9ed4c674bcc646fde837a8bcfa370b06d3b150165c6a0f5faf4f4a6789
6
+ metadata.gz: d6d71138d3d1267f8924858ee6f94a6a9969e3ce5b4e377d0f484194c5b595e99124f74a24e30761c611a730ee78f08b8542b51870c74b1c90d318935bdf2352
7
+ data.tar.gz: 9ece4fe3dd36379e07bc1024a3cc35aa005ab9fad3aedd42f26ed16e58b2bfee4445ce95adb71780a0cc2b3f38be2a519028d6c597ef7c16c911967809f15108
@@ -11,7 +11,7 @@ assignees: ''
11
11
 
12
12
  There are common setup gotchas that happen with Percy's SDKs, it would be worth reading
13
13
  the debugging document, which might already answer your question:
14
- https://docs.percy.io/docs/debugging-sdk
14
+ https://www.browserstack.com/docs/percy/integrate/percy-sdk-workflow#debugging-sdks
15
15
 
16
16
  ## Reach out to Percy support instead?
17
17
 
@@ -43,7 +43,7 @@ If necessary, describe the problem you have been experiencing in more detail.
43
43
  ## Debug logs
44
44
 
45
45
  If you are reporting a bug, _always_ include logs! [Give the "Debugging SDKs"
46
- document a quick read for how to gather logs](https://docs.percy.io/docs/debugging-sdks#debugging-sdks)
46
+ document a quick read for how to gather logs](https://www.browserstack.com/docs/percy/integrate/percy-sdk-workflow#debugging-sdks)
47
47
 
48
48
  Please do not trim or edit these logs, often times there are hints in the full
49
49
  logs that help debug what is going on.
data/README.md CHANGED
@@ -61,4 +61,4 @@ $ percy exec -- [ruby test command]
61
61
 
62
62
  - `driver` (**required**) - A selenium-webdriver driver instance
63
63
  - `name` (**required**) - The snapshot name; must be unique to each snapshot
64
- - `options` - [See per-snapshot configuration options](https://docs.percy.io/docs/cli-configuration#per-snapshot-configuration)
64
+ - `options` - [See per-snapshot configuration options](https://www.browserstack.com/docs/percy/take-percy-snapshots/overview#per-snapshot-configuration)
data/lib/percy.rb CHANGED
@@ -11,6 +11,7 @@ module Percy
11
11
  PERCY_DEBUG = ENV['PERCY_LOGLEVEL'] == 'debug'
12
12
  PERCY_SERVER_ADDRESS = ENV['PERCY_SERVER_ADDRESS'] || 'http://localhost:5338'
13
13
  LABEL = "[\u001b[35m" + (PERCY_DEBUG ? 'percy:ruby' : 'percy') + "\u001b[39m]"
14
+ RESONSIVE_CAPTURE_SLEEP_TIME = ENV['RESONSIVE_CAPTURE_SLEEP_TIME']
14
15
 
15
16
  # Take a DOM snapshot and post it to the snapshot endpoint
16
17
  def self.snapshot(driver, name, options = {})
@@ -18,7 +19,11 @@ module Percy
18
19
 
19
20
  begin
20
21
  driver.execute_script(fetch_percy_dom)
21
- dom_snapshot = driver.execute_script("return PercyDOM.serialize(#{options.to_json})")
22
+ dom_snapshot = if responsive_snapshot_capture?(options)
23
+ capture_responsive_dom(driver, options)
24
+ else
25
+ get_serialized_dom(driver, options)
26
+ end
22
27
 
23
28
  response = fetch('percy/snapshot',
24
29
  name: name,
@@ -36,9 +41,96 @@ module Percy
36
41
  body['data']
37
42
  rescue StandardError => e
38
43
  log("Could not take DOM snapshot '#{name}'")
44
+ log(e, 'debug')
45
+ end
46
+ end
47
+
48
+ def self.get_browser_instance(driver)
49
+ if driver.is_a?(Capybara::Session)
50
+ return driver.driver.browser.manage
51
+ end
52
+
53
+ driver.manage
54
+ end
55
+
56
+ def self.get_serialized_dom(driver, options)
57
+ dom_snapshot = driver.execute_script("return PercyDOM.serialize(#{options.to_json})")
58
+
59
+ dom_snapshot['cookies'] = get_browser_instance(driver).all_cookies
60
+ dom_snapshot
61
+ end
62
+
63
+ def self.get_widths_for_multi_dom(options)
64
+ user_passed_widths = options[:widths] || []
65
+
66
+ # Deep copy mobile widths otherwise it will get overridden
67
+ all_widths = @eligible_widths['mobile']&.dup || []
68
+ if user_passed_widths.any?
69
+ all_widths.concat(user_passed_widths)
70
+ else
71
+ all_widths.concat(@eligible_widths['config'] || [])
72
+ end
73
+
74
+ all_widths.uniq
75
+ end
76
+
77
+ def self.change_window_dimension_and_wait(driver, width, height, resize_count)
78
+ begin
79
+ if driver.capabilities.browser_name == 'chrome' && driver.respond_to?(:execute_cdp)
80
+ driver.execute_cdp('Emulation.setDeviceMetricsOverride', {
81
+ height: height, width: width, deviceScaleFactor: 1, mobile: false,
82
+ },)
83
+ else
84
+ get_browser_instance(driver).window.resize_to(width, height)
85
+ end
86
+ rescue StandardError => e
87
+ log("Resizing using cdp failed, falling back to driver for width #{width} #{e}", 'debug')
88
+ get_browser_instance(driver).window.resize_to(width, height)
89
+ end
90
+
91
+ begin
92
+ wait = Selenium::WebDriver::Wait.new(timeout: 1)
93
+ wait.until { driver.execute_script('return window.resizeCount') == resize_count }
94
+ rescue Selenium::WebDriver::Error::TimeoutError
95
+ log("Timed out waiting for window resize event for width #{width}", 'debug')
96
+ end
97
+ end
98
+
99
+ def self.capture_responsive_dom(driver, options)
100
+ widths = get_widths_for_multi_dom(options)
101
+ dom_snapshots = []
102
+ window_size = get_browser_instance(driver).window.size
103
+ current_width = window_size.width
104
+ current_height = window_size.height
105
+ last_window_width = current_width
106
+ resize_count = 0
107
+ driver.execute_script('PercyDOM.waitForResize()')
108
+
109
+ widths.each do |width|
110
+ if last_window_width != width
111
+ resize_count += 1
112
+ change_window_dimension_and_wait(driver, width, current_height, resize_count)
113
+ last_window_width = width
114
+ end
39
115
 
40
- if PERCY_DEBUG then log(e) end
116
+ sleep(RESONSIVE_CAPTURE_SLEEP_TIME.to_i) if defined?(RESONSIVE_CAPTURE_SLEEP_TIME)
117
+
118
+ dom_snapshot = get_serialized_dom(driver, options)
119
+ dom_snapshot['width'] = width
120
+ dom_snapshots << dom_snapshot
41
121
  end
122
+
123
+ change_window_dimension_and_wait(driver, current_width, current_height, resize_count + 1)
124
+ dom_snapshots
125
+ end
126
+
127
+ def self.responsive_snapshot_capture?(options)
128
+ # Don't run responsive snapshot capture when defer uploads is enabled
129
+ return false if @cli_config&.dig('percy', 'deferUploads')
130
+
131
+ options[:responsive_snapshot_capture] ||
132
+ options[:responsiveSnapshotCapture] ||
133
+ @cli_config&.dig('snapshot', 'responsiveSnapshotCapture')
42
134
  end
43
135
 
44
136
  # Determine if the Percy server is running, caching the result so it is only checked once
@@ -53,7 +145,7 @@ module Percy
53
145
  log('You may be using @percy/agent ' \
54
146
  'which is no longer supported by this SDK. ' \
55
147
  'Please uninstall @percy/agent and install @percy/cli instead. ' \
56
- 'https://docs.percy.io/docs/migrating-to-percy-cli')
148
+ 'https://www.browserstack.com/docs/percy/migration/migrate-to-cli')
57
149
  @percy_enabled = false
58
150
  return false
59
151
  end
@@ -64,12 +156,14 @@ module Percy
64
156
  return false
65
157
  end
66
158
 
159
+ response_body = JSON.parse(response.body)
160
+ @eligible_widths = response_body['widths']
161
+ @cli_config = response_body['config']
67
162
  @percy_enabled = true
68
163
  true
69
164
  rescue StandardError => e
70
165
  log('Percy is not running, disabling snapshots')
71
-
72
- if PERCY_DEBUG then log(e) end
166
+ log(e, 'debug')
73
167
  @percy_enabled = false
74
168
  false
75
169
  end
@@ -83,8 +177,19 @@ module Percy
83
177
  @percy_dom = response.body
84
178
  end
85
179
 
86
- def self.log(msg)
87
- puts "#{LABEL} #{msg}"
180
+ def self.log(msg, lvl = 'info')
181
+ msg = "#{LABEL} #{msg}"
182
+ begin
183
+ fetch('percy/log', {message: msg, level: lvl})
184
+ rescue StandardError => e
185
+ if PERCY_DEBUG
186
+ puts "Sending log to CLI Failed #{e}"
187
+ end
188
+ ensure
189
+ if lvl != 'debug' || PERCY_DEBUG
190
+ puts msg
191
+ end
192
+ end
88
193
  end
89
194
 
90
195
  # Make an HTTP request (GET,POST) using Ruby's Net::HTTP. If `data` is present,
@@ -112,5 +217,7 @@ module Percy
112
217
  def self._clear_cache!
113
218
  @percy_dom = nil
114
219
  @percy_enabled = nil
220
+ @eligible_widths = nil
221
+ @cli_config = nil
115
222
  end
116
223
  end
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Percy
2
- VERSION = '1.0.2'.freeze
2
+ VERSION = '1.1.0-beta.1'.freeze
3
3
  end
data/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
- "private": true,
3
- "scripts": {
4
- "test": "percy exec --testing -- bundle exec rspec"
5
- },
6
- "devDependencies": {
7
- "@percy/cli": "^1.28.0"
8
- }
2
+ "private": true,
3
+ "scripts": {
4
+ "test": "percy exec --testing -- bundle exec rspec"
5
+ },
6
+ "devDependencies": {
7
+ "@percy/cli": "^1.29.5-beta.0"
9
8
  }
10
-
9
+ }
@@ -1,7 +1,23 @@
1
1
  # rubocop:disable RSpec/MultipleDescribes
2
2
  RSpec.describe Percy, type: :feature do
3
+ dom_string = "<html><head><title>I am a page</title></head><body>Snapshot me\n</body></html>"
4
+ fetch_script_string = 'window.PercyDOM = {' \
5
+ 'serialize: () => {' \
6
+ 'return {' \
7
+ 'html: document.documentElement.outerHTML,' \
8
+ 'cookies: ""' \
9
+ '}' \
10
+ '},' \
11
+ 'waitForResize: () => {' \
12
+ 'if(!window.resizeCount) {' \
13
+ 'window.addEventListener(\'resize\', () => window.resizeCount++)' \
14
+ '}' \
15
+ 'window.resizeCount = 0;' \
16
+ '}};'
17
+
3
18
  before(:each) do
4
19
  WebMock.disable_net_connect!(allow: '127.0.0.1', disallow: 'localhost')
20
+ stub_request(:post, 'http://localhost:5338/percy/log').to_raise(StandardError)
5
21
  Percy._clear_cache!
6
22
  end
7
23
 
@@ -23,7 +39,7 @@ RSpec.describe Percy, type: :feature do
23
39
  "#{Percy::LABEL} You may be using @percy/agent which" \
24
40
  ' is no longer supported by this SDK. Please uninstall' \
25
41
  ' @percy/agent and install @percy/cli instead.' \
26
- " https://docs.percy.io/docs/migrating-to-percy-cli\n",
42
+ " https://www.browserstack.com/docs/percy/migration/migrate-to-cli\n",
27
43
  ).to_stdout
28
44
  end
29
45
 
@@ -59,12 +75,13 @@ RSpec.describe Percy, type: :feature do
59
75
 
60
76
  it 'logs an error when sending a snapshot fails' do
61
77
  stub_request(:get, "#{Percy::PERCY_SERVER_ADDRESS}/percy/healthcheck")
62
- .to_return(status: 200, body: '', headers: {'x-percy-core-version': '1.0.0'})
78
+ .to_return(status: 200, body: '{"success": "true" }',
79
+ headers: {'x-percy-core-version': '1.0.0'},)
63
80
 
64
81
  stub_request(:get, "#{Percy::PERCY_SERVER_ADDRESS}/percy/dom.js")
65
82
  .to_return(
66
83
  status: 200,
67
- body: 'window.PercyDOM = { serialize: () => document.documentElement.outerHTML };',
84
+ body: fetch_script_string,
68
85
  headers: {},
69
86
  )
70
87
 
@@ -77,12 +94,14 @@ RSpec.describe Percy, type: :feature do
77
94
 
78
95
  it 'sends snapshots to the local server' do
79
96
  stub_request(:get, "#{Percy::PERCY_SERVER_ADDRESS}/percy/healthcheck")
80
- .to_return(status: 200, body: '', headers: {'x-percy-core-version': '1.0.0'})
97
+ .to_return(status: 200, body: '{"success": "true" }', headers: {
98
+ 'x-percy-core-version': '1.0.0',
99
+ },)
81
100
 
82
101
  stub_request(:get, "#{Percy::PERCY_SERVER_ADDRESS}/percy/dom.js")
83
102
  .to_return(
84
103
  status: 200,
85
- body: 'window.PercyDOM = { serialize: () => document.documentElement.outerHTML };',
104
+ body: fetch_script_string,
86
105
  headers: {},
87
106
  )
88
107
 
@@ -98,7 +117,7 @@ RSpec.describe Percy, type: :feature do
98
117
  name: 'Name',
99
118
  url: 'http://127.0.0.1:3003/index.html',
100
119
  dom_snapshot:
101
- "<html><head><title>I am a page</title></head><body>Snapshot me\n</body></html>",
120
+ {"cookies": [], "html": dom_string},
102
121
  client_info: "percy-selenium-ruby/#{Percy::VERSION}",
103
122
  environment_info: "selenium/#{Selenium::WebDriver::VERSION} ruby/#{RUBY_VERSION}",
104
123
  widths: [944],
@@ -108,14 +127,106 @@ RSpec.describe Percy, type: :feature do
108
127
  expect(data).to eq(nil)
109
128
  end
110
129
 
130
+ it 'sends multiple dom snapshots to the local server' do
131
+ stub_request(:get, "#{Percy::PERCY_SERVER_ADDRESS}/percy/healthcheck").to_return(
132
+ status: 200,
133
+ body: '{"success": "true", "widths": { "mobile": [390], "config": [765, 1280]} }',
134
+ headers: {
135
+ 'x-percy-core-version': '1.0.0',
136
+ 'config': {}, 'widths': {'mobile': [375], 'config': [765, 1280]},
137
+ },
138
+ )
139
+
140
+ stub_request(:get, "#{Percy::PERCY_SERVER_ADDRESS}/percy/dom.js")
141
+ .to_return(
142
+ status: 200,
143
+ body: fetch_script_string,
144
+ headers: {},
145
+ )
146
+
147
+ stub_request(:post, 'http://localhost:5338/percy/snapshot')
148
+ .to_return(status: 200, body: '{"success": "true" }', headers: {})
149
+
150
+ visit 'index.html'
151
+ data = Percy.snapshot(page, 'Name', {responsive_snapshot_capture: true})
152
+
153
+ expect(WebMock).to have_requested(:post, "#{Percy::PERCY_SERVER_ADDRESS}/percy/snapshot")
154
+ .with(
155
+ body: {
156
+ name: 'Name',
157
+ url: 'http://127.0.0.1:3003/index.html',
158
+ dom_snapshot: [
159
+ {'cookies': [], 'html': dom_string, 'width': 390},
160
+ {'cookies': [], 'html': dom_string, 'width': 765},
161
+ {'cookies': [], 'html': dom_string, 'width': 1280},
162
+ ],
163
+ client_info: "percy-selenium-ruby/#{Percy::VERSION}",
164
+ environment_info: "selenium/#{Selenium::WebDriver::VERSION} ruby/#{RUBY_VERSION}",
165
+ responsive_snapshot_capture: true,
166
+ }.to_json,
167
+ ).once
168
+
169
+ expect(data).to eq(nil)
170
+ end
171
+
172
+ it 'sends multiple dom snapshots to the local server using selenium' do
173
+ stub_request(:get, "#{Percy::PERCY_SERVER_ADDRESS}/percy/healthcheck").to_return(
174
+ status: 200,
175
+ body: '{"success": "true", "widths": { "mobile": [390], "config": [765, 1280]} }',
176
+ headers: {
177
+ 'x-percy-core-version': '1.0.0',
178
+ 'config': {}, 'widths': {'mobile': [375], 'config': [765, 1280]},
179
+ },
180
+ )
181
+
182
+ stub_request(:get, "#{Percy::PERCY_SERVER_ADDRESS}/percy/dom.js")
183
+ .to_return(
184
+ status: 200,
185
+ body: fetch_script_string,
186
+ headers: {},
187
+ )
188
+
189
+ stub_request(:post, 'http://localhost:5338/percy/snapshot')
190
+ .to_return(status: 200, body: '{"success": "true" }', headers: {})
191
+
192
+ driver = Selenium::WebDriver.for :firefox
193
+
194
+ driver.navigate.to 'http://localhost:5338/test/snapshot'
195
+ driver.manage.add_cookie({name: 'cookie-name', value: 'cookie-value'})
196
+ data = Percy.snapshot(driver, 'Name', {responsive_snapshot_capture: true})
197
+
198
+ expected_cookie = {name: 'cookie-name', value: 'cookie-value', path: '/',
199
+ domain: 'localhost', "expires": nil, "same_site": 'Lax',
200
+ "http_only": false, "secure": false,}
201
+ expected_dom = '<html><head></head><body><p>Snapshot Me!</p></body></html>'
202
+ expect(WebMock).to have_requested(:post, "#{Percy::PERCY_SERVER_ADDRESS}/percy/snapshot")
203
+ .with(
204
+ body: {
205
+ name: 'Name',
206
+ url: 'http://localhost:5338/test/snapshot',
207
+ dom_snapshot: [
208
+ {'cookies': [expected_cookie], 'html': expected_dom, 'width': 390},
209
+ {'cookies': [expected_cookie], 'html': expected_dom, 'width': 765},
210
+ {'cookies': [expected_cookie], 'html': expected_dom, 'width': 1280},
211
+ ],
212
+ client_info: "percy-selenium-ruby/#{Percy::VERSION}",
213
+ environment_info: "selenium/#{Selenium::WebDriver::VERSION} ruby/#{RUBY_VERSION}",
214
+ responsive_snapshot_capture: true,
215
+ }.to_json,
216
+ ).once
217
+
218
+ expect(data).to eq(nil)
219
+ end
220
+
111
221
  it 'sends snapshots for sync' do
112
222
  stub_request(:get, "#{Percy::PERCY_SERVER_ADDRESS}/percy/healthcheck")
113
- .to_return(status: 200, body: '', headers: {'x-percy-core-version': '1.0.0'})
223
+ .to_return(status: 200, body: '{"success": "true" }',
224
+ headers: {'x-percy-core-version': '1.0.0'},)
114
225
 
115
226
  stub_request(:get, "#{Percy::PERCY_SERVER_ADDRESS}/percy/dom.js")
116
227
  .to_return(
117
228
  status: 200,
118
- body: 'window.PercyDOM = { serialize: () => document.documentElement.outerHTML };',
229
+ body: fetch_script_string,
119
230
  headers: {},
120
231
  )
121
232
 
@@ -131,10 +242,10 @@ RSpec.describe Percy, type: :feature do
131
242
  name: 'Name',
132
243
  url: 'http://127.0.0.1:3003/index.html',
133
244
  dom_snapshot:
134
- "<html><head><title>I am a page</title></head><body>Snapshot me\n</body></html>",
245
+ {'cookies' => [], 'html' => dom_string},
135
246
  client_info: "percy-selenium-ruby/#{Percy::VERSION}",
136
247
  environment_info: "selenium/#{Selenium::WebDriver::VERSION} ruby/#{RUBY_VERSION}",
137
- widths: [944],
248
+ sync: true,
138
249
  }.to_json,
139
250
  ).once
140
251
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: percy-selenium
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0.pre.beta.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Perceptual Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-06 00:00:00.000000000 Z
11
+ date: 2024-10-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: selenium-webdriver
@@ -145,11 +145,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
145
145
  version: 2.3.0
146
146
  required_rubygems_version: !ruby/object:Gem::Requirement
147
147
  requirements:
148
- - - ">="
148
+ - - ">"
149
149
  - !ruby/object:Gem::Version
150
- version: '0'
150
+ version: 1.3.1
151
151
  requirements: []
152
- rubygems_version: 3.5.3
152
+ rubygems_version: 3.4.19
153
153
  signing_key:
154
154
  specification_version: 4
155
155
  summary: Percy visual testing for Ruby Selenium