lookbook_visual_tester 0.1.6 → 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.
@@ -1,92 +1,298 @@
1
1
  # lib/tasks/lookbook_visual_tester.rake
2
- require 'fileutils'
3
- require 'mini_magick'
4
- require 'ruby-prof'
5
- require 'concurrent-ruby'
6
-
2
+ require 'lookbook_visual_tester/runner'
7
3
  require 'lookbook_visual_tester/report_generator'
8
- require 'lookbook_visual_tester/screenshot_taker'
9
- require 'lookbook_visual_tester/image_comparator'
10
- require 'lookbook_visual_tester/scenario_run'
4
+ require 'lookbook_visual_tester/json_output_handler'
11
5
 
12
- namespace :lookbook_visual_tester do
13
- desc 'Profile the lookbook_visual_tester:run task'
14
- task profile: :environment do
15
- RubyProf.start
16
- Rake::Task['lookbook_visual_tester:run'].invoke
17
- result = RubyProf.stop
6
+ namespace :lookbook do
7
+ desc 'List all available previews'
8
+ task :list, [:format] => :environment do |_, args|
9
+ require 'lookbook'
10
+ previews = Lookbook.previews.flat_map do |preview|
11
+ group = preview.respond_to?(:scenarios) ? preview.scenarios : preview.examples
12
+ group.map { |scenario| scenario.lookup_path }
13
+ end.sort
18
14
 
19
- printer = RubyProf::FlatPrinter.new(result)
20
- printer.print(STDOUT)
15
+ if args[:format] == 'json'
16
+ LookbookVisualTester::JsonOutputHandler.print(previews)
17
+ else
18
+ puts previews
19
+ end
21
20
  end
22
21
 
23
- desc 'Run and copy to clipboard first scenario matching the given name'
24
- task :copy, [:name] => :environment do |t, args|
25
- # example on how to run: `rake lookbook_visual_tester:copy["Button"]`
22
+ desc 'Generate screenshots for a specific preview (and run comparison)'
23
+ task :screenshot, %i[preview_name format] => :environment do |_, args|
24
+ preview_name = args[:preview_name]
25
+ format = args[:format]
26
+
27
+ unless preview_name
28
+ if format == 'json'
29
+ LookbookVisualTester::JsonOutputHandler.print({ error: 'Please provide a preview name' })
30
+ else
31
+ puts 'Please provide a preview name: rake lookbook:screenshot[Button]'
32
+ end
33
+ exit 1
34
+ end
26
35
 
27
- previews = Lookbook.previews
36
+ # Run the test (which generates screenshots)
37
+ runner = LookbookVisualTester::Runner.new(pattern: preview_name)
28
38
 
29
- scenario_run = LookbookVisualTester::ScenarioFinder.call(args[:name], previews)
30
- unless scenario_run
31
- screenshot_taker = LookbookVisualTester::ScreenshotTaker.new(scenario_run:)
32
- puts "No Lookbook previews found matching #{args[:name]}"
33
- exit
39
+ # Silence stdout if json format, to avoid pollution
40
+ original_stdout = $stdout
41
+ $stdout = File.new('/dev/null', 'w') if format == 'json'
42
+
43
+ begin
44
+ results = runner.run
45
+ ensure
46
+ $stdout = original_stdout if format == 'json'
47
+ end
48
+
49
+ if format == 'json'
50
+ # Transform results to simple hash
51
+ json_results = results.map do |r|
52
+ {
53
+ scenario_name: r.scenario_name,
54
+ status: r.status,
55
+ mismatch: r.mismatch,
56
+ diff_path: r.diff_path,
57
+ baseline_path: r.baseline_path,
58
+ current_path: r.current_path,
59
+ error: r.error
60
+ }
61
+ end
62
+
63
+ # If single result, return just object, else array
64
+ output = json_results.size == 1 ? json_results.first : json_results
65
+ LookbookVisualTester::JsonOutputHandler.print(output)
66
+ else
67
+ print_cli_summary(results)
34
68
  end
