simulacrum-browserstack 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6d33c5ed98259b24be10580a2e31b397eb97551d
4
+ data.tar.gz: c80c8ea8b9ec5656acaaa6d10cfac461007ce05f
5
+ SHA512:
6
+ metadata.gz: 90d4e359d4504ae1a354a2f9cbc9694b37c89895c780364d99b38f4ac36210be7455ce179f5b69182f8544926402adf016e6e9d006d558e65a18c102ed59da09
7
+ data.tar.gz: cd99c323cee573b9a63e5a4cc4e740fdd4e69d4180374f4a80101b17e1015ae63523b283b84aa9a4fcc278c3652318c6b1b006906b0ad34ebf1f73092f00dc6d
data/.gitignore ADDED
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in simulacrum-browserstack.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Justin Morris
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ simulacrum-browserstack
2
+ =======================
3
+
4
+ A BrowserStack runner for Simulacrum
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,7 @@
1
+ require 'simulacrum/browserstack/version'
2
+
3
+ module Simulacrum
4
+ module Browserstack
5
+ # Your code goes here...
6
+ end
7
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: UTF-8
2
+ require 'uri'
3
+ require 'net/https'
4
+
5
+ module Simulacrum
6
+ module Browserstack
7
+ # The Browserstack API class wraps up functionality for talking to the
8
+ # Browserstack REST API.
9
+ class API
10
+ def initialize(username, apikey)
11
+ @username = username
12
+ @apikey = apikey
13
+ end
14
+
15
+ def account_details
16
+ request = request('https://www.browserstack.com/automate/plan.json')
17
+ account_details = OpenStruct.new
18
+ account_details.sessions_running = request['parallel_sessions_running'].to_i
19
+ account_details.sessions_allowed = request['parallel_sessions_max_allowed'].to_i
20
+ account_details
21
+ end
22
+
23
+ private
24
+
25
+ def request(url)
26
+ response = make_request(url)
27
+ JSON.parse(response.body)
28
+ end
29
+
30
+ def parse_url(url)
31
+ URI.parse(url)
32
+ end
33
+
34
+ def prepare_http(uri)
35
+ http = Net::HTTP.new(uri.host, uri.port)
36
+ http.read_timeout = 30
37
+ http.use_ssl = true
38
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
39
+ http
40
+ end
41
+
42
+ def make_request(url)
43
+ uri = parse_url(url)
44
+ http = prepare_http(uri)
45
+ request = Net::HTTP::Get.new(uri.request_uri)
46
+ request.basic_auth(@username, @apikey)
47
+ http.request(request)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+
3
+ module Capybara
4
+ module Selenium
5
+ # Monkey patch the Capybara Driver class to ensure that we can call `quit`
6
+ # at the end of a parallel process run and not generate errors if capybara
7
+ # then attempts to quit.
8
+ #
9
+ # We need to do this so that sessions are terminated as early as possible
10
+ # at the remote end.
11
+ class Driver < Capybara::Driver::Base
12
+ def quit
13
+ @browser.quit if @browser
14
+ rescue Errno::ECONNREFUSED,
15
+ ::Selenium::WebDriver::Error::UnknownError
16
+ # Browser must have already gone
17
+ ensure
18
+ @browser = nil
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,93 @@
1
+ # encoding: UTF-8
2
+ require 'simulacrum/driver'
3
+ require 'capybara'
4
+ require 'selenium-webdriver'
5
+ require 'selenium/webdriver/remote/http/persistent'
6
+
7
+ module Simulacrum
8
+ module Browserstack
9
+ # Responsible for registering a custom Capybara driver for use with
10
+ # Selenium grid endpoints (Browserstack, SauceLabs, etc.). Configures
11
+ # selenium options via ENV vars so that they can be passed into
12
+ class Driver < Simulacrum::Driver
13
+ private
14
+
15
+ def configuration
16
+ {
17
+ browser: :remote,
18
+ url: selenium_remote_url,
19
+ desired_capabilities: capabilities,
20
+ http_client: persistent_http_client
21
+ }
22
+ end
23
+
24
+ def persistent_http_client
25
+ Selenium::WebDriver::Remote::Http::Persistent.new
26
+ end
27
+
28
+ def capabilities
29
+ caps = Selenium::WebDriver::Remote::Capabilities.new
30
+ caps['project'] = Simulacrum.configuration.project_name
31
+ caps['build'] = Simulacrum.configuration.build_name
32
+ caps['browserstack.local'] = true
33
+ caps['browserstack.debug'] = false
34
+ caps['browser'] = browser
35
+ caps['browserName'] = browser_name
36
+ caps['browser_version'] = browser_version
37
+ caps['os'] = os
38
+ caps['os_version'] = os_version
39
+ caps['device'] = device
40
+ caps['deviceOrientation'] = device_orientation
41
+ caps['platform'] = platform
42
+ caps['resolution'] = resolution
43
+ caps['requireWindowFocus'] = require_window_focus
44
+ caps['realMobile'] = real_mobile
45
+ caps
46
+ end
47
+
48
+ def browser
49
+ ENV['SELENIUM_BROWSER']
50
+ end
51
+
52
+ def browser_name
53
+ ENV['BS_BROWSERNAME'] || ''
54
+ end
55
+
56
+ def real_mobile
57
+ ENV['BS_REALMOBILE'] || false
58
+ end
59
+
60
+ def browser_version
61
+ ENV['SELENIUM_VERSION'] || ''
62
+ end
63
+
64
+ def os
65
+ ENV['BS_AUTOMATE_OS'] || ''
66
+ end
67
+
68
+ def os_version
69
+ ENV['BS_AUTOMATE_OS_VERSION'] || ''
70
+ end
71
+
72
+ def device
73
+ ENV['BS_AUTOMATE_DEVICE'] || ''
74
+ end
75
+
76
+ def device_orientation
77
+ ENV['BS_AUTOMATE_DEVICEORIENTATION'] || ''
78
+ end
79
+
80
+ def platform
81
+ ENV['BS_AUTOMATE_PLATFORM'] || ''
82
+ end
83
+
84
+ def resolution
85
+ ENV['BS_AUTOMATE_RESOLUTION'] || ''
86
+ end
87
+
88
+ def require_window_focus
89
+ ENV['BS_AUTOMATE_REQUIREWINDOWFOCUS'] || ''
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,42 @@
1
+ # # encoding: UTF-8
2
+
3
+ # module Simulacrum
4
+ # module Browserstack
5
+ # # Class for ensuring BrowserStackLocal is available
6
+ # class LocalWrapper
7
+ # Simulacrum.logger.debug('BrowserStack') { 'Downloading BrowserStackLocal binary for ' }
8
+
9
+ # def download_binary
10
+
11
+ # end
12
+
13
+ # "https://www.browserstack.com/browserstack-local/BrowserStackLocal-"
14
+
15
+ # private
16
+
17
+ # def platform
18
+
19
+ # end
20
+
21
+ # def windows?
22
+ # (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
23
+ # end
24
+
25
+ # def mac?
26
+ # (/darwin/ =~ RUBY_PLATFORM) != nil
27
+ # end
28
+
29
+ # def linux?
30
+ # (/linux/ =~ RUBY_PLATFORM) != nil
31
+ # end
32
+
33
+ # def x64?
34
+ # (/x86_64/ =~ RUBY_PLATFORM) != nil
35
+ # end
36
+
37
+ # def i386?
38
+ # (/i386/ =~ RUBY_PLATFORM) != nil
39
+ # end
40
+ # end
41
+ # end
42
+ # end
@@ -0,0 +1,193 @@
1
+ # encoding: UTF-8
2
+ require_relative './capybara_patch'
3
+ require_relative './tunnel'
4
+ require_relative './summary'
5
+ require_relative './api'
6
+ require_relative './driver'
7
+ require 'simulacrum/runner'
8
+ require 'parallel'
9
+ require 'yaml'
10
+ require 'retries'
11
+ require 'pry'
12
+
13
+ module Simulacrum
14
+ module Browserstack
15
+ # A Runner Class for Browserstack that handles creating a Browserstack
16
+ # tunnel, closing it when done. Also handles running the suite in parallel.
17
+ class Runner < Simulacrum::Runner
18
+ # Exception to indicate that Browserstack has no available sessions
19
+ # to start a new test run, this is used inside a retries loop but will
20
+ # be raised if maximum retries is exceeded
21
+ class NoRemoteSessionsAvailable < RuntimeError; end
22
+
23
+ attr_reader :app_ports
24
+
25
+ def initialize
26
+ start_timer
27
+
28
+ @username = Simulacrum.runner_options.username
29
+ @apikey = Simulacrum.runner_options.apikey
30
+ @app_ports = app_ports
31
+ @api = Simulacrum::Browserstack::API.new(@username, @apikey)
32
+ @tunnel = Simulacrum::Browserstack::Tunnel.new(@username, @apikey, @app_ports)
33
+
34
+ set_global_env
35
+ execute
36
+ summarize_results
37
+ summarize_exit_codes
38
+ ensure
39
+ @tunnel.close if @tunnel
40
+ end
41
+
42
+ def execute
43
+ Simulacrum.logger.info('BrowserStack') { "Using runner with #{processes} remote workers" }
44
+ @process_exit_codes, @process_results = Parallel.map_with_index(browsers, in_processes: processes) do |(name, caps), index|
45
+ begin
46
+ ensure_available_remote_runner
47
+ configure_app_port(index)
48
+ configure_environment(name, caps)
49
+ configure_browser_setting(name)
50
+ exit_code = run
51
+ [exit_code, { results: dump_results }]
52
+ rescue SystemExit
53
+ exit 1
54
+ ensure
55
+ quit_browser
56
+ end
57
+ end.transpose
58
+ ensure
59
+ stop_timer
60
+ end
61
+
62
+ private
63
+
64
+ def quit_browser
65
+ Capybara.current_session.driver.browser.quit
66
+ end
67
+
68
+ def configure_browser_setting(name)
69
+ RSpec.configuration.around do |example|
70
+ example.metadata[:browser] = name
71
+ begin
72
+ example.run
73
+ end
74
+ end
75
+ end
76
+
77
+ def configure_rspec
78
+ super
79
+ RSpec.configuration.instance_variable_set(:@reporter, reporter)
80
+ end
81
+
82
+ def configure_driver
83
+ Simulacrum::Browserstack::Driver.use
84
+ end
85
+
86
+ def reporter
87
+ @reporter ||= RSpec::Core::Reporter.new(formatter)
88
+ end
89
+
90
+ def formatter
91
+ @formatter ||= Simulacrum::Formatters::SimulacrumFormatter.new($stdout)
92
+ end
93
+
94
+ def dump_results
95
+ Marshal.dump(formatter.output_hash)
96
+ end
97
+
98
+ def ensure_available_remote_runner
99
+ with_retries(max_tries: 20, base_sleep_seconds: 0.5, max_sleep_seconds: 15) do
100
+ remote_worker_available?
101
+ end
102
+ end
103
+
104
+ def remote_worker_available?
105
+ account_details = @api.account_details
106
+ unless account_details.sessions_running < account_details.sessions_allowed
107
+ fail NoRemoteSessionsAvailable
108
+ end
109
+ end
110
+
111
+ def start_timer
112
+ @start_time = Time.now
113
+ end
114
+
115
+ def stop_timer
116
+ @end_time = Time.now
117
+ end
118
+
119
+ def summarize_results
120
+ summary = Simulacrum::Browserstack::Summary.new(@process_results, @start_time, @end_time)
121
+ summary.dump_summary
122
+ summary.dump_failures
123
+ summary.dump_pending
124
+ end
125
+
126
+ def summarize_exit_codes
127
+ @exit_code = (@process_exit_codes.inject(&:+) == 0) ? 0 : 1
128
+ end
129
+
130
+ def configure_app_port(index)
131
+ ENV['APP_SERVER_PORT'] = app_ports[index].to_s
132
+ end
133
+
134
+ # rubocop:disable MethodLength
135
+ def configure_environment(name, caps)
136
+ ENV['SELENIUM_REMOTE_URL'] = @tunnel.selenium_remote_url
137
+ ENV['BS_DRIVER_NAME'] = name
138
+ ENV['SELENIUM_BROWSER'] = caps['browser']
139
+ ENV['SELENIUM_VERSION'] = caps['browser_version'].to_s
140
+ ENV['BS_AUTOMATE_OS'] = caps['os']
141
+ ENV['BS_AUTOMATE_OS_VERSION'] = caps['os_version'].to_s
142
+ ENV['BS_AUTOMATE_RESOLUTION'] = caps['resolution']
143
+ ENV['BS_AUTOMATE_REQUIREWINDOWFOCUS'] = caps['requireWindowFocus'].to_s
144
+ ENV['BS_AUTOMATE_PLATFORM'] = caps['platform']
145
+ ENV['BS_AUTOMATE_DEVICE'] = caps['device']
146
+ ENV['BS_AUTOMATE_DEVICEORIENTATION'] = caps['deviceOrientation']
147
+ ENV['BS_BROWSERNAME'] = caps['browserName']
148
+ ENV['BS_REALMOBILE'] = caps['realMobile'].to_s
149
+ end
150
+ # rubocop:enable MethodLength
151
+
152
+ def processes
153
+ Simulacrum.runner_options.max_processes || @api.account_details.sessions_allowed
154
+ end
155
+
156
+ def set_global_env
157
+ ENV['SELENIUM_REMOTE_URL'] = @tunnel.selenium_remote_url
158
+ end
159
+
160
+ def app_ports
161
+ @app_ports ||= begin
162
+ if browsers.any?
163
+ browsers.length.times.map { find_available_port }
164
+ else
165
+ [find_available_port]
166
+ end
167
+ end
168
+ end
169
+
170
+ def find_available_port
171
+ server = TCPServer.new('127.0.0.1', 0)
172
+ server.addr[1]
173
+ ensure
174
+ server.close if server
175
+ end
176
+
177
+ def browsers
178
+ @browsers ||= begin
179
+ if Simulacrum.config_file?
180
+ browsers = Simulacrum.config_file['browsers']
181
+ browsers = browsers.select do |name, value|
182
+ name == Simulacrum.runner_options.browser
183
+ end if Simulacrum.runner_options.browser
184
+ browsers
185
+ else
186
+ # TODO: Raise a better error...
187
+ fail 'DERP!'
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,89 @@
1
+ # encoding: UTF-8
2
+ require 'rspec/core/formatters/helpers'
3
+ require 'rspec/core/formatters/base_text_formatter'
4
+
5
+ module Simulacrum
6
+ module Browserstack
7
+ # The Summary Class is responsible for combining dumped examlpes from one
8
+ # or more other RSpec runs.
9
+ class Summary < RSpec::Core::Formatters::BaseTextFormatter
10
+ # TODO: Rename me, I'm really just a formatter anyway
11
+ # TODO: Also, should this just be an external gem...? Prolly not?
12
+ def initialize(results_set, start_time, end_time)
13
+ super($stdout)
14
+ @results_set = results_set
15
+ @start_time = start_time
16
+ @end_time = end_time
17
+ end
18
+
19
+ def dump_commands_to_rerun_failed_examples
20
+ return if failed_examples.empty?
21
+ output.puts
22
+ output.puts('Failed examples:')
23
+ output.puts
24
+
25
+ failed_examples.each do |example|
26
+ output.puts(failure_color("simulacrum --browser=#{example.metadata[:browser]} #{RSpec::Core::Metadata::relative_path(example.location)}") + ' ' + detail_color("# #{example.full_description}"))
27
+ end
28
+ end
29
+
30
+ def dump_summary
31
+ super(duration, example_count, failure_count, pending_count)
32
+ end
33
+
34
+ def failed_examples
35
+ examples.select do |example|
36
+ example.execution_result[:status] == 'failed'
37
+ end
38
+ end
39
+
40
+ def pending_examples
41
+ examples.select do |example|
42
+ example.execution_result[:status] == 'pending'
43
+ end
44
+ end
45
+
46
+ def duration
47
+ @end_time - @start_time
48
+ end
49
+
50
+ def example_count
51
+ summaries.map { |x| x[:example_count] }.reduce(:+)
52
+ end
53
+
54
+ def failure_count
55
+ summaries.map { |x| x[:failure_count] }.reduce(:+)
56
+ end
57
+
58
+ def pending_count
59
+ summaries.map { |x| x[:pending_count] }.reduce(:+)
60
+ end
61
+
62
+ def find_shared_group(example)
63
+ end
64
+
65
+ def group_and_parent_groups(example)
66
+ end
67
+
68
+ private
69
+
70
+ def color_enabled?
71
+ Simulacrum.runner_options.color
72
+ end
73
+
74
+ def examples
75
+ dumped_results.map { |result| result[:examples] }.flatten
76
+ end
77
+
78
+ def summaries
79
+ dumped_results.map { |result| result[:summary] }.flatten
80
+ end
81
+
82
+ def dumped_results
83
+ @dumped_results ||= @results_set.map do |result|
84
+ Marshal.load(result[:results])
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,105 @@
1
+ # encoding: UTF-8
2
+ require 'timeout'
3
+ require 'fileutils'
4
+ require 'English'
5
+
6
+ module Simulacrum
7
+ module Browserstack
8
+ # Class for handling Browserstack tunnel opening/closing/management
9
+ class Tunnel
10
+ attr_reader :selenium_remote_url, :pid, :ports, :open
11
+ alias_method :open?, :open
12
+
13
+ DEFAULT_OPTIONS = {
14
+ skip_check: true,
15
+ only_automate: false,
16
+ verbose: false,
17
+ force: true
18
+ }
19
+
20
+ def initialize(username, apikey, ports, options = {})
21
+ @pid = nil
22
+ @open = false
23
+ @username = username
24
+ @apikey = apikey
25
+ @ports = ports
26
+ @options = OpenStruct.new(DEFAULT_OPTIONS.clone.merge!(options))
27
+
28
+ create_tunnel
29
+ ensure_open
30
+ end
31
+
32
+ def selenium_remote_url
33
+ "http://#{@username}:#{@apikey}@hub.browserstack.com/wd/hub"
34
+ end
35
+
36
+ def close
37
+ kill
38
+ end
39
+
40
+ private
41
+
42
+ def binary_path
43
+ binary_path = `which BrowserStackLocal`.strip
44
+ if $CHILD_STATUS.success? && File.executable?(binary_path)
45
+ binary_path
46
+ else
47
+ Simulacrum.logger.fail('BrowserStack') { 'BrowserStackLocal binary not found or not executable' }
48
+ fail
49
+ end
50
+ end
51
+
52
+ def create_tunnel
53
+ @process = IO.popen(command)
54
+ @pid = @process.pid
55
+ Simulacrum.logger.debug('BrowserStack') { "Openning tunnel (pid #{@pid})" }
56
+ end
57
+
58
+ def command
59
+ cmd = [binary_path]
60
+ cmd << '-skipCheck' if @options.skip_check == true
61
+ cmd << '-onlyAutomate' if @options.only_automate == true
62
+ cmd << '-force' if @options.force == true
63
+ cmd << '-v' if @options.verbose == true
64
+ cmd << @apikey
65
+ cmd << formatted_ports
66
+ cmd.join(' ')
67
+ end
68
+
69
+ def formatted_ports
70
+ ports.map { |port| "localhost,#{port},0" }.join(',')
71
+ end
72
+
73
+ def kill
74
+ Process.kill('TERM', @pid) if running?
75
+ end
76
+
77
+ def ensure_open
78
+ Simulacrum.logger.debug('BrowserStack') { 'Waiting for tunnel to open' }
79
+ Timeout.timeout(240) do
80
+ sleep 1 until tunnel_open?
81
+ end
82
+ @open = true
83
+ Simulacrum.logger.debug('BrowserStack') { 'Tunnel open' }
84
+ rescue Timeout::Error
85
+ Simulacrum.logger.debug('BrowserStack') { 'Tunnel failed to open, exiting.' }
86
+ exit(1)
87
+ end
88
+
89
+ def tunnel_open?
90
+ `curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:45691`.to_i == 200
91
+ end
92
+
93
+ def running?
94
+ if @pid.nil?
95
+ false
96
+ else
97
+ Process.getpgid(@pid)
98
+ true
99
+ end
100
+ rescue Errno::ESRCH
101
+ false
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,5 @@
1
+ module Simulacrum
2
+ module Browserstack
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'simulacrum/browserstack/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'simulacrum-browserstack'
8
+ gem.version = Simulacrum::Browserstack::VERSION
9
+ gem.authors = ['Justin Morris']
10
+ gem.email = ['desk@pixelbloom.com']
11
+ gem.summary = %q{BrowserStack runner for Simulacrum}
12
+ gem.description = %q{BrowserStack runner for Simulacrum}
13
+ gem.homepage = ''
14
+ gem.license = 'MIT'
15
+
16
+ gem.files = `git ls-files -z`.split("\x0")
17
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ['lib']
20
+
21
+ gem.add_dependency 'parallel', ['~> 1.2.0']
22
+ gem.add_dependency 'net-http-persistent'
23
+
24
+ gem.add_development_dependency 'bundler', '~> 1.6'
25
+ gem.add_development_dependency 'rake'
26
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simulacrum-browserstack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Justin Morris
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: parallel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: net-http-persistent
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.6'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: BrowserStack runner for Simulacrum
70
+ email:
71
+ - desk@pixelbloom.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - LICENSE
79
+ - README.md
80
+ - Rakefile
81
+ - lib/simulacrum/browserstack.rb
82
+ - lib/simulacrum/browserstack/api.rb
83
+ - lib/simulacrum/browserstack/capybara_patch.rb
84
+ - lib/simulacrum/browserstack/driver.rb
85
+ - lib/simulacrum/browserstack/local_wrapper.rb
86
+ - lib/simulacrum/browserstack/runner.rb
87
+ - lib/simulacrum/browserstack/summary.rb
88
+ - lib/simulacrum/browserstack/tunnel.rb
89
+ - lib/simulacrum/browserstack/version.rb
90
+ - simulacrum-browserstack.gemspec
91
+ homepage: ''
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.0.14
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: BrowserStack runner for Simulacrum
115
+ test_files: []
116
+ has_rdoc: