simulacrum 0.1.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.cane +9 -0
- data/.env.example +2 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.rubocop.yml +14 -0
- data/.travis.yml +28 -0
- data/Gemfile +2 -0
- data/README.md +78 -19
- data/Rakefile +20 -0
- data/examples/README.md +3 -0
- data/examples/basic/Gemfile +4 -0
- data/examples/basic/README.md +13 -0
- data/examples/basic/config.ru +3 -0
- data/examples/basic/example_app.rb +7 -0
- data/examples/basic/public/button.html +13 -0
- data/examples/basic/public/index.html +22 -0
- data/examples/basic/public/panel.html +13 -0
- data/examples/basic/public/stylesheets/button.css +15 -0
- data/examples/basic/public/stylesheets/main.css +3 -0
- data/examples/basic/public/stylesheets/normalize.css +425 -0
- data/examples/basic/script/start +4 -0
- data/examples/basic/spec/simulacrum_helper.rb +9 -0
- data/examples/basic/spec/ui/button_spec.rb +10 -0
- data/exe/simulacrum +5 -0
- data/features/command_line/help.feature +8 -0
- data/features/exit_codes/failing.feature +24 -0
- data/features/exit_codes/passing.feature +24 -0
- data/features/exit_codes/pending.feature +22 -0
- data/features/output/candidate.feature +32 -0
- data/features/output/diff.feature +5 -0
- data/features/step_definitions/dummy_steps.rb +15 -0
- data/features/step_definitions/file_steps.rb +19 -0
- data/features/support/env.rb +15 -0
- data/fixtures/a1.png +0 -0
- data/fixtures/app/fixture_app.rb +12 -0
- data/fixtures/app/public/images/a1.png +0 -0
- data/fixtures/app/public/ui_component.html +10 -0
- data/fixtures/app/spec/component_spec.rb +9 -0
- data/fixtures/app/spec/simulacrum_helper.rb +37 -0
- data/fixtures/app/spec/ui/references/ui_component/test_driver/candidate.png +0 -0
- data/fixtures/diff.png +0 -0
- data/lib/simulacrum.rb +74 -15
- data/lib/simulacrum/cli.rb +38 -0
- data/lib/simulacrum/cli/parser.rb +152 -0
- data/lib/simulacrum/comparator.rb +15 -15
- data/lib/simulacrum/component.rb +22 -11
- data/lib/simulacrum/configuration.rb +20 -13
- data/lib/simulacrum/diff.rb +2 -0
- data/lib/simulacrum/diff/rmagick.rb +8 -6
- data/lib/simulacrum/driver.rb +45 -0
- data/lib/simulacrum/matchers.rb +6 -16
- data/lib/simulacrum/methods.rb +1 -0
- data/lib/simulacrum/renderer.rb +23 -8
- data/lib/simulacrum/runner.rb +44 -0
- data/lib/simulacrum/version.rb +2 -1
- data/rubocop-todo.yml +29 -0
- data/script/bootstrap +3 -0
- data/script/quality +7 -0
- data/script/spec +10 -0
- data/simulacrum.gemspec +52 -0
- data/spec/lib/simulacrum/cli/parser_spec.rb +113 -0
- data/spec/lib/simulacrum/cli_spec.rb +18 -0
- data/spec/lib/simulacrum/comparator_spec.rb +75 -0
- data/spec/lib/simulacrum/component_spec.rb +208 -0
- data/spec/lib/simulacrum/driver/local_spec.rb +11 -0
- data/spec/lib/simulacrum/version_spec.rb +12 -0
- data/spec/lib/simulacrum_spec.rb +53 -0
- data/spec/spec_helper.rb +13 -8
- data/spec/use_codeclimate.rb +3 -0
- data/spec/use_simplecov.rb +5 -12
- metadata +217 -32
- data/lib/simulacrum/diff/pdiff.rb +0 -47
- data/spec/fixtures/a.png +0 -0
- data/spec/fixtures/a2.png +0 -0
- data/spec/use_coveralls.rb +0 -2
data/exe/simulacrum
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
Feature: `--help` option
|
2
|
+
|
3
|
+
The `--help` option should display usage information for simulacrum
|
4
|
+
|
5
|
+
Scenario: Using `--help`
|
6
|
+
When I run `simulacrum --help`
|
7
|
+
Then the output should contain "Usage: simulacrum [options] [files or directories]"
|
8
|
+
And the exit status should be 0
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: Failure exit code
|
2
|
+
|
3
|
+
The exit code should always be non-zero (1) when there are failing tests
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given a fixture application
|
7
|
+
And a file named "spec/ui/ui_component_spec.rb" with:
|
8
|
+
"""ruby
|
9
|
+
require 'simulacrum_helper'
|
10
|
+
|
11
|
+
describe 'UI Component' do
|
12
|
+
component :ui_component do |options|
|
13
|
+
options.url = '/ui_component.html'
|
14
|
+
end
|
15
|
+
it { should look_the_same }
|
16
|
+
end
|
17
|
+
"""
|
18
|
+
|
19
|
+
Scenario: There are failing tests
|
20
|
+
Given a reference image for "ui_component" with content: "diff.png"
|
21
|
+
When I run `simulacrum`
|
22
|
+
Then the exit status should be 1
|
23
|
+
And the output should contain "Failure/Error: it { should look_the_same }"
|
24
|
+
And the output should contain "1 example, 1 failure"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: Passing exit code
|
2
|
+
|
3
|
+
The exit code should always be zero when the tests pass
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given a fixture application
|
7
|
+
And a file named "spec/ui/ui_component_spec.rb" with:
|
8
|
+
"""ruby
|
9
|
+
require 'simulacrum_helper'
|
10
|
+
|
11
|
+
describe 'UI Component' do
|
12
|
+
component :ui_component do |options|
|
13
|
+
options.url = '/ui_component.html'
|
14
|
+
end
|
15
|
+
it { should look_the_same }
|
16
|
+
end
|
17
|
+
"""
|
18
|
+
|
19
|
+
Scenario: Passing tests
|
20
|
+
Given a reference image for "ui_component" with content: "a1.png"
|
21
|
+
When I run `simulacrum`
|
22
|
+
Then the exit status should be 0
|
23
|
+
And the output should contain "1 example, 0 failures"
|
24
|
+
And the output should not contain "pending"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
Feature: Pending exit code
|
2
|
+
|
3
|
+
The exit code should always be zero when there were pending tests
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given a fixture application
|
7
|
+
And a file named "spec/ui/ui_component_spec.rb" with:
|
8
|
+
"""ruby
|
9
|
+
require 'simulacrum_helper'
|
10
|
+
|
11
|
+
describe 'UI Component' do
|
12
|
+
component :ui_component do |options|
|
13
|
+
options.url = '/ui_component.html'
|
14
|
+
end
|
15
|
+
it { should look_the_same }
|
16
|
+
end
|
17
|
+
"""
|
18
|
+
|
19
|
+
Scenario: There is a pending test
|
20
|
+
When I run `simulacrum`
|
21
|
+
Then the exit status should be 0
|
22
|
+
And the output should contain "1 example, 0 failures, 1 pending"
|
@@ -0,0 +1,32 @@
|
|
1
|
+
Feature: candidate image output
|
2
|
+
|
3
|
+
A candidate image should be produced when there is no reference image present
|
4
|
+
or if the diff threshold is met.
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given a fixture application
|
8
|
+
And a file named "spec/ui/ui_component_spec.rb" with:
|
9
|
+
"""ruby
|
10
|
+
require 'simulacrum_helper'
|
11
|
+
|
12
|
+
describe 'UI Component' do
|
13
|
+
component :ui_component do |options|
|
14
|
+
options.url = '/ui_component.html'
|
15
|
+
end
|
16
|
+
it { should look_the_same }
|
17
|
+
end
|
18
|
+
"""
|
19
|
+
|
20
|
+
Scenario: There is already a reference, and there is no diff so a candidate should not be created
|
21
|
+
Given a reference image for "ui_component" with content: "a1.png"
|
22
|
+
When I run `simulacrum`
|
23
|
+
Then a candidate for "ui_component" should not exist
|
24
|
+
|
25
|
+
Scenario: There is no reference present so a candidate should be created
|
26
|
+
When I run `simulacrum`
|
27
|
+
Then a candidate for "ui_component" should exist
|
28
|
+
|
29
|
+
Scenario: There is a diff when the test is run so a candidate should be created
|
30
|
+
Given a reference image for "ui_component" with content: "diff.png"
|
31
|
+
When I run `simulacrum`
|
32
|
+
Then a candidate for "ui_component" should exist
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
Given(/^a fixture application$/) do
|
5
|
+
step %Q(a directory named "app")
|
6
|
+
target_path = File.join(ENV['PROJECT_ROOT_PATH'], 'fixtures', 'app')
|
7
|
+
FileUtils.cp_r(target_path, current_dir)
|
8
|
+
step %Q(I cd to "app")
|
9
|
+
end
|
10
|
+
|
11
|
+
When(/^I debug$/) do
|
12
|
+
# rubocop:disable Debugger
|
13
|
+
require 'pry'
|
14
|
+
binding.pry
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
Given(/^a (reference|candidate) image for "([^"]*)" with content: "([^"]*)"$/) do |type, component_name, fixture_path|
|
3
|
+
fixture_path = File.join(ENV['PROJECT_ROOT_PATH'], 'fixtures', fixture_path)
|
4
|
+
destination_path = File.join('spec/ui/references/', component_name, 'test_driver', "#{type}.png")
|
5
|
+
rel_destination_path = File.join(current_dir, destination_path)
|
6
|
+
FileUtils.mkdir_p(File.dirname(rel_destination_path))
|
7
|
+
FileUtils.cp(fixture_path, rel_destination_path)
|
8
|
+
step %Q(a #{type} for "#{component_name}" should exist)
|
9
|
+
end
|
10
|
+
|
11
|
+
Then(/^a (reference|candidate) for "(.*?)" should not exist$/) do |type, component_name|
|
12
|
+
path = File.join('spec/ui/references/', component_name, 'test_driver', "#{type}.png")
|
13
|
+
step %Q(a file named "#{path}" should not exist)
|
14
|
+
end
|
15
|
+
|
16
|
+
Then(/^a (reference|candidate) for "(.*?)" should exist$/) do |type, component_name|
|
17
|
+
path = File.join('spec/ui/references/', component_name, 'test_driver', "#{type}.png")
|
18
|
+
step %Q(a file named "#{path}" should exist)
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'aruba'
|
3
|
+
require 'aruba/cucumber'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'dotenv'
|
6
|
+
Dotenv.load
|
7
|
+
|
8
|
+
ENV['PROJECT_ROOT_PATH'] = File.expand_path('../../../', __FILE__)
|
9
|
+
|
10
|
+
Dir.glob('features/step_definitions/**/*steps.rb') { |f| load f, true }
|
11
|
+
|
12
|
+
Before do
|
13
|
+
@aruba_timeout_seconds = (ENV['CI']) ? 240 : 120
|
14
|
+
@aruba_io_wait_seconds = (ENV['CI']) ? 240 : 120
|
15
|
+
end
|
data/fixtures/a1.png
ADDED
Binary file
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'sinatra/base'
|
3
|
+
|
4
|
+
# Fixture application
|
5
|
+
class FixtureApp < Sinatra::Application
|
6
|
+
set :public_folder, File.dirname(__FILE__) + '/public'
|
7
|
+
|
8
|
+
if app_file == $PROGRAM_NAME
|
9
|
+
puts '[fixture app] Booting...' * 100
|
10
|
+
run!
|
11
|
+
end
|
12
|
+
end
|
Binary file
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<meta charset="UTF-8">
|
4
|
+
<title>Simulacrum Example — Index</title>
|
5
|
+
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no,user-scalable=0">
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
<img src="/images/a1.png" id="test-capture-selector">
|
9
|
+
</body>
|
10
|
+
</html>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
ENV['RAILS_ENV'] = 'test'
|
3
|
+
ENV['RACK_ENV'] = 'test'
|
4
|
+
|
5
|
+
require './fixture_app'
|
6
|
+
require 'capybara'
|
7
|
+
require 'sauce/capybara'
|
8
|
+
require 'simulacrum'
|
9
|
+
|
10
|
+
Capybara.app = FixtureApp
|
11
|
+
Capybara.run_server = true
|
12
|
+
Capybara.app_host = "http://0.0.0.0:#{Capybara.server_port}"
|
13
|
+
Capybara.default_driver = :test_driver
|
14
|
+
|
15
|
+
# Defines a custom driver so that we can assume a predictable output dir
|
16
|
+
Capybara.register_driver :test_driver do |app|
|
17
|
+
if ENV['CI']
|
18
|
+
caps = Selenium::WebDriver::Remote::Capabilities.firefox
|
19
|
+
caps.platform = 'Linux'
|
20
|
+
caps.version = '31'
|
21
|
+
caps['name'] = 'Simulacrum'
|
22
|
+
if ENV['TRAVIS']
|
23
|
+
caps['build'] = "#{ENV['TRAVIS_JOB_NUMBER']} (#{ENV['TRAVIS_COMMIT']})"
|
24
|
+
caps['tunnel-identifier'] = ENV['TRAVIS_JOB_NUMBER']
|
25
|
+
end
|
26
|
+
caps['selenium-version'] = '2.41.0'
|
27
|
+
url = "http://#{ENV['SAUCE_USERNAME']}:#{ENV['SAUCE_ACCESS_KEY']}@ondemand.saucelabs.com:80/wd/hub"
|
28
|
+
Capybara::Selenium::Driver.new(app, browser: :remote, url: url, desired_capabilities: caps)
|
29
|
+
else
|
30
|
+
Capybara::Selenium::Driver.new(app, browser: :firefox)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Simulacrum.configure do |config|
|
35
|
+
config.component.capture_selector = '#test-capture-selector'
|
36
|
+
config.component.delta_threshold = 0.1 # allow for colour-space differences between platforms
|
37
|
+
end
|
data/fixtures/diff.png
ADDED
Binary file
|
data/lib/simulacrum.rb
CHANGED
@@ -1,37 +1,96 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'yaml'
|
1
3
|
require 'ostruct'
|
2
4
|
require 'capybara'
|
3
|
-
|
4
|
-
|
5
|
-
|
5
|
+
require 'logger'
|
6
|
+
require 'simulacrum/configuration'
|
7
|
+
require 'simulacrum/runner'
|
6
8
|
|
7
9
|
# Gem module
|
8
10
|
module Simulacrum
|
9
|
-
|
11
|
+
CONFIG_FILE = './.simulacrum.yml'
|
12
|
+
|
10
13
|
@components = {}
|
11
14
|
@current_browser = nil
|
12
15
|
@configuration = Simulacrum::Configuration.new
|
16
|
+
@logger = Logger.new($stdout)
|
17
|
+
|
18
|
+
def logger
|
19
|
+
@logger
|
20
|
+
end
|
21
|
+
module_function :logger
|
13
22
|
|
14
|
-
def
|
23
|
+
def components
|
15
24
|
@components
|
16
25
|
end
|
26
|
+
module_function :components
|
17
27
|
|
18
|
-
def
|
28
|
+
def configuration
|
19
29
|
@configuration
|
20
30
|
end
|
31
|
+
module_function :configuration
|
32
|
+
|
33
|
+
def runner_options
|
34
|
+
@runner_options
|
35
|
+
end
|
36
|
+
module_function :runner_options
|
21
37
|
|
22
|
-
def
|
23
|
-
|
38
|
+
def root
|
39
|
+
File.expand_path('../..', __FILE__)
|
40
|
+
end
|
41
|
+
module_function :root
|
42
|
+
|
43
|
+
def run(options)
|
44
|
+
@runner_options = options
|
45
|
+
configure_logger
|
46
|
+
configure_runner.run
|
47
|
+
end
|
48
|
+
module_function :run
|
49
|
+
|
50
|
+
def configure(&block)
|
51
|
+
options = OpenStruct.new(component: OpenStruct.new)
|
24
52
|
yield options
|
25
|
-
|
53
|
+
configuration.configure(options.to_h)
|
26
54
|
end
|
55
|
+
module_function :configure
|
27
56
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
57
|
+
def config_file
|
58
|
+
YAML.load_file(Simulacrum.config_file_path)
|
59
|
+
end
|
60
|
+
module_function :config_file
|
31
61
|
|
32
|
-
|
33
|
-
|
34
|
-
|
62
|
+
def config_file?
|
63
|
+
File.exist?(Simulacrum.config_file_path)
|
64
|
+
end
|
65
|
+
module_function :config_file?
|
66
|
+
|
67
|
+
def config_file_path
|
68
|
+
if defined? Rails
|
69
|
+
Rails.root.join(CONFIG_FILE)
|
70
|
+
else
|
71
|
+
CONFIG_FILE
|
72
|
+
end
|
73
|
+
end
|
74
|
+
module_function :config_file_path
|
75
|
+
|
76
|
+
def self.configure_runner
|
77
|
+
case Simulacrum.runner_options.runner
|
78
|
+
when nil
|
79
|
+
Simulacrum::Runner.new
|
80
|
+
when :browserstack
|
81
|
+
use_browserstack_runner
|
35
82
|
end
|
36
83
|
end
|
84
|
+
|
85
|
+
def self.use_browserstack_runner
|
86
|
+
gem 'simulacrum-browserstack', '>= 0.0.1'
|
87
|
+
require 'simulacrum-browserstack'
|
88
|
+
Simulacrum::Browserstack::Runner.new
|
89
|
+
rescue Gem::LoadError
|
90
|
+
raise
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.configure_logger
|
94
|
+
@logger.level = @runner_options.verbose ? Logger::DEBUG : Logger::INFO
|
95
|
+
end
|
37
96
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'simulacrum/cli/parser'
|
3
|
+
|
4
|
+
module Simulacrum
|
5
|
+
# Command-line interface for driving Simulacrum
|
6
|
+
module CLI
|
7
|
+
def execute!(argv)
|
8
|
+
Command.new(argv).run_and_exit
|
9
|
+
end
|
10
|
+
module_function :execute!
|
11
|
+
|
12
|
+
# Class for wrappin up logic for running the process and handling exit
|
13
|
+
class Command
|
14
|
+
def initialize(argv, stdin = $stdin, stdout = $stdout, stderr = $stderr, kernel = Kernel)
|
15
|
+
@argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
|
16
|
+
end
|
17
|
+
|
18
|
+
def run_and_exit
|
19
|
+
@exit_code = run
|
20
|
+
@kernel.exit(@exit_code)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def run
|
26
|
+
if parsed_argv == true
|
27
|
+
0
|
28
|
+
else
|
29
|
+
Simulacrum.run(parsed_argv)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def parsed_argv
|
34
|
+
@parsed_argv ||= CLI::Parser.parse(@argv)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|