35
- screenshot_taker.capture(scenario_run.preview_url)
36
- exit
37
69
  end
38
70
 
39
- desc 'Run visual regression tests for Lookbook previews'
40
- task run: :environment do
41
- image_comparator = LookbookVisualTester::ImageComparator.new
42
- report = LookbookVisualTester::ReportGenerator.new
71
+ desc 'Run visual regression tests for all previews'
72
+ task :test, [:format] => :environment do |_, args|
73
+ runner = LookbookVisualTester::Runner.new
43
74
 
44
- previews = Lookbook.previews
75
+ # Check for ENV var or arg
76
+ json_mode = args[:format] == 'json' || ENV['JSON_OUTPUT'] == 'true'
45
77
 
46
- if previews.empty?
47
- puts 'No Lookbook previews found.'
48
- exit
78
+ original_stdout = $stdout
79
+ $stdout = File.new('/dev/null', 'w') if json_mode
80
+
81
+ begin
82
+ results = runner.run
83
+ ensure
84
+ $stdout = original_stdout if json_mode
49
85
  end
50
86
 
51
- pool = Concurrent::FixedThreadPool.new(LookbookVisualTester.config.threads)
52
- previews.each do |preview|
53
- preview_name = preview.name.underscore
54
- puts "Processing Preview: #{preview_name}"
87
+ # Save results for retry logic
88
+ if defined?(LookbookVisualTester.config.diff_dir)
89
+ result_file = LookbookVisualTester.config.diff_dir.join('last_run.json')
90
+ FileUtils.mkdir_p(File.dirname(result_file))
55
91
 
56
- preview.scenarios.each do |scenario|
57
- Concurrent::Promises.future_on(pool) do
58
- scenario_run = LookbookVisualTester::ScenarioRun.new(scenario)
59
- screenshot_taker = LookbookVisualTester::ScreenshotTaker.new(scenario_run:)
60
- screenshot_taker.capture(scenario_run.preview_url, scenario_run.current_path)
61
- puts " Visiting URL: #{preview_url}"
92
+ simplified_results = results.map do |r|
93
+ {
94
+ scenario_name: r.scenario_name,
95
+ status: r.status,
96
+ mismatch: r.mismatch,
97
+ diff_path: r.diff_path.to_s,
98
+ current_path: r.current_path.to_s,
99
+ baseline_path: r.baseline_path.to_s
100
+ }
101
+ end
102
+ File.write(result_file, JSON.dump(simplified_results))
103
+ end
62
104
 
