simulacrum 0.1.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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