lookbook_visual_tester 0.1.6 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 18c7c1416411fad57a1b3aea4b748cc2d861f24f84d9187e81de98352741e620
4
- data.tar.gz: 46ea4cf22145443a9edff07fa0a4e35673e590d628739a70f2d8c2c96c3cce95
3
+ metadata.gz: 60819c43209d30cab93b22e923f369c0fdb85ae2f0cbc5780a9670ca26571677
4
+ data.tar.gz: 231ec3a33592037451d4f8a9354c702971314405b4ab13a6332c911baa915681
5
5
  SHA512:
6
- metadata.gz: 07c1a331af6c9747215ce41c3a7db6e263b474b95ab1ff6eb49d5672ef63e857bc7d53e710e038032dba5e633eec8eb5ba9034b90de69fd13e85f46cbcdcaa6e
7
- data.tar.gz: c3c24f9c64bbf4b872e56636205a3fc27f6dcf1439e6f939f8d5aa1c147594d308befb263ef8f4059712f8b490e1d6fd1bb814ad8138570988302ad4e19ecb9d
6
+ metadata.gz: 782a83e7388499ededdc31453a708969a839b060e09b24de481f55c48f193397d30c873a8137bbec40e4da0f30688cb4485f66d3e93b07ecb19f9d7dc0517bb4
7
+ data.tar.gz: 699056dca68df2a0961547c639da77b62bda599dd5a44013e4b7717d6dd8afeb02195b34c447bb66a72fb8ef8a0b35168cce7bc12cffaa4570a4b9629b8dbc21
data/.rubocop.yml CHANGED
@@ -7,7 +7,6 @@ AllCops:
7
7
  - "vendor/**/*"
8
8
  - "app/assets/**/*"
9
9
  - "db/schema.rb"
10
- TargetRubyVersion: 3.2.1
11
10
  DisplayCopNames: true
12
11
  NewCops: enable
13
12
 
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.4.2
1
+ 3.4.7
data/CHANGELOG.md CHANGED
@@ -1,10 +1,46 @@
1
1
  # Changelog
2
+ ## [0.5.0] - 2026-01-03
2
3
 
3
- Here is the changelog for version **0.1.5** of *LookbookVisualTester*:
4
+ ### New Features & Improvements
5
+ - **Preview Health Checks**: Added new Rake tasks to verify preview integrity:
6
+ - `lookbook:check`: Rapidly checks if previews load and instantiate without errors.
7
+ - `lookbook:deep_check`: Verified previews by effectively rendering them to catch runtime and template errors.
8
+ - `lookbook:missing`: Identifies ViewComponents that lack a corresponding preview.
9
+ - **Parallel Preview Checks**: Health checks run in parallel using `concurrent-ruby`.
10
+ - **Comprehensive Reporting**: Checks generate colored terminal output and a detailed HTML report (`coverage/preview_check_report.html`) including timing stats and slowest previews.
11
+ - **Custom Deep Check Setup**: Added `config.preview_checker_setup` to allow defining mocks (e.g., User, Warden) required for deep checking.
4
12
 
5
- ---
13
+ ## [0.4.0] - 2026-01-03
6
14
 
7
- # Changelog
15
+ ### ✨ New Features & Improvements
16
+ - **Multiple Screenshot Variants**: Support for defining screenshot variants (e.g., specific viewports, themes) using Lookbook's `preview_display_options`.
17
+ - Configurable via `VARIANTS` environment variable (JSON array).
18
+ - Screenshots are saved in subdirectories corresponding to the variant options.
19
+ - Automatic browser resizing based on `width` options.
20
+
21
+ ### 🧹 Housekeeping
22
+ - **Removed Minitest**: Switched completely to RSpec for internal testing. Deleted unused Minitest files and configuration.
23
+
24
+ ## [0.3.0] - 2026-01-03
25
+
26
+ ### ✨ New Features & Improvements
27
+ - **Concurrent Screenshot Capture**: Speed up your test suite significantly by running screenshot capture in parallel.
28
+ - Enabled by default with 4 threads.
29
+ - Configurable via `LOOKBOOK_THREADS` environment variable or Rails configuration.
30
+ - **Configurable Concurrency**: Added `threads` configuration option.
31
+
32
+ ## [0.2.0] - 2026-01-02
33
+
34
+ ### ✨ New Features & Improvements
35
+ - **Visual Diff Aesthetics**: Unchanged context in diff images now rendered with a light blue tint for better human readability.
36
+ - **Standalone Test Harness**: Integrated a dummy Rails application within `spec/dummy` for self-contained development and testing.
37
+ - **Full-Flow Integration Tests**: Added RSpec integration tests that boot a local server and verify the entire screenshot/comparison pipeline.
38
+ - **Lookbook 2.x Compatibility**: Added robust support for Lookbook 2.x `scenarios` while maintaining compatibility with 1.x `examples`.
39
+ - **Filename Normalization**: Improved screenshot naming to handle nested preview paths and avoid directory conflicts.
40
+
41
+ ### Fixed
42
+ - Fixed Ferrum driver compatibility issue with `traffic_factor` argument in `wait_for_idle`.
43
+ - Fixed various unit tests to correctly mock configuration and scenario run state.
8
44
 
