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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +9 -0
  3. data/.env.example +2 -0
  4. data/.gitignore +10 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +14 -0
  7. data/.travis.yml +28 -0
  8. data/Gemfile +2 -0
  9. data/README.md +78 -19
  10. data/Rakefile +20 -0
  11. data/examples/README.md +3 -0
  12. data/examples/basic/Gemfile +4 -0
  13. data/examples/basic/README.md +13 -0
  14. data/examples/basic/config.ru +3 -0
  15. data/examples/basic/example_app.rb +7 -0
  16. data/examples/basic/public/button.html +13 -0
  17. data/examples/basic/public/index.html +22 -0
  18. data/examples/basic/public/panel.html +13 -0
  19. data/examples/basic/public/stylesheets/button.css +15 -0
  20. data/examples/basic/public/stylesheets/main.css +3 -0
  21. data/examples/basic/public/stylesheets/normalize.css +425 -0
  22. data/examples/basic/script/start +4 -0
  23. data/examples/basic/spec/simulacrum_helper.rb +9 -0
  24. data/examples/basic/spec/ui/button_spec.rb +10 -0
  25. data/exe/simulacrum +5 -0
  26. data/features/command_line/help.feature +8 -0
  27. data/features/exit_codes/failing.feature +24 -0
  28. data/features/exit_codes/passing.feature +24 -0
  29. data/features/exit_codes/pending.feature +22 -0
  30. data/features/output/candidate.feature +32 -0
  31. data/features/output/diff.feature +5 -0
  32. data/features/step_definitions/dummy_steps.rb +15 -0
  33. data/features/step_definitions/file_steps.rb +19 -0
  34. data/features/support/env.rb +15 -0
  35. data/fixtures/a1.png +0 -0
  36. data/fixtures/app/fixture_app.rb +12 -0
  37. data/fixtures/app/public/images/a1.png +0 -0
  38. data/fixtures/app/public/ui_component.html +10 -0
  39. data/fixtures/app/spec/component_spec.rb +9 -0
  40. data/fixtures/app/spec/simulacrum_helper.rb +37 -0
  41. data/fixtures/app/spec/ui/references/ui_component/test_driver/candidate.png +0 -0
  42. data/fixtures/diff.png +0 -0
  43. data/lib/simulacrum.rb +74 -15
  44. data/lib/simulacrum/cli.rb +38 -0
  45. data/lib/simulacrum/cli/parser.rb +152 -0
  46. data/lib/simulacrum/comparator.rb +15 -15
  47. data/lib/simulacrum/component.rb +22 -11
  48. data/lib/simulacrum/configuration.rb +20 -13
  49. data/lib/simulacrum/diff.rb +2 -0
  50. data/lib/simulacrum/diff/rmagick.rb +8 -6
  51. data/lib/simulacrum/driver.rb +45 -0
  52. data/lib/simulacrum/matchers.rb +6 -16
  53. data/lib/simulacrum/methods.rb +1 -0
  54. data/lib/simulacrum/renderer.rb +23 -8
  55. data/lib/simulacrum/runner.rb +44 -0
  56. data/lib/simulacrum/version.rb +2 -1
  57. data/rubocop-todo.yml +29 -0
  58. data/script/bootstrap +3 -0
  59. data/script/quality +7 -0
  60. data/script/spec +10 -0
  61. data/simulacrum.gemspec +52 -0
  62. data/spec/lib/simulacrum/cli/parser_spec.rb +113 -0
  63. data/spec/lib/simulacrum/cli_spec.rb +18 -0
  64. data/spec/lib/simulacrum/comparator_spec.rb +75 -0
  65. data/spec/lib/simulacrum/component_spec.rb +208 -0
  66. data/spec/lib/simulacrum/driver/local_spec.rb +11 -0
  67. data/spec/lib/simulacrum/version_spec.rb +12 -0
  68. data/spec/lib/simulacrum_spec.rb +53 -0
  69. data/spec/spec_helper.rb +13 -8
  70. data/spec/use_codeclimate.rb +3 -0
  71. data/spec/use_simplecov.rb +5 -12
  72. metadata +217 -32
  73. data/lib/simulacrum/diff/pdiff.rb +0 -47
  74. data/spec/fixtures/a.png +0 -0
  75. data/spec/fixtures/a2.png +0 -0
  76. data/spec/use_coveralls.rb +0 -2
@@ -0,0 +1,4 @@
1
+ #!/bin/sh -xe
2
+
3
+ bundle --quiet
4
+ rackup --port 8000
@@ -0,0 +1,9 @@
1
+ require 'capybara'
2
+ require 'simulacrum'
3
+ require './example_app'
4
+
5
+ Capybara.app = ExampleApp
6
+
7
+ Simulacrum.configure do |simulacrum|
8
+ simulacrum.component.delta_threshold = 1.0 # 1.0% change allowed
9
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+ require 'simulacrum_helper'
3
+
4
+ describe 'My Button' do
5
+ component :my_button do |options|
6
+ options.url = '/button.html'
7
+ end
8
+
9
+ it { is_expected.to look_the_same }
10
+ end
data/exe/simulacrum ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require 'simulacrum/cli'
5
+ Simulacrum::CLI.execute!(ARGV.dup)
@@ -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,5 @@
1
+ Feature: diff image output
2
+
3
+ A diff image should be produced when the diff threshold is met.
4
+
5
+ Scenario: There is a diff when the test is run
@@ -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,9 @@
1
+ # encoding: UTF-8
2
+ require 'simulacrum_helper'
3
+
4
+ describe 'UI Component' do
5
+ component :ui_component do |options|
6
+ options.url = '/ui_component.html'
7
+ end
8
+ it { should look_the_same }
9
+ end
@@ -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
- require_relative './simulacrum/methods'
4
- require_relative './simulacrum/matchers'
5
- require_relative './simulacrum/configuration'
5
+ require 'logger'
6
+ require 'simulacrum/configuration'
7
+ require 'simulacrum/runner'
6
8
 
7
9
  # Gem module
8
10
  module Simulacrum
9
- @browsers = {}
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 self.components
23
+ def components
15
24
  @components
16
25
  end
26
+ module_function :components
17
27
 
18
- def self.configuration
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 self.configure(&block)
23
- options = OpenStruct.new(defaults: OpenStruct.new)
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
- @configuration.configure(options.to_h)
53
+ configuration.configure(options.to_h)
26
54
  end
55
+ module_function :configure
27
56
 
28
- def self.included(receiver, &block)
29
- receiver.extend Simulacrum::Methods
30
- receiver.send :include, Simulacrum::Matchers
57
+ def config_file
58
+ YAML.load_file(Simulacrum.config_file_path)
59
+ end
60
+ module_function :config_file
31
61
 
32
- if defined?(Rails)
33
- receiver.send :include, Rails.application.routes.url_helpers
34
- receiver.send :include, Rails.application.routes.mounted_helpers
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