terminus 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ module Terminus
2
+ class Controller
3
+
4
+ include Timeouts
5
+ attr_accessor :dock_host
6
+
7
+ def initialize
8
+ @connected = false
9
+ @browsers = {}
10
+ @ping_callbacks = []
11
+ trap('INT') { exit }
12
+ end
13
+
14
+ def browser(id = nil)
15
+ return @browser if id.nil?
16
+ @browsers[id] ||= Browser.new(self)
17
+ end
18
+
19
+ def browser=(params)
20
+ @browser = case params
21
+ when :docked then @browsers.values.find { |b| b.docked? }
22
+ when Browser then params
23
+ when Hash then
24
+ wait_with_timeout(:named_window) do
25
+ @browsers.values.find { |b| b.name == params[:name] }
26
+ end
27
+ end
28
+ end
29
+
30
+ def drop_browser(browser)
31
+ @browsers.delete(browser.id)
32
+ @browser = nil if @browser == browser
33
+ end
34
+
35
+ def ensure_browser
36
+ ensure_connection
37
+ wait_with_timeout(:browser) { @browser }
38
+ end
39
+
40
+ def ensure_docked_browser
41
+ ensure_connection
42
+ wait_with_timeout(:docked_browser) { @browsers.any? { |id,b| b.docked? } }
43
+ end
44
+
45
+ def messenger
46
+ Terminus.ensure_reactor_running
47
+ return @messenger if defined?(@messenger)
48
+
49
+ @messenger = Faye::Client.new(Terminus.endpoint)
50
+
51
+ @messenger.subscribe('/terminus/ping', &method(:accept_ping))
52
+ @messenger.subscribe('/terminus/results', &method(:accept_result))
53
+ @messenger
54
+ end
55
+
56
+ def return_to_dock
57
+ @browsers.each { |id, b| b.return_to_dock }
58
+ end
59
+
60
+ private
61
+
62
+ def accept_ping(message)
63
+ browser(message['id']).ping!(message)
64
+ end
65
+
66
+ def accept_result(message)
67
+ browser(message['id']).result!(message)
68
+ end
69
+
70
+ def ensure_connection
71
+ return if @connected
72
+ messenger.connect { @connected = true }
73
+ wait_with_timeout(:connection) { @connected }
74
+ end
75
+
76
+ end
77
+ end
78
+
@@ -0,0 +1,68 @@
1
+ module Terminus
2
+ class Node
3
+
4
+ attr_reader :id
5
+
6
+ def initialize(browser, id)
7
+ @browser, @id = browser, id
8
+ end
9
+
10
+ def click
11
+ page = @browser.page_id
12
+ command = @browser.tell([:click, @id])
13
+
14
+ @browser.wait_with_timeout(:click_response) do
15
+ @browser.result(command) || (@browser.page_id != page)
16
+ end
17
+ end
18
+
19
+ def drag_to(node)
20
+ @browser.ask([:drag, {:from => @id, :to => node.id}])
21
+ end
22
+
23
+ def find(xpath)
24
+ @browser.ask([:find, xpath, @id]).map { |id| Node.new(@browser, id) }
25
+ end
26
+
27
+ def select
28
+ @browser.ask([:select, @id])
29
+ end
30
+
31
+ def set(value)
32
+ result = @browser.ask([:set, @id, value])
33
+ raise Capybara::NotSupportedByDriverError.new if result == 'not_allowed'
34
+ end
35
+
36
+ def trigger(event_type)
37
+ @browser.ask([:trigger, @id, event_type])
38
+ end
39
+
40
+ def unselect
41
+ allowed = @browser.ask([:unselect, @id])
42
+ raise Capybara::UnselectNotAllowed.new unless allowed
43
+ end
44
+
45
+ alias :select_option :select
46
+ alias :unselect_option :unselect
47
+
48
+ SYNC_DSL_METHODS = [ [:[], :attribute],
49
+ :tag_name,
50
+ :text,
51
+ :value,
52
+ [:visible?, :is_visible]
53
+ ]
54
+
55
+ SYNC_DSL_METHODS.each do |method|
56
+ if Array === method
57
+ name, command = *method
58
+ else
59
+ name = command = method
60
+ end
61
+ define_method(name) do |*args|
62
+ @browser.ask([command, @id, *args])
63
+ end
64
+ end
65
+
66
+ end
67
+ end
68
+
@@ -16,7 +16,7 @@ module Terminus
16
16
  def run!
17
17
  return if running?
