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
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'oily_png'
|
2
|
+
require 'selenium/webdriver'
|
3
|
+
require 'janus/screenshot'
|
4
|
+
|
5
|
+
module Janus
|
6
|
+
module IO
|
7
|
+
class Selenium
|
8
|
+
@@driver_pool = {}
|
9
|
+
|
10
|
+
def initialize(username, access_key, browser)
|
11
|
+
@username = username
|
12
|
+
@access_key = access_key
|
13
|
+
@browser = browser
|
14
|
+
|
15
|
+
@driver = @@driver_pool[browser] || build_driver
|
16
|
+
end
|
17
|
+
|
18
|
+
def read(test)
|
19
|
+
@driver.get(test.url)
|
20
|
+
|
21
|
+
png = @driver.screenshot_as(:png)
|
22
|
+
image = ChunkyPNG::Image.from_blob(png)
|
23
|
+
Janus::Screenshot.new(image)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def build_driver
|
29
|
+
capabilities = ::Selenium::WebDriver::Remote::Capabilities.new(
|
30
|
+
platform: @browser.platform,
|
31
|
+
browser_name: @browser.name,
|
32
|
+
version: @browser.version
|
33
|
+
)
|
34
|
+
|
35
|
+
driver = ::Selenium::WebDriver.for(:remote, {
|
36
|
+
url: "http://#{@username}:#{@access_key}@ondemand.saucelabs.com/wd/hub",
|
37
|
+
desired_capabilities: capabilities
|
38
|
+
})
|
39
|
+
@@driver_pool[@browser] = driver
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/janus/screenshot.rb
CHANGED
@@ -1,37 +1,17 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require 'selenium/webdriver'
|
3
|
-
|
4
1
|
module Janus
|
5
2
|
class Screenshot
|
6
|
-
|
7
|
-
|
8
|
-
def self.capture(test, options = {})
|
9
|
-
driver = Selenium::WebDriver.for(:remote, {
|
10
|
-
url: "http://#{options[:username]}:#{options[:access_key]}@ondemand.saucelabs.com/wd/hub",
|
11
|
-
desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome
|
12
|
-
})
|
13
|
-
driver.get(test.url)
|
14
|
-
|
15
|
-
Screenshot.new(test: test, image: driver.screenshot_as(:png))
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.load(test, options = {})
|
19
|
-
path = File.join(options[:path], "#{test.name}.janus", 'screenshot.png')
|
20
|
-
image = IO.read(path, mode: 'rb')
|
3
|
+
attr_reader :image
|
21
4
|
|
22
|
-
|
5
|
+
def initialize(image)
|
6
|
+
@image = image
|
23
7
|
end
|
24
8
|
|
25
|
-
def
|
26
|
-
@
|
27
|
-
@image = parameters[:image]
|
9
|
+
def dimensions
|
10
|
+
{ width: @image.width, height: @image.height }
|
28
11
|
end
|
29
12
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
FileUtils.mkpath(directory) unless Dir.exists?(directory)
|
34
|
-
IO.write(File.join(directory, 'screenshot.png'), @image, mode: 'wb')
|
13
|
+
def pixels
|
14
|
+
@image.pixels
|
35
15
|
end
|
36
16
|
end
|
37
17
|
end
|
data/lib/janus/version.rb
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'oily_png'
|
2
|
+
require 'janus/configuration'
|
3
|
+
require 'janus/core/engine'
|
4
|
+
require 'janus/core/error'
|
5
|
+
require 'janus/screenshot'
|
6
|
+
|
7
|
+
describe 'Engine Integration Test' do
|
8
|
+
let(:config) { Janus::Configuration.new('threshold' => 0.2) }
|
9
|
+
let(:engine) { Janus::Core::Engine.create(config) }
|
10
|
+
let(:square_base) { ChunkyPNG::Image::from_file('spec/support/square-base.png') }
|
11
|
+
let(:square_big) { ChunkyPNG::Image::from_file('spec/support/square-big.png') }
|
12
|
+
let(:square_ten) { ChunkyPNG::Image::from_file('spec/support/square-ten.png') }
|
13
|
+
let(:square_twenty) { ChunkyPNG::Image::from_file('spec/support/square-twenty.png') }
|
14
|
+
let(:square_thirty) { ChunkyPNG::Image::from_file('spec/support/square-thirty.png') }
|
15
|
+
|
16
|
+
it 'succeeds for matching image' do
|
17
|
+
original = Janus::Screenshot.new(square_base)
|
18
|
+
fresh = Janus::Screenshot.new(square_base)
|
19
|
+
|
20
|
+
expect { engine.execute(original, fresh) }.not_to raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'succeeds for image below threshold' do
|
24
|
+
original = Janus::Screenshot.new(square_base)
|
25
|
+
fresh = Janus::Screenshot.new(square_ten)
|
26
|
+
|
27
|
+
expect { engine.execute(original, fresh) }.not_to raise_error
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'succeeds for image matching threshold' do
|
31
|
+
original = Janus::Screenshot.new(square_base)
|
32
|
+
fresh = Janus::Screenshot.new(square_twenty)
|
33
|
+
|
34
|
+
expect { engine.execute(original, fresh) }.not_to raise_error
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'fails for image exceeding threshold' do
|
38
|
+
original = Janus::Screenshot.new(square_base)
|
39
|
+
fresh = Janus::Screenshot.new(square_thirty)
|
40
|
+
|
41
|
+
expect { engine.execute(original, fresh) }.to raise_error
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'fails for different sized image' do
|
45
|
+
original = Janus::Screenshot.new(square_base)
|
46
|
+
fresh = Janus::Screenshot.new(square_big)
|
47
|
+
|
48
|
+
expect { engine.execute(original, fresh) }.to raise_error
|
49
|
+
end
|
50
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'janus/browser'
|
2
|
+
|
3
|
+
describe Janus::Browser do
|
4
|
+
it 'sets platform' do
|
5
|
+
browser = Janus::Browser.new('platform' => 'platform')
|
6
|
+
browser.platform.should == 'platform'
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'sets name' do
|
10
|
+
browser = Janus::Browser.new('name' => 'name')
|
11
|
+
browser.name.should == 'name'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'sets version' do
|
15
|
+
browser = Janus::Browser.new('version' => '7')
|
16
|
+
browser.version.should == '7'
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#eql' do
|
20
|
+
let(:platform) { 'platform' }
|
21
|
+
let(:name) { 'name' }
|
22
|
+
let(:version) { 'version' }
|
23
|
+
let(:a) { Janus::Browser.new('platform' => platform, 'name' => name, 'version' => version) }
|
24
|
+
|
25
|
+
it 'is false when platform is different' do
|
26
|
+
b = Janus::Browser.new('platform' => 'different', 'name' => name, 'version' => version)
|
27
|
+
b.should_not eql(a)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'is false when name is different' do
|
31
|
+
b = Janus::Browser.new('platform' => platform, 'name' => 'different', 'version' => version)
|
32
|
+
b.should_not eql(a)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'is false when version is different' do
|
36
|
+
b = Janus::Browser.new('platform' => platform, 'name' => name, 'version' => 'different')
|
37
|
+
b.should_not eql(a)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'is true when browser, name, and version are the same' do
|
41
|
+
b = Janus::Browser.new('platform' => platform, 'name' => name, 'version' => version)
|
42
|
+
b.should eql(a)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#to_s' do
|
47
|
+
it 'converts to string for browser with version' do
|
48
|
+
browser = Janus::Browser.new('platform' => 'platform', 'name' => 'name', 'version' => 0.7)
|
49
|
+
browser.to_s.should == 'platform, name 0.7'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'converts to string for browser without version' do
|
53
|
+
browser = Janus::Browser.new('platform' => 'platform', 'name' => 'name')
|
54
|
+
browser.to_s.should == 'platform, name'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -8,7 +8,7 @@ describe Janus::Command::Initialize do
|
|
8
8
|
it 'writes samples configuration if file does not exist' do
|
9
9
|
File.stub(:exists?) { false }
|
10
10
|
|
11
|
-
source = File.expand_path('
|
11
|
+
source = File.expand_path('../../../../../lib/janus/template/Janusfile', __FILE__)
|
12
12
|
destination = 'Janusfile'
|
13
13
|
FileUtils.should_receive(:copy).with(source, destination)
|
14
14
|
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'sauce/connect'
|
2
|
+
require 'janus/command/record'
|
3
|
+
require 'janus/configuration'
|
4
|
+
|
5
|
+
describe Janus::Command::Record do
|
6
|
+
let(:config) { Janus::Configuration.new('username' => 'username', 'access_key' => 'key', 'directory' => 'base') }
|
7
|
+
let(:record) { Janus::Command::Record.new(config) }
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
record.stub(:puts)
|
11
|
+
record.stub(:print)
|
12
|
+
|
13
|
+
Sauce::Connect.stub(:connect!)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#execute' do
|
17
|
+
before :each do
|
18
|
+
config.stub(:browsers) { ['red', 'blue'] }
|
19
|
+
config.stub(:tests) { ['one', 'two'] }
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'starts tunnel when tunnel is true' do
|
23
|
+
config.stub(:tunnel?) { true }
|
24
|
+
record.stub(:record_screenshot)
|
25
|
+
|
26
|
+
Sauce::Connect.should_receive(:connect!)
|
27
|
+
|
28
|
+
record.execute
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'does not start tunnel when tunnel is false' do
|
32
|
+
config.stub(:tunnel?) { false }
|
33
|
+
record.stub(:record_screenshot)
|
34
|
+
|
35
|
+
Sauce::Connect.should_not_receive(:connect!)
|
36
|
+
|
37
|
+
record.execute
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'records screenshot for each configured test and browser combination' do
|
41
|
+
record.should_receive(:record_screenshot).with('red', 'one')
|
42
|
+
record.should_receive(:record_screenshot).with('red', 'two')
|
43
|
+
record.should_receive(:record_screenshot).with('blue', 'one')
|
44
|
+
record.should_receive(:record_screenshot).with('blue', 'two')
|
45
|
+
|
46
|
+
record.execute
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#record_screenshot' do
|
51
|
+
let(:screenshot) { double }
|
52
|
+
let(:browser) { double }
|
53
|
+
|
54
|
+
let(:test) do
|
55
|
+
test = double
|
56
|
+
test.stub(:name)
|
57
|
+
test
|
58
|
+
end
|
59
|
+
|
60
|
+
# TODO: Builder
|
61
|
+
let(:directory) do
|
62
|
+
directory = double
|
63
|
+
directory.stub(:write)
|
64
|
+
directory
|
65
|
+
end
|
66
|
+
|
67
|
+
# TODO: Builder
|
68
|
+
let(:selenium) do
|
69
|
+
selenium = double
|
70
|
+
selenium.stub(:read) { screenshot }
|
71
|
+
selenium
|
72
|
+
end
|
73
|
+
|
74
|
+
before :each do
|
75
|
+
Janus::IO::Directory.stub(:new) { directory }
|
76
|
+
Janus::IO::Selenium.stub(:new) { selenium }
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'reads screenshot from Selenium' do
|
80
|
+
Janus::IO::Selenium.should_receive(:new).with('username', 'key', browser) { selenium }
|
81
|
+
selenium.should_receive(:read).with(test) { screenshot }
|
82
|
+
|
83
|
+
record.record_screenshot(browser, test)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'writes screenshot to Janus directory' do
|
87
|
+
Janus::IO::Directory.should_receive(:new).with('base', browser) { directory}
|
88
|
+
directory.should_receive(:write).with(test, screenshot)
|
89
|
+
|
90
|
+
record.record_screenshot(browser, test)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'janus/command/validate'
|
2
|
+
require 'janus/configuration'
|
3
|
+
|
4
|
+
describe Janus::Command::Validate do
|
5
|
+
let(:config) { Janus::Configuration.new('username' => 'username', 'access_key' => 'key', 'directory' => 'base') }
|
6
|
+
let(:validate) { Janus::Command::Validate.new(config) }
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
validate.stub(:puts)
|
10
|
+
validate.stub(:print)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#execute' do
|
14
|
+
before :each do
|
15
|
+
config.stub(:browsers) { %w(red blue) }
|
16
|
+
config.stub(:tests) { %w(one two) }
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'starts tunnel when tunnel is true' do
|
20
|
+
config.stub(:tunnel?) { true }
|
21
|
+
validate.stub(:validate_screenshot)
|
22
|
+
|
23
|
+
Sauce::Connect.should_receive(:connect!)
|
24
|
+
|
25
|
+
validate.execute
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'does not start tunnel when tunnel is false' do
|
29
|
+
config.stub(:tunnel?) { false }
|
30
|
+
validate.stub(:validate_screenshot)
|
31
|
+
|
32
|
+
Sauce::Connect.should_not_receive(:connect!)
|
33
|
+
|
34
|
+
validate.execute
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'validates screenshots for each configured test' do
|
38
|
+
validate.should_receive(:validate_screenshot).with('red', 'one')
|
39
|
+
validate.should_receive(:validate_screenshot).with('red', 'two')
|
40
|
+
validate.should_receive(:validate_screenshot).with('blue', 'one')
|
41
|
+
validate.should_receive(:validate_screenshot).with('blue', 'two')
|
42
|
+
|
43
|
+
validate.execute
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#validate_screenshot' do
|
48
|
+
let(:test) do
|
49
|
+
test = double
|
50
|
+
test.stub(:name)
|
51
|
+
test
|
52
|
+
end
|
53
|
+
|
54
|
+
let(:fresh) { double }
|
55
|
+
let(:original) { double }
|
56
|
+
let(:browser) { double }
|
57
|
+
|
58
|
+
# TODO: Builder
|
59
|
+
let(:engine) do
|
60
|
+
engine = double
|
61
|
+
engine.stub(:execute)
|
62
|
+
engine
|
63
|
+
end
|
64
|
+
|
65
|
+
# TODO: Builder
|
66
|
+
let(:directory) do
|
67
|
+
directory = double
|
68
|
+
directory.stub(:read) { original }
|
69
|
+
directory
|
70
|
+
end
|
71
|
+
|
72
|
+
# TODO: Builder
|
73
|
+
let(:selenium) do
|
74
|
+
selenium = double
|
75
|
+
selenium.stub(:read) { fresh }
|
76
|
+
selenium
|
77
|
+
end
|
78
|
+
|
79
|
+
before :each do
|
80
|
+
Janus::IO::Directory.stub(:new) { directory }
|
81
|
+
Janus::IO::Selenium.stub(:new) { selenium }
|
82
|
+
Janus::Core::Engine.stub(:create) { engine }
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'reads screenshot from Selenium' do
|
86
|
+
Janus::IO::Selenium.should_receive(:new).with('username', 'key', browser) { selenium }
|
87
|
+
selenium.should_receive(:read).with(test) { fresh }
|
88
|
+
|
89
|
+
validate.validate_screenshot(browser, test)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'reads screenshot from directory' do
|
93
|
+
Janus::IO::Directory.should_receive(:new).with('base', browser) { directory }
|
94
|
+
selenium.should_receive(:read).with(test) { original }
|
95
|
+
|
96
|
+
validate.validate_screenshot(browser, test)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'executes engine' do
|
100
|
+
engine.should_receive(:execute).with(original, fresh)
|
101
|
+
|
102
|
+
validate.validate_screenshot(browser, test)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -40,6 +40,48 @@ describe Janus::Configuration do
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
describe '#tunnel' do
|
44
|
+
it 'returns tunnel setting' do
|
45
|
+
configuration = Janus::Configuration.new('tunnel' => true)
|
46
|
+
configuration.tunnel?.should == true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#directory' do
|
51
|
+
it 'returns directory' do
|
52
|
+
configuration = Janus::Configuration.new('directory' => 'directory')
|
53
|
+
configuration.directory.should == 'directory'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#threshold' do
|
58
|
+
it 'returns threshold' do
|
59
|
+
configuration = Janus::Configuration.new('threshold' => 0.1)
|
60
|
+
configuration.threshold.should == 0.1
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'defaults threshold to zero' do
|
64
|
+
configuration = Janus::Configuration.new
|
65
|
+
configuration.threshold.should == 0
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#browsers' do
|
70
|
+
it 'creates browser for each entry in configuration file' do
|
71
|
+
browser_configuration = []
|
72
|
+
browser_configuration << { 'platform' => 'a', 'name' => 'a', 'version' => 'a' }
|
73
|
+
browser_configuration << { 'platform' => 'b', 'name' => 'b', 'version' => 'b' }
|
74
|
+
|
75
|
+
configuration = Janus::Configuration.new({ 'browsers' => browser_configuration })
|
76
|
+
|
77
|
+
configuration.browsers.each_with_index do |browser, i|
|
78
|
+
browser.platform.should == browser_configuration[i]['platform']
|
79
|
+
browser.name.should == browser_configuration[i]['name']
|
80
|
+
browser.version.should == browser_configuration[i]['version']
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
43
85
|
describe '#tests' do
|
44
86
|
it 'creates test for each entry in configuration file' do
|
45
87
|
test_configuration = []
|