63
- image_comparator.compare(scenario_run)
105
+ if json_mode
106
+ summary = {
107
+ total: results.size,
108
+ passed: results.count { |r| r.status == :passed },
109
+ failed: results.count { |r| r.status == :failed },
110
+ new: results.count { |r| r.status == :new },
111
+ errors: results.count { |r| r.status == :error },
112
+ results: results.map do |r|
113
+ {
114
+ name: r.scenario_name,
115
+ status: r.status,
116
+ mismatch: r.mismatch,
117
+ diff_path: r.diff_path
118
+ }
64
119
  end
120
+ }
121
+ LookbookVisualTester::JsonOutputHandler.print(summary)
122
+ else
123
+ print_cli_summary(results)
124
+
125
+ # Generate detailed HTML report
126
+ reporter = LookbookVisualTester::ReportGenerator.new(results)
127
+ reporter.call
128
+
129
+ exit 1 if results.any? { |r| r.status == :failed }
130
+ end
131
+ end
132
+
133
+ desc 'Approve a specific preview change (update baseline)'
134
+ task :approve, [:preview_name] => :environment do |_, args|
135
+ preview_name = args[:preview_name]
136
+ unless preview_name
137
+ puts 'Please provide a preview name: rake lookbook:approve[Button/default]'
138
+ exit 1
139
+ end
140
+
141
+ baseline_dir = LookbookVisualTester.config.baseline_dir
142
+ current_dir = LookbookVisualTester.config.current_dir
143
+
144
+ # Find matching files in current_dir
145
+ candidates = Dir.glob(current_dir.join('*')).select do |f|
146
+ File.basename(f).include?(preview_name.gsub('/', '_'))
147
+ end
148
+
149
+ if candidates.empty?
150
+ puts "No current runs found matching '#{preview_name}'."
151
+ exit 1
152
+ end
153
+
154
+ candidates.each do |current_file|
155
+ filename = File.basename(current_file)
156
+ next if filename.include?('_diff.png') # Don't copy diffs
157
+
158
+ baseline_file = baseline_dir.join(filename)
159
+ FileUtils.mkdir_p(baseline_dir)
160
+ FileUtils.cp(current_file, baseline_file)
161
+ puts "Approved: #{filename}"
162
+ end
163
+ end
164
+
165
+ desc 'Re-run only failing tests from the last run'
166
+ task :retry, [:format] => :environment do |_, args|
167
+ result_file = LookbookVisualTester.config.diff_dir.join('last_run.json')
168
+ unless File.exist?(result_file)
169
+ puts "No previous run data found at #{result_file}. Run 'rake lookbook:test' first."
170
+ exit 1
171
+ end
172
+
173
+ last_run = JSON.parse(File.read(result_file), symbolize_names: true)
174
+ failures = last_run.select { |r| r[:status] == 'failed' || r[:status] == 'error' }
175
+
176
+ if failures.empty?
177
+ puts 'No failures found in last run.'
178
+ exit 0
179
+ end
180
+
181
+ puts "Retrying #{failures.size} failure(s)..."
182
+
183
+ failures.each do |failure|
184
+ puts "Retrying #{failure[:scenario_name]}..."
185
+ runner = LookbookVisualTester::Runner.new(pattern: failure[:scenario_name])
186
+ runner.run
187
+ end
188
+ end
189
+
190
+ def print_cli_summary(results)
191
+ total = results.size
192
+ passed = results.count { |r| r.status == :passed }
193
+ failed = results.count { |r| r.status == :failed }
194
+ new_scenarios = results.count { |r| r.status == :new }
195
+ errors = results.count { |r| r.status == :error }
196
+
197
+ puts "\n"
198
+ puts '========================================'
199
+ puts ' Test Summary '
200
+ puts '========================================'
201
+ puts "Total Scenarios: #{total}"
202
+ puts "Passed: #{passed}"
203
+ puts "Failed: #{failed}"
204
+ puts "New (Baselines): #{new_scenarios}" if new_scenarios > 0
205
+ puts "Errors: #{errors}" if errors > 0
206
+ puts '========================================'
207
+
208
+ if failed > 0
209
+ puts "\nFailed Scenarios:"
210
+ results.select { |r| r.status == :failed }.each do |r|
211
+ puts " [FAIL] #{r.scenario_name}"
212
+ puts " Mismatch: #{r.mismatch.round(2)}%"
213
+ puts " Diff: #{r.diff_path}"
65
214
  end
66
215
  end
67
216
 
68
- pool.shutdown
69
- pool.wait_for_termination
70
- report_path = report.generate
71
- puts "Visual regression report generated at #{report_path}"
217
+ if errors > 0
218
+ puts "\nErrors:"
219
+ results.select { |r| r.status == :error }.each do |r|
220
+ puts " [ERROR] #{r.scenario_name}"
221
+ puts " Message: #{r.error}"
222
+ end
223
+ end
224
+ puts "\n"
72
225
  end
226
+ end
73
227
 