9
45
  ## [0.1.6] - 2025-03-04
10
46
 
data/README.md CHANGED
@@ -2,70 +2,182 @@
2
2
 
3
3
  # Lookbook Visual Tester
4
4
 
5
- This gem was built to serve as a lookbook regression tester, getting prints from previews, comparing them and generating a report of differences.
5
+ A powerful visual regression testing tool for [ViewComponent](https://viewcomponent.org/) via [Lookbook](https://lookbook.build/). It automates the process of capturing screenshots of your components, comparing them against baselines, and highlighting differences with human-friendly aesthetics.
6
6
 
7
+ ### Key Features
7
8
 
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.)
9
+ - **Automated Visual Regression**: Captures and compares screenshots of all Lookbook previews.
10
+ - **Intelligent Diffing**: High-quality image comparison using `chunky_png`.
11
+ - **Human-Friendly Aesthetics**: Context is rendered with a light blue tint to make red highlights of differences pop.
12
+ - **Robust Capture**: Automatically disables animations, waits for network idle, and allows masking/cropping.
13
+ - **Lookbook Support**: Compatible with both Lookbook 1.x (`examples`) and 2.x (`scenarios`).
14
+ - **Internal Test Harness**: Includes a dummy Rails app for self-contained testing and development.
15
15
 
16
16
  ## Installation
17
17
 
18
- Tested on linux. A couple of changes to work on mac, etc. You will need tools like xclip, imagemagick
18
+ ### System Dependencies
19
19
 
20
- For Ubuntu-based systems, install the necessary dependencies by running:
20
+ The gem requires `imagemagick` for image processing and `xclip` for clipboard integration (Linux).
21
21
 
22
+ For Ubuntu-based systems:
22
23
  ```bash
23
- sudo apt-get install xclip imagemagick
24
+ sudo apt-get install imagemagick xclip
25
+ ```
26
+
27
+ ### Gem Installation
28
+
29
+ Add to your application's Gemfile:
30
+ ```ruby
31
+ group :test do
32
+ gem 'lookbook_visual_tester'
33
+ end
34
+ ```
35
+
36
+ Then install:
37
+ ```bash
38
+ bundle install
39
+ ```
40
+
41
+ ## Configuration
42
+
43
+ You can configure the tester in a Rails initializer:
44
+
45
+ ```ruby
46
+ LookbookVisualTester.configure do |config|
47
+ config.lookbook_host = "http://localhost:3000" # Where your rails app is running
48
+ config.base_path = "spec/visual_regression" # Root for screenshots
49
+ config.copy_to_clipboard = true # Enable xclip support
50
+ config.threads = 4 # Number of parallel threads (default: 4)
51
+ end
24
52
  ```
25
53
 
54
+ ## Usage
26
55
 
56
+ ### Running Visual Tests
27
57
 
28
- Install the gem and add to the application's Gemfile by executing:
58
+ The gem provides Rake tasks to execute the visual regression suite.
29
59
 
60
+ #### Run All Tests
61
+ Runs all Lookbook previews, generates a terminal summary, and creates an HTML report.
30
62
  ```bash
31
- bundle add lookbook_visual_tester
63
+ bundle exec rake lookbook:test
32
64
  ```
33
65
 
34
- If bundler is not being used to manage dependencies, install the gem by executing:
66
+ #### Test a Specific Preview
67
+ Filter previews by name or label.
68
+ ```bash
69
+ bundle exec rake lookbook:screenshot[Button]
70
+ ```
35
71
 
