vizzly 0.2.1 → 0.3.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/README.md +24 -2
- data/lib/vizzly.rb +122 -18
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 00b928505560c41d2c1402ccb75ce47a3814cfaedf3d15094b5685d21a5ae2fb
|
|
4
|
+
data.tar.gz: dafb6128f52bc6e76b9cba415ef1e8823615758048d58613fb1688e337ab8c15
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f9afac44fa26d16dd705e82dbe6f38cc161004ebe97580f6c47d1d68f2f808a57f644008e718339f1695b9cc421206ca30ec58229ef7d8fb68d1b1bb1658d9f
|
|
7
|
+
data.tar.gz: d9af13050f7617fd2e0c94abc364a8b90ca890d92d3df7542fe18797dc4aea014a21dcb1d1e62f96e7ab8ac699441f5a8c8a84cfb5015f72c182749dd17800b0
|
data/README.md
CHANGED
|
@@ -43,20 +43,40 @@ Vizzly.screenshot('checkout-page', image_data,
|
|
|
43
43
|
browser: 'chrome',
|
|
44
44
|
viewport: { width: 1920, height: 1080 }
|
|
45
45
|
},
|
|
46
|
-
threshold: 5
|
|
46
|
+
threshold: 5,
|
|
47
|
+
min_cluster_size: 3,
|
|
48
|
+
full_page: true,
|
|
49
|
+
build_id: 'build_123',
|
|
50
|
+
request_timeout: 60_000
|
|
47
51
|
)
|
|
48
52
|
```
|
|
49
53
|
|
|
54
|
+
Ruby option names use snake_case. The client also accepts camelCase aliases for
|
|
55
|
+
parity with the JavaScript API: `minClusterSize`, `fullPage`, `buildId`, and
|
|
56
|
+
`requestTimeout`. `request_timeout` is measured in milliseconds, so
|
|
57
|
+
`60_000` means one minute.
|
|
58
|
+
|
|
50
59
|
### Using a Client Instance
|
|
51
60
|
|
|
52
61
|
```ruby
|
|
53
62
|
client = Vizzly::Client.new
|
|
54
63
|
client.screenshot('login-form', image_data)
|
|
55
64
|
|
|
65
|
+
# Override local TDD visual diff behavior for this client
|
|
66
|
+
strict_client = Vizzly::Client.new(fail_on_diff: true)
|
|
67
|
+
|
|
68
|
+
# Attach screenshots to a known build and tune request timeout in milliseconds
|
|
69
|
+
client.screenshot(
|
|
70
|
+
'checkout',
|
|
71
|
+
image_data,
|
|
72
|
+
build_id: 'build_123',
|
|
73
|
+
request_timeout: 60_000
|
|
74
|
+
)
|
|
75
|
+
|
|
56
76
|
# Check if client is ready
|
|
57
77
|
puts "Ready: #{client.ready?}"
|
|
58
78
|
|
|
59
|
-
# Get client info
|
|
79
|
+
# Get client info, including the effective fail_on_diff setting
|
|
60
80
|
puts client.info
|
|
61
81
|
```
|
|
62
82
|
|
|
@@ -100,6 +120,8 @@ You can also configure via environment variables:
|
|
|
100
120
|
|
|
101
121
|
- `VIZZLY_SERVER_URL` - Server URL (e.g., `http://localhost:47392`)
|
|
102
122
|
- `VIZZLY_BUILD_ID` - Build identifier for grouping screenshots
|
|
123
|
+
- `VIZZLY_FAIL_ON_DIFF` - Set to `true` or `1` to raise when a local TDD
|
|
124
|
+
comparison returns a visual diff
|
|
103
125
|
|
|
104
126
|
## Development
|
|
105
127
|
|
data/lib/vizzly.rb
CHANGED
|
@@ -11,12 +11,15 @@ module Vizzly
|
|
|
11
11
|
# Default port for local TDD server
|
|
12
12
|
DEFAULT_TDD_PORT = 47392
|
|
13
13
|
|
|
14
|
+
# rubocop:disable Metrics/ClassLength
|
|
14
15
|
class Client
|
|
15
16
|
attr_reader :server_url, :disabled
|
|
16
17
|
|
|
17
|
-
def initialize(server_url: nil)
|
|
18
|
+
def initialize(server_url: nil, fail_on_diff: nil)
|
|
19
|
+
@server_info = nil
|
|
20
|
+
@configured_fail_on_diff = fail_on_diff
|
|
18
21
|
@server_url = server_url || discover_server_url
|
|
19
|
-
@disabled = false
|
|
22
|
+
@disabled = ENV['VIZZLY_ENABLED'] == 'false'
|
|
20
23
|
@warned = false
|
|
21
24
|
end
|
|
22
25
|
|
|
@@ -26,9 +29,13 @@ module Vizzly
|
|
|
26
29
|
# @param image_data [String] PNG image data as binary string
|
|
27
30
|
# @param options [Hash] Optional configuration
|
|
28
31
|
# @option options [Hash] :properties Additional properties to attach
|
|
29
|
-
# @option options [
|
|
30
|
-
#
|
|
32
|
+
# @option options [Numeric] :threshold Delta E comparison threshold. When
|
|
33
|
+
# omitted, the server's configured threshold is used.
|
|
34
|
+
# @option options [Integer] :min_cluster_size Ignore connected diff
|
|
35
|
+
# clusters smaller than this size. When omitted, the server config is used.
|
|
31
36
|
# @option options [Boolean] :full_page Whether this is a full page screenshot
|
|
37
|
+
# @option options [String] :build_id Build ID for grouping screenshots
|
|
38
|
+
# @option options [Numeric] :request_timeout Request timeout in milliseconds
|
|
32
39
|
#
|
|
33
40
|
# @return [Hash, nil] Response data or nil if disabled/failed
|
|
34
41
|
#
|
|
@@ -42,7 +49,7 @@ module Vizzly
|
|
|
42
49
|
# properties: { browser: 'chrome', viewport: { width: 1920, height: 1080 } },
|
|
43
50
|
# threshold: 5
|
|
44
51
|
# )
|
|
45
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
52
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
46
53
|
def screenshot(name, image_data, options = {})
|
|
47
54
|
return nil if disabled?
|
|
48
55
|
|
|
@@ -53,27 +60,34 @@ module Vizzly
|
|
|
53
60
|
end
|
|
54
61
|
|
|
55
62
|
image_base64 = Base64.strict_encode64(image_data)
|
|
63
|
+
options = normalize_options(options)
|
|
64
|
+
normalized = normalize_screenshot_options(options)
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
fullPage: options[:full_page]
|
|
63
|
-
).compact
|
|
66
|
+
normalized[:warnings].each { |warning| warn warning[:message] }
|
|
67
|
+
|
|
68
|
+
request_timeout = normalized[:request_timeout]
|
|
69
|
+
request_timeout_seconds = request_timeout ? request_timeout.to_f / 1000.0 : 30
|
|
70
|
+
build_id = normalized[:build_id] || ENV.fetch('VIZZLY_BUILD_ID', nil)
|
|
64
71
|
|
|
65
72
|
payload = {
|
|
66
73
|
name: name,
|
|
67
74
|
image: image_base64,
|
|
68
75
|
type: 'base64',
|
|
69
|
-
buildId:
|
|
70
|
-
properties: properties
|
|
76
|
+
buildId: build_id,
|
|
77
|
+
properties: normalized[:properties],
|
|
78
|
+
warnings: normalized[:warnings]
|
|
71
79
|
}.compact
|
|
72
80
|
|
|
73
81
|
uri = URI("#{@server_url}/screenshot")
|
|
74
82
|
|
|
75
83
|
begin
|
|
76
|
-
response = Net::HTTP.start(
|
|
84
|
+
response = Net::HTTP.start(
|
|
85
|
+
uri.host,
|
|
86
|
+
uri.port,
|
|
87
|
+
use_ssl: uri.scheme == 'https',
|
|
88
|
+
open_timeout: 10,
|
|
89
|
+
read_timeout: request_timeout_seconds
|
|
90
|
+
) do |http|
|
|
77
91
|
request = Net::HTTP::Post.new(uri)
|
|
78
92
|
request['Content-Type'] = 'application/json'
|
|
79
93
|
request.body = JSON.generate(payload)
|
|
@@ -92,6 +106,11 @@ module Vizzly
|
|
|
92
106
|
comp = error_data['comparison']
|
|
93
107
|
diff_percent = comp['diffPercentage']&.round(2) || 0.0
|
|
94
108
|
|
|
109
|
+
if fail_on_diff?
|
|
110
|
+
raise Error,
|
|
111
|
+
"Visual diff detected for \"#{comp['name'] || name}\" (#{comp['diffPercentage'] || 0}% difference)"
|
|
112
|
+
end
|
|
113
|
+
|
|
95
114
|
# Extract port from server_url
|
|
96
115
|
port = begin
|
|
97
116
|
@server_url.match(/:(\d+)/)[1]
|
|
@@ -114,7 +133,13 @@ module Vizzly
|
|
|
114
133
|
"Screenshot failed: #{response.code} #{response.message} - #{error_data['error'] || 'Unknown error'}"
|
|
115
134
|
end
|
|
116
135
|
|
|
117
|
-
JSON.parse(response.body)
|
|
136
|
+
body = JSON.parse(response.body)
|
|
137
|
+
if body['tddMode'] && %w[diff failed].include?(body['status']) && fail_on_diff?
|
|
138
|
+
raise Error,
|
|
139
|
+
"Visual diff detected for \"#{body['name'] || name}\" (#{body['diffPercentage'] || 0}% difference)"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
body
|
|
118
143
|
rescue Error => e
|
|
119
144
|
# Re-raise Vizzly errors (like visual diffs)
|
|
120
145
|
raise if e.message.include?('Visual diff')
|
|
@@ -152,7 +177,7 @@ module Vizzly
|
|
|
152
177
|
nil
|
|
153
178
|
end
|
|
154
179
|
end
|
|
155
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
180
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
156
181
|
|
|
157
182
|
# Wait for all queued screenshots to be processed
|
|
158
183
|
# (Simple client doesn't need explicit flushing)
|
|
@@ -193,14 +218,82 @@ module Vizzly
|
|
|
193
218
|
{
|
|
194
219
|
enabled: !disabled?,
|
|
195
220
|
server_url: @server_url,
|
|
221
|
+
serverUrl: @server_url,
|
|
196
222
|
ready: ready?,
|
|
197
223
|
build_id: ENV.fetch('VIZZLY_BUILD_ID', nil),
|
|
198
|
-
|
|
224
|
+
buildId: ENV.fetch('VIZZLY_BUILD_ID', nil),
|
|
225
|
+
disabled: disabled?,
|
|
226
|
+
fail_on_diff: fail_on_diff?,
|
|
227
|
+
failOnDiff: fail_on_diff?
|
|
199
228
|
}
|
|
200
229
|
end
|
|
201
230
|
|
|
202
231
|
private
|
|
203
232
|
|
|
233
|
+
def normalize_options(options)
|
|
234
|
+
options.transform_keys { |key| key.is_a?(String) ? key.to_sym : key }
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def option_value(options, *keys)
|
|
238
|
+
keys.each do |key|
|
|
239
|
+
return options[key] if options.key?(key)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
nil
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def normalize_screenshot_options(options)
|
|
246
|
+
threshold = options[:threshold]
|
|
247
|
+
min_cluster_size = option_value(options, :min_cluster_size, :minClusterSize)
|
|
248
|
+
full_page = options.key?(:full_page) ? options[:full_page] : options[:fullPage]
|
|
249
|
+
build_id = option_value(options, :build_id, :buildId)
|
|
250
|
+
request_timeout = option_value(options, :request_timeout, :requestTimeout)
|
|
251
|
+
properties = {}
|
|
252
|
+
warnings = []
|
|
253
|
+
|
|
254
|
+
(options[:properties] || {}).each do |key, value|
|
|
255
|
+
option = key.to_s
|
|
256
|
+
case option
|
|
257
|
+
when 'threshold'
|
|
258
|
+
threshold = value if threshold.nil?
|
|
259
|
+
when 'min_cluster_size', 'minClusterSize'
|
|
260
|
+
min_cluster_size = value if min_cluster_size.nil?
|
|
261
|
+
when 'full_page', 'fullPage'
|
|
262
|
+
full_page = value if full_page.nil?
|
|
263
|
+
when 'build_id', 'buildId'
|
|
264
|
+
build_id = value if build_id.nil?
|
|
265
|
+
when 'request_timeout', 'requestTimeout'
|
|
266
|
+
request_timeout = value if request_timeout.nil?
|
|
267
|
+
else
|
|
268
|
+
properties[key] = value
|
|
269
|
+
next
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
warnings << reserved_property_warning(option)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
properties = properties.merge(
|
|
276
|
+
threshold: threshold,
|
|
277
|
+
minClusterSize: min_cluster_size,
|
|
278
|
+
fullPage: full_page
|
|
279
|
+
).compact
|
|
280
|
+
|
|
281
|
+
{
|
|
282
|
+
build_id: build_id,
|
|
283
|
+
request_timeout: request_timeout,
|
|
284
|
+
properties: properties,
|
|
285
|
+
warnings: warnings
|
|
286
|
+
}
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def reserved_property_warning(option)
|
|
290
|
+
{
|
|
291
|
+
code: 'reserved-property-option',
|
|
292
|
+
option: option,
|
|
293
|
+
message: "Move \"#{option}\" out of properties; properties is only for user metadata."
|
|
294
|
+
}
|
|
295
|
+
end
|
|
296
|
+
|
|
204
297
|
def warn_once(message)
|
|
205
298
|
return if @warned
|
|
206
299
|
|
|
@@ -217,6 +310,15 @@ module Vizzly
|
|
|
217
310
|
auto_discover_tdd_server
|
|
218
311
|
end
|
|
219
312
|
|
|
313
|
+
def fail_on_diff?
|
|
314
|
+
return @configured_fail_on_diff unless @configured_fail_on_diff.nil?
|
|
315
|
+
|
|
316
|
+
env_value = ENV.fetch('VIZZLY_FAIL_ON_DIFF', '').downcase
|
|
317
|
+
return true if %w[true 1].include?(env_value)
|
|
318
|
+
|
|
319
|
+
@server_info && @server_info['failOnDiff'] == true
|
|
320
|
+
end
|
|
321
|
+
|
|
220
322
|
# Auto-discover local TDD server by checking for server.json
|
|
221
323
|
def auto_discover_tdd_server
|
|
222
324
|
dir = Dir.pwd
|
|
@@ -228,6 +330,7 @@ module Vizzly
|
|
|
228
330
|
if File.exist?(server_json_path)
|
|
229
331
|
begin
|
|
230
332
|
server_info = JSON.parse(File.read(server_json_path))
|
|
333
|
+
@server_info = server_info
|
|
231
334
|
port = server_info['port'] || DEFAULT_TDD_PORT
|
|
232
335
|
return "http://localhost:#{port}"
|
|
233
336
|
rescue JSON::ParserError, Errno::ENOENT
|
|
@@ -241,6 +344,7 @@ module Vizzly
|
|
|
241
344
|
nil
|
|
242
345
|
end
|
|
243
346
|
end
|
|
347
|
+
# rubocop:enable Metrics/ClassLength
|
|
244
348
|
|
|
245
349
|
class << self
|
|
246
350
|
# Get or create the shared client instance
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vizzly
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vizzly
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A lightweight client SDK for capturing screenshots and sending them to
|
|
14
14
|
Vizzly for visual regression testing
|
|
@@ -44,7 +44,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
45
|
version: '0'
|
|
46
46
|
requirements: []
|
|
47
|
-
rubygems_version: 3.
|
|
47
|
+
rubygems_version: 3.5.22
|
|
48
48
|
signing_key:
|
|
49
49
|
specification_version: 4
|
|
50
50
|
summary: Vizzly visual regression testing client for Ruby
|