testingbot 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,6 +1,4 @@
1
1
  source "http://rubygems.org"
2
2
  gem 'rspec'
3
- gem 'cucumber'
4
- gem 'capybara'
5
3
  # Specify your gem's dependencies in testingbot.gemspec
6
4
  gemspec
data/README.rdoc CHANGED
@@ -233,5 +233,5 @@ Get more information on http://testingbot.com
233
233
 
234
234
  == Copyright
235
235
 
236
- Copyright (c) 2012 TestingBot
236
+ Copyright (c) TestingBot
237
237
  Please see our LICENSE
data/bin/testingbot CHANGED
File without changes
data/examples/android.rb CHANGED
@@ -4,13 +4,11 @@ require File.expand_path(File.dirname(__FILE__) + '/../lib/testingbot/config.rb'
4
4
  require File.expand_path(File.dirname(__FILE__) + '/../lib/testingbot/selenium.rb')
5
5
 
6
6
  TestingBot::config do |config|
7
- config[:desired_capabilities] = Selenium::WebDriver::Remote::Capabilities.iphone
8
- #config[:desired_capabilities] = { "platform" => "ANDROID", "browserName" => "galaxytab" }
7
+ config[:desired_capabilities] = Selenium::WebDriver::Remote::Capabilities.android
9
8
  end
10
9
 
11
10
  driver = TestingBot::SeleniumWebdriver.new
12
11
 
13
- driver.navigate.to "http://www.google.com"
12
+ driver.navigate.to "http://testingbot.com/"
14
13
  puts driver.title
15
- #sleep 140
16
- driver.quit
14
+ driver.quit
data/examples/capybara.rb CHANGED
@@ -4,11 +4,12 @@ require 'testingbot'
4
4
  require 'testingbot/capybara'
5
5
 
6
6
  TestingBot::config do |config|
7
- config[:desired_capabilities] = [{ :browserName => "internet explorer", :version => 9, :platform => "WINDOWS" }, { :browserName => "firefox", :version => 11, :platform => "WINDOWS" }]
7
+ config[:desired_capabilities] = { :browserName => "firefox", :version => 9, :platform => "WINDOWS" }
8
+ config[:options] = { :screenshot => false, :extra => "Some extra data" }
8
9
  #config.require_tunnel
9
10
  end
10
11
 
11
- describe "People", :type => :request, :multibrowser => true do
12
+ describe "People", :type => :request do
12
13
  before :all do
13
14
  Capybara.current_driver = :testingbot
14
15
  Capybara.app_host = "http://testingbot.com"
@@ -16,9 +17,6 @@ describe "People", :type => :request, :multibrowser => true do
16
17
 
17
18
  it 'has a homepage with the word Selenium' do
18
19
  visit '/'
19
- #sleep 150
20
- p "inspect"
21
- p page.inspect
22
20
  page.should have_content('Selenium')
23
21
  end
24
- end
22
+ end
@@ -5,7 +5,7 @@ require 'testingbot'
5
5
  require 'testingbot/tunnel'
6
6
 
7
7
  TestingBot::config do |config|
8
- config[:desired_capabilities] = [{ :browserName => "chrome", :platform => "WINDOWS" }]
8
+ config[:desired_capabilities] = [{ :browserName => "firefox", :version => 9, :platform => "WINDOWS" }, { :browserName => "firefox", :version => 11, :platform => "WINDOWS" }]
9
9
  end
10
10
 
11
11
  # rspec 2
@@ -14,4 +14,4 @@ describe "People", :type => :selenium, :multibrowser => true do
14
14
  page.navigate.to "http://www.google.com"
15
15
  page.title.should eql("Google")
16
16
  end
17
- end
17
+ end
@@ -4,7 +4,7 @@ module TestingBot
4
4
  class Api
5
5
 
6
6
  VERSION = 1
7
- API_URL = "http://api.testingbot.com"
7
+ API_URL = "https://api.testingbot.com"
8
8
 
9
9
  attr_reader :config
10
10
 
@@ -26,7 +26,7 @@ module TestingBot
26
26
  end
27
27
 
28
28
  def get_tests(offset = 0, count = 10)
29
- get("/tests")
29
+ get("/tests?offset=#{offset}&count=#{count}")
30
30
  end
31
31
 
32
32
  def get_single_test(test_id)
@@ -109,4 +109,4 @@ module TestingBot
109
109
  response = http.post(url.path, params.map { |k, v| "#{k.to_s}=#{v}" }.join("&"))
110
110
  end
111
111
  end
112
- end
112
+ end
@@ -89,7 +89,14 @@ module TestingBot
89
89
  def load_config_file
90
90
  options = {}
91
91
 
92
- is_windows = (RUBY_PLATFORM =~ /w.*32/)
92
+ is_windows = false
93
+
94
+ begin
95
+ require 'rbconfig'
96
+ is_windows = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
97
+ rescue
98
+ is_windows = (RUBY_PLATFORM =~ /w.*32/) || (ENV["OS"] && ENV["OS"] == "Windows_NT")
99
+ end
93
100
 
94
101
  if is_windows
95
102
  config_file = "#{ENV['HOMEDRIVE']}\\.testingbot"
@@ -0,0 +1,29 @@
1
+ require 'jasmine'
2
+ require 'testingbot/tunnel'
3
+
4
+ module TestingBot
5
+ module Jasmine
6
+
7
+ class Driver < ::Jasmine::SeleniumDriver
8
+ attr_reader :http_address, :driver, :browser
9
+
10
+ def initialize(browser, http_address)
11
+ tunnel = TestingBot::Tunnel.new(TestingBot.get_config[:tunnel_options] || {})
12
+ tunnel.start
13
+
14
+ @browser = browser
15
+ @http_address = http_address
16
+ @driver = TestingBot::SeleniumWebdriver.new({ :browserName => browser, :name => "Jasmine Test" })
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ module Jasmine
23
+ class Config
24
+ def start
25
+ @client = TestingBot::Jasmine::Driver.new(browser, "#{jasmine_host}:3001/")
26
+ @client.connect
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,16 @@
1
+ = TestingBot
2
+
3
+ Testingbot.com is a website where you can use our cloud based Selenium grid. Test your web applications in various environments/browsers/devices.
4
+
5
+ == How to run a Jasmine test on TestingBot
6
+
7
+ Make sure you have the Jasmine gem installed
8
+
9
+ $ gem install jasmine
10
+
11
+ Now run our example Rake task:
12
+ $ rake jasmine:testingbot
13
+
14
+ == More information
15
+
16
+ Get more information on http://testingbot.com
@@ -0,0 +1,35 @@
1
+ require 'jasmine'
2
+ require 'rspec/core/rake_task'
3
+ require 'testingbot'
4
+
5
+ namespace :jasmine do
6
+ def run_jasmine_server
7
+ ENV['JASMINE_PORT'] = '3001'
8
+ Jasmine::Config.new.start_jasmine_server
9
+ end
10
+
11
+ desc "Execute Jasmine tests in a Chrome browser on testingbot"
12
+ task :testingbot do
13
+ run_jasmine_server
14
+ Rake::Task['jasmine:testingbot:chrome'].execute
15
+ end
16
+
17
+ namespace :testingbot do
18
+ desc "Execute Jasmine tests in Chrome and Firefox on TestingBot"
19
+ task :all do
20
+ run_jasmine_server
21
+ threads = []
22
+ [:chrome, :firefox].each do |browser|
23
+ t = Thread.new do
24
+ Rake::Task["jasmine:testingbot:#{browser}"].invoke
25
+ end
26
+ t.abort_on_exception = true
27
+ threads << t
28
+ end
29
+
30
+ threads.each do |t|
31
+ t.join
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ function Player() {
2
+ }
3
+ Player.prototype.play = function(song) {
4
+ this.currentlyPlayingSong = song;
5
+ this.isPlaying = true;
6
+ };
7
+
8
+ Player.prototype.pause = function() {
9
+ this.isPlaying = false;
10
+ };
11
+
12
+ Player.prototype.resume = function() {
13
+ if (this.isPlaying) {
14
+ throw new Error("song is already playing");
15
+ }
16
+
17
+ this.isPlaying = true;
18
+ };
19
+
20
+ Player.prototype.makeFavorite = function() {
21
+ this.currentlyPlayingSong.persistFavoriteStatus(true);
22
+ };
@@ -0,0 +1,7 @@
1
+ function Song() {
2
+ }
3
+
4
+ Song.prototype.persistFavoriteStatus = function(value) {
5
+ // something complicated
6
+ throw new Error("not yet implemented");
7
+ };
@@ -0,0 +1,4 @@
1
+ require 'testingbot'
2
+ require 'testingbot/jasmine'
3
+ ENV['JASMINE_PORT'] = '3001'
4
+ require 'jasmine/runner'
@@ -0,0 +1,58 @@
1
+ describe("Player", function() {
2
+ var player;
3
+ var song;
4
+
5
+ beforeEach(function() {
6
+ player = new Player();
7
+ song = new Song();
8
+ });
9
+
10
+ it("should be able to play a Song", function() {
11
+ player.play(song);
12
+ expect(player.currentlyPlayingSong).toEqual(song);
13
+
14
+ //demonstrates use of custom matcher
15
+ expect(player).toBePlaying(song);
16
+ });
17
+
18
+ describe("when song has been paused", function() {
19
+ beforeEach(function() {
20
+ player.play(song);
21
+ player.pause();
22
+ });
23
+
24
+ it("should indicate that the song is currently paused", function() {
25
+ expect(player.isPlaying).toBeFalsy();
26
+
27
+ // demonstrates use of 'not' with a custom matcher
28
+ expect(player).not.toBePlaying(song);
29
+ });
30
+
31
+ it("should be possible to resume", function() {
32
+ player.resume();
33
+ expect(player.isPlaying).toBeTruthy();
34
+ expect(player.currentlyPlayingSong).toEqual(song);
35
+ });
36
+ });
37
+
38
+ // demonstrates use of spies to intercept and test method calls
39
+ it("tells the current song if the user has made it a favorite", function() {
40
+ spyOn(song, 'persistFavoriteStatus');
41
+
42
+ player.play(song);
43
+ player.makeFavorite();
44
+
45
+ expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
46
+ });
47
+
48
+ //demonstrates use of expected exceptions
49
+ describe("#resume", function() {
50
+ it("should throw an exception if song is already playing", function() {
51
+ player.play(song);
52
+
53
+ expect(function() {
54
+ player.resume();
55
+ }).toThrow("song is already playing");
56
+ });
57
+ });
58
+ });
@@ -0,0 +1,9 @@
1
+ beforeEach(function() {
2
+ this.addMatchers({
3
+ toBePlaying: function(expectedSong) {
4
+ var player = this.actual;
5
+ return player.currentlyPlayingSong === expectedSong &&
6
+ player.isPlaying;
7
+ }
8
+ });
9
+ });
@@ -0,0 +1,73 @@
1
+ # src_files
2
+ #
3
+ # Return an array of filepaths relative to src_dir to include before jasmine specs.
4
+ # Default: []
5
+ #
6
+ # EXAMPLE:
7
+ #
8
+ # src_files:
9
+ # - lib/source1.js
10
+ # - lib/source2.js
11
+ # - dist/**/*.js
12
+ #
13
+ src_files:
14
+ - public/javascripts/**/*.js
15
+
16
+ # stylesheets
17
+ #
18
+ # Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs.
19
+ # Default: []
20
+ #
21
+ # EXAMPLE:
22
+ #
23
+ # stylesheets:
24
+ # - css/style.css
25
+ # - stylesheets/*.css
26
+ #
27
+ stylesheets:
28
+
29
+ # helpers
30
+ #
31
+ # Return an array of filepaths relative to spec_dir to include before jasmine specs.
32
+ # Default: ["helpers/**/*.js"]
33
+ #
34
+ # EXAMPLE:
35
+ #
36
+ # helpers:
37
+ # - helpers/**/*.js
38
+ #
39
+ helpers:
40
+
41
+ # spec_files
42
+ #
43
+ # Return an array of filepaths relative to spec_dir to include.
44
+ # Default: ["**/*[sS]pec.js"]
45
+ #
46
+ # EXAMPLE:
47
+ #
48
+ # spec_files:
49
+ # - **/*[sS]pec.js
50
+ #
51
+ spec_files:
52
+
53
+ # src_dir
54
+ #
55
+ # Source directory path. Your src_files must be returned relative to this path. Will use root if left blank.
56
+ # Default: project root
57
+ #
58
+ # EXAMPLE:
59
+ #
60
+ # src_dir: public
61
+ #
62
+ src_dir:
63
+
64
+ # spec_dir
65
+ #
66
+ # Spec directory path. Your spec_files must be returned relative to this path.
67
+ # Default: spec/javascripts
68
+ #
69
+ # EXAMPLE:
70
+ #
71
+ # spec_dir: spec/javascripts
72
+ #
73
+ spec_dir:
@@ -0,0 +1,9 @@
1
+
2
+ begin
3
+ require 'jasmine'
4
+ load 'jasmine/tasks/jasmine.rake'
5
+ rescue LoadError
6
+ task :jasmine do
7
+ abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine"
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ function Player() {
2
+ }
3
+ Player.prototype.play = function(song) {
4
+ this.currentlyPlayingSong = song;
5
+ this.isPlaying = true;
6
+ };
7
+
8
+ Player.prototype.pause = function() {
9
+ this.isPlaying = false;
10
+ };
11
+
12
+ Player.prototype.resume = function() {
13
+ if (this.isPlaying) {
14
+ throw new Error("song is already playing");
15
+ }
16
+
17
+ this.isPlaying = true;
18
+ };
19
+
20
+ Player.prototype.makeFavorite = function() {
21
+ this.currentlyPlayingSong.persistFavoriteStatus(true);
22
+ };
@@ -0,0 +1,7 @@
1
+ function Song() {
2
+ }
3
+
4
+ Song.prototype.persistFavoriteStatus = function(value) {
5
+ // something complicated
6
+ throw new Error("not yet implemented");
7
+ };
@@ -0,0 +1,58 @@
1
+ describe("Player", function() {
2
+ var player;
3
+ var song;
4
+
5
+ beforeEach(function() {
6
+ player = new Player();
7
+ song = new Song();
8
+ });
9
+
10
+ it("should be able to play a Song", function() {
11
+ player.play(song);
12
+ expect(player.currentlyPlayingSong).toEqual(song);
13
+
14
+ //demonstrates use of custom matcher
15
+ expect(player).toBePlaying(song);
16
+ });
17
+
18
+ describe("when song has been paused", function() {
19
+ beforeEach(function() {
20
+ player.play(song);
21
+ player.pause();
22
+ });
23
+
24
+ it("should indicate that the song is currently paused", function() {
25
+ expect(player.isPlaying).toBeFalsy();
26
+
27
+ // demonstrates use of 'not' with a custom matcher
28
+ expect(player).not.toBePlaying(song);
29
+ });
30
+
31
+ it("should be possible to resume", function() {
32
+ player.resume();
33
+ expect(player.isPlaying).toBeTruthy();
34
+ expect(player.currentlyPlayingSong).toEqual(song);
35
+ });
36
+ });
37
+
38
+ // demonstrates use of spies to intercept and test method calls
39
+ it("tells the current song if the user has made it a favorite", function() {
40
+ spyOn(song, 'persistFavoriteStatus');
41
+
42
+ player.play(song);
43
+ player.makeFavorite();
44
+
45
+ expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
46
+ });
47
+
48
+ //demonstrates use of expected exceptions
49
+ describe("#resume", function() {
50
+ it("should throw an exception if song is already playing", function() {
51
+ player.play(song);
52
+
53
+ expect(function() {
54
+ player.resume();
55
+ }).toThrow("song is already playing");
56
+ });
57
+ });
58
+ });
@@ -0,0 +1,9 @@
1
+ beforeEach(function() {
2
+ this.addMatchers({
3
+ toBePlaying: function(expectedSong) {
4
+ var player = this.actual;
5
+ return player.currentlyPlayingSong === expectedSong &&
6
+ player.isPlaying;
7
+ }
8
+ });
9
+ });
@@ -0,0 +1,73 @@
1
+ # src_files
2
+ #
3
+ # Return an array of filepaths relative to src_dir to include before jasmine specs.
4
+ # Default: []
5
+ #
6
+ # EXAMPLE:
7
+ #
8
+ # src_files:
9
+ # - lib/source1.js
10
+ # - lib/source2.js
11
+ # - dist/**/*.js
12
+ #
13
+ src_files:
14
+ - public/javascripts/**/*.js
15
+
16
+ # stylesheets
17
+ #
18
+ # Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs.
19
+ # Default: []
20
+ #
21
+ # EXAMPLE:
22
+ #
23
+ # stylesheets:
24
+ # - css/style.css
25
+ # - stylesheets/*.css
26
+ #
27
+ stylesheets:
28
+
29
+ # helpers
30
+ #
31
+ # Return an array of filepaths relative to spec_dir to include before jasmine specs.
32
+ # Default: ["helpers/**/*.js"]
33
+ #
34
+ # EXAMPLE:
35
+ #
36
+ # helpers:
37
+ # - helpers/**/*.js
38
+ #
39
+ helpers:
40
+
41
+ # spec_files
42
+ #
43
+ # Return an array of filepaths relative to spec_dir to include.
44
+ # Default: ["**/*[sS]pec.js"]
45
+ #
46
+ # EXAMPLE:
47
+ #
48
+ # spec_files:
49
+ # - **/*[sS]pec.js
50
+ #
51
+ spec_files:
52
+
53
+ # src_dir
54
+ #
55
+ # Source directory path. Your src_files must be returned relative to this path. Will use root if left blank.
56
+ # Default: project root
57
+ #
58
+ # EXAMPLE:
59
+ #
60
+ # src_dir: public
61
+ #
62
+ src_dir:
63
+
64
+ # spec_dir
65
+ #
66
+ # Spec directory path. Your spec_files must be returned relative to this path.
67
+ # Default: spec/javascripts
68
+ #
69
+ # EXAMPLE:
70
+ #
71
+ # spec_dir: spec/javascripts
72
+ #
73
+ spec_dir:
@@ -47,8 +47,9 @@ if defined?(Selenium) && defined?(Selenium::Client) && defined?(Selenium::Client
47
47
  module Protocol
48
48
  # add custom parameters for testingbot.com
49
49
  def http_request_for_testingbot(verb, args)
50
+ raise "Please specify your key and secret in a ~/.testingbot or C:\\.testingbot file" if TestingBot.get_config[:client_key].nil?
50
51
  data = http_request_for_original(verb, args)
51
- data << "&client_key=#{TestingBot.get_config[:client_key]}&client_secret=#{TestingBot.get_config[:client_secret]}" unless TestingBot.get_config[:client_key].nil?
52
+ data << "&client_key=#{TestingBot.get_config[:client_key]}&client_secret=#{TestingBot.get_config[:client_secret]}"
52
53
  end
53
54
 
54
55
  begin
@@ -1,6 +1,6 @@
1
1
  module TestingBot
2
2
  class Tunnel
3
- TIMEOUT_SECONDS = 70
3
+ TIMEOUT_SECONDS = 140
4
4
 
5
5
  @@running = false
6
6
 
@@ -1,3 +1,3 @@
1
1
  module Testingbot
2
- VERSION = "0.1.4"
3
- end
2
+ VERSION = "0.1.5"
3
+ end
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: testingbot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-18 00:00:00.000000000 Z
12
+ date: 2013-04-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
16
- requirement: &70172125749020 !ruby/object:Gem::Requirement
16
+ requirement: &70143118085740 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70172125749020
24
+ version_requirements: *70143118085740
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: net-http-persistent
27
- requirement: &70172125748600 !ruby/object:Gem::Requirement
27
+ requirement: &70143118085320 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70172125748600
35
+ version_requirements: *70143118085320
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: selenium-webdriver
38
- requirement: &70172125748180 !ruby/object:Gem::Requirement
38
+ requirement: &70143118084900 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70172125748180
46
+ version_requirements: *70143118084900
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rspec
49
- requirement: &70172125747660 !ruby/object:Gem::Requirement
49
+ requirement: &70143118084380 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 2.9.0
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70172125747660
57
+ version_requirements: *70143118084380
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rake
60
- requirement: &70172125747240 !ruby/object:Gem::Requirement
60
+ requirement: &70143118100340 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70172125747240
68
+ version_requirements: *70143118100340
69
69
  description: This gem makes using our Selenium grid on testingbot.com easy
70
70
  email:
71
71
  - info@testingbot.com
@@ -98,6 +98,21 @@ files:
98
98
  - lib/testingbot/config.rb
99
99
  - lib/testingbot/cucumber.rb
100
100
  - lib/testingbot/hooks.rb
101
+ - lib/testingbot/jasmine.rb
102
+ - lib/testingbot/jasmine/README.rdoc
103
+ - lib/testingbot/jasmine/Rakefile
104
+ - lib/testingbot/jasmine/public/javascripts/Player.js
105
+ - lib/testingbot/jasmine/public/javascripts/Song.js
106
+ - lib/testingbot/jasmine/runner.rb
107
+ - lib/testingbot/jasmine/spec/javascripts/PlayerSpec.js
108
+ - lib/testingbot/jasmine/spec/javascripts/helpers/SpecHelper.js
109
+ - lib/testingbot/jasmine/spec/javascripts/support/jasmine.yml
110
+ - lib/testingbot/jasmine/test/Rakefile
111
+ - lib/testingbot/jasmine/test/public/javascripts/Player.js
112
+ - lib/testingbot/jasmine/test/public/javascripts/Song.js
113
+ - lib/testingbot/jasmine/test/spec/javascripts/PlayerSpec.js
114
+ - lib/testingbot/jasmine/test/spec/javascripts/helpers/SpecHelper.js
115
+ - lib/testingbot/jasmine/test/spec/javascripts/support/jasmine.yml
101
116
  - lib/testingbot/selenium.rb
102
117
  - lib/testingbot/tunnel.rb
103
118
  - lib/testingbot/version.rb