72
+ #### Configuration Overrides
73
+ You can override the host or other settings inline:
36
74
  ```bash
37
- gem install lookbook_visual_tester
75
+ LOOKBOOK_HOST=http://localhost:5000 LOOKBOOK_THREADS=8 bundle exec rake lookbook:test
38
76
  ```
39
77
 
40
- ## Usage
78
+ #### Screenshot Variants
41
79
 
42
- To run everything
43
- bundle exec rake lookbook_visual_tester:run LOOKBOOK_HOST=https://localhost:5000
80
+ You can run your visual tests against multiple configurations (variants), such as different themes or viewports, by leveraging Lookbook's `preview_display_options`.
44
81
 
45
- ### Features
82
+ 1. **Define Options in Lookbook**:
83
+ Ensure your Rails app has display options configured:
84
+ ```ruby
85
+ # config/lookbook.rb
86
+ Lookbook.config.preview_display_options = {
87
+ theme: ["light", "dark"],
88
+ width: [["Mobile", "375px"], ["Desktop", "1280px"]]
89
+ }
90
+ ```
46
91
 
47
- When installed this gem gets your changes and generates
92
+ 2. **Run with Variants**:
93
+ Use the `VARIANTS` environment variable to define a JSON array of option sets to test.
48
94
 
49
- ## Development
95
+ *Example: Run standard tests + Dark Mode + Mobile View*
96
+ ```bash
97
+ VARIANTS='[{}, {"theme":"dark"}, {"width":"Mobile"}]' bundle exec rake lookbook:test
98
+ ```
50
99
 
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.
100
+ * **`{}`**: Runs the default/standard preview.
101
+ * **`{"theme":"dark"}`**: Runs with `_display[theme]=dark`.
102
+ * **`{"width":"Mobile"}`**: Runs with `_display[width]=375px` AND automatically resizes the browser window to 375px width.
52
103
 
53
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
104
+ Screenshots for variants are saved in dedicated subfolders (e.g., `spec/visual_regression/baseline/theme-dark/`).
105
+
106
+
107
+ ### Baseline Management
108
+
109
+ 1. **First Run**: When you run the tests for the first time, all screenshots are saved as **Baselines**.
110
+ 2. **Subsequent Runs**: New screenshots are compared against the baselines.
111
+ 3. **Mismatches**: If a change is detected, a **Diff** image is generated.
112
+ 4. **Approval**: To approve a change (update the baseline), simply copy the file from `current_run` to `baseline`. The HTML report provides a convenient "Copy Approval Command" button for this.
113
+
114
+ ### Reporting
115
+
116
+ After running `rake lookbook:test`, a detailed HTML report is generated at:
117
+ `coverage/visual_report.html`
118
+
119
+ The report allows you to:
120
+ - See side-by-side comparisons of Baseline vs. Actual.
121
+ - View the diff highlighting changes in neon red.
122
+ - Quickly copy terminal commands to approve changes.
54
123
 
55
- ## Contributing
56
124
 
57
- Bug reports and pull requests are welcome on GitHub at https://github.com/muriloime/lookbook_visual_tester. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/lookbook_visual_tester/blob/main/CODE_OF_CONDUCT.md).
125
+ ### Human-Readable Diffs
58
126
 
127
+ When a difference is detected, a diff image is generated where:
128
+ - **Neon Red**: Parts of the component that changed.
129
+ - **Blue Tint**: The unchanged context, making it easy to identify where the change occurred.
59
130
 
60
- ## Deployment
131
+ ## Internal Testing (Development)
132
+
133
+ The project includes an internal dummy Rails application for testing the gem itself.
134
+
135
+ ### Running the Test Suite
136
+ ```bash
137
+ bundle exec rspec
138
+ ```
139
+
140
+ ### Running the Full Flow Integration Test
141
+ ```bash
142
+ bundle exec rspec spec/integration/full_flow_spec.rb
143
+ ```
144
+
145
+ ### Preview Health Checks
146
+
147
+ The gem provides tasks to ensure your previews are healthy and up-to-date.
148
+
149
+ #### Check Load/Syntax
150
+ Checks if all previews can be loaded and instantiated without errors.
151
+ ```bash
152
+ bundle exec rake lookbook:check
153
+ ```
154
+
155
+ #### Deep Check (Render)
156
+ effectively renders all previews to catch runtime and template errors.
157
+ ```bash
158
+ bundle exec rake lookbook:deep_check
159
+ ```
160
+
161
+ #### Find Missing Previews
162
+ Identifies components that don't have a corresponding preview file.
163
+ ```bash
164
+ bundle exec rake lookbook:missing
165
+ ```
166
+
167
+ ## Next Steps
168
+
169
+ - **CI/CD Integration**: Provide recipes for GitHub Actions to run visual regression on PRs.
170
+
171
+ ## Contributing
61
172
 
