simulacrum 0.1.1 → 0.3.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/.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
|