percy-selenium 1.1.2 → 1.1.3.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 +4 -4
- data/.gitignore +1 -0
- data/lib/percy.rb +76 -2
- data/lib/version.rb +1 -1
- data/package.json +1 -1
- data/spec/lib/percy/percy_spec.rb +53 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 685b6a737cf5d8ebe9ce59dc70736248b7d5f30a6131bcc34d98ccdb7d1b6a03
|
|
4
|
+
data.tar.gz: b25796e9354d99d64ce0022263e4987cae49a57bb5e0e31443afc490240d7ec2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5e410bb024a3e3bac312041bd907e3a39a5f9f61f14450c0962dbb82af505ebec08319672e63d6e525da1679f14052c0c243bcfd818c65295bfb0c60baf53f4f
|
|
7
|
+
data.tar.gz: 46991f060f602406612f78986767bd5c5f64cfc0e21e4ef6834ee0609f113725dafd455d16d5ef3c0b3eb4b430475e0d9c4ce87a988e1328ad963914383397b5
|
data/.gitignore
CHANGED
data/lib/percy.rb
CHANGED
|
@@ -85,13 +85,16 @@ module Percy
|
|
|
85
85
|
get_serialized_dom(driver, options, percy_dom_script: percy_dom_script)
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
+
# Strip `readiness` before POSTing -- SDK-local config that the CLI
|
|
89
|
+
# already has via healthcheck.
|
|
90
|
+
post_options = options.reject { |k, _| k.to_s == 'readiness' }
|
|
88
91
|
response = fetch('percy/snapshot',
|
|
89
92
|
name: name,
|
|
90
93
|
url: driver.current_url,
|
|
91
94
|
dom_snapshot: dom_snapshot,
|
|
92
95
|
client_info: CLIENT_INFO,
|
|
93
96
|
environment_info: ENV_INFO,
|
|
94
|
-
**
|
|
97
|
+
**post_options,)
|
|
95
98
|
|
|
96
99
|
body = JSON.parse(response.body)
|
|
97
100
|
unless body['success']
|
|
@@ -113,8 +116,79 @@ module Percy
|
|
|
113
116
|
driver.manage
|
|
114
117
|
end
|
|
115
118
|
|
|
119
|
+
# Shallow-merge of global (@cli_config.snapshot.readiness) and per-snapshot
|
|
120
|
+
# (options[:readiness] / options['readiness']) readiness config. Per-snapshot
|
|
121
|
+
# keys win, unspecified global keys (notably preset: disabled) are inherited.
|
|
122
|
+
def self.resolve_readiness_config(options)
|
|
123
|
+
global = @cli_config&.dig('snapshot', 'readiness')
|
|
124
|
+
global = {} unless global.is_a?(Hash)
|
|
125
|
+
per_snapshot = options[:readiness] || options['readiness']
|
|
126
|
+
per_snapshot = {} unless per_snapshot.is_a?(Hash)
|
|
127
|
+
# Normalise symbol keys to strings so the merge collapses :preset and 'preset'.
|
|
128
|
+
[global, per_snapshot].each_with_object({}) do |hash, merged|
|
|
129
|
+
hash.each { |k, v| merged[k.to_s] = v }
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Readiness gate: runs PercyDOM.waitForReady via
|
|
134
|
+
# execute_async_script BEFORE serialize. Graceful on old CLIs that lack the
|
|
135
|
+
# method. Returns readiness diagnostics (or nil) for attachment to domSnapshot.
|
|
136
|
+
def self.wait_for_ready(driver, options)
|
|
137
|
+
readiness_config = resolve_readiness_config(options)
|
|
138
|
+
return nil if readiness_config['preset'] == 'disabled'
|
|
139
|
+
|
|
140
|
+
# Match the driver's async-script timeout to readiness.timeoutMs so a
|
|
141
|
+
# higher user-configured timeout isn't silently capped by Selenium's
|
|
142
|
+
# default (~30s) firing ScriptTimeoutException before the in-page
|
|
143
|
+
# Promise resolves.
|
|
144
|
+
timeout_ms = readiness_config['timeoutMs']
|
|
145
|
+
previous_timeout = nil
|
|
146
|
+
if timeout_ms.is_a?(Numeric) && timeout_ms > 0
|
|
147
|
+
begin
|
|
148
|
+
previous_timeout = driver.manage.timeouts.script_timeout
|
|
149
|
+
driver.manage.timeouts.script_timeout = (timeout_ms / 1000.0) + 2
|
|
150
|
+
rescue StandardError
|
|
151
|
+
previous_timeout = nil # best-effort; older Selenium / unsupported
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
begin
|
|
156
|
+
script = <<~JS
|
|
157
|
+
var cfg = #{readiness_config.to_json};
|
|
158
|
+
var done = arguments[arguments.length - 1];
|
|
159
|
+
try {
|
|
160
|
+
if (typeof PercyDOM !== 'undefined' && typeof PercyDOM.waitForReady === 'function') {
|
|
161
|
+
PercyDOM.waitForReady(cfg).then(function(r){ done(r); }).catch(function(){ done(); });
|
|
162
|
+
} else { done(); }
|
|
163
|
+
} catch (e) { done(); }
|
|
164
|
+
JS
|
|
165
|
+
driver.execute_async_script(script)
|
|
166
|
+
rescue StandardError => e
|
|
167
|
+
log("waitForReady failed, proceeding to serialize: #{e}", 'debug')
|
|
168
|
+
nil
|
|
169
|
+
ensure
|
|
170
|
+
if previous_timeout
|
|
171
|
+
begin
|
|
172
|
+
driver.manage.timeouts.script_timeout = previous_timeout
|
|
173
|
+
rescue StandardError
|
|
174
|
+
# best-effort
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
116
180
|
def self.get_serialized_dom(driver, options, percy_dom_script: nil)
|
|
117
|
-
|
|
181
|
+
# Readiness gate before serialize. Graceful on old CLI.
|
|
182
|
+
readiness_diagnostics = wait_for_ready(driver, options)
|
|
183
|
+
# Strip `readiness` from forwarded serialize args -- it's consumed by
|
|
184
|
+
# wait_for_ready upstream, not a PercyDOM.serialize argument.
|
|
185
|
+
serialize_options = options.reject { |k, _| k.to_s == 'readiness' }
|
|
186
|
+
dom_snapshot = driver.execute_script("return PercyDOM.serialize(#{serialize_options.to_json})")
|
|
187
|
+
# `!nil?` preserves legitimate falsy returns like {} ("gate ran, no
|
|
188
|
+
# notable diagnostics").
|
|
189
|
+
if !readiness_diagnostics.nil? && dom_snapshot.is_a?(Hash)
|
|
190
|
+
dom_snapshot['readiness_diagnostics'] = readiness_diagnostics
|
|
191
|
+
end
|
|
118
192
|
begin
|
|
119
193
|
page_origin = get_origin(driver.current_url)
|
|
120
194
|
iframes = percy_dom_script ? driver.find_elements(:tag_name, 'iframe') : []
|
data/lib/version.rb
CHANGED
data/package.json
CHANGED
|
@@ -748,6 +748,59 @@ RSpec.describe Percy do
|
|
|
748
748
|
expect(dom['corsIframes'].length).to eq(1)
|
|
749
749
|
expect(dom['corsIframes'][0]['frameUrl']).to eq('https://other.example.com/page')
|
|
750
750
|
end
|
|
751
|
+
|
|
752
|
+
# --- Readiness gate --------------------------------------
|
|
753
|
+
|
|
754
|
+
it 'runs waitForReady before serialize and attaches diagnostics' do
|
|
755
|
+
allow(driver).to receive(:execute_async_script).and_return(
|
|
756
|
+
'ok' => true, 'timed_out' => false,
|
|
757
|
+
)
|
|
758
|
+
allow(driver).to receive(:execute_script).and_return({'html' => '<html/>'})
|
|
759
|
+
allow(driver).to receive(:current_url).and_return('http://main.example.com/')
|
|
760
|
+
allow(driver).to receive(:find_elements).and_return([])
|
|
761
|
+
|
|
762
|
+
dom = Percy.get_serialized_dom(driver, {})
|
|
763
|
+
expect(driver).to have_received(:execute_async_script) do |script| # rubocop:disable RSpec/MessageSpies
|
|
764
|
+
expect(script).to include('waitForReady')
|
|
765
|
+
expect(script).to include("typeof PercyDOM.waitForReady === 'function'")
|
|
766
|
+
end
|
|
767
|
+
expect(dom['readiness_diagnostics']).to eq('ok' => true, 'timed_out' => false)
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
it 'embeds per-snapshot readiness config in the script' do
|
|
771
|
+
allow(driver).to receive(:execute_async_script).and_return(nil)
|
|
772
|
+
allow(driver).to receive(:execute_script).and_return({'html' => '<html/>'})
|
|
773
|
+
allow(driver).to receive(:current_url).and_return('http://main.example.com/')
|
|
774
|
+
allow(driver).to receive(:find_elements).and_return([])
|
|
775
|
+
|
|
776
|
+
Percy.get_serialized_dom(driver, readiness: {preset: 'strict', stabilityWindowMs: 500})
|
|
777
|
+
expect(driver).to have_received(:execute_async_script) do |script| # rubocop:disable RSpec/MessageSpies
|
|
778
|
+
expect(script).to include('"preset":"strict"')
|
|
779
|
+
expect(script).to include('"stabilityWindowMs":500')
|
|
780
|
+
end
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
it 'skips execute_async_script when preset is disabled' do
|
|
784
|
+
allow(driver).to receive(:execute_script).and_return({'html' => '<html/>'})
|
|
785
|
+
allow(driver).to receive(:current_url).and_return('http://main.example.com/')
|
|
786
|
+
allow(driver).to receive(:find_elements).and_return([])
|
|
787
|
+
expect(driver).to_not receive(:execute_async_script)
|
|
788
|
+
|
|
789
|
+
dom = Percy.get_serialized_dom(driver, readiness: {preset: 'disabled'})
|
|
790
|
+
expect(dom).to_not have_key('readiness_diagnostics')
|
|
791
|
+
expect(dom['html']).to eq('<html/>')
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
it 'still serializes when execute_async_script raises' do
|
|
795
|
+
allow(driver).to receive(:execute_async_script).and_raise(StandardError, 'readiness boom')
|
|
796
|
+
allow(driver).to receive(:execute_script).and_return({'html' => '<html/>'})
|
|
797
|
+
allow(driver).to receive(:current_url).and_return('http://main.example.com/')
|
|
798
|
+
allow(driver).to receive(:find_elements).and_return([])
|
|
799
|
+
|
|
800
|
+
dom = Percy.get_serialized_dom(driver, {})
|
|
801
|
+
expect(dom).to_not have_key('readiness_diagnostics')
|
|
802
|
+
expect(dom['html']).to eq('<html/>')
|
|
803
|
+
end
|
|
751
804
|
end
|
|
752
805
|
|
|
753
806
|
describe '.change_window_dimension_and_wait' do
|