62
- Generate artifacts for changelog :
173
+ Bug reports and pull requests are welcome on GitHub at https://github.com/muriloime/lookbook_visual_tester.
63
174
 
64
- `git log --pretty=format:"%h %ad | %s [%an]" --date=short --no-merges --name-only | xclip -selection clipboard`
175
+ ## Deployment
65
176
 
66
- Update changelog
67
- `rake realease`
177
+ To release a new version:
178
+ 1. Update the version in `lib/lookbook_visual_tester/version.rb`.
179
+ 2. Run `bundle exec rake release`.
68
180
 
69
- ## Code of Conduct
181
+ ## License
70
182
 
71
- Everyone interacting in the LookbookVisualTester project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/lookbook_visual_tester/blob/main/CODE_OF_CONDUCT.md).
183
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/RELEASING.md ADDED
@@ -0,0 +1,31 @@
1
+ # Releasing
2
+
3
+ This gem uses `bundler/gem_tasks` to manage releases.
4
+
5
+ ## Instructions
6
+
7
+ 1. Make sure your working directory is clean and you are on the `main` branch.
8
+ 2. Update the version in `lib/lookbook_visual_tester/version.rb`.
9
+ 3. Update the `CHANGELOG.md`.
10
+ 4. Run the release task:
11
+ ```bash
12
+ bundle exec rake release:otp
13
+ ```
14
+ This will:
15
+ - Create a git tag for the version.
16
+ - Push git commits and tags.
17
+ - Build the gem.
18
+ - Push the `.gem` file to RubyGems.org.
19
+
20
+ ## Troubleshooting
21
+
22
+ If you encounter issues with authentication, ensure you are logged in to RubyGems via the `gem` CLI:
23
+ ```bash
24
+ gem signin
25
+ ```
26
+
27
+ If you need to supply a 2FA/OTP code manually or for a script, you can use the `release:otp` task or set the `GEM_HOST_OTP_CODE` environment variable:
28
+
29
+ ```bash
30
+ GEM_HOST_OTP_CODE=123456 bundle exec rake release
31
+ ```
data/Rakefile CHANGED
@@ -1,12 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/gem_tasks'
4
- require 'minitest/test_task'
5
-
6
- Minitest::TestTask.create
7
-
4
+ require 'rspec/core/rake_task'
8
5
  require 'rubocop/rake_task'
9
6
 
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
10
9
  RuboCop::RakeTask.new
11
10
 
