percy-capybara 5.0.1.pre.1 → 5.0.1.pre.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 +4 -4
- data/lib/percy/capybara.rb +148 -1
- data/lib/percy/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6958cc3537971e6eab60cb6025b1e3a9efd55fb5ca28440444590470bb88eea8
|
|
4
|
+
data.tar.gz: c010a8c95a4633f85546b2bfd748a4523bfd38a2e20ffe3e6782ff582ce8f10e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 305fb4b9322e2bbf30d1abd0814edf3be79b0fae8e55acd9d3822c2cd79e52f6f255ccc2cb4c3eed3e6cd15da591bd1c1b20c481cc32f575b2f9f1d5eb384495
|
|
7
|
+
data.tar.gz: 1dc9d2230d122d6c92b8bb1417fdd2c8c9a62d85167938088cbf6f52575328343925b748f11e6c8abf111f480bfcd74ef417b08826a2f462d40ad3fee177363f
|
data/lib/percy/capybara.rb
CHANGED
|
@@ -14,6 +14,12 @@ module PercyCapybara
|
|
|
14
14
|
private_constant :CLIENT_INFO
|
|
15
15
|
private_constant :ENV_INFO
|
|
16
16
|
|
|
17
|
+
UNSUPPORTED_IFRAME_SRCS = %w[
|
|
18
|
+
about:blank about:srcdoc javascript: data: blob: vbscript: chrome: chrome-extension:
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
private_constant :UNSUPPORTED_IFRAME_SRCS
|
|
22
|
+
|
|
17
23
|
# Take a DOM snapshot and post it to the snapshot endpoint
|
|
18
24
|
def percy_snapshot(name, options = {})
|
|
19
25
|
return unless percy_enabled?
|
|
@@ -21,6 +27,9 @@ module PercyCapybara
|
|
|
21
27
|
page = Capybara.current_session
|
|
22
28
|
|
|
23
29
|
begin
|
|
30
|
+
percy_dom_script = fetch_percy_dom
|
|
31
|
+
page.evaluate_script(percy_dom_script)
|
|
32
|
+
dom_snapshot = get_serialized_dom(page, options, percy_dom_script)
|
|
24
33
|
page.evaluate_script(fetch_percy_dom)
|
|
25
34
|
|
|
26
35
|
# Readiness gate -- runs before serialize when CLI supports it.
|
|
@@ -44,7 +53,16 @@ module PercyCapybara
|
|
|
44
53
|
environment_info: ENV_INFO,
|
|
45
54
|
**options,)
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
data =
|
|
57
|
+
begin
|
|
58
|
+
JSON.parse(response.body)
|
|
59
|
+
rescue JSON::ParserError => e
|
|
60
|
+
log("Could not parse snapshot response for '#{name}': invalid JSON")
|
|
61
|
+
if PERCY_DEBUG then log(e) end
|
|
62
|
+
return
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
unless data['success']
|
|
48
66
|
raise StandardError, data['error']
|
|
49
67
|
end
|
|
50
68
|
rescue StandardError => e
|
|
@@ -54,6 +72,135 @@ module PercyCapybara
|
|
|
54
72
|
end
|
|
55
73
|
end
|
|
56
74
|
|
|
75
|
+
private def get_serialized_dom(page, options, percy_dom_script)
|
|
76
|
+
dom_snapshot = page
|
|
77
|
+
.evaluate_script("(function() { return PercyDOM.serialize(#{options.to_json}) })()")
|
|
78
|
+
|
|
79
|
+
driver = page.driver.browser
|
|
80
|
+
begin
|
|
81
|
+
page_origin = get_origin(page.current_url)
|
|
82
|
+
iframes = driver.find_elements(:tag_name, 'iframe')
|
|
83
|
+
log("Found #{iframes.length} total iframe(s) on page") if PERCY_DEBUG
|
|
84
|
+
|
|
85
|
+
processed_frames = []
|
|
86
|
+
iframes.each do |frame|
|
|
87
|
+
frame_src = frame.attribute('src')
|
|
88
|
+
|
|
89
|
+
if unsupported_iframe_src?(frame_src)
|
|
90
|
+
log("Skipping unsupported iframe src: #{frame_src}") if PERCY_DEBUG && frame_src
|
|
91
|
+
next
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
begin
|
|
95
|
+
frame_origin = get_origin(URI.join(page.current_url, frame_src).to_s)
|
|
96
|
+
rescue StandardError => e
|
|
97
|
+
log("Skipping iframe with invalid URL \"#{frame_src}\": #{e}") if PERCY_DEBUG
|
|
98
|
+
next
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if frame_origin == page_origin
|
|
102
|
+
log("Skipping same-origin iframe: #{frame_src}") if PERCY_DEBUG
|
|
103
|
+
next
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Pre-flight check: avoid the switch/serialize round-trip if there's no
|
|
107
|
+
# data-percy-element-id, since process_frame would discard the snapshot
|
|
108
|
+
# anyway and we'd silently waste a frame switch.
|
|
109
|
+
unless frame.attribute('data-percy-element-id')
|
|
110
|
+
log("Skipping iframe #{frame_src}: no data-percy-element-id found") if PERCY_DEBUG
|
|
111
|
+
next
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
result = process_frame(driver, frame, options, percy_dom_script)
|
|
115
|
+
processed_frames << result if result
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
if processed_frames.any?
|
|
119
|
+
dom_snapshot['corsIframes'] = processed_frames
|
|
120
|
+
log("Captured #{processed_frames.length} cross-origin iframe(s)")
|
|
121
|
+
end
|
|
122
|
+
rescue StandardError => e
|
|
123
|
+
log("Failed to process cross-origin iframes: #{e}") if PERCY_DEBUG
|
|
124
|
+
begin
|
|
125
|
+
driver.switch_to.default_content
|
|
126
|
+
rescue StandardError
|
|
127
|
+
nil
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
dom_snapshot
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
private def unsupported_iframe_src?(src)
|
|
135
|
+
return true if src.nil? || src.empty?
|
|
136
|
+
|
|
137
|
+
UNSUPPORTED_IFRAME_SRCS.any? { |prefix| src == prefix || src.start_with?(prefix) }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
private def get_origin(url)
|
|
141
|
+
uri = URI.parse(url)
|
|
142
|
+
raise URI::InvalidURIError, "no host in #{url}" if uri.host.nil?
|
|
143
|
+
|
|
144
|
+
default_ports = {'http' => 80, 'https' => 443}
|
|
145
|
+
netloc = uri.host.to_s
|
|
146
|
+
netloc += ":#{uri.port}" if uri.port && uri.port != default_ports[uri.scheme]
|
|
147
|
+
"#{uri.scheme}://#{netloc}"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
private def process_frame(driver, frame_element, options, percy_dom_script)
|
|
151
|
+
frame_url = frame_element.attribute('src') || 'unknown-src'
|
|
152
|
+
log("Processing cross-origin iframe: #{frame_url}") if PERCY_DEBUG
|
|
153
|
+
|
|
154
|
+
begin
|
|
155
|
+
driver.switch_to.frame(frame_element)
|
|
156
|
+
begin
|
|
157
|
+
driver.execute_script(percy_dom_script)
|
|
158
|
+
iframe_options = options.merge('enableJavaScript' => true)
|
|
159
|
+
iframe_snapshot =
|
|
160
|
+
driver.execute_script("return PercyDOM.serialize(#{iframe_options.to_json})")
|
|
161
|
+
log("Serialized cross-origin iframe: #{frame_url}") if PERCY_DEBUG
|
|
162
|
+
rescue StandardError => e
|
|
163
|
+
log("Failed to serialize cross-origin iframe #{frame_url}: #{e}") if PERCY_DEBUG
|
|
164
|
+
return nil
|
|
165
|
+
ensure
|
|
166
|
+
begin
|
|
167
|
+
driver.switch_to.default_content
|
|
168
|
+
rescue StandardError
|
|
169
|
+
begin
|
|
170
|
+
driver.switch_to.parent_frame
|
|
171
|
+
rescue StandardError
|
|
172
|
+
nil
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
rescue StandardError => e
|
|
177
|
+
log("Failed to switch to frame #{frame_url}: #{e}") if PERCY_DEBUG
|
|
178
|
+
begin
|
|
179
|
+
driver.switch_to.default_content
|
|
180
|
+
rescue StandardError
|
|
181
|
+
nil
|
|
182
|
+
end
|
|
183
|
+
return nil
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
percy_element_id = frame_element.attribute('data-percy-element-id')
|
|
187
|
+
unless percy_element_id
|
|
188
|
+
log("Skipping frame #{frame_url}: no data-percy-element-id found") if PERCY_DEBUG
|
|
189
|
+
return nil
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
if PERCY_DEBUG
|
|
193
|
+
log("Successfully captured cross-origin iframe: #{frame_url} " \
|
|
194
|
+
"(percyElementId: #{percy_element_id})")
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
{
|
|
198
|
+
'iframeData' => {'percyElementId' => percy_element_id},
|
|
199
|
+
'iframeSnapshot' => iframe_snapshot,
|
|
200
|
+
'frameUrl' => frame_url,
|
|
201
|
+
}
|
|
202
|
+
end
|
|
203
|
+
|
|
57
204
|
# Determine if the Percy server is running, caching the result so it is only checked once
|
|
58
205
|
private def percy_enabled?
|
|
59
206
|
return @percy_enabled unless @percy_enabled.nil?
|
data/lib/percy/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: percy-capybara
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.0.1.pre.
|
|
4
|
+
version: 5.0.1.pre.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: 2026-06-
|
|
11
|
+
date: 2026-06-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: capybara
|