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

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
  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