12
- task default: %i[test rubocop]
11
+ task default: %i[spec rubocop]
12
+
13
+ namespace :release do
14
+ desc 'Release with OTP (MFA) support'
15
+ task :otp do
16
+ require 'io/console'
17
+ print 'Enter OTP code: '
18
+ otp = $stdin.noecho(&:gets).strip
19
+ puts "\n"
20
+ ENV['GEM_HOST_OTP_CODE'] = otp
21
+ Rake::Task['release'].invoke
22
+ end
23
+ end
@@ -0,0 +1,81 @@
1
+ require 'rainbow'
2
+ require 'thor/group'
3
+
4
+ module LookbookVisualTester
5
+ class CheckReporter < Thor::Group
6
+ include Thor::Actions
7
+
8
+ argument :results
9
+
10
+ def self.source_root
11
+ File.dirname(__FILE__)
12
+ end
13
+
14
+ def report
15
+ @errors = results.select { |r| r.status == :failed }
16
+ @success_count = results.count { |r| r.status == :passed }
17
+ @total_duration = results.sum { |r| r.duration.to_f }
18
+
19
+ report_terminal
20
+ report_html
21
+ end
22
+
23
+ def self.report_missing(missing)
24
+ if missing.any?
25
+ puts Rainbow("\nFound #{missing.size} components missing previews:").yellow
26
+ missing.sort_by { |m| m.component_path }.each do |m|
27
+ puts " - #{m.component_path}"
28
+ end
29
+ puts "\nTotal: #{missing.size} missing previews."
30
+ else
31
+ puts Rainbow('All components have previews!').green
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :errors, :success_count, :total_duration
38
+
39
+ def report_terminal
40
+ puts "\n--- Check Results ---"
41
+
42
+ results.each do |result|
43
+ if result.status == :passed
44
+ print Rainbow('.').green
45
+ else
46
+ print Rainbow('F').red
47
+ end
48
+ end
49
+ puts "\n"
50
+
51
+ if errors.any?
52
+ puts Rainbow("\n#{errors.size} errors found:").red
53
+ errors.each do |err|
54
+ puts "\n--------------------------------------------------"
55
+ puts "Preview: #{err.preview_name}##{err.example_name}"
56
+ puts "Error: #{err.error}"
57
+ puts "Time: #{err.duration.to_f.round(4)}s"
58
+ puts "Backtrace: #{err.backtrace&.first(3)&.join("\n ")}"
59
+ end
60
+ else
61
+ puts Rainbow("\nAll #{results.size} previews passed!").green
62
+ end
63
+
64
+ puts "\nTotal time: #{total_duration.round(2)}s"
65
+
66
+ report_slowest_previews
67
+ end
68
+
69
+ def report_slowest_previews
70
+ puts "\n--- Top 5 Slowest Previews ---"
71
+ slowest = results.sort_by { |r| -r.duration.to_f }.first(5)
72
+ slowest.each do |res|
73
+ puts "#{Rainbow("#{res.duration.to_f.round(4)}s").yellow} - #{res.preview_name}##{res.example_name}"
74
+ end
75
+ end
76
+
77
+ def report_html
78
+ template 'templates/preview_check_report.html.tt', 'coverage/preview_check_report.html'
79
+ end
80
+ end
81
+ end
@@ -1,9 +1,14 @@
1
1
  module LookbookVisualTester
2
2
  class Configuration
3
- attr_accessor :base_path, :lookbook_host,
3
+ attr_reader :base_path
4
+ attr_accessor :lookbook_host,
4
5
  :baseline_dir, :current_dir, :diff_dir, :history_dir,
5
6
  :history_keep_last_n, :threads, :copy_to_clipboard,
6
- :components_folder
7
+ :components_folder,
8
+ :automatic_run,
9
+ :mask_selectors, :driver_adapter,
10
+ :preview_checker_setup,
11
+ :logger
7
12
 
8
13
  DEFAULT_THREADS = 4
9
14
 
@@ -20,12 +25,30 @@ module LookbookVisualTester
20
25
  @current_dir = @base_path.join('current_run')
21
26
  @diff_dir = @base_path.join('diff')
22
27
  @history_dir = @base_path.join('history')
23
- @threads = DEFAULT_THREADS
28
+ @threads = ENV.fetch('LOOKBOOK_THREADS', DEFAULT_THREADS).to_i
24
29
  @history_keep_last_n = 5
25
30
  @copy_to_clipboard = true
26
31
  @components_folder = 'app/components'
32
+ @automatic_run = ENV.fetch('LOOKBOOK_AUTOMATIC_RUN', false)
33
+ @mask_selectors = []
34
+ @driver_adapter = :ferrum
35
+ @preview_checker_setup = nil
36
+ @logger = if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
37
+ Rails.logger
38
+ else
39
+ require 'logger'
40
+ Logger.new($stdout).tap { |l| l.level = Logger::INFO }
41
+ end
27
42
 
28
- @lookbook_host = ENV.fetch('LOOKBOOK_HOST', 'https://localhost:5000')
43
+ @lookbook_host = ENV.fetch('LOOKBOOK_HOST', 'http://localhost:5000')
44
+ end
45
+
46
+ def base_path=(value)
47
+ @base_path = Pathname.new(value)
48
+ @baseline_dir = @base_path.join('baseline')
49
+ @current_dir = @base_path.join('current_run')
50
+ @diff_dir = @base_path.join('diff')
51
+ @history_dir = @base_path.join('history')
29
52
  end
30
53
 
31
54
  class << self
