lookbook_visual_tester 0.1.4 → 0.1.5
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/.rubocop.yml +206 -5
- data/CHANGELOG.md +26 -0
- data/README.md +29 -1
- data/Rakefile +3 -3
- data/assets/logo.jpg +0 -0
- data/lib/lookbook_visual_tester/baseline_manager.rb +34 -0
- data/lib/lookbook_visual_tester/capybara_setup.rb +17 -3
- data/lib/lookbook_visual_tester/configuration.rb +26 -8
- data/lib/lookbook_visual_tester/image_comparator.rb +3 -3
- data/lib/lookbook_visual_tester/railtie.rb +29 -6
- data/lib/lookbook_visual_tester/report_generator.rb +16 -16
- data/lib/lookbook_visual_tester/scenario_finder.rb +34 -0
- data/lib/lookbook_visual_tester/scenario_run.rb +15 -4
- data/lib/lookbook_visual_tester/screenshot_taker.rb +78 -34
- data/lib/lookbook_visual_tester/service.rb +11 -0
- data/lib/lookbook_visual_tester/session_manager.rb +3 -3
- data/lib/lookbook_visual_tester/store.rb +45 -0
- data/lib/lookbook_visual_tester/update_previews.rb +15 -17
- data/lib/lookbook_visual_tester/version.rb +1 -1
- data/lib/lookbook_visual_tester.rb +5 -2
- data/lib/tasks/lookbook_visual_tester.rake +25 -32
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a29c54b1403f4faeaea71ef267faf9aba07d033c3c0bfc79617c0bf0cf77a490
|
4
|
+
data.tar.gz: 2d8cb90e20f1ad2dd92ca6c677bb4e8185c62a16b0b070721551e789575de219
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8328f592622c337d11dca1c10811834e85f0a00cecc92eeb6f6a62c04cf2e1df50e9eed9700ac1b11041e39373710c371454c19d71db0b17c95b2790a67ebaeb
|
7
|
+
data.tar.gz: 3dba60c0c0d2867b185e45c53d68569b29e87c4ac57bf649767aa6c17ada5405d1cf0f493c6b96d75a1755405c70d050b165a72fe58a9f39648609ea703adecb
|
data/.rubocop.yml
CHANGED
@@ -1,8 +1,209 @@
|
|
1
1
|
AllCops:
|
2
|
-
|
2
|
+
Exclude:
|
3
|
+
- "bin/**/*"
|
4
|
+
- "db/**/*"
|
5
|
+
- "config/**/*"
|
6
|
+
- "node_modules/**/*"
|
7
|
+
- "vendor/**/*"
|
8
|
+
- "app/assets/**/*"
|
9
|
+
- "db/schema.rb"
|
10
|
+
TargetRubyVersion: 3.2.1
|
11
|
+
DisplayCopNames: true
|
12
|
+
NewCops: enable
|
3
13
|
|
4
|
-
|
5
|
-
|
14
|
+
Metrics/BlockLength:
|
15
|
+
Exclude:
|
16
|
+
- "spec/**/*"
|
17
|
+
- "lib/tasks/**/*"
|
6
18
|
|
7
|
-
Style/
|
8
|
-
|
19
|
+
Style/Documentation:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Style/HashEachMethods:
|
23
|
+
Enabled: true
|
24
|
+
|
25
|
+
Style/FormatString:
|
26
|
+
Exclude:
|
27
|
+
- "app/components/**/*"
|
28
|
+
Style/HashTransformKeys:
|
29
|
+
Enabled: true
|
30
|
+
|
31
|
+
Style/HashTransformValues:
|
32
|
+
Enabled: true
|
33
|
+
|
34
|
+
Style/NumericPredicate:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Layout/EmptyLinesAroundAttributeAccessor:
|
38
|
+
Enabled: true
|
39
|
+
|
40
|
+
Layout/LineLength:
|
41
|
+
Max: 100
|
42
|
+
AllowedPatterns: ['(\A|\s)#']
|
43
|
+
Exclude:
|
44
|
+
- "spec/**/*"
|
45
|
+
|
46
|
+
Layout/SpaceAroundMethodCallOperator:
|
47
|
+
Enabled: true
|
48
|
+
|
49
|
+
Layout/SpaceInsideHashLiteralBraces:
|
50
|
+
Enabled: false
|
51
|
+
|
52
|
+
Style/FrozenStringLiteralComment:
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
Metrics/AbcSize:
|
56
|
+
Max: 21
|
57
|
+
|
58
|
+
Style/AsciiComments:
|
59
|
+
Enabled: false
|
60
|
+
|
61
|
+
Metrics/MethodLength:
|
62
|
+
Max: 15
|
63
|
+
|
64
|
+
Bundler/OrderedGems:
|
65
|
+
Enabled: true
|
66
|
+
|
67
|
+
Style/ClassAndModuleChildren:
|
68
|
+
Enabled: false
|
69
|
+
|
70
|
+
Style/MethodCallWithoutArgsParentheses:
|
71
|
+
Enabled: false
|
72
|
+
|
73
|
+
Style/ExponentialNotation:
|
74
|
+
Enabled: true
|
75
|
+
|
76
|
+
Style/SlicingWithRange:
|
77
|
+
Enabled: true
|
78
|
+
|
79
|
+
Style/RedundantRegexpCharacterClass:
|
80
|
+
Enabled: true
|
81
|
+
|
82
|
+
Style/RedundantRegexpEscape:
|
83
|
+
Enabled: true
|
84
|
+
|
85
|
+
Style/AccessorGrouping:
|
86
|
+
Enabled: true
|
87
|
+
|
88
|
+
Style/ArrayCoercion:
|
89
|
+
Enabled: true
|
90
|
+
|
91
|
+
Style/BisectedAttrAccessor:
|
92
|
+
Enabled: false
|
93
|
+
|
94
|
+
Style/CaseLikeIf:
|
95
|
+
Enabled: true
|
96
|
+
|
97
|
+
Style/HashAsLastArrayItem:
|
98
|
+
Enabled: true
|
99
|
+
|
100
|
+
Style/HashLikeCase:
|
101
|
+
Enabled: false
|
102
|
+
|
103
|
+
Style/Lambda:
|
104
|
+
Enabled: false
|
105
|
+
|
106
|
+
Style/RedundantAssignment:
|
107
|
+
Enabled: true
|
108
|
+
|
109
|
+
Style/RedundantFetchBlock:
|
110
|
+
Enabled: true
|
111
|
+
|
112
|
+
Style/RedundantFileExtensionInRequire:
|
113
|
+
Enabled: true
|
114
|
+
|
115
|
+
Lint/StructNewOverride:
|
116
|
+
Enabled: true
|
117
|
+
|
118
|
+
Lint/AmbiguousBlockAssociation:
|
119
|
+
Enabled: false
|
120
|
+
|
121
|
+
Lint/RaiseException:
|
122
|
+
Enabled: true
|
123
|
+
|
124
|
+
Lint/DeprecatedOpenSSLConstant:
|
125
|
+
Enabled: true
|
126
|
+
|
127
|
+
Lint/MixedRegexpCaptureTypes:
|
128
|
+
Enabled: true
|
129
|
+
|
130
|
+
Lint/DuplicateElsifCondition:
|
131
|
+
Enabled: true
|
132
|
+
|
133
|
+
Layout/BeginEndAlignment:
|
134
|
+
Enabled: true
|
135
|
+
|
136
|
+
Lint/BinaryOperatorWithIdenticalOperands:
|
137
|
+
Enabled: true
|
138
|
+
|
139
|
+
Lint/ConstantDefinitionInBlock: # (new in 0.91)
|
140
|
+
Enabled: true
|
141
|
+
|
142
|
+
Lint/DuplicateRequire: # (new in 0.90)
|
143
|
+
Enabled: true
|
144
|
+
|
145
|
+
Lint/DuplicateRescueException: # (new in 0.89)
|
146
|
+
Enabled: true
|
147
|
+
|
148
|
+
Lint/EmptyConditionalBody: # (new in 0.89)
|
149
|
+
Enabled: true
|
150
|
+
|
151
|
+
Lint/EmptyFile: # (new in 0.90)
|
152
|
+
Enabled: true
|
153
|
+
|
154
|
+
Lint/FloatComparison: # (new in 0.89)
|
155
|
+
Enabled: true
|
156
|
+
|
157
|
+
Lint/IdentityComparison:
|
158
|
+
Enabled: true
|
159
|
+
|
160
|
+
Lint/MissingSuper: # (new in 0.89)
|
161
|
+
Enabled: true
|
162
|
+
|
163
|
+
Lint/OutOfRangeRegexpRef: # (new in 0.89)
|
164
|
+
Enabled: true
|
165
|
+
|
166
|
+
Lint/SelfAssignment: # (new in 0.89)
|
167
|
+
Enabled: true
|
168
|
+
|
169
|
+
Lint/TopLevelReturnWithArgument: # (new in 0.89)
|
170
|
+
Enabled: true
|
171
|
+
|
172
|
+
Lint/TrailingCommaInAttributeDeclaration: # (new in 0.90)
|
173
|
+
Enabled: true
|
174
|
+
|
175
|
+
Lint/UnreachableLoop: # (new in 0.89)
|
176
|
+
Enabled: true
|
177
|
+
|
178
|
+
Lint/UselessMethodDefinition: # (new in 0.90)
|
179
|
+
Enabled: true
|
180
|
+
|
181
|
+
Lint/UselessTimes: # (new in 0.91)
|
182
|
+
Enabled: true
|
183
|
+
|
184
|
+
Style/CombinableLoops:
|
185
|
+
Enabled: true
|
186
|
+
|
187
|
+
Style/ExplicitBlockArgument: # (new in 0.89)
|
188
|
+
Enabled: true
|
189
|
+
|
190
|
+
Style/GlobalStdStream: # (new in 0.89)
|
191
|
+
Enabled: true
|
192
|
+
|
193
|
+
Style/KeywordParametersOrder: # (new in 0.90)
|
194
|
+
Enabled: true
|
195
|
+
|
196
|
+
Style/OptionalBooleanParameter: # (new in 0.89)
|
197
|
+
Enabled: true
|
198
|
+
|
199
|
+
Style/RedundantSelfAssignment: # (new in 0.90)
|
200
|
+
Enabled: true
|
201
|
+
|
202
|
+
Style/SingleArgumentDig: # (new in 0.89)
|
203
|
+
Enabled: true
|
204
|
+
|
205
|
+
Style/SoleNestedConditional: # (new in 0.89)
|
206
|
+
Enabled: true
|
207
|
+
|
208
|
+
Style/StringConcatenation:
|
209
|
+
Enabled: true
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,31 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
Here is the changelog for version **0.1.5** of *LookbookVisualTester*:
|
4
|
+
|
5
|
+
---
|
6
|
+
|
7
|
+
# Changelog
|
8
|
+
|
9
|
+
## [0.1.5] - 2025-02-26
|
10
|
+
|
11
|
+
### ✨ New Features & Improvements
|
12
|
+
|
13
|
+
- **State Management & History Tracking**
|
14
|
+
- Introduced a **state store** with the new `Store` class for better history management.
|
15
|
+
- Implemented file history tracking, enabling more efficient change monitoring.
|
16
|
+
|
17
|
+
- **Component Refactoring**
|
18
|
+
- Improved **state and history handling** in `LookbookVisualTester`.
|
19
|
+
- Updated `ScreenshotTaker` signature for better integration with other system components.
|
20
|
+
- Refactored `CapybaraSetup` to enhance test environment flexibility.
|
21
|
+
- `UpdatePreviews` now inherits from `Service`, standardizing code structure.
|
22
|
+
- Improved **clipboard functionality**, allowing better manipulation and copying of screenshots.
|
23
|
+
|
24
|
+
- **Testing & Code Quality Enhancements**
|
25
|
+
- Added new unit tests for `ScreenshotTaker` and `UpdatePreviews`.
|
26
|
+
- Refactored logging and scenario handling to improve maintainability.
|
27
|
+
|
28
|
+
|
3
29
|
## [0.1.4] - 2025-02-20
|
4
30
|
### Added
|
5
31
|
- Extracted `n_threads` configuration for improved concurrency.
|
data/README.md
CHANGED
@@ -1,9 +1,30 @@
|
|
1
|
-
|
1
|
+
<img src="./assets/logo.jpg" alt="Logo" width="300" />
|
2
|
+
|
3
|
+
# Lookbook Visual Tester
|
2
4
|
|
3
5
|
This gem was built to serve as a lookbook regression tester, getting prints from previews, comparing them and generating a report of differences.
|
4
6
|
|
7
|
+
|
8
|
+
### Features
|
9
|
+
|
10
|
+
- Perform visual regression testing on changes.
|
11
|
+
- Integrate seamlessly with your Lookbook previews.
|
12
|
+
- Automatically generate image differences.
|
13
|
+
- Simplify debugging and quality checks.
|
14
|
+
- Very useful for AI coding (e.g. aider, etc.)
|
15
|
+
|
5
16
|
## Installation
|
6
17
|
|
18
|
+
Tested on linux. A couple of changes to work on mac, etc. You will need tools like xclip, imagemagick
|
19
|
+
|
20
|
+
For Ubuntu-based systems, install the necessary dependencies by running:
|
21
|
+
|
22
|
+
```bash
|
23
|
+
sudo apt-get install xclip imagemagick
|
24
|
+
```
|
25
|
+
|
26
|
+
|
27
|
+
|
7
28
|
Install the gem and add to the application's Gemfile by executing:
|
8
29
|
|
9
30
|
```bash
|
@@ -18,8 +39,13 @@ gem install lookbook_visual_tester
|
|
18
39
|
|
19
40
|
## Usage
|
20
41
|
|
42
|
+
To run everything
|
21
43
|
bundle exec rake lookbook_visual_tester:run LOOKBOOK_HOST=https://localhost:5000
|
22
44
|
|
45
|
+
### Features
|
46
|
+
|
47
|
+
When installed this gem gets your changes and generates
|
48
|
+
|
23
49
|
## Development
|
24
50
|
|
25
51
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -33,6 +59,8 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/murilo
|
|
33
59
|
|
34
60
|
## Deployment
|
35
61
|
|
62
|
+
Generate artifacts for changelog :
|
63
|
+
|
36
64
|
`git log --pretty=format:"%h %ad | %s [%an]" --date=short --no-merges --name-only | xclip -selection clipboard`
|
37
65
|
|
38
66
|
Update changelog
|
data/Rakefile
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'minitest/test_task'
|
5
5
|
|
6
6
|
Minitest::TestTask.create
|
7
7
|
|
8
|
-
require
|
8
|
+
require 'rubocop/rake_task'
|
9
9
|
|
10
10
|
RuboCop::RakeTask.new
|
11
11
|
|
data/assets/logo.jpg
ADDED
Binary file
|
@@ -0,0 +1,34 @@
|
|
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,15 +1,29 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'capybara'
|
2
|
+
require 'capybara/cuprite'
|
3
3
|
|
4
4
|
module LookbookVisualTester
|
5
5
|
module CapybaraSetup
|
6
|
+
@setup_complete = false
|
7
|
+
|
6
8
|
def self.setup
|
9
|
+
return if @setup_complete
|
10
|
+
|
7
11
|
Capybara.register_driver :cuprite do |app|
|
8
|
-
Capybara::Cuprite::Driver.new(
|
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
|
+
)
|
9
20
|
end
|
10
21
|
|
11
22
|
Capybara.default_driver = :cuprite
|
23
|
+
Capybara.default_max_wait_time = 15
|
12
24
|
Capybara.server = :puma, { Silent: true }
|
25
|
+
|
26
|
+
@setup_complete = true
|
13
27
|
end
|
14
28
|
end
|
15
29
|
end
|
@@ -1,20 +1,38 @@
|
|
1
1
|
module LookbookVisualTester
|
2
2
|
class Configuration
|
3
|
-
attr_accessor :base_path, :
|
3
|
+
attr_accessor :base_path, :lookbook_host,
|
4
|
+
:baseline_dir, :current_dir, :diff_dir, :history_dir,
|
5
|
+
:history_keep_last_n, :threads, :copy_to_clipboard
|
4
6
|
|
5
7
|
DEFAULT_THREADS = 4
|
6
8
|
|
7
9
|
def initialize
|
8
|
-
@base_path = Rails.
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
@base_path = if defined?(Rails) && Rails.respond_to?(:env) && Rails.env.test?
|
11
|
+
Pathname.new(Dir.pwd).join('spec/visual_screenshots')
|
12
|
+
elsif defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
13
|
+
Rails.root.join('spec/visual_screenshots')
|
14
|
+
else
|
15
|
+
# Fallback for non-Rails environments
|
16
|
+
Pathname.new(Dir.pwd).join('spec/visual_screenshots')
|
17
|
+
end
|
18
|
+
@baseline_dir = @base_path.join('baseline')
|
19
|
+
@current_dir = @base_path.join('current_run')
|
20
|
+
@diff_dir = @base_path.join('diff')
|
21
|
+
@history_dir = @base_path.join('history')
|
12
22
|
@threads = DEFAULT_THREADS
|
23
|
+
@history_keep_last_n = 5
|
24
|
+
@copy_to_clipboard = true
|
13
25
|
|
14
|
-
@
|
26
|
+
@lookbook_host = ENV.fetch('LOOKBOOK_HOST', 'https://localhost:5000')
|
27
|
+
end
|
28
|
+
|
29
|
+
class << self
|
30
|
+
def config
|
31
|
+
@config ||= new
|
32
|
+
end
|
15
33
|
|
16
|
-
|
17
|
-
|
34
|
+
def configure
|
35
|
+
yield(config)
|
18
36
|
end
|
19
37
|
end
|
20
38
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'lookbook_visual_tester/configuration'
|
2
2
|
|
3
3
|
module LookbookVisualTester
|
4
4
|
class ImageComparator
|
@@ -22,7 +22,7 @@ module LookbookVisualTester
|
|
22
22
|
current_image = MiniMagick::Image.open(current_path)
|
23
23
|
|
24
24
|
unless baseline_image.dimensions == current_image.dimensions
|
25
|
-
puts
|
25
|
+
puts ' Image dimensions do not match. Skipping comparison.'
|
26
26
|
return
|
27
27
|
end
|
28
28
|
|
@@ -34,7 +34,7 @@ module LookbookVisualTester
|
|
34
34
|
if distortion > 0
|
35
35
|
puts " Differences found! Diff image saved to #{diff_path}"
|
36
36
|
else
|
37
|
-
puts
|
37
|
+
puts ' No differences detected.'
|
38
38
|
File.delete(diff_path) if diff_path.exist?
|
39
39
|
end
|
40
40
|
rescue StandardError => e
|
@@ -1,18 +1,41 @@
|
|
1
1
|
# lib/lookbook_visual_tester/railtie.rb
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'lookbook_visual_tester/capybara_setup'
|
4
|
+
require 'lookbook_visual_tester/update_previews'
|
5
5
|
|
6
6
|
module LookbookVisualTester
|
7
7
|
class Railtie < ::Rails::Railtie
|
8
8
|
rake_tasks do
|
9
|
-
load
|
9
|
+
load 'tasks/lookbook_visual_tester.rake'
|
10
10
|
end
|
11
11
|
|
12
|
-
initializer
|
13
|
-
|
12
|
+
initializer 'LookbookVisualTester.lookbook_after_change' do |_app|
|
13
|
+
Rails.logger.info "LookbookVisualTester initialized with host: #{LookbookVisualTester.config.lookbook_host}"
|
14
14
|
Lookbook.after_change do |app, changes|
|
15
|
-
|
15
|
+
# get hash of content of modified files to see if has changed
|
16
|
+
modified = changes[:modified]
|
17
|
+
my_hash = modified.sort.map { |f| File.read(f) }.hash
|
18
|
+
|
19
|
+
lock_file = Rails.root.join('tmp', 'lookbook_visual_tester.lock')
|
20
|
+
Rails.logger.info ">>> LookbookVisualTester: No changes detected in #{LookbookVisualTester.data}"
|
21
|
+
|
22
|
+
# Rails.logger.info ">>> LookbookVisualTester: Stack trace: #{caller.join("\n")}"
|
23
|
+
Rails.logger.info ">>> LookbookVisualTester: Changes in #{Rails.cache.instance_variable_get(:@data).keys.inspect}"
|
24
|
+
File.open(lock_file, 'w') do |file|
|
25
|
+
if file.flock(File::LOCK_EX | File::LOCK_NB)
|
26
|
+
if LookbookVisualTester.data[:last_hash] == my_hash
|
27
|
+
Rails.logger.info 'LookbookVisualTester: No changes detected in Lookbook'
|
28
|
+
else
|
29
|
+
LookbookVisualTester.data[:last_hash] = my_hash
|
30
|
+
Rails.logger.info "LookbookVisualTester: Running UpdatePreviews, updattin to #{LookbookVisualTester.data.inspect}"
|
31
|
+
LookbookVisualTester::UpdatePreviews.call(changes)
|
32
|
+
end
|
33
|
+
file.flock(File::LOCK_UN)
|
34
|
+
Rails.logger.info 'LookbookVisualTester: UpdatePreviews File unlocked.'
|
35
|
+
else
|
36
|
+
Rails.logger.info 'LookbookVisualTester: UpdatePreviews already running, skipping this call.'
|
37
|
+
end
|
38
|
+
end
|
16
39
|
end
|
17
40
|
end
|
18
41
|
end
|
@@ -7,29 +7,29 @@ module LookbookVisualTester
|
|
7
7
|
[config.baseline_dir, config.current_dir, config.diff_dir, config.base_path]
|
8
8
|
end
|
9
9
|
|
10
|
-
@report_path = base_path.join(
|
10
|
+
@report_path = base_path.join('report.html')
|
11
11
|
end
|
12
12
|
|
13
13
|
def generate
|
14
|
-
File.open(report_path,
|
15
|
-
file.puts
|
14
|
+
File.open(report_path, 'w') do |file|
|
15
|
+
file.puts '<!DOCTYPE html>'
|
16
16
|
file.puts "<html lang='en'>"
|
17
17
|
file.puts "<head><meta charset='UTF-8'><title>Visual Regression Report</title></head>"
|
18
|
-
file.puts
|
19
|
-
file.puts
|
20
|
-
file.puts
|
18
|
+
file.puts '<body>'
|
19
|
+
file.puts '<h1>Visual Regression Report</h1>'
|
20
|
+
file.puts '<ul>'
|
21
21
|
|
22
|
-
diff_files = Dir.glob(diff_dir.join(
|
22
|
+
diff_files = Dir.glob(diff_dir.join('*_diff.png'))
|
23
23
|
diff_files.each do |diff_file|
|
24
24
|
filename = File.basename(diff_file)
|
25
25
|
# Extract preview and scenario names
|
26
|
-
preview_scenario = filename.sub(
|
27
|
-
preview, scenario = preview_scenario.split(
|
26
|
+
preview_scenario = filename.sub('_diff.png', '')
|
27
|
+
preview, scenario = preview_scenario.split('_', 2)
|
28
28
|
|
29
29
|
baseline_image = baseline_dir.join("#{preview}_#{scenario}.png")
|
30
30
|
current_image = current_dir.join("#{preview}_#{scenario}.png")
|
31
31
|
|
32
|
-
file.puts
|
32
|
+
file.puts '<li>'
|
33
33
|
file.puts "<h2>#{preview.titleize} - #{scenario.titleize}</h2>"
|
34
34
|
file.puts "<div style='display: flex; gap: 10px;'>"
|
35
35
|
|
@@ -40,15 +40,15 @@ module LookbookVisualTester
|
|
40
40
|
file.puts "<div><h3>Baseline</h3><img src='#{baseline_image_path.relative_path_from(base_path)}' alt='Baseline'></div>"
|
41
41
|
file.puts "<div><h3>Current</h3><img src='#{current_image_path.relative_path_from(base_path)}' alt='Current'></div>"
|
42
42
|
file.puts "<div><h3>Diff</h3><img src='#{diff_file_path.relative_path_from(base_path)}' alt='Diff'></div>"
|
43
|
-
file.puts
|
44
|
-
file.puts
|
43
|
+
file.puts '</div>'
|
44
|
+
file.puts '</li>'
|
45
45
|
end
|
46
46
|
|
47
|
-
file.puts
|
47
|
+
file.puts '<li>No differences found!</li>' if diff_files.empty?
|
48
48
|
|
49
|
-
file.puts
|
50
|
-
file.puts
|
51
|
-
file.puts
|
49
|
+
file.puts '</ul>'
|
50
|
+
file.puts '</body>'
|
51
|
+
file.puts '</html>'
|
52
52
|
end
|
53
53
|
|
54
54
|
@report_path
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'lookbook_visual_tester/service'
|
2
|
+
require 'lookbook_visual_tester/scenario_run'
|
3
|
+
|
4
|
+
module LookbookVisualTester
|
5
|
+
class ScenarioFinder < Service
|
6
|
+
attr_reader :fuzze, :search, :previews
|
7
|
+
|
8
|
+
def initialize(search, fuzzy: true, previews: Lookbook.previews)
|
9
|
+
@search = search
|
10
|
+
@fuzzy = fuzzy
|
11
|
+
@previews = previews
|
12
|
+
end
|
13
|
+
|
14
|
+
def regex
|
15
|
+
@regex = Regexp.new(search.chars.join('.*'), Regexp::IGNORECASE)
|
16
|
+
end
|
17
|
+
|
18
|
+
def matched_previews
|
19
|
+
@matched_previews ||= previews.select { |preview| regex.match?(preview.name.downcase) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def call
|
23
|
+
return nil if search.nil? || search == '' || previews.empty?
|
24
|
+
|
25
|
+
previews.each do |preview|
|
26
|
+
preview.scenarios.each do |scenario|
|
27
|
+
return ScenarioRun.new(scenario) if scenario.name.downcase.include?(search.downcase)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'lookbook_visual_tester/configuration'
|
2
2
|
|
3
3
|
module LookbookVisualTester
|
4
4
|
class ScenarioRun
|
@@ -19,8 +19,19 @@ module LookbookVisualTester
|
|
19
19
|
scenario.name.underscore
|
20
20
|
end
|
21
21
|
|
22
|
+
def name
|
23
|
+
"#{preview_name}_#{scenario_name}"
|
24
|
+
end
|
25
|
+
|
22
26
|
def filename
|
23
|
-
"#{
|
27
|
+
"#{name}.png"
|
28
|
+
end
|
29
|
+
|
30
|
+
def timestamp_filename
|
31
|
+
@timestamp_filename ||= begin
|
32
|
+
timestamp = Time.now.strftime('%Y%m%d-%H%M%S')
|
33
|
+
"#{name}_#{timestamp}.png"
|
34
|
+
end
|
24
35
|
end
|
25
36
|
|
26
37
|
def diff_filename
|
@@ -37,8 +48,8 @@ module LookbookVisualTester
|
|
37
48
|
|
38
49
|
def preview_url
|
39
50
|
Lookbook::Engine.routes.url_helpers.lookbook_preview_url(
|
40
|
-
path: preview.lookup_path +
|
41
|
-
host: LookbookVisualTester.config.
|
51
|
+
path: preview.lookup_path + '/' + scenario.name,
|
52
|
+
host: LookbookVisualTester.config.lookbook_host
|
42
53
|
)
|
43
54
|
end
|
44
55
|
end
|
@@ -1,31 +1,31 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
|
1
|
+
require 'lookbook_visual_tester/session_manager'
|
2
|
+
require 'lookbook_visual_tester/service'
|
3
|
+
|
4
|
+
require 'fileutils'
|
4
5
|
|
5
6
|
module LookbookVisualTester
|
6
|
-
class ScreenshotTaker
|
7
|
-
attr_reader :
|
8
|
-
|
9
|
-
CLIPBOARD =
|
10
|
-
|
11
|
-
def initialize(logger: Kernel)
|
12
|
-
Capybara.register_driver :cuprite do |app|
|
13
|
-
Capybara::Cuprite::Driver.new(
|
14
|
-
app,
|
15
|
-
window_size: [1400, 1400],
|
16
|
-
browser_options: { "ignore-certificate-errors" => nil },
|
17
|
-
timeout: 20,
|
18
|
-
process_timeout: 20
|
19
|
-
)
|
20
|
-
end
|
7
|
+
class ScreenshotTaker < Service
|
8
|
+
attr_reader :scenario_run, :path, :crop, :logger
|
9
|
+
|
10
|
+
CLIPBOARD = 'clipboard'
|
21
11
|
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
25
21
|
@logger = logger
|
26
22
|
end
|
27
23
|
|
28
|
-
def
|
24
|
+
def session
|
25
|
+
@session ||= SessionManager.instance.session
|
26
|
+
end
|
27
|
+
|
28
|
+
def call
|
29
29
|
FileUtils.mkdir_p(File.dirname(path))
|
30
30
|
|
31
31
|
session.visit(preview_url)
|
@@ -40,35 +40,79 @@ module LookbookVisualTester
|
|
40
40
|
# nil
|
41
41
|
# end
|
42
42
|
if path == CLIPBOARD
|
43
|
-
|
43
|
+
print_and_save_to_clipboard
|
44
44
|
else
|
45
|
-
save_printscreen
|
45
|
+
save_printscreen
|
46
46
|
end
|
47
47
|
# Additional wait for any JavaScript animations
|
48
48
|
sleep 1
|
49
49
|
rescue StandardError => e
|
50
|
-
logger.
|
50
|
+
logger.info "Error capturing screenshot for #{preview_url}: #{e.message}"
|
51
51
|
raise e
|
52
52
|
end
|
53
53
|
|
54
|
-
|
54
|
+
def save_to_clipboard(path = @path)
|
55
|
+
system("xclip -selection clipboard -t image/png -i #{path}")
|
56
|
+
end
|
55
57
|
|
56
|
-
|
57
|
-
Tempfile.create(["screenshot", ".png"]) do |file|
|
58
|
-
session.save_screenshot(file.path, crop: crop)
|
58
|
+
private
|
59
59
|
|
60
|
-
|
61
|
-
|
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)
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
65
|
-
def save_printscreen(path
|
67
|
+
def save_printscreen(path = @path)
|
66
68
|
session.save_screenshot(path)
|
67
69
|
|
68
|
-
# remove white space
|
69
70
|
system("convert #{path} -trim -bordercolor white -border 10x10 #{path}") if crop
|
70
71
|
|
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) }
|
72
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
|
73
117
|
end
|
74
118
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module LookbookVisualTester
|
2
|
+
class Store
|
3
|
+
attr_accessor :stored_hash
|
4
|
+
|
5
|
+
HASH_KEY = 'lookbook_visual_tester:stored_hash'
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@stored_hash = Rails.cache.read(HASH_KEY) || {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
@stored_hash[key.to_s]
|
13
|
+
end
|
14
|
+
|
15
|
+
def []=(key, value)
|
16
|
+
save(key.to_s, value)
|
17
|
+
end
|
18
|
+
|
19
|
+
def save(key, value)
|
20
|
+
@stored_hash[key.to_s] = value
|
21
|
+
Rails.cache.write(HASH_KEY, @stored_hash)
|
22
|
+
end
|
23
|
+
|
24
|
+
# pretty inspect of the object
|
25
|
+
def inspect
|
26
|
+
"#<#{self.class.name}
|
27
|
+
stored_hash: #{stored_hash}>"
|
28
|
+
end
|
29
|
+
|
30
|
+
class << self
|
31
|
+
def data
|
32
|
+
@data ||= new
|
33
|
+
# @data ||= new
|
34
|
+
end
|
35
|
+
|
36
|
+
def dataset
|
37
|
+
yield(data)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.data
|
43
|
+
@data ||= Store.new
|
44
|
+
end
|
45
|
+
end
|
@@ -1,20 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
def self.call(...)
|
4
|
-
new(...).call
|
5
|
-
end
|
1
|
+
require_relative 'service'
|
2
|
+
require_relative 'screenshot_taker'
|
6
3
|
|
7
|
-
|
4
|
+
module LookbookVisualTester
|
5
|
+
class UpdatePreviews < Service
|
6
|
+
attr_reader :changes
|
8
7
|
|
9
|
-
def initialize(
|
10
|
-
@app = app
|
8
|
+
def initialize(changes)
|
11
9
|
@changes = changes[:modified]
|
12
10
|
@changes_hash = changes
|
13
11
|
end
|
14
12
|
|
15
13
|
def update_app_data
|
16
|
-
|
17
|
-
|
14
|
+
LookbookVisualTester.data[:last_changed_files] = changes.presence || []
|
15
|
+
LookbookVisualTester.data[:last_changed_previews] = previews
|
18
16
|
end
|
19
17
|
|
20
18
|
def call
|
@@ -34,7 +32,7 @@ module LookbookVisualTester
|
|
34
32
|
end
|
35
33
|
|
36
34
|
def process_change?(change)
|
37
|
-
change.to_s.downcase.include?(
|
35
|
+
change.to_s.downcase.include?('preview.rb') || change.to_s.downcase.match?(/component\.(html|haml|rb|erb)/)
|
38
36
|
end
|
39
37
|
|
40
38
|
def should_process?
|
@@ -44,24 +42,24 @@ module LookbookVisualTester
|
|
44
42
|
end
|
45
43
|
|
46
44
|
def clean_file_name(file)
|
47
|
-
file.split(
|
45
|
+
'/' + file.split('/')[-1].split('.')[0]
|
48
46
|
end
|
49
47
|
|
50
|
-
def
|
51
|
-
@
|
48
|
+
def selected_previews
|
49
|
+
@selected_previews ||= Lookbook.previews.select do |preview|
|
52
50
|
selected_changes.any? { |file| preview.file_path.to_s.include?(clean_file_name(file)) }
|
53
51
|
end
|
54
52
|
end
|
55
53
|
|
56
54
|
def process_changes
|
57
|
-
Rails.logger.info "LookbookVisualTester:
|
58
|
-
|
55
|
+
Rails.logger.info "LookbookVisualTester: previews #{selected_previews.count}"
|
56
|
+
selected_previews.each do |preview|
|
59
57
|
Rails.logger.info "LookbookVisualTester: entering #{preview.inspect}"
|
60
58
|
|
61
59
|
preview.scenarios.each do |scenario|
|
62
60
|
scenario_run = LookbookVisualTester::ScenarioRun.new(scenario)
|
63
61
|
Rails.logger.info "LookbookVisualTester: Processing scenario #{scenario_run.inspect}"
|
64
|
-
LookbookVisualTester::ScreenshotTaker.
|
62
|
+
LookbookVisualTester::ScreenshotTaker.call(scenario_run:)
|
65
63
|
end
|
66
64
|
end
|
67
65
|
end
|
@@ -1,8 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
# lib/lookbook_visual_tester.rb
|
3
4
|
|
4
|
-
require_relative
|
5
|
-
require_relative
|
5
|
+
require_relative 'lookbook_visual_tester/version'
|
6
|
+
require_relative 'lookbook_visual_tester/railtie' if defined?(Rails)
|
7
|
+
require 'lookbook_visual_tester/scenario_finder'
|
8
|
+
require 'lookbook_visual_tester/store'
|
6
9
|
|
7
10
|
module LookbookVisualTester
|
8
11
|
class Error < StandardError; end
|
@@ -1,57 +1,50 @@
|
|
1
1
|
# lib/tasks/lookbook_visual_tester.rake
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
2
|
+
require 'fileutils'
|
3
|
+
require 'mini_magick'
|
4
|
+
require 'ruby-prof'
|
5
|
+
require 'concurrent-ruby'
|
6
6
|
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
7
|
+
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'
|
11
11
|
|
12
12
|
namespace :lookbook_visual_tester do
|
13
|
-
desc
|
13
|
+
desc 'Profile the lookbook_visual_tester:run task'
|
14
14
|
task profile: :environment do
|
15
15
|
RubyProf.start
|
16
|
-
Rake::Task[
|
16
|
+
Rake::Task['lookbook_visual_tester:run'].invoke
|
17
17
|
result = RubyProf.stop
|
18
18
|
|
19
19
|
printer = RubyProf::FlatPrinter.new(result)
|
20
20
|
printer.print(STDOUT)
|
21
21
|
end
|
22
22
|
|
23
|
-
desc
|
23
|
+
desc 'Run and copy to clipboard first scenario matching the given name'
|
24
24
|
task :copy, [:name] => :environment do |t, args|
|
25
25
|
# example on how to run: `rake lookbook_visual_tester:copy["Button"]`
|
26
26
|
|
27
|
-
screenshot_taker = LookbookVisualTester::ScreenshotTaker.new
|
28
27
|
previews = Lookbook.previews
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
scenario_run = LookbookVisualTester::ScenarioFinder.call(args[:name], previews)
|
30
|
+
unless scenario_run
|
31
|
+
screenshot_taker = LookbookVisualTester::ScreenshotTaker.new(scenario_run:)
|
33
32
|
puts "No Lookbook previews found matching #{args[:name]}"
|
34
33
|
exit
|
35
34
|
end
|
36
|
-
|
37
|
-
|
38
|
-
scenario_run = LookbookVisualTester::ScenarioRun.new(scenario)
|
39
|
-
screenshot_taker.capture(scenario_run.preview_url)
|
40
|
-
exit
|
41
|
-
end
|
42
|
-
end
|
35
|
+
screenshot_taker.capture(scenario_run.preview_url)
|
36
|
+
exit
|
43
37
|
end
|
44
38
|
|
45
|
-
desc
|
39
|
+
desc 'Run visual regression tests for Lookbook previews'
|
46
40
|
task run: :environment do
|
47
|
-
screenshot_taker = LookbookVisualTester::ScreenshotTaker.new
|
48
41
|
image_comparator = LookbookVisualTester::ImageComparator.new
|
49
42
|
report = LookbookVisualTester::ReportGenerator.new
|
50
43
|
|
51
44
|
previews = Lookbook.previews
|
52
45
|
|
53
46
|
if previews.empty?
|
54
|
-
puts
|
47
|
+
puts 'No Lookbook previews found.'
|
55
48
|
exit
|
56
49
|
end
|
57
50
|
|
@@ -63,7 +56,7 @@ namespace :lookbook_visual_tester do
|
|
63
56
|
preview.scenarios.each do |scenario|
|
64
57
|
Concurrent::Promises.future_on(pool) do
|
65
58
|
scenario_run = LookbookVisualTester::ScenarioRun.new(scenario)
|
66
|
-
|
59
|
+
screenshot_taker = LookbookVisualTester::ScreenshotTaker.new(scenario_run:)
|
67
60
|
screenshot_taker.capture(scenario_run.preview_url, scenario_run.current_path)
|
68
61
|
puts " Visiting URL: #{preview_url}"
|
69
62
|
|
@@ -78,18 +71,18 @@ namespace :lookbook_visual_tester do
|
|
78
71
|
puts "Visual regression report generated at #{report_path}"
|
79
72
|
end
|
80
73
|
|
81
|
-
desc
|
74
|
+
desc 'Update baseline screenshots with current_run screenshots'
|
82
75
|
task update_baseline: :environment do
|
83
|
-
base_path = Rails.root.join(
|
84
|
-
baseline_dir = base_path.join(
|
85
|
-
current_dir = base_path.join(
|
76
|
+
base_path = Rails.root.join('spec/visual_screenshots')
|
77
|
+
baseline_dir = base_path.join('baseline')
|
78
|
+
current_dir = base_path.join('current_run')
|
86
79
|
|
87
80
|
unless current_dir.exist?
|
88
|
-
puts
|
81
|
+
puts 'Current run directory does not exist. Run the visual regression tests first.'
|
89
82
|
exit
|
90
83
|
end
|
91
84
|
|
92
|
-
Dir.glob(current_dir.join(
|
85
|
+
Dir.glob(current_dir.join('*.png')).each do |current_file|
|
93
86
|
filename = File.basename(current_file)
|
94
87
|
baseline_file = baseline_dir.join(filename)
|
95
88
|
FileUtils.cp(current_file, baseline_file)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lookbook_visual_tester
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Murilo Vasconcelos
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-02-
|
10
|
+
date: 2025-02-26 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: capybara
|
@@ -164,15 +164,20 @@ files:
|
|
164
164
|
- CODE_OF_CONDUCT.md
|
165
165
|
- README.md
|
166
166
|
- Rakefile
|
167
|
+
- assets/logo.jpg
|
167
168
|
- lib/lookbook_visual_tester.rb
|
169
|
+
- lib/lookbook_visual_tester/baseline_manager.rb
|
168
170
|
- lib/lookbook_visual_tester/capybara_setup.rb
|
169
171
|
- lib/lookbook_visual_tester/configuration.rb
|
170
172
|
- lib/lookbook_visual_tester/image_comparator.rb
|
171
173
|
- lib/lookbook_visual_tester/railtie.rb
|
172
174
|
- lib/lookbook_visual_tester/report_generator.rb
|
175
|
+
- lib/lookbook_visual_tester/scenario_finder.rb
|
173
176
|
- lib/lookbook_visual_tester/scenario_run.rb
|
174
177
|
- lib/lookbook_visual_tester/screenshot_taker.rb
|
178
|
+
- lib/lookbook_visual_tester/service.rb
|
175
179
|
- lib/lookbook_visual_tester/session_manager.rb
|
180
|
+
- lib/lookbook_visual_tester/store.rb
|
176
181
|
- lib/lookbook_visual_tester/update_previews.rb
|
177
182
|
- lib/lookbook_visual_tester/version.rb
|
178
183
|
- lib/tasks/lookbook_visual_tester.rake
|
@@ -183,6 +188,7 @@ licenses:
|
|
183
188
|
- MIT
|
184
189
|
metadata:
|
185
190
|
homepage_uri: https://github.com/muriloime/lookbook_visual_tester
|
191
|
+
rubygems_mfa_required: 'true'
|
186
192
|
rdoc_options: []
|
187
193
|
require_paths:
|
188
194
|
- lib
|