lookbook_visual_tester 0.5.7 → 0.5.8

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: 5198b58b3f8bffad2cb68f1bce804b1bce9d2ccc3e32513699f68a83e04fb054
4
- data.tar.gz: bca7f0aa36413c702b5c65e3ed330ac18c4c05edeeeb36e5cb94b0ec1aaa4831
3
+ metadata.gz: 8a9ad38fc0f69bfa66579bfb8b1228a6a9df8dc9899442174162811586336814
4
+ data.tar.gz: c258c211b4181e49b4ec8ea13ce5fc8b00a7095c8f5bad664ad8082d6c7e4c34
5
5
  SHA512:
6
- metadata.gz: 7908ba3fb95002233392bc7b570bdf21f7bfd38f25c0206b0521f5fc89a8773173ea2689fbc1ddaca41b1e838d904118d47d4d3a2760dd829265e7e46d8148c9
7
- data.tar.gz: ee266274b9ee9bfd9dd24c146bf6537618da8c10490cee78c8a667d072a8c1bb06e5a43d0ac402ca98131de425fe7e5df9ace5c9b015ca8830db35788940f8b6
6
+ metadata.gz: 7790e3658daf75df2fe6c18892c4f20ef4738568b1e3731391be847b6857ffd2602a6ade0e7e0850adfaec1c5af8d09b08eb3fa671beda67b9a4af485b2d520c
7
+ data.tar.gz: 5fd17716ff8b4a1bd0d424201a96a75c0dd8c4f2ccdf69207a96c231c73d0ad7115bf047644afde6684590f40e3361d2fa684916ed526984725aaed4be114cbf
data/CHANGELOG.md CHANGED
@@ -1,4 +1,11 @@
1
1
  # Changelog
2
+ ## [0.5.8] - 2026-01-08
3
+
4
+ ### ✨ New Features & Improvements
5
+ - **AI-Native Inspection Tools**: Added `lookbook:inspect` and `lookbook:context` Rake tasks to assist AI agents in understanding preview structure and code context.
6
+ - **Debug Artifacts**: Automatically save HTML DOM snapshots alongside failed visual regression tests for easier debugging.
7
+ - **Visual Tolerance**: Added `tolerance` configuration option and fuzzy matching (Euclidean distance) to `ImageComparator` to reduce flakiness.
8
+
2
9
  ## [0.5.7] - 2026-01-06
3
10
 
4
11
  ### Fixed
data/README.md CHANGED
@@ -51,9 +51,26 @@ LookbookVisualTester.configure do |config|
51
51
  config.copy_to_clipboard = true # Enable xclip support
52
52
  config.threads = 4 # Number of parallel threads (default: 4)
53
53
  config.wait_time = 0.5 # Optional: Wait time (seconds) before screenshot (fixes blank screens)
54
+ config.tolerance = 0.05 # Optional: Mismatch tolerance (0.0 to 1.0) to ignore minor rendering differences
54
55
  end