@@ -0,0 +1,51 @@
1
+ module LookbookVisualTester
2
+ class Driver
3
+ attr_reader :config
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def visit(url)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def resize_window(width, height)
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def inject_style(css)
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def run_script(script)
22
+ raise NotImplementedError
23
+ end
24
+
25
+ def mask_elements(selectors)
26
+ raise NotImplementedError
27
+ end
28
+
29
+ # Smart wait implementation
30
+ def wait_for_assets
31
+ wait_for_fonts
32
+ wait_for_network_idle
33
+ end
34
+
35
+ def wait_for_fonts
36
+ raise NotImplementedError
37
+ end
38
+
39
+ def wait_for_network_idle
40
+ raise NotImplementedError
41
+ end
42
+
43
+ def save_screenshot(path)
44
+ raise NotImplementedError
45
+ end
46
+
47
+ def cleanup
48
+ raise NotImplementedError
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,111 @@
1
+ require_relative '../driver'
2
+ require 'ferrum'
3
+
4
+ module LookbookVisualTester
5
+ module Drivers
6
+ class FerrumDriver < Driver
7
+ def initialize(config)
8
+ super
9
+ @browser = Ferrum::Browser.new(
10
+ headless: true,
11
+ window_size: [1280, 800], # Default, can be resized
12
+ timeout: 10 # Configurable?
13
+ )
14
+ end
15
+
16
+ def visit(url)
17
+ @browser.go_to(url)
18
+ prepare_page
19
+ end
20
+
21
+ def resize_window(width, height)
22
+ @browser.resize(width: width, height: height)
23
+ end
24
+
25
+ def inject_style(css)
26
+ # Ferrum allows adding style tags via JS
27
+ script = <<~JS
28
+ const style = document.createElement('style');
29
+ style.innerHTML = `#{css}`;
30
+ document.head.appendChild(style);
31
+ JS
32
+ @browser.execute(script)
33
+ end
34
+
35
+ def run_script(script)
36
+ @browser.execute(script)
37
+ end
38
+
39
+ def mask_elements(selectors)
40
+ return if selectors.empty?
41
+
42
+ selectors.each do |selector|
43
+ script = <<~JS
44
+ document.querySelectorAll('#{selector}').forEach(el => {
45
+ el.style.visibility = 'hidden';
46
+ });
47
+ JS
48
+ @browser.execute(script)
49
+ end
50
+ end
51
+
52
+ def wait_for_assets
53
+ wait_for_network_idle
54
+ wait_for_fonts
55
+ wait_for_custom_selectors
56
+ end
57
+
58
+ def wait_for_fonts
59
+ @browser.execute("return document.fonts.ready")
60
+ end
61
+
62
+ def wait_for_network_idle(timeout: 2, duration: 0.5)
63
+ # Ferrum has built-in network idle waiting
64
+ @browser.network.wait_for_idle
65
+ rescue Ferrum::TimeoutError
66
+ # Log warning but proceed?
67
+ end
68
+
69
+ def save_screenshot(path)
70
+ @browser.screenshot(path: path, full: true)
71
+ # Note: full: true captures the whole page.
72
+ # If we capture viewport only, we should remove full: true.
73
+ # Usually for visual testing full page is better unless specifically testing viewport.
74
+ end
75
+
76
+ def cleanup
77
+ @browser.quit
78
+ end
79
+
80
+ private
81
+
82
+ def prepare_page
83
+ disable_animations
84
+ mask_elements(config.mask_selectors || [])
85
+ wait_for_assets
86
+ end
87
+
88
+ def disable_animations
89
+ css = "* { transition: none !important; animation: none !important; caret-color: transparent !important; }"
90
+ inject_style(css)
91
+ end
92
+
93
+ def wait_for_custom_selectors
94
+ # If we have specific selectors to wait for e.g. document.fonts.status check loop
95
+ start = Time.now
96
+ until @browser.execute("return document.fonts.status === 'loaded'")
97
+ break if Time.now - start > 5
98
+ sleep 0.1
99
+ end
100
+
101
+ # Also check for images loading attributes if needed
102
+ # "check that no images have loading attributes active"
103
+ # Often checking complete property is enough
104
+ until @browser.execute("return Array.from(document.images).every(i => i.complete)")
105
+ break if Time.now - start > 5
106
+ sleep 0.1
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,9 @@
1
+ require 'json'
2
+
3
+ module LookbookVisualTester
4
+ class JsonOutputHandler
5
+ def self.print(data)
6
+ puts JSON.pretty_generate(data)
7
+ end
8
+ end
9
+ end