lib/capybara/screenshot/diff/capybara_setup.rb in capybara-screenshot-diff-0.1.0 vs lib/capybara/screenshot/diff/capybara_setup.rb in capybara-screenshot-diff-0.2.1
- old
+ new
@@ -1,9 +1,14 @@
require 'capybara'
require 'capybara/screenshot/diff/image_compare'
+require 'action_controller'
+require 'action_dispatch'
-# Add the `screenshot`method to ActionDispatch::IntegrationTest
+# TODO(uwe): Move this code to module Capybara::Screenshot::Diff::TestMethods,
+# and use Module#prepend/include to insert.
+# Add the `screenshot` method to ActionDispatch::IntegrationTest
+# rubocop:disable Metrics/ClassLength
class ActionDispatch::IntegrationTest
ON_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
SILENCE_ERRORS = ON_WINDOWS ? '2>nul' : '2>/dev/null'
def self.os_name
@@ -17,26 +22,55 @@
else
'unknown'
end
end
- def self.screenshot_dir
+ def self.screenshot_root
+ Capybara::Screenshot.screenshot_root ||
+ (defined?(Rails.root) && Rails.root) || File.expand_path('.')
+ end
+
+ def self.screenshot_area
parts = ['doc/screenshots']
parts << Capybara.default_driver.to_s if Capybara::Screenshot.add_driver_path
parts << os_name if Capybara::Screenshot.add_os_path
File.join parts
end
- def self.screenshot_dir_abs
- "#{Rails.root}/#{screenshot_dir}".freeze
+ def self.screenshot_area_abs
+ "#{screenshot_root}/#{screenshot_area}".freeze
end
+ def initialize(*)
+ super
+ @screenshot_counter = nil
+ @screenshot_group = nil
+ @screenshot_section = nil
+ @test_screenshot_errors = nil
+ @test_screenshots = nil
+ end
+
+ def group_parts
+ parts = []
+ parts << @screenshot_section if @screenshot_section.present?
+ parts << @screenshot_group if @screenshot_group.present?
+ parts
+ end
+
+ def full_name(name)
+ File.join group_parts.<<(name).map(&:to_s)
+ end
+
+ def screenshot_dir
+ File.join [self.class.screenshot_area] + group_parts
+ end
+
setup do
if Capybara::Screenshot.window_size
if Capybara.default_driver == :selenium
page.driver.browser.manage.window.resize_to(*Capybara::Screenshot.window_size)
- else
+ elsif Capybara.default_driver == :poltergeist
page.driver.resize(*Capybara::Screenshot.window_size)
end
end
end
@@ -45,64 +79,102 @@
@test_screenshots.each { |args| assert_image_not_changed(*args) }
end
fail(@test_screenshot_errors.join("\n\n")) if @test_screenshot_errors
end
+ def screenshot_section(name)
+ @screenshot_section = name.to_s
+ end
+
def screenshot_group(name)
- @screenshot_group = name
+ @screenshot_group = name.to_s
@screenshot_counter = 0
- FileUtils.rm_rf "#{self.class.screenshot_dir_abs}/#{name}" if name.present?
+ return unless Capybara::Screenshot.active? && name.present?
+ FileUtils.rm_rf screenshot_dir
end
def screenshot(name)
- return unless Capybara::Screenshot.enabled || (Capybara::Screenshot.enabled.nil? && Capybara::Screenshot::Diff.enabled)
+ return unless Capybara::Screenshot.active?
if Capybara.default_driver == :selenium && Capybara::Screenshot.window_size
return unless page.driver.browser.manage.window
.size == Selenium::WebDriver::Dimension.new(*Capybara::Screenshot.window_size)
end
if @screenshot_counter
name = "#{'%02i' % @screenshot_counter}_#{name}"
@screenshot_counter += 1
end
- name = "#{@screenshot_group}/#{name}" if @screenshot_group.present?
- file_name = "#{self.class.screenshot_dir_abs}/#{name}.png"
- org_name = "#{self.class.screenshot_dir_abs}/#{name}_0.png~"
- new_name = "#{self.class.screenshot_dir_abs}/#{name}_1.png~"
+ name = full_name(name)
+ file_name = "#{self.class.screenshot_area_abs}/#{name}.png"
+ org_name = "#{self.class.screenshot_area_abs}/#{name}_0.png~"
+ new_name = "#{self.class.screenshot_area_abs}/#{name}_1.png~"
FileUtils.mkdir_p File.dirname(file_name)
- svn_file_name = "#{self.class.screenshot_dir_abs}/.svn/text-base/#{name}.png.svn-base"
+ svn_file_name = "#{self.class.screenshot_area_abs}/.svn/text-base/#{name}.png.svn-base"
if File.exist?(svn_file_name)
committed_file_name = svn_file_name
else
svn_info = `svn info #{file_name} #{SILENCE_ERRORS}`
if svn_info.present?
- wc_root = svn_info.slice /(?<=Working Copy Root Path: ).*$/
- checksum = svn_info.slice /(?<=Checksum: ).*$/
+ wc_root = svn_info.slice(/(?<=Working Copy Root Path: ).*$/)
+ checksum = svn_info.slice(/(?<=Checksum: ).*$/)
if checksum
committed_file_name = "#{wc_root}/.svn/pristine/#{checksum[0..1]}/#{checksum}.svn-base"
end
else
committed_file_name = org_name
- `git show HEAD~0:#{self.class.screenshot_dir}/#{name}.png > #{committed_file_name} #{SILENCE_ERRORS}`
+ `git show HEAD~0:#{self.class.screenshot_area}/#{name}.png > #{committed_file_name} #{SILENCE_ERRORS}`
if File.size(committed_file_name) == 0
FileUtils.rm_f committed_file_name
end
end
end
+ take_stable_screenshot(file_name)
+ return unless committed_file_name && File.exist?(committed_file_name)
+ (@test_screenshots ||= []) << [caller[0], name, file_name, committed_file_name, new_name, org_name]
+ end
+
+ IMAGE_WAIT_SCRIPT = <<EOF.freeze
+function pending_image() {
+ var images = document.images;
+ for (var i = 0; i < images.length; i++) {
+ if (!images[i].complete) {
+ return images[i].src;
+ }
+ }
+ return false;
+}()
+EOF
+
+ def assert_images_loaded(timeout: Capybara.default_max_wait_time)
+ return unless respond_to? :evaluate_script
+ start = Time.now
+ loop do
+ pending_image = evaluate_script IMAGE_WAIT_SCRIPT
+ break unless pending_image
+ assert (Time.now - start) < timeout,
+ "Image not loaded after #{timeout}s: #{pending_image.inspect}"
+ sleep 0.1
+ end
+ end
+
+ def take_stable_screenshot(file_name)
+ assert_images_loaded
old_file_size = nil
loop do
- page.save_screenshot(file_name)
- break if old_file_size == File.size(file_name)
- old_file_size = File.size(file_name)
- sleep 0.5
+ save_screenshot(file_name)
+ break unless Capybara::Screenshot.stability_time_limit
+ new_file_size = File.size(file_name)
+ break if new_file_size == old_file_size
+ old_file_size = new_file_size
+ sleep Capybara::Screenshot.stability_time_limit
end
- return unless File.exist?(committed_file_name)
- (@test_screenshots ||= []) << [caller[0], name, file_name, committed_file_name, new_name, org_name]
end
def assert_image_not_changed(caller, name, file_name, committed_file_name, new_name, org_name)
- if Capybara::Screenshot::Diff::ImageCompare.compare(file_name, committed_file_name, Capybara::Screenshot.window_size)
+ if Capybara::Screenshot::Diff::ImageCompare.compare(committed_file_name, file_name,
+ Capybara::Screenshot.window_size)
(@test_screenshot_errors ||= []) <<
- "Screenshot does not match for #{name.inspect}\n#{file_name}\n#{org_name}\n#{new_name}\nat #{caller}"
+ "Screenshot does not match for '#{name}'\n#{file_name}\n#{org_name}\n#{new_name}\nat #{caller}"
end
end
end
+# rubocop:enable Metrics/ClassLength