74
- desc 'Update baseline screenshots with current_run screenshots'
75
- task update_baseline: :environment do
76
- base_path = Rails.root.join('spec/visual_screenshots')
77
- baseline_dir = base_path.join('baseline')
78
- current_dir = base_path.join('current_run')
228
+ # Compatibility aliases and extra tools
229
+ namespace :lookbook_visual_tester do
230
+ desc 'Run visual regression tests (alias for lookbook:test)'
231
+ task run: 'lookbook:test'
232
+
233
+ desc 'Update baseline screenshots from current_run'
234
+ task update_baseline: 'lookbook_visual_tester:environment' do
235
+ baseline_dir = LookbookVisualTester.config.baseline_dir
236
+ current_dir = LookbookVisualTester.config.current_dir
79
237
 
80
238
  unless current_dir.exist?
81
- puts 'Current run directory does not exist. Run the visual regression tests first.'
82
- exit
239
+ puts 'Current run directory does not exist.'
240
+ exit 1
83
241
  end
84
242
 
85
243
  Dir.glob(current_dir.join('*.png')).each do |current_file|
86
244
  filename = File.basename(current_file)
87
245
  baseline_file = baseline_dir.join(filename)
246
+ FileUtils.mkdir_p(baseline_dir)
88
247
  FileUtils.cp(current_file, baseline_file)
89
- puts "Baseline updated for #{filename}"
248
+ puts "Updated baseline: #{filename}"
90
249
  end
91
250
  end
251
+
252
+ desc 'Profile the visual tester'
253
+ task profile: :environment do
254
+ require 'ruby-prof'
255
+ RubyProf.start
256
+ Rake::Task['lookbook:test'].invoke
257
+ result = RubyProf.stop
258
+ printer = RubyProf::FlatPrinter.new(result)
259
+ printer.print($stdout)
260
+ end
261
+
262
+ desc 'Get list of images for the given component'
263
+ task :images, %i[name skip_capture] => :environment do |_, args|
264
+ require 'lookbook_visual_tester/scenario_finder'
265
+ scenario_run = LookbookVisualTester::ScenarioFinder.call(args[:name])
266
+
267
+ unless scenario_run
268
+ puts "No Lookbook previews found matching #{args[:name]}"
269
+ exit 1
270
+ end
271
+
272
+ if args[:skip_capture].to_s == 'true'
273
+ # Just print existing
274
+ else
275
+ # Run it first but silenty
276
+ runner = LookbookVisualTester::Runner.new(pattern: args[:name])
277
+ $stdout = File.new('/dev/null', 'w')
278
+ runner.run
279
+ $stdout = STDOUT
280
+ end
281
+
282
+ puts scenario_run.current_path
283
+ end
284
+
285
+ desc 'Run and copy to clipboard first scenario matching the given name'
286
+ task :copy, [:name] => :environment do |_, args|
287
+ require 'lookbook_visual_tester/scenario_finder'
288
+ scenario_run = LookbookVisualTester::ScenarioFinder.call(args[:name])
289
+ unless scenario_run
290
+ puts "No Lookbook previews found matching #{args[:name]}"
291
+ exit 1
292
+ end
293
+
294
+ # Use our runner to capture it (which handles clipboard if enabled)
295
+ runner = LookbookVisualTester::Runner.new(pattern: args[:name])
296
+ runner.run
297
+ end
92
298
  end
metadata CHANGED
@@ -1,30 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lookbook_visual_tester
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Murilo Vasconcelos
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-03-04 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: capybara
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
16
- - - ">="
17
- - !ruby/object:Gem::Version
18
- version: '0'
19
- type: :runtime
20
- prerelease: false
21
- version_requirements: !ruby/object:Gem::Requirement
22
- requirements:
23
- - - ">="
24
- - !ruby/object:Gem::Version
25
- version: '0'
26
- - !ruby/object:Gem::Dependency
27
- name: cuprite
13
+ name: chunky_png
28
14
  requirement: !ruby/object:Gem::Requirement
29
15
  requirements:
30
16
  - - ">="
@@ -52,7 +38,7 @@ dependencies:
52
38
  - !ruby/object:Gem::Version
53
39
  version: '0'
54
40
  - !ruby/object:Gem::Dependency
55
- name: lookbook
41
+ name: ferrum
56
42
  requirement: !ruby/object:Gem::Requirement