18
18
  handler = Rack::Handler.get('thin')
19
- ensure_reactor_running!
19
+ Terminus.ensure_reactor_running
20
20
  handler.run(app, :Port => @options[:port]) { |s| @server = s }
21
21
  end
22
22
 
@@ -38,11 +38,6 @@ module Terminus
38
38
  app.get_client
39
39
  end
40
40
 
41
- def ensure_reactor_running!
42
- Thread.new { EM.run unless EM.reactor_running? }
43
- while not EM.reactor_running?; end
44
- end
45
-
46
41
  end
47
42
  end
48
43
 
@@ -0,0 +1,27 @@
1
+ module Terminus
2
+ module Timeouts
3
+
4
+ class TimeoutError < StandardError
5
+ end
6
+
7
+ include Faye::Timeouts
8
+ TIMEOUT = 10
9
+
10
+ def wait_with_timeout(name, duration = TIMEOUT, &predicate)
11
+ result, time_out = nil, false
12
+ add_timeout(name, duration) { time_out = true }
13
+
14
+ while !result and !time_out
15
+ result = predicate.call
16
+ sleep 0.1
17
+ end
18
+
19
+ raise TimeoutError.new("Waited #{duration}s but could not get a #{name}") if time_out
20
+ remove_timeout(name)
21
+
22
+ result
23
+ end
24
+
25
+ end
26
+ end
27
+
@@ -2,6 +2,11 @@
2
2
  var faye = '<%= ::Terminus::FAYE_MOUNT %>',
3
3
  host = '<%= host %>';
4
4
 