55
56
  ```
56
57
 
58
+ ## AI Integration
59
+
60
+ The gem includes tools designed to help AI agents understand and debug your components.
61
+
62
+ ### Inspection
63
+ Get JSON metadata about a preview:
64
+ ```bash
65
+ bundle exec rake lookbook:inspect[example/default]
66
+ ```
67
+
68
+ ### Context Dump
69
+ Get full source code of the component, preview, and template:
70
+ ```bash
71
+ bundle exec rake lookbook:context[example/default]
72
+ ```
73
+
57
74
  ## Usage
58
75
 
59
76
  ### Running Visual Tests
@@ -170,7 +187,6 @@ bundle exec rake lookbook:missing
170
187
  ## Next Steps
171
188
 
172
189
  - **Enhanced Approval Workflow**: Implement a `rake lookbook:approve` task (and potentially an interactive mode) to verify and promote changes to baselines without manual file operations.
173
- - **Tolerance Configuration**: Add support for configuring pixel mismatch tolerance levels to reduce flakiness in rendering (anti-aliasing, shadow rendering differences).
174
190
  - **Ignore Regions**: Allow defining specific areas of a component to exclude from visual comparison (useful for unavoidable dynamic content).
175
191
  - **Markdown Reports**: Generate markdown summaries of test runs suitable for posting automatically as Pull Request comments.
176
192
  - **Cloud Storage**: Integration with cloud storage (S3, GCS) for managing baseline images to keep the git repository light.
@@ -4,7 +4,7 @@ module LookbookVisualTester
4
4
  attr_accessor :lookbook_host, :ui_comparison, :diff_dir, :baseline_dir, :current_dir,
5
5
  :history_dir, :history_keep_last_n, :threads, :copy_to_clipboard,
6
6
  :components_folder, :automatic_run, :mask_selectors, :driver_adapter,
7
- :preview_checker_setup, :logger, :wait_time
7
+ :preview_checker_setup, :logger, :wait_time, :tolerance
8
8
 
9
9
  DEFAULT_THREADS = 4
10
10
 
@@ -27,7 +27,9 @@ module LookbookVisualTester
27
27
  @mask_selectors = []
28
28
  @driver_adapter = :ferrum
29
29
  @preview_checker_setup = nil
30
+ @preview_checker_setup = nil
30
31
  @wait_time = 0.5
32
+ @tolerance = 0.0
31
33
  @logger = if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
32
34
  Rails.logger
33
35
  else
@@ -77,6 +77,10 @@ module LookbookVisualTester
77
77
  # Usually for visual testing full page is better unless specifically testing viewport.
78
78
  end
79
79
 
80
+ def page_source
81
+ @browser.body
82
+ end
83
+
80
84
  def cleanup
81
85
  @browser.quit
82
86
  end
@@ -182,6 +182,12 @@ module LookbookVisualTester
182
182
  elsif result[:mismatch] > 0
183
183
  mismatch = result[:mismatch]
184
184
  puts " [FAIL] Mismatch: #{mismatch.round(2)}%. Diff saved to #{diff_path}"
185
+
186
+ # Save DOM snapshot for debugging
187
+ dom_path = diff_path.sub('.png', '.html')
188
+ File.write(dom_path, driver.page_source)
189
+ puts " DOM Snapshot saved to #{dom_path}"
190
+
185
191
  status = :failed
186
192
 
187
193
  # Clipboard (Feature parity with legacy ScreenshotTaker)
@@ -37,7 +37,13 @@ module LookbookVisualTester
37
37
  pixel1 = baseline[x, y]
38
38
  pixel2 = current[x, y]
39
39
 
40
- if pixel1 == pixel2
40
+ score = 0.0
41
+ if pixel1 != pixel2
42
+ score = ChunkyPNG::Color.euclidean_distance_rgba(pixel1, pixel2) / 510.0
43
+ end
44
+
45
+ if score <= LookbookVisualTester.config.tolerance
46
+ # Match (including exact match where score is 0.0)
41
47
  # Blue context for unchanged pixels to make it easier for humans
42
48
  gray_val = ChunkyPNG::Color.r(ChunkyPNG::Color.grayscale_teint(pixel1))
43
49
  # Keep intensity in R/G but push blue to make it the dominant tint
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LookbookVisualTester
4
- VERSION = '0.5.7'
4
+ VERSION = '0.5.8'
5
5
  end
@@ -253,6 +253,139 @@ namespace :lookbook do
253
253
  end
254
254
  puts "\n"
255
255
  end
256
+
257
+ desc 'Get detailed inspection data for a preview (JSON)'
258
+ task :inspect, [:preview_name] => :environment do |_, args|
259
+ require 'lookbook'
260
+ require 'lookbook_visual_tester/scenario_finder'
261
+ require 'lookbook_visual_tester/json_output_handler'
262
+
263
+ preview_name = args[:preview_name]
264
+ unless preview_name
265
+ LookbookVisualTester::JsonOutputHandler.print({ error: 'Please provide a preview name' })
266
+ exit 1
267
+ end
268
+
269
+ scenario_run = LookbookVisualTester::ScenarioFinder.call(preview_name)
270
+
271
+ unless scenario_run
272
+ LookbookVisualTester::JsonOutputHandler.print({ error: "No preview found for #{preview_name}" })
273
+ exit 1
274
+ end
275
+
276
+ scenario = scenario_run.scenario
277
+ preview = scenario.preview
278
+ file_path = preview.file_path
279
+
280
+ # Try to find line number
281
+ line_number = nil
282
+ if File.exist?(file_path)
283
+ File.foreach(file_path).with_index do |line, index|
284
+ if line.match?(/^\s*def\s+#{scenario.name}\b/)
285
+ line_number = index + 1
286
+ break
287
+ end
288
+ end
289
+ end
290
+
291
+ # Inspect component details if available
292
+ component_class_name = nil
293
+ component_file_path = nil
294
+ template_file_path = nil
295
+
296
+ if scenario.respond_to?(:component)
297
+ comp = scenario.component
298
+ if comp.respond_to?(:component_class) && comp.component_class
299
+ component_class_name = comp.component_class.name
300
+ # Best effort to find component source
301
+ if comp.component_class.instance_methods(false).include?(:initialize)
302
+ component_file_path = comp.component_class.instance_method(:initialize).source_location&.first
303
+ end
304
+ # Fallback to const_source_location if supported (Ruby 2.7+)
305
+ if component_file_path.nil? && Object.respond_to?(:const_source_location)
306
+ component_file_path = Object.const_source_location(component_class_name)&.first
307
+ end
308
+ end
309
+
310
+ template_file_path = comp.template_file_path if comp.respond_to?(:template_file_path)
311
+ end
312
+
313
+ output = {
314
+ name: scenario.lookup_path,
315
+ preview_class: preview.name,
316
+ file_path: file_path,
317
+ line_number: line_number,
318
+ url_path: scenario.url_path,
319
+ component_class: component_class_name,
320
+ component_path: component_file_path,
321
+ template_path: template_file_path
322
+ }
323
+
324
+ LookbookVisualTester::JsonOutputHandler.print(output)
325
+ end
326
+
327
+ desc 'Dump content of preview, component, and template for context'
328
+ task :context, [:preview_name] => :environment do |_, args|
329
+ require 'lookbook_visual_tester/json_output_handler'
330
+
331
+ require 'lookbook'
332
+ require 'lookbook_visual_tester/scenario_finder'
333
+
334
+ preview_name = args[:preview_name]
335
+ unless preview_name
336
+ puts 'Error: Please provide a preview name'
337
+ exit 1
338
+ end
339
+
340
+ scenario_run = LookbookVisualTester::ScenarioFinder.call(preview_name)
341
+
342
+ unless scenario_run
343
+ puts "Error: No preview found for #{preview_name}"
344
+ exit 1
345
+ end
346
+
347
+ scenario = scenario_run.scenario
348
+ preview = scenario.preview
349
+
350
+ files_to_dump = []
351
+
352
+ # 1. Preview File
353
+ files_to_dump << { path: preview.file_path, label: "Preview: #{preview.name}" }
354
+
355
+ # 2. Template File (if available)
356
+ if scenario.respond_to?(:component)
357
+ comp = scenario.component
358
+ if comp.respond_to?(:template_file_path) && comp.template_file_path
359
+ files_to_dump << { path: comp.template_file_path, label: 'Template' }
360
+ end
361
+
362
+ # 3. Component File (best effort)
363
+ if comp.respond_to?(:component_class) && comp.component_class
364
+ component_file_path = nil
365
+ if comp.component_class.instance_methods(false).include?(:initialize)
366
+ component_file_path = comp.component_class.instance_method(:initialize).source_location&.first
367
+ end
368
+ if component_file_path.nil? && Object.respond_to?(:const_source_location)
369
+ component_file_path = Object.const_source_location(comp.component_class.name)&.first
370
+ end
371
+
372
+ if component_file_path
373
+ files_to_dump << { path: component_file_path, label: "Component: #{comp.component_class.name}" }
374
+ end
375
+ end
376
+ end
377
+
378
+ files_to_dump.each do |file|
379
+ next unless file[:path] && File.exist?(file[:path])
380
+
381
+ puts "\n"
382
+ puts '========================================'
383
+ puts "#{file[:label]} (#{file[:path]})"
384
+ puts '========================================'
385
+ puts File.read(file[:path])
386
+ puts '========================================'
387
+ end
388
+ end
256
389
  end
257
390
 
258
391
  # Compatibility aliases and extra tools
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lookbook_visual_tester
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.7
4
+ version: 0.5.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Murilo Vasconcelos
@@ -51,6 +51,20 @@ dependencies:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: cuprite
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: ferrum
56
70
  requirement: !ruby/object:Gem::Requirement