57
43
  requirements:
58
44
  - - ">="
@@ -66,7 +52,7 @@ dependencies:
66
52
  - !ruby/object:Gem::Version
67
53
  version: '0'
68
54
  - !ruby/object:Gem::Dependency
69
- name: mini_magick
55
+ name: lookbook
70
56
  requirement: !ruby/object:Gem::Requirement
71
57
  requirements:
72
58
  - - ">="
@@ -93,20 +79,6 @@ dependencies:
93
79
  - - ">="
94
80
  - !ruby/object:Gem::Version
95
81
  version: '0'
96
- - !ruby/object:Gem::Dependency
97
- name: ruby-prof
98
- requirement: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - ">="
101
- - !ruby/object:Gem::Version
102
- version: '0'
103
- type: :runtime
104
- prerelease: false
105
- version_requirements: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - ">="
108
- - !ruby/object:Gem::Version
109
- version: '0'
110
82
  - !ruby/object:Gem::Dependency
111
83
  name: bundler
112
84
  requirement: !ruby/object:Gem::Requirement
@@ -149,6 +121,20 @@ dependencies:
149
121
  - - "~>"
150
122
  - !ruby/object:Gem::Version
151
123
  version: '3.10'
124
+ - !ruby/object:Gem::Dependency
125
+ name: ruby-prof
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
152
138
  description: A Rails gem that captures screenshots of Lookbook component previews,
153
139
  compares them against baseline images, and highlights visual differences to assist
154
140
  in safe refactoring.
@@ -163,26 +149,32 @@ files:
163
149
  - CHANGELOG.md
164
150
  - CODE_OF_CONDUCT.md
165
151
  - README.md
152
+ - RELEASING.md
166
153
  - Rakefile
167
154
  - assets/logo.jpg
168
155
  - lib/lookbook_visual_tester.rb
169
156
  - lib/lookbook_visual_tester/baseline_manager.rb
170
157
  - lib/lookbook_visual_tester/capybara_setup.rb
171
158
  - lib/lookbook_visual_tester/configuration.rb
159
+ - lib/lookbook_visual_tester/driver.rb
160
+ - lib/lookbook_visual_tester/drivers/ferrum_driver.rb
172
161
  - lib/lookbook_visual_tester/image_comparator.rb
162
+ - lib/lookbook_visual_tester/json_output_handler.rb
173
163
  - lib/lookbook_visual_tester/railtie.rb
174
164
  - lib/lookbook_visual_tester/report_generator.rb
165
+ - lib/lookbook_visual_tester/runner.rb
175
166
  - lib/lookbook_visual_tester/scenario_finder.rb
176
167
  - lib/lookbook_visual_tester/scenario_run.rb
177
168
  - lib/lookbook_visual_tester/screenshot_taker.rb
178
169
  - lib/lookbook_visual_tester/service.rb
170
+ - lib/lookbook_visual_tester/services/image_comparator.rb
179
171
  - lib/lookbook_visual_tester/session_manager.rb
180
172
  - lib/lookbook_visual_tester/store.rb
173
+ - lib/lookbook_visual_tester/templates/report.html.erb
181
174
  - lib/lookbook_visual_tester/update_previews.rb
182
175
  - lib/lookbook_visual_tester/version.rb
183
176
  - lib/tasks/lookbook_visual_tester.rake
184
177
  - sig/lookbook_visual_tester.rbs
185
- - tasks/lookbook_visual_tester.rake
186
178
  homepage: https://github.com/muriloime/lookbook_visual_tester
187
179
  licenses:
188
180
  - MIT
@@ -203,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
203
195
  - !ruby/object:Gem::Version
204
196
  version: '0'
205
197
  requirements: []
206
- rubygems_version: 3.6.2
198
+ rubygems_version: 3.6.9
207
199
  specification_version: 4
208
200
  summary: Visual regression testing for Lookbook previews in Rails applications.
209
201
  test_files: []
File without changes