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

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: 93874c7ca2563a5bcc985ab857035658b57dade066310c6a059f6b0c545836bf
4
+ data.tar.gz: fb3ee7e5c65d28c29da169febd33eba48d4f4b52987aa1af4709ebb2479b50d6
5
5
  SHA512:
6
- metadata.gz: bca3d0f5ebce8d35f9bffc7727b8af1b42c5fb19c30b6a578a1ff7573b50e5d3613f26885227f3d906c8cd5b7677302af4cb31b20ae0651564a61fc7c5f784b4
7
- data.tar.gz: 0f6631b9d1ca037ff738c59ff5a8d3fd9abecde33a71b4bbae266748bd00b8269bb0cc9ed4c674bcc646fde837a8bcfa370b06d3b150165c6a0f5faf4f4a6789
6
+ metadata.gz: 8c3181f97b40acc391064ab3795a5708fb332f6725dbfdfc67dc84f836658cc151ad12550fd27acccdc7a57fc03109780ab8cd7742ae3975cecae06e1f98816c
7
+ data.tar.gz: c942d7b712fe9af71edc61b07f883a1978546377cbbe52b7d73d81caad4b286b5af61fbc3579fdfb89b26487953259fe4dffd2a9cc08ee7bda1007ca5e1f98ce
@@ -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,97 @@ 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
+ # this means it is a capybara session
50
+ if driver.respond_to?(:driver) && driver.driver.respond_to?(:browser)
51
+ return driver.driver.browser.manage
52
+ end
53
+
54
+ driver.manage
55
+ end
56
+
57
+ def self.get_serialized_dom(driver, options)
58
+ dom_snapshot = driver.execute_script("return PercyDOM.serialize(#{options.to_json})")
59
+
60
+ dom_snapshot['cookies'] = get_browser_instance(driver).all_cookies
61
+ dom_snapshot
62
+ end
63
+
64
+ def self.get_widths_for_multi_dom(options)
65
+ user_passed_widths = options[:widths] || []
66
+
67
+ # Deep copy mobile widths otherwise it will get overridden
68
+ all_widths = @eligible_widths['mobile']&.dup || []
69
+ if user_passed_widths.any?
70
+ all_widths.concat(user_passed_widths)
71
+ else
72
+ all_widths.concat(@eligible_widths['config'] || [])
73
+ end
74
+
75
+ all_widths.uniq
76
+ end
77
+
78
+ def self.change_window_dimension_and_wait(driver, width, height, resize_count)
79
+ begin
80
+ if driver.capabilities.browser_name == 'chrome' && driver.respond_to?(:execute_cdp)
81
+ driver.execute_cdp('Emulation.setDeviceMetricsOverride', {
82
+ height: height, width: width, deviceScaleFactor: 1, mobile: false,
83
+ },)
84
+ else
85
+ get_browser_instance(driver).window.resize_to(width, height)
86
+ end
87
+ rescue StandardError => e
88
+ log("Resizing using cdp failed, falling back to driver for width #{width} #{e}", 'debug')
89
+ get_browser_instance(driver).window.resize_to(width, height)
90
+ end
91
+
92
+ begin
93
+ wait = Selenium::WebDriver::Wait.new(timeout: 1)
94
+ wait.until { driver.execute_script('return window.resizeCount') == resize_count }
95
+ rescue Selenium::WebDriver::Error::TimeoutError
96
+ log("Timed out waiting for window resize event for width #{width}", 'debug')
97
+ end
98
+ end
99
+
100
+ def self.capture_responsive_dom(driver, options)
101
+ widths = get_widths_for_multi_dom(options)
102
+ dom_snapshots = []
103
+ window_size = get_browser_instance(driver).window.size
104
+ current_width = window_size.width
105
+ current_height = window_size.height
106
+ last_window_width = current_width
107
+ resize_count = 0
108
+ driver.execute_script('PercyDOM.waitForResize()')
109
+
110
+ widths.each do |width|
111
+ if last_window_width != width
112
+ resize_count += 1
113
+ change_window_dimension_and_wait(driver, width, current_height, resize_count)
114
+ last_window_width = width
115
+ end
39
116
 
40
- if PERCY_DEBUG then log(e) end
117
+ sleep(RESONSIVE_CAPTURE_SLEEP_TIME.to_i) if defined?(RESONSIVE_CAPTURE_SLEEP_TIME)
118
+
119
+ dom_snapshot = get_serialized_dom(driver, options)
120
+ dom_snapshot['width'] = width
121
+ dom_snapshots << dom_snapshot
41
122
  end
123
+
124
+ change_window_dimension_and_wait(driver, current_width, current_height, resize_count + 1)
125
+ dom_snapshots
126
+ end
127
+
128
+ def self.responsive_snapshot_capture?(options)
129
+ # Don't run responsive snapshot capture when defer uploads is enabled
130
+ return false if @cli_config&.dig('percy', 'deferUploads')
131
+
132
+ options[:responsive_snapshot_capture] ||
133
+ options[:responsiveSnapshotCapture] ||
134
+ @cli_config&.dig('snapshot', 'responsiveSnapshotCapture')
42
135
  end
43
136
 
44
137
  # Determine if the Percy server is running, caching the result so it is only checked once
@@ -53,7 +146,7 @@ module Percy
53
146
  log('You may be using @percy/agent ' \
54
147
  'which is no longer supported by this SDK. ' \
55
148
  'Please uninstall @percy/agent and install @percy/cli instead. ' \
56
- 'https://docs.percy.io/docs/migrating-to-percy-cli')
149
+ 'https://www.browserstack.com/docs/percy/migration/migrate-to-cli')
57
150
  @percy_enabled = false
58
151
  return false
59
152
  end
@@ -64,12 +157,14 @@ module Percy
64
157
  return false
65
158
  end
66
159
 
160
+ response_body = JSON.parse(response.body)
161
+ @eligible_widths = response_body['widths']
162
+ @cli_config = response_body['config']
67
163
  @percy_enabled = true
68
164
  true
69
165
  rescue StandardError => e
70
166
  log('Percy is not running, disabling snapshots')
71
-
72
- if PERCY_DEBUG then log(e) end
167
+ log(e, 'debug')
73
168
  @percy_enabled = false
74
169
  false
75
170
  end
@@ -83,8 +178,19 @@ module Percy
83
178
  @percy_dom = response.body
84
179
  end
85
180
 
86
- def self.log(msg)
87
- puts "#{LABEL} #{msg}"
181
+ def self.log(msg, lvl = 'info')
182
+ msg = "#{LABEL} #{msg}"
183
+ begin
184
+ fetch('percy/log', {message: msg, level: lvl})
185
+ rescue StandardError => e
186
+ if PERCY_DEBUG
187
+ puts "Sending log to CLI Failed #{e}"
188
+ end
189
+ ensure
190
+ if lvl != 'debug' || PERCY_DEBUG
191
+ puts msg
192
+ end
193
+ end
88
194
  end
89
195
 
90
196
  # Make an HTTP request (GET,POST) using Ruby's Net::HTTP. If `data` is present,
@@ -112,5 +218,7 @@ module Percy
112
218
  def self._clear_cache!
113
219
  @percy_dom = nil
114
220
  @percy_enabled = nil
221
+ @eligible_widths = nil
222
+ @cli_config = nil
115
223
  end
116
224
  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.2'.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.2
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