lookbook_visual_tester 0.6.0 → 0.7.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/.env.example +13 -0
- data/CHANGELOG.md +26 -0
- data/README.md +18 -9
- data/Rakefile +53 -0
- data/docs/superpowers/plans/2026-08-09-lookbook-visual-tester-hardening.md +1792 -0
- data/lib/lookbook_visual_tester/browser_discovery.rb +31 -0
- data/lib/lookbook_visual_tester/configuration.rb +11 -4
- data/lib/lookbook_visual_tester/drivers/ferrum_driver.rb +8 -4
- data/lib/lookbook_visual_tester/preview_checker.rb +41 -214
- data/lib/lookbook_visual_tester/railtie.rb +2 -8
- data/lib/lookbook_visual_tester/runner.rb +108 -92
- data/lib/lookbook_visual_tester/server_test_runner.rb +102 -0
- data/lib/lookbook_visual_tester/services/image_trimmer.rb +59 -0
- data/lib/lookbook_visual_tester/update_previews.rb +3 -12
- data/lib/lookbook_visual_tester/version.rb +1 -1
- data/lib/lookbook_visual_tester.rb +4 -0
- data/lib/tasks/lookbook_visual_tester.rake +50 -51
- metadata +38 -51
- data/lib/lookbook_visual_tester/baseline_manager.rb +0 -34
- data/lib/lookbook_visual_tester/capybara_setup.rb +0 -29
- data/lib/lookbook_visual_tester/screenshot_taker.rb +0 -118
- data/lib/lookbook_visual_tester/session_manager.rb +0 -17
|
@@ -38,8 +38,7 @@ namespace :lookbook do
|
|
|
38
38
|
task :list, [:format] => :environment do |_, args|
|
|
39
39
|
require 'lookbook'
|
|
40
40
|
previews = Lookbook.previews.flat_map do |preview|
|
|
41
|
-
|
|
42
|
-
group.map { |scenario| scenario.lookup_path }
|
|
41
|
+
preview.scenarios.map { |scenario| scenario.lookup_path }
|
|
43
42
|
end.sort
|
|
44
43
|
|
|
45
44
|
if args[:format] == 'json'
|
|
@@ -54,8 +53,10 @@ namespace :lookbook do
|
|
|
54
53
|
preview_name = args[:preview_name]
|
|
55
54
|
format = args[:format]
|
|
56
55
|
|
|
56
|
+
json_mode = format == 'json'
|
|
57
|
+
|
|
57
58
|
unless preview_name
|
|
58
|
-
if
|
|
59
|
+
if json_mode
|
|
59
60
|
LookbookVisualTester::JsonOutputHandler.print({ error: 'Please provide a preview name' })
|
|
60
61
|
else
|
|
61
62
|
puts 'Please provide a preview name: rake lookbook:screenshot[Button]'
|
|
@@ -63,21 +64,12 @@ namespace :lookbook do
|
|
|
63
64
|
exit 1
|
|
64
65
|
end
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
runner = LookbookVisualTester::Runner.new(pattern: preview_name)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
original_stdout = $stdout
|
|
71
|
-
$stdout = File.new('/dev/null', 'w') if format == 'json'
|
|
72
|
-
|
|
73
|
-
begin
|
|
74
|
-
results = runner.run
|
|
75
|
-
ensure
|
|
76
|
-
$stdout = original_stdout if format == 'json'
|
|
77
|
-
end
|
|
67
|
+
output = json_mode ? File.open(File::NULL, 'w') : $stdout
|
|
68
|
+
runner = LookbookVisualTester::Runner.new(pattern: preview_name, output: output)
|
|
69
|
+
results = runner.run
|
|
70
|
+
output.close if json_mode
|
|
78
71
|
|
|
79
|
-
if
|
|
80
|
-
# Transform results to simple hash
|
|
72
|
+
if json_mode
|
|
81
73
|
json_results = results.map do |r|
|
|
82
74
|
{
|
|
83
75
|
scenario_name: r.scenario_name,
|
|
@@ -90,9 +82,8 @@ namespace :lookbook do
|
|
|
90
82
|
}
|
|
91
83
|
end
|
|
92
84
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
LookbookVisualTester::JsonOutputHandler.print(output)
|
|
85
|
+
output_payload = json_results.size == 1 ? json_results.first : json_results
|
|
86
|
+
LookbookVisualTester::JsonOutputHandler.print(output_payload)
|
|
96
87
|
else
|
|
97
88
|
print_cli_summary(results)
|
|
98
89
|
end
|
|
@@ -100,21 +91,16 @@ namespace :lookbook do
|
|
|
100
91
|
|
|
101
92
|
desc 'Run visual regression tests for all previews'
|
|
102
93
|
task :test, [:format] => :environment do |_, args|
|
|
103
|
-
runner = LookbookVisualTester::Runner.new(force_update: args[:format] == 'force' || ENV['UPDATE'] == 'true')
|
|
104
|
-
|
|
105
|
-
# Check for ENV var or arg
|
|
106
94
|
json_mode = args[:format] == 'json' || ENV['JSON_OUTPUT'] == 'true'
|
|
95
|
+
output = json_mode ? File.open(File::NULL, 'w') : $stdout
|
|
107
96
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
$stdout = original_stdout if json_mode
|
|
115
|
-
end
|
|
97
|
+
runner = LookbookVisualTester::Runner.new(
|
|
98
|
+
force_update: args[:format] == 'force' || ENV['UPDATE'] == 'true',
|
|
99
|
+
output: output
|
|
100
|
+
)
|
|
101
|
+
results = runner.run
|
|
102
|
+
output.close if json_mode
|
|
116
103
|
|
|
117
|
-
# Save results for retry logic
|
|
118
104
|
if defined?(LookbookVisualTester.config.diff_dir)
|
|
119
105
|
result_file = LookbookVisualTester.config.diff_dir.join('last_run.json')
|
|
120
106
|
FileUtils.mkdir_p(File.dirname(result_file))
|
|
@@ -160,35 +146,45 @@ namespace :lookbook do
|
|
|
160
146
|
end
|
|
161
147
|
end
|
|
162
148
|
|
|
149
|
+
desc 'Start Rails server, run visual tests, then stop the server'
|
|
150
|
+
task server_and_test: :environment do
|
|
151
|
+
LookbookVisualTester::ServerTestRunner.call(
|
|
152
|
+
timeout: ENV.fetch('LOOKBOOK_SERVER_TIMEOUT', '60').to_i,
|
|
153
|
+
test_task: 'lookbook:test',
|
|
154
|
+
log_path: ENV['LOOKBOOK_SERVER_LOG']
|
|
155
|
+
)
|
|
156
|
+
end
|
|
157
|
+
|
|
163
158
|
desc 'Approve a specific preview change (update baseline)'
|
|
164
159
|
task :approve, [:preview_name] => :environment do |_, args|
|
|
165
160
|
preview_name = args[:preview_name]
|
|
166
161
|
unless preview_name
|
|
167
|
-
puts 'Please provide a preview name: rake lookbook:approve[
|
|
162
|
+
puts 'Please provide a preview name: rake "lookbook:approve[ui/button/default]"'
|
|
168
163
|
exit 1
|
|
169
164
|
end
|
|
170
165
|
|
|
166
|
+
normalized = preview_name.tr('/', '_').tr(' ', '_')
|
|
171
167
|
baseline_dir = LookbookVisualTester.config.baseline_dir
|
|
172
168
|
current_dir = LookbookVisualTester.config.current_dir
|
|
173
169
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
170
|
+
candidates = Dir.glob(current_dir.join('**', '*.png')).reject do |f|
|
|
171
|
+
File.basename(f).end_with?('_diff.png')
|
|
172
|
+
end.select do |f|
|
|
173
|
+
base = File.basename(f, '.png')
|
|
174
|
+
base == normalized || base.end_with?("_#{normalized}")
|
|
177
175
|
end
|
|
178
176
|
|
|
179
177
|
if candidates.empty?
|
|
180
|
-
puts "No current runs found matching '#{preview_name}'."
|
|
178
|
+
puts "No current runs found matching '#{preview_name}' in #{current_dir}."
|
|
181
179
|
exit 1
|
|
182
180
|
end
|
|
183
181
|
|
|
184
182
|
candidates.each do |current_file|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
baseline_file = baseline_dir.join(filename)
|
|
189
|
-
FileUtils.mkdir_p(baseline_dir)
|
|
183
|
+
relative = Pathname.new(current_file).relative_path_from(current_dir)
|
|
184
|
+
baseline_file = baseline_dir.join(relative)
|
|
185
|
+
FileUtils.mkdir_p(File.dirname(baseline_file))
|
|
190
186
|
FileUtils.cp(current_file, baseline_file)
|
|
191
|
-
puts "Approved: #{
|
|
187
|
+
puts "Approved: #{relative}"
|
|
192
188
|
end
|
|
193
189
|
end
|
|
194
190
|
|
|
@@ -210,10 +206,15 @@ namespace :lookbook do
|
|
|
210
206
|
|
|
211
207
|
puts "Retrying #{failures.size} failure(s)..."
|
|
212
208
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
209
|
+
json_mode = args[:format] == 'json' || ENV['JSON_OUTPUT'] == 'true'
|
|
210
|
+
output = json_mode ? File.open(File::NULL, 'w') : $stdout
|
|
211
|
+
begin
|
|
212
|
+
failures.each do |failure|
|
|
213
|
+
puts "Retrying #{failure[:scenario_name]}..."
|
|
214
|
+
LookbookVisualTester::Runner.new(pattern: failure[:scenario_name], output: output).run
|
|
215
|
+
end
|
|
216
|
+
ensure
|
|
217
|
+
output.close if json_mode
|
|
217
218
|
end
|
|
218
219
|
end
|
|
219
220
|
|
|
@@ -435,11 +436,9 @@ namespace :lookbook_visual_tester do
|
|
|
435
436
|
if args[:skip_capture].to_s == 'true'
|
|
436
437
|
# Just print existing
|
|
437
438
|
else
|
|
438
|
-
# Run it first
|
|
439
|
-
runner = LookbookVisualTester::Runner.new(pattern: args[:name])
|
|
440
|
-
$stdout = File.new('/dev/null', 'w')
|
|
439
|
+
# Run it first, silently
|
|
440
|
+
runner = LookbookVisualTester::Runner.new(pattern: args[:name], output: File.open(File::NULL, 'w'))
|
|
441
441
|
runner.run
|
|
442
|
-
$stdout = STDOUT
|
|
443
442
|
end
|
|
444
443
|
|
|
445
444
|
puts scenario_run.current_path
|
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.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Murilo Vasconcelos
|
|
@@ -13,114 +13,100 @@ dependencies:
|
|
|
13
13
|
name: benchmark
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "
|
|
16
|
+
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0'
|
|
18
|
+
version: '0.4'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
|
-
- - "
|
|
23
|
+
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '0'
|
|
25
|
+
version: '0.4'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: chunky_png
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
|
-
- - "
|
|
30
|
+
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
32
|
+
version: '1.4'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
39
|
+
version: '1.4'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: concurrent-ruby
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
|
-
- - "
|
|
45
|
-
- !ruby/object:Gem::Version
|
|
46
|
-
version: '0'
|
|
47
|
-
type: :runtime
|
|
48
|
-
prerelease: false
|
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
-
requirements:
|
|
51
|
-
- - ">="
|
|
52
|
-
- !ruby/object:Gem::Version
|
|
53
|
-
version: '0'
|
|
54
|
-
- !ruby/object:Gem::Dependency
|
|
55
|
-
name: cuprite
|
|
56
|
-
requirement: !ruby/object:Gem::Requirement
|
|
57
|
-
requirements:
|
|
58
|
-
- - ">="
|
|
44
|
+
- - "~>"
|
|
59
45
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
46
|
+
version: '1.3'
|
|
61
47
|
type: :runtime
|
|
62
48
|
prerelease: false
|
|
63
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
50
|
requirements:
|
|
65
|
-
- - "
|
|
51
|
+
- - "~>"
|
|
66
52
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '
|
|
53
|
+
version: '1.3'
|
|
68
54
|
- !ruby/object:Gem::Dependency
|
|
69
55
|
name: ferrum
|
|
70
56
|
requirement: !ruby/object:Gem::Requirement
|
|
71
57
|
requirements:
|
|
72
|
-
- - "
|
|
58
|
+
- - "~>"
|
|
73
59
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '0'
|
|
60
|
+
version: '0.16'
|
|
75
61
|
type: :runtime
|
|
76
62
|
prerelease: false
|
|
77
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
64
|
requirements:
|
|
79
|
-
- - "
|
|
65
|
+
- - "~>"
|
|
80
66
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: '0'
|
|
67
|
+
version: '0.16'
|
|
82
68
|
- !ruby/object:Gem::Dependency
|
|
83
69
|
name: lookbook
|
|
84
70
|
requirement: !ruby/object:Gem::Requirement
|
|
85
71
|
requirements:
|
|
86
|
-
- - "
|
|
72
|
+
- - "~>"
|
|
87
73
|
- !ruby/object:Gem::Version
|
|
88
|
-
version: '0'
|
|
74
|
+
version: '2.0'
|
|
89
75
|
type: :runtime
|
|
90
76
|
prerelease: false
|
|
91
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
92
78
|
requirements:
|
|
93
|
-
- - "
|
|
79
|
+
- - "~>"
|
|
94
80
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '0'
|
|
81
|
+
version: '2.0'
|
|
96
82
|
- !ruby/object:Gem::Dependency
|
|
97
83
|
name: rails
|
|
98
84
|
requirement: !ruby/object:Gem::Requirement
|
|
99
85
|
requirements:
|
|
100
|
-
- - "
|
|
86
|
+
- - "~>"
|
|
101
87
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '0'
|
|
88
|
+
version: '7.0'
|
|
103
89
|
type: :runtime
|
|
104
90
|
prerelease: false
|
|
105
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
92
|
requirements:
|
|
107
|
-
- - "
|
|
93
|
+
- - "~>"
|
|
108
94
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: '0'
|
|
95
|
+
version: '7.0'
|
|
110
96
|
- !ruby/object:Gem::Dependency
|
|
111
97
|
name: rainbow
|
|
112
98
|
requirement: !ruby/object:Gem::Requirement
|
|
113
99
|
requirements:
|
|
114
|
-
- - "
|
|
100
|
+
- - "~>"
|
|
115
101
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: '0'
|
|
102
|
+
version: '3.0'
|
|
117
103
|
type: :runtime
|
|
118
104
|
prerelease: false
|
|
119
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
120
106
|
requirements:
|
|
121
|
-
- - "
|
|
107
|
+
- - "~>"
|
|
122
108
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: '0'
|
|
109
|
+
version: '3.0'
|
|
124
110
|
- !ruby/object:Gem::Dependency
|
|
125
111
|
name: bundler
|
|
126
112
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -167,16 +153,16 @@ dependencies:
|
|
|
167
153
|
name: ruby-prof
|
|
168
154
|
requirement: !ruby/object:Gem::Requirement
|
|
169
155
|
requirements:
|
|
170
|
-
- - "
|
|
156
|
+
- - "~>"
|
|
171
157
|
- !ruby/object:Gem::Version
|
|
172
|
-
version: '
|
|
158
|
+
version: '1.7'
|
|
173
159
|
type: :development
|
|
174
160
|
prerelease: false
|
|
175
161
|
version_requirements: !ruby/object:Gem::Requirement
|
|
176
162
|
requirements:
|
|
177
|
-
- - "
|
|
163
|
+
- - "~>"
|
|
178
164
|
- !ruby/object:Gem::Version
|
|
179
|
-
version: '
|
|
165
|
+
version: '1.7'
|
|
180
166
|
description: A Rails gem that captures screenshots of Lookbook component previews,
|
|
181
167
|
compares them against baseline images, and highlights visual differences to assist
|
|
182
168
|
in safe refactoring.
|
|
@@ -187,6 +173,7 @@ extensions: []
|
|
|
187
173
|
extra_rdoc_files: []
|
|
188
174
|
files:
|
|
189
175
|
- ".agent/rules/style_guide.md"
|
|
176
|
+
- ".env.example"
|
|
190
177
|
- ".rubocop.yml"
|
|
191
178
|
- ".ruby-version"
|
|
192
179
|
- CHANGELOG.md
|
|
@@ -195,9 +182,9 @@ files:
|
|
|
195
182
|
- RELEASING.md
|
|
196
183
|
- Rakefile
|
|
197
184
|
- assets/logo.jpg
|
|
185
|
+
- docs/superpowers/plans/2026-08-09-lookbook-visual-tester-hardening.md
|
|
198
186
|
- lib/lookbook_visual_tester.rb
|
|
199
|
-
- lib/lookbook_visual_tester/
|
|
200
|
-
- lib/lookbook_visual_tester/capybara_setup.rb
|
|
187
|
+
- lib/lookbook_visual_tester/browser_discovery.rb
|
|
201
188
|
- lib/lookbook_visual_tester/check_reporter.rb
|
|
202
189
|
- lib/lookbook_visual_tester/configuration.rb
|
|
203
190
|
- lib/lookbook_visual_tester/driver.rb
|
|
@@ -209,10 +196,10 @@ files:
|
|
|
209
196
|
- lib/lookbook_visual_tester/runner.rb
|
|
210
197
|
- lib/lookbook_visual_tester/scenario_finder.rb
|
|
211
198
|
- lib/lookbook_visual_tester/scenario_run.rb
|
|
212
|
-
- lib/lookbook_visual_tester/
|
|
199
|
+
- lib/lookbook_visual_tester/server_test_runner.rb
|
|
213
200
|
- lib/lookbook_visual_tester/service.rb
|
|
214
201
|
- lib/lookbook_visual_tester/services/image_comparator.rb
|
|
215
|
-
- lib/lookbook_visual_tester/
|
|
202
|
+
- lib/lookbook_visual_tester/services/image_trimmer.rb
|
|
216
203
|
- lib/lookbook_visual_tester/store.rb
|
|
217
204
|
- lib/lookbook_visual_tester/templates/preview_check_report.html.tt
|
|
218
205
|
- lib/lookbook_visual_tester/templates/report.html.erb
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
module LookbookVisualTester
|
|
2
|
-
class BaselineManager < Service
|
|
3
|
-
attr_reader :preview_name, :scenario_name
|
|
4
|
-
|
|
5
|
-
def initialize(preview_name:, scenario_name:)
|
|
6
|
-
@preview_name = preview_name
|
|
7
|
-
@scenario_name = scenario_name
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def self.update_baseline_if_approved(preview_name:, scenario_name:)
|
|
11
|
-
new(preview_name:, scenario_name:).update_baseline_if_approved
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def update_baseline_if_approved
|
|
15
|
-
return false unless last_screenshot_path
|
|
16
|
-
|
|
17
|
-
FileUtils.cp(
|
|
18
|
-
last_screenshot_path,
|
|
19
|
-
File.join(LookbookVisualTester.config.baseline_dir, scenario_path)
|
|
20
|
-
)
|
|
21
|
-
true
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
private
|
|
25
|
-
|
|
26
|
-
def last_screenshot_path
|
|
27
|
-
LookbookVisualTester.data[:last_changed_screenshot]&.[](preview_name)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def scenario_path
|
|
31
|
-
"#{preview_name}/#{scenario_name}.png"
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
require 'capybara'
|
|
2
|
-
require 'capybara/cuprite'
|
|
3
|
-
|
|
4
|
-
module LookbookVisualTester
|
|
5
|
-
module CapybaraSetup
|
|
6
|
-
@setup_complete = false
|
|
7
|
-
|
|
8
|
-
def self.setup
|
|
9
|
-
return if @setup_complete
|
|
10
|
-
|
|
11
|
-
Capybara.register_driver :cuprite do |app|
|
|
12
|
-
Capybara::Cuprite::Driver.new(
|
|
13
|
-
app,
|
|
14
|
-
window_size: [1200, 800],
|
|
15
|
-
timeout: 20,
|
|
16
|
-
process_timeout: 20,
|
|
17
|
-
headless: true,
|
|
18
|
-
browser_options: { 'ignore-certificate-errors' => true }
|
|
19
|
-
)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
Capybara.default_driver = :cuprite
|
|
23
|
-
Capybara.default_max_wait_time = 15
|
|
24
|
-
Capybara.server = :puma, { Silent: true }
|
|
25
|
-
|
|
26
|
-
@setup_complete = true
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
require 'lookbook_visual_tester/session_manager'
|
|
2
|
-
require 'lookbook_visual_tester/service'
|
|
3
|
-
|
|
4
|
-
require 'fileutils'
|
|
5
|
-
|
|
6
|
-
module LookbookVisualTester
|
|
7
|
-
class ScreenshotTaker < Service
|
|
8
|
-
attr_reader :scenario_run, :path, :crop, :logger
|
|
9
|
-
|
|
10
|
-
CLIPBOARD = 'clipboard'
|
|
11
|
-
|
|
12
|
-
delegate :preview_url, to: :scenario_run
|
|
13
|
-
|
|
14
|
-
def initialize(scenario_run:,
|
|
15
|
-
path: nil,
|
|
16
|
-
crop: true,
|
|
17
|
-
logger: Rails.logger)
|
|
18
|
-
@scenario_run = scenario_run
|
|
19
|
-
@path = path || File.join(LookbookVisualTester.config.current_dir, scenario_run.filename)
|
|
20
|
-
@crop = crop
|
|
21
|
-
@logger = logger
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def session
|
|
25
|
-
@session ||= SessionManager.instance.session
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def call
|
|
29
|
-
FileUtils.mkdir_p(File.dirname(path))
|
|
30
|
-
|
|
31
|
-
session.visit(preview_url)
|
|
32
|
-
|
|
33
|
-
# Wait for network requests to complete
|
|
34
|
-
# session.driver.network_idle?
|
|
35
|
-
|
|
36
|
-
# # Wait for any loading indicators to disappear
|
|
37
|
-
# begin
|
|
38
|
-
# session.has_no_css?(".loading", wait: 10)
|
|
39
|
-
# rescue StandardError
|
|
40
|
-
# nil
|
|
41
|
-
# end
|
|
42
|
-
if path == CLIPBOARD
|
|
43
|
-
print_and_save_to_clipboard
|
|
44
|
-
else
|
|
45
|
-
save_printscreen
|
|
46
|
-
end
|
|
47
|
-
# Additional wait for any JavaScript animations
|
|
48
|
-
sleep 1
|
|
49
|
-
rescue StandardError => e
|
|
50
|
-
logger.info "Error capturing screenshot for #{preview_url}: #{e.message}"
|
|
51
|
-
raise e
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def save_to_clipboard(path = @path)
|
|
55
|
-
system("xclip -selection clipboard -t image/png -i #{path}")
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
private
|
|
59
|
-
|
|
60
|
-
def print_and_save_to_clipboard
|
|
61
|
-
Tempfile.create(['screenshot', '.png']) do |file|
|
|
62
|
-
save_printscreen(file.path)
|
|
63
|
-
# save_to_clipboard(file.path)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def save_printscreen(path = @path)
|
|
68
|
-
session.save_screenshot(path)
|
|
69
|
-
|
|
70
|
-
system("convert #{path} -trim -bordercolor white -border 10x10 #{path}") if crop
|
|
71
|
-
|
|
72
|
-
if different_from_baseline?(path)
|
|
73
|
-
save_to_clipboard(path) if LookbookVisualTester.config.copy_to_clipboard
|
|
74
|
-
|
|
75
|
-
save_to_last_modified(path)
|
|
76
|
-
save_to_history(path)
|
|
77
|
-
LookbookVisualTester.data[:last_changed_screenshot] ||= {}
|
|
78
|
-
LookbookVisualTester.data[:last_changed_screenshot][scenario_run.preview_name] =
|
|
79
|
-
history_path
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
clean_old_history
|
|
83
|
-
logger.info "Screenshot saved to #{path}"
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def different_from_baseline?(current_path)
|
|
87
|
-
baseline_path = File.join(LookbookVisualTester.config.baseline_dir, scenario_run.filename)
|
|
88
|
-
return true unless File.exist?(baseline_path)
|
|
89
|
-
|
|
90
|
-
system("compare -metric AE #{current_path} #{baseline_path} null: 2>&1") != '0'
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def clean_old_history(keep: LookbookVisualTester.config.history_keep_last_n)
|
|
94
|
-
history_files = Dir.glob(File.join(LookbookVisualTester.config.history_dir,
|
|
95
|
-
"#{scenario_run.preview_name}_*.png"))
|
|
96
|
-
history_files.sort_by! { |f| File.mtime(f) }
|
|
97
|
-
history_files[0...-keep].each { |f| File.delete(f) }
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def save_to_history(current_path)
|
|
101
|
-
@history_path = File.join(
|
|
102
|
-
LookbookVisualTester.config.history_dir,
|
|
103
|
-
scenario_run.timestamp_filename
|
|
104
|
-
)
|
|
105
|
-
FileUtils.cp(current_path, history_path)
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def save_to_last_modified(current_path)
|
|
109
|
-
last_path = File.join(
|
|
110
|
-
LookbookVisualTester.config.current_dir,
|
|
111
|
-
'last.png'
|
|
112
|
-
)
|
|
113
|
-
FileUtils.cp(current_path, last_path)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
attr_reader :history_path
|
|
117
|
-
end
|
|
118
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Setup Capybara
|
|
2
|
-
require 'singleton'
|
|
3
|
-
require 'capybara'
|
|
4
|
-
require 'capybara/cuprite'
|
|
5
|
-
|
|
6
|
-
module LookbookVisualTester
|
|
7
|
-
class SessionManager
|
|
8
|
-
include Singleton
|
|
9
|
-
|
|
10
|
-
def initialize
|
|
11
|
-
CapybaraSetup.setup
|
|
12
|
-
@session = Capybara::Session.new(:cuprite)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
attr_reader :session
|
|
16
|
-
end
|
|
17
|
-
end
|