janus-cli 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +1 -1
- data/CHANGELOG.md +7 -0
- data/Guardfile +2 -2
- data/README.md +11 -20
- data/Rakefile +11 -1
- data/bin/janus +1 -1
- data/janus.gemspec +3 -1
- data/lib/janus.rb +0 -1
- data/lib/janus/browser.rb +19 -0
- data/lib/janus/command/record.rb +35 -4
- data/lib/janus/command/validate.rb +39 -5
- data/lib/janus/configuration.rb +21 -2
- data/lib/janus/core/engine.rb +26 -0
- data/lib/janus/core/error.rb +6 -0
- data/lib/janus/core/rule.rb +46 -0
- data/lib/janus/io/directory.rb +40 -0
- data/lib/janus/io/selenium.rb +43 -0
- data/lib/janus/screenshot.rb +7 -27
- data/lib/janus/version.rb +1 -1
- data/spec/integration/engine_spec.rb +50 -0
- data/spec/support/square-base.png +0 -0
- data/spec/support/square-big.png +0 -0
- data/spec/support/square-ten.png +0 -0
- data/spec/support/square-thirty.png +0 -0
- data/spec/support/square-twenty.png +0 -0
- data/spec/unit/janus/browser_spec.rb +57 -0
- data/spec/{janus → unit/janus}/command/initialize_spec.rb +1 -1
- data/spec/unit/janus/command/record_spec.rb +93 -0
- data/spec/unit/janus/command/validate_spec.rb +105 -0
- data/spec/{janus → unit/janus}/configuration_spec.rb +42 -0
- data/spec/unit/janus/core/engine_spec.rb +36 -0
- data/spec/unit/janus/core/error_spec.rb +4 -0
- data/spec/unit/janus/core/rule_spec.rb +65 -0
- data/spec/unit/janus/io/directory_spec.rb +83 -0
- data/spec/unit/janus/io/selenium_spec.rb +78 -0
- data/spec/unit/janus/screenshot_spec.rb +26 -0
- data/spec/{janus → unit/janus}/test_spec.rb +0 -0
- data/spec/{spec_helper.rb → unit/spec_helper.rb} +0 -0
- metadata +78 -19
- data/spec/janus/command/record_spec.rb +0 -36
- data/spec/janus/command/validate_spec.rb +0 -44
- data/spec/janus/screenshot_spec.rb +0 -79
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 937b8709825ebcdb797849676eb8828953b18ce3
|
4
|
+
data.tar.gz: 2359bd29a0b4a314918215693a98b68d4682212b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32799bec8eab0f1460b418a14b20c0a1f08ba10594d0a461c9964b9545b367e8c27de752f534c26cc132cf33552d10767cb419e5b0b3fa7bcafd1c8444a6d504
|
7
|
+
data.tar.gz: a9a8fc7030531eac97a34c327c9cb206790ec1c2fc6891a827bc9827ab8bd65a3acb7a1f993c44731341ce3afece9dcaf68e87d587f2e44a693cbb9903f75b38
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/CHANGELOG.md
ADDED
data/Guardfile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
guard :rspec do
|
2
2
|
watch(%r{^spec/.+_spec\.rb$})
|
3
|
-
watch(%r{^lib/(.+)\.rb$})
|
4
|
-
watch('spec/spec_helper.rb') { "spec" }
|
3
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
|
4
|
+
watch('spec/unit/spec_helper.rb') { "spec/unit" }
|
5
5
|
end
|
6
6
|
|
data/README.md
CHANGED
@@ -49,23 +49,10 @@ directory for an example of Janus in action.
|
|
49
49
|
|
50
50
|
## Configuring Janus
|
51
51
|
|
52
|
-
**
|
53
|
-
|
54
|
-
The location where Janus screenshots are stored.
|
55
|
-
|
56
|
-
**url** (optional)
|
57
|
-
|
58
|
-
The base URL for all screenshots. All test URLs will be relative to this URL.
|
59
|
-
|
60
|
-
**threshold** (optional)
|
61
|
-
|
62
|
-
The percentage of change that is allowed in a screenshot. This value can be
|
63
|
-
overridden for each individual test.
|
64
|
-
|
65
|
-
**resolution** (optional)
|
52
|
+
**tests** (required)
|
66
53
|
|
67
|
-
|
68
|
-
|
54
|
+
An array of screenshots to take. Each entry in the array contains a name and
|
55
|
+
URL. All URLs are relative to the specified base URL.
|
69
56
|
|
70
57
|
**browsers** (required)
|
71
58
|
|
@@ -73,11 +60,15 @@ An array of browsers to run your tests against. Each entry in the array contains
|
|
73
60
|
a platform, browser, and optional version. See the [Sauce Labs platform documentation](https://saucelabs.com/docs/platforms)
|
74
61
|
for more information on the valid browsers.
|
75
62
|
|
76
|
-
**
|
63
|
+
**directory** (required)
|
64
|
+
|
65
|
+
The location where Janus screenshots are stored.
|
66
|
+
|
67
|
+
**threshold** (optional)
|
68
|
+
|
69
|
+
The percentage of change that is allowed in a screenshot. This value can be
|
70
|
+
overridden for each individual test. *Defaults to 0*.
|
77
71
|
|
78
|
-
An array of screenshots to take. Each entry in the array contains a name, URL,
|
79
|
-
and optional threshold. All URLs are relative to the specified base URL. If a
|
80
|
-
threshold is defined, it will override the global theshold.
|
81
72
|
|
82
73
|
## Best Practices
|
83
74
|
|
data/Rakefile
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rspec/core/rake_task'
|
3
3
|
|
4
|
-
|
4
|
+
task :spec => ['spec:unit', 'spec:integration']
|
5
|
+
|
6
|
+
namespace :spec do
|
7
|
+
RSpec::Core::RakeTask.new(:unit) do |t|
|
8
|
+
t.pattern = 'spec/unit/**/*_spec.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec::Core::RakeTask.new(:integration) do |t|
|
12
|
+
t.pattern = 'spec/integration/**/*_spec.rb'
|
13
|
+
end
|
14
|
+
end
|
5
15
|
|
6
16
|
task :default => :spec
|
data/bin/janus
CHANGED
data/janus.gemspec
CHANGED
@@ -16,8 +16,10 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
17
|
spec.require_paths = ['lib']
|
18
18
|
|
19
|
-
spec.add_dependency('
|
19
|
+
spec.add_dependency('colorize', '~> 0.7.0')
|
20
20
|
spec.add_dependency('gli', '~> 2.8')
|
21
|
+
spec.add_dependency('oily_png', '~> 1.1')
|
22
|
+
spec.add_dependency('sauce-connect', '~> 3.3.1')
|
21
23
|
spec.add_dependency('selenium-webdriver', '~> 2.37')
|
22
24
|
|
23
25
|
spec.add_development_dependency('bundler', '~> 1.3')
|
data/lib/janus.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Janus
|
2
|
+
class Browser
|
3
|
+
attr_reader :platform, :name, :version
|
4
|
+
|
5
|
+
def initialize(attributes = {})
|
6
|
+
@platform = attributes['platform']
|
7
|
+
@name = attributes['name']
|
8
|
+
@version = attributes['version']
|
9
|
+
end
|
10
|
+
|
11
|
+
def eql?(other)
|
12
|
+
platform == other.platform && name == other.name && version == other.version
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"#{platform}, #{name} #{version}".strip
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/janus/command/record.rb
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'colorize'
|
4
|
+
require 'sauce/connect'
|
5
|
+
require 'janus/io/directory'
|
6
|
+
require 'janus/io/selenium'
|
1
7
|
require 'janus/screenshot'
|
2
8
|
|
3
9
|
module Janus
|
@@ -8,14 +14,39 @@ module Janus
|
|
8
14
|
end
|
9
15
|
|
10
16
|
def execute
|
17
|
+
Sauce::Connect.connect!(quiet: true) if @configuration.tunnel?
|
18
|
+
|
19
|
+
puts 'Recording screenshots...'
|
20
|
+
puts ''
|
21
|
+
|
22
|
+
@configuration.browsers.each do |browser|
|
23
|
+
record_screenshots_for_browser(browser)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def record_screenshots_for_browser(browser)
|
28
|
+
puts "#{browser}"
|
29
|
+
puts ''
|
30
|
+
|
11
31
|
@configuration.tests.each do |test|
|
12
|
-
record_screenshot(test)
|
32
|
+
record_screenshot(browser, test)
|
13
33
|
end
|
34
|
+
|
35
|
+
puts ''
|
14
36
|
end
|
15
37
|
|
16
|
-
def record_screenshot(test)
|
17
|
-
|
18
|
-
screenshot.
|
38
|
+
def record_screenshot(browser, test)
|
39
|
+
selenium = Janus::IO::Selenium.new(@configuration.username, @configuration.access_key, browser)
|
40
|
+
screenshot = selenium.read(test)
|
41
|
+
|
42
|
+
directory = Janus::IO::Directory.new(@configuration.directory, browser)
|
43
|
+
directory.write(test, screenshot)
|
44
|
+
|
45
|
+
print '✔ '.green
|
46
|
+
rescue
|
47
|
+
print '✖ '.red
|
48
|
+
ensure
|
49
|
+
puts test.name
|
19
50
|
end
|
20
51
|
end
|
21
52
|
end
|
@@ -1,3 +1,11 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'colorize'
|
4
|
+
require 'sauce/connect'
|
5
|
+
require 'janus/core/engine'
|
6
|
+
require 'janus/io/directory'
|
7
|
+
require 'janus/io/selenium'
|
8
|
+
|
1
9
|
module Janus
|
2
10
|
module Command
|
3
11
|
class Validate
|
@@ -6,16 +14,42 @@ module Janus
|
|
6
14
|
end
|
7
15
|
|
8
16
|
def execute
|
17
|
+
Sauce::Connect.connect!(quiet: true) if @configuration.tunnel?
|
18
|
+
|
19
|
+
puts 'Validating screenshots...'
|
20
|
+
puts ''
|
21
|
+
|
22
|
+
@configuration.browsers.each do |browser|
|
23
|
+
validate_screenshots_for_browser(browser)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_screenshots_for_browser(browser)
|
28
|
+
puts "#{browser}"
|
29
|
+
puts ''
|
30
|
+
|
9
31
|
@configuration.tests.each do |test|
|
10
|
-
validate_screenshot(test)
|
32
|
+
validate_screenshot(browser, test)
|
11
33
|
end
|
34
|
+
|
35
|
+
puts ''
|
12
36
|
end
|
13
37
|
|
14
|
-
def validate_screenshot(test)
|
15
|
-
|
16
|
-
fresh =
|
38
|
+
def validate_screenshot(browser, test)
|
39
|
+
selenium = Janus::IO::Selenium.new(@configuration.username, @configuration.access_key, browser)
|
40
|
+
fresh = selenium.read(test)
|
41
|
+
|
42
|
+
directory = Janus::IO::Directory.new(@configuration.directory, browser)
|
43
|
+
original = directory.read(test)
|
44
|
+
|
45
|
+
engine = Janus::Core::Engine.create(@configuration)
|
46
|
+
engine.execute(original, fresh)
|
17
47
|
|
18
|
-
|
48
|
+
print '✔ '.green
|
49
|
+
rescue
|
50
|
+
print '✖ '.red
|
51
|
+
ensure
|
52
|
+
puts test.name
|
19
53
|
end
|
20
54
|
end
|
21
55
|
end
|
data/lib/janus/configuration.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'yaml'
|
2
|
+
require 'janus/browser'
|
2
3
|
require 'janus/test'
|
3
4
|
|
4
5
|
module Janus
|
@@ -12,7 +13,7 @@ module Janus
|
|
12
13
|
Janus::Configuration.new(options)
|
13
14
|
end
|
14
15
|
|
15
|
-
def initialize(options)
|
16
|
+
def initialize(options = {})
|
16
17
|
@options = options
|
17
18
|
end
|
18
19
|
|
@@ -24,6 +25,24 @@ module Janus
|
|
24
25
|
@options['access_key']
|
25
26
|
end
|
26
27
|
|
28
|
+
def tunnel?
|
29
|
+
@options['tunnel']
|
30
|
+
end
|
31
|
+
|
32
|
+
def directory
|
33
|
+
@options['directory']
|
34
|
+
end
|
35
|
+
|
36
|
+
def threshold
|
37
|
+
@options['threshold'] || 0
|
38
|
+
end
|
39
|
+
|
40
|
+
def browsers
|
41
|
+
@options['browsers'].map do |browser|
|
42
|
+
Janus::Browser.new(browser)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
27
46
|
def tests
|
28
47
|
@options['tests'].map do |test|
|
29
48
|
Janus::Test.new(test)
|
@@ -34,7 +53,7 @@ module Janus
|
|
34
53
|
|
35
54
|
def self.load_configuration_file
|
36
55
|
if File.exists?('Janusfile')
|
37
|
-
YAML.load(IO.read('Janusfile'))
|
56
|
+
YAML.load(::IO.read('Janusfile'))
|
38
57
|
else
|
39
58
|
raise 'Could not find Janus configuration file!'
|
40
59
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'janus/core/rule'
|
2
|
+
|
3
|
+
module Janus
|
4
|
+
module Core
|
5
|
+
class Engine
|
6
|
+
def self.create(configuration)
|
7
|
+
engine = Engine.new
|
8
|
+
engine.add_rule(Janus::Core::DimensionsRule.new(configuration))
|
9
|
+
engine.add_rule(Janus::Core::ThresholdRule.new(configuration))
|
10
|
+
engine
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@rules = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_rule(rule)
|
18
|
+
@rules << rule
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute(original, fresh)
|
22
|
+
@rules.each { |r| r.execute(original, fresh) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'oily_png'
|
2
|
+
require 'janus/core/error'
|
3
|
+
|
4
|
+
module Janus
|
5
|
+
module Core
|
6
|
+
class Rule
|
7
|
+
def initialize(configuration)
|
8
|
+
@configuration = configuration
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute(original, fresh)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class DimensionsRule < Rule
|
16
|
+
def execute(original, fresh)
|
17
|
+
raise Janus::Core::ComparisonError unless original.dimensions == fresh.dimensions
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class ThresholdRule < Rule
|
22
|
+
def execute(original, fresh)
|
23
|
+
difference = calculate_total_difference(original, fresh)
|
24
|
+
raise Janus::Core::ComparisonError unless difference <= @configuration.threshold
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def calculate_total_difference(original, fresh)
|
30
|
+
pixels = original.pixels.zip(fresh.pixels)
|
31
|
+
pixel_differences = pixels.map do |a, b|
|
32
|
+
calculate_pixel_difference(a, b)
|
33
|
+
end
|
34
|
+
|
35
|
+
pixel_differences.reduce { |sum, value| sum + value } / pixels.length
|
36
|
+
end
|
37
|
+
|
38
|
+
def calculate_pixel_difference(original, fresh)
|
39
|
+
r = (ChunkyPNG::Color.r(fresh) - ChunkyPNG::Color.r(original)) ** 2
|
40
|
+
g = (ChunkyPNG::Color.g(fresh) - ChunkyPNG::Color.g(original)) ** 2
|
41
|
+
b = (ChunkyPNG::Color.b(fresh) - ChunkyPNG::Color.b(original)) ** 2
|
42
|
+
Math.sqrt(r + g + b) / Math.sqrt(ChunkyPNG::Color::MAX ** 2 * 3)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'oily_png'
|
2
|
+
require 'janus/screenshot'
|
3
|
+
|
4
|
+
module Janus
|
5
|
+
module IO
|
6
|
+
class Directory
|
7
|
+
def initialize(directory, browser)
|
8
|
+
@directory = directory
|
9
|
+
@browser = browser
|
10
|
+
end
|
11
|
+
|
12
|
+
def read(test)
|
13
|
+
path = image_path(test)
|
14
|
+
image = ChunkyPNG::Image.from_file(path)
|
15
|
+
Janus::Screenshot.new(image)
|
16
|
+
end
|
17
|
+
|
18
|
+
def write(test, screenshot)
|
19
|
+
directory = image_directory(test)
|
20
|
+
FileUtils.mkpath(directory) unless Dir.exists?(directory)
|
21
|
+
|
22
|
+
path = image_path(test)
|
23
|
+
screenshot.image.save(path)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def image_path(test)
|
29
|
+
segments = [@browser.platform, @browser.name, @browser.version]
|
30
|
+
file_name = segments.compact.join('-')
|
31
|
+
|
32
|
+
File.join(image_directory(test), "#{file_name}.png")
|
33
|
+
end
|
34
|
+
|
35
|
+
def image_directory(test)
|
36
|
+
File.join(@directory, "#{test.name}.janus")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|