janus-cli 0.1.0 → 0.2.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 +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
|