chrome_diff 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 63efaa6953d427032ff166c99839064311cfd6d7febbc34b66ec377975f1e991
4
+ data.tar.gz: a46a9b426140b30d78b14a3093ef6c4df5a4ce445e13d76477f8f8ffafb21005
5
+ SHA512:
6
+ metadata.gz: 16a7ab5574bbf3a177b47a796d82fedcfbfbd81eeea47a9064eda41d654b901099c3f999e546070b1fb1c61f7be27d9cb93254324a8fd34a77ba6d9a6860f990
7
+ data.tar.gz: 5da408f12b623dc122ec685dd48982323243edc3ef9c8d628b6299606a424cf404ef743f7e4374f53fa6bccd27cfeb5e69b08722490977898f5830361c3607d6
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ Gemfile.lock
data/.keep ADDED
File without changes
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.5
7
+ before_install: gem install bundler -v 1.17.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in chrome_diff.gemspec
6
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 hogelog
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,28 @@
1
+ # ChromeDiff
2
+
3
+ Visual diff tool powered by headless chrome.
4
+
5
+ ## Installation
6
+
7
+ ```console
8
+ $ gem install chrome_diff
9
+ ```
10
+
11
+ ## Requirements
12
+ - Google Chrome (or Chromium)
13
+ - ImageMagick
14
+
15
+ ## Usage
16
+
17
+ ```console
18
+ $ chrome-diff -f "https://google.com/?q=hello" -t "https://google.com/?q=world"
19
+ There are some diffefences (1.44%): diff.png
20
+ ```
21
+
22
+ ## Contributing
23
+
24
+ Bug reports and pull requests are welcome on GitHub at https://github.com/hogelog/chrome_diff.
25
+
26
+ ## License
27
+
28
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "chrome_diff"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rake' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rake", "rake")
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rspec-core", "rspec")
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "chrome_diff/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "chrome_diff"
8
+ spec.version = ChromeDiff::VERSION
9
+ spec.authors = ["hogelog"]
10
+ spec.email = ["konbu.komuro@gmail.com"]
11
+
12
+ spec.summary = %q{Generate visual diff by chrome.}
13
+ spec.homepage = "https://github.com/hogelog/chrome_diff"
14
+ spec.license = "MIT"
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+ spec.add_dependency "ferrum", "< 1.0"
25
+
26
+ spec.add_development_dependency "bundler"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency "rspec", "< 4.0"
29
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
4
+ require "chrome_diff"
5
+
6
+ result = ChromeDiff::CLI.run(ARGV)
7
+
8
+ exit(result.success? ? 0 : 1)
@@ -0,0 +1,30 @@
1
+ require "chrome_diff/version"
2
+ require "chrome_diff/cli"
3
+ require "chrome_diff/session"
4
+ require "chrome_diff/site_checker"
5
+
6
+ module ChromeDiff
7
+ class Error < StandardError; end
8
+
9
+ DEFAULT_OPTIONS = {
10
+ output: "diff.png",
11
+ width: 800,
12
+ height: 600,
13
+ fuzz: 1,
14
+ timeout: 5,
15
+ }
16
+
17
+ def self.run(**args)
18
+ width = args.delete(:width)
19
+ height = args.delete(:height)
20
+ timeout = args.delete(:timeout)
21
+ debug = args.delete(:debug)
22
+
23
+ begin
24
+ session = ChromeDiff::Session.new(width: width, height: height, timeout: timeout, debug: debug)
25
+ session.compare(**args)
26
+ ensure
27
+ session.browser&.quit
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,53 @@
1
+ require "optparse"
2
+
3
+ module ChromeDiff
4
+ class CLI
5
+ def self.run(argv)
6
+ opts = ChromeDiff::DEFAULT_OPTIONS.merge(parse_argv(argv.dup))
7
+ result = ChromeDiff.run(**opts)
8
+ output = opts[:output]
9
+
10
+ unless opts[:quiet]
11
+ if result.success?
12
+ message = "There is no difference (%.2f%%)" % result.diff_percent
13
+ else
14
+ message = "There are some diffefences (%.2f%%)" % result.diff_percent
15
+ end
16
+ message += ": #{output}" if output
17
+ puts message
18
+ end
19
+
20
+ result
21
+ end
22
+
23
+ def self.parse_argv(argv)
24
+ opts = {}
25
+ parser = OptionParser.new
26
+ parser.on("-f [FROM_URL]", "--from-url", "From url") {|v| opts[:from_url] = v }
27
+ parser.on("-t [TO_URL]", "--to-url", "To url") {|v| opts[:to_url] = v }
28
+ parser.on("-o [OUTPUT]", "--output", "Output file") {|v| opts[:output] = v }
29
+ parser.on("-w [WIDTH]", "--width", "Window width (default 800)") {|v| opts[:width] = v.to_i }
30
+ parser.on("-h [HEIGHT]", "--height", "Window height (default 600)") {|v| opts[:height] = v.to_i }
31
+ parser.on("-q", "--quiet", "Quiet mode") {|v| opts[:quiet] = v }
32
+ parser.on("--fuzz [FUZZ]", "Fuzz factor percent (default 5%)") {|v| opts[:fuzz] = v.to_f }
33
+ parser.on("--debug", "Debug mode (default false)") {|v| opts[:debug] = !!v }
34
+ parser.on("--no-output", "Don't output diff file") {|v| opts[:output] = v }
35
+ parser.on("--full-screenshot", "Capture full screenshot") {|v| opts[:full_screenshot] = !!v }
36
+ parser.parse(argv)
37
+
38
+ required(opts, :from_url)
39
+ required(opts, :to_url)
40
+
41
+ opts
42
+ rescue ArgumentError => e
43
+ STDERR.puts(e.message)
44
+ puts(parser.help)
45
+ exit 1
46
+ end
47
+
48
+ def self.required(opts, key)
49
+ arg_key = "--#{key.to_s.gsub("_", "-")}"
50
+ opts[key] or raise ArgumentError, "#{arg_key} is required"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,86 @@
1
+ require "ferrum"
2
+
3
+ require "fileutils"
4
+ require "tmpdir"
5
+ require "open3"
6
+ require "logger"
7
+
8
+ module ChromeDiff
9
+ class CompareStatus
10
+ attr_reader :status, :diff, :diff_percent
11
+
12
+ def initialize(status, stderr, width, height, fuzz)
13
+ @status = status
14
+ @fuzz = fuzz
15
+
16
+ @diff = stderr.to_f.to_i
17
+ @diff_percent = @diff.to_f / width / height * 100
18
+ end
19
+
20
+ def success?
21
+ @diff_percent <= @fuzz
22
+ end
23
+ end
24
+
25
+ class Session
26
+ attr_reader :browser
27
+
28
+ def initialize(width: nil, height: nil, timeout: nil, debug: false)
29
+ @width = width || ChromeDiff::DEFAULT_OPTIONS[:width]
30
+ @height = height || ChromeDiff::DEFAULT_OPTIONS[:height]
31
+ @timeout = timeout || ChromeDiff::DEFAULT_OPTIONS[:timeout]
32
+ @debug = debug
33
+ options = {
34
+ window_size: [@width, @height],
35
+ timeout: @timeout,
36
+ browser_options: {
37
+ "device-scale-factor" => 1,
38
+ "force-device-scale-factor" => nil,
39
+ },
40
+ }
41
+ if @debug
42
+ options[:logger] = STDOUT
43
+ options[:headless] = false
44
+ end
45
+
46
+ @browser = Ferrum::Browser.new(options)
47
+ @browser.resize(width: @width, height: @height)
48
+ end
49
+
50
+ def compare(from_url:, to_url:, output:, fuzz: nil, full_screenshot: nil)
51
+ fuzz ||= ChromeDiff::DEFAULT_OPTIONS[:fuzz]
52
+ result = nil
53
+ Dir.mktmpdir do |tmpdir|
54
+ from_file = File.join(tmpdir, "from.png")
55
+ to_file = File.join(tmpdir, "to.png")
56
+ diff_file = File.join(tmpdir, "diff.png")
57
+
58
+ screenshot(from_url, from_file, full_screenshot)
59
+ screenshot(to_url, to_file, full_screenshot)
60
+
61
+ _stdout, stderr, status = Open3.capture3("compare", "-metric", "AE", from_file, to_file, diff_file)
62
+ result = CompareStatus.new(status, stderr, @width, @height, fuzz)
63
+
64
+ File.rename(diff_file, output) if output
65
+ end
66
+
67
+ result
68
+ end
69
+
70
+ def screenshot(url, file, full)
71
+ @browser.goto(url)
72
+ wait_complete
73
+ @browser.resize(width: @width, height: @height)
74
+ @browser.screenshot(path: file, full: full)
75
+ end
76
+
77
+ def wait_complete
78
+ @browser.network.wait_for_idle
79
+ Timeout.timeout(@timeout) do
80
+ until @browser.page.evaluate('document.readyState') == "complete"
81
+ sleep 0.1
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,55 @@
1
+ require "fileutils"
2
+
3
+ module ChromeDiff
4
+ class SiteChecker
5
+ def initialize(domains:, sizes:, paths:, options: {})
6
+ @domains = domains
7
+ @sizes = sizes
8
+ @paths = paths
9
+ @options = options
10
+ end
11
+
12
+ def run
13
+ options = @options.dup
14
+ @sizes.each do |size|
15
+ shots_dir = File.join("shots", size.map(&:to_s).join("x"))
16
+ FileUtils.mkdir_p(shots_dir)
17
+
18
+ options.merge()
19
+ timeout = options.delete(:timeout)
20
+ debug = options.delete(:debug) || false
21
+ session = ChromeDiff::Session.new(width: size[0], height: size[1], timeout: timeout, debug: debug)
22
+
23
+ html = ""
24
+ images = []
25
+ @paths.each do |name, path|
26
+ from = "#{@domains[0]}#{path}"
27
+ to = "#{@domains[1]}#{path}"
28
+ filename = "#{name}.png"
29
+ output = File.join(shots_dir, filename)
30
+ result = session.compare(from_url: from, to_url: to, output: output)
31
+ puts "#{output} (%.2f%%)" % result.diff_percent
32
+
33
+ title = "#{filename} (%.2f%%)" % result.diff_percent
34
+ images << [name, title]
35
+ html += <<~HTML
36
+ <h2 id="#{ name }">#{ title }</h2>
37
+ <ul>
38
+ <li><a href="#{from}">#{from}</a></li>
39
+ <li><a href="#{to}">#{to}</a></li>
40
+ <li>
41
+ <a href="#{ filename }">
42
+ <img src="#{ filename }">
43
+ </a>
44
+ </li>
45
+ </ul>
46
+ HTML
47
+ end
48
+
49
+ html = "<ul>\n" + images.map{|name, title| "<li><a href='##{ name }'>#{ title }</a></li>"}.join("\n") + "</ul>\n\n" + html
50
+
51
+ File.write(File.join(shots_dir, "index.html"), html)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ module ChromeDiff
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chrome_diff
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - hogelog
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-10-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ferrum
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "<"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "<"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "<"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "<"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.0'
69
+ description:
70
+ email:
71
+ - konbu.komuro@gmail.com
72
+ executables:
73
+ - chrome-diff
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".keep"
79
+ - ".rspec"
80
+ - ".travis.yml"
81
+ - Gemfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - bin/console
86
+ - bin/rake
87
+ - bin/rspec
88
+ - bin/setup
89
+ - chrome_diff.gemspec
90
+ - exe/chrome-diff
91
+ - lib/chrome_diff.rb
92
+ - lib/chrome_diff/cli.rb
93
+ - lib/chrome_diff/session.rb
94
+ - lib/chrome_diff/site_checker.rb
95
+ - lib/chrome_diff/version.rb
96
+ homepage: https://github.com/hogelog/chrome_diff
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubygems_version: 3.1.4
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Generate visual diff by chrome.
119
+ test_files: []