5
+ var html = document.getElementsByTagName('html')[0],
6
+ source = '<html>\n' + html.innerHTML + '\n</html>\n';
7
+
8
+ JSCLASS_PATH = host + '/js.class/';
9
+
5
10
  var withPackageManager = function(callback) {
6
11
  if (window.JS && JS.Packages) return callback();
7
12
 
@@ -9,7 +14,7 @@
9
14
  head = document.getElementsByTagName('head')[0];
10
15
 
11
16
  script.type = 'text/javascript';
12
- script.src = host + '/js.packages.js';
17
+ script.src = host + '/loader.js';
13
18
 
14
19
  script.onload = script.onreadystatechange = function() {
15
20
  var state = script.readyState;
@@ -25,14 +30,19 @@
25
30
  withPackageManager(function() {
26
31
  JS.Packages(function() {
27
32
  this.file(host + faye + '/client.js')
28
- .provides('Faye', 'Faye.Client');
33
+ .provides('Faye', 'Faye.Client')
34
+ .setup(function() { Faye.Client.prototype.MAX_DELAY = 0 });
35
+
36
+ this.file(host + '/syn.js')
37
+ .provides('Syn');
29
38
 
30
39
  this.file(host + '/terminus.js')
31
- .requires('Faye.Client')
40
+ .requires('Faye.Client', 'document.evaluate', 'Syn')
32
41
  .provides('Terminus');
33
42
  });
34
43
 
35
- require('Terminus', function() {
44
+ JS.require('Terminus', function() {
45
+ Terminus.originalSource = source;
36
46
  Terminus.connect(host + faye);
37
47
  });
38
48
  });
@@ -7,21 +7,24 @@
7
7
  <title>Terminus</title>
8
8
  </head>
9
9
  <body>
10
+ <%= Terminus.driver_script request.host %>
10
11
 
11
12
  <div class="container">
12
13
  <h1><a href="javascript:<%= bookmarklet %>">Terminus</a>.</h1>
13
14
 
14
- <p>Terminus is a distributed JavaScript console. You can find out
15
- more at <a href="http://github.com/jcoglan/terminus">its GitHub page</a>.
16
- To connect the console to your browser, you&rsquo;ll need this
17
- bookmarklet. Just drag the word &lsquo;Terminus&rsquo; up to your
18
- bookmark toolbar and click it to connect any page to your console.</p>
15
+ <p>This is the holding page. Browsers visiting this page are
16
+ connected to your Terminus server and can be taken over and
17
+ controlled by your Ruby programs.</p>
18
+
19
+ <p>That big pink title up there is a bookmarklet. Drag it up
20
+ to your bookmarks bar and use it to connect any page on the
21
+ web to your Terminus console.</p>
19
22
  </div>
20
23
 
21
24
  <div class="footer">
22
- <p>Copyright &copy; 2010
23
- <a href="http://jcoglan.com">James Coglan</a>.
24
- Released under the MIT license.</p>
25
+ <p>Copyright &copy; 2010 <a href="http://jcoglan.com">James Coglan</a>.<br>
26
+ Released under the MIT license.<br>
27
+ Source code hosted by <a href="http://github.com/jcoglan/terminus">GitHub</a>.</p>
25
28
  </div>
26
29
 
27
30
  </body>
@@ -0,0 +1,22 @@
1
+ root = File.dirname(__FILE__) + '/../'
2
+
3
+ require root + 'vendor/capybara/spec/spec_helper'
4
+ require root + 'lib/terminus'
5
+
6
+ class TestApp
7
+ template :layout do
8
+ <<-HTML
9
+ <html>
10
+ <head>
11
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
12
+ <title>Capybara Driver Test</title>
13
+ </head>
14
+ <body>
15
+ <%= yield %>
16
+ <%= Terminus.driver_script request.host %>
17
+ </body>
18
+ </html>
19
+ HTML
20
+ end
21
+ end
22
+
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Capybara::Driver::Terminus do
4
+ before do
5
+ @driver = Capybara::Driver::Terminus.new(TestApp)
6
+ Terminus.ensure_docked_browser
7
+ Terminus.browser = :docked
8
+ end
9
+
10
+ after do
11
+ Terminus.return_to_dock
12
+ end
13
+
14
+ it_should_behave_like "driver"
15
+ it_should_behave_like "driver with javascript support"
16
+ it_should_behave_like "driver without status code support"
17
+ it_should_behave_like "driver with support for window switching"
18
+ it_should_behave_like "driver with cookies support"
19
+ it_should_behave_like "driver with infinite redirect detection"
20
+ end
@@ -0,0 +1,32 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Capybara::Session do
4
+ context 'with terminus driver' do
5
+ before do
6
+ @session = Capybara::Session.new(:terminus, TestApp)
7
+ Terminus.ensure_docked_browser
8
+ Terminus.browser = :docked
9
+ end
10
+
11
+ after do
12
+ Terminus.return_to_dock
13
+ end
14
+
15
+ describe '#driver' do
16
+ it "should be a terminus driver" do
17
+ @session.driver.should be_an_instance_of(Capybara::Driver::Terminus)
18
+ end
19
+ end
20
+
21
+ describe '#mode' do
22
+ it "should remember the mode" do
23
+ @session.mode.should == :terminus
24
+ end
25
+ end
26
+
27
+ it_should_behave_like "session"
28
+ it_should_behave_like "session without headers support"
29
+ it_should_behave_like "session with javascript support"
30
+ it_should_behave_like "session without status code support"
31
+ end
32
+ end
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: terminus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
5
10
  platform: ruby
6
11
  authors:
7
12
  - James Coglan
@@ -9,81 +14,125 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-04-05 00:00:00 +01:00
17
+ date: 2010-10-24 00:00:00 +01:00
13
18
  default_executable:
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
21
  name: rack
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
20
25
  requirements:
21
26
  - - ">="
22
27
  - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 0
23
31
  version: "1.0"
24
- version:
32
+ type: :runtime
33
+ version_requirements: *id001
25
34
  - !ruby/object:Gem::Dependency
26
35
  name: thin
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
30
39
  requirements:
31
40
  - - ">="
32
41
  - !ruby/object:Gem::Version
42
+ segments:
43
+ - 1
44
+ - 2
33
45
  version: "1.2"
34
- version:
46
+ type: :runtime
47
+ version_requirements: *id002
35
48
  - !ruby/object:Gem::Dependency
36
49
  name: eventmachine
37
- type: :runtime
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
40
53
  requirements:
41
54
  - - ">="
42
55
  - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ - 12
43
59
  version: "0.12"
44
- version:
60
+ type: :runtime
61
+ version_requirements: *id003
45
62
  - !ruby/object:Gem::Dependency
46
63
  name: faye
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ - 5
73
+ - 2
74
+ version: 0.5.2
47
75
  type: :runtime
48
- version_requirement:
49
- version_requirements: !ruby/object:Gem::Requirement
76
+ version_requirements: *id004
77
+ - !ruby/object:Gem::Dependency
78
+ name: capybara
79
+ prerelease: false
80
+ requirement: &id005 !ruby/object:Gem::Requirement
81
+ none: false
50
82
  requirements:
51
- - - ~>
83
+ - - ">="
52
84
  - !ruby/object:Gem::Version
53
- version: 0.3.2
54
- version:
85
+ segments:
86
+ - 0
87
+ - 4
88
+ - 0
89
+ version: 0.4.0
90
+ type: :runtime
91
+ version_requirements: *id005
55
92
  - !ruby/object:Gem::Dependency
56
93
  name: sinatra
57
- type: :runtime
58
- version_requirement:
59
- version_requirements: !ruby/object:Gem::Requirement
94
+ prerelease: false
95
+ requirement: &id006 !ruby/object:Gem::Requirement
96
+ none: false
60
97
  requirements:
61
98
  - - ">="
62
99
  - !ruby/object:Gem::Version
100
+ segments:
101
+ - 1
102
+ - 0
63
103
  version: "1.0"
64
- version:
104
+ type: :runtime
105
+ version_requirements: *id006
65
106
  - !ruby/object:Gem::Dependency
66
107
  name: packr
67
- type: :runtime
68
- version_requirement:
69
- version_requirements: !ruby/object:Gem::Requirement
108
+ prerelease: false
109
+ requirement: &id007 !ruby/object:Gem::Requirement
110
+ none: false
70
111
  requirements:
71
112
  - - ">="
72
113
  - !ruby/object:Gem::Version
114
+ segments:
115
+ - 3
116
+ - 1
73
117
  version: "3.1"
74
- version:
118
+ type: :runtime
119
+ version_requirements: *id007
75
120
  - !ruby/object:Gem::Dependency
76
121
  name: oyster
77
- type: :runtime
78
- version_requirement:
79
- version_requirements: !ruby/object:Gem::Requirement
122
+ prerelease: false
123
+ requirement: &id008 !ruby/object:Gem::Requirement
124
+ none: false
80
125
  requirements:
81
126
  - - ">="
82
127
  - !ruby/object:Gem::Version
128
+ segments:
129
+ - 0
130
+ - 9
83
131
  version: "0.9"
84
- version:
132
+ type: :runtime
133
+ version_requirements: *id008
85
134
  description:
86
- email: jcoglan@googlemail.com
135
+ email: jcoglan@gmail.com
87
136
  executables:
88
137
  - terminus
89
138
  extensions: []
@@ -92,14 +141,24 @@ extra_rdoc_files:
92
141
  - README.rdoc
93
142
  files:
94
143
  - README.rdoc
95
- - lib/terminus.rb
96
- - lib/public/style.css
97
- - lib/public/terminus.js
98
- - lib/public/js.packages.js
99
- - lib/views/bookmarklet.erb
100
144
  - lib/views/index.erb
101
- - lib/terminus/server.rb
145
+ - lib/views/bookmarklet.erb
146
+ - lib/public/loader.js
147
+ - lib/public/syn.js
148
+ - lib/public/terminus.js
149
+ - lib/public/style.css
150
+ - lib/terminus.rb
151
+ - lib/terminus/node.rb
152
+ - lib/terminus/timeouts.rb
153
+ - lib/terminus/browser.rb
102
154
  - lib/terminus/application.rb
155
+ - lib/terminus/server.rb
156
+ - lib/terminus/controller.rb
157
+ - lib/capybara/driver/terminus.rb
158
+ - spec/terminus_session_spec.rb
159
+ - spec/terminus_driver_spec.rb
160
+ - spec/spec_helper.rb
161
+ - bin/terminus
103
162
  has_rdoc: true
104
163
  homepage: http://github.com/jcoglan/terminus
105
164
  licenses: []
@@ -111,23 +170,27 @@ rdoc_options:
111
170
  require_paths:
112
171
  - lib
113
172
  required_ruby_version: !ruby/object:Gem::Requirement
173
+ none: false
114
174
  requirements:
115
175
  - - ">="
116
176
  - !ruby/object:Gem::Version
177
+ segments:
178
+ - 0
117
179
  version: "0"
118
- version:
119
180
  required_rubygems_version: !ruby/object:Gem::Requirement
181
+ none: false
120
182
  requirements:
121
183
  - - ">="
122
184
  - !ruby/object:Gem::Version
185
+ segments:
186
+ - 0
123
187
  version: "0"
124
- version:
125
188
  requirements: []
126
189
 
127
190
  rubyforge_project:
128
- rubygems_version: 1.3.5
191
+ rubygems_version: 1.3.7
129
192
  signing_key:
130
193
  specification_version: 3
131
- summary: Control multiple web browsers from the command line
194
+ summary: Capybara driver for cross-browser testing
132
195
  test_files: []
133
196